Merge "Add values to json module graph"
diff --git a/android/module.go b/android/module.go
index eb9d3d7..e190e8b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1470,8 +1470,10 @@
 }
 
 type propInfo struct {
-	Name string
-	Type string
+	Name   string
+	Type   string
+	Value  string
+	Values []string
 }
 
 func (m *ModuleBase) propertiesWithValues() []propInfo {
@@ -1511,18 +1513,60 @@
 				return
 			}
 			elKind := v.Type().Elem().Kind()
-			info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+			info = append(info, propInfo{Name: name, Type: elKind.String() + " " + kind.String(), Values: sliceReflectionValue(v)})
 		default:
-			info = append(info, propInfo{name, kind.String()})
+			info = append(info, propInfo{Name: name, Type: kind.String(), Value: reflectionValue(v)})
 		}
 	}
 
 	for _, p := range props {
 		propsWithValues("", reflect.ValueOf(p).Elem())
 	}
+	sort.Slice(info, func(i, j int) bool {
+		return info[i].Name < info[j].Name
+	})
 	return info
 }
 
+func reflectionValue(value reflect.Value) string {
+	switch value.Kind() {
+	case reflect.Bool:
+		return fmt.Sprintf("%t", value.Bool())
+	case reflect.Int64:
+		return fmt.Sprintf("%d", value.Int())
+	case reflect.String:
+		return fmt.Sprintf("%s", value.String())
+	case reflect.Struct:
+		if value.IsZero() {
+			return "{}"
+		}
+		length := value.NumField()
+		vals := make([]string, length, length)
+		for i := 0; i < length; i++ {
+			sTyp := value.Type().Field(i)
+			if proptools.ShouldSkipProperty(sTyp) {
+				continue
+			}
+			name := sTyp.Name
+			vals[i] = fmt.Sprintf("%s: %s", name, reflectionValue(value.Field(i)))
+		}
+		return fmt.Sprintf("%s{%s}", value.Type(), strings.Join(vals, ", "))
+	case reflect.Array, reflect.Slice:
+		vals := sliceReflectionValue(value)
+		return fmt.Sprintf("[%s]", strings.Join(vals, ", "))
+	}
+	return ""
+}
+
+func sliceReflectionValue(value reflect.Value) []string {
+	length := value.Len()
+	vals := make([]string, length, length)
+	for i := 0; i < length; i++ {
+		vals[i] = reflectionValue(value.Index(i))
+	}
+	return vals
+}
+
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
 
 func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
diff --git a/android/module_test.go b/android/module_test.go
index 1dcddf7..77ef146 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -563,6 +563,12 @@
 	Embedded_prop *string
 }
 
+type StructInSlice struct {
+	G string
+	H bool
+	I []string
+}
+
 type propsTestModule struct {
 	ModuleBase
 	DefaultableModuleBase
@@ -579,6 +585,8 @@
 			E *string
 		}
 		F *string `blueprint:"mutated"`
+
+		Slice_of_struct []StructInSlice
 	}
 }
 
@@ -621,7 +629,7 @@
 		}
 	`,
 			expectedProps: []propInfo{
-				propInfo{"Name", "string"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
 			},
 		},
 		{
@@ -634,10 +642,10 @@
 		}
 	`,
 			expectedProps: []propInfo{
-				propInfo{"A", "string"},
-				propInfo{"B", "bool"},
-				propInfo{"D", "int64"},
-				propInfo{"Name", "string"},
+				propInfo{Name: "A", Type: "string", Value: "abc"},
+				propInfo{Name: "B", Type: "bool", Value: "true"},
+				propInfo{Name: "D", Type: "int64", Value: "123"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
 			},
 		},
 		{
@@ -650,10 +658,10 @@
 	`,
 			expectedProps: []propInfo{
 				// for non-pointer cannot distinguish between unused and intentionally set to empty
-				propInfo{"A", "string"},
-				propInfo{"B", "bool"},
-				propInfo{"D", "int64"},
-				propInfo{"Name", "string"},
+				propInfo{Name: "A", Type: "string", Value: ""},
+				propInfo{Name: "B", Type: "bool", Value: "true"},
+				propInfo{Name: "D", Type: "int64", Value: "123"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
 			},
 		},
 		{
@@ -666,8 +674,8 @@
 		}
 	`,
 			expectedProps: []propInfo{
-				propInfo{"Nested.E", "string"},
-				propInfo{"Name", "string"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
+				propInfo{Name: "Nested.E", Type: "string", Value: "abc"},
 			},
 		},
 		{
@@ -682,8 +690,8 @@
 		}
 	`,
 			expectedProps: []propInfo{
-				propInfo{"Name", "string"},
-				propInfo{"Arch.X86_64.A", "string"},
+				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
 			},
 		},
 		{
@@ -694,8 +702,34 @@
 		}
 	`,
 			expectedProps: []propInfo{
-				propInfo{"Embedded_prop", "string"},
-				propInfo{"Name", "string"},
+				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
+			},
+		},
+		{
+			desc: "struct slice",
+			bp: `test {
+			name: "foo",
+			slice_of_struct: [
+				{
+					g: "abc",
+					h: false,
+					i: ["baz"],
+				},
+				{
+					g: "def",
+					h: true,
+					i: [],
+				},
+			]
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
+				propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{
+					`android.StructInSlice{G: abc, H: false, I: [baz]}`,
+					`android.StructInSlice{G: def, H: true, I: []}`,
+				}},
 			},
 		},
 		{
@@ -705,19 +739,20 @@
 	name: "foo_defaults",
 	a: "a",
 	b: true,
+	c: ["default_c"],
 	embedded_prop:"a",
 	arch: {
 		x86_64: {
-			a: "a",
+			a: "x86_64 a",
 		},
 	},
 }
 test {
 	name: "foo",
 	defaults: ["foo_defaults"],
-	c: ["a"],
+	c: ["c"],
 	nested: {
-		e: "d",
+		e: "nested e",
 	},
 	target: {
 		linux: {
@@ -727,15 +762,15 @@
 }
 	`,
 			expectedProps: []propInfo{
-				propInfo{"A", "string"},
-				propInfo{"B", "bool"},
-				propInfo{"C", "string slice"},
-				propInfo{"Embedded_prop", "string"},
-				propInfo{"Nested.E", "string"},
-				propInfo{"Name", "string"},
-				propInfo{"Arch.X86_64.A", "string"},
-				propInfo{"Target.Linux.A", "string"},
-				propInfo{"Defaults", "string slice"},
+				propInfo{Name: "A", Type: "string", Value: "a"},
+				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
+				propInfo{Name: "B", Type: "bool", Value: "true"},
+				propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
+				propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
+				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+				propInfo{Name: "Name", Type: "string", Value: "foo"},
+				propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
+				propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"},
 			},
 		},
 	}