1687. gopy code, frame, generator
What we are porting
Four files, ~6000 lines. The objects that exist because there is a bytecode interpreter:
Objects/codeobject.c:codeobject. Bytecode, consts, names, varnames, freevars, cellvars, line/exception tables, co_flags, co_qualname, co_name, co_filename, co_firstlineno. PEP 626 / 657 tables.Objects/frameobject.c:frameobject. The runtime activation record. f_back, f_code, f_locals, f_globals, f_builtins, f_lasti, f_lineno, f_trace, f_trace_lines, f_trace_opcodes. The PEP 558 fast-locals proxy.Objects/genobject.c:generator,coroutine,async_generatorstate machines.send,throw,close,__await__,__aiter__,__anext__.Objects/cellobject.c:cell. One slot, one ref. Used by the closure of a function for free variables.
Phasing
- v0.5: code object scaffold (already used by
compile.Compile). - v0.5.5: code object port to
objects/code.goproper. - v0.6: frame, generator, coroutine, async generator, cell. Lands together because the VM (1620 hot path) needs all four to run a real program.
Go shape
// Code mirrors PyCodeObject.
type Code struct {
VarHeader
Argcount int
PosOnlyArgcount int
KwOnlyArgcount int
Stacksize int
Flags uint32
Code []byte // bytecode
Consts *Tuple
Names *Tuple // *Str
Localsplus []localKind // flat 3.11+ layout
Localsplusnames *Tuple
Filename *Str
Name *Str
Qualname *Str
Firstlineno int
Linetable []byte // PEP 626
Exceptiontable []byte // PEP 657
Version uint32 // bumped on specialization
}
// Frame mirrors PyFrameObject. Allocated per call.
type Frame struct {
Header
Back *Frame
Code *Code
Builtins *Dict
Globals *Dict
Locals *Dict // lazy; promotion via PEP 558
Lasti int // -1 before first dispatch
Lineno int
Trace Object // tracing callback or None
Stack []Object // value stack
StackTop int
Localsplus []Object // flat fast / cell / free layout
Stop bool // set on return / raise
}
// Generator mirrors PyGenObject (and Coroutine, AsyncGenerator).
type Generator struct {
Header
Frame *Frame // suspended frame
Code *Code
Name *Str
Qualname *Str
State GenState // CREATED, RUNNING, SUSPENDED, CLOSED
Closing bool
Origin int // for coroutines: tracked via sys.set_coroutine_origin_tracking_depth
}
// Cell mirrors PyCellObject.
type Cell struct {
Header
Ref Object // may be nil (unbound)
}
PEP 558 fast-locals proxy
frame.f_locals does NOT directly mutate fast locals. Instead it
returns a snapshot dict; writes through the proxy update the dict,
which is then folded back into fast locals at controlled points
(trace function call, exception handling). 3.13+ semantics. Port
the proxy from frameobject.c:PyFrame_GetLocals.
Generator close semantics
gen.close() throws GeneratorExit into the suspended frame. If the
generator catches it and yields again, RuntimeError. If
GeneratorExit is suppressed in finally, run finalisation.
Mirrors gen_close_iter exactly.
Async generator athrow / aclose
async_generator.aclose() returns a coroutine that, when awaited,
performs the close. athrow is similar. The state machine in
genobject.c:async_gen_athrow_send lands here.
File mapping
| C source | Go target |
|---|---|
Objects/codeobject.c | objects/code.go |
| linetable / exceptiontable readers | objects/code_tables.go |
Objects/frameobject.c | objects/frame.go |
| f_locals proxy (PEP 558) | objects/frame_locals.go |
Objects/genobject.c generator | objects/gen.go |
| coroutine | objects/coroutine.go |
| async_generator | objects/async_gen.go |
Objects/cellobject.c | objects/cell.go |
Checklist
Status legend: [x] shipped, [ ] pending, [~] partial / scaffold,
[n] deferred / not in scope this phase.
Files (code, v0.5.5)
-
compile/code.go: scaffold used bycompile.Compile(v0.5). -
objects/code.go: full Code struct, replace_fields builders (replace), repr, hash (over the bytecode + consts + names + filename + name + firstlineno tuple). -
objects/code_tables.go:co_lines(),co_positions(), exception-table walker. -
objects/code_test.go: linetable round-trip, exception table round-trip, hash stability, replace().
Files (frame, v0.6)
-
objects/frame.go: Frame struct, allocation, reset,f_backwalk. -
objects/frame_locals.go: PEP 558 proxy, fold-back at trace entry / exception, fast/cell/free dispatch. -
objects/frame_test.go: f_locals visibility panel, f_lineno follows linetable.
Files (generator / coroutine / async-gen, v0.6)
-
objects/gen.go: Generator, send, throw, close, iter, iter_next, GeneratorExit handling. -
objects/coroutine.go: Coroutine, send, throw, close, await. -
objects/async_gen.go: AsyncGenerator, asend, athrow, aclose, the_PyAsyncGenAThrow/_PyAsyncGenASendhelper coroutines. -
objects/gen_test.go: state-machine panel, close semantics, async iteration.
Files (cell, v0.6)
-
objects/cell.go: Cell struct, MakeCell, get/set cell_contents, equality across cells.
Surface guarantees
-
code.replace(co_consts=())returns a new Code with the rest unchanged. -
code.co_lines()yields(start, end, line)triples matching CPython byte-for-byte for every linetable form. -
code.co_positions()yields the four-int tuples. -
frame.f_localsreturns a dict that mutates back into fast locals per PEP 558. -
gen.close()swallows GeneratorExit if the body completes; raises RuntimeError if the body re-yields. -
coroutine.__await__()returns the coroutine itself (matching CPython). -
async_gen.aclose()returns a coroutine; awaiting it performs the close. -
cell_contentsAttributeError when unbound. -
hash(code)is stable across equivalent code objects (same bytecode + consts + names + filename + name + firstlineno).
Cross-references
- Compile pipeline that builds Code: 1620 series.
- Function object that wraps Code: 1685.
- VM execution loop (creates Frames, drives Generators): 1620 hot path / future ceval spec.
Out of scope
- PEP 657 enriched-locations accuracy improvements beyond what 3.14 already offers.
- Free-threaded
dk_versionfor code object specialization. Lands in v0.14.