blob: c54612c3437f655619fbf19e521843732a79896f [file] [log] [blame]
// 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
import (
"fmt"
"strings"
"mojo/public/interfaces/bindings/mojom_types"
"v.io/v23/vdl"
)
/*// Given a descriptor mapping, produce 2 maps.
// The former maps from mojom identifiers to VDL Type.
// The latter maps from VDL Type string (hash cons) to the mojom identifier.
// These maps are used to interconvert more easily.
func AnalyzeMojomDescriptors(mp map[string]mojom_types.UserDefinedType) map[string]*vdl.Type {
m2V := make(map[string]*vdl.Type)
for s, udt := range mp {
m2V[s] = mojomToVDLTypeUDT(udt, mp)
}
return m2V
}*/
// Convert the known type reference to a vdl type.
// Panics if the type reference was not known.
/*func TypeReferenceToVDLType(tr mojom_types.TypeReference, mp map[string]mojom_types.UserDefinedType) *vdl.Type {
if udt, ok := mp[tr.TypeKey]; ok {
return mojomToVDLTypeUDT(udt, mp)
}
panic("Type Key %s was not present in the mapping", tr.typeKey)
}*/
func mojomToVDLTypeUDT(udt mojom_types.UserDefinedType, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
u := interface{}(udt)
switch u := u.(type) { // To do the type switch, udt has to be converted to interface{}.
case *mojom_types.UserDefinedTypeEnumType: // enum
me := u.Value
// TODO: Assumes that the maximum enum index is len(me.Values) - 1.
labels := make([]string, len(me.Values))
for _, ev := range me.Values { // per EnumValue...
// EnumValue has DeclData, EnumTypeKey, and IntValue.
// We just need the first and last.
labels[int(ev.IntValue)] = *ev.DeclData.ShortName
}
vt = vdl.NamedType(mojomToVdlPath(*me.DeclData.FullIdentifier), vdl.EnumType(labels...))
case *mojom_types.UserDefinedTypeStructType: // struct
ms := u.Value
vt = MojomStructToVDLType(ms, mp)
case *mojom_types.UserDefinedTypeUnionType: // union
mu := u.Value
vfields := make([]vdl.Field, len(mu.Fields))
for ix, mfield := range mu.Fields {
vfields[ix] = vdl.Field{
Name: *mfield.DeclData.ShortName,
Type: MojomToVDLType(mfield.Type, mp),
}
}
vt = vdl.NamedType(mojomToVdlPath(*mu.DeclData.FullIdentifier), vdl.UnionType(vfields...))
case *mojom_types.UserDefinedTypeInterfaceType: // interface
panic("interfaces don't exist in vdl")
default: // unknown
panic(fmt.Errorf("user defined type %#v with unknown tag %d", udt, udt.Tag()))
}
return vt
}
func MojomStructToVDLType(ms mojom_types.MojomStruct, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
vfields := make([]vdl.Field, len(ms.Fields))
for ix, mfield := range ms.Fields {
vfields[ix] = vdl.Field{
Name: *mfield.DeclData.ShortName,
Type: MojomToVDLType(mfield.Type, mp),
}
}
vt = vdl.NamedType(mojomToVdlPath(*ms.DeclData.FullIdentifier), vdl.StructType(vfields...))
return vt
}
// Given a mojom Type and the descriptor mapping, produce the corresponding vdltype.
func MojomToVDLType(mojomtype mojom_types.Type, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
// TODO(alexfandrianto): Cyclic types?
mt := interface{}(mojomtype)
switch mt := interface{}(mt).(type) { // To do the type switch, mt has to be converted to interface{}.
case *mojom_types.TypeSimpleType: // TypeSimpleType
switch mt.Value {
case mojom_types.SimpleType_Bool:
vt = vdl.BoolType
case mojom_types.SimpleType_Double:
vt = vdl.Float64Type
case mojom_types.SimpleType_Float:
vt = vdl.Float32Type
case mojom_types.SimpleType_InT8:
vt = vdl.Int8Type
case mojom_types.SimpleType_InT16:
vt = vdl.Int16Type
case mojom_types.SimpleType_InT32:
vt = vdl.Int32Type
case mojom_types.SimpleType_InT64:
vt = vdl.Int64Type
case mojom_types.SimpleType_UinT8:
vt = vdl.ByteType
case mojom_types.SimpleType_UinT16:
vt = vdl.Uint16Type
case mojom_types.SimpleType_UinT32:
vt = vdl.Uint32Type
case mojom_types.SimpleType_UinT64:
vt = vdl.Uint64Type
}
case *mojom_types.TypeStringType: // TypeStringType
st := mt.Value
if st.Nullable {
panic("nullable strings don't exist in vdl")
}
vt = vdl.StringType
case *mojom_types.TypeArrayType: // TypeArrayType
at := mt.Value
if at.Nullable {
panic("nullable arrays don't exist in vdl")
}
if at.FixedLength > 0 {
vt = vdl.ArrayType(int(at.FixedLength), MojomToVDLType(at.ElementType, mp))
} else {
vt = vdl.ListType(MojomToVDLType(at.ElementType, mp))
}
case *mojom_types.TypeMapType: // TypeMapType
// Note that mojom doesn't have sets.
m := mt.Value
if m.Nullable {
panic("nullable maps don't exist in vdl")
}
vt = vdl.MapType(MojomToVDLType(m.KeyType, mp), MojomToVDLType(m.ValueType, mp))
case *mojom_types.TypeHandleType: // TypeHandleType
panic("handles don't exist in vdl")
case *mojom_types.TypeTypeReference: // TypeTypeReference
tr := mt.Value
if tr.IsInterfaceRequest {
panic("interface requests don't exist in vdl")
}
udt := mp[*tr.TypeKey]
if udt.Tag() != 1 && tr.Nullable {
panic("nullable non-struct type reference cannot be represented in vdl")
}
vt = mojomToVDLTypeUDT(udt, mp)
default:
panic(fmt.Errorf("%#v has unknown tag %d", mojomtype, mojomtype.Tag()))
}
return 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, true, false, mp)
return
}
func vdlToMojomTypeInternal(t *vdl.Type, outermostType bool, 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()),
}
case vdl.String:
return &mojom_types.TypeStringType{
stringType(nullable),
}
case vdl.Array:
elem := vdlToMojomTypeInternal(t.Elem(), false, false, mp)
return &mojom_types.TypeArrayType{
arrayType(elem, nullable, t.Len()),
}
case vdl.List:
elem := vdlToMojomTypeInternal(t.Elem(), false, false, mp)
return &mojom_types.TypeArrayType{
listType(elem, nullable),
}
case vdl.Map:
key := vdlToMojomTypeInternal(t.Key(), false, false, mp)
elem := vdlToMojomTypeInternal(t.Elem(), false, false, mp)
return &mojom_types.TypeMapType{
mapType(key, elem, nullable),
}
case vdl.Struct, vdl.Union, vdl.Enum:
udtKey := addUserDefinedType(t, mp)
ret := &mojom_types.TypeTypeReference{
mojom_types.TypeReference{
Nullable: nullable,
TypeKey: &udtKey,
},
}
if !outermostType {
// This is needed to match the output of the generator exactly, the outermost type
// is not given an identifier.
ret.Value.Identifier = ret.Value.TypeKey
}
return ret
case vdl.Optional:
return vdlToMojomTypeInternal(t.Elem(), false, true, mp)
default:
panic(fmt.Sprintf("conversion from VDL kind %v to mojom type not implemented", t.Kind()))
}
}
func addUserDefinedType(t *vdl.Type, mp map[string]mojom_types.UserDefinedType) string {
key := mojomTypeKey(t)
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 {
structFields := make([]mojom_types.StructField, t.NumField())
for i := 0; i < t.NumField(); i++ {
structFields[i] = mojom_types.StructField{
DeclData: &mojom_types.DeclarationData{ShortName: strPtr(t.Field(i).Name)},
Type: vdlToMojomTypeInternal(t.Field(i).Type, false, false, mp),
Offset: 0, // Despite the fact that we can calculated the offset, set it to zero to match the generator
}
}
_, name := vdl.SplitIdent(t.Name())
return &mojom_types.UserDefinedTypeStructType{
mojom_types.MojomStruct{
DeclData: &mojom_types.DeclarationData{
ShortName: strPtr(name),
FullIdentifier: strPtr(mojomIdentifier(t)),
},
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{
DeclData: &mojom_types.DeclarationData{ShortName: strPtr(t.Field(i).Name)},
Type: vdlToMojomTypeInternal(t.Field(i).Type, false, false, mp),
Tag: uint32(i),
}
}
_, name := vdl.SplitIdent(t.Name())
return &mojom_types.UserDefinedTypeUnionType{
mojom_types.MojomUnion{
DeclData: &mojom_types.DeclarationData{
ShortName: strPtr(name),
FullIdentifier: strPtr(mojomIdentifier(t)),
},
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{
DeclData: &mojom_types.DeclarationData{ShortName: strPtr(t.EnumLabel(i))},
IntValue: int32(i),
EnumTypeKey: mojomTypeKey(t),
}
}
_, name := vdl.SplitIdent(t.Name())
return &mojom_types.UserDefinedTypeEnumType{
mojom_types.MojomEnum{
DeclData: &mojom_types.DeclarationData{
ShortName: strPtr(name),
FullIdentifier: strPtr(mojomIdentifier(t)),
},
Values: enumValues,
},
}
}
func strPtr(x string) *string {
return &x
}
// mojomTypeKey creates a key from the vdl type's name that matches the generator's key.
// The reason for exactly matching the generator is to simplify the tests.
func mojomTypeKey(t *vdl.Type) string {
pkgPath, name := vdl.SplitIdent(t.Name())
pathComponents := strings.Split(pkgPath, "/")
return fmt.Sprintf("%s_%s__", pathComponents[len(pathComponents)-1], name)
}
func mojomIdentifier(t *vdl.Type) string {
return strings.Replace(t.Name(), "/", ".", -1)
}
// "a.b.c.D" -> "a/b/c.D"
func mojomToVdlPath(path string) string {
lastDot := strings.LastIndex(path, ".")
if lastDot == -1 {
return path
}
return strings.Replace(path[:lastDot], ".", "/", -1) + path[lastDot:]
}