Skip to content

Commit 08eff15

Browse files
committed
Implement closures and LOAD_CLOSURE, LOAD_DEREF, MAKE_CLOSURE opcodes
1 parent c85199b commit 08eff15

File tree

6 files changed

+65
-32
lines changed

6 files changed

+65
-32
lines changed

builtin/builtin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ func builtin___build_class__(self py.Object, args py.Tuple, kwargs py.StringDict
278278
// fmt.Printf("Calling %v with %p and %p\n", fn.Name, fn.Globals, ns)
279279
// fmt.Printf("Code = %#v\n", fn.Code)
280280
locals := fn.LocalsForCall(py.Tuple{ns})
281-
cell, err := vm.Run(fn.Globals, locals, fn.Code) // FIXME PyFunction_GET_CLOSURE(fn))
281+
cell, err := vm.Run(fn.Globals, locals, fn.Code, fn.Closure)
282282

283283
// fmt.Printf("result = %#v err = %s\n", cell, err)
284284
// fmt.Printf("locals = %#v\n", locals)

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func main() {
6262
}
6363
code := obj.(*py.Code)
6464
module := py.NewModule("__main__", "", nil, nil)
65-
res, err := vm.Run(module.Globals, module.Globals, code)
65+
res, err := vm.Run(module.Globals, module.Globals, code, nil)
6666
if err != nil {
6767
log.Fatal(err)
6868
}

py/frame.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ type Frame struct {
1717
Globals StringDict // global symbol table
1818
Locals StringDict // local symbol table
1919
Stack []Object // Valuestack
20+
Closure Tuple // Tuple of Cells that this function is closed over
2021
// Next free slot in f_valuestack. Frame creation sets to f_valuestack.
2122
// Frame evaluation usually NULLs it, but a frame that yields sets it
2223
// to the current stack top.
2324
// Stacktop *Object
24-
Yielded bool // set if the function yielded, cleared otherwise
25-
Trace Object // Trace function
25+
Yielded bool // set if the function yielded, cleared otherwise
26+
// Trace Object // Trace function
2627

2728
// In a generator, we need to be able to swap between the exception
2829
// state inside the generator and the exception state of the calling
@@ -31,11 +32,11 @@ type Frame struct {
3132
// These three fields exist exactly for that, and are unused for
3233
// non-generator frames. See the save_exc_state and swap_exc_state
3334
// functions in ceval.c for details of their use.
34-
Exc_type Object
35-
Exc_value *Object
36-
Exc_traceback *Object
35+
// Exc_type Object
36+
// Exc_value *Object
37+
// Exc_traceback *Object
3738
// Borrowed reference to a generator, or NULL
38-
Gen Object
39+
// Gen Object
3940

4041
// FIXME Tstate *PyThreadState
4142
Lasti int32 // Last instruction if called
@@ -44,12 +45,12 @@ type Frame struct {
4445
// active (i.e. when f_trace is set). At other times we use
4546
// PyCode_Addr2Line to calculate the line from the current
4647
// bytecode index.
47-
Lineno int // Current line number
48-
Iblock int // index in f_blockstack
49-
Executing byte // whether the frame is still executing
48+
// Lineno int // Current line number
49+
// Iblock int // index in f_blockstack
50+
// Executing byte // whether the frame is still executing
5051
Blockstack []TryBlock // for try and loop blocks
5152
Block *TryBlock // pointer to current block or nil
52-
Localsplus []Object // locals+stack, dynamically sized
53+
// Localsplus []Object // locals+stack, dynamically sized
5354
}
5455

5556
var FrameType = NewType("frame", "Represents a stack frame")
@@ -60,11 +61,12 @@ func (o *Frame) Type() *Type {
6061
}
6162

6263
// Make a new frame for a code object
63-
func NewFrame(globals, locals StringDict, code *Code) *Frame {
64+
func NewFrame(globals, locals StringDict, code *Code, closure Tuple) *Frame {
6465
return &Frame{
6566
Globals: globals,
6667
Locals: locals,
6768
Code: code,
69+
Closure: closure,
6870
Builtins: Builtins.Globals,
6971
Stack: make([]Object, 0, code.Stacksize),
7072
}

py/function.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (f *Function) M__call__(args Tuple, kwargs StringDict) Object {
123123
} else {
124124
locals = f.LocalsForCall(args)
125125
}
126-
result, err := Run(f.Globals, locals, f.Code)
126+
result, err := Run(f.Globals, locals, f.Code, f.Closure)
127127
if err != nil {
128128
// Propagate the error
129129
panic(err)

py/py.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Object interface {
1010
var (
1111
Ellipsis Object
1212
// See vm/eval.go - set to avoid circular import
13-
Run func(globals, locals StringDict, code *Code) (res Object, err error)
13+
Run func(globals, locals StringDict, code *Code, closure Tuple) (res Object, err error)
1414
RunFrame func(frame *Frame) (res Object, err error)
1515
)
1616

vm/eval.go

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,21 +1005,45 @@ func do_DELETE_FAST(vm *Vm, var_num int32) {
10051005
}
10061006
}
10071007

1008+
// Name of slot for LOAD_CLOSURE / LOAD_DEREF / etc
1009+
//
1010+
// Returns name of variable and bool, true for free var, false for
1011+
// cell var
1012+
func _var_name(vm *Vm, i int32) (string, bool) {
1013+
cellvars := vm.frame.Code.Cellvars
1014+
if int(i) < len(cellvars) {
1015+
return cellvars[i], false
1016+
}
1017+
return vm.frame.Code.Freevars[int(i)-len(cellvars)], true
1018+
}
1019+
10081020
// Pushes a reference to the cell contained in slot i of the cell and
10091021
// free variable storage. The name of the variable is co_cellvars[i]
10101022
// if i is less than the length of co_cellvars. Otherwise it is
10111023
// co_freevars[i - len(co_cellvars)].
10121024
func do_LOAD_CLOSURE(vm *Vm, i int32) {
10131025
defer vm.CheckException()
1014-
vm.NotImplemented("LOAD_CLOSURE", i)
1026+
varname, _ := _var_name(vm, i)
1027+
// FIXME this is making a new cell each time rather than
1028+
// returning a reference to an old one
1029+
vm.PUSH(py.NewCell(vm.frame.Locals[varname]))
10151030
}
10161031

10171032
// Loads the cell contained in slot i of the cell and free variable
10181033
// storage. Pushes a reference to the object the cell contains on the
10191034
// stack.
10201035
func do_LOAD_DEREF(vm *Vm, i int32) {
10211036
defer vm.CheckException()
1022-
vm.NotImplemented("LOAD_DEREF", i)
1037+
res := vm.frame.Closure[i].(*py.Cell).Get()
1038+
if res == nil {
1039+
varname, free := _var_name(vm, i)
1040+
if free {
1041+
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundFreeErrorMsg, varname))
1042+
} else {
1043+
vm.SetException(py.ExceptionNewf(py.UnboundLocalError, unboundLocalErrorMsg, varname))
1044+
}
1045+
}
1046+
vm.PUSH(res)
10231047
}
10241048

10251049
// Much like LOAD_DEREF but first checks the locals dictionary before
@@ -1111,24 +1135,18 @@ func do_CALL_FUNCTION(vm *Vm, argc int32) {
11111135
vm.Call(fn, args, kwargs)
11121136
}
11131137

1114-
// Pushes a new function object on the stack. TOS is the code
1115-
// associated with the function. The function object is defined to
1116-
// have argc default parameters, which are found below TOS.
1117-
//
1118-
// FIXME these docs are slightly wrong.
1119-
func do_MAKE_FUNCTION(vm *Vm, argc int32) {
1120-
defer vm.CheckException()
1138+
// Implementation for MAKE_FUNCTION and MAKE_CLOSURE
1139+
func _make_function(vm *Vm, argc int32, opcode byte) {
11211140
posdefaults := argc & 0xff
11221141
kwdefaults := (argc >> 8) & 0xff
11231142
num_annotations := (argc >> 16) & 0x7fff
11241143
qualname := vm.POP()
11251144
code := vm.POP()
11261145
function := py.NewFunction(code.(*py.Code), vm.frame.Globals, string(qualname.(py.String)))
11271146

1128-
// FIXME share code with MAKE_CLOSURE
1129-
// if opcode == MAKE_CLOSURE {
1130-
// function.Closure = vm.POP();
1131-
// }
1147+
if opcode == MAKE_CLOSURE {
1148+
function.Closure = vm.POP().(py.Tuple)
1149+
}
11321150

11331151
if num_annotations > 0 {
11341152
names := vm.POP().(py.Tuple) // names of args with annotations
@@ -1167,15 +1185,24 @@ func do_MAKE_FUNCTION(vm *Vm, argc int32) {
11671185
vm.PUSH(function)
11681186
}
11691187

1188+
// Pushes a new function object on the stack. TOS is the code
1189+
// associated with the function. The function object is defined to
1190+
// have argc default parameters, which are found below TOS.
1191+
//
1192+
// FIXME these docs are slightly wrong.
1193+
func do_MAKE_FUNCTION(vm *Vm, argc int32) {
1194+
defer vm.CheckException()
1195+
_make_function(vm, argc, MAKE_FUNCTION)
1196+
}
1197+
11701198
// Creates a new function object, sets its func_closure slot, and
11711199
// pushes it on the stack. TOS is the code associated with the
11721200
// function, TOS1 the tuple containing cells for the closure’s free
11731201
// variables. The function also has argc default parameters, which are
11741202
// found below the cells.
11751203
func do_MAKE_CLOSURE(vm *Vm, argc int32) {
11761204
defer vm.CheckException()
1177-
vm.NotImplemented("MAKE_CLOSURE", argc)
1178-
// see MAKE_FUNCTION
1205+
_make_function(vm, argc, MAKE_CLOSURE)
11791206
}
11801207

11811208
// Pushes a slice object on the stack. argc must be 2 or 3. If it is
@@ -1278,6 +1305,8 @@ func (vm *Vm) UnwindExceptHandler(frame *py.Frame, block *py.TryBlock) {
12781305
// FIXME figure out how we are going to signal exceptions!
12791306
//
12801307
// Returns an Object and an error. The error will be a py.ExceptionInfo
1308+
//
1309+
// This is the equivalent of PyEval_EvalFrame
12811310
func RunFrame(frame *py.Frame) (res py.Object, err error) {
12821311
vm := NewVm(frame)
12831312
// defer func() {
@@ -1412,8 +1441,10 @@ func RunFrame(frame *py.Frame) (res py.Object, err error) {
14121441
// Any parameters are expected to have been decoded into locals
14131442
//
14141443
// Returns an Object and an error. The error will be a py.ExceptionInfo
1415-
func Run(globals, locals py.StringDict, code *py.Code) (res py.Object, err error) {
1416-
frame := py.NewFrame(globals, locals, code)
1444+
//
1445+
// This is the equivalent of PyEval_EvalCode with closure support
1446+
func Run(globals, locals py.StringDict, code *py.Code, closure py.Tuple) (res py.Object, err error) {
1447+
frame := py.NewFrame(globals, locals, code, closure)
14171448

14181449
// If this is a generator then make a generator object from
14191450
// the frame and return that instead

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy