v23+mojo: vdl -> mojo type conversion + tests for vdl <-> mojo type conv

This branch depends on the struct offset fixes going in.

Change-Id: Iae6d7bb42a5c890600519b9264151db3f9e1377c
diff --git a/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go b/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
index a0bc9bb..feaf017 100644
--- a/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
+++ b/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
@@ -17,6 +17,8 @@
 // the desired value.  The datatype describes the type of the encoded data.
 // Returns an error if the data cannot be decoded into valptr, based on the VDL
 // value conversion rules.
+// TODO(bprosnitz) Consider reimplementing this using mojom_type instead of vdl type
+// so that we can take advantage of the struct offset in the mojom type.
 func MojomToVdl(data []byte, datatype *vdl.Type, valptr interface{}) error {
 	target, err := vdl.ReflectTarget(reflect.ValueOf(valptr))
 	if err != nil {
diff --git a/go/src/v.io/x/mojo/transcoder/transcoder_test.go b/go/src/v.io/x/mojo/transcoder/transcoder_test.go
index 0c634e3..ee262ac 100644
--- a/go/src/v.io/x/mojo/transcoder/transcoder_test.go
+++ b/go/src/v.io/x/mojo/transcoder/transcoder_test.go
@@ -59,6 +59,28 @@
 	}
 }
 
