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
typeFramestruct{
1414
// Back *Frame // previous frame, or nil
15-
Code*Code// code segment
16-
BuiltinsStringDict// builtin symbol table
17-
GlobalsStringDict// global symbol table
18-
LocalsStringDict// local symbol table
19-
Stack []Object// Valuestack
20-
ClosureTuple// Tuple of Cells that this function is closed over
15+
Code*Code// code segment
16+
BuiltinsStringDict// builtin symbol table
17+
GlobalsStringDict// global symbol table
18+
LocalsStringDict// local symbol table
19+
Stack []Object// Valuestack
20+
LocalVarsTuple// Fast access local vars
21+
CellAndFreeVarsTuple// 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
funcNewFrame(globals, localsStringDict, code*Code, closureTuple) *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
funcdo_LOAD_CLOSURE(vm*Vm, iint32){
10571060
defervm.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
funcdo_LOAD_DEREF(vm*Vm, iint32){
10681068
defervm.CheckException()
1069-
res:=vm.frame.Closure[i].(*py.Cell).Get()
1069+
res:=vm.frame.CellAndFreeVars[i].(*py.Cell).Get()
10701070
ifres==nil{
10711071
varname, free:=_var_name(vm, i)
10721072
iffree{
@@ -1470,6 +1470,32 @@ func RunFrame(frame *py.Frame) (res py.Object, err error){
14701470
funcRun(globals, locals py.StringDict, code*py.Code, closure py.Tuple) (res py.Object, errerror){
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+
fori:=rangecode.Cellvars{
1476+
varc py.Object
1477+
// Possibly account for the cell variable being an argument.
1478+
ifcode.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+
fori:=rangecode.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
ifcode.Flags&py.CO_GENERATOR!=0{

0 commit comments

Comments
(0)