forked from traefik/yaegi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scope.go
243 lines (222 loc) · 6.87 KB
/
scope.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package interp
import (
"log"
"reflect"
"strconv"
)
// A sKind represents the kind of symbol.
type sKind uint
// Symbol kinds for the Go interpreter.
const (
undefSym sKind = iota
binSym // Binary from runtime
bltnSym // Builtin
constSym // Constant
funcSym // Function
labelSym // Label
pkgSym // Package
typeSym // Type
varSym // Variable
)
var symKinds = [...]string{
undefSym: "undefSym",
binSym: "binSym",
bltnSym: "bltnSym",
constSym: "constSym",
funcSym: "funcSym",
labelSym: "labelSym",
pkgSym: "pkgSym",
typeSym: "typeSym",
varSym: "varSym",
}
func (k sKind) String() string {
if k < sKind(len(symKinds)) {
return symKinds[k]
}
return "SymKind(" + strconv.Itoa(int(k)) + ")"
}
// A symbol represents an interpreter object such as type, constant, var, func,
// label, builtin or binary object. Symbols are defined within a scope.
type symbol struct {
kind sKind
typ *itype // Type of value
node *node // Node value if index is negative
from []*node // list of nodes jumping to node if kind is label, or nil
recv *receiver // receiver node value, if sym refers to a method
index int // index of value in frame or -1
rval reflect.Value // default value (used for constants)
builtin bltnGenerator // Builtin function or nil
global bool // true if symbol is defined in global space
}
// scope type stores symbols in maps, and frame layout as array of types
// The purposes of scopes are to manage the visibility of each symbol
// and to store the memory frame layout information (type and index in frame)
// at each level (global, package, functions)
//
// scopes are organized in a stack fashion: a first scope (universe) is created
// once at global level, and for each block (package, func, for, etc...), a new
// scope is pushed at entry, and poped at exit.
//
// Nested scopes with the same level value use the same frame: it allows to have
// exactly one frame per function, with a fixed position for each variable (named
// or not), no matter the inner complexity (number of nested blocks in the function)
//
// In symbols, the index value corresponds to the index in scope.types, and at
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // ancestor upper scope
child []*scope // included scopes
def *node // function definition node this scope belongs to, or nil
loop *node // loop exit node for break statement
loopRestart *node // loop restart node for continue statement
pkgID string // unique id of package in which scope is defined
pkgName string // package name for the package
types []reflect.Type // frame layout, may be shared by same level scopes
level int // frame level: number of frame indirections to access var during execution
sym map[string]*symbol // map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
iota int // iota value in this scope
}
// push creates a new child scope and chain it to the current one.
func (s *scope) push(indirect bool) *scope {
sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
s.child = append(s.child, sc)
if indirect {
sc.types = []reflect.Type{}
sc.level = s.level + 1
} else {
// Propagate size, types, def and global as scopes at same level share the same frame.
sc.types = s.types
sc.def = s.def
sc.global = s.global
sc.level = s.level
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return sc
}
func (s *scope) pushBloc() *scope { return s.push(false) }
func (s *scope) pushFunc() *scope { return s.push(true) }
func (s *scope) pop() *scope {
if s.level == s.anc.level {
// Propagate size and types, as scopes at same level share the same frame.
s.anc.types = s.types
}
return s.anc
}
func (s *scope) upperLevel() *scope {
level := s.level
for s != nil && s.level == level {
s = s.anc
}
return s
}
// lookup searches for a symbol in the current scope, and upper ones if not found
// it returns the symbol, the number of indirections level from the current scope
// and status (false if no result).
func (s *scope) lookup(ident string) (*symbol, int, bool) {
level := s.level
for {
if sym, ok := s.sym[ident]; ok {
if sym.global {
return sym, globalFrame, true
}
return sym, level - s.level, true
}
if s.anc == nil {
break
}
s = s.anc
}
return nil, 0, false
}
// lookdown searches for a symbol in the current scope and included ones, recursively.
// It returns the first found symbol and true, or nil and false.
func (s *scope) lookdown(ident string) (*symbol, bool) {
if sym, ok := s.sym[ident]; ok {
return sym, true
}
for _, c := range s.child {
if sym, ok := c.lookdown(ident); ok {
return sym, true
}
}
return nil, false
}
func (s *scope) rangeChanType(n *node) *itype {
if sym, _, found := s.lookup(n.child[1].ident); found {
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
return t
}
}
c := n.child[1]
if c.typ == nil {
return nil
}
switch {
case c.typ.cat == chanT, c.typ.cat == chanRecvT:
return c.typ
case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
dir := chanSendRecv
switch c.typ.rtype.ChanDir() {
case reflect.RecvDir:
dir = chanRecv
case reflect.SendDir:
dir = chanSend
}
return chanOf(valueTOf(c.typ.rtype.Elem()), dir)
}
return nil
}
// fixType returns the input type, or a valid default type for untyped constant.
func (s *scope) fixType(t *itype) *itype {
if !t.untyped || t.cat != valueT {
return t
}
switch typ := t.TypeOf(); typ.Kind() {
case reflect.Int64:
return s.getType("int")
case reflect.Uint64:
return s.getType("uint")
case reflect.Float64:
return s.getType("float64")
case reflect.Complex128:
return s.getType("complex128")
}
return t
}
func (s *scope) getType(ident string) *itype {
var t *itype
if sym, _, found := s.lookup(ident); found {
if sym.kind == typeSym {
t = sym.typ
}
}
return t
}
// add adds a type to the scope types array, and returns its index.
func (s *scope) add(typ *itype) (index int) {
if typ == nil {
log.Panic("nil type")
}
index = len(s.types)
t := typ.frameType()
if t == nil {
log.Panic("nil reflect type")
}
s.types = append(s.types, t)
return
}
func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
sc := interp.universe
interp.mutex.Lock()
if _, ok := interp.scopes[pkgID]; !ok {
interp.scopes[pkgID] = sc.pushBloc()
}
sc = interp.scopes[pkgID]
sc.pkgID = pkgID
sc.pkgName = pkgName
interp.mutex.Unlock()
return sc
}