Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 106a3a4b authored by Paul Duffin's avatar Paul Duffin
Browse files

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
parent 545c5927
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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
		}
	}

+52 −0
Original line number Diff line number Diff line
@@ -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
+22 −0
Original line number Diff line number Diff line
@@ -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)
	})
}