Merge remote-tracking branch 'aosp/upstream' into master

* aosp/upstream:
  Remove blueprint:"filter(*)" tag support
  Make FilterPropertyStructSharded smarter

Bug: 146234651
Test: m checkbuild
Change-Id: Ib3de8d8dd43e6354c17f1734705a9feb2ca7f701
diff --git a/bootstrap/bpdoc/bpdoc.go b/bootstrap/bpdoc/bpdoc.go
index a33886d..4abf2e7 100644
--- a/bootstrap/bpdoc/bpdoc.go
+++ b/bootstrap/bpdoc/bpdoc.go
@@ -145,14 +145,6 @@
 				return nil, fmt.Errorf("nesting point %q not found", nestedName)
 			}
 
-			key, value, err := proptools.HasFilter(nestPoint.Tag)
-			if err != nil {
-				return nil, err
-			}
-			if key != "" {
-				nested.IncludeByTag(key, value)
-			}
-
 			nestPoint.Nest(nested)
 		}
 		mt.PropertyStructs = append(mt.PropertyStructs, ps)
diff --git a/proptools/filter.go b/proptools/filter.go
index 7a61b02..59eca5a 100644
--- a/proptools/filter.go
+++ b/proptools/filter.go
@@ -15,12 +15,60 @@
 package proptools
 
 import (
+	"fmt"
 	"reflect"
+	"strconv"
 )
 
 type FilterFieldPredicate func(field reflect.StructField, string string) (bool, reflect.StructField)
 
