Documentation
¶
Overview ¶
Package machine implements the Scheme virtual machine, compiler, and macro expander.
The machine package is the core execution engine for Wile, providing:
Compilation Pipeline ¶
Scheme source undergoes three phases:
- Expansion: macro-expand via compilation.ExpanderTimeContinuation
- Compilation: generate bytecode via compilation.CompileTimeContinuation
- Execution: run bytecode via MachineContext
Virtual Machine ¶
The VM is a stack-based bytecode virtual machine with:
- MachineContext: execution state (value register, eval stack, environment, PC)
- MachineContext.Run: main execution loop
- NativeTemplate: compiled bytecode container
- MachineClosure: first-class procedure representation
Bytecode Assembly ¶
NewNativeTemplate, NativeTemplate.AppendInstruction, NativeTemplate.AppendOperations, NativeTemplate.AppendSideTableOp and NativeTemplate.PatchInstructionArg are a trusted-producer surface, exported for the compiler and for test fixtures. The instruction stream is taken as well-formed: an out-of-range Arg (a local-binding index, a branch offset, a side-table index) is not validated and reaches the dispatch loop as an unchecked slice index.
The resulting panic is contained only on entry via MachineContext.RunResumable, which recovers at the VM boundary and returns it as an error. MachineContext.Run and MachineContext.RunWithinBoundary do not recover, so a malformed template assembled through this surface unwinds into the host goroutine.
Continuations ¶
- MachineContinuation: captured continuation state for call/cc
- ComposableContinuation: delimited continuations for prompts
- dynamic-wind support via wind/unwind stacks
Macro System ¶
Implements R7RS hygienic macros with Flatt's "sets of scopes" model:
- compilation.ExpanderTimeContinuation: macro expansion driver
- syntax-rules pattern matching via internal/match
- syntax-case with fenders and procedural macros
Foreign Functions ¶
Go functions are exposed as ForeignFunction implementations:
type ForeignFunction func(CallContext) error
CallContext is the narrow interface (8 methods) that extensions depend on. The ForeignClosure type wraps these for the VM.
Index ¶
- Constants
- Variables
- func ConditionFromError(err error) values.Value
- func DecodeLocalIndex(arg int32) (slot, depth int)
- func DecodeMakeClosure(arg int32) (freeCount, selfSlot int)
- func DecodeSelfTailCall(arg int32) (argCount, popCount int)
- func DisassembleString(tpl *NativeTemplate) string
- func EncodeLocalIndex(li *environment.LocalIndex) int32
- func EncodeMakeClosure(freeCount, selfSlot int) int32
- func EncodeSelfTailCall(argCount, popCount int) int32
- func ErrorContextKey() values.Value
- func ExceptionHandlerParam() values.Value
- func FieldMatches[T comparable, Op any](p, v *Op, ok bool, getField func(*Op) T) bool
- func FindCommonWindingPrefix(current, target WindingStack) int
- func GraftContinuation(segment, target *MachineContinuation)
- func PromotedMutatorNames() []string
- func RaiseInPlace(mc *MachineContext, cond values.Value, continuable bool) error
- func ReleaseSubContext(mc *MachineContext)
- func ReleaseTopLevelContext(mc *MachineContext)
- func SameType[T any](p, v *T, ok bool) bool
- func SetCallCounting(on bool)
- func SliceMatches[T comparable, Op any](p, v *Op, ok bool, getField func(*Op) []T) bool
- func StackTraceToSchemeList(st StackTrace) values.Tuple
- type BarrierToken
- type BoxedValues
- type BreakSnapshot
- type Breakpoint
- type BreakpointID
- type CallContext
- type CapturedContinuation
- type CaseLambdaClosure
- func (p *CaseLambdaClosure) AcceptsArity(n int) bool
- func (p *CaseLambdaClosure) Clauses() []*MachineClosure
- func (p *CaseLambdaClosure) Doc() string
- func (p *CaseLambdaClosure) EqualTo(o values.Value) bool
- func (p *CaseLambdaClosure) FindMatchingClause(argCount int) (*MachineClosure, bool)
- func (p *CaseLambdaClosure) IsVoid() bool
- func (p *CaseLambdaClosure) Name() string
- func (p *CaseLambdaClosure) SchemeString() string
- type Closure
- type ComposableContinuation
- func (*ComposableContinuation) AcceptsArity(int) bool
- func (p *ComposableContinuation) AcquireSegment() *MachineContinuation
- func (p *ComposableContinuation) BarrierValid() *BarrierToken
- func (p *ComposableContinuation) Cont() *MachineContinuation
- func (p *ComposableContinuation) EqualTo(o values.Value) bool
- func (p *ComposableContinuation) IsVoid() bool
- func (p *ComposableContinuation) SchemeString() string
- func (p *ComposableContinuation) WindingStack() WindingStack
- type ContinuationMarkSet
- func (p *ContinuationMarkSet) EqualTo(o values.Value) bool
- func (p *ContinuationMarkSet) First(key, defaultVal values.Value) values.Value
- func (p *ContinuationMarkSet) IsVoid() bool
- func (p *ContinuationMarkSet) SchemeString() string
- func (p *ContinuationMarkSet) ToList(key values.Value) values.Tuple
- func (p *ContinuationMarkSet) ToListStar(keys []values.Value, noneVal values.Value) values.Tuple
- type Debugger
- func (p *Debugger) BreakState() (values.DebugState, *Breakpoint)
- func (p *Debugger) Breakpoints() []*Breakpoint
- func (p *Debugger) CanStop() bool
- func (p *Debugger) CheckBreakpoint(mc *MachineContext) *Breakpoint
- func (p *Debugger) Continue()
- func (p *Debugger) DisableBreakpoint(id BreakpointID) bool
- func (p *Debugger) EnableBreakpoint(id BreakpointID) bool
- func (p *Debugger) IsStepping() bool
- func (p *Debugger) OnBreak(fn func(mc *MachineContext, bp *Breakpoint))
- func (p *Debugger) RemoveBreakpoint(id BreakpointID) bool
- func (p *Debugger) SetBreakpoint(file string, line, column int) BreakpointID
- func (p *Debugger) ShouldStep(mc *MachineContext) bool
- func (p *Debugger) StepInto()
- func (p *Debugger) StepOut(depth int)
- func (p *Debugger) StepOver(depth int)
- func (p *Debugger) TriggerBreak(mc *MachineContext, bp *Breakpoint)
- type DisassembledInstruction
- type DisassembledTemplate
- type DynamicWindFrame
- type EditPlan
- func (p *EditPlan) AddLiteral(v values.Value) LiteralIndex
- func (p *EditPlan) Apply() int
- func (p *EditPlan) Delete(start, end int)
- func (p *EditPlan) HasEdits() bool
- func (p *EditPlan) Insert(at int, instrs []Instruction, src uint32)
- func (p *EditPlan) Replace(start, end int, instrs []Instruction, src uint32)
- type ErrBreakInterrupt
- type ErrExceptionEscape
- type ErrPromptAbort
- type ErrResumeContinuation
- type ErrTimerInterrupt
- type ErrorContext
- func (p *ErrorContext) EqualTo(other values.Value) bool
- func (p *ErrorContext) IsVoid() bool
- func (p *ErrorContext) Marks() *ContinuationMarkSet
- func (p *ErrorContext) SchemeString() string
- func (p *ErrorContext) Source() *syntax.SourceContext
- func (p *ErrorContext) SourceLocation() string
- func (p *ErrorContext) StackTraceFrames() StackTrace
- type ExpanderCtx
- type ForeignClosure
- func (p *ForeignClosure) AcceptsArity(n int) bool
- func (p *ForeignClosure) Doc() string
- func (p *ForeignClosure) Env() *environment.EnvironmentFrame
- func (p *ForeignClosure) EqualTo(o values.Value) bool
- func (p *ForeignClosure) Fn() ForeignFunction
- func (p *ForeignClosure) Identity() *PrimitiveIdentity
- func (p *ForeignClosure) IsVariadic() bool
- func (p *ForeignClosure) IsVoid() bool
- func (p *ForeignClosure) Name() string
- func (p *ForeignClosure) ParameterCount() int
- func (p *ForeignClosure) SchemeString() string
- func (p *ForeignClosure) SetDoc(doc string)
- func (p *ForeignClosure) SetIdentity(identity *PrimitiveIdentity)
- func (p *ForeignClosure) SetName(name string)
- func (p *ForeignClosure) SetValidator(v ForeignFunction)
- func (p *ForeignClosure) Validator() ForeignFunction
- type ForeignFunction
- type FreeList
- type GoFrameFunc
- type InlinedOperation
- type Instruction
- type LiteralIndex
- type MachineClosure
- func (p *MachineClosure) AcceptsArity(n int) bool
- func (p *MachineClosure) Doc() string
- func (p *MachineClosure) Env() *environment.EnvironmentFrame
- func (p *MachineClosure) EqualTo(o values.Value) bool
- func (p *MachineClosure) Free() []values.Value
- func (p *MachineClosure) IsVoid() bool
- func (p *MachineClosure) Link() *environment.EnvironmentFrame
- func (p *MachineClosure) Name() string
- func (p *MachineClosure) SchemeString() string
- func (p *MachineClosure) Template() *NativeTemplate
- type MachineContext
- func AcquireTopLevelContext(ctx context.Context, tpl *NativeTemplate, env *environment.EnvironmentFrame) *MachineContext
- func NewMachineContext(ctx context.Context, cont *MachineContinuation) *MachineContext
- func NewThreadSubContext(params SubContextParams, thread *values.Thread) *MachineContext
- func RequireMachineContext(cc CallContext, name string) (*MachineContext, error)
- func (p *MachineContext) Apply(mcls *MachineClosure, vs ...values.Value) (*MachineContext, error)
- func (p *MachineContext) ApplyCallable(callable values.Value, args ...values.Value) (*MachineContext, error)
- func (p *MachineContext) ApplyCaseLambda(clcls *CaseLambdaClosure, vs ...values.Value) (*MachineContext, error)
- func (p *MachineContext) Arg(index int) values.Value
- func (p *MachineContext) Authorizer() security.Authorizer
- func (p *MachineContext) BarrierValid() *BarrierToken
- func (p *MachineContext) BreakResumeValues() []values.Value
- func (p *MachineContext) CallDepth() int
- func (p *MachineContext) CaptureInterruptContinuationAt(boundary *MachineContinuation) *MachineContinuation
- func (p *MachineContext) CaptureStackTrace(maxDepth int) StackTrace
- func (p *MachineContext) CaptureSubContextParams() SubContextParams
- func (p *MachineContext) ClosureEnv() *environment.EnvironmentFrame
- func (p *MachineContext) CollectContinuationMarks(tag *PromptTag) *ContinuationMarkSet
- func (p *MachineContext) Context() context.Context
- func (p *MachineContext) Counters() VMCounters
- func (p *MachineContext) CurrentContinuation() *MachineContinuation
- func (p *MachineContext) CurrentLocation() *values.DebugLocation
- func (p *MachineContext) CurrentSource() *syntax.SourceContext
- func (p *MachineContext) Debugger() *Debugger
- func (p *MachineContext) DeleteMark(key values.Value)
- func (p *MachineContext) EnvironmentFrame() *environment.EnvironmentFrame
- func (p *MachineContext) Error(msg string) *SchemeError
- func (p *MachineContext) EscapeCont() *MachineContinuation
- func (p *MachineContext) Evals() *Stack
- func (p *MachineContext) ExecutingNamespace() *environment.Namespace
- func (p *MachineContext) ExpanderContext() ExpanderCtx
- func (p *MachineContext) FindPrompt(tag *PromptTag) (*MachineContinuation, bool)
- func (p *MachineContext) FormatStackTrace(maxDepth int) string
- func (p *MachineContext) GetImmediateMark(key values.Value) values.Value
- func (p *MachineContext) GetMark(key values.Value) values.Value
- func (p *MachineContext) GetValue() values.Value
- func (p *MachineContext) GetValues() MultipleValues
- func (p *MachineContext) IncrPC()
- func (p *MachineContext) InstallBreakPrompt(handler values.Callable) *PromptTag
- func (p *MachineContext) MaxCallDepth() int
- func (p *MachineContext) MaxStackSize() uint64
- func (p *MachineContext) NewSubContext() *MachineContext
- func (p *MachineContext) NewSubContextWithTemplate(tpl *NativeTemplate, env *environment.EnvironmentFrame) *MachineContext
- func (p *MachineContext) NewSubContextWithWinding(windingStack WindingStack) *MachineContext
- func (p *MachineContext) PC() int
- func (p *MachineContext) Parent() *MachineContinuation
- func (p *MachineContext) ParentMC() *MachineContext
- func (p *MachineContext) PopWindingFrame() (DynamicWindFrame, bool)
- func (p *MachineContext) PromptTag() *PromptTag
- func (p *MachineContext) PushValues(v ...values.Value)
- func (p *MachineContext) PushWindingFrame(frame DynamicWindFrame)
- func (p *MachineContext) ReinstallSegment(comp *ComposableContinuation, boundary *MachineContinuation, ...) (bool, error)
- func (p *MachineContext) ResolveParameterValue(param *Parameter) values.Value
- func (p *MachineContext) Restore(cont *MachineContinuation)
- func (p *MachineContext) RestoreAndRelease(cont *MachineContinuation)
- func (p *MachineContext) RestoreWithWindingFrom(cont *MachineContinuation, sourceStack, targetStack WindingStack) error
- func (p *MachineContext) RewindTo(target WindingStack, commonDepth int) error
- func (p *MachineContext) Run() error
- func (p *MachineContext) RunBodyUnderBarrier(body values.Value, token *BarrierToken) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderConsumer(producer values.Value, consumer values.Value, args ...values.Value) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderExitFrame(body values.Value, tag *PromptTag, finalizer values.Value, ...) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderFrame(frame *MachineContinuation, body values.Value, args ...values.Value) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderGoFrame(body values.Value, fn GoFrameFunc, args ...values.Value) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderPrompt(body values.Value, tag *PromptTag, handler values.Callable, ...) (*MachineContext, error)
- func (p *MachineContext) RunBodyUnderTimer(timerCtx context.Context, cancel context.CancelFunc, thunk values.Value, ...) (*MachineContext, error)
- func (p *MachineContext) RunResumable() (rerr error)
- func (p *MachineContext) RunWithEscapeHandling() error
- func (p *MachineContext) RunWithinBoundary() error
- func (p *MachineContext) SaveContinuation(off int) error
- func (p *MachineContext) SetBreakHandler(handler values.Callable)
- func (p *MachineContext) SetContext(ctx context.Context)
- func (p *MachineContext) SetDebugger(d *Debugger)
- func (p *MachineContext) SetEnvPooled(v bool)
- func (p *MachineContext) SetEnvironmentFrame(env *environment.EnvironmentFrame)
- func (p *MachineContext) SetEscapeCont(cont *MachineContinuation)
- func (p *MachineContext) SetExpanderContext(ctx ExpanderCtx)
- func (p *MachineContext) SetMark(key, val values.Value)
- func (p *MachineContext) SetMaxCallDepth(n int)
- func (p *MachineContext) SetMaxStackSize(n uint64)
- func (p *MachineContext) SetPC(v int)
- func (p *MachineContext) SetPromptTag(tag *PromptTag)
- func (p *MachineContext) SetSyntaxCaseState(v any)
- func (p *MachineContext) SetThread(t *values.Thread)
- func (p *MachineContext) SetValue(v values.Value)
- func (p *MachineContext) SetValues(vs ...values.Value)
- func (p *MachineContext) SliceContinuationAt(prompt *MachineContinuation) *MachineContinuation
- func (p *MachineContext) SnapshotReachableMarksInto(comp *ComposableContinuation)
- func (p *MachineContext) SyntaxCaseState() any
- func (p *MachineContext) Template() *NativeTemplate
- func (p *MachineContext) Thread() *values.Thread
- func (p *MachineContext) ThreadID() uint64
- func (p *MachineContext) UnwindTo(commonDepth int) error
- func (p *MachineContext) WindingStack() WindingStack
- func (p *MachineContext) WrapError(err error, msg string) *SchemeError
- type MachineContinuation
- func NewMachineContinuation(parent *MachineContinuation, tpl *NativeTemplate, ...) *MachineContinuation
- func NewMachineContinuationFromMachineContext(mc *MachineContext, off int) *MachineContinuation
- func NewMachineContinuationWithPrompt(parent *MachineContinuation, tpl *NativeTemplate, ...) *MachineContinuation
- func (p *MachineContinuation) CallDepth() int
- func (p *MachineContinuation) Copy() *MachineContinuation
- func (p *MachineContinuation) DeepCopy() *MachineContinuation
- func (p *MachineContinuation) EnvironmentFrame() *environment.EnvironmentFrame
- func (p *MachineContinuation) EqualTo(o values.Value) bool
- func (p *MachineContinuation) GetValue() values.Value
- func (p *MachineContinuation) GetValues() MultipleValues
- func (p *MachineContinuation) IsVoid() bool
- func (p *MachineContinuation) MarkChainShared()
- func (p *MachineContinuation) PC() int
- func (p *MachineContinuation) Parent() *MachineContinuation
- func (p *MachineContinuation) PromptHandler() values.Callable
- func (p *MachineContinuation) PromptTag() *PromptTag
- func (p *MachineContinuation) PushValues(v ...values.Value)
- func (p *MachineContinuation) SchemeString() string
- func (p *MachineContinuation) SetPC(v int)
- func (p *MachineContinuation) SetPromptTag(t *PromptTag)
- func (p *MachineContinuation) SetValue(v values.Value)
- func (p *MachineContinuation) SetValues(vs ...values.Value)
- func (p *MachineContinuation) Template() *NativeTemplate
- type MacroEvaluator
- type MultipleValues
- type NamedCallable
- type NativeTemplate
- func (p *NativeTemplate) AppendCachedBinding(bd *environment.Binding) int32
- func (p *NativeTemplate) AppendInstruction(instr Instruction)
- func (p *NativeTemplate) AppendInstructionWithSource(src *syntax.SourceContext, instr Instruction)
- func (p *NativeTemplate) AppendOperations(ops ...Operation)
- func (p *NativeTemplate) AppendOperationsWithSource(src *syntax.SourceContext, ops ...Operation)
- func (p *NativeTemplate) AppendSideTableOp(op InlinedOperation) Instruction
- func (p *NativeTemplate) CachedBindings() []*environment.Binding
- func (p *NativeTemplate) Code() []Instruction
- func (p *NativeTemplate) CodeLen() int
- func (p *NativeTemplate) Copy() *NativeTemplate
- func (p *NativeTemplate) Doc() string
- func (p *NativeTemplate) EnableCoverage()
- func (p *NativeTemplate) EqualTo(o values.Value) bool
- func (p *NativeTemplate) Executed() []bool
- func (p *NativeTemplate) FreeBoxed() []bool
- func (p *NativeTemplate) FreeNames() []*values.Symbol
- func (p *NativeTemplate) IncrementParameterCount()
- func (p *NativeTemplate) IsCoverageEnabled() bool
- func (p *NativeTemplate) IsVariadic() bool
- func (p *NativeTemplate) IsVoid() bool
- func (p *NativeTemplate) Literals() MultipleValues
- func (p *NativeTemplate) MaybeAppendLiteral(v values.Value) LiteralIndex
- func (p *NativeTemplate) Name() string
- func (p *NativeTemplate) Operations() Operations
- func (p *NativeTemplate) Optimize()
- func (p *NativeTemplate) ParameterCount() int
- func (p *NativeTemplate) PatchInstructionArg(codeIdx int, arg int32)
- func (p *NativeTemplate) RetainsLexicalEnv() bool
- func (p *NativeTemplate) SchemeString() string
- func (p *NativeTemplate) SetDoc(doc string)
- func (p *NativeTemplate) SetFreeLayout(names []*values.Symbol, boxed []bool)
- func (p *NativeTemplate) SetName(name string)
- func (p *NativeTemplate) SetRetainsLexicalEnv()
- func (p *NativeTemplate) SetShape(env *environment.EnvironmentFrame)
- func (p *NativeTemplate) SetVariadic()
- func (p *NativeTemplate) Shape() *environment.EnvironmentFrame
- func (p *NativeTemplate) SideTable() []InlinedOperation
- func (p *NativeTemplate) SourceAt(pc int) *syntax.SourceContext
- type OpCode
- type OperandKind
- type Operation
- type OperationApply
- type OperationBase
- type OperationBoxSlot
- type OperationBoxValues
- type OperationBranchOffsetImmediate
- type OperationBranchOnFalseValueOffsetImmediate
- type OperationDrop
- type OperationGoReturn
- type OperationLoadCachedBinding
- type OperationLoadFree
- type OperationLoadGlobalByGlobalIndexLiteralIndexImmediate
- type OperationLoadLiteralByLiteralIndexImmediate
- type OperationLoadLocalByLocalIndexImmediate
- type OperationLoadVoid
- type OperationMakeCaseLambdaClosure
- type OperationMakeClosure
- type OperationPeekK
- type OperationPop
- type OperationPopEnv
- type OperationPopWind
- type OperationPull
- type OperationPush
- type OperationPushEnv
- type OperationPushValues
- type OperationPushWind
- type OperationReleaseEnvFrame
- type OperationRestoreContMark
- type OperationRestoreContinuation
- type OperationSaveContMark
- type OperationSaveContinuationOffsetImmediate
- type OperationSelfTailCall
- type OperationSetContMark
- type OperationStoreFree
- type OperationStoreGlobalByGlobalIndexLiteralIndexImmediate
- type OperationStoreLocalByLocalIndexImmediate
- type OperationStoreThroughBox
- type OperationUnbox
- type OperationUnboxValues
- type OperationUnpackListToStack
- type Operations
- type Parameter
- func (p *Parameter) AcceptsArity(n int) bool
- func (p *Parameter) Converter() values.Callable
- func (p *Parameter) EqualTo(v values.Value) bool
- func (p *Parameter) HasConverter() bool
- func (p *Parameter) HasImmutableBase() bool
- func (p *Parameter) IsVoid() bool
- func (p *Parameter) SchemeString() string
- func (p *Parameter) SetValue(v values.Value)
- func (p *Parameter) Value() values.Value
- type ParameterBase
- type Pool
- type PoolSnapshot
- type PrimitiveIdentity
- type PromptTag
- type SchemeError
- type Stack
- func (p Stack) AsList() values.Tuple
- func (p *Stack) Clear()
- func (p Stack) Copy() *Stack
- func (p *Stack) Drain() []values.Value
- func (p *Stack) DrainN(n int) []values.Value
- func (p Stack) IsVoid() bool
- func (p Stack) Len() int
- func (p Stack) PeekK(i int) values.Value
- func (p *Stack) Pop() values.Value
- func (p *Stack) Pop2() (q0, q1 values.Value)
- func (p *Stack) PopAll() []values.Value
- func (p *Stack) PopN(n int) []values.Value
- func (p *Stack) Pull() values.Value
- func (p *Stack) PullDrain() (values.Value, []values.Value)
- func (p *Stack) Push(v values.Value)
- func (p *Stack) PushAll(vs []values.Value)
- func (p Stack) SchemeString() string
- func (p Stack) String() string
- type StackFrame
- type StackTrace
- type StepMode
- type SubContextParams
- type VMCounters
- type WindingStack
Constants ¶
const DefaultBacktraceDepth = 20
DefaultBacktraceDepth bounds the number of frames CaptureStackTrace walks when building the backtrace attached to a user-facing Scheme error or exception, and is the default for (current-stack-trace) called without a depth. Deep enough to locate the fault, shallow enough to keep error messages readable; a presentation choice, not a correctness one.
Variables ¶
var ( IdentityEqQ = NewPrimitiveIdentity("eq?") IdentityVectorQ = NewPrimitiveIdentity("vector?") IdentityVectorRef = NewPrimitiveIdentity("vector-ref") IdentityNullQ = NewPrimitiveIdentity("null?") IdentityPairQ = NewPrimitiveIdentity("pair?") IdentityCar = NewPrimitiveIdentity("car") IdentityCdr = NewPrimitiveIdentity("cdr") IdentityCons = NewPrimitiveIdentity("cons") IdentityAdd = NewPrimitiveIdentity("+") IdentitySub = NewPrimitiveIdentity("-") IdentityMul = NewPrimitiveIdentity("*") IdentityDiv = NewPrimitiveIdentity("/") IdentityNumLt = NewPrimitiveIdentity("<") IdentityNumLe = NewPrimitiveIdentity("<=") IdentityNumGt = NewPrimitiveIdentity(">") IdentityNumGe = NewPrimitiveIdentity(">=") IdentityNumEq = NewPrimitiveIdentity("=") IdentitySetCdr = NewPrimitiveIdentity("set-cdr!") )
The identity token of each promoted primitive, minted once here. The registry declares one on the matching PrimitiveSpec (registry/core), which stamps it onto every ForeignClosure built from that spec; execPromoted and the peephole optimizer then compare it by pointer against the descriptor's.
They are exported only because the specs that declare them live in pkg/registry/core, which imports pkg/machine and not the reverse. A Go embedder naming one of these in a spec of their own therefore opts INTO promoted dispatch deliberately — unlike the accidental name collision this replaces, where any closure spelled "cons" was inlined whether it wanted to be or not.
var DefaultPromptTag = NewPromptTag("default")
DefaultPromptTag is the default prompt tag installed at the top of execution.
var ErrBindingNotFound = werr.NewStaticError("binding not found")
var ErrFreeIndexOutOfRange = werr.NewStaticError("free variable index out of range")
ErrFreeIndexOutOfRange reports a free-vector index that the executing closure's vector does not contain. It is a compiler/VM disagreement, not a user error: the index is an immediate the emitter chose against a layout the same emitter fixed.
var ErrInvalidGlobalIndex = werr.NewStaticError("literal is not a global index")
var ErrInvalidLiteralIndex = werr.NewStaticError("invalid literal index")
var ErrInvalidProgramCounter = werr.NewStaticError("invalid program counter")
var ErrLocalIndexOverflow = werr.NewStaticError("local index overflow")
ErrLocalIndexOverflow signals that a De Bruijn index slot or depth exceeds the int16 encoding range (-32768..32767). EncodeLocalIndex panics with this sentinel when the bytecode format cannot represent the index.
var ErrNoFreeVector = werr.NewStaticError("no free vector installed")
ErrNoFreeVector reports a free-vector access executed with no closure vector installed — a body compiled as a closure body running outside Apply.
var ErrTimerExpired = werr.NewStaticError("timer expired")
ErrTimerExpired is the context cause set by with-timeout via context.WithTimeoutCause. Interrupt check sites compare against this to distinguish timer expiry from external cancellation (e.g. Ctrl+C).
Functions ¶
func ConditionFromError ¶ added in v1.20.0
ConditionFromError converts a Go error into a Scheme condition object (a NativeError). It detects ForeignFileError and ForeignReadError to set the appropriate NativeError kind per R7RS §6.11. RaiseInPlace later stamps the raise-site source location and stack trace onto it.
This is THE converter, and it is exported so that it stays the only one: the compile funnel (wile.wrapCompilationError, filling CompilationError.Condition) and the runtime bridge (applyCallableError) both go through it, so a Go failure has one Scheme-visible representation no matter which verb was running when it happened. A second conversion written next to a caller is the defect this signature exists to prevent.
A primitive that RETURNS a *values.NativeError is already holding a condition, so it is forwarded rather than rebuilt: rebuilding reads only err.Error(), which drops the irritants and the kind. CODING_STYLE.md has prescribed that return form since 2026-06-27 and it did not work until wave 4 item 4.
The assertion is deliberately DIRECT, never errors.As. errors.As would also match a *NativeError reached through a werr wrap, and forwarding that inner value would discard the wrap's own message — one losslessness bug traded for another. A wrapped condition is a Go error something has added context to, and it belongs on the rebuild path. Pinned by TestWrappedNativeErrorIsNotForwarded (pkg/wile/native_error_passthrough_test.go).
A rebuilt condition is pre-stamped when the chain carries a compile-time location. Without that, a compile failure returned by load, eval or compile gets the location of the CALL from enrichNativeError, because the raise happens at the frame that invoked the compiler — so error-object-source contradicted the message text, which held the real site. See innermostCompileLocation.
func DecodeLocalIndex ¶
DecodeLocalIndex unpacks slot and depth from a bit-packed Instruction.Arg.
func DecodeMakeClosure ¶ added in v1.20.0
DecodeMakeClosure unpacks OpMakeClosure's free-variable count and self-reference slot. selfSlot is -1 when the closure has none.
func DecodeSelfTailCall ¶ added in v1.20.0
DecodeSelfTailCall unpacks OpSelfTailCall's argument count and frame-pop count.
func DisassembleString ¶
func DisassembleString(tpl *NativeTemplate) string
DisassembleString produces a columnar human-readable listing from a NativeTemplate.
func EncodeLocalIndex ¶
func EncodeLocalIndex(li *environment.LocalIndex) int32
EncodeLocalIndex packs a LocalIndex's slot and depth into a single int32 for storage in Instruction.Arg. Slot occupies the low 16 bits; depth occupies the high 16 bits. Both values must fit in int16 range (max 32767).
De Bruijn indices (de Bruijn 1972). Variables are addressed by numeric coordinates, eliminating name lookup at runtime.
addr(x) = (slot, depth), where: depth = number of enclosing λ-binders from use to definition slot = index within the binding array at that depth Encoding: Arg = (depth << 16) | (slot & 0xFFFF) Invariant: the same variable always has the same (slot, depth) regardless of its name. Alpha-equivalence is a non-issue at runtime. Constrains: GetLocalBindingBySlotDepth / SetLocalValueBySlotDepth (runtime access walks depth parent pointers, indexes by slot), linked closures (parent chain must match compile-time depth). Constrained by: resolveLocal (compile-time computation of depth by walking the EnvironmentFrame parent chain).
See BIBLIOGRAPHY.md "De Bruijn Indices / Lexical Addressing".
func EncodeMakeClosure ¶ added in v1.20.0
EncodeMakeClosure packs OpMakeClosure's two operands: the free-variable count in the low half, the self-reference slot in the high half.
selfSlot is stored as selfSlot+1 so that 0 means "no self slot". The natural -1 cannot ride in a half of a codec that refuses a negative operand, which is the same constraint EncodeSelfTailCall works under.
freeCount 0 with no self slot encodes to 0, so every closure that captures nothing emits the same instruction word OpMakeClosure emitted before this change — invisible to the peephole, to the disassembler's goldens, and to any stored template.
func EncodeSelfTailCall ¶ added in v1.20.0
EncodeSelfTailCall packs OpSelfTailCall's two operands: the argument count in the low half, the count of intermediate env frames to pop in the high half.
The low half carries argCount so that popCount == 0 encodes to argCount itself. Every depth-0 self-tail site therefore emits the same instruction word it did before Phase C widened the op, which keeps the change invisible to the peephole pass, to the disassembler's existing goldens, and to any stored template.
Both operands are non-negative and small — argCount is bounded by the frame's slot capacity and popCount by lexical let nesting — so the int16 halves are not a practical limit; the panic is a corrupt-compiler assertion, matching EncodeLocalIndex.
func ErrorContextKey ¶
ErrorContextKey returns the continuation mark key for error context. Exposed for use by registry primitives that read the mark.
func ExceptionHandlerParam ¶
ExceptionHandlerParam returns the canonical exception-handler parameter so the core registry can bind it to the Scheme global %exception-handlers. The return type is values.Value, not *Parameter: callers only need to bind the object, and narrowing keeps the mutating Parameter.SetValue out of reach so the shared singleton's base value (the immutability the cross-Engine safety argument rests on) can't be rewritten through this accessor. RaiseInPlace uses the package var directly.
func FieldMatches ¶
func FieldMatches[T comparable, Op any](p, v *Op, ok bool, getField func(*Op) T) bool
FieldMatches is a helper for EqualTo methods on single-field operations. The caller must perform the type assertion and pass the result.
Usage:
func (p *OperationType) EqualTo(o values.Value) bool {
v, ok := o.(*OperationType)
return FieldMatches(p, v, ok, func(op *OperationType) int { return op.Field })
}
func FindCommonWindingPrefix ¶
func FindCommonWindingPrefix(current, target WindingStack) int
FindCommonWindingPrefix finds the longest common prefix of two winding stacks. Returns the index where they diverge (0 means no common frames).
func GraftContinuation ¶
func GraftContinuation(segment, target *MachineContinuation)
GraftContinuation walks the segment chain to its bottom frame and sets its parent to target, effectively splicing the segment onto the target chain.
func PromotedMutatorNames ¶ added in v1.20.0
func PromotedMutatorNames() []string
PromotedMutatorNames returns the Scheme names of the promoted primitives that write through to a program-visible object. Exported for the immutability ratchet, which must run through the full optimizing pipeline (promoted opcodes are the peephole's output) and therefore lives outside this package.
func RaiseInPlace ¶
func RaiseInPlace(mc *MachineContext, cond values.Value, continuable bool) error
RaiseInPlace invokes the current exception handler on cond, in the dynamic extent of the raise (R7RS §6.11), with the parent handler installed as current. It is the single implementation shared by raise, raise-continuable, error, and the Go-error bridge (applyCallableError).
- continuable=false (raise / error / Go errors): the handler is expected to escape (e.g. via guard's captured continuation); if it returns, a secondary non-continuable exception escalates to the parent handler. The bridge only ever calls with continuable=false, so it never needs an in-place resume.
- continuable=true (raise-continuable): the handler's return value becomes the value of the raise-continuable expression, resumed inline on mc.
When the handler stack is empty the condition is uncaught: a slim *ErrExceptionEscape carrier bubbles to RunWithEscapeHandling and surfaces to the embedder (engine.wrapRuntimeError) with the condition, source, and stack trace.
func ReleaseSubContext ¶
func ReleaseSubContext(mc *MachineContext)
ReleaseSubContext zeros the MachineContext and returns it to the pool. Exported because call sites live in other packages (registry/, extensions/).
func ReleaseTopLevelContext ¶
func ReleaseTopLevelContext(mc *MachineContext)
ReleaseTopLevelContext zeros the MachineContext and returns it to the pool. Exported because the primary call sites live in the root wile package (engine.go).
func SameType ¶
SameType is a helper for EqualTo methods on zero-field operations. The caller must perform the type assertion and pass the result.
Usage:
func (p *OperationType) EqualTo(o values.Value) bool {
v, ok := o.(*OperationType)
return SameType(p, v, ok)
}
func SetCallCounting ¶
func SetCallCounting(on bool)
SetCallCounting enables or disables per-callee call counting for MachineContexts created AFTER this call, independently of WILE_OPCODE_HITS. It is intended for measurement harnesses and embedders profiling call distribution; the counts are exposed via VMCounters.CallCounts. Existing contexts are unaffected.
func SliceMatches ¶
func SliceMatches[T comparable, Op any](p, v *Op, ok bool, getField func(*Op) []T) bool
SliceMatches is a helper for EqualTo methods on operations with a single comparable-element slice field.
Usage:
func (p *OperationType) EqualTo(o values.Value) bool {
v, ok := o.(*OperationType)
return SliceMatches(p, v, ok, func(op *OperationType) []string { return op.Items })
}
func StackTraceToSchemeList ¶
func StackTraceToSchemeList(st StackTrace) values.Tuple
StackTraceToSchemeList converts a StackTrace to a Scheme list of alists, one per frame. Each alist always carries name; file, line, and column are present only when the frame has a source LOCATION, and file is #f when that location has a position but no filename. Lives here (not in the registry) because it converts machine types and is needed by both RaiseInPlace and the error-context primitives.
Types ¶
type BarrierToken ¶
type BarrierToken struct {
// contains filtered or unexported fields
}
BarrierToken is an opaque identity for a with-continuation-barrier scope. Each call to call-with-continuation-barrier creates a fresh token. Barrier crossing is detected by pointer identity comparison: if the capture-time token differs from the invocation-time token, the continuation would cross a barrier boundary.
nil means "not inside any barrier."
The _ field ensures non-zero struct size. Go may return the same pointer for all zero-sized allocations (runtime.zerobase), which would defeat pointer identity comparison.
func NewBarrierToken ¶
func NewBarrierToken() *BarrierToken
NewBarrierToken creates a fresh barrier identity token.
type BoxedValues ¶ added in v1.20.0
type BoxedValues struct {
// contains filtered or unexported fields
}
BoxedValues is an internal carrier that collapses a zero- or many-valued value register into a SINGLE values.Value so it can be saved on the eval stack as one slot. It never escapes to Scheme: it lives only on the eval stack between an OperationBoxValues and its paired OperationUnboxValues (currently dynamic-wind, bracketing the after-thunk call). Identified by type, like noMarkSentinelType.
It is used through a POINTER (*BoxedValues), and that is load-bearing, not incidental. The struct holds a slice, so the bare struct type is not Go-comparable; boxed into a values.Value it would fault any `==` or map-key hash of the interface — values.EqIdentity (eq?) is exactly such an `==`. A pointer is comparable, which is what keeps this carrier inside the Value contract while it sits in the value register. See values.Value's doc comment, and TestSliceCarriersAreNotValues.
func (*BoxedValues) IsVoid ¶ added in v1.20.0
func (*BoxedValues) IsVoid() bool
func (*BoxedValues) SchemeString ¶ added in v1.20.0
func (*BoxedValues) SchemeString() string
type BreakSnapshot ¶ added in v1.20.0
type BreakSnapshot struct {
// contains filtered or unexported fields
}
BreakSnapshot is the frozen view of the VM at a break: the location, the rendered stack trace, and the call depth, all read from the LIVE continuation chain before the suspension restores past it.
A snapshot rather than the *MachineContext itself, for two reasons. The context is pool-recycled and zeroed by ReleaseTopLevelContext the moment the evaluation ends, so a stored pointer reads as a blank context afterwards; and during the suspension p.cont has already been moved to the break boundary, so the live context no longer describes the break point either.
func (*BreakSnapshot) CallDepth ¶ added in v1.20.0
func (p *BreakSnapshot) CallDepth() int
CallDepth returns the continuation depth at the break point. It is the key step-over and step-out are re-armed on: by the time a handler runs, the live context sits at the break boundary and its own depth is no longer the break point's.
func (*BreakSnapshot) CurrentLocation ¶ added in v1.20.0
func (p *BreakSnapshot) CurrentLocation() *values.DebugLocation
CurrentLocation returns the source location of the break point. Implements values.DebugState.
func (*BreakSnapshot) FormatStackTrace ¶ added in v1.20.0
func (p *BreakSnapshot) FormatStackTrace(maxDepth int) string
FormatStackTrace renders at most maxDepth frames of the trace frozen at the break, per values.DebugState. maxDepth <= 0 means "no bound", matching the interface's other implementation, whose walk a non-positive budget does not enter.
The frames are the ones captured at the break, not a fresh walk: the live chain has moved to the break boundary (or been recycled) by then, so a request deeper than breakTraceDepth can only be answered with what was captured. Implements values.DebugState.
type Breakpoint ¶
type Breakpoint struct {
ID BreakpointID
File string
Line int
Column int // 0 = any column on line
Enabled bool
HitCount int
}
Breakpoint represents a source-level breakpoint.
type CallContext ¶
type CallContext interface {
// Arg returns the argument at the given positional index.
// For variadic functions, the last parameter index holds the rest list.
Arg(index int) values.Value
// SetValue sets the single return value.
SetValue(v values.Value)
// SetValues sets multiple return values for R7RS values/call-with-values.
SetValues(vs ...values.Value)
// Authorizer returns the security authorizer for permission checks.
Authorizer() security.Authorizer
// Context returns the Go context for cancellation and timeout.
Context() context.Context
// EnvironmentFrame returns the current lexical environment frame.
EnvironmentFrame() *environment.EnvironmentFrame
// Thread returns the SRFI-18 thread object (nil for primordial thread).
Thread() *values.Thread
}
CallContext is the extension-facing subset of MachineContext. Extensions and ForeignFunctions should depend on this interface, not on *MachineContext directly.
This interface captures the 8 methods that extensions actually use, reducing coupling from 30+ methods to 8. *MachineContext satisfies this interface with zero implementation cost.
Internal code that needs full VM access (sub-context creation, continuation manipulation, exception handling) should type-assert to *MachineContext.
type CapturedContinuation ¶
type CapturedContinuation struct {
// contains filtered or unexported fields
}
CapturedContinuation is the value returned by call/cc to Scheme code. It is both callable (invoking it escapes to the captured point) and introspectable (continuation-marks can extract marks from its chain).
This replaces the opaque ForeignClosure that call/cc previously returned. The escape logic (thread check, barrier check, compose-then-abort) now lives in applyCapturedContinuation rather than inside a Go closure.
func NewCapturedContinuation ¶
func NewCapturedContinuation( cc *ComposableContinuation, threadID uint64, barrierValid *BarrierToken, ) *CapturedContinuation
NewCapturedContinuation creates a captured continuation value from a composable continuation segment and the safety context at capture time.
func (*CapturedContinuation) AcceptsArity ¶
func (*CapturedContinuation) AcceptsArity(int) bool
AcceptsArity reports whether this continuation can be called with n arguments. A continuation accepts ANY number of values (R7RS §6.10): it resumes the captured computation with however many values it is invoked with — zero, one, or several. Always true; there is no rejecting arity (n is an argument count, never negative).
func (*CapturedContinuation) ComposableContinuation ¶
func (p *CapturedContinuation) ComposableContinuation() *ComposableContinuation
ComposableContinuation returns the underlying composable continuation, which carries the MachineContinuation chain for mark extraction.
func (*CapturedContinuation) IsVoid ¶
func (p *CapturedContinuation) IsVoid() bool
func (*CapturedContinuation) SchemeString ¶
func (p *CapturedContinuation) SchemeString() string
type CaseLambdaClosure ¶
type CaseLambdaClosure struct {
// contains filtered or unexported fields
}
CaseLambdaClosure dispatches to the first clause whose arity matches the argument count. Each clause is a MachineClosure with its own template and captured environment.
func NewCaseLambdaClosure ¶
func NewCaseLambdaClosure(closures []*MachineClosure) *CaseLambdaClosure
func (*CaseLambdaClosure) AcceptsArity ¶
func (p *CaseLambdaClosure) AcceptsArity(n int) bool
AcceptsArity reports whether any clause in this case-lambda can accept n arguments.
func (*CaseLambdaClosure) Clauses ¶
func (p *CaseLambdaClosure) Clauses() []*MachineClosure
func (*CaseLambdaClosure) Doc ¶
func (p *CaseLambdaClosure) Doc() string
Doc returns the documentation from the first clause's template, or "" if no clauses.
func (*CaseLambdaClosure) EqualTo ¶
func (p *CaseLambdaClosure) EqualTo(o values.Value) bool
EqualTo implements Scheme equality for case-lambda closures. Two void closures (nil receivers) are considered equal to each other. Non-void closures are equal only if they have the same number of clauses and each corresponding clause is EqualTo its counterpart.
func (*CaseLambdaClosure) FindMatchingClause ¶
func (p *CaseLambdaClosure) FindMatchingClause(argCount int) (*MachineClosure, bool)
FindMatchingClause finds the first clause that matches the given argument count. It returns the matching closure and true when a clause can accept exactly argCount arguments (for fixed-arity clauses) or at least argCount arguments (for variadic clauses). If the receiver is nil or no clause matches, it returns nil, false.
func (*CaseLambdaClosure) IsVoid ¶
func (p *CaseLambdaClosure) IsVoid() bool
IsVoid reports whether this value represents the absence of a case-lambda closure. A nil receiver is treated as a distinguished "void" closure value, used as a sentinel to mean "no closure" rather than an error.
func (*CaseLambdaClosure) Name ¶
func (p *CaseLambdaClosure) Name() string
Name returns the name from the first clause's template, or "" if no clauses.
func (*CaseLambdaClosure) SchemeString ¶
func (p *CaseLambdaClosure) SchemeString() string
SchemeString returns the Scheme-readable representation of a case-lambda closure. Note that the void value (nil receiver) still prints as a case-lambda closure; callers must use IsVoid to distinguish the sentinel.
type Closure ¶
type Closure interface {
values.Callable
NamedCallable
// contains filtered or unexported methods
}
Closure is a callable that can be directly applied — either a compiled Scheme closure (*MachineClosure) or a Go foreign function (*ForeignClosure). Distinguished from other Callable types (CaseLambdaClosure, Parameter, ComposableContinuation) which have different application semantics.
type ComposableContinuation ¶
type ComposableContinuation struct {
// contains filtered or unexported fields
}
ComposableContinuation is a callable value wrapping a delimited continuation segment (a chain of MachineContinuation frames) plus the captured winding stack. When applied, it splices its frames onto the current continuation, effectively composing the captured computation with the current one.
This implements Racket-style composable continuations as described in Flatt, Yu, Findler, Felleisen "Adding Delimited and Composable Control to a Production Programming Environment" (ICFP 2007).
func NewComposableContinuation ¶
func NewComposableContinuation(cont *MachineContinuation, windingStack WindingStack, threadID uint64, barrierValid *BarrierToken) *ComposableContinuation
NewComposableContinuation creates a composable continuation from a continuation chain segment and the winding stack captured at the point of capture. barrierValid is mc.BarrierValid() at capture time; nil means the capture happened outside any with-continuation-barrier.
func (*ComposableContinuation) AcceptsArity ¶
func (*ComposableContinuation) AcceptsArity(int) bool
AcceptsArity reports whether this composable continuation can be called with n arguments. A continuation accepts ANY number of values (R7RS §6.10): it resumes with however many values it is invoked with — zero, one, or several. Always true; there is no rejecting arity (n is an argument count, never negative).
func (*ComposableContinuation) AcquireSegment ¶
func (p *ComposableContinuation) AcquireSegment() *MachineContinuation
AcquireSegment returns the continuation segment for grafting.
First invocation marks the segment shared and returns it directly, avoiding a DeepCopy. Shared marking ensures RestoreAndRelease preserves frame evals for potential re-invocation.
Subsequent invocations reset the bottom frame's parent to nil (undoing GraftContinuation's mutation from the prior invocation) and deep-copy from the preserved shared frames.
This optimizes one-shot continuations (the common case in Schelog-style backtracking), eliminating O(depth) frame allocations per invocation.
func (*ComposableContinuation) BarrierValid ¶
func (p *ComposableContinuation) BarrierValid() *BarrierToken
BarrierValid returns the barrier identity token captured when this continuation was created. Used by applyComposableContinuation to detect barrier crossings.
func (*ComposableContinuation) Cont ¶
func (p *ComposableContinuation) Cont() *MachineContinuation
Cont returns the continuation segment captured by this composable continuation.
func (*ComposableContinuation) EqualTo ¶
func (p *ComposableContinuation) EqualTo(o values.Value) bool
EqualTo reports whether this composable continuation is equal to another.
func (*ComposableContinuation) IsVoid ¶
func (p *ComposableContinuation) IsVoid() bool
IsVoid reports whether this composable continuation is void.
func (*ComposableContinuation) SchemeString ¶
func (p *ComposableContinuation) SchemeString() string
SchemeString returns a string representation of this composable continuation.
func (*ComposableContinuation) WindingStack ¶
func (p *ComposableContinuation) WindingStack() WindingStack
WindingStack returns the winding stack captured by this composable continuation.
type ContinuationMarkSet ¶
type ContinuationMarkSet struct {
// contains filtered or unexported fields
}
ContinuationMarkSet is an immutable snapshot of continuation marks collected from a walk of the continuation chain.
The frames slice contains per-frame mark slices, nearest frame first. Only frames with non-nil marks are included. Each frame is a []markEntry searched with values.EqIdentity (eq? semantics).
func CollectMarksFromContinuation ¶
func CollectMarksFromContinuation(cont *MachineContinuation, tag *PromptTag) *ContinuationMarkSet
CollectMarksFromContinuation walks a captured MachineContinuation chain and builds a ContinuationMarkSet snapshot. Used by the (continuation-marks cont) primitive to extract marks from a continuation captured via call/cc.
Starts from cont (nearest frame) and walks parent links, stopping at the first frame whose promptTag matches tag (inclusive). Pass DefaultPromptTag for an unbounded walk.
func (*ContinuationMarkSet) First ¶
func (p *ContinuationMarkSet) First(key, defaultVal values.Value) values.Value
First returns the value for key from the nearest frame, or defaultVal if no frame contains the key. Uses eq? semantics (values.EqIdentity) for key comparison.
func (*ContinuationMarkSet) IsVoid ¶
func (p *ContinuationMarkSet) IsVoid() bool
func (*ContinuationMarkSet) SchemeString ¶
func (p *ContinuationMarkSet) SchemeString() string
func (*ContinuationMarkSet) ToList ¶
func (p *ContinuationMarkSet) ToList(key values.Value) values.Tuple
ToList returns a list of values for key across all frames, nearest first. Returns the empty list if no frame contains the key. Uses eq? semantics (values.EqIdentity) for key comparison.
func (*ContinuationMarkSet) ToListStar ¶
ToListStar returns a list of vectors for multiple keys across all frames. Each vector corresponds to a frame that has at least one of the requested keys. Vector positions correspond to the keys slice; noneVal fills missing keys. Uses eq? semantics (values.EqIdentity) for key comparison.
Racket §10.5: continuation-mark-set->list*
type Debugger ¶
type Debugger struct {
// contains filtered or unexported fields
}
Debugger manages breakpoints and stepping.
func (*Debugger) BreakState ¶ added in v1.20.0
func (p *Debugger) BreakState() (values.DebugState, *Breakpoint)
BreakState returns the most recent break's frozen state and the breakpoint that caused it, or (nil, nil) if no break has happened. The breakpoint is nil for a step stop.
func (*Debugger) Breakpoints ¶
func (p *Debugger) Breakpoints() []*Breakpoint
Breakpoints returns all breakpoints.
func (*Debugger) CanStop ¶ added in v1.20.0
CanStop reports whether anything could stop execution right now: an enabled breakpoint, or an armed step mode.
It is the arming test for the break boundary. A boundary nothing can reach is not free — it is a real continuation frame, so it shows up in every stack trace and costs one unit of the call-depth budget for the whole run.
It is a snapshot, not a subscription: a breakpoint set from another goroutine after a run has begun is not seen by that run, which falls back to the render-only callback for its remainder.
func (*Debugger) CheckBreakpoint ¶
func (p *Debugger) CheckBreakpoint(mc *MachineContext) *Breakpoint
CheckBreakpoint reports the breakpoint execution should stop at, or nil.
It is not a pure query: a matching breakpoint's HitCount is incremented, and mc's de-duplication cursor is advanced on every call. The increment is a write and is taken under the WRITE lock, so two SRFI-18 threads sharing one Debugger no longer lose increments against each other. That is all the lock buys: the returned *Breakpoint is the live object, and every field a caller then reads (HitCount, Enabled) is read outside this lock. The returned pointer being live is deliberate — callers see Enable/Disable's effect — not a claim of safety.
Each breakpoint fires once per ENTRY to its source line, not once per instruction on it: a single source line compiles to several instructions, and stopping on each of them reported one stop as several (four, for a `(+ x x)` body) and inflated HitCount by the same factor. The cursor advances on every call so that leaving a line and returning to it re-arms — which is what makes a procedure called four times break four times.
Suppression is per BREAKPOINT, not per line. Keying it on the line alone let the first breakpoint to fire mask every other breakpoint on that line for the whole entry — including one on a later column that had not yet been reached — and left which of them survived to Go's map iteration order.
Known limitation: a recursion or loop whose ENTIRE body is one source line never leaves that line, so it breaks once, not once per activation.
func (*Debugger) DisableBreakpoint ¶
func (p *Debugger) DisableBreakpoint(id BreakpointID) bool
DisableBreakpoint disables a breakpoint.
func (*Debugger) EnableBreakpoint ¶
func (p *Debugger) EnableBreakpoint(id BreakpointID) bool
EnableBreakpoint enables a breakpoint.
func (*Debugger) IsStepping ¶
IsStepping returns whether the debugger is in stepping mode. It takes the read lock like every other stepMode accessor: an embedder polling this from a supervising goroutine while a break handler on the VM goroutine arms a step mode is otherwise an unsynchronized read of the same field.
func (*Debugger) OnBreak ¶
func (p *Debugger) OnBreak(fn func(mc *MachineContext, bp *Breakpoint))
OnBreak sets the callback for when a breakpoint is hit.
func (*Debugger) RemoveBreakpoint ¶
func (p *Debugger) RemoveBreakpoint(id BreakpointID) bool
RemoveBreakpoint removes a breakpoint.
func (*Debugger) SetBreakpoint ¶
func (p *Debugger) SetBreakpoint(file string, line, column int) BreakpointID
SetBreakpoint adds a breakpoint at the given source location.
func (*Debugger) ShouldStep ¶
func (p *Debugger) ShouldStep(mc *MachineContext) bool
ShouldStep checks if we should break due to stepping.
func (*Debugger) StepOut ¶
StepOut enables step-out mode, relative to the call depth at the stop that armed it. See StepOver for why the depth is a parameter.
func (*Debugger) StepOver ¶
StepOver enables step-over mode, relative to the call depth at the stop that armed it. Callers pass the depth explicitly because the context that applies the verdict is suspended at the break BOUNDARY, whose depth is not the break point's.
func (*Debugger) TriggerBreak ¶
func (p *Debugger) TriggerBreak(mc *MachineContext, bp *Breakpoint)
TriggerBreak records the break and calls the break callback if set. It is the fallback taken when no break boundary is armed on mc: the callback runs INLINE on the live context, so it can render the stop but cannot influence execution.
The state is frozen into a snapshot here for the same reason the suspending path does it — mc is pool-recycled and zeroed when the evaluation ends, so a consumer reading it after the run gets a blank context.
type DisassembledInstruction ¶
type DisassembledInstruction struct {
PC int
Op string
Arg int32
HasArg bool // true when Arg is meaningful (distinguishes unused from Arg=0)
Slot int // decoded slot for local ops; -1 when not applicable
Depth int // decoded depth for local ops; -1 when not applicable
Target int // absolute PC for branch/save ops; -1 when not applicable
Literal string // SchemeString() of resolved literal; "" when not applicable
Binding string // name of cached binding's value; "" when not applicable
SideOp string // String() of InlinedOperation; "" when not applicable
Detail string // pre-rendered operand detail for ops with a bespoke encoding; "" when not applicable
Source string // "file:line:col" or ""
}
DisassembledInstruction holds the annotation for a single bytecode instruction.
type DisassembledTemplate ¶
type DisassembledTemplate struct {
Name string
ParamCount int
IsVariadic bool
Doc string
Literals []string // SchemeString() of each literal
Bindings []string // name of each cached binding
Instructions []DisassembledInstruction
}
DisassembledTemplate holds the annotated disassembly of a NativeTemplate.
func Disassemble ¶
func Disassemble(tpl *NativeTemplate) DisassembledTemplate
Disassemble walks the template's bytecode and produces a DisassembledTemplate with annotations resolved per opcode category.
type DynamicWindFrame ¶
type DynamicWindFrame struct {
// Before/After are values.Callable, not Closure: both are only ever applied,
// via ApplyCallable, which dispatches six types where Closure has two. A
// case-lambda, a parameter object or a continuation is a procedure by
// procedure?'s own answer and must be accepted here.
//
// Assign only untyped nil. A typed nil stored in an interface field is not
// nil, and the winding reconcile tests `frame.Before != nil`.
Before values.Callable // Called when entering this extent
After values.Callable // Called when exiting this extent
ID uint64 // Unique identifier for extent matching
// contains filtered or unexported fields
}
DynamicWindFrame represents a single dynamic-wind extent. Each frame tracks the before/after thunks for one dynamic-wind call.
R7RS §6.10: dynamic-wind establishes a dynamic extent during which the before and after thunks are called whenever control enters or exits.
Continuation-wind interaction (Friedman & Haynes 1985, Clinger et al. 1999). The winding stack W is separate from continuation chain K.
W = [w₁, w₂, ...wₙ] where wᵢ = (before_i, after_i, id_i)
On continuation invocation from Wsrc to Wtgt:
prefix = FindCommonWindingPrefix(Wsrc, Wtgt)
UnwindTo(prefix): call after_n, after_{n-1}, ... (innermost first)
RewindTo(Wtgt): call before_{prefix+1}, ... (outermost first)
Invariant: W is captured by value at call/cc time (copied into
ComposableContinuation), NOT stored per continuation frame.
W belongs to the dynamic extent, not the lexical continuation.
Constrains: RestoreWithWindingFrom (must compute prefix and run
thunks in correct order). Sub-contexts inherit W from their parent
via NewSubContext; NewSubContextWithWinding overrides for truncated
stacks during unwind/exception cleanup.
Constrained by: CESK model (W is NOT part of K — it is orthogonal
state). PushWind/PopWind opcodes maintain W during normal execution.
See BIBLIOGRAPHY.md "Dynamic-Wind" and "Continuation-Wind Interaction".
func NewDynamicWindFrame ¶
func NewDynamicWindFrame(before, after values.Callable) DynamicWindFrame
NewDynamicWindFrame creates a new winding frame with a unique ID.
type EditPlan ¶
type EditPlan struct {
// contains filtered or unexported fields
}
EditPlan collects non-overlapping bytecode edits for a NativeTemplate. Edits are applied in a single pass via Apply, which rewrites code, sourceTableRefs, and branch offsets, then garbage-collects unreferenced sideTable entries.
Apply remaps these Arg categories automatically:
- PC offsets: Branch, BranchOnFalseValue, SaveContinuation
- SideTable indices: Complex (unreferenced entries are GC'd)
These are stable across Apply and not touched:
- Literal pool indices: LoadLiteral, LoadGlobal, StoreGlobal, PushLiteral, PushGlobal
- Local indices: LoadLocal, StoreLocal, PushLocal, CallLocal
- Cached binding indices: every OperandCachedBinding op (LoadCachedBinding, PushCachedBinding, CallCachedBinding, CallForeignCached and its tail variant, the promoted primitives) — the dominant operand class after peephole; Apply never touches tpl.cachedBindings
- Stack offsets: PeekK
func NewEditPlan ¶
func NewEditPlan(tpl *NativeTemplate) *EditPlan
NewEditPlan creates a new edit plan for the given template.
func (*EditPlan) AddLiteral ¶
func (p *EditPlan) AddLiteral(v values.Value) LiteralIndex
AddLiteral adds a value to the template's literal pool, with deduplication. Returns the index suitable for use in Instruction.Arg fields that reference the literal pool (OpLoadLiteral, OpLoadGlobal, OpStoreGlobal).
func (*EditPlan) Apply ¶
Apply rewrites the template according to all accumulated edits:
- Sorts and validates edits (panics on overlap)
- Builds a PC remap from old positions to new positions
- Fixes branch offsets for surviving original instructions
- Rebuilds code + sourceTableRefs, splicing in replacements
- Garbage-collects unreferenced sideTable entries, remaps OpComplex.Arg
Returns the net change in instruction count (negative means code shrunk).
type ErrBreakInterrupt ¶ added in v1.20.0
type ErrBreakInterrupt struct {
Handler values.Callable
// Tag identifies the break boundary (the prompt frame InstallBreakPrompt
// pushed) on the continuation chain. A driver does FindPrompt(Tag) to locate
// that frame and resolves the break there.
Tag *PromptTag
// BP is the breakpoint that matched, or nil when the stop is a step
// completion rather than a breakpoint.
BP *Breakpoint
}
ErrBreakInterrupt signals that the debugger wants execution suspended, either because a breakpoint matched or because a step completed. It propagates through the Go error return path and is resolved by the nearest driver (RunResumable at the top level, RunWithinBoundary in a surviving sub-context), which locates the break prompt frame via FindPrompt(Tag).
This is a signal, not an exception. It is NOT caught by Scheme exception handlers — only by the VM infrastructure that installed the break prompt.
func (*ErrBreakInterrupt) Error ¶ added in v1.20.0
func (p *ErrBreakInterrupt) Error() string
type ErrExceptionEscape ¶
type ErrExceptionEscape struct {
Condition values.Value // The raised condition/object
Source *syntax.SourceContext // Source location where exception was raised
StackTrace StackTrace // VM stack trace at raise point
}
ErrExceptionEscape carries an UNCAUGHT Scheme exception out to the embedder. It is produced by RaiseInPlace when the exception-handler stack is empty (no handler caught the condition) and bubbles to RunWithEscapeHandling, where the public Engine turns it into a wile.RuntimeError (engine.wrapRuntimeError reads Condition / Source / StackTrace). Handler dispatch itself no longer flows through this type — handlers ride the %exception-handlers parameter and are invoked in place by RaiseInPlace (see exception_raise.go), so the old handler-machinery fields (Continuable / Continuation / Handled / WindingStack) are gone.
func (*ErrExceptionEscape) ConditionText ¶
func (p *ErrExceptionEscape) ConditionText() string
ConditionText returns the raised condition's human-readable text alone — without the source-location prefix, the "error:"/"exception:" category word, or the stack trace that Error() adds. Callers that render their own prefix (the REPL's "Exception:", the CLI's "Error:", wile.RuntimeError's structured Source/StackTrace fields) use this to surface the message exactly once instead of embedding the whole Error() string and duplicating location and trace. A *NativeError yields its plain message; any other condition yields its SchemeString; a nil condition yields "<nil>".
Deliberately NOT shared with Error(): Error() gates a *NativeError's plain message on hasSource (a *NativeError with no source falls back to SchemeString there, for backward compatibility), whereas ConditionText always uses the plain message since it never pairs with a source prefix. The two ladders stay separate on purpose — do not "unify" them without preserving that divergence.
func (*ErrExceptionEscape) Error ¶
func (p *ErrExceptionEscape) Error() string
Error implements the error interface.
When Source is present and the condition is a NativeError, produces clean human-readable output like "file:5:3: error: division by zero". For non-NativeError conditions with Source, produces "file:5:3: exception: foo". When no Source is set, falls back to the original format for backward compat.
func (*ErrExceptionEscape) Unwrap ¶
func (p *ErrExceptionEscape) Unwrap() error
Unwrap returns the underlying error when the condition implements the error interface (e.g., *NativeError). This enables errors.Is/errors.As to traverse through ErrExceptionEscape into the wrapped error chain, supporting sentinel matching like errors.Is(err, werr.ErrDivisionByZero) from Go callers.
type ErrPromptAbort ¶
type ErrPromptAbort struct {
Tag *PromptTag
Values []values.Value
// SourceWinding is a .Copy() of the winding stack live at the abort ORIGIN — the
// escape point, which may be a deeper sub-context than the driver that catches the
// abort. When set, the driver reconciles dynamic-wind from it (not from its own
// p.windingStack) so the after-thunks between the escape point and the target
// prompt fire exactly once: removing exitFn's own UnwindTo fixes the exit-path
// double-fire (C2), and reconciling from the escape-point winding fixes the
// after-thunk silently skipped when escaping through a deeper sub (C3). nil means
// "reconcile from the driver's own winding" (the value-delivery aborts, e.g.
// composable continuation, where the abort site and driver share a winding).
SourceWinding WindingStack
}
ErrPromptAbort signals an abort to the nearest continuation prompt matching the given tag. It propagates up through Run() and is caught by RunWithEscapeHandling() or by call-with-continuation-prompt sub-contexts.
When caught, the handler finds the matching prompt frame, unwinds dynamic-wind extents, and invokes the prompt's handler with the abort values.
func (*ErrPromptAbort) Error ¶
func (p *ErrPromptAbort) Error() string
type ErrResumeContinuation ¶
type ErrResumeContinuation struct {
Tag *PromptTag
Segment *ComposableContinuation // carried UNRUN
Values []values.Value // resume args, already copied off the eval stack
SourceWinding WindingStack // .Copy() of the winding live at the (k v) site
// contains filtered or unexported fields
}
ErrResumeContinuation is the control signal that a call/cc-captured continuation is being invoked. It is NOT a value-delivery abort (that is ErrPromptAbort): it asks the nearest DefaultPromptTag driver loop to REINSTALL the carried segment into the live continuation and keep looping — the trampoline that replaces the per-resume nested sub.Run(). It implements error so it rides the existing return-err plumbing and errors.As matching across nested sub.Run() frames unchanged.
func (*ErrResumeContinuation) Error ¶
func (p *ErrResumeContinuation) Error() string
type ErrTimerInterrupt ¶
type ErrTimerInterrupt struct {
Handler values.Callable
// Tag identifies the with-timeout boundary (its finalizer frame) on the
// continuation chain. A driver does FindPrompt(Tag) to locate that frame on the
// chain the with-timeout actually installed — its OWN chain, even when the
// with-timeout sits inside a surviving sub-context — and resolves the timer there.
Tag *PromptTag
}
ErrTimerInterrupt signals that a wall-clock timer has expired. It propagates through the Go error return path and is resolved by the nearest driver (RunResumable at the top level, or RunWithinBoundary in a surviving sub-context), which locates the with-timeout's finalizer frame via FindPrompt(Tag).
This is a signal, not an exception. It is NOT caught by Scheme exception handlers — only by the VM infrastructure that installed the timer.
func (*ErrTimerInterrupt) Error ¶
func (p *ErrTimerInterrupt) Error() string
type ErrorContext ¶
type ErrorContext struct {
// contains filtered or unexported fields
}
ErrorContext carries diagnostic information captured at a raise site. It is attached as a continuation mark during exception handler dispatch, enabling Scheme code to inspect source location, stack trace, and continuation marks at the point where the exception was raised.
func NewErrorContext ¶
func NewErrorContext( source *syntax.SourceContext, stackTrace StackTrace, marks *ContinuationMarkSet, ) *ErrorContext
NewErrorContext creates an ErrorContext with the given diagnostics.
func (*ErrorContext) EqualTo ¶
func (p *ErrorContext) EqualTo(other values.Value) bool
EqualTo uses pointer identity. ErrorContext values are not structurally comparable — each raise site produces a unique context.
func (*ErrorContext) IsVoid ¶
func (p *ErrorContext) IsVoid() bool
IsVoid returns true if the receiver is nil.
func (*ErrorContext) Marks ¶
func (p *ErrorContext) Marks() *ContinuationMarkSet
Marks returns the continuation mark set snapshot from the raise site, or nil if not captured.
func (*ErrorContext) SchemeString ¶
func (p *ErrorContext) SchemeString() string
SchemeString returns the Scheme representation of the error context.
func (*ErrorContext) Source ¶
func (p *ErrorContext) Source() *syntax.SourceContext
Source returns the source location at the raise site.
func (*ErrorContext) SourceLocation ¶
func (p *ErrorContext) SourceLocation() string
SourceLocation returns the formatted source location string ("file:line:col"), or "" if unavailable.
func (*ErrorContext) StackTraceFrames ¶
func (p *ErrorContext) StackTraceFrames() StackTrace
StackTraceFrames returns the stack trace captured at the raise site.
type ExpanderCtx ¶
type ExpanderCtx interface {
Env() *environment.EnvironmentFrame
Expand(syntax.SyntaxValue) (syntax.SyntaxValue, error)
ExpandOnce(syntax.SyntaxValue) (syntax.SyntaxValue, bool, error)
IntroductionScope() *syntax.Scope
SetIntroductionScope(*syntax.Scope)
UseSiteScope() *syntax.Scope
SetUseSiteScope(*syntax.Scope)
}
ExpanderCtx abstracts the macro expansion context so that code needing expansion capabilities can depend on this interface rather than the concrete ExpanderContext. This enables the future machine/compilation sub-package to provide ExpanderContext without circular imports back to machine/.
type ForeignClosure ¶
type ForeignClosure struct {
// contains filtered or unexported fields
}
ForeignClosure wraps a Go function as a directly-callable Scheme procedure. Unlike MachineClosure, it holds the ForeignFunction directly and bypasses the bytecode VM — no template, no opcodes, no VM loop iteration.
func NewForeignClosure ¶
func NewForeignClosure(env *environment.EnvironmentFrame, pcnt int, variadic bool, fn ForeignFunction) *ForeignClosure
func (*ForeignClosure) AcceptsArity ¶
func (p *ForeignClosure) AcceptsArity(n int) bool
AcceptsArity reports whether this closure can be called with n arguments.
func (*ForeignClosure) Doc ¶
func (p *ForeignClosure) Doc() string
func (*ForeignClosure) Env ¶
func (p *ForeignClosure) Env() *environment.EnvironmentFrame
func (*ForeignClosure) EqualTo ¶
func (p *ForeignClosure) EqualTo(o values.Value) bool
EqualTo uses identity semantics — two foreign closures are equal only if they are the same pointer.
func (*ForeignClosure) Fn ¶
func (p *ForeignClosure) Fn() ForeignFunction
func (*ForeignClosure) Identity ¶ added in v1.20.0
func (p *ForeignClosure) Identity() *PrimitiveIdentity
Identity returns the registered primitive this closure was built from, or nil if its spec declared none. See PrimitiveIdentity for why a pointer compare on the closure itself is not the same question.
func (*ForeignClosure) IsVariadic ¶
func (p *ForeignClosure) IsVariadic() bool
func (*ForeignClosure) IsVoid ¶
func (p *ForeignClosure) IsVoid() bool
func (*ForeignClosure) Name ¶
func (p *ForeignClosure) Name() string
func (*ForeignClosure) ParameterCount ¶
func (p *ForeignClosure) ParameterCount() int
func (*ForeignClosure) SchemeString ¶
func (p *ForeignClosure) SchemeString() string
func (*ForeignClosure) SetDoc ¶
func (p *ForeignClosure) SetDoc(doc string)
func (*ForeignClosure) SetIdentity ¶ added in v1.20.0
func (p *ForeignClosure) SetIdentity(identity *PrimitiveIdentity)
SetIdentity records the registered primitive this closure was built from. It is called by the registry at registration; every closure the registry mints for one spec — in the sealed base and in each library environment — gets the same token.
func (*ForeignClosure) SetName ¶
func (p *ForeignClosure) SetName(name string)
func (*ForeignClosure) SetValidator ¶
func (p *ForeignClosure) SetValidator(v ForeignFunction)
SetValidator installs a contract validation function that runs before the implementation. Called during registration when contract enforcement is enabled.
func (*ForeignClosure) Validator ¶
func (p *ForeignClosure) Validator() ForeignFunction
Validator returns the installed contract validator, or nil if none.
type ForeignFunction ¶
type ForeignFunction func(mc CallContext) error
ForeignFunction is the signature for Go-implemented Scheme primitives. The CallContext provides access to arguments, the value register, and the cancellation context (via mc.Context()).
Most implementations only need CallContext methods (Arg, SetValue, SetValues, Authorizer, Context, EnvironmentFrame, Thread). Implementations that need full VM access (sub-context creation, continuation manipulation) should type-assert to *MachineContext.
type FreeList ¶
type FreeList[T any] struct { // contains filtered or unexported fields }
FreeList is a type-safe, observable object pool backed by a mutex-guarded slice instead of sync.Pool. Unlike sync.Pool, the freelist is NOT cleared by the garbage collector, so recycled objects (and any capacity they retain) persist across GC cycles.
Use FreeList when the GC feedback loop makes sync.Pool ineffective: high allocation rates trigger frequent GC, which clears sync.Pool, causing more allocations. FreeList breaks this loop at the cost of no automatic shrinkage.
func NewFreeList ¶
NewFreeList creates a FreeList[T] with the given name, constructor, and reset function.
func (*FreeList[T]) Acquire ¶
func (p *FreeList[T]) Acquire() *T
Acquire returns a recycled object from the freelist. If the freelist is empty, it calls newFn to allocate a new object.
func (*FreeList[T]) Release ¶
func (p *FreeList[T]) Release(v *T)
Release resets the object and appends it to the freelist.
func (*FreeList[T]) Stats ¶
func (p *FreeList[T]) Stats() PoolSnapshot
Stats returns a point-in-time snapshot of the freelist's counters.
type GoFrameFunc ¶ added in v1.20.0
type GoFrameFunc func(mc *MachineContext, v values.Value) error
GoFrameFunc is the post-body work RunBodyUnderGoFrame reifies as a continuation-chain frame. It receives the body's result — mc.GetValue(), so the first of several values, Void if none; a callback that cares about the count reads mc.GetValues() instead.
Two legal endings, which OperationGoReturn tells apart the same way applyForeign does for a foreign function:
- Deliver a value: leave it in the register (mc.SetValue); the frame returns it to its parent.
- Continue the VM: call ApplyCallable or another RunBodyUnder* helper; the frame's return is skipped and whatever was applied produces its result.
type InlinedOperation ¶
type InlinedOperation interface {
Operation
Apply(mc *MachineContext) (*MachineContext, error)
}
InlinedOperation is the interface for operations dispatched through the side table via OpComplex. These operations carry their own Apply method because the Run() loop delegates to them rather than inlining the logic.
type Instruction ¶
Instruction is a single VM instruction for the switch-dispatch loop. Op selects the operation; Arg carries an immediate operand whose meaning depends on Op:
- Zero-operand ops (Push, Pop, ...): Arg is unused (0).
- Single-operand ops (Branch, LoadLiteral, ...): Arg is the offset, index, or depth.
- Two-operand ops (LoadLocal, StoreLocal): Arg is bit-packed (slot in low 16, depth in high 16).
- OpComplex: Arg is the index into the template's sideTable.
Size: 8 bytes (uint16 Op + 2 bytes padding + int32 Arg).
func (Instruction) String ¶
func (instr Instruction) String() string
String returns a human-readable representation of the instruction.
type LiteralIndex ¶
type LiteralIndex int32
type MachineClosure ¶
type MachineClosure struct {
// contains filtered or unexported fields
}
MachineClosure is a linked closure (Church 1936, Landin 1964, Cardelli 1983): a pair of compiled code and the lexical environment at definition time.
closure = ⟨λ, E⟩, where: λ = template — compiled bytecode (NativeTemplate) E = (frame, parent) — the enclosing environment, kept as a pair Access cost: O(1) creation (capture pointer), O(depth) free variable lookup (traverse parent chain). Flat closures invert this trade-off. Invariant: E is a live pointer into the frame chain, not a copy. Mutations via set! are visible through the closure because the closure shares the frame, not a snapshot. Constrains: OperationMakeClosure (must link E to runtime parent), Apply (builds a fresh frame from the pair on every call, to prevent aliasing across recursive calls and SRFI-18 thread races). Constrained by: de Bruijn addressing (free vars addressed by slot,depth in E's chain), CESK model (E is the environment component).
See BIBLIOGRAPHY.md "Linked Closure Representation".
E is never materialized into one frame. Materializing it cost an extra 80-byte object per evaluated lambda that every consumer then took apart again: InitApplyFrame reads exactly p.local and p.parent and derives global/phase/namespace from the parent. The frame was a carrier, not an environment.
parent the runtime environment captured at closure creation — the ONLY
per-activation word, and the whole of what makes this a closure.
template the compiled body, which also owns the local parameter shape
(NativeTemplate.shape). One template, one shape: the shape is a
property of the compiled body, so every closure over a lambda
agrees on it and none of them need to carry it.
The shape was briefly a third field here, which is why an intermediate design paid 24 bytes to avoid the 80-byte frame. It is now on the template and this type is back to two words.
BOTH constructors capture parent eagerly, so there is one representation and no nil branch discriminating a second: OpMakeClosure passes mc.env, and NewClosureWithTemplate reads it off the frame it is handed. A nil parent is unreachable from production — NewClosureCapturing panics on one, and both NewClosureWithTemplate callers (extensions/eval PrimCompile, compilation/compile_syntax_rules.go createTransformerClosure) pass a frame from NewEnvironmentFrameWithParent, which panics on a nil parent. Apply faults on it anyway rather than running with no global, namespace or phases.
Reading it eagerly gives up one check an earlier late frame.Parent() read bought: a frame RELEASED after the closure was built zeroes its own parent, and this closure no longer notices. That check only ever covered the two NewClosureWithTemplate sites — OpMakeClosure has always captured eagerly, and builds every closure a Scheme program makes — and both of them now build a fresh frame instead of borrowing a pooled one, which is what actually closed the (compile ...) use-after-release the check was standing in for. The live protection is mc.envPooled = false at OpMakeClosure, not a nil read.
func NewClosureCapturing ¶ added in v1.20.0
func NewClosureCapturing(tpl *NativeTemplate, link *environment.EnvironmentFrame, free []values.Value) *MachineClosure
NewClosureCapturing builds a closure over a template whose shape is already recorded and the runtime environment captured at creation, without materializing the frame the two would combine into. This is the OpMakeClosure path. parent must be non-nil: a nil one would leave the closure with no record of what it closed over, and the compile-time frame reachable through the template holds placeholders, so any fallback would be a wrong answer rather than a crash.
func NewClosureWithTemplate ¶
func NewClosureWithTemplate(tpl *NativeTemplate, env *environment.EnvironmentFrame) *MachineClosure
NewClosureWithTemplate builds a closure over an already-materialized environment, for the two callers that build a template and its environment together rather than going through compileClosureBody. It splits env the same way OpMakeClosure does: the local half becomes the template's shape, the parent becomes the closure's captured environment. env must therefore have a parent, which NewEnvironmentFrameWithParent guarantees for both.
Recording the shape on the template is a write to a shared object, so it is sound only because both callers pass a template they just built and have not yet published. A template already carrying a different shape is a caller error, not a case to merge.
func (*MachineClosure) AcceptsArity ¶
func (p *MachineClosure) AcceptsArity(n int) bool
AcceptsArity reports whether this closure can be called with n arguments. Fixed-arity closures require exactly paramCount args; variadic closures require at least paramCount-1 (the rest parameter collects the remainder).
func (*MachineClosure) Doc ¶
func (p *MachineClosure) Doc() string
Doc returns the closure's documentation string from its compiled template.
func (*MachineClosure) Env ¶
func (p *MachineClosure) Env() *environment.EnvironmentFrame
Env materializes the environment from the template's shape and the captured parent. Callers get a fresh frame each time rather than a shared one, so this is a reflection and debugging accessor, not an apply-path call: Apply goes straight to InitApplyFrameWithParent and never builds this. The local half is copied by value (see EnvironmentFrame.local), so the result reads the same bindings, not a snapshot of them.
nil for a closure that recorded no environment, or over a template with no shape — the same degenerate states Apply faults on, reported here as "no environment" because callers already treat a nil frame that way.
func (*MachineClosure) EqualTo ¶
func (p *MachineClosure) EqualTo(o values.Value) bool
EqualTo is OBJECT IDENTITY, and must stay that way.
R7RS §6.1 requires equal? to agree with eqv? on procedures, and equal? reaches this method as its leaf comparison (values.Equal -> EqualTo), so any field-wise comparison here is an equal?/eqv? divergence at the Scheme level. It also decides member and assoc, which the stdlib defines over equal?.
Field-wise comparison is not merely risky, it cannot work: frame is the lambda's compile-time frame, a template constant shared by every evaluation, and parent is the activation. Two closures built by one lambda form in one activation therefore agree on every field while being distinct procedures. That is not hypothetical — it is reachable from a tail loop or a call/cc re-entry, and it made (equal? a b) answer #t where (eqv? a b) answered #f.
This read as safe before the shape+parent split only because each closure then carried a freshly allocated runtime frame, which made a field compare accidentally equivalent to identity. The accident is gone; the guarantee is now explicit.
func (*MachineClosure) Free ¶ added in v1.20.0
func (p *MachineClosure) Free() []values.Value
Free returns the closure's free-variable vector, in the slot order the template's FreeNames name. nil for a closure that captures nothing. The slice is the closure's own — treat it as read-only.
func (*MachineClosure) IsVoid ¶
func (p *MachineClosure) IsVoid() bool
func (*MachineClosure) Link ¶ added in v1.20.0
func (p *MachineClosure) Link() *environment.EnvironmentFrame
Link returns the static link: the environment the closure's apply frame hangs from. A nil result is not a shape, it is a closure that recorded no environment; see the type comment.
func (*MachineClosure) Name ¶
func (p *MachineClosure) Name() string
Name returns the closure's name from its compiled template.
func (*MachineClosure) SchemeString ¶
func (p *MachineClosure) SchemeString() string
func (*MachineClosure) Template ¶
func (p *MachineClosure) Template() *NativeTemplate
type MachineContext ¶
type MachineContext struct {
// contains filtered or unexported fields
}
MachineContext represents the execution context of a virtual machine. It holds the current environment, values, evaluation stack, continuation, and program counter. It is created from a MachineContinuation and can be modified during execution.
func AcquireTopLevelContext ¶
func AcquireTopLevelContext(ctx context.Context, tpl *NativeTemplate, env *environment.EnvironmentFrame) *MachineContext
AcquireTopLevelContext returns a pooled MachineContext initialized for top-level execution (no parent continuation). This eliminates the intermediate MachineContinuation allocation that NewMachineContinuation + NewMachineContext would otherwise perform when parent is nil.
The caller MUST call ReleaseTopLevelContext after Run returns.
func NewMachineContext ¶
func NewMachineContext(ctx context.Context, cont *MachineContinuation) *MachineContext
NewMachineContext creates a new machine context with the given context and continuation. The context enables cancellation/timeout support in the VM loop. For callers that don't need cancellation, pass context.Background().
func NewThreadSubContext ¶
func NewThreadSubContext(params SubContextParams, thread *values.Thread) *MachineContext
NewThreadSubContext creates a sub-context for a thread using previously captured parent state. Unlike NewSubContext, this doesn't access the parent MachineContext fields, making it safe to call from a different goroutine. The thread parameter should be the new thread object, which provides the thread identity for the new context.
This function is specifically designed for SRFI-18 thread creation. For other uses of sub-contexts (like map, for-each, dynamic-wind), use NewSubContext instead.
func RequireMachineContext ¶
func RequireMachineContext(cc CallContext, name string) (*MachineContext, error)
RequireMachineContext asserts that cc is a *MachineContext, returning a wrapped ErrNotAMachineContext naming the primitive on failure. Foreign primitives that need full VM internals (eval, load, expand, syntax-local-*) share this prologue instead of repeating the checked type assertion.
func (*MachineContext) Apply ¶
func (p *MachineContext) Apply(mcls *MachineClosure, vs ...values.Value) (*MachineContext, error)
func (*MachineContext) ApplyCallable ¶
func (p *MachineContext) ApplyCallable(callable values.Value, args ...values.Value) (*MachineContext, error)
ApplyCallable dispatches a procedure call to the appropriate handler based on the callee's concrete type. This is the unified entry point for all Scheme procedure application, symmetric with OperationApply.
Supported callable types:
- *MachineClosure: standard Scheme lambda
- *ForeignClosure: Go foreign function (direct call, no bytecode)
- *CaseLambdaClosure: R7RS case-lambda (§4.2.9)
- *Parameter: R7RS parameter object (§4.2.6)
- *ComposableContinuation: delimited continuation
- *CapturedContinuation: full call/cc continuation, resumed by returning an *ErrResumeContinuation trampoline signal to the RunResumable driver
Precondition: p.ctx must be set (always true for contexts created via NewMachineContext or NewSubContext).
func (*MachineContext) ApplyCaseLambda ¶
func (p *MachineContext) ApplyCaseLambda(clcls *CaseLambdaClosure, vs ...values.Value) (*MachineContext, error)
ApplyCaseLambda applies a case-lambda closure by finding the matching clause.
func (*MachineContext) Authorizer ¶
func (p *MachineContext) Authorizer() security.Authorizer
Authorizer returns the security authorizer from this context's namespace, falling back to the snapshot taken before the frame was released. See snapshotEngineState for why the fallback exists and why nil here is dangerous rather than merely absent.
func (*MachineContext) BarrierValid ¶
func (p *MachineContext) BarrierValid() *BarrierToken
BarrierValid returns the barrier validity flag for the current context. Non-nil means execution is inside a with-continuation-barrier; the pointer identity distinguishes which barrier. Nil means no active barrier.
Escape closures and composable continuations use this to detect barrier crossings: if the capture-time pointer differs from the invocation-time pointer, the continuation would cross a barrier boundary.
func (*MachineContext) BreakResumeValues ¶ added in v1.20.0
func (p *MachineContext) BreakResumeValues() []values.Value
BreakResumeValues returns the arguments a suspension handler must apply the resumable continuation to in order to resume unchanged: the value register frozen at the break point. Resuming with no arguments instead clears the register, because ReinstallSegment ends with SetValues(args...) and Restore does not carry a register of its own.
func (*MachineContext) CallDepth ¶
func (p *MachineContext) CallDepth() int
CallDepth returns the depth of the current continuation stack.
func (*MachineContext) CaptureInterruptContinuationAt ¶
func (p *MachineContext) CaptureInterruptContinuationAt(boundary *MachineContinuation) *MachineContinuation
CaptureInterruptContinuationAt captures the full VM execution state at an interrupt point as a deep-copied continuation chain, delimited at boundary: it slices the live chain down to (but excluding) boundary, so a timer interrupt captures the suspended thunk DELIMITED at its with-timeout finalizer frame (boundary), not the whole chain — the correct composable continuation to hand the handler. boundary == nil captures the full chain.
Unlike SliceContinuationAt, this includes the live registers (template, pc, env, evals, value) that haven't been saved by a SaveContinuation instruction. The MachineContext is not modified; the returned chain is a deep copy suitable for wrapping in a ComposableContinuation.
It still pushes the synthetic live frame first, so the in-progress frame's registers (template, pc, env, evals, value) — which no SaveContinuation has saved — are captured. SliceContinuationAt deep-copies the eval stacks, so the returned segment is independent of p.evals.
func (*MachineContext) CaptureStackTrace ¶
func (p *MachineContext) CaptureStackTrace(maxDepth int) StackTrace
CaptureStackTrace walks the continuation chain and builds a stack trace.
The walk spans sub-context boundaries via parentMC. When a trace is captured inside a sub-context — e.g. a parameter converter, eval/load, or an exception thunk that runs Scheme via NewSubContext — the inner frames alone would truncate at that sub-context's nil-rooted continuation chain. Hopping parentMC restores the full logical Scheme stack, inserting a foreignBoundaryName frame at each crossing.
This deliberately diverges from SliceContinuationAt, which walks the same cont.parent chain for *control* capture and MUST stop at the boundary to keep a captured continuation re-runnable (it cannot reproduce the Go residual between the inner and outer frames). A stack trace is read-only — it re-executes nothing — so spanning the boundary is both safe and desirable.
The walk stops at a context with isolatedMarks set (a re-invoked continuation running a grafted chain), mirroring findParameterInMarks: the trace then shows the continuation's own extent, not the invoker's.
The debugger's break-boundary frame is elided: it is VM scaffolding rather than a Scheme call, and it is transparent to the computation it wraps.
func (*MachineContext) CaptureSubContextParams ¶
func (p *MachineContext) CaptureSubContextParams() SubContextParams
CaptureSubContextParams extracts the state needed to create a sub-context in a different goroutine. This is used by thread creation to avoid race conditions when accessing the parent MachineContext from a child goroutine (T4 from architectural review).
Call this in the parent goroutine before creating the child goroutine, then pass the result to NewThreadSubContext in the child goroutine.
func (*MachineContext) ClosureEnv ¶ added in v1.20.0
func (p *MachineContext) ClosureEnv() *environment.EnvironmentFrame
ClosureEnv returns the environment a primitive should parent a ForeignClosure on when that closure OUTLIVES the primitive call — a finalizer handed to RunBodyUnderConsumer, an escape procedure, a timer teardown. It is the namespace's mutable runtime, which is long-lived and shared.
The fallback matters more than the happy path. p.env can be a detached frame with no namespace anywhere in its lexical chain (see MutableRuntimeOrNil), and then the only environment available is p.env itself — which, inside a primitive, is a POOLED apply frame that the closure would outlive. Parenting on it therefore clears envPooled, exactly as OpMakeClosure does when a Scheme closure captures mc.env. That clear is Invariant H at the Go level: "a frame with envPooled=true is never any other frame's parent" is what lets RestoreAndRelease recycle a frame without walking the parent chain, and it is what makes RunBodyUnderFrame's ownership transfer safe. Leaking the frame to the GC is the price, and it is the same price OpMakeClosure pays.
Use this rather than open-coding the fallback: five sites did, and the clear was missing from all five.
func (*MachineContext) CollectContinuationMarks ¶
func (p *MachineContext) CollectContinuationMarks(tag *PromptTag) *ContinuationMarkSet
CollectContinuationMarks walks the continuation chain and builds a ContinuationMarkSet snapshot. Collects marks from the current frame and all continuation frames up to and including the nearest frame with a matching promptTag.
func (*MachineContext) Context ¶
func (p *MachineContext) Context() context.Context
Context returns the context for this machine context.
func (*MachineContext) Counters ¶
func (p *MachineContext) Counters() VMCounters
Counters returns a snapshot of the performance counters for this context.
func (*MachineContext) CurrentContinuation ¶
func (p *MachineContext) CurrentContinuation() *MachineContinuation
func (*MachineContext) CurrentLocation ¶
func (p *MachineContext) CurrentLocation() *values.DebugLocation
CurrentLocation returns the current source location as a values.DebugLocation, or nil if no source info is available. Implements values.DebugState.
func (*MachineContext) CurrentSource ¶
func (p *MachineContext) CurrentSource() *syntax.SourceContext
CurrentSource returns the source location for the current execution point. When the current template has no source (e.g., inside a foreign function), walks up the continuation chain to find the nearest call site with source info. Continuation PCs are return addresses (one past the call), so pc-1 gives the call site.
func (*MachineContext) Debugger ¶
func (p *MachineContext) Debugger() *Debugger
Debugger returns the attached debugger, or nil if none.
func (*MachineContext) DeleteMark ¶
func (p *MachineContext) DeleteMark(key values.Value)
DeleteMark removes the continuation mark for key from the current frame. Nils the slice when empty to maintain the "nil = zero-cost" invariant. Uses eq? semantics for key comparison. Deletion uses swap-with-last for O(1) removal; insertion order is not preserved.
func (*MachineContext) EnvironmentFrame ¶
func (p *MachineContext) EnvironmentFrame() *environment.EnvironmentFrame
func (*MachineContext) Error ¶
func (p *MachineContext) Error(msg string) *SchemeError
Error creates a SchemeError with the current source location and stack trace but no sentinel cause, so errors.Is cannot match the result against any error family. Prefer WrapError(sentinel, msg) at any site whose failure has a sentinel — every production call site does (R9). Reach for Error only when no sentinel genuinely applies.
func (*MachineContext) EscapeCont ¶
func (p *MachineContext) EscapeCont() *MachineContinuation
EscapeCont returns the escape continuation for this context. This is set by foreign functions (like dynamic-wind) that need call/cc inside their sub-contexts to know where to continue after completion.
func (*MachineContext) Evals ¶
func (p *MachineContext) Evals() *Stack
Evals returns the eval stack for inspection (primarily for testing).
func (*MachineContext) ExecutingNamespace ¶ added in v1.20.0
func (p *MachineContext) ExecutingNamespace() *environment.Namespace
ExecutingNamespace returns the namespace this context is executing in, or nil when none was established.
This is what "the current namespace" means for a primitive that TARGETS one — (interaction-environment), 1-arg (eval …), (load …). Those three used to read mc.EnvironmentFrame().Namespace(), which is the registering namespace, so sandboxed code was handed the host's top level to define into.
func (*MachineContext) ExpanderContext ¶
func (p *MachineContext) ExpanderContext() ExpanderCtx
ExpanderContext returns the expander context, or nil if not in expansion context.
func (*MachineContext) FindPrompt ¶
func (p *MachineContext) FindPrompt(tag *PromptTag) (*MachineContinuation, bool)
FindPrompt walks the continuation chain to find the nearest frame with a matching prompt tag. Also checks the context's own prompt tag (set by call-with-continuation-prompt on sub-contexts). Returns the matching frame and true, or nil and false.
When the prompt is on the context itself (not a continuation frame), returns nil and true — the caller should treat this as "prompt at the boundary of this sub-context" and slice the entire continuation chain.
func (*MachineContext) FormatStackTrace ¶
func (p *MachineContext) FormatStackTrace(maxDepth int) string
FormatStackTrace returns a human-readable stack trace string. Implements values.DebugState.
func (*MachineContext) GetImmediateMark ¶
func (p *MachineContext) GetImmediateMark(key values.Value) values.Value
GetImmediateMark returns the nearest mark for key, checking the current frame first, then the immediately saved continuation frame.
This is the correct lookup for call-with-immediate-continuation-mark: in tail position, with-continuation-mark sets the mark on the live frame (mc.marks); in non-tail position, SaveContinuation moves mc.marks to mc.cont and nils the live frame. Both cases are handled here.
func (*MachineContext) GetMark ¶
func (p *MachineContext) GetMark(key values.Value) values.Value
GetMark returns the continuation mark for key on the current frame, or nil if no mark is set. Uses eq? semantics for key comparison. Invariant: nil is used exclusively as the "not found" sentinel. Mark values are always non-nil Scheme values (GetValue never returns nil); SetMark must never be called with a nil val, as nil would be indistinguishable from absent.
func (*MachineContext) GetValue ¶
GetValue returns the first (or only) value from the value register. Returns Void if the register is empty.
func (*MachineContext) GetValues ¶
func (p *MachineContext) GetValues() MultipleValues
GetValues returns all values from the value register as a MultipleValues slice. For the single-value case this allocates a one-element slice; callers on the hot path should use GetValue instead.
func (*MachineContext) IncrPC ¶
func (p *MachineContext) IncrPC()
IncrPC increments the program counter by one.
func (*MachineContext) InstallBreakPrompt ¶ added in v1.20.0
func (p *MachineContext) InstallBreakPrompt(handler values.Callable) *PromptTag
InstallBreakPrompt arms a break boundary on this context and returns its tag. It pushes a transparent prompt frame (returnTemplate) carrying that tag, so a break emitted anywhere below it routes here via a driver's FindPrompt, and records the boundary in p.brk so the emit site knows a suspension is possible.
handler may be nil at install time: the closure that suspends generally needs the tag this call mints, so callers install first and supply the handler with SetBreakHandler.
Unlike RunBodyUnderFrame this applies no body — runCompiled has already loaded the template to run — so it copies only the frame-push half: thread identity, a winding copy, a marks clone, and the current barrier.
A nil parent (p.cont == nil, the top-level case) is correct. The frame is transparent, so the body's value(s) flow through it untouched; on normal completion the VM runs off the end of the template and never restores the frame at all, and when it is restored — by a top-level tail call taking applyForeign's rooted arm instead of its rootless one — returnTemplate's OpRestoreContinuation returns on the nil parent with the value register intact.
func (*MachineContext) MaxCallDepth ¶
func (p *MachineContext) MaxCallDepth() int
MaxCallDepth returns the maximum call depth limit. 0 means unlimited. The stored value is always non-negative; SetMaxCallDepth clamps negatives.
func (*MachineContext) MaxStackSize ¶
func (p *MachineContext) MaxStackSize() uint64
MaxStackSize returns the maximum eval stack size limit. 0 means unlimited.
func (*MachineContext) NewSubContext ¶
func (p *MachineContext) NewSubContext() *MachineContext
NewSubContext creates a new MachineContext for running sub-calls (e.g., apply, map, for-each). The sub-context shares the global environment but has a fresh call stack, eval stack, and value register. This allows foreign functions to call Scheme closures without corrupting the parent context's state.
The parent's dynamic-wind winding stack is inherited automatically so that continuations captured inside the sub-context preserve the enclosing dynamic-wind context. For the rare sites that need a different stack (unwind truncation, exception cleanup), use NewSubContextWithWinding.
Note: Sub-contexts have isolated continuation chains (cont = nil). When call/cc captures a continuation inside a sub-context, it captures mc.Parent() which refers to the sub-context's chain (nil). For continuations to escape back to the outer context, the escape error propagates up through the call stack and is handled by RunWithEscapeHandling at the top level.
The parentMC field tracks the parent context, allowing call/cc to find an outer continuation for proper R7RS continuation semantics when captured inside sub-contexts.
The escapeCont field is inherited, allowing nested sub-contexts to know where execution should continue after their completion (set by dynamic-wind and similar constructs).
func (*MachineContext) NewSubContextWithTemplate ¶
func (p *MachineContext) NewSubContextWithTemplate( tpl *NativeTemplate, env *environment.EnvironmentFrame, ) *MachineContext
NewSubContextWithTemplate creates a sub-context configured to execute the given template in the given environment. This is the correct way for primitives like eval and load to create execution contexts — it propagates all parent fields automatically via NewSubContext, preventing the "forgotten field" bug class that NewMachineContext + manual setters is vulnerable to.
The template and env override NewSubContext's defaults (nil template, the parent's mutable runtime global). pc starts at 0 (pool zero-value).
The debugger is propagated here and NOT in NewSubContext. These two callers (eval and load) are the only ones that run freshly compiled code on a child context, so they are the only ones where a breakpoint could go unnoticed; map, for-each, apply, dynamic-wind bodies and call-with-values consumers all run their closure on the parent context and already break correctly.
func (*MachineContext) NewSubContextWithWinding ¶
func (p *MachineContext) NewSubContextWithWinding(windingStack WindingStack) *MachineContext
NewSubContextWithWinding creates a sub-context with an explicit winding stack instead of inheriting the parent's. Use this only when the sub-context must run with a winding stack that differs from the parent — for example, a truncated stack during unwind (machine_context_winding.go) or exception cleanup (prim_exceptions.go). All other sub-context creation should use NewSubContext.
func (*MachineContext) PC ¶
func (p *MachineContext) PC() int
func (*MachineContext) Parent ¶
func (p *MachineContext) Parent() *MachineContinuation
func (*MachineContext) ParentMC ¶
func (p *MachineContext) ParentMC() *MachineContext
ParentMC returns the parent machine context (for sub-contexts), or nil for top-level contexts.
func (*MachineContext) PopWindingFrame ¶
func (p *MachineContext) PopWindingFrame() (DynamicWindFrame, bool)
PopWindingFrame removes the innermost frame from the winding stack, reporting false when the stack was already empty.
func (*MachineContext) PromptTag ¶
func (p *MachineContext) PromptTag() *PromptTag
PromptTag returns the prompt tag for this context, or nil.
func (*MachineContext) PushValues ¶
PushValues appends values to the value register. If the register currently holds a single value, it is promoted to the multi-value representation before appending. This promote-then-append pattern avoids losing the existing single value when transitioning to the multi-value path.
Order matters: we nil singleValue *before* installing the promoted MultipleValues so the mutual-exclusion invariant is never violated, even in the intra-method transient window.
func (*MachineContext) PushWindingFrame ¶
func (p *MachineContext) PushWindingFrame(frame DynamicWindFrame)
PushWindingFrame adds a frame to the winding stack.
func (*MachineContext) ReinstallSegment ¶
func (p *MachineContext) ReinstallSegment( comp *ComposableContinuation, boundary *MachineContinuation, srcWinding WindingStack, vals []values.Value, isolate bool, revived []*escalatorArm, ) (bool, error)
ReinstallSegment installs a captured/composable continuation segment into the current context. It is the single resume primitive shared by composable resume (boundary = p.cont, isolate = compose) and abortive call/cc resume (boundary = the matching prompt frame from FindPrompt, or nil at the top-level context boundary; isolate = true).
Returns wasEmpty = true when the captured continuation is empty (no chain to drive): the values are delivered and the CALLER performs its own post-delivery control transfer (composable: returnImmediate; the DefaultPromptTag driver: terminate or continue).
Order is load-bearing and matches the pre-refactor applyComposableContinuation:
- marks BEFORE the winding reconcile — RestoreWithWindingFrom runs dynamic-wind before/after thunks (arbitrary Scheme that may read marks / parameters), so the marks snapshot must already be installed.
- AcquireSegment BEFORE Restore — AcquireSegment marks the chain shared (first invoke) so a later normal return through a reinstalled frame takes RestoreAndRelease's copy-don't-pool branch (shared-bit safety; the tail-frame-recycling-unsound failure class hides here).
- the escalator-arm revival set is decided by the CALLER, at the invocation site, and APPLIED here only after RestoreWithWindingFrom succeeds — a failed reinstatement never re-entered the frame, so it must contribute no revival.
revived carries that set (see pendingEscalatorRevivals). It is a caller argument rather than something computed here because this context is not always the one the continuation was invoked on: the call/cc trampoline re-raises out of any enclosing sub-context to the top driver, so p.cont here can be a chain that never held the arms' frames. nil for every resume that crosses no escalator, which is essentially all of them.
isolate is written unconditionally as a per-resume reset on the (now long-lived) driver context: a stale isolatedMarks from a prior resume must not leak into a later one (design guard #1). The broader stale-marks safety across separate top-level forms is discharged by the pool zeroing the whole *MachineContext on release (pool.go, via ReleaseTopLevelContext/ReleaseSubContext), not by this per-resume reset alone — if Acquire*Context ever stops zeroing, that guard breaks.
func (*MachineContext) ResolveParameterValue ¶
func (p *MachineContext) ResolveParameterValue(param *Parameter) values.Value
ResolveParameterValue returns the effective value of a parameter, checking continuation marks (from parameterize) before falling back to the base value. This is the Go-side equivalent of calling the parameter with 0 args.
func (*MachineContext) Restore ¶
func (p *MachineContext) Restore(cont *MachineContinuation)
func (*MachineContext) RestoreAndRelease ¶
func (p *MachineContext) RestoreAndRelease(cont *MachineContinuation)
RestoreAndRelease is the fast path for normal function return. It transfers the continuation's state into the MachineContext (like Restore) but avoids copying evals — instead it transfers ownership directly and pools the consumed frame. This is safe because normal return consumes the frame exactly once; call/cc and escape paths must use Restore (which copies).
Shared frames (marked by MarkChainShared during call/cc capture) cannot be pooled because a captured continuation may re-invoke them. For shared frames, evals are copied (like Restore) and the frame is left for GC instead of pooling.
The sequence for unshared frames:
- Release mc's current evals to the stack pool (it's dead after restore)
- Transfer cont's evals directly to mc (no copy)
- Nil cont.evals so releaseContinuation won't double-release it
- Pool the consumed continuation frame
func (*MachineContext) RestoreWithWindingFrom ¶
func (p *MachineContext) RestoreWithWindingFrom(cont *MachineContinuation, sourceStack, targetStack WindingStack) error
RestoreWithWindingFrom restores a continuation with proper dynamic-wind handling: it unwinds from sourceStack, rewinds to targetStack, then restores the machine state.
The source stack is explicit because the escape may have originated in a sub-context whose winding stack differs from the restoring context's. When call/cc captures inside a sub-context and the escape propagates up, the source stack holds frames the top-level context never saw. Pass p.WindingStack() to unwind from the restoring context's own extent.
If cont is nil (continuation captured in a sub-context), the winding runs but machine state is not restored; the caller handles continued execution.
Parameters:
- cont: The continuation to restore to
- sourceStack: The winding stack where the escape originated (for unwinding)
- targetStack: The winding stack to restore to (for rewinding)
func (*MachineContext) RewindTo ¶
func (p *MachineContext) RewindTo(target WindingStack, commonDepth int) error
RewindTo runs before thunks from common ancestor to target depth. Returns error if any before thunk fails.
func (*MachineContext) Run ¶
func (p *MachineContext) Run() error
Run executes the VM loop starting from the current pc. The pc is NOT reset here - callers are responsible for ensuring the correct initial pc:
- NewMachineContext copies pc from the continuation (typically 0 for fresh execution)
- Apply sets pc = 0 for fresh closure invocation
- Restore sets pc from the saved continuation for resumption
This design allows continuation resumption (e.g., raise-continuable) to work correctly by preserving the pc set by Restore rather than unconditionally resetting to 0.
Dispatch: Run always uses switch-dispatch over integer opcodes.
Context cancellation: The loop checks p.ctx.Done() every 1024 ops, allowing preemption via context.WithTimeout or context.WithCancel. This enables:
- Test timeouts that actually stop execution
- REPL interrupt support (Ctrl+C)
- Resource management for long-running computations
Run executes the VM loop using switch-dispatch with integer opcodes. Waves 1-10 are inlined as switch cases: stack, branch, load/store, fused calls, promoted primitives and arithmetic, closures, direct foreign calls. The remaining complex operations (macro expansion, build-syntax, syntax-case, continuation marks) are dispatched via OpComplex to the template's sideTable.
Set the context via SetContext() before calling Run().
func (*MachineContext) RunBodyUnderBarrier ¶
func (p *MachineContext) RunBodyUnderBarrier(body values.Value, token *BarrierToken) (*MachineContext, error)
RunBodyUnderBarrier reifies with-continuation-barrier. It pushes a transparent chain frame carrying the OUTER barrier token (the restore target), flips the live barrier to `token` for the body, and inline-applies body on the live chain (no sub-context). On the body's normal return the transparent frame's restore reverts p.barrierValid to the outer token and forwards the body's value(s); a continuation captured inside the body spans the frame, so resume-out replays the same revert. An abort skips the frame and lands on an outer frame whose stamped barrierValid reverts via the driver's Restore. Because barrierValid is now a chain-carried register, the crossing check at the (k v) site (applyCapturedContinuation / applyComposableContinuation) is unchanged and correct at every transition: a continuation captured inside records `token`; one captured outside records the outer barrier; invoking across the boundary trips the pointer check.
func (*MachineContext) RunBodyUnderConsumer ¶
func (p *MachineContext) RunBodyUnderConsumer(producer values.Value, consumer values.Value, args ...values.Value) (*MachineContext, error)
RunBodyUnderConsumer reifies a "run a body, then apply a consumer to its result" boundary on the live chain. It pushes a consumer apply-frame and inline-applies producer (to args) on the live chain, so a continuation captured inside producer spans the consumer frame and the rest of the program (fixing the sub-context producer truncation). On producer's normal return the consumer frame applies consumer to the produced values exactly once; a full call/cc continuation invoked inside producer aborts to DefaultPromptTag, discarding the chain-resident consumer frame (escape-past preserved).
call-with-values passes no args — its producer is a thunk. make-parameter passes the init value as the single arg to the user converter (the producer), with the consumer being a finalizer that wraps the converted value in a Parameter. The args path is the same one RunBodyUnderExitFrame already exercises.
func (*MachineContext) RunBodyUnderExitFrame ¶
func (p *MachineContext) RunBodyUnderExitFrame(body values.Value, tag *PromptTag, finalizer values.Value, exitArg values.Value) (*MachineContext, error)
RunBodyUnderExitFrame reifies call-with-exit. It pushes a prompt apply-frame carrying tag whose literal is `finalizer`, then inline-applies body (proc) with exitArg (the one-shot exit closure). On proc's normal return the frame applies finalizer to proc's value(s) — clearing the one-shot and forwarding the value(s); an (exit v) abort to tag routes via the driver's FindPrompt to this frame (no handler), which delivers v and re-enters the same finalizer template (idempotent). The frame is NOT transparent (returnTemplate): the one-shot must be cleared on the normal-return path, which RunBodyUnderPrompt's returnTemplate cannot do.
func (*MachineContext) RunBodyUnderFrame ¶
func (p *MachineContext) RunBodyUnderFrame(frame *MachineContinuation, body values.Value, args ...values.Value) (*MachineContext, error)
RunBodyUnderFrame pushes frame as mc.cont and inline-applies body on this context (no sub-context, no nested Run), so body runs ON THE LIVE CHAIN under frame: O(1) Go frames, and a continuation captured in body spans frame and everything below it. frame.parent MUST already be the current p.cont (the continuation constructors used by the callers below set it).
frame comes in three shapes:
- a transparent prompt frame (RunBodyUnderPrompt): carries a tag, and returnTemplate passes the body's value(s) straight through on normal return;
- a plain continuation frame (RunBodyUnderConsumer, raise-continuable, the raise escalator): no tag, so it is not an abort target, and its code runs on normal return;
- a non-transparent prompt frame (RunBodyUnderExitFrame, RunBodyUnderTimer): carries a tag AND runs a finalizer template on normal return, so it is reached both ways.
On normal completion the VM restores frame and executes its template; on abort to a frame-carried tag the driver's FindPrompt routes to it.
func (*MachineContext) RunBodyUnderGoFrame ¶ added in v1.20.0
func (p *MachineContext) RunBodyUnderGoFrame(body values.Value, fn GoFrameFunc, args ...values.Value) (*MachineContext, error)
RunBodyUnderGoFrame reifies "run a body, then do some work in GO" on the live chain: the Go-side counterpart of RunBodyUnderConsumer, with a Go callback in place of a Scheme consumer, so no ForeignClosure is minted and no apply frame is bound to carry the value across.
A primitive whose post-body work must happen in Go otherwise has to run the body in a sub-context to get the value back, truncating any continuation captured inside it. force is the first caller; see GoFrameFunc for the callback's two legal endings.
func (*MachineContext) RunBodyUnderPrompt ¶
func (p *MachineContext) RunBodyUnderPrompt(body values.Value, tag *PromptTag, handler values.Callable, args ...values.Value) (*MachineContext, error)
RunBodyUnderPrompt installs a continuation-chain prompt frame on top of the current chain and applies body INLINE on this context, so the body runs on the live continuation. A continuation captured inside body spans the prompt frame and everything below it; an abort to tag routes through the chain (FindPrompt) to this frame's handler in RunResumable.
The frame is transparent on normal completion (returnTemplate). tag identifies the prompt for abort/capture. handler may be nil: nil means the abort values become the prompt's result delivered to the parent (call-with-continuation-prompt with a #f handler); a non-nil handler is invoked by the driver with the abort values (call-with-continuation-prompt). call-with-exit does NOT use this wrapper — it needs a non-transparent finalizer frame (RunBodyUnderExitFrame).
func (*MachineContext) RunBodyUnderTimer ¶
func (p *MachineContext) RunBodyUnderTimer( timerCtx context.Context, cancel context.CancelFunc, thunk values.Value, handler values.Callable, ) (*MachineContext, error)
RunBodyUnderTimer reifies with-timeout. It installs a wall-clock timer for the duration of thunk and runs thunk INLINE on the live chain under a finalizer prompt frame (no sub-context), so a continuation captured inside thunk spans the finalizer frame and the rest of the program (the sub-context truncation is fixed). The frame carries a fresh tag so that a fired timer is routed to THIS with-timeout's frame by a driver's FindPrompt (resolveTimerInterrupt) — on whichever chain the with-timeout sits, top level or inside a surviving sub-context.
timerState is pushed as a LINKED stack (parent pointer) for nesting, and carries the tag plus the saved outer ctx. The finalizer does an IDEMPOTENT ABSOLUTE teardown — cancel the timer, restore the outer timer + ctx, forward the body's 0/1/N values — so it is correct on normal completion AND, run a second time, after the timer arm already tore the fired timer down (resolveTimerInterrupt restores the same outer state). This helper installs and restores p.timer directly: nesting requires pushing a child timer while the parent is still live, via the linked parent pointer.
func (*MachineContext) RunResumable ¶
func (p *MachineContext) RunResumable() (rerr error)
RunResumable drives the VM loop under a DefaultPromptTag prompt, catching control bounces (ErrPromptAbort and ErrResumeContinuation — the trampoline resume signal) and looping. It is the single resume/abort driver shared by every DefaultPromptTag owner. It handles continuation escapes that weren't caught by an enclosing call/cc — used at the top level (REPL and file execution) to catch continuations invoked outside their original dynamic extent.
When a continuation captured inside a foreign function (like dynamic-wind's thunk) is invoked from outside, the escape error propagates up. This method catches it and restores the continuation with proper dynamic-wind handling.
The loop resolves ErrPromptAbort, ErrResumeContinuation, ErrTimerInterrupt, and recovered panics. It never consults escapeCont.
When execution completes normally (Run returns nil), any remaining frames on the winding stack are unwound (after thunks are called).
func (*MachineContext) RunWithEscapeHandling ¶
func (p *MachineContext) RunWithEscapeHandling() error
RunWithEscapeHandling is the top-level entry point that runs the VM under the DefaultPromptTag prompt. It delegates to RunResumable, the shared abort/resume driver every DefaultPromptTag owner routes through.
func (*MachineContext) RunWithinBoundary ¶
func (p *MachineContext) RunWithinBoundary() error
RunWithinBoundary drives this sub-context like Run, but resolves an ErrPromptAbort whose tag names a prompt ON THIS CONTEXT'S OWN CHAIN inline — the way RunResumable's abort arm does at the top level — instead of letting it escape to a Go-stack errors.As catch in the calling primitive.
It exists because the continuation cluster reifies call-with-exit / call-with-continuation-prompt as continuation-CHAIN prompt frames run inline. A reified boundary that appears INSIDE a surviving sub-context (a with-continuation-barrier thunk, a RaiseInPlace handler, a dynamic-wind thunk, a parameter converter, ...) lands its prompt frame on THAT sub-context's chain. When it aborts, FindPrompt(tag) on the sub's own chain finds the frame and this driver routes the abort here; without it the abort escapes to the top RunResumable, whose FindPrompt walks the PARENT chain and cannot see the frame ("no prompt found for tag").
It is a strict superset of Run for any NewSubContext: such a context has promptTag == nil (NewSubContext does not inherit it), so FindPrompt only matches a prompt FRAME pushed inside the sub by a reified boundary. An abort whose tag is NOT on this chain — a full call/cc to DefaultPromptTag, or an exit targeting an OUTER boundary — is re-raised unchanged so the enclosing driver owns it; and ErrResumeContinuation (the call/cc trampoline signal) is never an ErrPromptAbort, so it always re-raises to the top RunResumable. No DefaultPromptTag install, no timer or panic handling: those belong to the one top-level RunResumable.
func (*MachineContext) SaveContinuation ¶
func (p *MachineContext) SaveContinuation(off int) error
SaveContinuation pushes a new continuation onto the machine context with the given offset to the current program counter. Returns ErrCallDepthExceeded if the call depth limit has been reached.
Note: callDepth is incremented BEFORE calling NewMachineContinuationFromMachineContext. The continuation's own callDepth is derived from mc.cont (the parent pointer), not from mc.callDepth, so this pre-increment does not affect the continuation's cached depth. See the comment on NewMachineContinuationFromMachineContext for why this matters.
func (*MachineContext) SetBreakHandler ¶ added in v1.20.0
func (p *MachineContext) SetBreakHandler(handler values.Callable)
SetBreakHandler installs the callable a break suspension applies, on the innermost armed boundary. It is a no-op when no boundary is armed.
func (*MachineContext) SetContext ¶
func (p *MachineContext) SetContext(ctx context.Context)
SetContext sets the context for this machine context. This should be called before Run() to enable context cancellation/timeout.
func (*MachineContext) SetDebugger ¶
func (p *MachineContext) SetDebugger(d *Debugger)
SetDebugger attaches a debugger to this context.
func (*MachineContext) SetEnvPooled ¶
func (p *MachineContext) SetEnvPooled(v bool)
SetEnvPooled controls whether the current environment frame will be recycled by RestoreAndRelease. Set to false after replacing env with a heap-allocated frame that must not be returned to envFramePool.
func (*MachineContext) SetEnvironmentFrame ¶
func (p *MachineContext) SetEnvironmentFrame(env *environment.EnvironmentFrame)
SetEnvironmentFrame replaces the current environment frame. Used by operations that push new scopes (e.g., OperationBindPatternVars).
func (*MachineContext) SetEscapeCont ¶
func (p *MachineContext) SetEscapeCont(cont *MachineContinuation)
SetEscapeCont sets the escape continuation for this context.
func (*MachineContext) SetExpanderContext ¶
func (p *MachineContext) SetExpanderContext(ctx ExpanderCtx)
SetExpanderContext sets the expander context for this machine context. This is called when invoking macro transformers to enable syntax-local-* primitives. Lazy-allocates the expansion sub-record on first write.
func (*MachineContext) SetMark ¶
func (p *MachineContext) SetMark(key, val values.Value)
SetMark sets a continuation mark on the current frame using eq? semantics. Updates an existing entry with the same key, or appends a new one.
func (*MachineContext) SetMaxCallDepth ¶
func (p *MachineContext) SetMaxCallDepth(n int)
SetMaxCallDepth sets the maximum call depth limit. 0 means unlimited. Negative values are clamped to 0 (also unlimited).
func (*MachineContext) SetMaxStackSize ¶
func (p *MachineContext) SetMaxStackSize(n uint64)
SetMaxStackSize sets the maximum eval stack size limit. 0 means unlimited.
func (*MachineContext) SetPC ¶
func (p *MachineContext) SetPC(v int)
SetPC sets the program counter. Used by PrimCallCC for inline lambda execution.
func (*MachineContext) SetPromptTag ¶
func (p *MachineContext) SetPromptTag(tag *PromptTag)
SetPromptTag sets the prompt tag on this context. Used by call-with-continuation-prompt to mark sub-contexts as prompt boundaries.
func (*MachineContext) SetSyntaxCaseState ¶
func (p *MachineContext) SetSyntaxCaseState(v any)
SetSyntaxCaseState installs the syntax-case expansion state on the context, or nil to clear it. In production, the only legitimate concrete type is *compilation.syntaxCaseState; the constraint is enforced by encapsulation (unexported field, single-package consumer) rather than the type system. Lazy-allocates the expansion sub-record on first write.
func (*MachineContext) SetThread ¶
func (p *MachineContext) SetThread(t *values.Thread)
SetThread sets the SRFI-18 thread identity on this context. Both the thread object and its ID are stored for efficient comparison.
func (*MachineContext) SetValue ¶
SetValue stores a single value in the value register without allocating. This is the hot path: every LoadLocal, LoadGlobal, LoadLiteral, Pull, Pop, MakeClosure, etc. goes through here.
func (*MachineContext) SetValues ¶
SetValues sets the value register. Three paths:
- len == 0: canonical empty state, both fields nil. Distinguishes the (nil, nil) empty register from (nil, []) which a naive 'multiValues = vs' would produce when vs is an empty-but-non-nil spread (e.g. SetValues(emptySlice...)).
- len == 1: zero-allocation fast path via singleValue.
- len > 1: fall back to the multiValues slice.
func (*MachineContext) SliceContinuationAt ¶
func (p *MachineContext) SliceContinuationAt(prompt *MachineContinuation) *MachineContinuation
SliceContinuationAt deep-copies the continuation chain segment from p.cont down to (but not including) the prompt frame. The returned chain's bottom frame has parent = nil, making it a standalone segment suitable for composable continuation capture.
func (*MachineContext) SnapshotReachableMarksInto ¶
func (p *MachineContext) SnapshotReachableMarksInto(comp *ComposableContinuation)
SnapshotReachableMarksInto attaches p's capture-time reachable marks to comp, so resuming comp restores the outer parameter/handler environment. Called at call/cc capture sites (PrimCallCC).
func (*MachineContext) SyntaxCaseState ¶
func (p *MachineContext) SyntaxCaseState() any
SyntaxCaseState returns the per-context syntax-case expansion state, or nil when not in a syntax-case expansion. The concrete type is owned by machine/compilation/; callers within that subpackage type-assert to *syntaxCaseState. See the comment on expansionState for why this is any-typed.
func (*MachineContext) Template ¶
func (p *MachineContext) Template() *NativeTemplate
func (*MachineContext) Thread ¶
func (p *MachineContext) Thread() *values.Thread
Thread returns the SRFI-18 thread object for this context, or nil for the primordial thread.
func (*MachineContext) ThreadID ¶
func (p *MachineContext) ThreadID() uint64
ThreadID returns the SRFI-18 thread ID for this context. 0 means the primordial thread (main goroutine).
func (*MachineContext) UnwindTo ¶
func (p *MachineContext) UnwindTo(commonDepth int) error
UnwindTo runs after thunks from innermost to the common ancestor. Returns error if any after thunk fails.
func (*MachineContext) WindingStack ¶
func (p *MachineContext) WindingStack() WindingStack
WindingStack returns the current winding stack.
func (*MachineContext) WrapError ¶
func (p *MachineContext) WrapError(err error, msg string) *SchemeError
WrapError wraps an existing error with the current source location and stack trace.
type MachineContinuation ¶
type MachineContinuation struct {
// contains filtered or unexported fields
}
func NewMachineContinuation ¶
func NewMachineContinuation(parent *MachineContinuation, tpl *NativeTemplate, env *environment.EnvironmentFrame) *MachineContinuation
NewMachineContinuation creates a new machine continuation with the given parent, template, environment frame, and initial values.
func NewMachineContinuationFromMachineContext ¶
func NewMachineContinuationFromMachineContext(mc *MachineContext, off int) *MachineContinuation
NewMachineContinuationFromMachineContext creates a new machine continuation from the given machine context and an offset to the program counter. The new continuation inherits the environment, template, and evaluation stack from the machine context.
callDepth derivation: the new frame's parent is mc.cont, so its depth is derived from mc.cont's cached depth — NOT from mc.callDepth. These values differ because SaveContinuation pre-increments mc.callDepth before calling this function, but other callers do not:
- SaveContinuation: mc.callDepth already incremented → mc.callDepth != chain length
- CaptureInterruptContinuationAt: on a rootless context (the timer's) both mc.callDepth == 0 and mc.cont == nil; on a debugger break neither holds, because InstallBreakPrompt pushed the boundary frame and counted it
Using mc.callDepth - 1 would underflow to -1 in that case (mc.callDepth == 0). The parent-pointer formula is correct for all callers and immune to underflow.
func NewMachineContinuationWithPrompt ¶
func NewMachineContinuationWithPrompt(parent *MachineContinuation, tpl *NativeTemplate, env *environment.EnvironmentFrame, tag *PromptTag, handler values.Callable) *MachineContinuation
NewMachineContinuationWithPrompt creates a continuation frame that acts as a continuation prompt. The tag identifies the prompt for abort/capture, and the handler is invoked when an abort reaches this prompt.
func (*MachineContinuation) CallDepth ¶
func (p *MachineContinuation) CallDepth() int
CallDepth returns the depth of the continuation stack. The depth is cached in each frame at creation time, so this is O(1).
func (*MachineContinuation) Copy ¶
func (p *MachineContinuation) Copy() *MachineContinuation
func (*MachineContinuation) DeepCopy ¶
func (p *MachineContinuation) DeepCopy() *MachineContinuation
DeepCopy creates a deep copy of the entire continuation chain. Each frame in the chain is copied, with parent pointers updated to point to the copied frames. This is needed for composable continuations which must be safely re-invoked multiple times.
func (*MachineContinuation) EnvironmentFrame ¶
func (p *MachineContinuation) EnvironmentFrame() *environment.EnvironmentFrame
func (*MachineContinuation) EqualTo ¶
func (p *MachineContinuation) EqualTo(o values.Value) bool
EqualTo compares by identity. A MachineContinuation is an internal continuation-chain node, never surfaced to Scheme equal?/eqv? (first-class continuations are *CapturedContinuation / *ComposableContinuation), so two distinct nodes are never "equal" in any observable sense. The former field-by-field comparison was structurally unsound (it mixed pointer-equal parent checks with value comparison of the rest) and effectively dead.
func (*MachineContinuation) GetValue ¶
GetValue returns the first (or only) value from the value register. Returns Void if the register is empty.
func (*MachineContinuation) GetValues ¶
func (p *MachineContinuation) GetValues() MultipleValues
GetValues returns all values from the value register as a MultipleValues slice. For the single-value case this allocates a one-element slice; callers on the hot path should use GetValue instead.
func (*MachineContinuation) IsVoid ¶
func (p *MachineContinuation) IsVoid() bool
func (*MachineContinuation) MarkChainShared ¶
func (p *MachineContinuation) MarkChainShared()
MarkChainShared marks every frame in the continuation chain as shared. Shared frames are not pooled by RestoreAndRelease — their evals are copied instead of transferred, preserving them for re-invocation.
Early-exits when a frame is already shared: all ancestors must already be shared from a prior capture (sharing propagates toward the root).
func (*MachineContinuation) PC ¶
func (p *MachineContinuation) PC() int
func (*MachineContinuation) Parent ¶
func (p *MachineContinuation) Parent() *MachineContinuation
func (*MachineContinuation) PromptHandler ¶
func (p *MachineContinuation) PromptHandler() values.Callable
func (*MachineContinuation) PromptTag ¶
func (p *MachineContinuation) PromptTag() *PromptTag
func (*MachineContinuation) PushValues ¶
PushValues appends values to the value register. If the register currently holds a single value, it is promoted to the multi-value representation before appending. This promote-then-append pattern avoids losing the existing single value when transitioning to the multi-value path.
Order matters: we nil singleValue *before* installing the promoted MultipleValues so the mutual-exclusion invariant is never violated, even in the intra-method transient window.
func (*MachineContinuation) SchemeString ¶
func (p *MachineContinuation) SchemeString() string
func (*MachineContinuation) SetPC ¶
func (p *MachineContinuation) SetPC(v int)
func (*MachineContinuation) SetPromptTag ¶
func (p *MachineContinuation) SetPromptTag(t *PromptTag)
func (*MachineContinuation) SetValue ¶
SetValue stores a single value in the value register without allocating. This is the hot path: every LoadLocal, LoadGlobal, LoadLiteral, Pull, Pop, MakeClosure, etc. goes through here.
func (*MachineContinuation) SetValues ¶
SetValues sets the value register. Three paths:
- len == 0: canonical empty state, both fields nil. Distinguishes the (nil, nil) empty register from (nil, []) which a naive 'multiValues = vs' would produce when vs is an empty-but-non-nil spread (e.g. SetValues(emptySlice...)).
- len == 1: zero-allocation fast path via singleValue.
- len > 1: fall back to the multiValues slice.
func (*MachineContinuation) Template ¶
func (p *MachineContinuation) Template() *NativeTemplate
type MacroEvaluator ¶
type MacroEvaluator interface {
// EvalTemplate evaluates a compiled template in the given environment
// and returns the resulting value. Used by compileAndEvalLambdaTransformer
// for define-syntax lambda transformers at compile time.
EvalTemplate(ctx context.Context, tpl *NativeTemplate, env *environment.EnvironmentFrame) (values.Value, error)
// InvokeTransformer calls a closure as a macro transformer.
// expanderCtx is set on the VM context for auxiliary syntax
// hygiene (R7RS Section 4.3.2); nil is valid when no hygiene context
// is needed (e.g. ExpandOnce). args are passed to Apply — one arg for
// syntax-rules/lambda transformers, three for ER macros (form, rename,
// compare). On success, the caller receives the MachineContext with the
// result in the value register and must call ReleaseSubContext when done.
InvokeTransformer(ctx context.Context, cls Closure, expanderCtx ExpanderCtx, args ...values.Value) (*MachineContext, error)
}
MacroEvaluator abstracts VM execution for the compiler and expander so that compile-time evaluation and transformer invocation can be tested and wired without depending on the concrete MachineContext construction path.
func NewVMMacroEvaluator ¶
func NewVMMacroEvaluator() MacroEvaluator
NewVMMacroEvaluator returns a MacroEvaluator backed by the real VM.
type MultipleValues ¶
MultipleValues represents multiple return values from a function. It is deliberately NOT a values.Value.
Multiple values are carried in the VM's dedicated multi-value register (vmState.multiValues), under a singleValue-XOR-multiValues invariant — never in a Value slot. The Value conformance this type used to carry was therefore unreachable as a Scheme datum, and it was actively harmful: []values.Value is not Go-comparable, so a MultipleValues boxed into a values.Value would fault any `==` or map-key hash of that interface. values.EqIdentity (eq?) is exactly such an `==`.
It still carries SchemeString and IsVoid for diagnostics, which leaves it ONE method — EqualTo(values.Value) — from silently re-entering the Value set as a non-comparable slice. TestSliceCarriersAreNotValues exists to stop that.
The type remains a convenient []values.Value alias for internal carriers (NativeTemplate.literals, the value register). It just is not a Scheme value.
func NewMultipleValues ¶
func NewMultipleValues(values ...values.Value) MultipleValues
NewMultipleValues creates a new MultipleValues from the given values.
func (MultipleValues) Copy ¶
func (p MultipleValues) Copy() MultipleValues
Copy creates a copy of the MultipleValues.
func (MultipleValues) EqualTo ¶
func (p MultipleValues) EqualTo(v MultipleValues) bool
EqualTo reports whether two value lists are element-wise equal.
It takes a concrete MultipleValues, not a values.Value: this type is not a Scheme datum and does not implement the Value interface.
func (MultipleValues) IsVoid ¶
func (p MultipleValues) IsVoid() bool
IsVoid reports whether the register content this carries is the absence of a result: no values at all, or exactly one void value.
Not part of any interface — see the type's doc comment. It is a predicate the VM asks of a register snapshot, kept because the callers want it, not because values.Value demands it.
func (MultipleValues) Len ¶
func (p MultipleValues) Len() int
Len returns the number of values in the MultipleValues.
func (MultipleValues) SchemeString ¶
func (p MultipleValues) SchemeString() string
SchemeString renders the values space-separated, as `(values …)` would print them. Diagnostic support (disassembly, test failure messages); not a Scheme external representation, since this type is not a Scheme datum.
type NamedCallable ¶
NamedCallable is implemented by callable values that carry a name and documentation string. MachineClosure, ForeignClosure, and CaseLambdaClosure all satisfy this interface.
type NativeTemplate ¶
type NativeTemplate struct {
// contains filtered or unexported fields
}
NativeTemplate is the compiled representation of a Scheme procedure. It is a trusted-producer surface: the operations are not validated, see the package doc.
func NewEmptyNativeTemplate ¶
func NewEmptyNativeTemplate() *NativeTemplate
NewEmptyNativeTemplate creates a new NativeTemplate with default empty parameters. This is used when a template is initialized without any known parameters or operations yet.
func NewNativeTemplate ¶
func NewNativeTemplate(pcnt int, vcnt int, vd bool, operations ...Operation) *NativeTemplate
NewNativeTemplate assembles a template taking pcnt parameters and vcnt local slots, variadic when vd, over an optional initial operation stream. Trusted-producer surface: the operations are not validated, see the package doc.
func (*NativeTemplate) AppendCachedBinding ¶
func (p *NativeTemplate) AppendCachedBinding(bd *environment.Binding) int32
AppendCachedBinding adds a *Binding to the cached bindings array, deduplicating by pointer identity. Returns the index for use as an OpLoadCachedBinding/OpPushCachedBinding operand.
func (*NativeTemplate) AppendInstruction ¶
func (p *NativeTemplate) AppendInstruction(instr Instruction)
AppendInstruction appends a single instruction with no source attribution.
func (*NativeTemplate) AppendInstructionWithSource ¶
func (p *NativeTemplate) AppendInstructionWithSource(src *syntax.SourceContext, instr Instruction)
AppendInstructionWithSource appends a single instruction to the integer-dispatch bytecode and tags it with the given source context.
func (*NativeTemplate) AppendOperations ¶
func (p *NativeTemplate) AppendOperations(ops ...Operation)
AppendOperations appends operations with no source attribution (index 0 = nil). Converts operations to instructions using AppendOperationsWithSource.
func (*NativeTemplate) AppendOperationsWithSource ¶
func (p *NativeTemplate) AppendOperationsWithSource(src *syntax.SourceContext, ops ...Operation)
AppendOperationsWithSource converts operations to instructions and tags each with the given source. Operations with a dedicated opcode (all of opcode.go's waves) become direct switch cases; the remaining complex operations (case-lambda closures, dynamic-wind, continuation marks, box/unbox, the un-fused FFI call) go through the sideTable and are dispatched via OpComplex. This is a public method for test use.
func (*NativeTemplate) AppendSideTableOp ¶
func (p *NativeTemplate) AppendSideTableOp(op InlinedOperation) Instruction
AppendSideTableOp adds a complex operation to the side table and returns an OpComplex instruction that references it.
func (*NativeTemplate) CachedBindings ¶
func (p *NativeTemplate) CachedBindings() []*environment.Binding
CachedBindings returns the compile-time resolved bindings array. Used by the disassembler to annotate binding references.
func (*NativeTemplate) Code ¶
func (p *NativeTemplate) Code() []Instruction
Code returns the integer-dispatch bytecode slice.
func (*NativeTemplate) CodeLen ¶
func (p *NativeTemplate) CodeLen() int
CodeLen returns the current code[] length (number of instructions emitted).
func (*NativeTemplate) Copy ¶
func (p *NativeTemplate) Copy() *NativeTemplate
func (*NativeTemplate) Doc ¶
func (p *NativeTemplate) Doc() string
func (*NativeTemplate) EnableCoverage ¶
func (p *NativeTemplate) EnableCoverage()
EnableCoverage allocates the per-PC executed array (if not already allocated) so the VM dispatch loop will record executions. Length is kept parallel to code via AppendInstruction. Idempotent: safe to call multiple times; an existing array is preserved.
func (*NativeTemplate) Executed ¶
func (p *NativeTemplate) Executed() []bool
Executed returns the per-PC executed array, or nil if coverage is disabled. Returned slice aliases internal state; callers must not resize it.
func (*NativeTemplate) FreeBoxed ¶ added in v1.20.0
func (p *NativeTemplate) FreeBoxed() []bool
FreeBoxed reports, per free-vector slot, whether that slot holds a shared box rather than a copied value. nil when no slot is boxed. Read-only.
func (*NativeTemplate) FreeNames ¶ added in v1.20.0
func (p *NativeTemplate) FreeNames() []*values.Symbol
FreeNames returns the free-variable names in free-vector slot order. nil for a template that closes over nothing, and for every template not compiled as a closure body. The slice is the template's own — treat it as read-only.
func (*NativeTemplate) IncrementParameterCount ¶
func (p *NativeTemplate) IncrementParameterCount()
IncrementParameterCount adds one to the parameter count.
func (*NativeTemplate) IsCoverageEnabled ¶
func (p *NativeTemplate) IsCoverageEnabled() bool
IsCoverageEnabled reports whether coverage tracking is active on this template.
func (*NativeTemplate) IsVariadic ¶
func (p *NativeTemplate) IsVariadic() bool
func (*NativeTemplate) IsVoid ¶
func (p *NativeTemplate) IsVoid() bool
func (*NativeTemplate) Literals ¶
func (p *NativeTemplate) Literals() MultipleValues
Literals returns the literals pool.
func (*NativeTemplate) MaybeAppendLiteral ¶
func (p *NativeTemplate) MaybeAppendLiteral(v values.Value) LiteralIndex
func (*NativeTemplate) Name ¶
func (p *NativeTemplate) Name() string
func (*NativeTemplate) Operations ¶
func (p *NativeTemplate) Operations() Operations
Operations reconstructs the operation sequence from the bytecode. Converts Instructions back to Operation values for compatibility with existing code that expects Operations (e.g., tests, EqualTo).
func (*NativeTemplate) Optimize ¶
func (p *NativeTemplate) Optimize()
Optimize performs a peephole optimization pass on the template's bytecode. It removes dead instructions identified by pattern-matching rules, fixes branch offsets to account for removed instructions, and compacts the code and source reference arrays in parallel.
Superinstruction formation (Ertl & Gregg 2003). Fusing adjacent instructions reduces dispatch overhead in the Run() switch loop.
Four EditPlan passes, each applied before the next matches on its output:
1. markDeadLoadVoidEdits, fuseLoadPush, fusePullApply
Load* + Push → Push* (eliminates 1 dispatch)
Pull + Apply → PullApply (eliminates 1 dispatch)
2. fuseCallForeignCached
PushCachedBinding ... PullApply → CallForeignCached
(SaveCont retained; eliminates ~5 dispatches)
or, for a promoted primitive, one of the 34 promoted opcodes
3. fuseCallGeneric → CallLocal / CallCachedBinding
4. fusePromotedCompoundArgs (gated on a preceding OpReleaseEnvFrame)
Cost model: each fusion saves one (fetch opcode + switch branch).
In switch-dispatch interpreters, dispatch dominates execution time.
Invariant: fusions must not change observable semantics. Branch
offsets are recomputed after compaction.
Constrains: Run() must implement fused opcodes with identical
semantics to the original sequence.
Constrained by: must preserve semantic equivalence of fused
instruction sequences.
See BIBLIOGRAPHY.md "Superinstruction Formation".
After compacting its own code, Optimize recurses into any *NativeTemplate values in the literals pool (lambda closures compiled as sub-templates).
Idempotent: a second call finds nothing to remove.
func (*NativeTemplate) ParameterCount ¶
func (p *NativeTemplate) ParameterCount() int
func (*NativeTemplate) PatchInstructionArg ¶
func (p *NativeTemplate) PatchInstructionArg(codeIdx int, arg int32)
PatchInstructionArg updates the Arg field of the instruction at code[codeIdx]. Used for patching branch offsets and continuation save offsets after the target PC is known.
An out-of-range codeIdx panics rather than returning an error, and arg is not checked against the opcode's operand domain: a well-formed-looking patch can defer the panic to the dispatch loop. Trusted-producer surface, see the package doc.
func (*NativeTemplate) RetainsLexicalEnv ¶ added in v1.20.0
func (p *NativeTemplate) RetainsLexicalEnv() bool
RetainsLexicalEnv reports whether closures over this template must keep the creating frame as their static link rather than the lexical root.
func (*NativeTemplate) SchemeString ¶
func (p *NativeTemplate) SchemeString() string
func (*NativeTemplate) SetDoc ¶
func (p *NativeTemplate) SetDoc(doc string)
func (*NativeTemplate) SetFreeLayout ¶ added in v1.20.0
func (p *NativeTemplate) SetFreeLayout(names []*values.Symbol, boxed []bool)
SetFreeLayout records the free-variable names in free-vector slot order and, parallel to them, which slots are boxed. Written once, by compileClosureBody, before the body is compiled. boxed may be nil when no slot is boxed; when it is not nil it must have the same length as names.
func (*NativeTemplate) SetName ¶
func (p *NativeTemplate) SetName(name string)
func (*NativeTemplate) SetRetainsLexicalEnv ¶ added in v1.20.0
func (p *NativeTemplate) SetRetainsLexicalEnv()
SetRetainsLexicalEnv records that closures over this template must keep the creating frame as their static link. See the field comment.
func (*NativeTemplate) SetShape ¶ added in v1.20.0
func (p *NativeTemplate) SetShape(env *environment.EnvironmentFrame)
SetShape records the frame this template's body was compiled against. Called once, by whoever compiled the body, before any closure over the template can exist — compileClosureBody for lambda and case-lambda clauses, and the two callers that build a template and its environment together (extensions/eval PrimCompile, createTransformerClosure).
func (*NativeTemplate) SetVariadic ¶
func (p *NativeTemplate) SetVariadic()
SetVariadic marks this template as accepting a variadic rest argument.
func (*NativeTemplate) Shape ¶ added in v1.20.0
func (p *NativeTemplate) Shape() *environment.EnvironmentFrame
Shape returns the frame this template's body was compiled against, whose local half every closure over the template applies with. nil for a template that is not a closure body; see the field comment.
func (*NativeTemplate) SideTable ¶
func (p *NativeTemplate) SideTable() []InlinedOperation
SideTable returns the complex operations referenced by OpComplex instructions.
func (*NativeTemplate) SourceAt ¶
func (p *NativeTemplate) SourceAt(pc int) *syntax.SourceContext
SourceAt returns the source location for the operation at pc. Returns nil if pc is out of bounds or no source was recorded. O(1) lookup via the parallel sourceTableRefs array.
type OpCode ¶
type OpCode uint16
OpCode is an integer opcode for the switch-dispatch VM loop. Operations migrated from interface dispatch to integer dispatch get a dedicated OpCode. Complex operations that remain as interface values use OpComplex with a side table index.
Adding a new opcode requires changes in:
- opcode.go — add OpXxx constant and entry in opcodeTable (name + metadata flags; operandKind must match step 3's extraction logic)
- machine_context.go Run() — add dispatch case in the main switch
- native_template.go — add a case in instructionToOperation() (required unless the new op's operandKind is OperandCachedBinding or OperandLocalIdx, which the default branch decomposes generically) and in operationToInstruction() (required for operand-bearing ops; zero-operand ops fall through the default branch, which cross-checks opcodeTable[kind].operandKind == OperandNone)
- operation_xxx.go — create new operation type (or add to existing file)
- op_kind.go — add OpKind() returning the new OpCode (or compilation/op_kind.go for compilation/ types); for OpComplex types, also add a var _ InlinedOperation assertion
- compile_*.go — add compiler method to emit the new opcode
- Relevant _test.go files
- peephole.go — if the new op participates in fusion/chaining (e.g. loadToFusedPush)
For promoted primitive ops specifically, see the guide comment at the top of call_promoted.go — promoted ops have a different (smaller) set of edit sites.
const ( OpInvalid OpCode = iota OpPush OpPop OpPull OpLoadVoid OpDrop OpPopEnv OpReleaseEnvFrame // Release the current (dead, pool-owned) env frame before a reclaimable tail call OpApply OpUnpackListToStack OpRestoreContinuation OpBranchOnFalseValue OpBranch OpSaveContinuation OpLoadLiteral OpLoadGlobal OpStoreGlobal OpPeekK OpPushEnv // Push new env frame with Arg local slots OpSelfTailCall // Self-recursive tail call: pop Arg's high half of env frames, rebind its low half of param slots in place, pc=0 OpLoadLocal OpStoreLocal OpBoxSlot // Replace slot's contents with a fresh box holding them OpStoreThroughBox // Pop a value and write it INTO the slot's box OpUnbox // Replace the value register with the box's contents OpPushLiteral // LoadLiteral + Push OpPushGlobal // LoadGlobal + Push OpPushLocal // LoadLocal + Push OpPullApply // Pull + Apply OpMakeClosure OpLoadFree // Load free[Arg] into the value register OpPushFree // LoadFree + Push OpCallFree // Resolve free[Arg] as the callee, drain args, ApplyCallable OpStoreFree // Pop a value and write it INTO the box at free[Arg] OpLoadCachedBinding // Load from compile-time resolved *Binding OpPushCachedBinding // LoadCachedBinding + Push (fused) OpCallForeignCached // Non-tail: call ForeignClosure, then mc.pc++ OpCallForeignCachedTail // Tail: call ForeignClosure, then returnImmediate() OpCallLocal // Resolve local binding, drain args, ApplyCallable OpCallCachedBinding // Resolve cached binding, drain args, ApplyCallable OpEqQ // Non-tail inlined eq? OpEqQTail // Tail inlined eq? OpVectorQ // Non-tail inlined vector? OpVectorQTail // Tail inlined vector? OpVectorRef // Non-tail inlined vector-ref OpVectorRefTail // Tail inlined vector-ref OpNullQ // Non-tail inlined null? OpNullQTail // Tail inlined null? OpPairQ // Non-tail inlined pair? OpPairQTail // Tail inlined pair? OpCar // Non-tail inlined car OpCarTail // Tail inlined car OpCdr // Non-tail inlined cdr OpCdrTail // Tail inlined cdr OpAdd // Non-tail inlined 2-arg + OpAddTail // Tail inlined 2-arg + OpSub // Non-tail inlined 2-arg - OpSubTail // Tail inlined 2-arg - OpNumLt // Non-tail inlined 2-arg < OpNumLtTail // Tail inlined 2-arg < OpNumLe // Non-tail inlined 2-arg <= OpNumLeTail // Tail inlined 2-arg <= OpNumGt // Non-tail inlined 2-arg > OpNumGtTail // Tail inlined 2-arg > OpNumGe // Non-tail inlined 2-arg >= OpNumGeTail // Tail inlined 2-arg >= OpNumEq // Non-tail inlined 2-arg = OpNumEqTail // Tail inlined 2-arg = OpCons // Non-tail inlined cons OpConsTail // Tail inlined cons OpMul // Non-tail inlined 2-arg * OpMulTail // Tail inlined 2-arg * OpDiv // Non-tail inlined 2-arg / OpDivTail // Tail inlined 2-arg / OpSetCdr // Non-tail inlined set-cdr! OpSetCdrTail // Tail inlined set-cdr! // OpPushValues spreads the value register's 0/1/N values onto the eval // stack. Unlike OpPush — which delivers exactly one value into one slot // and raises when the register holds any other count — this op is the // multiple-value delivery seam, used only by applyToValuesCode to hand a // body's results to a consumer (call-with-values and friends). OpPushValues OpComplex )
type OperandKind ¶
type OperandKind uint8
OperandKind classifies what an opcode's Arg field means. Used by cold-path consumers (Disassemble, instructionToOperation) to avoid re-deriving operand semantics in per-opcode switch branches.
const ( // OperandNone means Arg is unused (zero-operand ops). OperandNone OperandKind = iota // OperandRaw means Arg is a meaningful integer but needs no resolution // (e.g., PushEnv slot count, PeekK depth). OperandRaw // OperandLiteralIdx means Arg indexes into the literals pool. OperandLiteralIdx // OperandLocalIdx means Arg is a bit-packed (slot, depth) pair. OperandLocalIdx // OperandBranchOffset means Arg is a relative PC offset. OperandBranchOffset // OperandCachedBinding means Arg indexes into cachedBindings. OperandCachedBinding // OperandSideTable means Arg indexes into the side table. OperandSideTable )
type Operation ¶
Operation is the base interface for all bytecode operations. Every operation is also a values.Value so it can appear in the literals pool and be printed. Operations that are inlined into the Run() switch loop only need this base interface. Operations dispatched through the side table (OpComplex) must additionally implement InlinedOperation.
OpKind reports the OpCode the operation encodes to. For operations that have a dedicated opcode in the main Run() switch, OpKind returns that opcode. For operations dispatched via the side table, OpKind returns OpComplex. This is the discriminator the compiler consults in operationToInstruction; placing it on the interface lets each type carry its dispatch identity rather than re-deriving it via a central type switch.
type OperationApply ¶
type OperationApply struct {
OperationBase
}
OperationApply is the bytecode operation that dispatches procedure calls. The compiler emits it after pushing arguments onto the eval stack and placing the callee in the value register. Apply pops all arguments and delegates to MachineContext.ApplyCallable, which handles the six callable types (MachineClosure, ForeignClosure, CaseLambdaClosure, Parameter, ComposableContinuation, CapturedContinuation).
func NewOperationApply ¶
func NewOperationApply() *OperationApply
NewOperationApply returns a new apply operation.
func (*OperationApply) EqualTo ¶
func (p *OperationApply) EqualTo(o values.Value) bool
EqualTo returns true if o is also an OperationApply (identity by type).
func (*OperationApply) OpKind ¶
func (*OperationApply) OpKind() OpCode
type OperationBase ¶
type OperationBase struct {
// contains filtered or unexported fields
}
OperationBase provides default SchemeString, IsVoid, and String methods for VM operations. Embed as the first field in operation structs. EqualTo is intentionally not provided: Go requires per-type assertions.
func NewOperationBase ¶
func NewOperationBase(opName string) OperationBase
NewOperationBase creates an OperationBase with the given Scheme name. The name is used as: "#<" + opName + ">".
func NewOperationBaseWithGoName ¶
func NewOperationBaseWithGoName(opName, goName string) OperationBase
NewOperationBaseWithGoName creates an OperationBase with both a Scheme name and a separate Go fmt.Stringer name.
func (OperationBase) IsVoid ¶
func (p OperationBase) IsVoid() bool
IsVoid returns false; operations are never void. Unlike other values.Value implementations, this does not handle nil receivers — operations are bytecode instructions constructed by the compiler and stored in NativeTemplate slices; they are never nil.
func (OperationBase) SchemeString ¶
func (p OperationBase) SchemeString() string
SchemeString returns the Scheme representation of the operation.
func (OperationBase) String ¶
func (p OperationBase) String() string
String returns the Go string representation of the operation.
type OperationBoxSlot ¶ added in v1.20.0
type OperationBoxSlot struct {
OperationBase
LocalIndex *environment.LocalIndex
}
OperationBoxSlot replaces a local slot's contents with a fresh box holding them. Emitted once per boxed slot at the binder, after the slot's initial value is in place.
One opcode rather than a load/wrap/store triple so the peephole cannot split the pair, and so re-entering the body — which OpSelfTailCall does, by jumping to pc=0 — allocates a FRESH cell per iteration rather than reusing the one the previous iteration's closures captured.
func NewOperationBoxSlot ¶ added in v1.20.0
func NewOperationBoxSlot(li *environment.LocalIndex) *OperationBoxSlot
NewOperationBoxSlot creates a box-the-slot operation for li.
func (*OperationBoxSlot) EqualTo ¶ added in v1.20.0
func (p *OperationBoxSlot) EqualTo(o values.Value) bool
EqualTo compares the target slot.
func (*OperationBoxSlot) OpKind ¶ added in v1.20.0
func (*OperationBoxSlot) OpKind() OpCode
func (*OperationBoxSlot) SchemeString ¶ added in v1.20.0
func (p *OperationBoxSlot) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationBoxValues ¶
type OperationBoxValues struct {
OperationBase
}
OperationBoxValues reduces the value register to something a following OpPush saves as exactly one eval-stack slot, so callers that must preserve a multiple-value result across an intervening call (dynamic-wind's after-thunk) keep a fixed one-slot footprint regardless of value count. Paired with OperationUnboxValues.
Zero or several values become a *BoxedValues carrier. Exactly one value is left ALONE: it already occupies one slot, so boxing it bought nothing and cost three allocations per dynamic-wind (the carrier, its slice, and the Clone on the way back out). That is the overwhelmingly common case, and skipping it is worth ~5.7% on a dynamic-wind-bound workload.
Pre: value register = v1 … vN Post: N == 1: unchanged. Otherwise a carrier holding v1 … vN. pc++
(the carrier prints as #<boxed-values>; it never escapes to Scheme)
func NewOperationBoxValues ¶
func NewOperationBoxValues() *OperationBoxValues
func (*OperationBoxValues) Apply ¶
func (*OperationBoxValues) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationBoxValues) OpKind ¶
func (*OperationBoxValues) OpKind() OpCode
type OperationBranchOffsetImmediate ¶
type OperationBranchOffsetImmediate struct {
OperationBase
Offset int
}
func NewOperationBranchOffsetImmediate ¶
func NewOperationBranchOffsetImmediate(offset int) *OperationBranchOffsetImmediate
func (*OperationBranchOffsetImmediate) EqualTo ¶
func (p *OperationBranchOffsetImmediate) EqualTo(o values.Value) bool
func (*OperationBranchOffsetImmediate) OpKind ¶
func (*OperationBranchOffsetImmediate) OpKind() OpCode
func (*OperationBranchOffsetImmediate) SchemeString ¶
func (p *OperationBranchOffsetImmediate) SchemeString() string
SchemeString overrides OperationBase to include offset value.
type OperationBranchOnFalseValueOffsetImmediate ¶
type OperationBranchOnFalseValueOffsetImmediate struct {
OperationBase
Offset int
}
OperationBranchOnFalseValueOffsetImmediate branches if the value register is #f. This reads directly from the value register instead of popping from the eval stack, eliminating the Push instruction that would otherwise be needed.
Peephole optimization (Aho et al., Compilers §8.9): examines a small window of generated instructions and replaces inefficient patterns. Here, the Push+BranchOnFalse+Pop sequence is replaced with a single BranchOnFalseValue that reads the value register directly. See BIBLIOGRAPHY.md "Peephole Optimization".
func NewOperationBranchOnFalseValueOffsetImmediate ¶
func NewOperationBranchOnFalseValueOffsetImmediate(offset int) *OperationBranchOnFalseValueOffsetImmediate
func (*OperationBranchOnFalseValueOffsetImmediate) EqualTo ¶
func (p *OperationBranchOnFalseValueOffsetImmediate) EqualTo(o values.Value) bool
func (*OperationBranchOnFalseValueOffsetImmediate) OpKind ¶
func (*OperationBranchOnFalseValueOffsetImmediate) OpKind() OpCode
func (*OperationBranchOnFalseValueOffsetImmediate) SchemeString ¶
func (p *OperationBranchOnFalseValueOffsetImmediate) SchemeString() string
SchemeString overrides OperationBase to include offset value.
type OperationDrop ¶
type OperationDrop struct {
OperationBase
}
OperationDrop removes the top value from the eval stack without affecting the value register. This is used when we need to clean up the stack but preserve the current result.
func NewOperationDrop ¶
func NewOperationDrop() *OperationDrop
func (*OperationDrop) OpKind ¶
func (*OperationDrop) OpKind() OpCode
type OperationGoReturn ¶ added in v1.20.0
type OperationGoReturn struct {
OperationBase
// contains filtered or unexported fields
}
OperationGoReturn calls a Go function with the value register's contents. It is the body of the frame RunBodyUnderGoFrame pushes — the seam that lets post-body work written in GO sit on the continuation chain, where every other RunBodyUnder* member can only hand a body's result to a SCHEME procedure (an apply-frame's consumer).
Pre: value register = the body's result, mc.cont = the frame's parent Post: fn has run; pc++ (so the frame returns) unless fn reconfigured the VM
func NewOperationGoReturn ¶ added in v1.20.0
func NewOperationGoReturn(fn GoFrameFunc) *OperationGoReturn
NewOperationGoReturn creates an OperationGoReturn that calls fn. The per-call path uses RunBodyUnderGoFrame instead, which co-allocates it with its template.
func (*OperationGoReturn) Apply ¶ added in v1.20.0
func (p *OperationGoReturn) Apply(mc *MachineContext) (*MachineContext, error)
Apply runs the Go callback and decides whether the frame returns.
The reconfiguration test mirrors applyForeign's: the flag catches an in-place Apply (which can leave the template unchanged, e.g. self-application), the template comparison catches continuation-restore paths that repoint the template without going through Apply. Advancing pc in either case would run the frame's OpRestoreContinuation on top of whatever the callback started.
func (*OperationGoReturn) EqualTo ¶ added in v1.20.0
func (p *OperationGoReturn) EqualTo(o values.Value) bool
EqualTo compares by identity: interchangeable means the same callback, and Go funcs are not comparable.
func (*OperationGoReturn) OpKind ¶ added in v1.20.0
func (*OperationGoReturn) OpKind() OpCode
type OperationLoadCachedBinding ¶
type OperationLoadCachedBinding struct {
OperationBase
BindingIndex int32
}
OperationLoadCachedBinding loads a global variable from a compile-time resolved *Binding pointer, bypassing the runtime environment lookup path used by OpLoadGlobal.
func NewOperationLoadCachedBinding ¶
func NewOperationLoadCachedBinding(idx int32) *OperationLoadCachedBinding
NewOperationLoadCachedBinding creates a new cached binding load operation.
func (*OperationLoadCachedBinding) EqualTo ¶
func (p *OperationLoadCachedBinding) EqualTo(o values.Value) bool
EqualTo returns true if both operations have the same binding index.
func (*OperationLoadCachedBinding) OpKind ¶
func (*OperationLoadCachedBinding) OpKind() OpCode
func (*OperationLoadCachedBinding) SchemeString ¶
func (p *OperationLoadCachedBinding) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationLoadFree ¶ added in v1.20.0
type OperationLoadFree struct {
OperationBase
// Index is the free-vector slot, in the order compileClosureBody fixed.
Index int
}
OperationLoadFree loads one entry of the executing closure's free vector into the value register.
The entry holds the free variable's VALUE, copied when the closure was built — or, when the template's FreeBoxed marks this slot, the shared *values.Box that stands in for it. The emitter follows a boxed load with OpUnbox, exactly as it does for a boxed local.
func NewOperationLoadFree ¶ added in v1.20.0
func NewOperationLoadFree(i int) *OperationLoadFree
NewOperationLoadFree creates a free-vector load for slot i.
func (*OperationLoadFree) EqualTo ¶ added in v1.20.0
func (p *OperationLoadFree) EqualTo(o values.Value) bool
EqualTo compares the free-vector index.
func (*OperationLoadFree) OpKind ¶ added in v1.20.0
func (*OperationLoadFree) OpKind() OpCode
func (*OperationLoadFree) SchemeString ¶ added in v1.20.0
func (p *OperationLoadFree) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationLoadGlobalByGlobalIndexLiteralIndexImmediate ¶
type OperationLoadGlobalByGlobalIndexLiteralIndexImmediate struct {
OperationBase
LiteralIndex LiteralIndex
}
OperationLoadGlobalByGlobalIndexLiteralIndexImmediate loads a global variable using an index from the literals pool.
func NewOperationLoadGlobalByGlobalIndexLiteralIndexImmediate ¶
func NewOperationLoadGlobalByGlobalIndexLiteralIndexImmediate(li LiteralIndex) *OperationLoadGlobalByGlobalIndexLiteralIndexImmediate
NewOperationLoadGlobalByGlobalIndexLiteralIndexImmediate creates a new global load operation.
func (*OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) EqualTo ¶
func (p *OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) EqualTo(o values.Value) bool
EqualTo returns true if both operations have the same literal index.
func (*OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) OpKind ¶
func (*OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) OpKind() OpCode
func (*OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) SchemeString ¶
func (p *OperationLoadGlobalByGlobalIndexLiteralIndexImmediate) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationLoadLiteralByLiteralIndexImmediate ¶
type OperationLoadLiteralByLiteralIndexImmediate struct {
OperationBase
LiteralIndex LiteralIndex
}
OperationLoadLiteralByLiteralIndexImmediate loads a literal value from the literals pool.
func NewOperationLoadLiteralByLiteralIndexImmediate ¶
func NewOperationLoadLiteralByLiteralIndexImmediate(li LiteralIndex) *OperationLoadLiteralByLiteralIndexImmediate
NewOperationLoadLiteralByLiteralIndexImmediate creates a new literal load operation.
func (*OperationLoadLiteralByLiteralIndexImmediate) EqualTo ¶
func (p *OperationLoadLiteralByLiteralIndexImmediate) EqualTo(o values.Value) bool
EqualTo returns true if both operations have the same literal index.
func (*OperationLoadLiteralByLiteralIndexImmediate) OpKind ¶
func (*OperationLoadLiteralByLiteralIndexImmediate) OpKind() OpCode
func (*OperationLoadLiteralByLiteralIndexImmediate) SchemeString ¶
func (p *OperationLoadLiteralByLiteralIndexImmediate) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationLoadLocalByLocalIndexImmediate ¶
type OperationLoadLocalByLocalIndexImmediate struct {
OperationBase
LocalIndex *environment.LocalIndex
}
func NewOperationLoadLocalByLocalIndexImmediate ¶
func NewOperationLoadLocalByLocalIndexImmediate(li *environment.LocalIndex) *OperationLoadLocalByLocalIndexImmediate
func (*OperationLoadLocalByLocalIndexImmediate) EqualTo ¶
func (p *OperationLoadLocalByLocalIndexImmediate) EqualTo(o values.Value) bool
func (*OperationLoadLocalByLocalIndexImmediate) OpKind ¶
func (*OperationLoadLocalByLocalIndexImmediate) OpKind() OpCode
func (*OperationLoadLocalByLocalIndexImmediate) SchemeString ¶
func (p *OperationLoadLocalByLocalIndexImmediate) SchemeString() string
type OperationLoadVoid ¶
type OperationLoadVoid struct {
OperationBase
}
func NewOperationLoadVoid ¶
func NewOperationLoadVoid() *OperationLoadVoid
func (*OperationLoadVoid) OpKind ¶
func (*OperationLoadVoid) OpKind() OpCode
type OperationMakeCaseLambdaClosure ¶
type OperationMakeCaseLambdaClosure struct {
OperationBase
// contains filtered or unexported fields
}
OperationMakeCaseLambdaClosure creates a case-lambda closure from multiple closures. Stack layout (top to bottom): closure_n, closure_n-1, ..., closure_1 The closureCount immediate specifies how many closures to pop.
func NewOperationMakeCaseLambdaClosure ¶
func NewOperationMakeCaseLambdaClosure(closureCount int) *OperationMakeCaseLambdaClosure
func (*OperationMakeCaseLambdaClosure) Apply ¶
func (p *OperationMakeCaseLambdaClosure) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationMakeCaseLambdaClosure) EqualTo ¶
func (p *OperationMakeCaseLambdaClosure) EqualTo(o values.Value) bool
func (*OperationMakeCaseLambdaClosure) OpKind ¶
func (*OperationMakeCaseLambdaClosure) OpKind() OpCode
type OperationMakeClosure ¶
type OperationMakeClosure struct {
OperationBase
// contains filtered or unexported fields
}
func NewOperationMakeClosure ¶
func NewOperationMakeClosure(freeCount, selfSlot int) *OperationMakeClosure
NewOperationMakeClosure returns a make-closure op that pops freeCount free values (pushed in slot order) and then the template. selfSlot is the free-vector index the closure must write ITSELF into once built — the letrec T2 back-patch — or -1 when there is none.
func (*OperationMakeClosure) Apply ¶
func (p *OperationMakeClosure) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationMakeClosure) EqualTo ¶
func (p *OperationMakeClosure) EqualTo(o values.Value) bool
EqualTo compares BOTH packed fields.
THIS IS LOAD-BEARING, NOT STYLE. The literal pool dedups templates through NativeTemplate.EqualTo, which compares their code, and two MakeClosure instructions that differ only in free count or self slot would otherwise compare equal — collapsing two templates whose closures capture different things. Mirrors OperationMakeCaseLambdaClosure.EqualTo.
func (*OperationMakeClosure) OpKind ¶
func (*OperationMakeClosure) OpKind() OpCode
OpKind returns OpMakeClosure.
OperationMakeClosure is the only Op type that both has a dedicated opcode (OpMakeClosure) and implements Apply. The Apply method is vestigial in production -- the compiler emits OpMakeClosure directly, routing through the Run() switch case rather than the side table.
Apply is preserved so OperationMakeClosure satisfies InlinedOperation, allowing TestEditPlan_SideTableGC to use it as a no-arg placeholder when populating tpl.sideTable. That test asserts only GC remapping of indices and never invokes Apply itself, so the production and test code paths are decoupled. If Apply's body is ever changed, the matching OpMakeClosure inline case in machine_context.go's Run() must be updated to keep production and test behavior consistent (or Apply should be removed and the test migrated to use testInlinedOp from machine_context_test.go).
type OperationPeekK ¶
type OperationPeekK struct {
OperationBase
Depth int
}
OperationPeekK copies the value at the given depth into the value register and LEAVES THE STACK UNCHANGED. Callers depend on that: dynamic-wind's bytecode reaches its three thunks with PeekK 2/1/0 across the whole extent (CompileValidatedDynamicWind), so a removing PeekK would misalign every subsequent offset.
func NewOperationPeekK ¶
func NewOperationPeekK(depth int) *OperationPeekK
func (*OperationPeekK) OpKind ¶
func (*OperationPeekK) OpKind() OpCode
func (*OperationPeekK) SchemeString ¶
func (p *OperationPeekK) SchemeString() string
SchemeString overrides OperationBase to include depth value.
type OperationPop ¶
type OperationPop struct {
OperationBase
}
OperationPop removes the top value from the eval stack into the value register. OperationDrop is the variant that discards it instead.
func NewOperationPop ¶
func NewOperationPop() *OperationPop
func (*OperationPop) OpKind ¶
func (*OperationPop) OpKind() OpCode
type OperationPopEnv ¶
type OperationPopEnv struct {
OperationBase
}
OperationPopEnv pops one level from the environment chain, restoring the parent environment. A nil parent is an error (wrapped werr.ErrNilParentEnvironment): the top-level environment cannot be popped. It also clears envPooled, since the parent frame was never acquired from the pool and RestoreAndRelease must not release it.
Emitted to close a frame opened by OpPushEnv: at the end of a non-tail let/let*/letrec/letrec* body (compile_let.go), and in syntax-case fender evaluation to restore the environment when the fender returns false, before branching to the next clause.
func NewOperationPopEnv ¶
func NewOperationPopEnv() *OperationPopEnv
func (*OperationPopEnv) OpKind ¶
func (*OperationPopEnv) OpKind() OpCode
type OperationPopWind ¶
type OperationPopWind struct {
OperationBase
}
OperationPopWind pops the innermost dynamic-wind frame from the winding stack. This operation does NOT call the after thunk - that is done explicitly in the bytecode stream to ensure proper continuation semantics.
The frame is simply removed from the winding stack. If a continuation captured inside the dynamic extent is later restored, RestoreWithWindingFrom will handle running the appropriate before/after thunks.
R7RS §6.10: dynamic-wind establishes a dynamic extent during which the before and after thunks are called whenever control enters or exits.
func NewOperationPopWind ¶
func NewOperationPopWind() *OperationPopWind
func (*OperationPopWind) Apply ¶
func (*OperationPopWind) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationPopWind) OpKind ¶
func (*OperationPopWind) OpKind() OpCode
type OperationPull ¶
type OperationPull struct {
OperationBase
}
OperationPull removes the BOTTOM value from the eval stack into the value register. The mirror of OperationPop, which takes the top.
func NewOperationPull ¶
func NewOperationPull() *OperationPull
func (*OperationPull) OpKind ¶
func (*OperationPull) OpKind() OpCode
type OperationPush ¶
type OperationPush struct {
OperationBase
}
OperationPush pushes a value onto the eval stack.
func NewOperationPush ¶
func NewOperationPush() *OperationPush
func (*OperationPush) OpKind ¶
func (*OperationPush) OpKind() OpCode
type OperationPushEnv ¶
type OperationPushEnv struct {
OperationBase
SlotCount int
}
OperationPushEnv allocates a new environment frame with the specified number of local binding slots and chains it to the current environment. Paired with OpPopEnv which restores the parent.
func NewOperationPushEnv ¶
func NewOperationPushEnv(slotCount int) *OperationPushEnv
func (*OperationPushEnv) OpKind ¶
func (*OperationPushEnv) OpKind() OpCode
type OperationPushValues ¶ added in v1.20.0
type OperationPushValues struct {
OperationBase
}
OperationPushValues spreads the value register's 0/1/N values onto the eval stack. It is the multiple-value delivery seam; OperationPush is the single-value one and raises on any other count.
func NewOperationPushValues ¶ added in v1.20.0
func NewOperationPushValues() *OperationPushValues
func (*OperationPushValues) EqualTo ¶ added in v1.20.0
func (p *OperationPushValues) EqualTo(o values.Value) bool
func (*OperationPushValues) OpKind ¶ added in v1.20.0
func (*OperationPushValues) OpKind() OpCode
type OperationPushWind ¶
type OperationPushWind struct {
OperationBase
}
OperationPushWind creates a dynamic-wind frame and pushes it onto the winding stack. It expects the stack to contain [before, thunk, after] where:
- before is at PeekK(2) - the before thunk
- thunk is at PeekK(1) - the main thunk (not used by this operation)
- after is at PeekK(0) - the after thunk
The frame is created from the before and after closures and pushed onto the winding stack. The stack is not modified.
R7RS §6.10: dynamic-wind establishes a dynamic extent during which the before and after thunks are called whenever control enters or exits.
func NewOperationPushWind ¶
func NewOperationPushWind() *OperationPushWind
func (*OperationPushWind) Apply ¶
func (*OperationPushWind) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationPushWind) OpKind ¶
func (*OperationPushWind) OpKind() OpCode
type OperationReleaseEnvFrame ¶
type OperationReleaseEnvFrame struct {
OperationBase
}
OperationReleaseEnvFrame releases the current pool-owned env frame back to the FreeList immediately before a tail call in a frame-releasable body (no capture, no escaping closure, only capture-safe callees). The frame is dead at this point — the tail call's args are already on the eval stack — so the next acquire reuses it, giving O(1) steady-state frame allocation for fib-shaped recursion. A no-op when the frame is not pool-owned (parentless thunk, continuation-shared).
func NewOperationReleaseEnvFrame ¶
func NewOperationReleaseEnvFrame() *OperationReleaseEnvFrame
NewOperationReleaseEnvFrame returns a release-env-frame op.
func (*OperationReleaseEnvFrame) EqualTo ¶
func (p *OperationReleaseEnvFrame) EqualTo(o values.Value) bool
EqualTo returns true if o is also an OperationReleaseEnvFrame (identity by type).
func (*OperationReleaseEnvFrame) OpKind ¶
func (*OperationReleaseEnvFrame) OpKind() OpCode
type OperationRestoreContMark ¶
type OperationRestoreContMark struct {
OperationBase
}
OperationRestoreContMark restores the previous mark value after body evaluation. Paired with OperationSaveContMark.
Pre: eval stack = [..., key, old_val_or_sentinel] Post: eval stack = [...], marks[key] restored or deleted, pc++
func NewOperationRestoreContMark ¶
func NewOperationRestoreContMark() *OperationRestoreContMark
func (*OperationRestoreContMark) Apply ¶
func (*OperationRestoreContMark) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationRestoreContMark) EqualTo ¶
func (p *OperationRestoreContMark) EqualTo(o values.Value) bool
func (*OperationRestoreContMark) OpKind ¶
func (*OperationRestoreContMark) OpKind() OpCode
type OperationRestoreContinuation ¶
type OperationRestoreContinuation struct {
OperationBase
}
func NewOperationRestoreContinuation ¶
func NewOperationRestoreContinuation() *OperationRestoreContinuation
func (*OperationRestoreContinuation) EqualTo ¶
func (p *OperationRestoreContinuation) EqualTo(o values.Value) bool
func (*OperationRestoreContinuation) OpKind ¶
func (*OperationRestoreContinuation) OpKind() OpCode
type OperationSaveContMark ¶
type OperationSaveContMark struct {
OperationBase
}
OperationSaveContMark saves the previous mark value and sets a new one. Used in non-tail position, paired with OperationRestoreContMark.
Pre: eval stack = [..., key], value register = val Post: eval stack = [..., key, old_val_or_sentinel], marks[key] = val, pc++
func NewOperationSaveContMark ¶
func NewOperationSaveContMark() *OperationSaveContMark
func (*OperationSaveContMark) Apply ¶
func (*OperationSaveContMark) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationSaveContMark) EqualTo ¶
func (p *OperationSaveContMark) EqualTo(o values.Value) bool
func (*OperationSaveContMark) OpKind ¶
func (*OperationSaveContMark) OpKind() OpCode
type OperationSaveContinuationOffsetImmediate ¶
type OperationSaveContinuationOffsetImmediate struct {
OperationBase
Offset int
}
func NewOperationSaveContinuationOffsetImmediate ¶
func NewOperationSaveContinuationOffsetImmediate(off int) *OperationSaveContinuationOffsetImmediate
func (*OperationSaveContinuationOffsetImmediate) EqualTo ¶
func (p *OperationSaveContinuationOffsetImmediate) EqualTo(o values.Value) bool
func (*OperationSaveContinuationOffsetImmediate) OpKind ¶
func (*OperationSaveContinuationOffsetImmediate) OpKind() OpCode
func (*OperationSaveContinuationOffsetImmediate) SchemeString ¶
func (p *OperationSaveContinuationOffsetImmediate) SchemeString() string
SchemeString overrides OperationBase to include offset value.
type OperationSelfTailCall ¶
type OperationSelfTailCall struct {
OperationBase
ArgCount int
PopCount int
}
OperationSelfTailCall is the in-place self-recursive tail call: it pops PopCount intermediate env frames, drains the ArgCount already-evaluated argument values off the eval stack, writes them into the parameter frame's slots 0..ArgCount-1 (parallel assignment — the args are on the stack, so old slot values stay intact during evaluation), and resets pc=0. No frame acquire, no SaveContinuation, no continuation growth.
PopCount is the number of `let` frames lexically between the parameter frame and the call, and it is why this op is ONE instruction rather than a pop sequence followed by a rebind. Between the pops and the rebind, mc.env points at a frame whose slots are about to be overwritten while its arguments sit on the eval stack; splitting that across instructions makes the intermediate state representable to the peephole pass and to anything that can interpose. Popping is also the one thing that must not use OpPopEnv: that op clears envPooled as a statement about the frame it pops TO, which is exactly the fact this op needs to leave alone.
PopCount == 0 encodes byte-identically to the pre-Phase-C operand, so every depth-0 site's instruction stream is unchanged.
Emitted only behind validate.BodyIsSelfTailReusable at a self-tail call site; that proof (no capture operator anywhere in the body, no escaping closure, non-variadic, no set! of the self name) is what makes reusing the live frame sound, and it covers let bodies for the same reason it covers the top level — it walks the whole body. So the popped frames are unreachable except through mc.env by the same argument that licenses the rebind (escape-gated plan Phase 4, widened to depth>0 by frame-reclaim Phase C).
func NewOperationSelfTailCall ¶
func NewOperationSelfTailCall(argCount, popCount int) *OperationSelfTailCall
NewOperationSelfTailCall returns a self-tail-call op that pops popCount intermediate frames and then rebinds argCount parameter slots.
func (*OperationSelfTailCall) EqualTo ¶
func (p *OperationSelfTailCall) EqualTo(o values.Value) bool
EqualTo returns true if o is also an OperationSelfTailCall with the same arity and pop count. Both are compared: two sites that agree on arity but not on depth are different instructions, and merging them would rebind the wrong frame.
func (*OperationSelfTailCall) OpKind ¶
func (*OperationSelfTailCall) OpKind() OpCode
type OperationSetContMark ¶
type OperationSetContMark struct {
OperationBase
}
OperationSetContMark sets a continuation mark on the current frame. Used in tail position where no restore is needed.
Pre: eval stack = [..., key], value register = val Post: eval stack = [...], marks[key] = val, pc++
func NewOperationSetContMark ¶
func NewOperationSetContMark() *OperationSetContMark
func (*OperationSetContMark) Apply ¶
func (*OperationSetContMark) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationSetContMark) OpKind ¶
func (*OperationSetContMark) OpKind() OpCode
type OperationStoreFree ¶ added in v1.20.0
type OperationStoreFree struct {
OperationBase
// Index is the free-vector slot whose box receives the value.
Index int
}
OperationStoreFree pops a value and writes it into the BOX held at one entry of the executing closure's free vector.
It exists because a free variable that is written is captured-and-assigned, hence boxed, and the cell lives in the vector rather than in a frame slot — so neither OpStoreLocal nor OpStoreThroughBox can reach it. Writing the vector entry instead of the cell would leave every other holder, including the frame the variable is bound in, reading the old value.
func NewOperationStoreFree ¶ added in v1.20.0
func NewOperationStoreFree(i int) *OperationStoreFree
NewOperationStoreFree creates a write-through-the-free-vector's-box operation.
func (*OperationStoreFree) EqualTo ¶ added in v1.20.0
func (p *OperationStoreFree) EqualTo(o values.Value) bool
EqualTo compares the free-vector index.
func (*OperationStoreFree) OpKind ¶ added in v1.20.0
func (*OperationStoreFree) OpKind() OpCode
func (*OperationStoreFree) SchemeString ¶ added in v1.20.0
func (p *OperationStoreFree) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationStoreGlobalByGlobalIndexLiteralIndexImmediate ¶
type OperationStoreGlobalByGlobalIndexLiteralIndexImmediate struct {
OperationBase
LiteralIndex LiteralIndex
}
OperationStoreGlobalByGlobalIndexLiteralIndexImmediate stores a value to a global variable using an index from the literals pool.
func NewOperationStoreGlobalByGlobalIndexLiteralIndexImmediate ¶
func NewOperationStoreGlobalByGlobalIndexLiteralIndexImmediate(liti LiteralIndex) *OperationStoreGlobalByGlobalIndexLiteralIndexImmediate
NewOperationStoreGlobalByGlobalIndexLiteralIndexImmediate creates a new global store operation.
func (*OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) EqualTo ¶
func (p *OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) EqualTo(o values.Value) bool
func (*OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) OpKind ¶
func (*OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) OpKind() OpCode
func (*OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) SchemeString ¶
func (p *OperationStoreGlobalByGlobalIndexLiteralIndexImmediate) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationStoreLocalByLocalIndexImmediate ¶
type OperationStoreLocalByLocalIndexImmediate struct {
OperationBase
LocalIndex *environment.LocalIndex
}
func NewOperationStoreLocalByLocalIndexImmediate ¶
func NewOperationStoreLocalByLocalIndexImmediate(li *environment.LocalIndex) *OperationStoreLocalByLocalIndexImmediate
func (*OperationStoreLocalByLocalIndexImmediate) EqualTo ¶
func (p *OperationStoreLocalByLocalIndexImmediate) EqualTo(o values.Value) bool
func (*OperationStoreLocalByLocalIndexImmediate) OpKind ¶
func (*OperationStoreLocalByLocalIndexImmediate) OpKind() OpCode
func (*OperationStoreLocalByLocalIndexImmediate) SchemeString ¶
func (p *OperationStoreLocalByLocalIndexImmediate) SchemeString() string
type OperationStoreThroughBox ¶ added in v1.20.0
type OperationStoreThroughBox struct {
OperationBase
LocalIndex *environment.LocalIndex
}
OperationStoreThroughBox pops a value and writes it INTO the box a local slot holds, leaving the slot's box pointer alone. Emitted for every write to a boxed slot — a set!, an internal define's store, a let init's store.
Writing the slot instead would install a new value where every sharer is still reading the old cell, which is exactly the aliasing the box exists to preserve.
func NewOperationStoreThroughBox ¶ added in v1.20.0
func NewOperationStoreThroughBox(li *environment.LocalIndex) *OperationStoreThroughBox
NewOperationStoreThroughBox creates a store-through-the-box operation for li.
func (*OperationStoreThroughBox) EqualTo ¶ added in v1.20.0
func (p *OperationStoreThroughBox) EqualTo(o values.Value) bool
EqualTo compares the target slot.
func (*OperationStoreThroughBox) OpKind ¶ added in v1.20.0
func (*OperationStoreThroughBox) OpKind() OpCode
func (*OperationStoreThroughBox) SchemeString ¶ added in v1.20.0
func (p *OperationStoreThroughBox) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationUnbox ¶ added in v1.20.0
type OperationUnbox struct {
OperationBase
}
OperationUnbox replaces the value register's contents with the value inside the box it holds. Emitted after every load of a boxed slot.
It operates on the value register rather than the eval stack, which is what keeps it out of the peephole's way: a boxed read compiles to LoadLocal · Unbox · Push, and the LoadLocal+Push fusion looks for those two ADJACENT, so it does not fire. A stack-top form would have had to be excluded from the call fusions by hand.
func NewOperationUnbox ¶ added in v1.20.0
func NewOperationUnbox() *OperationUnbox
NewOperationUnbox creates an unbox-the-value-register operation.
func (*OperationUnbox) EqualTo ¶ added in v1.20.0
func (p *OperationUnbox) EqualTo(o values.Value) bool
EqualTo reports whether o is also an unbox operation. The operation carries no operand, so type identity is the whole comparison.
func (*OperationUnbox) OpKind ¶ added in v1.20.0
func (*OperationUnbox) OpKind() OpCode
func (*OperationUnbox) SchemeString ¶ added in v1.20.0
func (*OperationUnbox) SchemeString() string
SchemeString returns the Scheme representation of the operation.
type OperationUnboxValues ¶
type OperationUnboxValues struct {
OperationBase
}
OperationUnboxValues expands a *boxedValues carrier in the value register back into the value register's 0/N values (the inverse of OperationBoxValues). A preceding OpPeekK loads the carrier into the value register; this op replaces it with the boxed values. A register holding exactly one non-carrier value is the single-value fast path and passes through untouched; anything else is a misaligned eval stack and errors.
Pre: value register = a carrier holding v1 … vN, or exactly one value Post: value register = v1 … vN, pc++
func NewOperationUnboxValues ¶
func NewOperationUnboxValues() *OperationUnboxValues
func (*OperationUnboxValues) Apply ¶
func (*OperationUnboxValues) Apply(mc *MachineContext) (*MachineContext, error)
func (*OperationUnboxValues) OpKind ¶
func (*OperationUnboxValues) OpKind() OpCode
type OperationUnpackListToStack ¶
type OperationUnpackListToStack struct {
OperationBase
}
OperationUnpackListToStack reads a proper list from the value register and pushes each element to the eval stack in order. Used by compiled (apply proc arg1 ... args) to flatten the final arg list onto the stack before Pull + OpApply.
Errors if the value is not a proper list (improper list or non-list).
func NewOperationUnpackListToStack ¶
func NewOperationUnpackListToStack() *OperationUnpackListToStack
NewOperationUnpackListToStack returns a new unpack-list-to-stack operation.
func (*OperationUnpackListToStack) EqualTo ¶
func (p *OperationUnpackListToStack) EqualTo(o values.Value) bool
EqualTo returns true if o is also an OperationUnpackListToStack (identity by type).
func (*OperationUnpackListToStack) OpKind ¶
func (*OperationUnpackListToStack) OpKind() OpCode
type Operations ¶
type Operations []Operation
Operations is the compiler's opcode list. It is deliberately NOT a values.Value.
It carried SchemeString/IsVoid/EqualTo for container convenience, which made the naked []Operation slice a legal dynamic type inside a values.Value interface — and a slice is not Go-comparable, so any `==` or map-key hash of that interface faults. values.EqIdentity (eq?) is exactly such an `==`. The conformance was never used: nothing outside this package treated an Operations as a Scheme datum, and its SchemeString rendered the non-datum "#<machine-operations>". Meanwhile it cost the equality core real defensiveness, since values.Equal could not assume its operands were comparable.
Having an equality method is not the same as being a Value. EqualTo below takes a concrete Operations, so callers still get structural comparison without the type entering the Value contract.
func NewOperations ¶
func NewOperations(ops ...Operation) Operations
func (Operations) AsList ¶
func (p Operations) AsList() values.Tuple
func (Operations) Copy ¶
func (p Operations) Copy() Operations
func (Operations) EqualTo ¶
func (p Operations) EqualTo(v Operations) bool
EqualTo reports whether two operation lists are element-wise equal.
It takes a concrete Operations, not a values.Value: this type is not a Scheme datum and does not implement the Value interface. See the type's doc comment.
func (Operations) Len ¶
func (p Operations) Len() int
type Parameter ¶
type Parameter struct {
// contains filtered or unexported fields
}
Parameter represents an R7RS parameter object. Parameters are dynamically-scoped variables that can be temporarily rebound using parameterize. They act as procedures:
- (param) returns the current value
- (param val) sets the parameter's BASE value (after applying converter if present); it does not affect an active parameterize binding, which is carried as a continuation mark and read first by (param)
A parameter constructed with ImmutableBase refuses the one-argument form; see ParameterBase.
func NewParameter ¶
NewParameter creates a new parameter with the given initial value and optional converter. The converter should be a procedure that takes one argument and returns the converted value. Pass nil for converter if no conversion is needed. Pass MutableBase unless the object is shared beyond one Engine (see ParameterBase).
func (*Parameter) AcceptsArity ¶
AcceptsArity reports whether this parameter can be called with n arguments. Parameters accept 0 args (get current value) or 1 arg (set value).
func (*Parameter) EqualTo ¶
EqualTo uses identity comparison for parameters. Two parameters are equal only if they are the same object.
func (*Parameter) HasConverter ¶
HasConverter returns true if the parameter has a converter procedure.
func (*Parameter) HasImmutableBase ¶ added in v1.20.0
HasImmutableBase reports whether this parameter refuses base-value rewrites.
func (*Parameter) SchemeString ¶
SchemeString returns the Scheme representation of the parameter.
type ParameterBase ¶ added in v1.20.0
type ParameterBase bool
ParameterBase says whether a parameter's base value may be rewritten by applying it to one argument. Parameterize is unaffected either way: it binds a continuation mark, never the base.
const ( // MutableBase is the ordinary R7RS parameter: (param val) rewrites the base. MutableBase ParameterBase = false // ImmutableBase refuses (param val). Required of any parameter object shared // across Engines, whose safety argument rests on the base never changing -- // exceptionHandlerParam is the one such singleton. ImmutableBase ParameterBase = true )
type Pool ¶
type Pool[T any] struct { // contains filtered or unexported fields }
Pool is a type-safe, observable object pool backed by sync.Pool. It wraps sync.Pool with atomic counters (acquires, releases, misses).
func (*Pool[T]) Acquire ¶
func (p *Pool[T]) Acquire() *T
Acquire returns an object from the pool, allocating via newFn on a miss.
func (*Pool[T]) Release ¶
func (p *Pool[T]) Release(v *T)
Release resets the object and returns it to the pool.
func (*Pool[T]) Stats ¶
func (p *Pool[T]) Stats() PoolSnapshot
Stats returns a point-in-time snapshot of the pool's counters.
type PoolSnapshot ¶
type PoolSnapshot struct {
Name string
Acquires uint64
Releases uint64
Misses uint64
InFlight uint64
}
PoolSnapshot is a point-in-time view of a pool's counters.
type PrimitiveIdentity ¶ added in v1.20.0
type PrimitiveIdentity struct {
// contains filtered or unexported fields
}
PrimitiveIdentity is the stable identity of one registered primitive, shared by every ForeignClosure built from that primitive's spec.
It exists because a primitive has no single closure object. A library environment is an island (environment.Namespace.NewChildRuntime roots it at a sealed base of its OWN, so nothing in it reaches the engine's frames at any phase), so the library env factory re-applies the whole registry into it and mints a SECOND closure for every primitive; a program that imports (scheme base) then holds that copy while the engine's sealed base still holds the first. Both are the same primitive and behave identically, and a pointer compare says they are not.
So Go code asking "is this value the registered equal-hash?" compares identities, not pointers. The question a pointer answers — "is this the sealed base's copy of it?" — is the wrong one, and stops being answerable at all once the visible surface narrows (WithStrictNamespace, or a dialect's PrimitiveRemover) so that the sealed base no longer holds the primitive.
Identity is by POINTER on the token itself, so two tokens with the same name are distinct. That is the fail-closed direction: an embedder registering their own equal-hash gets their own token (or none) and is refused, where a name compare would have accepted it and hashed with the wrong function. There is deliberately no way to construct a token from Scheme.
This is NOT a Binding Identity violation. That invariant forbids deciding two IDENTIFIERS denote the same variable by comparing SPELLINGS; nothing is resolved by spelling here. The name below is diagnostic only — never compared.
func IdentityOf ¶ added in v1.20.0
func IdentityOf(v values.Value) *PrimitiveIdentity
IdentityOf returns v's primitive identity, or nil if v is not a closure built from a spec that declared one. nil means NONE: a Scheme procedure, a primitive with no declared identity, and a non-procedure are all indistinguishable here, which is what makes an identity compare against a non-nil token fail closed.
func NewPrimitiveIdentity ¶ added in v1.20.0
func NewPrimitiveIdentity(name string) *PrimitiveIdentity
NewPrimitiveIdentity mints a fresh identity. Call it ONCE per primitive, at package scope: the returned pointer is the identity, so a second call for the same primitive produces a token that matches nothing.
func PromotedIdentities ¶ added in v1.20.0
func PromotedIdentities() []*PrimitiveIdentity
PromotedIdentities returns the identity token of every promoted primitive, one per descriptor in promotedOps. Exported for the deopt ratchet, which asserts that each token is stamped on the primitive an Engine actually binds and so lives outside this package.
Cardinality is the point. A promoted primitive whose spec loses its Identity: keeps computing the right answer and merely stops being inlined, forever, which no value assertion sees; without a way to enumerate the descriptors the ratchet is a hand-written table with nothing forcing a nineteenth op to appear in it.
func (*PrimitiveIdentity) Name ¶ added in v1.20.0
func (p *PrimitiveIdentity) Name() string
Name returns the primitive's name, for diagnostics. It is not the identity — see the type doc.
type PromptTag ¶
type PromptTag struct {
// contains filtered or unexported fields
}
PromptTag is an opaque identity value for delimited continuation prompts. Equality is pointer identity (each tag is unique). Follows the Racket model of continuation prompt tags.
See: Flatt, Yu, Findler, Felleisen "Adding Delimited and Composable Control to a Production Programming Environment" (ICFP 2007).
func NewPromptTag ¶
NewPromptTag creates a new prompt tag with an optional name for debugging.
func (*PromptTag) SchemeString ¶
SchemeString returns a string representation of the prompt tag for debugging.
type SchemeError ¶
type SchemeError struct {
Message string
Source *syntax.SourceContext // Where error occurred
StackTrace string // Formatted stack trace
Cause error // Underlying error (if any)
}
SchemeError is a runtime error with Scheme-level stack trace.
func NewSchemeError ¶
func NewSchemeError(msg string, source *syntax.SourceContext, stackTrace string) *SchemeError
NewSchemeError creates a new SchemeError with the given message, source context, and stack trace.
func NewSchemeErrorWithCause ¶
func NewSchemeErrorWithCause(msg string, source *syntax.SourceContext, stackTrace string, cause error) *SchemeError
NewSchemeErrorWithCause creates a new SchemeError with the given message, source context, stack trace, and underlying cause.
func (*SchemeError) EqualTo ¶
func (p *SchemeError) EqualTo(o values.Value) bool
EqualTo compares for equality.
func (*SchemeError) Error ¶
func (p *SchemeError) Error() string
Error implements the error interface for SchemeError.
func (*SchemeError) IsVoid ¶
func (p *SchemeError) IsVoid() bool
IsVoid returns false (errors are not void).
func (*SchemeError) SchemeString ¶
func (p *SchemeError) SchemeString() string
SchemeString returns the Scheme representation.
func (*SchemeError) Unwrap ¶
func (p *SchemeError) Unwrap() error
Unwrap returns the underlying cause of the SchemeError, if any.
type Stack ¶
Stack is the VM's evaluation stack. It is deliberately its own slice type and NOT defined as values.Vector: the two happen to have the same shape today, but they answer to different contracts. A Vector is a Scheme datum whose immutability is user-visible; the eval stack is VM-internal scratch that is pushed, popped and drained on every instruction. Keeping them separate means giving Vector a per-instance immutability flag does not silently widen the stack or drag it onto the datum's accessor API.
func (Stack) AsList ¶
AsList converts the stack to a Scheme list (values.Tuple). The list is in stack order (first pushed = first element).
func (*Stack) Drain ¶
Drain returns a view of all stack elements and resets the stack to empty. The returned slice shares the stack's backing array — it is valid only until the next mutation (Push, PushAll, or any append). Callers must finish reading before any stack mutation.
Unlike PopAll (which copies into a new slice), Drain is zero-allocation. Use Drain when the caller consumes values immediately (e.g., binding arguments in Apply); use PopAll when the caller needs to own the slice.
func (*Stack) DrainN ¶
DrainN returns the top n values (in stack order: first-pushed first) and removes them, leaving any values beneath them on the stack. Like Drain it returns a non-allocating view, not a copy — the caller must consume it before pushing. A request larger than the stack panics with a wrapped sentinel rather than letting a downstream index run out of range. Used by OpSelfTailCall, which must consume exactly the call's argument count and never silently discard anything beneath it.
func (Stack) PeekK ¶
PeekK returns the kth value from the top of the stack without removing it. `K` is zero-based, so PeekK(0) returns the top value. `K` is used for methods that need a numeric index. An index outside [0, len) panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (*Stack) Pop ¶
Pop removes and returns the top value from the stack. An empty stack panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (*Stack) Pop2 ¶
Pop2 removes and returns the top two values from the stack. q0 is the top-most value (popped first), q1 is the value beneath it. It checks the length once and is equivalent to two successive Pop() calls. Fewer than two values panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (*Stack) PopAll ¶
PopAll removes and returns all values from the stack. The caller gets exclusive ownership of the returned slice. The stack retains its backing array for reuse (avoids re-allocation on subsequent pushes). References in the retained portion are cleared so the GC can collect the values.
func (*Stack) PopN ¶
PopN removes and returns the top n values from the stack. Values are returned in stack order (top-most element last). This is more efficient than calling Pop() n times. A negative n, or one larger than the stack, panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (*Stack) Pull ¶
Pull removes and returns the bottom value from the stack. In the SECD model (Landin 1964), the procedure is evaluated first but needed last. Pull retrieves it from the bottom after all arguments have been pushed on top. See BIBLIOGRAPHY.md "Stack-Based Virtual Machines". An empty stack panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (*Stack) PullDrain ¶
PullDrain removes and returns the bottom value (position 0) as the first return, and all remaining values as the second return. The stack is cleared. This is O(1) — no element shifting, just slice header arithmetic.
The returned args slice shares the stack's backing array (same contract as Drain). Valid only until the next stack mutation.
An empty stack panics with a wrapped sentinel rather than letting a downstream index run out of range.
func (Stack) SchemeString ¶
SchemeString returns a Scheme-like string representation of the stack.
type StackFrame ¶
type StackFrame struct {
FunctionName string // Function name (or "<anonymous>")
CallSite *syntax.SourceContext // Where the call was made
CurrentLoc *syntax.SourceContext // Current execution point
}
StackFrame represents one frame in a Scheme stack trace.
func (*StackFrame) String ¶
func (p *StackFrame) String() string
String formats the frame for display.
Both locations render through SourceContext.Location, so a context carrying no position contributes nothing rather than a bare ":0:0". A frame whose CurrentLoc is position-less therefore falls through to its CallSite, and one with no position at all degrades to the name alone.
type StackTrace ¶
type StackTrace []StackFrame
StackTrace is a list of stack frames.
func (StackTrace) String ¶
func (p StackTrace) String() string
String formats the entire stack trace.
type SubContextParams ¶
type SubContextParams struct {
Ctx context.Context
Env *environment.EnvironmentFrame
// ExecNS is the spawning context's executing namespace, carried across the
// goroutine boundary so a thread started from sandboxed code stays under
// that sandbox's policy. Env is the shared mutable runtime global and does
// NOT answer this — it is one object for the whole engine.
ExecNS *environment.Namespace
EscapeCont *MachineContinuation
MaxCallDepth int
MaxStackSize uint64
WindingStack WindingStack
}
SubContextParams holds the parent state copied into a thread's sub-context across the goroutine boundary. It deliberately carries NO pointer to the parent MachineContext: a thread runs CONCURRENTLY with its parent, so a live parentMC link would let the thread's parentMC walks (CaptureStackTrace, findParameterInMarks, the subContextPool release counter) read the parent's still-mutating VM fields — an unsynchronized data race (see TODO.md Tier 1; reproduced by TestMutexAbandonedOnTermination under -race). Only values safe to snapshot once at spawn time are captured here.
type VMCounters ¶
type VMCounters struct {
OpsExecuted uint64
ClosuresApplied uint64
EnvsCopied uint64
BindingsCopied uint64
ContinuationsSaved uint64
ContinuationsRestored uint64
StackDrains uint64
StackElementsDrained uint64
ForeignCalls uint64
SubContextsCreated uint64
StackPoolReleases uint64
SubContextPoolReleases uint64
// Pool effectiveness under call/cc:
// ratio = SharedFrameRestores / (SharedFrameRestores + ContinuationPoolReleases)
// 0.0 = no call/cc impact (all frames recycled via pool)
// 1.0 = all frames shared (no recycling, pure GC pressure)
// > 0.5 = pool losing more than it saves; consider profiling GC pauses
ContinuationPoolReleases uint64
EnvFramePoolReleases uint64
InlineEvalsSaved uint64 // SaveContinuation used inline slots instead of stack pool
// Stack depth instrumentation (ongoing monitoring; prior cap-tuning investigation
// showed cap-8 is sufficient for observed workloads)
StackMaxDepth uint64
StackDepth0to2 uint64 // depth 0-2: fits trivially
StackDepth3to4 uint64 // depth 3-4: typical calls
StackDepth5to8 uint64 // depth 5-8: fits in pool cap 8
StackDepth9to16 uint64 // depth 9-16: requires 1 growth from cap 8
StackDepth17p uint64 // depth 17+: requires 2+ growths
// contains filtered or unexported fields
}
VMCounters holds performance counters for a single MachineContext execution. All counters are plain uint64 — no atomics needed because each MachineContext is single-goroutine. Sub-contexts have their own counters (not aggregated into the parent).
func (VMCounters) CallCounts ¶
func (p VMCounters) CallCounts() map[string]uint64
CallCounts returns the per-callee call-count map for this context, or nil when call counting was disabled. Keys are foreign primitive names and named Scheme procedures (NativeTemplate.Name); values are invocation counts. The map is the live counter map, not a copy — read it after the run completes.
func (VMCounters) CallHistogram ¶
func (p VMCounters) CallHistogram() string
CallHistogram returns a formatted histogram of per-callee call counts (both foreign primitives and named Scheme procedures), sorted by frequency (descending). Returns empty string when profiling is disabled.
func (*VMCounters) RecordCall ¶
func (p *VMCounters) RecordCall(name string)
RecordCall increments the call count for the named callee (foreign primitive or Scheme-defined procedure). No-op when profiling is disabled (nil map).
func (*VMCounters) RecordStackDepth ¶
func (p *VMCounters) RecordStackDepth(n int)
RecordStackDepth updates the depth histogram and max tracker.
func (VMCounters) String ¶
func (p VMCounters) String() string
String returns a tabular summary of all counters.
type WindingStack ¶
type WindingStack []DynamicWindFrame
WindingStack tracks the current dynamic-wind context. It's a slice of frames from outermost to innermost.
Frames are stored BY VALUE, so a push into retained capacity costs nothing: Pop reslices without dropping capacity, and dynamic-wind at a steady depth therefore allocates a spine once instead of a frame per extent.
A WindingStack does NOT own its backing array, and three rules follow from that one property:
- Copy clones the spine, so a captured stack and the live stack no longer share frame objects. Extent identity is ID, which is what FindCommonWindingPrefix has always compared, so that is not a semantic change.
- No one may retain &stack[i] across a push. Take the element by value, as unwindStackTo and RewindTo do.
- A header handed to another context must be capped or copied. Retained capacity means len < cap after any completed extent, so a bare header gives the recipient a writable alias of slots this stack will reuse. Across a goroutine boundary that is a data race on a 64-byte struct that cannot be written atomically; see CaptureSubContextParams.
The struct is deliberately not ==-comparable (entryMarks is a slice), so reach for ID rather than a map[DynamicWindFrame] or qt.Equals.
func (WindingStack) Copy ¶
func (p WindingStack) Copy() WindingStack
Copy clones the spine. Frames are copied by value; the Closure interfaces and the entryMarks backing array they hold are shared, which is safe only because entryMarks is treated as a read-only snapshot.
func (WindingStack) Depth ¶
func (p WindingStack) Depth() int
Depth returns the number of active dynamic-wind frames.
func (*WindingStack) Pop ¶
func (p *WindingStack) Pop() (DynamicWindFrame, bool)
Pop removes the innermost frame from the winding stack, reporting false when the stack was already empty. The reslice retains capacity, so the next Push at this depth reuses the vacated slot.
func (*WindingStack) Push ¶
func (p *WindingStack) Push(frame DynamicWindFrame)
Push adds a frame to the winding stack.
Source Files
¶
- arity.go
- barrier_token.go
- break_interrupt.go
- call_context.go
- call_foreign_cached.go
- call_promoted.go
- call_promoted_arithmetic.go
- captured_continuation.go
- case_lambda_closure.go
- closure.go
- composable_continuation.go
- continuation_mark_set.go
- counters.go
- debugger.go
- disassemble.go
- doc.go
- dynamic_wind.go
- edit_plan.go
- error_context.go
- eval_depth.go
- exception_escape.go
- exception_raise.go
- expander_ctx.go
- foreign_closure.go
- instruction.go
- machine_closure.go
- machine_context.go
- machine_context_apply.go
- machine_context_continuation.go
- machine_context_subcontext.go
- machine_context_winding.go
- machine_continuation.go
- macro_evaluator.go
- multiple_values.go
- native_template.go
- op_kind.go
- opcode.go
- operation.go
- operation_box_values.go
- operation_cont_mark.go
- operation_go_return.go
- operation_helpers.go
- operations.go
- operations_box.go
- operations_call.go
- operations_closure.go
- operations_control.go
- operations_free.go
- operations_load_store.go
- operations_stack.go
- operations_winding.go
- parameter.go
- peephole.go
- pool.go
- pool_generic.go
- primitive_identity.go
- prompt_abort.go
- prompt_tag.go
- run_body_under_frame.go
- run_body_under_timer.go
- scheme_error.go
- stack.go
- stack_frame.go
- timer_interrupt.go
- util.go
- vm_state.go
Directories
¶
| Path | Synopsis |
|---|---|
|
resolver
Package resolver provides file resolution infrastructure for the Scheme compiler.
|
Package resolver provides file resolution infrastructure for the Scheme compiler. |
|
sourceload
Package sourceload provides file-finding and load-stack tracking for locating source files across virtual filesystems.
|
Package sourceload provides file-finding and load-stack tracking for locating source files across virtual filesystems. |