]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: add FuncOf function
authorDave Day <djd@golang.org>
Tue, 23 Dec 2014 04:19:30 +0000 (15:19 +1100)
committerDave Day <djd@golang.org>
Thu, 16 Apr 2015 01:38:50 +0000 (01:38 +0000)
This also involves adding functions to typelinks along with a minor
change to ensure they are sorted correctly.

Change-Id: I054a79b6498a634cbccce17579f52c299733c2cf
Reviewed-on: https://go-review.googlesource.com/1996
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/cmd/internal/gc/reflect.go
src/reflect/all_test.go
src/reflect/export_test.go
src/reflect/type.go

index 77504fae2a7db04ffc1017688bf8d433bb93c959..dd751d40170c57942acf993ebd6f8c78169479ba 100644 (file)
@@ -852,10 +852,11 @@ func typelinksym(t *Type) *Sym {
        // %-T is the complete, unambiguous type name.
        // We want the types to end up sorted by string field,
        // so use that first in the name, and then add :%-T to
-       // disambiguate. The names are a little long but they are
-       // discarded by the linker and do not end up in the symbol
-       // table of the final binary.
-       p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + "/" + Tconv(t, obj.FmtLeft)
+       // disambiguate. We use a tab character as the separator to
+       // ensure the types appear sorted by their string field. The
+       // names are a little long but they are discarded by the linker
+       // and do not end up in the symbol table of the final binary.
+       p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + "\t" + Tconv(t, obj.FmtLeft)
 
        s := Pkglookup(p, typelinkpkg)
 
@@ -1212,7 +1213,7 @@ ok:
        // we want be able to find.
        if t.Sym == nil {
                switch t.Etype {
-               case TARRAY, TCHAN, TMAP:
+               case TARRAY, TCHAN, TFUNC, TMAP:
                        slink := typelinksym(t)
                        dsymptr(slink, 0, s, 0)
                        ggloblsym(slink, int32(Widthptr), int8(dupok|obj.RODATA))
index 2be32f31b4f35014a36bc3172655fba9556f602a..8abce526d913d98947fa6e44d07672940af97b1e 100644 (file)
@@ -3663,6 +3663,67 @@ func TestMapOfGCValues(t *testing.T) {
        }
 }
 
+func TestTypelinksSorted(t *testing.T) {
+       var last string
+       for i, n := range TypeLinks() {
+               if n < last {
+                       t.Errorf("typelinks not sorted: %q [%d] > %q [%d]", last, i-1, n, i)
+               }
+               last = n
+       }
+}
+
+func TestFuncOf(t *testing.T) {
+       // check construction and use of type not in binary
+       type K string
+       type V float64
+
+       fn := func(args []Value) []Value {
+               if len(args) != 1 {
+                       t.Errorf("args == %v, want exactly one arg", args)
+               } else if args[0].Type() != TypeOf(K("")) {
+                       t.Errorf("args[0] is type %v, want %v", args[0].Type, TypeOf(K("")))
+               } else if args[0].String() != "gopher" {
+                       t.Errorf("args[0] = %q, want %q", args[0].String(), "gopher")
+               }
+               return []Value{ValueOf(V(3.14))}
+       }
+       v := MakeFunc(FuncOf([]Type{TypeOf(K(""))}, []Type{TypeOf(V(0))}, false), fn)
+
+       outs := v.Call([]Value{ValueOf(K("gopher"))})
+       if len(outs) != 1 {
+               t.Fatalf("v.Call returned %v, want exactly one result", outs)
+       } else if outs[0].Type() != TypeOf(V(0)) {
+               t.Fatalf("c.Call[0] is type %v, want %v", outs[0].Type, TypeOf(V(0)))
+       }
+       f := outs[0].Float()
+       if f != 3.14 {
+               t.Errorf("constructed func returned %f, want %f", f, 3.14)
+       }
+
+       // check that types already in binary are found
+       type T1 int
+       testCases := []struct {
+               in, out  []Type
+               variadic bool
+               want     interface{}
+       }{
+               {in: []Type{TypeOf(T1(0))}, want: (func(T1))(nil)},
+               {in: []Type{TypeOf(int(0))}, want: (func(int))(nil)},
+               {in: []Type{SliceOf(TypeOf(int(0)))}, variadic: true, want: (func(...int))(nil)},
+               {in: []Type{TypeOf(int(0))}, out: []Type{TypeOf(false)}, want: (func(int) bool)(nil)},
+               {in: []Type{TypeOf(int(0))}, out: []Type{TypeOf(false), TypeOf("")}, want: (func(int) (bool, string))(nil)},
+       }
+       for _, tt := range testCases {
+               checkSameType(t, Zero(FuncOf(tt.in, tt.out, tt.variadic)).Interface(), tt.want)
+       }
+
+       // check that variadic requires last element be a slice.
+       FuncOf([]Type{TypeOf(1), TypeOf(""), SliceOf(TypeOf(false))}, nil, true)
+       shouldPanic(func() { FuncOf([]Type{TypeOf(0), TypeOf(""), TypeOf(false)}, nil, true) })
+       shouldPanic(func() { FuncOf(nil, nil, true) })
+}
+
 type B1 struct {
        X int
        Y int
index 9f06324bf86e6f2cc6230d1a41d6e6c73844e907..879c2b2abe09a0689c6b10c99c5307b1631c0400 100644 (file)
@@ -44,3 +44,13 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
        ptrs = ft.kind&kindNoPointers == 0
        return
 }
+
+func TypeLinks() []string {
+       var r []string
+       for _, m := range typelinks() {
+               for _, t := range m {
+                       r = append(r, *t.string)
+               }
+       }
+       return r
+}
index 3e46ce0aaaf8a9ac201201796b5dc4eed6c72119..e51b2e7bc7df906941ba6e97acd95beabf7a068a 100644 (file)
@@ -1396,6 +1396,14 @@ func cachePut(k cacheKey, t *rtype) Type {
        return t
 }
 
+// The funcLookupCache caches FuncOf lookups.
+// FuncOf does not share the common lookupCache since cacheKey is not
+// sufficient to represent functions unambiguously.
+var funcLookupCache struct {
+       sync.RWMutex
+       m map[uint32][]*rtype // keyed by hash calculated in FuncOf
+}
+
 // ChanOf returns the channel type with the given direction and element type.
 // For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int.
 //
@@ -1516,6 +1524,120 @@ func MapOf(key, elem Type) Type {
        return cachePut(ckey, &mt.rtype)
 }
 
+// FuncOf returns the function type with the given argument and result types.
+// For example if k represents int and e represents string,
+// FuncOf([]Type{k}, []Type{e}, false) represents func(int) string.
+//
+// The variadic argument controls whether the function is variadic. FuncOf
+// panics if the in[len(in)-1] does not represent a slice and variadic is
+// true.
+func FuncOf(in, out []Type, variadic bool) Type {
+       if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) {
+               panic("reflect.FuncOf: last arg of variadic func must be slice")
+       }
+
+       // Make a func type.
+       var ifunc interface{} = (func())(nil)
+       prototype := *(**funcType)(unsafe.Pointer(&ifunc))
+       ft := new(funcType)
+       *ft = *prototype
+
+       // Build a hash and minimally populate ft.
+       var hash uint32
+       var fin, fout []*rtype
+       for _, in := range in {
+               t := in.(*rtype)
+               fin = append(fin, t)
+               hash = fnv1(hash, byte(t.hash>>24), byte(t.hash>>16), byte(t.hash>>8), byte(t.hash))
+       }
+       if variadic {
+               hash = fnv1(hash, 'v')
+       }
+       hash = fnv1(hash, '.')
+       for _, out := range out {
+               t := out.(*rtype)
+               fout = append(fout, t)
+               hash = fnv1(hash, byte(t.hash>>24), byte(t.hash>>16), byte(t.hash>>8), byte(t.hash))
+       }
+       ft.hash = hash
+       ft.in = fin
+       ft.out = fout
+       ft.dotdotdot = variadic
+
+       // Look in cache.
+       funcLookupCache.RLock()
+       for _, t := range funcLookupCache.m[hash] {
+               if haveIdenticalUnderlyingType(&ft.rtype, t) {
+                       funcLookupCache.RUnlock()
+                       return t
+               }
+       }
+       funcLookupCache.RUnlock()
+
+       // Not in cache, lock and retry.
+       funcLookupCache.Lock()
+       defer funcLookupCache.Unlock()
+       if funcLookupCache.m == nil {
+               funcLookupCache.m = make(map[uint32][]*rtype)
+       }
+       for _, t := range funcLookupCache.m[hash] {
+               if haveIdenticalUnderlyingType(&ft.rtype, t) {
+                       return t
+               }
+       }
+
+       // Look in known types for the same string representation.
+       str := funcStr(ft)
+       for _, tt := range typesByString(str) {
+               if haveIdenticalUnderlyingType(&ft.rtype, tt) {
+                       funcLookupCache.m[hash] = append(funcLookupCache.m[hash], tt)
+                       return tt
+               }
+       }
+
+       // Populate the remaining fields of ft and store in cache.
+       ft.string = &str
+       ft.uncommonType = nil
+       ft.ptrToThis = nil
+       ft.zero = unsafe.Pointer(&make([]byte, ft.size)[0])
+       funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
+
+       return ft
+}
+
+// funcStr builds a string representation of a funcType.
+func funcStr(ft *funcType) string {
+       repr := make([]byte, 0, 64)
+       repr = append(repr, "func("...)
+       for i, t := range ft.in {
+               if i > 0 {
+                       repr = append(repr, ", "...)
+               }
+               if ft.dotdotdot && i == len(ft.in)-1 {
+                       repr = append(repr, "..."...)
+                       repr = append(repr, *(*sliceType)(unsafe.Pointer(t)).elem.string...)
+               } else {
+                       repr = append(repr, *t.string...)
+               }
+       }
+       repr = append(repr, ')')
+       if l := len(ft.out); l == 1 {
+               repr = append(repr, ' ')
+       } else if l > 1 {
+               repr = append(repr, " ("...)
+       }
+       for i, t := range ft.out {
+               if i > 0 {
+                       repr = append(repr, ", "...)
+               }
+               repr = append(repr, *t.string...)
+       }
+       if len(ft.out) > 1 {
+               repr = append(repr, ')')
+       }
+       return string(repr)
+}
+
 // isReflexive reports whether the == operation on the type is reflexive.
 // That is, x == x for all values x of type t.
 func isReflexive(t *rtype) bool {