[Dart][FIDL][JSON] Support for Tables and XUnions.

This changelist adds support for the Table and XUnion FIDL types in
the Dart FIDL JSON generation.

Fixes: SU-181
Test: fx run-host-tests dart_fidl_json_test
Change-Id: Ia8c7afe0d2ad547033903ee1159bfe0979627e0c
diff --git a/bin/dart_fidl_json/json.fidlmerge b/bin/dart_fidl_json/json.fidlmerge
index f478abc..93319a5 100644
--- a/bin/dart_fidl_json/json.fidlmerge
+++ b/bin/dart_fidl_json/json.fidlmerge
@@ -194,6 +194,47 @@
 {{ end }}
 {{- end }}
 
+{{- define "TableConverterDecls" }}
+  {{- range .Tables }}
+{{- if eq .MaxHandles 0 }}
+{{ $type := execTmpl "TypeTemplate" .Name }}
+{{ $className := (execTmpl "CvtClassTemplate" .Name) }}
+class {{ $className}} {
+  static Object toJson(Object tableVal) {
+    if (tableVal is {{ $type }}) {
+      return {
+      {{- range .Members }}
+        {{- if not .Reserved }}
+        {{- $member_name := toLowerCamelCase .Name }}
+        {{- $value_expr := printf "tableVal.%s" $member_name }}
+        '{{ .Name }}': {{ printf (execTmpl "EncodeMember" .) $value_expr }},
+        {{- end }}
+      {{- end }}
+      };
+    }
+    return null;
+  }
+
+  static {{ $type }} fromJson(Map<String, dynamic> json) {
+    if (json == null) {
+      return null;
+    }
+
+    return {{ $type }}(
+{{- range .Members }}
+  {{- if eq .Reserved false }}
+  {{- $jsonVal := printf "json['%s']" .Name }}
+      {{ toLowerCamelCase .Name}}: {{ $jsonVal }} != null ? {{ printf (execTmpl "DecodeMember" .) $jsonVal }} : null,
+  {{- end }}
+{{- end }}
+    );
+  }
+}
+
+{{- end }}
+  {{- end }}
+{{- end }}
+
 {{- define "StructConverterDecls" }}
   {{- range .Structs }}
 {{- if eq .MaxHandles 0 }}
@@ -232,7 +273,7 @@
 {{- end }}
 
 {{- define "UnionConverterDecls" -}}
-  {{- range .Unions -}}
+  {{- range . -}}
 {{- if eq .MaxHandles 0 }}
 {{- $type := execTmpl "TypeTemplate" .Name }}
 {{- $typeKey := "type" }}
@@ -282,8 +323,10 @@
   {{- template "FileHeader" . }}
   {{- template "FidlInclude" . }}
   {{- template "EnumConverterDecls" . }}
-  {{- template "UnionConverterDecls" . }}
+  {{- template "UnionConverterDecls" .Unions }}
+  {{- template "UnionConverterDecls" .XUnions }}
   {{- template "StructConverterDecls" . }}
+  {{- template "TableConverterDecls" . }}
 {{- end }}
 
 {{- define "Main" }}
diff --git a/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/BUILD.gn b/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/BUILD.gn
index 2e77c59..d861e6e 100644
--- a/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/BUILD.gn
+++ b/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/BUILD.gn
@@ -7,6 +7,6 @@
 fidl("dart.fidl.json.test") {
   name = "test.dart.fidl.json"
   sources = [
-    "types.fidl",
+    "types.test.fidl",
   ]
 }
diff --git a/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.fidl b/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.test.fidl
similarity index 67%
rename from bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.fidl
rename to bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.test.fidl
index 86f6f3c..c428b05 100644
--- a/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.fidl
+++ b/bin/dart_fidl_json/test/fidl/dart.fidl.json.test/types.test.fidl
@@ -17,8 +17,20 @@
   ExampleStruct2 struct2;
 };
 
+xunion ExampleXUnion {
+  ExampleStruct struct1;
+  ExampleStruct2 struct2;
+  int32 bar;
+};
+
 enum ExampleEnum {
   val1 = 1;
   val2 = 2;
   val3 = 3;
 };
+
+table ExampleTable {
+  1: string foo;
+  2: reserved;
+  3: int32 bar;
+};
diff --git a/bin/dart_fidl_json/test/test/json_test.dart b/bin/dart_fidl_json/test/test/json_test.dart
index 77bdc9c..ab35544 100644
--- a/bin/dart_fidl_json/test/test/json_test.dart
+++ b/bin/dart_fidl_json/test/test/json_test.dart
@@ -55,4 +55,25 @@
         ExampleUnionConverter.fromJson(jsonDecode(
             jsonEncode(ExampleUnionConverter.toJson(exampleUnion)))));
   });
+
+  // Ensure xunion encode/decode.
+  test('test_xunion', () async {
+    const ExampleXUnion exampleUnion = ExampleXUnion.withStruct1(ExampleStruct(
+        bar: 1, foo: 'test', structs: null, vals: null, integers: null));
+
+    expect(
+        exampleUnion,
+        ExampleXUnionConverter.fromJson(jsonDecode(
+            jsonEncode(ExampleXUnionConverter.toJson(exampleUnion)))));
+  });
+
+  // Ensure table encode/decode.
+  test('test_table', () async {
+    const ExampleTable exampleTable = ExampleTable(bar: 1, foo: 'test');
+
+    expect(
+        exampleTable,
+        ExampleTableConverter.fromJson(jsonDecode(
+            jsonEncode(ExampleTableConverter.toJson(exampleTable)))));
+  });
 }