Merge remote-tracking branch 'aosp/upstream' into master
am: d2a5c6d5f2
Change-Id: I74697189fa6527abe3f39bd85dffcfaaf1a11289
This commit is contained in:
commit
19ebe0019c
6 changed files with 421 additions and 2 deletions
|
@ -79,6 +79,7 @@ bootstrap_go_package {
|
||||||
"proptools/clone.go",
|
"proptools/clone.go",
|
||||||
"proptools/escape.go",
|
"proptools/escape.go",
|
||||||
"proptools/extend.go",
|
"proptools/extend.go",
|
||||||
|
"proptools/filter.go",
|
||||||
"proptools/proptools.go",
|
"proptools/proptools.go",
|
||||||
"proptools/tag.go",
|
"proptools/tag.go",
|
||||||
"proptools/typeequal.go",
|
"proptools/typeequal.go",
|
||||||
|
@ -87,6 +88,7 @@ bootstrap_go_package {
|
||||||
"proptools/clone_test.go",
|
"proptools/clone_test.go",
|
||||||
"proptools/escape_test.go",
|
"proptools/escape_test.go",
|
||||||
"proptools/extend_test.go",
|
"proptools/extend_test.go",
|
||||||
|
"proptools/filter_test.go",
|
||||||
"proptools/tag_test.go",
|
"proptools/tag_test.go",
|
||||||
"proptools/typeequal_test.go",
|
"proptools/typeequal_test.go",
|
||||||
],
|
],
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1 +1,3 @@
|
||||||
module github.com/google/blueprint
|
module github.com/google/blueprint
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
|
@ -712,7 +712,7 @@ type TopDownMutatorContext interface {
|
||||||
|
|
||||||
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
|
// 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.
|
// 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 {
|
type BottomUpMutatorContext interface {
|
||||||
|
@ -934,7 +934,7 @@ func (mctx *mutatorContext) Rename(name string) {
|
||||||
mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
|
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 := mctx.context.newModule(factory)
|
||||||
|
|
||||||
module.relBlueprintsFile = mctx.module.relBlueprintsFile
|
module.relBlueprintsFile = mctx.module.relBlueprintsFile
|
||||||
|
@ -950,6 +950,8 @@ func (mctx *mutatorContext) CreateModule(factory ModuleFactory, props ...interfa
|
||||||
}
|
}
|
||||||
|
|
||||||
mctx.newModules = append(mctx.newModules, module)
|
mctx.newModules = append(mctx.newModules, module)
|
||||||
|
|
||||||
|
return module.logicModule
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property
|
// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property
|
||||||
|
|
159
proptools/filter.go
Normal file
159
proptools/filter.go
Normal file
|
@ -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
|
||||||
|
}
|
239
proptools/filter_test.go
Normal file
239
proptools/filter_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,3 +82,18 @@ func StringDefault(s *string, def string) string {
|
||||||
func String(s *string) string {
|
func String(s *string) string {
|
||||||
return StringDefault(s, "")
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue