File: scope.go

package info (click to toggle)
golang-github-traefik-yaegi 0.16.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 24,608 kB
  • sloc: sh: 457; makefile: 39
file content (253 lines) | stat: -rw-r--r-- 7,202 bytes parent folder | download | duplicates (2)
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
244
245
246
247
248
249
250
251
252
253
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
	varTypeSym       // Variable type (generic)
	varSym           // Variable
)

var symKinds = [...]string{
	undefSym:   "undefSym",
	binSym:     "binSym",
	bltnSym:    "bltnSym",
	constSym:   "constSym",
	funcSym:    "funcSym",
	labelSym:   "labelSym",
	pkgSym:     "pkgSym",
	typeSym:    "typeSym",
	varTypeSym: "varTypeSym",
	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 goto nodes jumping to this label node, 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
}

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
}

// Globals returns a map of global variables and constants in the main package.
func (interp *Interpreter) Globals() map[string]reflect.Value {
	syms := map[string]reflect.Value{}
	interp.mutex.RLock()
	defer interp.mutex.RUnlock()

	v, ok := interp.srcPkg["main"]
	if !ok {
		return syms
	}

	for n, s := range v {
		switch s.kind {
		case constSym:
			syms[n] = s.rval
		case varSym:
			syms[n] = interp.frame.data[s.index]
		}
	}

	return syms
}