-func filterPropertyStructFields(fields []reflect.StructField, prefix string, predicate FilterFieldPredicate) (filteredFields []reflect.StructField, filtered bool) {
+type cantFitPanic struct {
+	field reflect.StructField
+	size  int
+}
+
+func (x cantFitPanic) Error() string {
+	return fmt.Sprintf("Can't fit field %s %s %s size %d into %d",
+		x.field.Name, x.field.Type.String(), strconv.Quote(string(x.field.Tag)),
+		fieldToTypeNameSize(x.field, true)+2, x.size)
+}
+
+// All runtime created structs will have a name that starts with "struct {" and ends with "}"
+const emptyStructTypeNameSize = len("struct {}")
+
+func filterPropertyStructFields(fields []reflect.StructField, prefix string, maxTypeNameSize int,
+	predicate FilterFieldPredicate) (filteredFieldsShards [][]reflect.StructField, filtered bool) {
+
+	structNameSize := emptyStructTypeNameSize
+
+	var filteredFields []reflect.StructField
+
+	appendAndShardIfNameFull := func(field reflect.StructField) {
+		fieldTypeNameSize := fieldToTypeNameSize(field, true)
+		// Every field will have a space before it and either a semicolon or space after it.
+		fieldTypeNameSize += 2
+
+		if maxTypeNameSize > 0 && structNameSize+fieldTypeNameSize > maxTypeNameSize {
+			if len(filteredFields) == 0 {
+				if field.Type.Kind() == reflect.Struct ||
+					field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
+					// An error fitting the nested struct should have been caught when recursing
+					// into the nested struct.
+					panic(fmt.Errorf("Shouldn't happen: can't fit nested struct %q (%d) into %d",
+						field.Type.String(), len(field.Type.String()), maxTypeNameSize-structNameSize))
+				}
+				panic(cantFitPanic{field, maxTypeNameSize - structNameSize})
+
+			}
+			filteredFieldsShards = append(filteredFieldsShards, filteredFields)
+			filteredFields = nil
+			structNameSize = emptyStructTypeNameSize
+		}
+
+		filteredFields = append(filteredFields, field)
+		structNameSize += fieldTypeNameSize
+	}
+
 	for _, field := range fields {
 		var keep bool
 		if keep, field = predicate(field, prefix); !keep {
@@ -33,32 +81,61 @@
 			subPrefix = prefix + "." + subPrefix
 		}
 
-		// Recurse into structs
-		switch field.Type.Kind() {
-		case reflect.Struct:
-			var subFiltered bool
-			field.Type, subFiltered = filterPropertyStruct(field.Type, subPrefix, predicate)
-			filtered = filtered || subFiltered
-			if field.Type == nil {
-				continue
-			}
-		case reflect.Ptr:
-			if field.Type.Elem().Kind() == reflect.Struct {
-				nestedType, subFiltered := filterPropertyStruct(field.Type.Elem(), subPrefix, predicate)
-				filtered = filtered || subFiltered
-				if nestedType == nil {
-					continue
-				}
-				field.Type = reflect.PtrTo(nestedType)
-			}
-		case reflect.Interface:
-			panic("Interfaces are not supported in filtered property structs")
+		ptrToStruct := false
+		if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
+			ptrToStruct = true
 		}
 
-		filteredFields = append(filteredFields, field)
+		// Recurse into structs
+		if ptrToStruct || field.Type.Kind() == reflect.Struct {
+			subMaxTypeNameSize := maxTypeNameSize
+			if maxTypeNameSize > 0 {
+				// In the worst case where only this nested struct will fit in the outer struct, the
+				// outer struct will contribute struct{}, the name and tag of the field that contains
+				// the nested struct, and one space before and after the field.
+				subMaxTypeNameSize -= emptyStructTypeNameSize + fieldToTypeNameSize(field, false) + 2
+			}
+			typ := field.Type
+			if ptrToStruct {
+				subMaxTypeNameSize -= len("*")
+				typ = typ.Elem()
+			}
+			nestedTypes, subFiltered := filterPropertyStruct(typ, subPrefix, subMaxTypeNameSize, predicate)
+			filtered = filtered || subFiltered
+			if nestedTypes == nil {
+				continue
+			}
+
+			for _, nestedType := range nestedTypes {
+				if ptrToStruct {
+					nestedType = reflect.PtrTo(nestedType)
+				}
+				field.Type = nestedType
+				appendAndShardIfNameFull(field)
+			}
+		} else {
+			appendAndShardIfNameFull(field)
+		}
 	}
 
-	return filteredFields, filtered
+	if len(filteredFields) > 0 {
+		filteredFieldsShards = append(filteredFieldsShards, filteredFields)
+	}
+
+	return filteredFieldsShards, filtered
+}
+
+func fieldToTypeNameSize(field reflect.StructField, withType bool) int {
+	nameSize := len(field.Name)
+	nameSize += len(" ")
+	if withType {
+		nameSize += len(field.Type.String())
+	}
+	if field.Tag != "" {
+		nameSize += len(" ")
+		nameSize += len(strconv.Quote(string(field.Tag)))
+	}
+	return nameSize
 }
 
 // FilterPropertyStruct takes a reflect.Type that is either a struct or a pointer to a struct, and returns a
@@ -66,10 +143,20 @@
 // that is true if the new struct type has fewer fields than the original type.  If there are no fields in the
 // original type for which predicate returns true it returns nil and true.
 func FilterPropertyStruct(prop reflect.Type, predicate FilterFieldPredicate) (filteredProp reflect.Type, filtered bool) {
-	return filterPropertyStruct(prop, "", predicate)
+	filteredFieldsShards, filtered := filterPropertyStruct(prop, "", -1, predicate)
+	switch len(filteredFieldsShards) {
+	case 0:
+		return nil, filtered
+	case 1:
+		return filteredFieldsShards[0], filtered
+	default:
+		panic("filterPropertyStruct should only return 1 struct if maxNameSize < 0")
+	}
 }
 
