Merge remote-tracking branch 'aosp/upstream' into master
* aosp/upstream:
Make proptools functions consistently take *struct types
Add isStruct and isStructPtr helpers
Fix null pointer dereference printing an expression.
Test: m checkbuild
Change-Id: I547f3cd5f286edd32efee60a7300c7eabe293e7b
diff --git a/context.go b/context.go
index 69778af..9a1e6cf 100644
--- a/context.go
+++ b/context.go
@@ -1202,8 +1202,8 @@
}
for i := range newProperties {
- dst := reflect.ValueOf(newProperties[i]).Elem()
- src := reflect.ValueOf(origModule.properties[i]).Elem()
+ dst := reflect.ValueOf(newProperties[i])
+ src := reflect.ValueOf(origModule.properties[i])
proptools.CopyProperties(dst, src)
}
diff --git a/parser/ast.go b/parser/ast.go
index b5053bb..57c4948 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -164,6 +164,7 @@
Int64Type
ListType
MapType
+ NotEvaluatedType
)
func (t Type) String() string {
@@ -178,6 +179,8 @@
return "list"
case MapType:
return "map"
+ case NotEvaluatedType:
+ return "notevaluated"
default:
panic(fmt.Errorf("Unknown type %d", t))
}
@@ -476,6 +479,29 @@
return string(buf)
}
+type NotEvaluated struct {
+ Position scanner.Position
+}
+
+func (n NotEvaluated) Copy() Expression {
+ return NotEvaluated{Position: n.Position}
+}
+
+func (n NotEvaluated) String() string {
+ return "Not Evaluated"
+}
+
+func (n NotEvaluated) Type() Type {
+ return NotEvaluatedType
+}
+
+func (n NotEvaluated) Eval() Expression {
+ return NotEvaluated{Position: n.Position}
+}
+
+func (n NotEvaluated) Pos() scanner.Position { return n.Position }
+func (n NotEvaluated) End() scanner.Position { return n.Position }
+
func endPos(pos scanner.Position, n int) scanner.Position {
pos.Offset += n
pos.Column += n
diff --git a/parser/parser.go b/parser/parser.go
index cb86246..6ae5df3 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -484,6 +484,8 @@
}
value = assignment.Value
}
+ } else {
+ value = &NotEvaluated{}
}
value = &Variable{
Name: text,
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 6377dc1..70151ad 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -1122,3 +1122,24 @@
}
}
}
+
+func TestParserNotEvaluated(t *testing.T) {
+ // When parsing without evaluation, create variables correctly
+ scope := NewScope(nil)
+ input := "FOO=abc\n"
+ _, errs := Parse("", bytes.NewBufferString(input), scope)
+ if errs != nil {
+ t.Errorf("unexpected errors:")
+ for _, err := range errs {
+ t.Errorf(" %s", err)
+ }
+ t.FailNow()
+ }
+ assignment, found := scope.Get("FOO")
+ if !found {
+ t.Fatalf("Expected to find FOO after parsing %s", input)
+ }
+ if s := assignment.String(); strings.Contains(s, "PANIC") {
+ t.Errorf("Attempt to print FOO returned %s", s)
+ }
+}
diff --git a/proptools/clone.go b/proptools/clone.go
index fe4e115..9e985f1 100644
--- a/proptools/clone.go
+++ b/proptools/clone.go
@@ -20,13 +20,32 @@
"sync"
)
+// CloneProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value
+// of a pointer to a new struct that copies of the values for its fields. It recursively clones
+// struct pointers and interfaces that contain struct pointers.
func CloneProperties(structValue reflect.Value) reflect.Value {
- result := reflect.New(structValue.Type())
- CopyProperties(result.Elem(), structValue)
+ if !isStructPtr(structValue.Type()) {
+ panic(fmt.Errorf("CloneProperties expected *struct, got %s", structValue.Type()))
+ }
+ result := reflect.New(structValue.Type().Elem())
+ copyProperties(result.Elem(), structValue.Elem())
return result
}
+// CopyProperties takes destination and source reflect.Values of a pointer to structs and returns
+// copies each field from the source into the destination. It recursively copies struct pointers
+// and interfaces that contain struct pointers.
func CopyProperties(dstValue, srcValue reflect.Value) {
+ if !isStructPtr(dstValue.Type()) {
+ panic(fmt.Errorf("CopyProperties expected dstValue *struct, got %s", dstValue.Type()))
+ }
+ if !isStructPtr(srcValue.Type()) {
+ panic(fmt.Errorf("CopyProperties expected srcValue *struct, got %s", srcValue.Type()))
+ }
+ copyProperties(dstValue.Elem(), srcValue.Elem())
+}
+
+func copyProperties(dstValue, srcValue reflect.Value) {
typ := dstValue.Type()
if srcValue.Type() != typ {
panic(fmt.Errorf("can't copy mismatching types (%s <- %s)",
@@ -47,7 +66,7 @@
case reflect.Bool, reflect.String, reflect.Int, reflect.Uint:
dstFieldValue.Set(srcFieldValue)
case reflect.Struct:
- CopyProperties(dstFieldValue, srcFieldValue)
+ copyProperties(dstFieldValue, srcFieldValue)
case reflect.Slice:
if !srcFieldValue.IsNil() {
if srcFieldValue != dstFieldValue {
@@ -67,13 +86,9 @@
srcFieldValue = srcFieldValue.Elem()
- if srcFieldValue.Kind() != reflect.Ptr {
- panic(fmt.Errorf("can't clone field %q: interface refers to a non-pointer",
- field.Name))
- }
- if srcFieldValue.Type().Elem().Kind() != reflect.Struct {
- panic(fmt.Errorf("can't clone field %q: interface points to a non-struct",
- field.Name))
+ if !isStructPtr(srcFieldValue.Type()) {
+ panic(fmt.Errorf("can't clone field %q: expected interface to contain *struct, found %s",
+ field.Name, srcFieldValue.Type()))
}
if dstFieldValue.IsNil() || dstFieldValue.Elem().Type() != srcFieldValue.Type() {
@@ -93,13 +108,11 @@
break
}
- srcFieldValue := srcFieldValue.Elem()
-
- switch srcFieldValue.Kind() {
+ switch srcFieldValue.Elem().Kind() {
case reflect.Struct:
if !dstFieldValue.IsNil() {
// Re-use the existing allocation.
- CopyProperties(dstFieldValue.Elem(), srcFieldValue)
+ copyProperties(dstFieldValue.Elem(), srcFieldValue.Elem())
break
} else {
newValue := CloneProperties(srcFieldValue)
@@ -110,21 +123,30 @@
}
}
case reflect.Bool, reflect.Int64, reflect.String:
- newValue := reflect.New(srcFieldValue.Type())
- newValue.Elem().Set(srcFieldValue)
+ newValue := reflect.New(srcFieldValue.Elem().Type())
+ newValue.Elem().Set(srcFieldValue.Elem())
origDstFieldValue.Set(newValue)
default:
- panic(fmt.Errorf("can't clone field %q: points to a %s",
- field.Name, srcFieldValue.Kind()))
+ panic(fmt.Errorf("can't clone pointer field %q type %s",
+ field.Name, srcFieldValue.Type()))
}
default:
- panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
- field.Name, srcFieldValue.Kind()))
+ panic(fmt.Errorf("unexpected type for property struct field %q: %s",
+ field.Name, srcFieldValue.Type()))
}
}
}
+// ZeroProperties takes a reflect.Value of a pointer to a struct and replaces all of its fields
+// with zero values, recursing into struct, pointer to struct and interface fields.
func ZeroProperties(structValue reflect.Value) {
+ if !isStructPtr(structValue.Type()) {
+ panic(fmt.Errorf("ZeroProperties expected *struct, got %s", structValue.Type()))
+ }
+ zeroProperties(structValue.Elem())
+}
+
+func zeroProperties(structValue reflect.Value) {
typ := structValue.Type()
for i, field := range typeFields(typ) {
@@ -146,13 +168,9 @@
// We leave the pointer intact and zero out the struct that's
// pointed to.
fieldValue = fieldValue.Elem()
- if fieldValue.Kind() != reflect.Ptr {
- panic(fmt.Errorf("can't zero field %q: interface refers to a non-pointer",
- field.Name))
- }
- if fieldValue.Type().Elem().Kind() != reflect.Struct {
- panic(fmt.Errorf("can't zero field %q: interface points to a non-struct",
- field.Name))
+ if !isStructPtr(fieldValue.Type()) {
+ panic(fmt.Errorf("can't zero field %q: expected interface to contain *struct, found %s",
+ field.Name, fieldValue.Type()))
}
fallthrough
case reflect.Ptr:
@@ -161,7 +179,7 @@
if fieldValue.IsNil() {
break
}
- ZeroProperties(fieldValue.Elem())
+ zeroProperties(fieldValue.Elem())
case reflect.Bool, reflect.Int64, reflect.String:
fieldValue.Set(reflect.Zero(fieldValue.Type()))
default:
@@ -169,7 +187,7 @@
field.Name, fieldValue.Elem().Kind()))
}
case reflect.Struct:
- ZeroProperties(fieldValue)
+ zeroProperties(fieldValue)
default:
panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
field.Name, fieldValue.Kind()))
@@ -177,9 +195,15 @@
}
}
+// CloneEmptyProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value
+// of a pointer to a new struct that has the zero values for its fields. It recursively clones
+// struct pointers and interfaces that contain struct pointers.
func CloneEmptyProperties(structValue reflect.Value) reflect.Value {
- result := reflect.New(structValue.Type())
- cloneEmptyProperties(result.Elem(), structValue)
+ if !isStructPtr(structValue.Type()) {
+ panic(fmt.Errorf("CloneEmptyProperties expected *struct, got %s", structValue.Type()))
+ }
+ result := reflect.New(structValue.Type().Elem())
+ cloneEmptyProperties(result.Elem(), structValue.Elem())
return result
}
@@ -206,13 +230,9 @@
}
srcFieldValue = srcFieldValue.Elem()
- if srcFieldValue.Kind() != reflect.Ptr {
- panic(fmt.Errorf("can't clone empty field %q: interface refers to a non-pointer",
- field.Name))
- }
- if srcFieldValue.Type().Elem().Kind() != reflect.Struct {
- panic(fmt.Errorf("can't clone empty field %q: interface points to a non-struct",
- field.Name))
+ if !isStructPtr(srcFieldValue.Type()) {
+ panic(fmt.Errorf("can't clone empty field %q: expected interface to contain *struct, found %s",
+ field.Name, srcFieldValue.Type()))
}
newValue := reflect.New(srcFieldValue.Type()).Elem()
@@ -226,7 +246,7 @@
if srcFieldValue.IsNil() {
break
}
- newValue := CloneEmptyProperties(srcFieldValue.Elem())
+ newValue := CloneEmptyProperties(srcFieldValue)
if dstFieldInterfaceValue.IsValid() {
dstFieldInterfaceValue.Set(newValue)
} else {
diff --git a/proptools/clone_test.go b/proptools/clone_test.go
index 660f1c0..3c03451 100644
--- a/proptools/clone_test.go
+++ b/proptools/clone_test.go
@@ -277,7 +277,7 @@
for _, testCase := range clonePropertiesTestCases {
testString := fmt.Sprintf("%s", testCase.in)
- got := CloneProperties(reflect.ValueOf(testCase.in).Elem()).Interface()
+ got := CloneProperties(reflect.ValueOf(testCase.in)).Interface()
if !reflect.DeepEqual(testCase.out, got) {
t.Errorf("test case %s", testString)
@@ -499,7 +499,7 @@
for _, testCase := range cloneEmptyPropertiesTestCases {
testString := fmt.Sprintf("%#v", testCase.in)
- got := CloneEmptyProperties(reflect.ValueOf(testCase.in).Elem()).Interface()
+ got := CloneEmptyProperties(reflect.ValueOf(testCase.in)).Interface()
if !reflect.DeepEqual(testCase.out, got) {
t.Errorf("test case %s", testString)
@@ -514,8 +514,8 @@
for _, testCase := range cloneEmptyPropertiesTestCases {
testString := fmt.Sprintf("%#v", testCase.in)
- got := CloneProperties(reflect.ValueOf(testCase.in).Elem()).Interface()
- ZeroProperties(reflect.ValueOf(got).Elem())
+ got := CloneProperties(reflect.ValueOf(testCase.in)).Interface()
+ ZeroProperties(reflect.ValueOf(got))
if !reflect.DeepEqual(testCase.out, got) {
t.Errorf("test case %s", testString)
diff --git a/proptools/extend.go b/proptools/extend.go
index b0c7459..d3c2b79 100644
--- a/proptools/extend.go
+++ b/proptools/extend.go
@@ -274,7 +274,7 @@
}
// Step into source pointers to structs
- if srcFieldValue.Kind() == reflect.Ptr && srcFieldValue.Type().Elem().Kind() == reflect.Struct {
+ if isStructPtr(srcFieldValue.Type()) {
if srcFieldValue.IsNil() {
continue
}
@@ -323,7 +323,7 @@
}
// Step into destination pointers to structs
- if dstFieldValue.Kind() == reflect.Ptr && dstFieldValue.Type().Elem().Kind() == reflect.Struct {
+ if isStructPtr(dstFieldValue.Type()) {
if dstFieldValue.IsNil() {
dstFieldValue = reflect.New(dstFieldValue.Type().Elem())
origDstFieldValue.Set(dstFieldValue)
@@ -501,11 +501,8 @@
func getStruct(in interface{}) (reflect.Value, error) {
value := reflect.ValueOf(in)
- if value.Kind() != reflect.Ptr {
- return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %T", in)
- }
- if value.Type().Elem().Kind() != reflect.Struct {
- return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %T", in)
+ if !isStructPtr(value.Type()) {
+ return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %s", value.Type())
}
if value.IsNil() {
return reflect.Value{}, getStructEmptyError{}
diff --git a/proptools/filter.go b/proptools/filter.go
index 59eca5a..e6b3336 100644
--- a/proptools/filter.go
+++ b/proptools/filter.go
@@ -50,8 +50,7 @@
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 {
+ if isStruct(field.Type) || isStructPtr(field.Type) {
// 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",
@@ -82,12 +81,12 @@
}
ptrToStruct := false
- if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
+ if isStructPtr(field.Type) {
ptrToStruct = true
}
// Recurse into structs
- if ptrToStruct || field.Type.Kind() == reflect.Struct {
+ if ptrToStruct || isStruct(field.Type) {
subMaxTypeNameSize := maxTypeNameSize
if maxTypeNameSize > 0 {
// In the worst case where only this nested struct will fit in the outer struct, the
diff --git a/proptools/proptools.go b/proptools/proptools.go
index 6881828..b91c92a 100644
--- a/proptools/proptools.go
+++ b/proptools/proptools.go
@@ -15,6 +15,7 @@
package proptools
import (
+ "reflect"
"unicode"
"unicode/utf8"
)
@@ -97,3 +98,11 @@
func Int(i *int64) int {
return IntDefault(i, 0)
}
+
+func isStruct(t reflect.Type) bool {
+ return t.Kind() == reflect.Struct
+}
+
+func isStructPtr(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
diff --git a/proptools/tag.go b/proptools/tag.go
index af5b97e..d69853a 100644
--- a/proptools/tag.go
+++ b/proptools/tag.go
@@ -36,7 +36,7 @@
// are tagged with the given key and value, including ones found in embedded structs or pointers to structs.
func PropertyIndexesWithTag(ps interface{}, key, value string) [][]int {
t := reflect.TypeOf(ps)
- if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
+ if !isStructPtr(t) {
panic(fmt.Errorf("type %s is not a pointer to a struct", t))
}
t = t.Elem()
@@ -49,7 +49,7 @@
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
ft := field.Type
- if ft.Kind() == reflect.Struct || (ft.Kind() == reflect.Ptr && ft.Elem().Kind() == reflect.Struct) {
+ if isStruct(ft) || isStructPtr(ft) {
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
diff --git a/proptools/unpack.go b/proptools/unpack.go
index e7d4fff..344327f 100644
--- a/proptools/unpack.go
+++ b/proptools/unpack.go
@@ -49,14 +49,11 @@
for _, properties := range propertiesStructs {
propertiesValue := reflect.ValueOf(properties)
- if propertiesValue.Kind() != reflect.Ptr {
- panic("properties must be a pointer to a struct")
+ if !isStructPtr(propertiesValue.Type()) {
+ panic(fmt.Errorf("properties must be *struct, got %s",
+ propertiesValue.Type()))
}
-
propertiesValue = propertiesValue.Elem()
- if propertiesValue.Kind() != reflect.Struct {
- panic("properties must be a pointer to a struct")
- }
newErrs := unpackStructValue("", propertiesValue, propertyMap)
errs = append(errs, newErrs...)
@@ -212,7 +209,7 @@
panic(fmt.Errorf("unsupported kind for field %s: %s", propertyName, kind))
}
- if field.Anonymous && fieldValue.Kind() == reflect.Struct {
+ if field.Anonymous && isStruct(fieldValue.Type()) {
newErrs := unpackStructValue(namePrefix, fieldValue, propertyMap)
errs = append(errs, newErrs...)
continue
@@ -239,7 +236,7 @@
var newErrs []error
- if fieldValue.Kind() == reflect.Struct {
+ if isStruct(fieldValue.Type()) {
newErrs = unpackStruct(propertyName+".", fieldValue,
packedProperty.property, propertyMap)
diff --git a/proptools/unpack_test.go b/proptools/unpack_test.go
index 931cfdd..234b4e7 100644
--- a/proptools/unpack_test.go
+++ b/proptools/unpack_test.go
@@ -37,7 +37,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
S *string
Blank *string
Unset *string
@@ -56,7 +56,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
S string
}{
S: "abc",
@@ -71,7 +71,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
IsGood bool
}{
IsGood: true,
@@ -87,7 +87,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
IsGood *bool
IsBad *bool
IsUgly *bool
@@ -108,7 +108,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Stuff []string
Empty []string
Nil []string
@@ -131,7 +131,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested struct {
S string
}
@@ -152,7 +152,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested interface{}
}{
Nested: &struct{ S string }{
@@ -173,7 +173,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested struct {
Foo string
}
@@ -200,7 +200,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested struct {
Foo string `allowNested:"true"`
} `blueprint:"filter(allowNested:\"true\")"`
@@ -229,7 +229,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
EmbeddedStruct
Nested struct {
EmbeddedStruct
@@ -260,7 +260,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
EmbeddedInterface
Nested struct {
EmbeddedInterface
@@ -291,7 +291,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
S string
EmbeddedStruct
Nested struct {
@@ -327,7 +327,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
S string
EmbeddedInterface
Nested struct {
@@ -365,7 +365,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
S string
List []string
List2 []string
@@ -387,7 +387,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested struct {
S string
}
@@ -396,7 +396,7 @@
S: "abc",
},
},
- struct {
+ &struct {
Nested struct {
S string
}
@@ -405,7 +405,7 @@
S: "abc",
},
},
- struct {
+ &struct {
}{},
},
},
@@ -420,7 +420,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested *struct {
S string
}
@@ -449,7 +449,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
Nested interface{}
}{
Nested: &EmbeddedStruct{
@@ -478,7 +478,7 @@
}
`,
output: []interface{}{
- struct {
+ &struct {
String string
String_ptr *string
Bool bool
@@ -558,7 +558,7 @@
}
for i := range output {
- got := reflect.ValueOf(output[i]).Elem().Interface()
+ got := reflect.ValueOf(output[i]).Interface()
if !reflect.DeepEqual(got, testCase.output[i]) {
t.Errorf("test case: %s", testCase.input)
t.Errorf("incorrect output:")