-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathframe.go
299 lines (268 loc) · 8.73 KB
/
frame.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// Copyright 2018 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Frame objects
package py
// What kind of block this is
type TryBlockType byte
const (
TryBlockSetupLoop TryBlockType = iota
TryBlockSetupExcept
TryBlockSetupFinally
TryBlockExceptHandler
)
// Store information about try blocks
type TryBlock struct {
Type TryBlockType // what kind of block this is
Handler int32 // where to jump to find handler
Level int // value stack level to pop to
}
// A python Frame object
type Frame struct {
// Back *Frame // previous frame, or nil
Context Context // host module (state) context
Code *Code // code segment
Builtins StringDict // builtin symbol table
Globals StringDict // global symbol table
Locals StringDict // local symbol table
Stack []Object // Valuestack
LocalVars Tuple // Fast access local vars
CellAndFreeVars Tuple // Cellvars then Freevars Cell objects in one Tuple
// Next free slot in f_valuestack. Frame creation sets to f_valuestack.
// Frame evaluation usually NULLs it, but a frame that yields sets it
// to the current stack top.
// Stacktop *Object
Yielded bool // set if the function yielded, cleared otherwise
// Trace Object // Trace function
// In a generator, we need to be able to swap between the exception
// state inside the generator and the exception state of the calling
// frame (which shouldn't be impacted when the generator "yields"
// from an except handler).
// These three fields exist exactly for that, and are unused for
// non-generator frames. See the save_exc_state and swap_exc_state
// functions in ceval.c for details of their use.
// Exc_type Object
// Exc_value *Object
// Exc_traceback *Object
// Borrowed reference to a generator, or NULL
// Gen Object
// FIXME Tstate *PyThreadState
Lasti int32 // Last instruction if called
// Call PyFrame_GetLineNumber() instead of reading this field
// directly. As of 2.3 f_lineno is only valid when tracing is
// active (i.e. when f_trace is set). At other times we use
// PyCode_Addr2Line to calculate the line from the current
// bytecode index.
// Lineno int // Current line number
// Iblock int // index in f_blockstack
// Executing byte // whether the frame is still executing
Blockstack []TryBlock // for try and loop blocks
Block *TryBlock // pointer to current block or nil
Localsplus []Object // LocalVars + CellAndFreeVars
}
var FrameType = NewType("frame", "Represents a stack frame")
// Type of this object
func (o *Frame) Type() *Type {
return FrameType
}
// Make a new frame for a code object
func NewFrame(ctx Context, globals, locals StringDict, code *Code, closure Tuple) *Frame {
nlocals := int(code.Nlocals)
ncells := len(code.Cellvars)
nfrees := len(code.Freevars)
varsize := nlocals + ncells + nfrees
// Allocate the stack, locals, cells and frees in a contigious block of memory
allocation := make([]Object, varsize)
localVars := allocation[:nlocals]
//cellVars := allocation[nlocals : nlocals+ncells]
//freeVars := allocation[nlocals+ncells : varsize]
cellAndFreeVars := allocation[nlocals:varsize]
return &Frame{
Context: ctx,
Globals: globals,
Locals: locals,
Code: code,
LocalVars: localVars,
CellAndFreeVars: cellAndFreeVars,
Builtins: ctx.Store().Builtins.Globals,
Localsplus: allocation,
Stack: make([]Object, 0, code.Stacksize),
}
}
// Python globals are looked up in two scopes
//
// The module global scope
// And finally the builtins
func (f *Frame) LookupGlobal(name string) (obj Object, ok bool) {
// Lookup in globals
// fmt.Printf("globals = %v\n", f.Globals)
if obj, ok = f.Globals[name]; ok {
return
}
// Lookup in builtins
// fmt.Printf("builtins = %v\n", Builtins.Globals)
if obj, ok = f.Builtins[name]; ok {
return
}
return nil, false
}
// Python names are looked up in three scopes
//
// First the local scope
// Next the module global scope
// And finally the builtins
func (f *Frame) Lookup(name string) (obj Object, ok bool) {
// Lookup in locals
// fmt.Printf("locals = %v\n", f.Locals)
if obj, ok = f.Locals[name]; ok {
return
}
return f.LookupGlobal(name)
}
// Make a new Block (try/for/while)
func (f *Frame) PushBlock(Type TryBlockType, Handler int32, Level int) {
f.Blockstack = append(f.Blockstack, TryBlock{
Type: Type,
Handler: Handler,
Level: Level,
})
f.Block = &f.Blockstack[len(f.Blockstack)-1]
}
// Pop the current block off
func (f *Frame) PopBlock() {
f.Blockstack = f.Blockstack[:len(f.Blockstack)-1]
if len(f.Blockstack) > 0 {
f.Block = &f.Blockstack[len(f.Blockstack)-1]
} else {
f.Block = nil
}
}
/*
Convert between "fast" version of locals and dictionary version.
map and values are input arguments. map is a tuple of strings.
values is an array of PyObject*. At index i, map[i] is the name of
the variable with value values[i]. The function copies the first
nmap variable from map/values into dict. If values[i] is NULL,
the variable is deleted from dict.
If deref is true, then the values being copied are cell variables
and the value is extracted from the cell variable before being put
in dict.
*/
func map_to_dict(mapping []string, nmap int, dict StringDict, values []Object, deref bool) {
for j := nmap - 1; j >= 0; j-- {
key := mapping[j]
value := values[j]
if deref && value != nil {
cell, ok := value.(*Cell)
if !ok {
panic("map_to_dict: expecting Cell")
}
value = cell.Get()
}
if value == nil {
delete(dict, key)
} else {
dict[key] = value
}
}
}
/*
Copy values from the "locals" dict into the fast locals.
dict is an input argument containing string keys representing
variables names and arbitrary PyObject* as values.
mapping and values are input arguments. mapping is a tuple of strings.
values is an array of PyObject*. At index i, mapping[i] is the name of
the variable with value values[i]. The function copies the first
nmap variable from mapping/values into dict. If values[i] is nil,
the variable is deleted from dict.
If deref is true, then the values being copied are cell variables
and the value is extracted from the cell variable before being put
in dict. If clear is true, then variables in mapping but not in dict
are set to nil in mapping; if clear is false, variables missing in
dict are ignored.
Exceptions raised while modifying the dict are silently ignored,
because there is no good way to report them.
*/
func dict_to_map(mapping []string, nmap int, dict StringDict, values []Object, deref bool, clear bool) {
for j := nmap - 1; j >= 0; j-- {
key := mapping[j]
value := dict[key]
/* We only care about nils if clear is true. */
if value == nil {
if !clear {
continue
}
}
if deref {
cell, ok := values[j].(*Cell)
if !ok {
panic("dict_to_map: expecting Cell")
}
if cell.Get() != value {
cell.Set(value)
}
} else if values[j] != value {
values[j] = value
}
}
}
// Merge fast locals into frame Locals
func (f *Frame) FastToLocals() {
locals := f.Locals
if locals == nil {
locals = NewStringDict()
f.Locals = locals
}
co := f.Code
mapping := co.Varnames
fast := f.Localsplus
j := len(mapping)
if j > int(co.Nlocals) {
j = int(co.Nlocals)
}
if co.Nlocals != 0 {
map_to_dict(mapping, j, locals, fast, false)
}
ncells := len(co.Cellvars)
nfreevars := len(co.Freevars)
if ncells != 0 || nfreevars != 0 {
map_to_dict(co.Cellvars, ncells, locals, fast[co.Nlocals:], true)
/* If the namespace is unoptimized, then one of the
following cases applies:
1. It does not contain free variables, because it
uses import * or is a top-level namespace.
2. It is a class namespace.
We don't want to accidentally copy free variables
into the locals dict used by the class.
*/
if co.Flags&CO_OPTIMIZED != 0 {
map_to_dict(co.Freevars, nfreevars, locals, fast[int(co.Nlocals)+ncells:], true)
}
}
}
// Merge frame Locals into fast locals
func (f *Frame) LocalsToFast(clear bool) {
locals := f.Locals
co := f.Code
mapping := co.Varnames
if locals == nil {
return
}
fast := f.Localsplus
j := len(mapping)
if j > int(co.Nlocals) {
j = int(co.Nlocals)
}
if co.Nlocals != 0 {
dict_to_map(co.Varnames, j, locals, fast, false, clear)
}
ncells := len(co.Cellvars)
nfreevars := len(co.Freevars)
if ncells != 0 || nfreevars != 0 {
dict_to_map(co.Cellvars, ncells, locals, fast[co.Nlocals:], true, clear)
/* Same test as in FastToLocals() above. */
if co.Flags&CO_OPTIMIZED != 0 {
dict_to_map(co.Freevars, nfreevars, locals, fast[int(co.Nlocals)+ncells:], true, clear)
}
}
}