-func filterPropertyStruct(prop reflect.Type, prefix string, predicate FilterFieldPredicate) (filteredProp reflect.Type, filtered bool) {
+func filterPropertyStruct(prop reflect.Type, prefix string, maxNameSize int,
+	predicate FilterFieldPredicate) (filteredProp []reflect.Type, filtered bool) {
+
 	var fields []reflect.StructField
 
 	ptr := prop.Kind() == reflect.Ptr
@@ -81,22 +168,26 @@
 		fields = append(fields, prop.Field(i))
 	}
 
-	filteredFields, filtered := filterPropertyStructFields(fields, prefix, predicate)
+	filteredFieldsShards, filtered := filterPropertyStructFields(fields, prefix, maxNameSize, predicate)
 
-	if len(filteredFields) == 0 {
+	if len(filteredFieldsShards) == 0 {
 		return nil, true
 	}
 
 	if !filtered {
 		if ptr {
-			return reflect.PtrTo(prop), false
+			return []reflect.Type{reflect.PtrTo(prop)}, false
 		}
-		return prop, false
+		return []reflect.Type{prop}, false
 	}
 
-	ret := reflect.StructOf(filteredFields)
-	if ptr {
-		ret = reflect.PtrTo(ret)
+	var ret []reflect.Type
+	for _, filteredFields := range filteredFieldsShards {
+		p := reflect.StructOf(filteredFields)
+		if ptr {
+			p = reflect.PtrTo(p)
+		}
+		ret = append(ret, p)
 	}
 
 	return ret, true
@@ -109,51 +200,6 @@
 // level fields in it to attempt to avoid hitting the 65535 byte type name length limit in reflect.StructOf
 // (reflect.nameFrom: name too long), although the limit can still be reached with a single struct field with many
 // fields in it.
-func FilterPropertyStructSharded(prop reflect.Type, predicate FilterFieldPredicate) (filteredProp []reflect.Type, filtered bool) {
-	var fields []reflect.StructField
-
-	ptr := prop.Kind() == reflect.Ptr
-	if ptr {
-		prop = prop.Elem()
-	}
-
-	for i := 0; i < prop.NumField(); i++ {
-		fields = append(fields, prop.Field(i))
-	}
-
-	fields, filtered = filterPropertyStructFields(fields, "", predicate)
-	if !filtered {
-		if ptr {
-			return []reflect.Type{reflect.PtrTo(prop)}, false
-		}
-		return []reflect.Type{prop}, false
-	}
-
-	if len(fields) == 0 {
-		return nil, true
-	}
-
-	shards := shardFields(fields, 10)
-
-	for _, shard := range shards {
-		s := reflect.StructOf(shard)
-		if ptr {
-			s = reflect.PtrTo(s)
-		}
-		filteredProp = append(filteredProp, s)
-	}
-
-	return filteredProp, true
-}
-
-func shardFields(fields []reflect.StructField, shardSize int) [][]reflect.StructField {
-	ret := make([][]reflect.StructField, 0, (len(fields)+shardSize-1)/shardSize)
-	for len(fields) > shardSize {
-		ret = append(ret, fields[0:shardSize])
-		fields = fields[shardSize:]
-	}
-	if len(fields) > 0 {
-		ret = append(ret, fields)
-	}
-	return ret
+func FilterPropertyStructSharded(prop reflect.Type, maxTypeNameSize int, predicate FilterFieldPredicate) (filteredProp []reflect.Type, filtered bool) {
+	return filterPropertyStruct(prop, "", maxTypeNameSize, predicate)
 }
diff --git a/proptools/filter_test.go b/proptools/filter_test.go
index 695549a..0ea04bb 100644
--- a/proptools/filter_test.go
+++ b/proptools/filter_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -237,3 +238,281 @@
 		})
 	}
 }
