-
Notifications
You must be signed in to change notification settings - Fork 0
/
type.go
209 lines (180 loc) · 5.04 KB
/
type.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2013 Tumblr, Inc.
// Use of this source code is governed by the license for
// The Go Circuit Project, found in the LICENSE file.
//
// Authors:
// 2013 Petar Maymounkov <p@gocircuit.org>
// Package types implements the runtime type system
package types
import (
"fmt"
"reflect"
"sync"
"unicode"
"unicode/utf8"
)
// TypeID is a handle for a type
type TypeID uint64
// TypeTabl assigns universal IDs to local, Go runtime types.
// These IDs are purely a function of the type's package path and name, thereby
// enabling interoperability between compatible but different binaries of the
// underlying Go code.
type TypeTabl struct {
lk sync.Mutex
im map[TypeID]*TypeChar
tm map[reflect.Type]*TypeChar
}
func makeTypeTabl() *TypeTabl {
return &TypeTabl{
im: make(map[TypeID]*TypeChar),
tm: make(map[reflect.Type]*TypeChar),
}
}
func (tt *TypeTabl) Add(t *TypeChar) {
tt.lk.Lock()
defer tt.lk.Unlock()
if _, present := tt.im[t.ID]; present {
// panic("type id collision")
return
}
tt.im[t.ID] = t
tt.tm[t.Type] = t
}
func (tt *TypeTabl) TypeWithID(id TypeID) *TypeChar {
tt.lk.Lock()
defer tt.lk.Unlock()
return tt.im[id]
}
func (tt *TypeTabl) TypeOf(x interface{}) *TypeChar {
tt.lk.Lock()
defer tt.lk.Unlock()
return tt.tm[reflect.TypeOf(x)]
}
// TypeChar reflects on the methods of a Go type, and maintains exportable IDs
// for its methods
type TypeChar struct {
ID TypeID
name string
Type reflect.Type // Go type of the receiver
Func map[FuncID]*funcChar // Public methods
Proc map[string]*funcChar
}
// makeType makes a new type structure for the receiver's value type
func makeType(receiver interface{}) *TypeChar {
t := reflect.TypeOf(receiver)
//
var q []string
switch t.Kind() {
case reflect.Ptr:
q = []string{t.Elem().PkgPath(), t.Elem().Name(), t.String()}
default:
q = []string{t.PkgPath(), t.Name(), t.String()}
}
k := &TypeChar{
// XXX: According to Go's documentation, Type.String alone is not
// guaranteed to be a unique type identifier. We bet on including
// PkgPath and Name. While better, this is still not unique. Pointer
// types, *T, for example have PkgName equals "", Name equals "" and
// String equals the non-qualified name of the containing package plus
// the type name. Thus pointers to identically-named types in different
// packages identically named directories will collide.
ID: TypeID(sliceStringID64(q)),
name: fmt.Sprintf("%s·%s(%s)", q[0], q[1], q[2]),
Type: t,
Func: make(map[FuncID]*funcChar),
Proc: make(map[string]*funcChar),
}
// Reflect methods
for j := 0; j < t.NumMethod(); j++ {
m := t.Method(j)
if p := makeFunc(m, k); p != nil {
k.Func[p.ID] = p
k.Proc[m.Name] = p
}
}
return k
}
// Name returns the canonical name of this circuit type
func (t *TypeChar) Name() string {
return t.name
}
func (t *TypeChar) FuncWithID(id FuncID) *funcChar {
return t.Func[id]
}
// Zero returns a new zero value of the underlying type
func (t *TypeChar) Zero() reflect.Value {
return reflect.Zero(t.Type)
}
// New returns a Value representing a pointer to a new zero value for the underlying type.
func (t *TypeChar) New() reflect.Value {
return reflect.New(t.Type)
}
// MainID returns the ID of the first method in Func.
// It is useful for types that have exactly one method.
func (t *TypeChar) MainID() FuncID {
for id, _ := range t.Func {
return id
}
panic("no func")
}
// FuncID uniquely represents a method in the context of an interface type
type FuncID int32
type funcChar struct {
// ID is a collision resistent hash of the method's signature, which
// includes the method name, its arguments and its replies.
ID FuncID
Method reflect.Method
InTypes []reflect.Type
OutTypes []reflect.Type
}
func makeFunc(m reflect.Method, parent *TypeChar) *funcChar {
if m.PkgPath != "" {
// This is an unexported method
return nil
}
p := &funcChar{Method: m}
t := m.Type
// ID computation
var sign []string
sign = append(sign, p.Method.Name)
// Reflect arguments
// Note that the 0-th argument is the receiver value
for i := 1; i < t.NumIn(); i++ {
at := t.In(i)
if !isExportedOrBuiltinType(at) {
return nil
}
p.InTypes = append(p.InTypes, at)
sign = append(sign, at.Name())
gobFlattenRegister(at)
}
// End of argument types, beginning of reply types
sign = append(sign, "—")
// Reflect return values
for j := 0; j < t.NumOut(); j++ {
rt := t.Out(j)
if !isExportedOrBuiltinType(rt) {
return nil
}
p.OutTypes = append(p.OutTypes, rt)
sign = append(sign, rt.Name())
gobFlattenRegister(rt)
}
// Precompute ID
p.ID = FuncID(sliceStringID32(sign))
return p
}
// Is this an exported — upper case — name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}