Allow pruning of unsupported fields in structs in maps
Adds support for traversing into a field that is of type: map[...]*struct{...} This is needed to allow java_sdk_library to mark scope specific properties, e.g. public.annotations as being target build release specific. It was necessary to change the Scope field from: Scope map[*apiScope]scopeProperties to: Scope map[*apiScope]*scopeProperties That is because there is no way in go to change the field of a struct value of a map. i.e. you cannot do the following, not even using reflection: Scope[apiScopePublic].AnnotationsZip = nil Bug: 204763318 Test: m nothing Change-Id: Id103f70f55d4202971321ef4925cbec4b55f8136
This commit is contained in:
@@ -2755,7 +2755,7 @@ type sdkLibrarySdkMemberProperties struct {
|
||||
android.SdkMemberPropertiesBase
|
||||
|
||||
// Scope to per scope properties.
|
||||
Scopes map[*apiScope]scopeProperties
|
||||
Scopes map[*apiScope]*scopeProperties
|
||||
|
||||
// The Java stubs source files.
|
||||
Stub_srcs []string
|
||||
@@ -2815,7 +2815,7 @@ type scopeProperties struct {
|
||||
func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
|
||||
sdk := variant.(*SdkLibrary)
|
||||
|
||||
s.Scopes = make(map[*apiScope]scopeProperties)
|
||||
s.Scopes = make(map[*apiScope]*scopeProperties)
|
||||
for _, apiScope := range allApiScopes {
|
||||
paths := sdk.findScopePaths(apiScope)
|
||||
if paths == nil {
|
||||
@@ -2838,7 +2838,7 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe
|
||||
if paths.annotationsZip.Valid() {
|
||||
properties.AnnotationsZip = paths.annotationsZip.Path()
|
||||
}
|
||||
s.Scopes[apiScope] = properties
|
||||
s.Scopes[apiScope] = &properties
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -269,6 +269,51 @@ func (p *propertyPruner) gatherFields(structType reflect.Type, containingStructA
|
||||
subNamePrefix = name + "."
|
||||
}
|
||||
p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
|
||||
|
||||
case reflect.Map:
|
||||
// Get the type of the values stored in the map.
|
||||
valueType := fieldType.Elem()
|
||||
// Skip over * types.
|
||||
if valueType.Kind() == reflect.Ptr {
|
||||
valueType = valueType.Elem()
|
||||
}
|
||||
if valueType.Kind() == reflect.Struct {
|
||||
// If this is not referenced by a pointer then it is an error as it is impossible to
|
||||
// modify a struct that is stored directly as a value in a map.
|
||||
if fieldType.Elem().Kind() != reflect.Ptr {
|
||||
panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
|
||||
" be pointers to structs",
|
||||
fieldType.Elem(), name))
|
||||
}
|
||||
|
||||
// Create a new pruner for the values of the map.
|
||||
valuePruner := newPropertyPrunerForStructType(valueType, selector)
|
||||
|
||||
// Create a new fieldPruner that will iterate over all the items in the map and call the
|
||||
// pruner on them.
|
||||
fieldPruner := func(container reflect.Value) {
|
||||
mapValue := fieldGetter(container)
|
||||
|
||||
for _, keyValue := range mapValue.MapKeys() {
|
||||
itemValue := mapValue.MapIndex(keyValue)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
|
||||
}
|
||||
}()
|
||||
|
||||
valuePruner.pruneProperties(itemValue.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
// Add the map field pruner to the list of property pruners.
|
||||
property := prunerProperty{
|
||||
name + "[*]",
|
||||
fieldPruner,
|
||||
}
|
||||
p.properties = append(p.properties, property)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,6 +349,13 @@ type fieldSelectorFunc func(name string, field reflect.StructField) bool
|
||||
// of properties.
|
||||
func newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
|
||||
structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
|
||||
return newPropertyPrunerForStructType(structType, selector)
|
||||
}
|
||||
|
||||
// newPropertyPruner creates a new property pruner for the supplied properties struct type.
|
||||
//
|
||||
// The returned pruner can be used on any properties structure of the supplied type.
|
||||
func newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
|
||||
pruner := &propertyPruner{}
|
||||
pruner.gatherFields(structType, nil, "", selector)
|
||||
return pruner
|
||||
|
@@ -126,11 +126,17 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
|
||||
F1_only string `supported_build_releases:"F1"`
|
||||
}
|
||||
|
||||
type mapped struct {
|
||||
Default string
|
||||
T_only string `supported_build_releases:"T"`
|
||||
}
|
||||
|
||||
type testBuildReleasePruner struct {
|
||||
Default string
|
||||
S_and_T_only string `supported_build_releases:"S-T"`
|
||||
T_later string `supported_build_releases:"T+"`
|
||||
Nested nested
|
||||
Mapped map[string]*mapped
|
||||
}
|
||||
|
||||
inputFactory := func() testBuildReleasePruner {
|
||||
@@ -141,6 +147,16 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
|
||||
Nested: nested{
|
||||
F1_only: "F1_only",
|
||||
},
|
||||
Mapped: map[string]*mapped{
|
||||
"one": {
|
||||
Default: "one-default",
|
||||
T_only: "one-t-only",
|
||||
},
|
||||
"two": {
|
||||
Default: "two-default",
|
||||
T_only: "two-t-only",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +185,8 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
|
||||
expected := inputFactory()
|
||||
expected.T_later = ""
|
||||
expected.Nested.F1_only = ""
|
||||
expected.Mapped["one"].T_only = ""
|
||||
expected.Mapped["two"].T_only = ""
|
||||
assertJsonEquals(t, expected, testStruct)
|
||||
})
|
||||
|
||||
@@ -189,6 +207,8 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
|
||||
|
||||
expected := inputFactory()
|
||||
expected.S_and_T_only = ""
|
||||
expected.Mapped["one"].T_only = ""
|
||||
expected.Mapped["two"].T_only = ""
|
||||
assertJsonEquals(t, expected, testStruct)
|
||||
})
|
||||
|
||||
@@ -200,6 +220,8 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
|
||||
expected := inputFactory()
|
||||
expected.S_and_T_only = ""
|
||||
expected.Nested.F1_only = ""
|
||||
expected.Mapped["one"].T_only = ""
|
||||
expected.Mapped["two"].T_only = ""
|
||||
assertJsonEquals(t, expected, testStruct)
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user