+
+func TestFilterPropertyStructSharded(t *testing.T) {
+	tests := []struct {
+		name        string
+		maxNameSize int
+		in          interface{}
+		out         []interface{}
+		filtered    bool
+	}{
+		// Property tests
+		{
+			name:        "basic",
+			maxNameSize: 20,
+			in: &struct {
+				A *string `keep:"true"`
+				B *string `keep:"true"`
+				C *string
+			}{},
+			out: []interface{}{
+				&struct {
+					A *string
+				}{},
+				&struct {
+					B *string
+				}{},
+			},
+			filtered: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			out, filtered := filterPropertyStruct(reflect.TypeOf(test.in), "", test.maxNameSize,
+				func(field reflect.StructField, prefix string) (bool, reflect.StructField) {
+					if HasTag(field, "keep", "true") {
+						field.Tag = ""
+						return true, field
+					}
+					return false, field
+				})
+			if filtered != test.filtered {
+				t.Errorf("expected filtered %v, got %v", test.filtered, filtered)
+			}
+			var expected []reflect.Type
+			for _, t := range test.out {
+				expected = append(expected, reflect.TypeOf(t))
+			}
+			if !reflect.DeepEqual(out, expected) {
+				t.Errorf("expected type %v, got %v", expected, out)
+			}
+		})
+	}
+}
+
+func Test_fieldToTypeNameSize(t *testing.T) {
+	tests := []struct {
+		name  string
+		field reflect.StructField
+	}{
+		{
+			name: "string",
+			field: reflect.StructField{
+				Name: "Foo",
+				Type: reflect.TypeOf(""),
+			},
+		},
+		{
+			name: "string pointer",
+			field: reflect.StructField{
+				Name: "Foo",
+				Type: reflect.TypeOf(StringPtr("")),
+			},
+		},
+		{
+			name: "anonymous struct",
+			field: reflect.StructField{
+				Name: "Foo",
+				Type: reflect.TypeOf(struct{ foo string }{}),
+			},
+		}, {
+			name: "anonymous struct pointer",
+			field: reflect.StructField{
+				Name: "Foo",
+				Type: reflect.TypeOf(&struct{ foo string }{}),
+			},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			typeName := reflect.StructOf([]reflect.StructField{test.field}).String()
+			typeName = strings.TrimPrefix(typeName, "struct { ")
+			typeName = strings.TrimSuffix(typeName, " }")
+			if g, w := fieldToTypeNameSize(test.field, true), len(typeName); g != w {
+				t.Errorf("want fieldToTypeNameSize(..., true) = %v, got %v", w, g)
+			}
+			if g, w := fieldToTypeNameSize(test.field, false), len(typeName)-len(test.field.Type.String()); g != w {
+				t.Errorf("want fieldToTypeNameSize(..., false) = %v, got %v", w, g)
+			}
+		})
+	}
+}
+
+func Test_filterPropertyStructFields(t *testing.T) {
+	type args struct {
+	}
+	tests := []struct {
+		name            string
+		maxTypeNameSize int
+		in              interface{}
+		out             []interface{}
+	}{
+		{
+			name:            "empty",
+			maxTypeNameSize: -1,
+			in:              struct{}{},
+			out:             nil,
+		},
+		{
+			name:            "one",
+			maxTypeNameSize: -1,
+			in: struct {
+				A *string
+			}{},
+			out: []interface{}{
+				struct {
+					A *string
+				}{},
+			},
+		},
+		{
+			name:            "two",
+			maxTypeNameSize: 20,
+			in: struct {
+				A *string
+				B *string
+			}{},
+			out: []interface{}{
+				struct {
+					A *string
+				}{},
+				struct {
+					B *string
+				}{},
+			},
+		},
+		{
+			name:            "nested",
+			maxTypeNameSize: 36,
+			in: struct {
+				AAAAA struct {
+					A string
+				}
+				BBBBB struct {
+					B string
+				}
+			}{},
+			out: []interface{}{
+				struct {
+					AAAAA struct {
+						A string
+					}
+				}{},
+				struct {
+					BBBBB struct {
+						B string
+					}
+				}{},
+			},
+		},
+		{
+			name:            "nested pointer",
+			maxTypeNameSize: 37,
+			in: struct {
+				AAAAA *struct {
+					A string
+				}
+				BBBBB *struct {
+					B string
+				}
+			}{},
+			out: []interface{}{
+				struct {
+					AAAAA *struct {
+						A string
+					}
+				}{},
+				struct {
+					BBBBB *struct {
+						B string
+					}
+				}{},
+			},
+		},
+		{
+			name:            "doubly nested",
+			maxTypeNameSize: 49,
+			in: struct {
+				AAAAA struct {
+					A struct {
+						A string
+					}
+				}
+				BBBBB struct {
+					B struct {
+						B string
+					}
+				}
+			}{},
+			out: []interface{}{
+				struct {
+					AAAAA struct {
+						A struct {
+							A string
+						}
+					}
+				}{},
+				struct {
+					BBBBB struct {
+						B struct {
+							B string
+						}
+					}
+				}{},
+			},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			inType := reflect.TypeOf(test.in)
+			var in []reflect.StructField
+			for i := 0; i < inType.NumField(); i++ {
+				in = append(in, inType.Field(i))
+			}
+
+			keep := func(field reflect.StructField, string string) (bool, reflect.StructField) {
+				return true, field
+			}
+
+			// Test that maxTypeNameSize is the
+			if test.maxTypeNameSize > 0 {
+				correctPanic := false
+				func() {
+					defer func() {
+						if r := recover(); r != nil {
+							if _, ok := r.(cantFitPanic); ok {
+								correctPanic = true
+							} else {
+								panic(r)
+							}
+						}
+					}()
+
+					_, _ = filterPropertyStructFields(in, "", test.maxTypeNameSize-1, keep)
+				}()
+
+				if !correctPanic {
+					t.Errorf("filterPropertyStructFields() with size-1 should produce cantFitPanic")
+				}
+			}
+
+			filteredFieldsShards, _ := filterPropertyStructFields(in, "", test.maxTypeNameSize, keep)
+
+			var out []interface{}
+			for _, filteredFields := range filteredFieldsShards {
+				typ := reflect.StructOf(filteredFields)
+				if test.maxTypeNameSize > 0 && len(typ.String()) > test.maxTypeNameSize {
+					t.Errorf("out %q expected size <= %d, got %d",
+						typ.String(), test.maxTypeNameSize, len(typ.String()))
+				}
+				out = append(out, reflect.Zero(typ).Interface())
+			}
+
+			if g, w := out, test.out; !reflect.DeepEqual(g, w) {
+				t.Errorf("filterPropertyStructFields() want %v, got %v", w, g)
+			}
+		})
+	}
+}
diff --git a/proptools/unpack.go b/proptools/unpack.go
index 1d95444..e7d4fff 100644
--- a/proptools/unpack.go
+++ b/proptools/unpack.go
@@ -17,8 +17,6 @@
 import (
 	"fmt"
 	"reflect"
-	"strconv"
-	"strings"
 	"text/scanner"
 
 	"github.com/google/blueprint/parser"
@@ -60,7 +58,7 @@
 			panic("properties must be a pointer to a struct")
 		}
 
