Skip to content

Commit 1827c0c

Browse files
committed
Fix Free and Cell variables to make closures work properly
1 parent 103d54d commit 1827c0c

File tree

2 files changed

+58
-17
lines changed

2 files changed

+58
-17
lines changed

py/frame.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ type TryBlock struct {
1212
// A python Frame object
1313
type Frame struct {
1414
// Back *Frame // previous frame, or nil
15-
Code *Code // code segment
16-
Builtins StringDict // builtin symbol table
17-
Globals StringDict // global symbol table
18-
Locals StringDict // local symbol table
19-
Stack []Object // Valuestack
20-
Closure Tuple // Tuple of Cells that this function is closed over
15+
Code *Code // code segment
16+
Builtins StringDict // builtin symbol table
17+
Globals StringDict // global symbol table
18+
Locals StringDict // local symbol table
19+
Stack []Object // Valuestack
20+
LocalVars Tuple // Fast access local vars
21+
CellAndFreeVars Tuple // Cellvars then Freevars Cell objects in one Tuple
22+
2123
// Next free slot in f_valuestack. Frame creation sets to f_valuestack.
2224
// Frame evaluation usually NULLs it, but a frame that yields sets it
2325
// to the current stack top.
@@ -62,13 +64,26 @@ func (o *Frame) Type() *Type {
6264

6365
// Make a new frame for a code object
6466
func NewFrame(globals, locals StringDict, code *Code, closure Tuple) *Frame {
67+
nlocals := int(code.Nlocals)
68+
ncells := len(code.Cellvars)
69+
nfrees := len(code.Freevars)
70+
varsize := nlocals + ncells + nfrees
71+
size := varsize + int(code.Stacksize)
72+
// Allocate the stack, locals, cells and frees in a contigious block of memory
73+
allocation := make([]Object, varsize, size)
74+
localVars := allocation[0:nlocals]
75+
//cellVars := allocation[nlocals : nlocals+ncells]
76+
//freeVars := allocation[nlocals+ncells : varsize]
77+
cellAndFreeVars := allocation[nlocals:varsize]
78+
6579
return &Frame{
66-
Globals: globals,
67-
Locals: locals,
68-
Code: code,
69-
Closure: closure,
70-
Builtins: Builtins.Globals,
71-
Stack: make([]Object, 0, code.Stacksize),
80+
Globals: globals,
81+
Locals: locals,
82+
Code: code,
83+
LocalVars: localVars,
84+
CellAndFreeVars: cellAndFreeVars,
85+
Builtins: Builtins.Globals,
86+
Stack: make([]Object, 0, code.Stacksize),
7287
}
7388
}
7489

vm/eval.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Evaluate opcodes
22
package vm
33

4+
// FIXME use LocalVars instead of storing everything in the Locals dict
5+
// see frameobject.c dict_to_map and LocalsToFast
6+
47
/* FIXME
58
69
cpython has one stack per frame, not one stack in total
@@ -1055,18 +1058,15 @@ func _var_name(vm *Vm, i int32) (string, bool) {
10551058
// co_freevars[i - len(co_cellvars)].
10561059
func do_LOAD_CLOSURE(vm *Vm, i int32) {
10571060
defer vm.CheckException()
1058-
varname, _ := _var_name(vm, i)
1059-
// FIXME this is making a new cell each time rather than
1060-
// returning a reference to an old one
1061-
vm.PUSH(py.NewCell(vm.frame.Locals[varname]))
1061+
vm.PUSH(vm.frame.CellAndFreeVars[i])
10621062
}
10631063

10641064
// Loads the cell contained in slot i of the cell and free variable
10651065
// storage. Pushes a reference to the object the cell contains on the
10661066
// stack.
10671067
func do_LOAD_DEREF(vm *Vm, i int32) {
10681068
defer vm.CheckException()
1069-
res := vm.frame.Closure[i].(*py.Cell).Get()
1069+
res := vm.frame.CellAndFreeVars[i].(*py.Cell).Get()
10701070
if res == nil {
10711071
varname, free := _var_name(vm, i)
10721072
if free {
@@ -1470,6 +1470,32 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
14701470
func Run(globals, locals py.StringDict, code *py.Code, closure py.Tuple) (res py.Object, err error) {
14711471
frame := py.NewFrame(globals, locals, code, closure)
14721472

1473+
// Allocate and initialize storage for cell vars, and copy free
1474+
// vars into frame.
1475+
for i := range code.Cellvars {
1476+
var c py.Object
1477+
// Possibly account for the cell variable being an argument.
1478+
if code.Cell2arg != nil && code.Cell2arg[i] != py.CO_CELL_NOT_AN_ARG {
1479+
arg := code.Cell2arg[i]
1480+
// FIXME if use localvars need to change this
1481+
// c = NewCell(GETLOCAL(arg))
1482+
// // Clear the local copy.
1483+
// SETLOCAL(arg, nil)
1484+
varname := code.Varnames[arg]
1485+
c = py.NewCell(locals[varname])
1486+
} else {
1487+
c = py.NewCell(nil)
1488+
}
1489+
//SETLOCAL(code.nlocals+i, c)
1490+
frame.CellAndFreeVars[i] = c
1491+
}
1492+
ncells := len(code.Cellvars)
1493+
for i := range code.Freevars {
1494+
//PyObject * o = PyTuple_GET_ITEM(closure, i)
1495+
//freevars[PyTuple_GET_SIZE(code.cellvars)+i] = o
1496+
frame.CellAndFreeVars[i+ncells] = closure[i]
1497+
}
1498+
14731499
// If this is a generator then make a generator object from
14741500
// the frame and return that instead
14751501
if code.Flags&py.CO_GENERATOR != 0 {

0 commit comments

Comments
 (0)