Merge remote-tracking branch 'aosp/upstream' into master
* aosp/upstream:
Add proptools.FilterPropertyStruct
Make CreateModule return the newly created module
Add proptools.Int and proptools.IntDefault
Test: m checkbuild
Change-Id: I4bbd45be12794c1a8575799950772d4ecd272628
diff --git a/Blueprints b/Blueprints
index d58169e..c3c8975 100644
--- a/Blueprints
+++ b/Blueprints
@@ -79,6 +79,7 @@
"proptools/clone.go",
"proptools/escape.go",
"proptools/extend.go",
+ "proptools/filter.go",
"proptools/proptools.go",
"proptools/tag.go",
"proptools/typeequal.go",
@@ -87,6 +88,7 @@
"proptools/clone_test.go",
"proptools/escape_test.go",
"proptools/extend_test.go",
+ "proptools/filter_test.go",
"proptools/tag_test.go",
"proptools/typeequal_test.go",
],
diff --git a/go.mod b/go.mod
index 933cd12..fe96d45 100644
--- a/go.mod
+++ b/go.mod
@@ -1 +1,3 @@
module github.com/google/blueprint
+
+go 1.13
diff --git a/module_ctx.go b/module_ctx.go
index bc05787..be5d974 100644
--- a/module_ctx.go
+++ b/module_ctx.go
@@ -712,7 +712,7 @@
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
// the specified property structs to it as if the properties were set in a blueprint file.
- CreateModule(ModuleFactory, ...interface{})
+ CreateModule(ModuleFactory, ...interface{}) Module
}
type BottomUpMutatorContext interface {
@@ -934,7 +934,7 @@
mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
}
-func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) {
+func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
module := mctx.context.newModule(factory)
module.relBlueprintsFile = mctx.module.relBlueprintsFile
@@ -950,6 +950,8 @@
}
mctx.newModules = append(mctx.newModules, module)
+
+ return module.logicModule
}
// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property
diff --git a/proptools/filter.go b/proptools/filter.go
new file mode 100644
index 0000000..7a61b02
--- /dev/null
+++ b/proptools/filter.go
@@ -0,0 +1,159 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package proptools
+
+import (
+ "reflect"
+)
+
+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) {
+ for _, field := range fields {
+ var keep bool
+ if keep, field = predicate(field, prefix); !keep {
+ filtered = true
+ continue
+ }
+
+ subPrefix := field.Name
+ if prefix != "" {
+ 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")
+ }
+
+ filteredFields = append(filteredFields, field)
+ }
+
+ return filteredFields, filtered
+}
+
+// FilterPropertyStruct takes a reflect.Type that is either a struct or a pointer to a struct, and returns a
+// reflect.Type that only contains the fields in the original type for which predicate returns true, and a bool
+// 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)
+}
+
+func filterPropertyStruct(prop reflect.Type, prefix string, 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))
+ }
+
+ filteredFields, filtered := filterPropertyStructFields(fields, prefix, predicate)
+
+ if len(filteredFields) == 0 {
+ return nil, true
+ }
+
+ if !filtered {
+ if ptr {
+ return reflect.PtrTo(prop), false
+ }
+ return prop, false
+ }
+
+ ret := reflect.StructOf(filteredFields)
+ if ptr {
+ ret = reflect.PtrTo(ret)
+ }
+
+ return ret, true
+}
+
+// FilterPropertyStructSharded takes a reflect.Type that is either a sturct or a pointer to a struct, and returns a list
+// of reflect.Type that only contains the fields in the original type for which predicate returns true, and a bool 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. Each returned struct type will have a maximum of 10 top
+// 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
+}
diff --git a/proptools/filter_test.go b/proptools/filter_test.go
new file mode 100644
index 0000000..695549a
--- /dev/null
+++ b/proptools/filter_test.go
@@ -0,0 +1,239 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package proptools
+
+import (
+ "reflect"
+ "testing"
+)
+
+type Named struct {
+ A *string `keep:"true"`
+ B *string
+}
+
+type NamedAllFiltered struct {
+ A *string
+}
+
+type NamedNoneFiltered struct {
+ A *string `keep:"true"`
+}
+
+func TestFilterPropertyStruct(t *testing.T) {
+ tests := []struct {
+ name string
+ in interface{}
+ out interface{}
+ filtered bool
+ }{
+ // Property tests
+ {
+ name: "basic",
+ in: &struct {
+ A *string `keep:"true"`
+ B *string
+ }{},
+ out: &struct {
+ A *string
+ }{},
+ filtered: true,
+ },
+ {
+ name: "all filtered",
+ in: &struct {
+ A *string
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "none filtered",
+ in: &struct {
+ A *string `keep:"true"`
+ }{},
+ out: &struct {
+ A *string `keep:"true"`
+ }{},
+ filtered: false,
+ },
+
+ // Sub-struct tests
+ {
+ name: "substruct",
+ in: &struct {
+ A struct {
+ A *string `keep:"true"`
+ B *string
+ } `keep:"true"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "substruct all filtered",
+ in: &struct {
+ A struct {
+ A *string
+ } `keep:"true"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "substruct none filtered",
+ in: &struct {
+ A struct {
+ A *string `keep:"true"`
+ } `keep:"true"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string `keep:"true"`
+ } `keep:"true"`
+ }{},
+ filtered: false,
+ },
+
+ // Named sub-struct tests
+ {
+ name: "named substruct",
+ in: &struct {
+ A Named `keep:"true"`
+ }{},
+ out: &struct {
+ A struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "substruct all filtered",
+ in: &struct {
+ A NamedAllFiltered `keep:"true"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "substruct none filtered",
+ in: &struct {
+ A NamedNoneFiltered `keep:"true"`
+ }{},
+ out: &struct {
+ A NamedNoneFiltered `keep:"true"`
+ }{},
+ filtered: false,
+ },
+
+ // Pointer to sub-struct tests
+ {
+ name: "pointer substruct",
+ in: &struct {
+ A *struct {
+ A *string `keep:"true"`
+ B *string
+ } `keep:"true"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "pointer substruct all filtered",
+ in: &struct {
+ A *struct {
+ A *string
+ } `keep:"true"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "pointer substruct none filtered",
+ in: &struct {
+ A *struct {
+ A *string `keep:"true"`
+ } `keep:"true"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string `keep:"true"`
+ } `keep:"true"`
+ }{},
+ filtered: false,
+ },
+
+ // Pointer to named sub-struct tests
+ {
+ name: "pointer named substruct",
+ in: &struct {
+ A *Named `keep:"true"`
+ }{},
+ out: &struct {
+ A *struct {
+ A *string
+ }
+ }{},
+ filtered: true,
+ },
+ {
+ name: "pointer substruct all filtered",
+ in: &struct {
+ A *NamedAllFiltered `keep:"true"`
+ }{},
+ out: nil,
+ filtered: true,
+ },
+ {
+ name: "pointer substruct none filtered",
+ in: &struct {
+ A *NamedNoneFiltered `keep:"true"`
+ }{},
+ out: &struct {
+ A *NamedNoneFiltered `keep:"true"`
+ }{},
+ filtered: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ out, filtered := FilterPropertyStruct(reflect.TypeOf(test.in),
+ 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)
+ }
+ expected := reflect.TypeOf(test.out)
+ if out != expected {
+ t.Errorf("expected type %v, got %v", expected, out)
+ }
+ })
+ }
+}
diff --git a/proptools/proptools.go b/proptools/proptools.go
index e6e3ae7..6881828 100644
--- a/proptools/proptools.go
+++ b/proptools/proptools.go
@@ -82,3 +82,18 @@
func String(s *string) string {
return StringDefault(s, "")
}
+
+// IntDefault takes a pointer to an int64 and returns the value pointed to by the pointer cast to int
+// if it is non-nil, or def if the pointer is nil.
+func IntDefault(i *int64, def int) int {
+ if i != nil {
+ return int(*i)
+ }
+ return def
+}
+
+// Int takes a pointer to an int64 and returns the value pointed to by the pointer cast to int
+// if it is non-nil, or 0 if the pointer is nil.
+func Int(i *int64) int {
+ return IntDefault(i, 0)
+}