package wazevo
import (
"context"
"fmt"
"reflect"
"runtime"
"sync/atomic"
"unsafe"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
"github.com/tetratelabs/wazero/internal/expctxkeys"
"github.com/tetratelabs/wazero/internal/internalapi"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wasmdebug"
"github.com/tetratelabs/wazero/internal/wasmruntime"
)
type (
callEngine struct {
internalapi .WazeroOnly
stack []byte
stackTop uintptr
executable *byte
preambleExecutable *byte
parent *moduleEngine
indexInModule wasm .Index
sizeOfParamResultSlice int
requiredParams int
execCtx executionContext
execCtxPtr uintptr
numberOfResults int
stackIteratorImpl stackIterator
}
executionContext struct {
exitCode wazevoapi .ExitCode
callerModuleContextPtr *byte
originalFramePointer uintptr
originalStackPointer uintptr
goReturnAddress uintptr
stackBottomPtr *byte
goCallReturnAddress *byte
stackPointerBeforeGoCall *uint64
stackGrowRequiredSize uintptr
memoryGrowTrampolineAddress *byte
stackGrowCallTrampolineAddress *byte
checkModuleExitCodeTrampolineAddress *byte
savedRegisters [64 ][2 ]uint64
goFunctionCallCalleeModuleContextOpaque uintptr
tableGrowTrampolineAddress *byte
refFuncTrampolineAddress *byte
memmoveAddress uintptr
framePointerBeforeGoCall uintptr
memoryWait32TrampolineAddress *byte
memoryWait64TrampolineAddress *byte
memoryNotifyTrampolineAddress *byte
}
)
func (c *callEngine ) requiredInitialStackSize () int {
const initialStackSizeDefault = 10240
stackSize := initialStackSizeDefault
paramResultInBytes := c .sizeOfParamResultSlice * 8 * 2
required := paramResultInBytes + 32 + 16
if required > stackSize {
stackSize = required
}
return stackSize
}
func (c *callEngine ) init () {
stackSize := c .requiredInitialStackSize ()
if wazevoapi .StackGuardCheckEnabled {
stackSize += wazevoapi .StackGuardCheckGuardPageSize
}
c .stack = make ([]byte , stackSize )
c .stackTop = alignedStackTop (c .stack )
if wazevoapi .StackGuardCheckEnabled {
c .execCtx .stackBottomPtr = &c .stack [wazevoapi .StackGuardCheckGuardPageSize ]
} else {
c .execCtx .stackBottomPtr = &c .stack [0 ]
}
c .execCtxPtr = uintptr (unsafe .Pointer (&c .execCtx ))
}
func alignedStackTop(s []byte ) uintptr {
stackAddr := uintptr (unsafe .Pointer (&s [len (s )-1 ]))
return stackAddr - (stackAddr & (16 - 1 ))
}
func (c *callEngine ) Definition () api .FunctionDefinition {
return c .parent .module .Source .FunctionDefinition (c .indexInModule )
}
func (c *callEngine ) Call (ctx context .Context , params ...uint64 ) ([]uint64 , error ) {
if c .requiredParams != len (params ) {
return nil , fmt .Errorf ("expected %d params, but passed %d" , c .requiredParams , len (params ))
}
paramResultSlice := make ([]uint64 , c .sizeOfParamResultSlice )
copy (paramResultSlice , params )
if err := c .callWithStack (ctx , paramResultSlice ); err != nil {
return nil , err
}
return paramResultSlice [:c .numberOfResults ], nil
}
func (c *callEngine ) addFrame (builder wasmdebug .ErrorBuilder , addr uintptr ) (def api .FunctionDefinition , listener experimental .FunctionListener ) {
eng := c .parent .parent .parent
cm := eng .compiledModuleOfAddr (addr )
if cm == nil {
if checkAddrInBytes (addr , c .parent .parent .executable ) {
cm = c .parent .parent
} else {
p := c .parent
for i := range p .importedFunctions {
candidate := p .importedFunctions [i ].me .parent
if checkAddrInBytes (addr , candidate .executable ) {
cm = candidate
break
}
}
}
}
if cm != nil {
index := cm .functionIndexOf (addr )
def = cm .module .FunctionDefinition (cm .module .ImportFunctionCount + index )
var sources []string
if dw := cm .module .DWARFLines ; dw != nil {
sourceOffset := cm .getSourceOffset (addr )
sources = dw .Line (sourceOffset )
}
builder .AddFrame (def .DebugName (), def .ParamTypes (), def .ResultTypes (), sources )
if len (cm .listeners ) > 0 {
listener = cm .listeners [index ]
}
}
return
}
func (c *callEngine ) CallWithStack (ctx context .Context , paramResultStack []uint64 ) (err error ) {
if c .sizeOfParamResultSlice > len (paramResultStack ) {
return fmt .Errorf ("need %d params, but stack size is %d" , c .sizeOfParamResultSlice , len (paramResultStack ))
}
return c .callWithStack (ctx , paramResultStack )
}
func (c *callEngine ) callWithStack (ctx context .Context , paramResultStack []uint64 ) (err error ) {
snapshotEnabled := ctx .Value (expctxkeys .EnableSnapshotterKey {}) != nil
if snapshotEnabled {
ctx = context .WithValue (ctx , expctxkeys .SnapshotterKey {}, c )
}
if wazevoapi .StackGuardCheckEnabled {
defer func () {
wazevoapi .CheckStackGuardPage (c .stack )
}()
}
p := c .parent
ensureTermination := p .parent .ensureTermination
m := p .module
if ensureTermination {
select {
case <- ctx .Done ():
m .CloseWithCtxErr (ctx )
return m .FailIfClosed ()
default :
}
}
var paramResultPtr *uint64
if len (paramResultStack ) > 0 {
paramResultPtr = ¶mResultStack [0 ]
}
defer func () {
r := recover ()
if s , ok := r .(*snapshot ); ok {
panic (s )
}
if r != nil {
type listenerForAbort struct {
def api .FunctionDefinition
lsn experimental .FunctionListener
}
var listeners []listenerForAbort
builder := wasmdebug .NewErrorBuilder ()
def , lsn := c .addFrame (builder , uintptr (unsafe .Pointer (c .execCtx .goCallReturnAddress )))
if lsn != nil {
listeners = append (listeners , listenerForAbort {def , lsn })
}
returnAddrs := unwindStack (
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )),
c .execCtx .framePointerBeforeGoCall ,
c .stackTop ,
nil ,
)
for _ , retAddr := range returnAddrs [:len (returnAddrs )-1 ] {
def , lsn = c .addFrame (builder , retAddr )
if lsn != nil {
listeners = append (listeners , listenerForAbort {def , lsn })
}
}
err = builder .FromRecovered (r )
for _ , lsn := range listeners {
lsn .lsn .Abort (ctx , m , lsn .def , err )
}
} else {
if err != wasmruntime .ErrRuntimeStackOverflow {
err = c .parent .module .FailIfClosed ()
}
}
if err != nil {
c .execCtx .exitCode = wazevoapi .ExitCodeOK
}
}()
if ensureTermination {
done := m .CloseModuleOnCanceledOrTimeout (ctx )
defer done ()
}
if c .stackTop &(16 -1 ) != 0 {
panic ("BUG: stack must be aligned to 16 bytes" )
}
entrypoint (c .preambleExecutable , c .executable , c .execCtxPtr , c .parent .opaquePtr , paramResultPtr , c .stackTop )
for {
switch ec := c .execCtx .exitCode ; ec & wazevoapi .ExitCodeMask {
case wazevoapi .ExitCodeOK :
return nil
case wazevoapi .ExitCodeGrowStack :
oldsp := uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall ))
oldTop := c .stackTop
oldStack := c .stack
var newsp , newfp uintptr
if wazevoapi .StackGuardCheckEnabled {
newsp , newfp , err = c .growStackWithGuarded ()
} else {
newsp , newfp , err = c .growStack ()
}
if err != nil {
return err
}
adjustClonedStack (oldsp , oldTop , newsp , newfp , c .stackTop )
runtime .KeepAlive (oldStack )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr , newsp , newfp )
case wazevoapi .ExitCodeGrowMemory :
mod := c .callerModuleInstance ()
mem := mod .MemoryInstance
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
argRes := &s [0 ]
if res , ok := mem .Grow (uint32 (*argRes )); !ok {
*argRes = uint64 (0xffffffff )
} else {
*argRes = uint64 (res )
}
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr , uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeTableGrow :
mod := c .callerModuleInstance ()
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
tableIndex , num , ref := uint32 (s [0 ]), uint32 (s [1 ]), uintptr (s [2 ])
table := mod .Tables [tableIndex ]
s [0 ] = uint64 (uint32 (int32 (table .Grow (num , ref ))))
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallGoFunction :
index := wazevoapi .GoFunctionIndexFromExitCode (ec )
f := hostModuleGoFuncFromOpaque [api .GoFunction ](index , c .execCtx .goFunctionCallCalleeModuleContextOpaque )
func () {
if snapshotEnabled {
defer snapshotRecoverFn (c )
}
f .Call (ctx , goCallStackView (c .execCtx .stackPointerBeforeGoCall ))
}()
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallGoFunctionWithListener :
index := wazevoapi .GoFunctionIndexFromExitCode (ec )
f := hostModuleGoFuncFromOpaque [api .GoFunction ](index , c .execCtx .goFunctionCallCalleeModuleContextOpaque )
listeners := hostModuleListenersSliceFromOpaque (c .execCtx .goFunctionCallCalleeModuleContextOpaque )
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
callerModule := c .callerModuleInstance ()
listener := listeners [index ]
hostModule := hostModuleFromOpaque (c .execCtx .goFunctionCallCalleeModuleContextOpaque )
def := hostModule .FunctionDefinition (wasm .Index (index ))
listener .Before (ctx , callerModule , def , s , c .stackIterator (true ))
func () {
if snapshotEnabled {
defer snapshotRecoverFn (c )
}
f .Call (ctx , s )
}()
listener .After (ctx , callerModule , def , s )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallGoModuleFunction :
index := wazevoapi .GoFunctionIndexFromExitCode (ec )
f := hostModuleGoFuncFromOpaque [api .GoModuleFunction ](index , c .execCtx .goFunctionCallCalleeModuleContextOpaque )
mod := c .callerModuleInstance ()
func () {
if snapshotEnabled {
defer snapshotRecoverFn (c )
}
f .Call (ctx , mod , goCallStackView (c .execCtx .stackPointerBeforeGoCall ))
}()
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallGoModuleFunctionWithListener :
index := wazevoapi .GoFunctionIndexFromExitCode (ec )
f := hostModuleGoFuncFromOpaque [api .GoModuleFunction ](index , c .execCtx .goFunctionCallCalleeModuleContextOpaque )
listeners := hostModuleListenersSliceFromOpaque (c .execCtx .goFunctionCallCalleeModuleContextOpaque )
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
callerModule := c .callerModuleInstance ()
listener := listeners [index ]
hostModule := hostModuleFromOpaque (c .execCtx .goFunctionCallCalleeModuleContextOpaque )
def := hostModule .FunctionDefinition (wasm .Index (index ))
listener .Before (ctx , callerModule , def , s , c .stackIterator (true ))
func () {
if snapshotEnabled {
defer snapshotRecoverFn (c )
}
f .Call (ctx , callerModule , s )
}()
listener .After (ctx , callerModule , def , s )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallListenerBefore :
stack := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
index := wasm .Index (stack [0 ])
mod := c .callerModuleInstance ()
listener := mod .Engine .(*moduleEngine ).listeners [index ]
def := mod .Source .FunctionDefinition (index + mod .Source .ImportFunctionCount )
listener .Before (ctx , mod , def , stack [1 :], c .stackIterator (false ))
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCallListenerAfter :
stack := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
index := wasm .Index (stack [0 ])
mod := c .callerModuleInstance ()
listener := mod .Engine .(*moduleEngine ).listeners [index ]
def := mod .Source .FunctionDefinition (index + mod .Source .ImportFunctionCount )
listener .After (ctx , mod , def , stack [1 :])
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeCheckModuleExitCode :
if err := m .FailIfClosed (); err != nil {
panic (err )
}
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeRefFunc :
mod := c .callerModuleInstance ()
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
funcIndex := wasm .Index (s [0 ])
ref := mod .Engine .FunctionInstanceReference (funcIndex )
s [0 ] = uint64 (ref )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeMemoryWait32 :
mod := c .callerModuleInstance ()
mem := mod .MemoryInstance
if !mem .Shared {
panic (wasmruntime .ErrRuntimeExpectedSharedMemory )
}
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
timeout , exp , addr := int64 (s [0 ]), uint32 (s [1 ]), uintptr (s [2 ])
base := uintptr (unsafe .Pointer (&mem .Buffer [0 ]))
offset := uint32 (addr - base )
res := mem .Wait32 (offset , exp , timeout , func (mem *wasm .MemoryInstance , offset uint32 ) uint32 {
addr := unsafe .Add (unsafe .Pointer (&mem .Buffer [0 ]), offset )
return atomic .LoadUint32 ((*uint32 )(addr ))
})
s [0 ] = res
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeMemoryWait64 :
mod := c .callerModuleInstance ()
mem := mod .MemoryInstance
if !mem .Shared {
panic (wasmruntime .ErrRuntimeExpectedSharedMemory )
}
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
timeout , exp , addr := int64 (s [0 ]), uint64 (s [1 ]), uintptr (s [2 ])
base := uintptr (unsafe .Pointer (&mem .Buffer [0 ]))
offset := uint32 (addr - base )
res := mem .Wait64 (offset , exp , timeout , func (mem *wasm .MemoryInstance , offset uint32 ) uint64 {
addr := unsafe .Add (unsafe .Pointer (&mem .Buffer [0 ]), offset )
return atomic .LoadUint64 ((*uint64 )(addr ))
})
s [0 ] = uint64 (res )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeMemoryNotify :
mod := c .callerModuleInstance ()
mem := mod .MemoryInstance
s := goCallStackView (c .execCtx .stackPointerBeforeGoCall )
count , addr := uint32 (s [0 ]), s [1 ]
offset := uint32 (uintptr (addr ) - uintptr (unsafe .Pointer (&mem .Buffer [0 ])))
res := mem .Notify (offset , count )
s [0 ] = uint64 (res )
c .execCtx .exitCode = wazevoapi .ExitCodeOK
afterGoFunctionCallEntrypoint (c .execCtx .goCallReturnAddress , c .execCtxPtr ,
uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall )
case wazevoapi .ExitCodeUnreachable :
panic (wasmruntime .ErrRuntimeUnreachable )
case wazevoapi .ExitCodeMemoryOutOfBounds :
panic (wasmruntime .ErrRuntimeOutOfBoundsMemoryAccess )
case wazevoapi .ExitCodeTableOutOfBounds :
panic (wasmruntime .ErrRuntimeInvalidTableAccess )
case wazevoapi .ExitCodeIndirectCallNullPointer :
panic (wasmruntime .ErrRuntimeInvalidTableAccess )
case wazevoapi .ExitCodeIndirectCallTypeMismatch :
panic (wasmruntime .ErrRuntimeIndirectCallTypeMismatch )
case wazevoapi .ExitCodeIntegerOverflow :
panic (wasmruntime .ErrRuntimeIntegerOverflow )
case wazevoapi .ExitCodeIntegerDivisionByZero :
panic (wasmruntime .ErrRuntimeIntegerDivideByZero )
case wazevoapi .ExitCodeInvalidConversionToInteger :
panic (wasmruntime .ErrRuntimeInvalidConversionToInteger )
case wazevoapi .ExitCodeUnalignedAtomic :
panic (wasmruntime .ErrRuntimeUnalignedAtomic )
default :
panic ("BUG" )
}
}
}
func (c *callEngine ) callerModuleInstance () *wasm .ModuleInstance {
return moduleInstanceFromOpaquePtr (c .execCtx .callerModuleContextPtr )
}
const callStackCeiling = uintptr (50000000 )
func (c *callEngine ) growStackWithGuarded () (newSP uintptr , newFP uintptr , err error ) {
if wazevoapi .StackGuardCheckEnabled {
wazevoapi .CheckStackGuardPage (c .stack )
}
newSP , newFP , err = c .growStack ()
if err != nil {
return
}
if wazevoapi .StackGuardCheckEnabled {
c .execCtx .stackBottomPtr = &c .stack [wazevoapi .StackGuardCheckGuardPageSize ]
}
return
}
func (c *callEngine ) growStack () (newSP , newFP uintptr , err error ) {
currentLen := uintptr (len (c .stack ))
if callStackCeiling < currentLen {
err = wasmruntime .ErrRuntimeStackOverflow
return
}
newLen := 2 *currentLen + c .execCtx .stackGrowRequiredSize + 16
newSP , newFP , c .stackTop , c .stack = c .cloneStack (newLen )
c .execCtx .stackBottomPtr = &c .stack [0 ]
return
}
func (c *callEngine ) cloneStack (l uintptr ) (newSP , newFP , newTop uintptr , newStack []byte ) {
newStack = make ([]byte , l )
relSp := c .stackTop - uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall ))
relFp := c .stackTop - c .execCtx .framePointerBeforeGoCall
var prevStackAligned , newStackAligned []byte
{
sh := (*reflect .SliceHeader )(unsafe .Pointer (&prevStackAligned ))
sh .Data = c .stackTop - relSp
sh .Len = int (relSp )
sh .Cap = int (relSp )
}
newTop = alignedStackTop (newStack )
{
newSP = newTop - relSp
newFP = newTop - relFp
sh := (*reflect .SliceHeader )(unsafe .Pointer (&newStackAligned ))
sh .Data = newSP
sh .Len = int (relSp )
sh .Cap = int (relSp )
}
copy (newStackAligned , prevStackAligned )
return
}
func (c *callEngine ) stackIterator (onHostCall bool ) experimental .StackIterator {
c .stackIteratorImpl .reset (c , onHostCall )
return &c .stackIteratorImpl
}
type stackIterator struct {
retAddrs []uintptr
retAddrCursor int
eng *engine
pc uint64
currentDef *wasm .FunctionDefinition
}
func (si *stackIterator ) reset (c *callEngine , onHostCall bool ) {
if onHostCall {
si .retAddrs = append (si .retAddrs [:0 ], uintptr (unsafe .Pointer (c .execCtx .goCallReturnAddress )))
} else {
si .retAddrs = si .retAddrs [:0 ]
}
si .retAddrs = unwindStack (uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall )), c .execCtx .framePointerBeforeGoCall , c .stackTop , si .retAddrs )
si .retAddrs = si .retAddrs [:len (si .retAddrs )-1 ]
si .retAddrCursor = 0
si .eng = c .parent .parent .parent
}
func (si *stackIterator ) Next () bool {
if si .retAddrCursor >= len (si .retAddrs ) {
return false
}
addr := si .retAddrs [si .retAddrCursor ]
cm := si .eng .compiledModuleOfAddr (addr )
if cm != nil {
index := cm .functionIndexOf (addr )
def := cm .module .FunctionDefinition (cm .module .ImportFunctionCount + index )
si .currentDef = def
si .retAddrCursor ++
si .pc = uint64 (addr )
return true
}
return false
}
func (si *stackIterator ) ProgramCounter () experimental .ProgramCounter {
return experimental .ProgramCounter (si .pc )
}
func (si *stackIterator ) Function () experimental .InternalFunction {
return si
}
func (si *stackIterator ) Definition () api .FunctionDefinition {
return si .currentDef
}
func (si *stackIterator ) SourceOffsetForPC (pc experimental .ProgramCounter ) uint64 {
upc := uintptr (pc )
cm := si .eng .compiledModuleOfAddr (upc )
return cm .getSourceOffset (upc )
}
type snapshot struct {
sp, fp, top uintptr
returnAddress *byte
stack []byte
savedRegisters [64 ][2 ]uint64
ret []uint64
c *callEngine
}
func (c *callEngine ) Snapshot () experimental .Snapshot {
returnAddress := c .execCtx .goCallReturnAddress
oldTop , oldSp := c .stackTop , uintptr (unsafe .Pointer (c .execCtx .stackPointerBeforeGoCall ))
newSP , newFP , newTop , newStack := c .cloneStack (uintptr (len (c .stack )) + 16 )
adjustClonedStack (oldSp , oldTop , newSP , newFP , newTop )
return &snapshot {
sp : newSP ,
fp : newFP ,
top : newTop ,
savedRegisters : c .execCtx .savedRegisters ,
returnAddress : returnAddress ,
stack : newStack ,
c : c ,
}
}
func (s *snapshot ) Restore (ret []uint64 ) {
s .ret = ret
panic (s )
}
func (s *snapshot ) doRestore () {
spp := *(**uint64 )(unsafe .Pointer (&s .sp ))
view := goCallStackView (spp )
copy (view , s .ret )
c := s .c
c .stack = s .stack
c .stackTop = s .top
ec := &c .execCtx
ec .stackBottomPtr = &c .stack [0 ]
ec .stackPointerBeforeGoCall = spp
ec .framePointerBeforeGoCall = s .fp
ec .goCallReturnAddress = s .returnAddress
ec .savedRegisters = s .savedRegisters
}
func (s *snapshot ) Error () string {
return "unhandled snapshot restore, this generally indicates restore was called from a different " +
"exported function invocation than snapshot"
}
func snapshotRecoverFn(c *callEngine ) {
if r := recover (); r != nil {
if s , ok := r .(*snapshot ); ok && s .c == c {
s .doRestore ()
} else {
panic (r )
}
}
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .