| // 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 vdl |
| |
| import ( |
| "fmt" |
| "reflect" |
| "sync" |
| ) |
| |
| // Register registers a type, identified by a value for that type. The type |
| // should be a type that will be sent over the wire. Subtypes are recursively |
| // registered. This creates a type name <-> reflect.Type bijective mapping. |
| // |
| // Type registration is only required for VDL conversion into interface{} |
| // values, so that values of the correct type may be generated. Conversion into |
| // interface{} values for types that are not registered will fill in *vdl.Value |
| // into the interface{} value. |
| // |
| // Panics if wire is not a valid wire type, or if the name<->type mapping is not |
| // bijective. |
| // |
| // Register is not intended to be called by end users; calls are auto-generated |
| // for all types defined in *.vdl files. |
| func Register(wire interface{}) { |
| if wire == nil { |
| return |
| } |
| if err := registerRecursive(reflect.TypeOf(wire)); err != nil { |
| panic(err) |
| } |
| } |
| |
| func registerRecursive(rt reflect.Type) error { |
| // 1) Normalize and derive reflect information. |
| rt = normalizeType(rt) |
| for rt.Kind() == reflect.Ptr { |
| rt = rt.Elem() |
| } |
| ri, added, err := deriveReflectInfo(rt) |
| if err != nil { |
| return err |
| } |
| if !added { |
| // Break cyles for recursive types. |
| // |
| // TODO(toddw): There is a glaring bug in this logic. Our first step is to |
| // call normalizeType, which itself calls deriveReflectInfo. Thus our |
| // subsequent call to deriveReflectInfo will always return added=false, and |
| // we will never run the logic below. In addition, deriveReflectInfo is |
| // also called outside of registerRecursive, with the same effect. |
| // |
| // The general philosophy for the fix: |
| // 1) Types may be registered explicitly via Register. |
| // 2) Types may be registered implicitly via all calls in vdl that take an |
| // interface{} argument. |
| // 3) RegisterNative registers both wire and native types. |
| // 4) Subtypes are always registered recursively. |
| // 5) Once a type is registered, decode/convert into an interface{} works |
| // as expected, returning the concrete Go value. |
| return nil |
| } |
| // 2) Recurse on subtypes contained in composite types. |
| if len(ri.UnionFields) > 0 { |
| // Special-case to recurse on union fields. |
| for _, field := range ri.UnionFields { |
| if err := registerRecursive(field.Type); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| switch wt := ri.Type; wt.Kind() { |
| case reflect.Array, reflect.Slice, reflect.Ptr: |
| return registerRecursive(wt.Elem()) |
| case reflect.Map: |
| if err := registerRecursive(wt.Key()); err != nil { |
| return err |
| } |
| return registerRecursive(wt.Elem()) |
| case reflect.Struct: |
| for ix := 0; ix < wt.NumField(); ix++ { |
| if err := registerRecursive(wt.Field(ix).Type); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| return nil |
| } |
| |
| // riRegistry holds the reflectInfo registry. Unlike rtRegistry (used for the |
| // rtCache), this information cannot be regenerated at will. We expect a |
| // limited number of types to be used within a single address space. |
| type riRegistry struct { |
| sync.RWMutex |
| fromName map[string]*reflectInfo |
| fromType map[reflect.Type]*reflectInfo |
| } |
| |
| var riReg = &riRegistry{ |
| fromName: make(map[string]*reflectInfo), |
| fromType: make(map[reflect.Type]*reflectInfo), |
| } |
| |
| // reflectInfoFromName returns the reflectInfo for the given vdl type name, or |
| // nil if Register has not been called for a type with the given name. |
| func reflectInfoFromName(name string) *reflectInfo { |
| riReg.RLock() |
| ri := riReg.fromName[name] |
| riReg.RUnlock() |
| return ri |
| } |
| |
| // reflectInfo holds the reflection information for a type. All fields are |
| // populated via reflection over the Type. |
| // |
| // The type may include a special __VDLReflect function to describe metadata. |
| // This is only required for enum and union vdl types, which don't have a |
| // canonical Go representation. All other fields are optional. |
| // |
| // type Foo struct{} |
| // func (Foo) __VDLReflect(struct{ |
| // // Type represents the base type. This is used by union to describe the |
| // // union interface type, as opposed to the concrete struct field types. |
| // Type Foo |
| // |
| // // Name holds the vdl type name, including the package path, in a tag. |
| // Name string "vdl/pkg.Foo" |
| // |
| // // Only one of Enum or Union should be set; they're both shown here for |
| // // explanatory purposes. |
| // |
| // // Enum describes the labels for an enum type. |
| // Enum struct { A, B string } |
| // |
| // // Union describes the union field names, along with the concrete struct |
| // // field types, which contain the actual field types. |
| // Union struct { |
| // A FieldA |
| // B FieldB |
| // } |
| // } |
| type reflectInfo struct { |
| // Type is the basis for all other information in this struct. |
| Type reflect.Type |
| |
| // Name is the vdl type name including the vdl package path, |
| // e.g. "v.io/v23/vdl.Foo". |
| Name string |
| |
| // EnumLabels holds the labels of an enum; it is non-empty iff the Type |
| // represents a vdl enum. |
| EnumLabels []string |
| |
| // UnionFields holds the fields of a union; it is non-empty iff the Type |
| // represents a vdl union. |
| UnionFields []reflectField |
| } |
| |
| // reflectField describes the reflection info for a Union field. |
| type reflectField struct { |
| // Given a vdl type Foo union{A bool;B string}, we generate: |
| // type Foo interface{...} |
| // type FooA struct{ Value bool } |
| // type FooB struct{ Value string } |
| Name string // Field name, e.g. "A", "B" |
| Type reflect.Type // Field type, e.g. bool, string |
| RepType reflect.Type // Concrete type representing the field, e.g. FooA, FooB |
| } |
| |
| // deriveReflectInfo returns the reflectInfo corresponding to rt. |
| // REQUIRES: rt has been normalized, and pointers have been flattened. |
| func deriveReflectInfo(rt reflect.Type) (*reflectInfo, bool, error) { |
| riReg.RLock() |
| if ri, ok := riReg.fromType[rt]; ok { |
| riReg.RUnlock() |
| return ri, false, nil |
| } |
| riReg.RUnlock() |
| |
| // Set reasonable defaults for types that don't have the __VDLReflect method. |
| ri := new(reflectInfo) |
| ri.Type = rt |
| if rt.PkgPath() != "" { |
| ri.Name = rt.PkgPath() + "." + rt.Name() |
| } |
| // If rt is an non-interface type, methods include the receiver as the first |
| // in-arg, otherwise they don't. |
| offsetIn := 1 |
| if rt.Kind() == reflect.Interface { |
| offsetIn = 0 |
| } |
| // If rt has a __VDLReflect method, use it to extract metadata. |
| if method, ok := rt.MethodByName("__VDLReflect"); ok { |
| mtype := method.Type |
| if mtype.NumOut() != 0 || mtype.NumIn() != 1+offsetIn || mtype.In(offsetIn).Kind() != reflect.Struct { |
| return nil, false, fmt.Errorf("type %q invalid __VDLReflect (want __VDLReflect(struct{...}))", rt) |
| } |
| // rtReflect corresponds to the argument to __VDLReflect. |
| rtReflect := mtype.In(offsetIn) |
| if field, ok := rtReflect.FieldByName("Type"); ok { |
| ri.Type = field.Type |
| if wt := ri.Type; wt.PkgPath() != "" { |
| ri.Name = wt.PkgPath() + "." + wt.Name() |
| } else { |
| ri.Name = "" |
| } |
| } |
| if field, ok := rtReflect.FieldByName("Name"); ok { |
| ri.Name = field.Tag.Get("vdl") |
| } |
| if field, ok := rtReflect.FieldByName("Enum"); ok { |
| if err := describeEnum(field.Type, rt, ri); err != nil { |
| return nil, false, err |
| } |
| } |
| if field, ok := rtReflect.FieldByName("Union"); ok { |
| if err := describeUnion(field.Type, rt, ri); err != nil { |
| return nil, false, err |
| } |
| } |
| if len(ri.EnumLabels) > 0 && len(ri.UnionFields) > 0 { |
| return nil, false, fmt.Errorf("type %q is both an enum and a union", rt) |
| } |
| } |
| |
| riReg.Lock() |
| defer riReg.Unlock() |
| |
| if ri, ok := riReg.fromType[rt]; ok { |
| return ri, false, nil |
| } |
| if ri.Name != "" { |
| if riDup := riReg.fromName[ri.Name]; riDup != nil && ri.Type != riDup.Type { |
| return nil, false, fmt.Errorf("vdl: Register(%v) duplicate name %q: %#v and %#v", rt, ri.Name, ri, riDup) |
| } |
| riReg.fromName[ri.Name] = ri |
| } |
| riReg.fromType[rt] = ri |
| return ri, true, nil |
| } |
| |
| // describeEnum fills in ri; we expect enumReflect has this format: |
| // struct {A, B, C Foo} |
| // |
| // Here's the full type for vdl type Foo enum{A;B} |
| // type Foo int |
| // const ( |
| // FooA Foo = iota |
| // FooB |
| // ) |
| // func (Foo) __VDLReflect(struct{ |
| // Type Foo |
| // Enum struct { A, B Foo } |
| // }) {} |
| // func (Foo) String() string {} |
| // func (*Foo) Set(string) error {} |
| func describeEnum(enumReflect, rt reflect.Type, ri *reflectInfo) error { |
| if rt != ri.Type || rt.Kind() == reflect.Interface { |
| return fmt.Errorf("enum type %q invalid (mismatched type %q)", rt, ri.Type) |
| } |
| if enumReflect.Kind() != reflect.Struct || enumReflect.NumField() == 0 { |
| return fmt.Errorf("enum type %q invalid (no labels)", rt) |
| } |
| for ix := 0; ix < enumReflect.NumField(); ix++ { |
| ri.EnumLabels = append(ri.EnumLabels, enumReflect.Field(ix).Name) |
| } |
| if s, ok := rt.MethodByName("String"); !ok || |
| s.Type.NumIn() != 1 || |
| s.Type.NumOut() != 1 || s.Type.Out(0) != rtString { |
| return fmt.Errorf("enum type %q must have method String() string", rt) |
| } |
| _, nonptr := rt.MethodByName("Set") |
| if a, ok := reflect.PtrTo(rt).MethodByName("Set"); !ok || nonptr || |
| a.Type.NumIn() != 2 || a.Type.In(1) != rtString || |
| a.Type.NumOut() != 1 || a.Type.Out(0) != rtError { |
| return fmt.Errorf("enum type %q must have pointer method Set(string) error", rt) |
| } |
| return nil |
| } |
| |
| // describeUnion fills in ri; we expect unionReflect has this format: |
| // struct { |
| // A FooA |
| // B FooB |
| // } |
| // |
| // Here's the full type for vdl type Foo union{A bool; B string} |
| // type ( |
| // // Foo is the union interface type, that can hold any field. |
| // Foo interface { |
| // Index() int |
| // Name() string |
| // __VDLReflect(__FooReflect) |
| // } |
| // // FooA and FooB are the concrete field types. |
| // FooA struct { Value bool } |
| // FooB struct { Value string } |
| // // __FooReflect lets us re-construct the union type via reflection. |
| // __FooReflect struct { |
| // Type Foo // Tells us the union interface type. |
| // Union struct { |
| // A FooA // Tells us field 0 has name A and concrete type FooA. |
| // B FooB // Tells us field 1 has name B and concrete type FooB. |
| // } |
| // } |
| // ) |
| func describeUnion(unionReflect, rt reflect.Type, ri *reflectInfo) error { |
| if ri.Type.Kind() != reflect.Interface { |
| return fmt.Errorf("union type %q has non-interface type %q", rt, ri.Type) |
| } |
| if unionReflect.Kind() != reflect.Struct || unionReflect.NumField() == 0 { |
| return fmt.Errorf("union type %q invalid (no fields)", rt) |
| } |
| for ix := 0; ix < unionReflect.NumField(); ix++ { |
| f := unionReflect.Field(ix) |
| if f.PkgPath != "" { |
| return fmt.Errorf("union type %q field %q.%q must be exported", rt, f.PkgPath, f.Name) |
| } |
| // f.Type corresponds to FooA and FooB in __FooReflect above. |
| if f.Type.Kind() != reflect.Struct || f.Type.NumField() != 1 || f.Type.Field(0).Name != "Value" { |
| return fmt.Errorf("union type %q field %q has bad concrete field type %q", rt, f.Name, f.Type) |
| } |
| ri.UnionFields = append(ri.UnionFields, reflectField{ |
| Name: f.Name, |
| Type: f.Type.Field(0).Type, |
| RepType: f.Type, |
| }) |
| } |
| // Check for Name method on interface and all concrete field structs. |
| if n, ok := ri.Type.MethodByName("Name"); !ok || n.Type.NumIn() != 0 || |
| n.Type.NumOut() != 1 || n.Type.Out(0) != rtString { |
| return fmt.Errorf("union interface type %q must have method Name() string", ri.Type) |
| } |
| for _, f := range ri.UnionFields { |
| if n, ok := f.RepType.MethodByName("Name"); !ok || n.Type.NumIn() != 1 || |
| n.Type.NumOut() != 1 || n.Type.Out(0) != rtString { |
| return fmt.Errorf("union field %q type %q must have method Name() string", f.Name, f.RepType) |
| } |
| } |
| return nil |
| } |
| |
| // TypeToReflect returns the reflect.Type corresponding to t. We look up |
| // named types in our registry, and build the unnamed types that we can via the |
| // Go reflect package. Returns nil for types that can't be manufactured. |
| func TypeToReflect(t *Type) reflect.Type { |
| if t.Name() != "" { |
| // Named types cannot be manufactured via Go reflect, so we lookup in our |
| // registry instead. |
| if ri := reflectInfoFromName(t.Name()); ri != nil { |
| if ni := nativeInfoFromWire(ri.Type); ni != nil { |
| return ni.NativeType |
| } |
| return ri.Type |
| } |
| return nil |
| } |
| // We can make some unnamed types via Go reflect. Everything else drops |
| // through and returns nil. |
| switch t.Kind() { |
| case Any, Array, Enum, Union: |
| // We can't make unnamed versions of any of these types. |
| return nil |
| case Optional: |
| if elem := TypeToReflect(t.Elem()); elem != nil { |
| return reflect.PtrTo(elem) |
| } |
| return nil |
| case List: |
| if elem := TypeToReflect(t.Elem()); elem != nil { |
| return reflect.SliceOf(elem) |
| } |
| return nil |
| case Set: |
| if key := TypeToReflect(t.Key()); key != nil { |
| return reflect.MapOf(key, rtUnnamedEmptyStruct) |
| } |
| return nil |
| case Map: |
| if key, elem := TypeToReflect(t.Key()), TypeToReflect(t.Elem()); key != nil && elem != nil { |
| return reflect.MapOf(key, elem) |
| } |
| return nil |
| case Struct: |
| if t.NumField() == 0 { |
| return rtUnnamedEmptyStruct |
| } |
| return nil |
| default: |
| return rtFromKind[t.Kind()] |
| } |
| } |
| |
| var rtFromKind = [...]reflect.Type{ |
| Bool: rtBool, |
| Byte: rtByte, |
| Uint16: rtUint16, |
| Uint32: rtUint32, |
| Uint64: rtUint64, |
| Int8: rtInt8, |
| Int16: rtInt16, |
| Int32: rtInt32, |
| Int64: rtInt64, |
| Float32: rtFloat32, |
| Float64: rtFloat64, |
| Complex64: rtComplex64, |
| Complex128: rtComplex128, |
| String: rtString, |
| TypeObject: rtPtrToType, |
| } |