| // 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 ( |
| "errors" |
| "fmt" |
| "reflect" |
| "sync" |
| "unsafe" |
| ) |
| |
| // rtRegistry is a map from reflect.Type to *Type. The only instance is |
| // rtCache, which is a global cache to speed up repeated lookups. |
| // |
| // All locking is performed in TypeFromReflect. |
| type rtRegistry struct { |
| sync.RWMutex |
| rtmap map[reflect.Type]*Type |
| } |
| |
| var ( |
| rtCache = &rtRegistry{ |
| rtmap: map[reflect.Type]*Type{ |
| // Ensure TypeOf(WireError{}) returns the built-in VDL error type. |
| reflect.TypeOf(WireError{}): ErrorType.Elem(), |
| }, |
| } |
| rtCacheEnabled = true |
| ) |
| |
| func (reg *rtRegistry) lookup(rt reflect.Type) *Type { |
| if !rtCacheEnabled { |
| return nil |
| } |
| return reg.rtmap[rt] |
| } |
| |
| func (reg *rtRegistry) update(pending map[reflect.Type]TypeOrPending) { |
| if !rtCacheEnabled { |
| return |
| } |
| for rt, top := range pending { |
| t, _ := getBuiltType(top) |
| reg.rtmap[rt] = t |
| } |
| } |
| |
| // getBuiltType returns the built type from top. |
| func getBuiltType(top TypeOrPending) (*Type, error) { |
| switch ttop := top.(type) { |
| case *Type: |
| return ttop, nil |
| case PendingType: |
| return ttop.Built() |
| } |
| panic(fmt.Errorf("vdl: unknown type in TypeOrPending: %T %v", top, top)) |
| } |
| |
| // TypeOf returns the type corresponding to v. It's a helper for calling |
| // TypeFromReflect, and panics on any errors. |
| func TypeOf(v interface{}) *Type { |
| t, err := TypeFromReflect(reflect.TypeOf(v)) |
| if err != nil { |
| panic(fmt.Errorf("vdl: can't take TypeOf(%T): %v", v, err)) |
| } |
| return t |
| } |
| |
| // Normalize the rt type. The VDL type system Optional is represented as a Go |
| // pointer. Only structs may be optional in VDL, but we still allow the pointer |
| // forms of other types in Go. E.g. VDL doesn't allow ?map, but we do allow Go |
| // **map, and consider that to be a VDL map; the pointers are flattened away. |
| // |
| // In addition all Go interfaces are represented with the single Any type, |
| // except for Go interfaces that describe Union types. |
| // |
| // By normalizing the rt type we simplify type creation, and also reduce |
| // redundancy in the rtCache. |
| func normalizeType(rt reflect.Type) reflect.Type { |
| // Flatten rt to no pointers, and rtAtMostOnePtr to at most one pointer. |
| hasPtr := false |
| for rt.Kind() == reflect.Ptr { |
| if ni := nativeInfoFromNative(rt); ni != nil { |
| if hasPtr { |
| return normalizeType(reflect.PtrTo(ni.WireType)) |
| } |
| return normalizeType(ni.WireType) |
| } |
| hasPtr = true |
| rt = rt.Elem() |
| } |
| if ni := nativeInfoFromNative(rt); ni != nil { |
| if hasPtr { |
| return normalizeType(reflect.PtrTo(ni.WireType)) |
| } |
| return normalizeType(ni.WireType) |
| } |
| rtAtMostOnePtr := rt |
| if hasPtr { |
| rtAtMostOnePtr = reflect.PtrTo(rt) |
| } |
| // Handle special cases. Union may be either an interface or a struct, and |
| // should be handled first. |
| ri, _, _ := deriveReflectInfo(rt) |
| if ri != nil && len(ri.UnionFields) > 0 { |
| return rt |
| } |
| switch { |
| case rt.ConvertibleTo(rtError) || rtAtMostOnePtr.ConvertibleTo(rtError): |
| return rtError |
| case rt.Kind() == reflect.Interface: |
| // Collapse all interfaces to interface{} |
| return rtInterface |
| case rt.Kind() == reflect.Struct && rt.PkgPath() != "": |
| // Named structs may be optional, so we keep the pointer. |
| return rtAtMostOnePtr |
| } |
| return rt |
| } |
| |
| // basicType returns the *Type corresponding to rt for basic types that cannot |
| // be named by the user, and have a well-known conversion. |
| func basicType(rt reflect.Type) *Type { |
| for rt.Kind() == reflect.Ptr { |
| rt = rt.Elem() |
| } |
| switch rt { |
| case rtError: |
| return ErrorType |
| case rtInterface, rtValue: |
| return AnyType |
| case rtType: |
| return TypeObjectType |
| } |
| return nil |
| } |
| |
| // TypeFromReflect returns the type corresponding to rt. Not all reflect types |
| // have a valid type; reflect.Chan, reflect.Func and reflect.UnsafePointer are |
| // unsupported, as are maps with pointer keys, as well as structs with only |
| // unexported fields. |
| func TypeFromReflect(rt reflect.Type) (*Type, error) { |
| if rt == nil { |
| return AnyType, nil |
| } |
| rt = normalizeType(rt) |
| if t := basicType(rt); t != nil { |
| return t, nil |
| } |
| // Fastpath - grab the reader lock and check if rt is already in the cache. |
| rtCache.RLock() |
| t := rtCache.lookup(rt) |
| rtCache.RUnlock() |
| if t != nil { |
| return t, nil |
| } |
| // Slowpath - first register rt and all subtypes, to ensure they're known. |
| // This avoids some of the tricky ordering issues between vdl.Register and |
| // vdl.TypeFromReflect, when they are called in init functions. |
| if err := registerRecursive(rt); err != nil { |
| return nil, err |
| } |
| // Slowpath - grab the writer lock. We hold the lock even while building the |
| // type, since TypeBuilder requires that if two types are identical, they must |
| // be represented by the same Type or PendingType. Here's an example: |
| // type Str string |
| // type Foo struct { A, B Str } |
| // |
| // If we built type Foo without the lock, we might end up with this ordering: |
| // thread_1 build Foo |
| // thread_1 build Foo.A |
| // |
| // thread_2 build Foo |
| // thread_2 build Foo.A |
| // thread_2 build Foo.B |
| // thread_2 update map |
| // |
| // thread_1 build Foo.B // type Str found in map, different from Foo.A |
| // |
| // The problem is that thread_1 now has two different representations of Str |
| // in its TypeBuilder - it built type Str itself for Foo.A, and it found Str |
| // from the map for Foo.B. The TypeBuilder notices this inconsistency, and |
| // returns an error. |
| // |
| // Side note: you might think there's a simpler fix; if each thread updated |
| // the map (under the lock) as it built each type, we wouldn't need to lock |
| // the entire type-building operation. But note that TypeBuilder only returns |
| // PendingType as types are being built, and only returns the final Type when |
| // Build is called at the very end, in order to support cyclic types. |
| rtCache.Lock() |
| defer rtCache.Unlock() |
| // The strategy is to recursively populate the builder with the type and |
| // subtypes, keeping track of new types in pending. After all types have been |
| // populated, we build the types and update rtCache with all pending types. |
| builder := new(TypeBuilder) |
| pending := make(map[reflect.Type]TypeOrPending) |
| result, err := typeFromReflectLocked(rt, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| builder.Build() |
| built, err := getBuiltType(result) |
| if built == nil { |
| // There must have been an error building one of the types. Return the |
| // first error we find, favoring errors from building the result itself. |
| if err != nil { |
| return nil, err |
| } |
| for _, top := range pending { |
| _, err := getBuiltType(top) |
| if err != nil { |
| return nil, err |
| } |
| } |
| panic(fmt.Errorf("vdl: inconsistent results from TypeBuilder.Build: %v", pending)) |
| } |
| rtCache.update(pending) |
| return built, nil |
| } |
| |
| // typeFromReflectLocked returns the Type or PendingType corresponding to rt. |
| // It either returns the type directly from the cache or pending map, or makes |
| // the type based on rt. |
| // |
| // REQUIRES: rtCache is locked |
| func typeFromReflectLocked(rt reflect.Type, builder *TypeBuilder, pending map[reflect.Type]TypeOrPending) (TypeOrPending, error) { |
| rt = normalizeType(rt) |
| if t := basicType(rt); t != nil { |
| return t, nil |
| } |
| if t := rtCache.lookup(rt); t != nil { |
| return t, nil |
| } |
| if p, ok := pending[rt]; ok { |
| // If the type is already in our pending map, we return it now. This breaks |
| // infinite loops from recursive types. |
| return p, nil |
| } |
| return makeTypeFromReflectLocked(rt, builder, pending) |
| } |
| |
| // validateType returns a non-nil error if rt is not a valid vdl type. |
| func validateType(rt reflect.Type) error { |
| // Flatten pointers to simplify the checks. |
| for rt.Kind() == reflect.Ptr { |
| rt = rt.Elem() |
| } |
| // Now check error conditions. |
| if rt == rtReflectValue { |
| return errTypeFromReflectValue |
| } |
| switch rt.Kind() { |
| case reflect.Chan, reflect.Func, reflect.UnsafePointer: |
| return fmt.Errorf("reflect type %q not supported", rt) |
| } |
| return nil |
| } |
| |
| // makeTypeFromReflectLocked makes the Type or PendingType corresponding to rt. |
| // Calls typeFromReflect to recursively generate subtypes. |
| // |
| // REQUIRES: rtCache is locked |
| // PRE-CONDITION: rt doesn't exist in rtCache or pending. |
| // POST-CONDITION: rt exists in pending. |
| func makeTypeFromReflectLocked(rt reflect.Type, builder *TypeBuilder, pending map[reflect.Type]TypeOrPending) (TypeOrPending, error) { |
| if err := validateType(rt); err != nil { |
| return nil, err |
| } |
| if rt.Kind() == reflect.Ptr { |
| // Pointers are turned into Optional. |
| opt := builder.Optional() |
| pending[rt] = opt |
| elem, err := typeFromReflectLocked(rt.Elem(), builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| opt.AssignElem(elem) |
| return opt, nil |
| } |
| ri,_, err := deriveReflectInfo(rt) |
| if err != nil { |
| return nil, err |
| } |
| if ri.Name == "" { |
| // Unnamed types are made directly. There's no way to create a recursive |
| // type based solely on unnamed types, so it's ok to to update pending |
| // *after* making the unnamed type. |
| unnamed, err := makeUnnamedFromReflectLocked(ri, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| pending[ri.Type] = unnamed |
| return unnamed, nil |
| } |
| // Named types are trickier, since they may be recursive. First create the |
| // named type and add it to pending. We must special-case union types; the |
| // interface type and all field types are keyed in the pending map to point to |
| // the created vdl type. |
| named := builder.Named(ri.Name) |
| pending[ri.Type] = named |
| for _, unionField := range ri.UnionFields { |
| pending[unionField.RepType] = named |
| } |
| // Now make the unnamed underlying type. Recursive types will find the |
| // existing entry in pending, and avoid the infinite loop. |
| unnamed, err := makeUnnamedFromReflectLocked(ri, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| // Finally assign the base type and we're done. |
| named.AssignBase(unnamed) |
| return named, nil |
| } |
| |
| // makeUnnamedFromReflectLocked makes the underlying unnamed Type or PendingType |
| // corresponding to ri. |
| // |
| // REQUIRES: rtCache is locked |
| func makeUnnamedFromReflectLocked(ri *reflectInfo, builder *TypeBuilder, pending map[reflect.Type]TypeOrPending) (TypeOrPending, error) { |
| // Handle enum types |
| if len(ri.EnumLabels) > 0 { |
| enum := builder.Enum() |
| for _, label := range ri.EnumLabels { |
| enum.AppendLabel(label) |
| } |
| return enum, nil |
| } |
| // Handle union types |
| if len(ri.UnionFields) > 0 { |
| union := builder.Union() |
| for _, f := range ri.UnionFields { |
| in, err := typeFromReflectLocked(f.Type, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| union.AppendField(f.Name, in) |
| } |
| return union, nil |
| } |
| // Handle composite types |
| rt := ri.Type |
| switch rt.Kind() { |
| case reflect.Array: |
| elem, err := typeFromReflectLocked(rt.Elem(), builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| return builder.Array().AssignLen(rt.Len()).AssignElem(elem), nil |
| case reflect.Slice: |
| elem, err := typeFromReflectLocked(rt.Elem(), builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| return builder.List().AssignElem(elem), nil |
| case reflect.Map: |
| if rt.Key().Kind() == reflect.Ptr { |
| return nil, fmt.Errorf("invalid key %q in %q", rt.Key(), rt) |
| } |
| key, err := typeFromReflectLocked(rt.Key(), builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| if rt.Elem() == rtUnnamedEmptyStruct { |
| // The map actually represents a set |
| return builder.Set().AssignKey(key), nil |
| } |
| elem, err := typeFromReflectLocked(rt.Elem(), builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| return builder.Map().AssignKey(key).AssignElem(elem), nil |
| case reflect.Struct: |
| st := builder.Struct() |
| for fx := 0; fx < rt.NumField(); fx++ { |
| rtField := rt.Field(fx) |
| if rtField.PkgPath != "" { |
| continue // field isn't exported |
| } |
| field, err := typeFromReflectLocked(rtField.Type, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| st.AppendField(rtField.Name, field) |
| } |
| if rt.NumField() > 0 && st.NumField() == 0 { |
| return nil, fmt.Errorf("type %q only has unexported fields", rt) |
| } |
| return st, nil |
| } |
| // Handle scalar types |
| if t := typeFromRTKind[rt.Kind()]; t != nil { |
| return t, nil |
| } |
| panic(fmt.Errorf("vdl: makeUnnamedFromReflectLocked unhandled %v %v", rt.Kind(), rt)) |
| } |
| |
| var ( |
| errTypeFromReflectValue = errors.New("invalid vdl.TypeOf(reflect.Value{})") |
| |
| rtInterface = reflect.TypeOf((*interface{})(nil)).Elem() |
| rtBool = reflect.TypeOf(false) |
| rtByte = reflect.TypeOf(byte(0)) |
| rtUint16 = reflect.TypeOf(uint16(0)) |
| rtUint32 = reflect.TypeOf(uint32(0)) |
| rtUint64 = reflect.TypeOf(uint64(0)) |
| rtInt8 = reflect.TypeOf(int8(0)) |
| rtInt16 = reflect.TypeOf(int16(0)) |
| rtInt32 = reflect.TypeOf(int32(0)) |
| rtInt64 = reflect.TypeOf(int64(0)) |
| rtFloat32 = reflect.TypeOf(float32(0)) |
| rtFloat64 = reflect.TypeOf(float64(0)) |
| rtComplex64 = reflect.TypeOf(complex64(0)) |
| rtComplex128 = reflect.TypeOf(complex128(0)) |
| rtString = reflect.TypeOf("") |
| rtError = reflect.TypeOf((*error)(nil)).Elem() |
| rtWireError = reflect.TypeOf(WireError{}) |
| rtType = reflect.TypeOf(Type{}) |
| rtValue = reflect.TypeOf(Value{}) |
| rtPtrToType = reflect.TypeOf((*Type)(nil)) |
| rtPtrToValue = reflect.TypeOf((*Value)(nil)) |
| rtReflectValue = reflect.TypeOf(reflect.Value{}) |
| rtUnnamedEmptyStruct = reflect.TypeOf(struct{}{}) |
| |
| typeFromRTKind = [...]*Type{ |
| reflect.Bool: BoolType, |
| reflect.Uint8: ByteType, |
| reflect.Uint16: Uint16Type, |
| reflect.Uint32: Uint32Type, |
| reflect.Uint64: Uint64Type, |
| reflect.Uint: uintType(8 * unsafe.Sizeof(uint(0))), |
| reflect.Uintptr: uintType(8 * unsafe.Sizeof(uintptr(0))), |
| reflect.Int8: Int8Type, |
| reflect.Int16: Int16Type, |
| reflect.Int32: Int32Type, |
| reflect.Int64: Int64Type, |
| reflect.Int: intType(8 * unsafe.Sizeof(int(0))), |
| reflect.Float32: Float32Type, |
| reflect.Float64: Float64Type, |
| reflect.Complex64: Complex64Type, |
| reflect.Complex128: Complex128Type, |
| reflect.String: StringType, |
| } |
| ) |
| |
| func uintType(bitlen uintptr) *Type { |
| switch bitlen { |
| case 32: |
| return Uint32Type |
| default: |
| return Uint64Type |
| } |
| } |
| |
| func intType(bitlen uintptr) *Type { |
| switch bitlen { |
| case 32: |
| return Int32Type |
| default: |
| return Int64Type |
| } |
| } |