-		newErrs := unpackStructValue("", propertiesValue, propertyMap, "", "")
+		newErrs := unpackStructValue("", propertiesValue, propertyMap)
 		errs = append(errs, newErrs...)
 
 		if len(errs) >= maxUnpackErrors {
@@ -130,7 +128,7 @@
 }
 
 func unpackStructValue(namePrefix string, structValue reflect.Value,
-	propertyMap map[string]*packedProperty, filterKey, filterValue string) []error {
+	propertyMap map[string]*packedProperty) []error {
 
 	structType := structValue.Type()
 
@@ -215,7 +213,7 @@
 		}
 
 		if field.Anonymous && fieldValue.Kind() == reflect.Struct {
-			newErrs := unpackStructValue(namePrefix, fieldValue, propertyMap, filterKey, filterValue)
+			newErrs := unpackStructValue(namePrefix, fieldValue, propertyMap)
 			errs = append(errs, newErrs...)
 			continue
 		}
@@ -239,40 +237,11 @@
 			continue
 		}
 
-		if filterKey != "" && !HasTag(field, filterKey, filterValue) {
-			errs = append(errs,
-				&UnpackError{
-					Err: fmt.Errorf("filtered field %s cannot be set in a Blueprint file", propertyName),
-					Pos: packedProperty.property.ColonPos,
-				})
-			if len(errs) >= maxUnpackErrors {
-				return errs
-			}
-			continue
-		}
-
 		var newErrs []error
 
 		if fieldValue.Kind() == reflect.Struct {
-			localFilterKey, localFilterValue := filterKey, filterValue
-			if k, v, err := HasFilter(field.Tag); err != nil {
-				errs = append(errs, err)
-				if len(errs) >= maxUnpackErrors {
-					return errs
-				}
-			} else if k != "" {
-				if filterKey != "" {
-					errs = append(errs, fmt.Errorf("nested filter tag not supported on field %q",
-						field.Name))
-					if len(errs) >= maxUnpackErrors {
-						return errs
-					}
-				} else {
-					localFilterKey, localFilterValue = k, v
-				}
-			}
 			newErrs = unpackStruct(propertyName+".", fieldValue,
-				packedProperty.property, propertyMap, localFilterKey, localFilterValue)
+				packedProperty.property, propertyMap)
 
 			errs = append(errs, newErrs...)
 			if len(errs) >= maxUnpackErrors {
@@ -365,8 +334,7 @@
 }
 
 func unpackStruct(namePrefix string, structValue reflect.Value,
-	property *parser.Property, propertyMap map[string]*packedProperty,
-	filterKey, filterValue string) []error {
+	property *parser.Property, propertyMap map[string]*packedProperty) []error {
 
 	m, ok := property.Value.Eval().(*parser.Map)
 	if !ok {
@@ -381,31 +349,5 @@
 		return errs
 	}
 
-	return unpackStructValue(namePrefix, structValue, propertyMap, filterKey, filterValue)
-}
-
-func HasFilter(field reflect.StructTag) (k, v string, err error) {
-	tag := field.Get("blueprint")
-	for _, entry := range strings.Split(tag, ",") {
-		if strings.HasPrefix(entry, "filter") {
-			if !strings.HasPrefix(entry, "filter(") || !strings.HasSuffix(entry, ")") {
-				return "", "", fmt.Errorf("unexpected format for filter %q: missing ()", entry)
-			}
-			entry = strings.TrimPrefix(entry, "filter(")
-			entry = strings.TrimSuffix(entry, ")")
-
-			s := strings.Split(entry, ":")
-			if len(s) != 2 {
-				return "", "", fmt.Errorf("unexpected format for filter %q: expected single ':'", entry)
-			}
-			k = s[0]
-			v, err = strconv.Unquote(s[1])
-			if err != nil {
-				return "", "", fmt.Errorf("unexpected format for filter %q: %s", entry, err.Error())
-			}
-			return k, v, nil
-		}
-	}
-
-	return "", "", nil
+	return unpackStructValue(namePrefix, structValue, propertyMap)
 }
diff --git a/proptools/unpack_test.go b/proptools/unpack_test.go
index 8d8c543..931cfdd 100644
--- a/proptools/unpack_test.go
+++ b/proptools/unpack_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"bytes"
-	"fmt"
 	"reflect"
 	"testing"
 	"text/scanner"
@@ -219,39 +218,6 @@
 		},
 	},
 
-	{
-		input: `
-			m {
-				nested: {
-					foo: "abc",
-				},
-				bar: false,
-				baz: ["def", "ghi"],
-			}
-		`,
-		output: []interface{}{
-			struct {
-				Nested struct {
-					Foo string
-				} `blueprint:"filter(allowNested:\"true\")"`
-				Bar bool
-				Baz []string
-			}{
-				Nested: struct{ Foo string }{
-					Foo: "",
-				},
-				Bar: false,
-				Baz: []string{"def", "ghi"},
-			},
-		},
-		errs: []error{
-			&UnpackError{
-				Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
-				Pos: mkpos(30, 4, 9),
-			},
-		},
-	},
-
 	// Anonymous struct
 	{
 		input: `