+func TestVomToMojoToVom(t *testing.T) {
+	for _, test := range testCases {
+		testName := test.Name + " vom->mojo->vom"
+
+		data, err := transcoder.VdlToMojom(test.VdlValue)
+		if err != nil {
+			t.Errorf("%s: error in VomToMojo: %v", testName, err)
+			continue
+		}
+
+		var out interface{}
+		if err := transcoder.MojomToVdl(data, vdl.TypeOf(test.VdlValue), &out); err != nil {
+			t.Errorf("%s: error in MojoToVom: %v (was transcoding from %x)", testName, err, data)
+			continue
+		}
+
+		if got, want := out, test.VdlValue; !reflect.DeepEqual(got, want) {
+			t.Errorf("%s: result doesn't match expectation. got %#v, but want %#v", testName, got, want)
+		}
+	}
+}
+
 func mojoEncode(mojoValue interface{}) ([]byte, error) {
 	payload, ok := mojoValue.(encodable)
 	if !ok {
diff --git a/go/src/v.io/x/mojo/transcoder/type.go b/go/src/v.io/x/mojo/transcoder/type.go
index 1b9a624..128b75e 100644
--- a/go/src/v.io/x/mojo/transcoder/type.go
+++ b/go/src/v.io/x/mojo/transcoder/type.go
@@ -93,11 +93,11 @@
 		case mojom_types.SimpleType_Bool:
 			vt = vdl.BoolType
 		case mojom_types.SimpleType_Double:
-			vt = vdl.BoolType
+			vt = vdl.Float64Type
 		case mojom_types.SimpleType_Float:
-			vt = vdl.BoolType
+			vt = vdl.Float32Type
 		case mojom_types.SimpleType_InT8:
-			panic("int8 doesn't exist in vdl")
+			vt = vdl.Int8Type
 		case mojom_types.SimpleType_InT16:
 			vt = vdl.Int16Type
 		case mojom_types.SimpleType_InT32:
@@ -155,111 +155,164 @@
 	return vt
 }
 
-/*func V2M(vt *vdl.Type, v2M map[string]string) mojom_types.Type {
-	if m, ok := v2M[vt.String()]; ok {
-		return mojom_types.TypeTypeReference{
-			Value: mojom_types.TypeReference{
-				Nullable:   nullable,
-				Identifier: m,
-				TypeKey:    m,
-			},
-		}
-	}
-	panic("vdl type %#v was not present in the mapping", vt)
+func VDLToMojomType(t *vdl.Type) (mojomtype mojom_types.Type, mp map[string]mojom_types.UserDefinedType) {
+	mp = map[string]mojom_types.UserDefinedType{}
+	mojomtype = vdlToMojomTypeInternal(t, false, mp)
+	return
 }
 
-// From the vdltype and the reverse mapping of the descriptor (hashcons vdltype string => typekey),
-// produce the corresponding mojom Type.
-func VDLToMojomType(vt *vdl.Type, v2M map[string]string) mojom_types.Type {
-	return vdlToMojomTypeImpl(vt, v2M, false)
-}
-
-func vdlToMojomTypeImpl(vt *vdl.Type, v2M map[string]string, bool nullable) mojom_types.Type {
-	if m, ok := v2M[vt.String()]; ok {
-		return mojom_types.TypeTypeReference{
-			Value: mojom_types.TypeReference{
-				Nullable:   nullable,
-				Identifier: m,
-				TypeKey:    m,
-			},
+func vdlToMojomTypeInternal(t *vdl.Type, nullable bool, mp map[string]mojom_types.UserDefinedType) (mojomtype mojom_types.Type) {
+	switch t.Kind() {
+	case vdl.Bool, vdl.Float64, vdl.Float32, vdl.Int8, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64:
+		return &mojom_types.TypeSimpleType{
+			simpleTypeCode(t.Kind()),
 		}
-	}
-
-	fmt.Println("Missed the vdl to mojom map")
-	// In the unlikely case where v2M was insufficient, we have the remaining logic.
-
-	switch vt.Kind() {
-	case vdl.Bool:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Bool}
-	case vdl.Byte:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT8}
-	case vdl.Uint16:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT16}
-	case vdl.Uint32:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT32}
-	case vdl.Uint64:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT64}
-	case vdl.Int16:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT16}
-	case vdl.Int32:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT32}
-	case vdl.Int64:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT64}
-	case vdl.Float32:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Float}
-	case vdl.Float64:
-		return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Double}
-	case vdl.Complex64:
-		panic("complex float doesn't exist in mojom")
-	case vdl.Complex128:
-		panic("complex double doesn't exist in mojom")
 	case vdl.String:
-		return mojom_types.TypeStringType{Value: mojom_types.StringType{}}
+		return &mojom_types.TypeStringType{
+			stringType(nullable),
+		}
 	case vdl.Array:
-		elemType := VDLToMojomType(vt.Elem(), v2M)
-		return mojom_types.TypeArrayType{
-			Value: mojom_types.ArrayType{
-				FixedLength: int64(vt.Len()),
-				ElementType: elemType,
-			},
+		elem := vdlToMojomTypeInternal(t.Elem(), false, mp)
+		return &mojom_types.TypeArrayType{
+			arrayType(elem, nullable, t.Len()),
 		}
 	case vdl.List:
-		elemType := VDLToMojomType(vt.Elem(), v2M)
-		return mojom_types.TypeArrayType{
-			Value: mojom_types.ArrayType{
-				FixedLength: -1,
-				ElementType: elemType,
-			},
+		elem := vdlToMojomTypeInternal(t.Elem(), false, mp)
+		return &mojom_types.TypeArrayType{
+			listType(elem, nullable),
 		}
-	case vdl.Set:
-		panic("set doesn't exist in mojom")
 	case vdl.Map:
-		keyType := VDLToMojomType(vt.Key(), v2M)
-		elemType := VDLToMojomType(vt.Elem(), v2M)
-		return mojom_types.TypeMapType{
-			Value: mojom_types.MapType{
-				KeyType:   &keyType,
-				ValueType: &elemType,
-			},
+		key := vdlToMojomTypeInternal(t.Key(), false, mp)
+		elem := vdlToMojomTypeInternal(t.Elem(), false, mp)
+		return &mojom_types.TypeMapType{
+			mapType(key, elem, nullable),
 		}
 	case vdl.Struct, vdl.Union, vdl.Enum:
-		mt := mojom_types.TypeTypeReference{
-			Value: mojom_types.TypeReference{
-				Nullable:   nullable,
-				Identifier: v2M[vt.String()],
-				TypeKey:    v2M[vt.String()],
+		udtKey := userDefinedTypeKey(t, mp)
+		return &mojom_types.TypeTypeReference{
+			mojom_types.TypeReference{
+				Nullable: nullable,
+				TypeKey:  &udtKey,
 			},
 		}
-		return mt
-	case vdl.TypeObject:
-		panic("typeobject doesn't exist in mojom")
-	case vdl.Any:
-		panic("any doesn't exist in mojom")
 	case vdl.Optional:
-		// TODO(alexfandrianto): Unfortunately, without changing vdl, we can only
-		// manage optional (named) structs. This doesn't Nullify anything else.
-		return vdlToMojomTypeImpl(vt.Elem(), v2M, true)
+		return vdlToMojomTypeInternal(t.Elem(), true, mp)
+	default:
+		panic(fmt.Sprintf("conversion from VDL kind %v to mojom type not implemented", t.Kind()))
 	}
-	panic(fmt.Errorf("%v can't be converted to MojomType", vt))
 }
-*/
+
+func userDefinedTypeKey(t *vdl.Type, mp map[string]mojom_types.UserDefinedType) string {
+	key := t.String()
+	if _, ok := mp[key]; ok {
+		return key
+	}
+	mp[key] = nil // placeholder to stop recursion
+
+	var udt mojom_types.UserDefinedType
+	switch t.Kind() {
+	case vdl.Struct:
+		udt = structType(t, mp)
+	case vdl.Union:
+		udt = unionType(t, mp)
+	case vdl.Enum:
+		udt = enumType(t)
+	default:
+		panic(fmt.Sprintf("conversion from VDL kind %v to mojom user defined type not implemented", t.Kind()))
+	}
+
+	mp[key] = udt
+	return key
+}
+
+func simpleTypeCode(k vdl.Kind) mojom_types.SimpleType {
+	switch k {
+	case vdl.Bool:
+		return mojom_types.SimpleType_Bool
+	case vdl.Float64:
+		return mojom_types.SimpleType_Double
+	case vdl.Float32:
+		return mojom_types.SimpleType_Float
+	case vdl.Int8:
+		return mojom_types.SimpleType_InT8
+	case vdl.Int16:
+		return mojom_types.SimpleType_InT16
+	case vdl.Int32:
+		return mojom_types.SimpleType_InT32
+	case vdl.Int64:
+		return mojom_types.SimpleType_InT64
+	case vdl.Byte:
+		return mojom_types.SimpleType_UinT8
+	case vdl.Uint16:
+		return mojom_types.SimpleType_UinT16
+	case vdl.Uint32:
+		return mojom_types.SimpleType_UinT32
+	case vdl.Uint64:
+		return mojom_types.SimpleType_UinT64
+	default:
+		panic(fmt.Sprintf("kind %v does not represent a simple type", k))
+	}
+}
+
+func stringType(nullable bool) mojom_types.StringType {
+	return mojom_types.StringType{nullable}
+}
+
+func arrayType(elem mojom_types.Type, nullable bool, length int) mojom_types.ArrayType {
+	return mojom_types.ArrayType{nullable, int32(length), elem}
+}
+
+func listType(elem mojom_types.Type, nullable bool) mojom_types.ArrayType {
+	return mojom_types.ArrayType{nullable, -1, elem}
+}
+
+func mapType(key, value mojom_types.Type, nullable bool) mojom_types.MapType {
+	return mojom_types.MapType{nullable, key, value}
+}
+
+func structType(t *vdl.Type, mp map[string]mojom_types.UserDefinedType) mojom_types.UserDefinedType {
+	layout := computeStructLayout(t)
+	structFields := make([]mojom_types.StructField, t.NumField())
+	for i := 0; i < t.NumField(); i++ {
+		byteOffset, _ := layout.MojoOffsetsFromVdlIndex(i)
+		structFields[i] = mojom_types.StructField{
+			Type:   vdlToMojomTypeInternal(t.Field(i).Type, false, mp),
+			Offset: int32(byteOffset),
+		}
+	}
+	return &mojom_types.UserDefinedTypeStructType{
+		mojom_types.MojomStruct{
+			Fields: structFields,
+		},
+	}
+}
+
+func unionType(t *vdl.Type, mp map[string]mojom_types.UserDefinedType) mojom_types.UserDefinedType {
+	unionFields := make([]mojom_types.UnionField, t.NumField())
+	for i := 0; i < t.NumField(); i++ {
+		unionFields[i] = mojom_types.UnionField{
+			Type: vdlToMojomTypeInternal(t.Field(i).Type, false, mp),
+			Tag:  uint32(i),
+		}
+	}
+	return &mojom_types.UserDefinedTypeUnionType{
+		mojom_types.MojomUnion{
+			Fields: unionFields,
+		},
+	}
+}
+
+func enumType(t *vdl.Type) mojom_types.UserDefinedType {
+	enumValues := make([]mojom_types.EnumValue, t.NumEnumLabel())
+	for i := 0; i < t.NumEnumLabel(); i++ {
+		enumValues[i] = mojom_types.EnumValue{
+			EnumTypeKey: t.EnumLabel(i),
+			IntValue:    int32(i),
+		}
+	}
+	return &mojom_types.UserDefinedTypeEnumType{
+		mojom_types.MojomEnum{
+			Values: enumValues,
+		},
+	}
+}
diff --git a/go/src/v.io/x/mojo/transcoder/type_test.go b/go/src/v.io/x/mojo/transcoder/type_test.go
new file mode 100644
index 0000000..56b3e0d
--- /dev/null
+++ b/go/src/v.io/x/mojo/transcoder/type_test.go
@@ -0,0 +1,164 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package transcoder_test
+
+import (
+	"mojo/public/interfaces/bindings/mojom_types"
+	"reflect"
+	"testing"
+
+	"v.io/v23/vdl"
+	"v.io/x/mojo/transcoder"
+)
+
+func TestVdlAndMojoTypeConversion(t *testing.T) {
+	// Create types.
+	/* Types are commented out pending the relevant tests being added.
+	enumType := vdl.NamedType("TestEnum", vdl.EnumType("A", "B", "C"))
+
+	basicStructType := vdl.NamedType("TestBasicStruct", vdl.StructType(vdl.Field{"TestEnum", enumType}, vdl.Field{"A", vdl.Int32Type}))
+
+	builder := vdl.TypeBuilder{}
+	strct := builder.Struct()
+	strct.AppendField("TestEnum", enumType)
+	namedStruct := builder.Named("TestStruct").AssignBase(strct)
+	strct.AppendField("TestStruct", builder.Optional().AssignElem(namedStruct))
+	strct.AppendField("A", vdl.Int32Type)
+	builder.Build()
+	cyclicStructType, err := namedStruct.Built()
+	if err != nil {
+		t.Fatalf("error building struct: %v", err)
+	}*/
+
+	tests := []struct {
+		vdl   *vdl.Type
+		mojom mojom_types.Type
+		mp    map[string]mojom_types.UserDefinedType
+	}{
+		{
+			vdl.BoolType,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_Bool},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.ByteType,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT8},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Uint16Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Uint32Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Uint64Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT64},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Int8Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT8},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Int16Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT16},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Int32Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Int64Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Float32Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_Float},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.Float64Type,
+			&mojom_types.TypeSimpleType{mojom_types.SimpleType_Double},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		{
+			vdl.StringType,
+			&mojom_types.TypeStringType{mojom_types.StringType{false}},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		// ?string is currently disallowed in vdl, so skipping
+		{
+			vdl.ArrayType(3, vdl.Int64Type),
+			&mojom_types.TypeArrayType{mojom_types.ArrayType{false, 3, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64}}},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		// ?[3]int64 is currently disallowed in vdl, so skipping
+		{
+			vdl.ListType(vdl.Int64Type),
+			&mojom_types.TypeArrayType{mojom_types.ArrayType{false, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64}}},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		// ?[]int64 is currently disallowed in vdl, so skipping
+		{
+			vdl.MapType(vdl.Int64Type, vdl.BoolType),
+			&mojom_types.TypeMapType{mojom_types.MapType{false, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_Bool}}},
+			map[string]mojom_types.UserDefinedType{},
+		},
+		// ?map[int64]bool is currently disallowed in vdl, so skipping
+		/*
+			TODO(bprosnitz) We need a method to compare mojo types for equality
+			{
+				enumType,
+				&mojom_types.TypeTypeReference{mojom_types.TypeReference{Nullable: false, TypeKey: stringPtr("transcoder_testcases_TestEnum__")}},
+				map[string]mojom_types.UserDefinedType{
+					"transcoder_testcases_TestEnum__": transcoder_testcases.GetAllMojomTypeDefinitions()["transcoder_testcases_TestEnum__"],
+				},
+			},
+			{
+				basicStructType,
+				&mojom_types.TypeTypeReference{mojom_types.TypeReference{Nullable: false, TypeKey: stringPtr("transcoder_testcases_TestBasicStruct__")}},
+				map[string]mojom_types.UserDefinedType{
+					"transcoder_testcases_TestBasicStruct__": transcoder_testcases.GetAllMojomTypeDefinitions()["transcoder_testcases_TestBasicStruct__"],
+					"transcoder_testcases_TestEnum__":        transcoder_testcases.GetAllMojomTypeDefinitions()["transcoder_testcases_TestEnum__"],
+				},
+			},
+		*/
+		/* mojo -> vdl currently doesn't handle cycles
+		{
+			cyclicStructType,
+			&mojom_types.TypeTypeReference{mojom_types.TypeReference{Nullable: false, TypeKey: stringPtr("transcoder_testcases_TestCyclicStruct__")}},
+			map[string]mojom_types.UserDefinedType{
+				"transcoder_testcases_TestCyclicStruct__": transcoder_testcases.GetAllMojomTypeDefinitions()["transcoder_testcases_TestCyclicStruct__"],
+				"transcoder_testcases_TestEnum__":         transcoder_testcases.GetAllMojomTypeDefinitions()["transcoder_testcases_TestEnum__"],
+			},
+		},
+		*/
+		// TODO(bprosnitz) Are there any optional types in common between vdl and mojo?
+	}
+
+	for _, test := range tests {
+		mojomtype, mp := transcoder.VDLToMojomType(test.vdl)
+		if !reflect.DeepEqual(mojomtype, test.mojom) {
+			t.Errorf("vdl type %v, when converted to mojo type was %#v. expected %#v", test.vdl, mojomtype, test.mojom)
+		}
+		if !reflect.DeepEqual(mp, test.mp) {
+			t.Errorf("vdl type %v, when converted to mojo type did not match expected user defined types. got %#v, expected %#v", test.vdl, mojomtype, test.mojom)
+		}
+
+		vt := transcoder.MojomToVDLType(test.mojom, test.mp)
+		if !reflect.DeepEqual(vt, test.vdl) {
+			t.Errorf("mojom type %#v (with user defined types %v), when converted to vdl type was %v. expected %v", test.mojom, test.mp, vt, test.vdl)
+		}
+	}
+}
diff --git a/mojom/mojom/tests/transcoder_testcases.mojom b/mojom/mojom/tests/transcoder_testcases.mojom
index 9ac4b6b..9c7429c 100644
--- a/mojom/mojom/tests/transcoder_testcases.mojom
+++ b/mojom/mojom/tests/transcoder_testcases.mojom
@@ -38,3 +38,18 @@
 struct ObjectUnionWrapper {
   mojo.test.ObjectUnion object_union;
 };
+
+enum TestEnum {
+    A, B, C
+};
+
+struct TestBasicStruct {
+  TestEnum TestEnum;
+  int32 a;
+};
+
+struct TestCyclicStruct {
+    TestEnum TestEnum;
+    TestCyclicStruct? TestCyclicStruct;
+    int32 a;
+};
\ No newline at end of file