github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/call_engine.go (about) 1 package wazevo 2 3 import ( 4 "context" 5 "encoding/binary" 6 "fmt" 7 "reflect" 8 "unsafe" 9 10 "github.com/wasilibs/wazerox/api" 11 "github.com/wasilibs/wazerox/experimental" 12 "github.com/wasilibs/wazerox/internal/engine/wazevo/wazevoapi" 13 "github.com/wasilibs/wazerox/internal/internalapi" 14 "github.com/wasilibs/wazerox/internal/wasm" 15 "github.com/wasilibs/wazerox/internal/wasmdebug" 16 "github.com/wasilibs/wazerox/internal/wasmruntime" 17 ) 18 19 type ( 20 // callEngine implements api.Function. 21 callEngine struct { 22 internalapi.WazeroOnly 23 stack []byte 24 // stackTop is the pointer to the *aligned* top of the stack. This must be updated 25 // whenever the stack is changed. This is passed to the assembly function 26 // at the very beginning of api.Function Call/CallWithStack. 27 stackTop uintptr 28 // executable is the pointer to the executable code for this function. 29 executable *byte 30 preambleExecutable *byte 31 // parent is the *moduleEngine from which this callEngine is created. 32 parent *moduleEngine 33 // indexInModule is the index of the function in the module. 34 indexInModule wasm.Index 35 // sizeOfParamResultSlice is the size of the parameter/result slice. 36 sizeOfParamResultSlice int 37 requiredParams int 38 // execCtx holds various information to be read/written by assembly functions. 39 execCtx executionContext 40 // execCtxPtr holds the pointer to the executionContext which doesn't change after callEngine is created. 41 execCtxPtr uintptr 42 numberOfResults int 43 stackIteratorImpl stackIterator 44 } 45 46 // executionContext is the struct to be read/written by assembly functions. 47 executionContext struct { 48 // exitCode holds the wazevoapi.ExitCode describing the state of the function execution. 49 exitCode wazevoapi.ExitCode 50 // callerModuleContextPtr holds the moduleContextOpaque for Go function calls. 51 callerModuleContextPtr *byte 52 // originalFramePointer holds the original frame pointer of the caller of the assembly function. 53 originalFramePointer uintptr 54 // originalStackPointer holds the original stack pointer of the caller of the assembly function. 55 originalStackPointer uintptr 56 // goReturnAddress holds the return address to go back to the caller of the assembly function. 57 goReturnAddress uintptr 58 // stackBottomPtr holds the pointer to the bottom of the stack. 59 stackBottomPtr *byte 60 // goCallReturnAddress holds the return address to go back to the caller of the Go function. 61 goCallReturnAddress *byte 62 // stackPointerBeforeGoCall holds the stack pointer before calling a Go function. 63 stackPointerBeforeGoCall *uint64 64 // stackGrowRequiredSize holds the required size of stack grow. 65 stackGrowRequiredSize uintptr 66 // memoryGrowTrampolineAddress holds the address of memory grow trampoline function. 67 memoryGrowTrampolineAddress *byte 68 // stackGrowCallTrampolineAddress holds the address of stack grow trampoline function. 69 stackGrowCallTrampolineAddress *byte 70 // checkModuleExitCodeTrampolineAddress holds the address of check-module-exit-code function. 71 checkModuleExitCodeTrampolineAddress *byte 72 // savedRegisters is the opaque spaces for save/restore registers. 73 // We want to align 16 bytes for each register, so we use [64][2]uint64. 74 savedRegisters [64][2]uint64 75 // goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque. 76 goFunctionCallCalleeModuleContextOpaque uintptr 77 // tableGrowTrampolineAddress holds the address of table grow trampoline function. 78 tableGrowTrampolineAddress *byte 79 // refFuncTrampolineAddress holds the address of ref-func trampoline function. 80 refFuncTrampolineAddress *byte 81 // memmoveAddress holds the address of memmove function implemented by Go runtime. See memmove.go. 82 memmoveAddress uintptr 83 } 84 ) 85 86 func (c *callEngine) requiredInitialStackSize() int { 87 const initialStackSizeDefault = 512 88 stackSize := initialStackSizeDefault 89 paramResultInBytes := c.sizeOfParamResultSlice * 8 * 2 // * 8 because uint64 is 8 bytes, and *2 because we need both separated param/result slots. 90 required := paramResultInBytes + 32 + 16 // 32 is enough to accommodate the call frame info, and 16 exists just in case when []byte is not aligned to 16 bytes. 91 if required > stackSize { 92 stackSize = required 93 } 94 return stackSize 95 } 96 97 func (c *callEngine) init() { 98 stackSize := c.requiredInitialStackSize() 99 if wazevoapi.StackGuardCheckEnabled { 100 stackSize += wazevoapi.StackGuardCheckGuardPageSize 101 } 102 c.stack = make([]byte, stackSize) 103 c.stackTop = alignedStackTop(c.stack) 104 if wazevoapi.StackGuardCheckEnabled { 105 c.execCtx.stackBottomPtr = &c.stack[wazevoapi.StackGuardCheckGuardPageSize] 106 } else { 107 c.execCtx.stackBottomPtr = &c.stack[0] 108 } 109 c.execCtxPtr = uintptr(unsafe.Pointer(&c.execCtx)) 110 } 111 112 // alignedStackTop returns 16-bytes aligned stack top of given stack. 113 // 16 bytes should be good for all platform (arm64/amd64). 114 func alignedStackTop(s []byte) uintptr { 115 stackAddr := uintptr(unsafe.Pointer(&s[len(s)-1])) 116 return stackAddr - (stackAddr & (16 - 1)) 117 } 118 119 // Definition implements api.Function. 120 func (c *callEngine) Definition() api.FunctionDefinition { 121 return c.parent.module.Source.FunctionDefinition(c.indexInModule) 122 } 123 124 // Call implements api.Function. 125 func (c *callEngine) Call(ctx context.Context, params ...uint64) ([]uint64, error) { 126 if c.requiredParams != len(params) { 127 return nil, fmt.Errorf("expected %d params, but passed %d", c.requiredParams, len(params)) 128 } 129 paramResultSlice := make([]uint64, c.sizeOfParamResultSlice) 130 copy(paramResultSlice, params) 131 if err := c.callWithStack(ctx, paramResultSlice); err != nil { 132 return nil, err 133 } 134 return paramResultSlice[:c.numberOfResults], nil 135 } 136 137 func (c *callEngine) addFrame(builder wasmdebug.ErrorBuilder, addr uintptr) (def api.FunctionDefinition, listener experimental.FunctionListener) { 138 eng := c.parent.parent.parent 139 cm := eng.compiledModuleOfAddr(addr) 140 if cm != nil { 141 index := cm.functionIndexOf(addr) 142 def = cm.module.FunctionDefinition(cm.module.ImportFunctionCount + index) 143 var sources []string 144 if dw := cm.module.DWARFLines; dw != nil { 145 sourceOffset := cm.getSourceOffset(addr) 146 sources = dw.Line(sourceOffset) 147 } 148 builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), sources) 149 if len(cm.listeners) > 0 { 150 listener = cm.listeners[index] 151 } 152 } 153 return 154 } 155 156 // CallWithStack implements api.Function. 157 func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint64) (err error) { 158 if c.sizeOfParamResultSlice > len(paramResultStack) { 159 return fmt.Errorf("need %d params, but stack size is %d", c.sizeOfParamResultSlice, len(paramResultStack)) 160 } 161 return c.callWithStack(ctx, paramResultStack) 162 } 163 164 // CallWithStack implements api.Function. 165 func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint64) (err error) { 166 if wazevoapi.StackGuardCheckEnabled { 167 defer func() { 168 wazevoapi.CheckStackGuardPage(c.stack) 169 }() 170 } 171 172 p := c.parent 173 ensureTermination := p.parent.ensureTermination 174 m := p.module 175 if ensureTermination { 176 select { 177 case <-ctx.Done(): 178 // If the provided context is already done, close the module and return the error. 179 m.CloseWithCtxErr(ctx) 180 return m.FailIfClosed() 181 default: 182 } 183 } 184 185 var paramResultPtr *uint64 186 if len(paramResultStack) > 0 { 187 paramResultPtr = ¶mResultStack[0] 188 } 189 defer func() { 190 if r := recover(); r != nil { 191 type listenerForAbort struct { 192 def api.FunctionDefinition 193 lsn experimental.FunctionListener 194 } 195 196 var listeners []listenerForAbort 197 builder := wasmdebug.NewErrorBuilder() 198 def, lsn := c.addFrame(builder, uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress))) 199 if lsn != nil { 200 listeners = append(listeners, listenerForAbort{def, lsn}) 201 } 202 returnAddrs := unwindStack(uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.stackTop, nil) 203 for _, retAddr := range returnAddrs[:len(returnAddrs)-1] { // the last return addr is the trampoline, so we skip it. 204 def, lsn = c.addFrame(builder, retAddr) 205 if lsn != nil { 206 listeners = append(listeners, listenerForAbort{def, lsn}) 207 } 208 } 209 err = builder.FromRecovered(r) 210 211 for _, lsn := range listeners { 212 lsn.lsn.Abort(ctx, m, lsn.def, err) 213 } 214 } else { 215 if err != wasmruntime.ErrRuntimeStackOverflow { // Stackoverflow case shouldn't be panic (to avoid extreme stack unwinding). 216 err = c.parent.module.FailIfClosed() 217 } 218 } 219 220 if err != nil { 221 // Ensures that we can reuse this callEngine even after an error. 222 c.execCtx.exitCode = wazevoapi.ExitCodeOK 223 } 224 }() 225 226 if ensureTermination { 227 done := m.CloseModuleOnCanceledOrTimeout(ctx) 228 defer done() 229 } 230 231 entrypoint(c.preambleExecutable, c.executable, c.execCtxPtr, c.parent.opaquePtr, paramResultPtr, c.stackTop) 232 for { 233 switch ec := c.execCtx.exitCode; ec & wazevoapi.ExitCodeMask { 234 case wazevoapi.ExitCodeOK: 235 return nil 236 case wazevoapi.ExitCodeGrowStack: 237 var newsp uintptr 238 if wazevoapi.StackGuardCheckEnabled { 239 newsp, err = c.growStackWithGuarded() 240 } else { 241 newsp, err = c.growStack() 242 } 243 if err != nil { 244 return err 245 } 246 c.execCtx.exitCode = wazevoapi.ExitCodeOK 247 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, newsp) 248 case wazevoapi.ExitCodeGrowMemory: 249 mod := c.callerModuleInstance() 250 mem := mod.MemoryInstance 251 s := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 252 argRes := &s[0] 253 if res, ok := mem.Grow(uint32(*argRes)); !ok { 254 *argRes = uint64(0xffffffff) // = -1 in signed 32-bit integer. 255 } else { 256 *argRes = uint64(res) 257 calleeOpaque := opaqueViewFromPtr(uintptr(unsafe.Pointer(c.execCtx.callerModuleContextPtr))) 258 if mod.Source.MemorySection != nil { // Local memory. 259 putLocalMemory(calleeOpaque, 8 /* local memory begins at 8 */, mem) 260 } else { 261 // Imported memory's owner at offset 16 of the callerModuleContextPtr. 262 opaquePtr := uintptr(binary.LittleEndian.Uint64(calleeOpaque[16:])) 263 importedMemOwner := opaqueViewFromPtr(opaquePtr) 264 putLocalMemory(importedMemOwner, 8 /* local memory begins at 8 */, mem) 265 } 266 } 267 c.execCtx.exitCode = wazevoapi.ExitCodeOK 268 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 269 case wazevoapi.ExitCodeTableGrow: 270 mod := c.callerModuleInstance() 271 s := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 272 tableIndex, num, ref := s[0], uint32(s[1]), uintptr(s[2]) 273 table := mod.Tables[tableIndex] 274 s[0] = uint64(uint32(int32(table.Grow(num, ref)))) 275 c.execCtx.exitCode = wazevoapi.ExitCodeOK 276 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 277 case wazevoapi.ExitCodeCallGoFunction: 278 index := wazevoapi.GoFunctionIndexFromExitCode(ec) 279 f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque) 280 f.Call(ctx, goCallStackView(c.execCtx.stackPointerBeforeGoCall)) 281 // Back to the native code. 282 c.execCtx.exitCode = wazevoapi.ExitCodeOK 283 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 284 case wazevoapi.ExitCodeCallGoFunctionWithListener: 285 index := wazevoapi.GoFunctionIndexFromExitCode(ec) 286 f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque) 287 listeners := hostModuleListenersSliceFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque) 288 s := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 289 // Call Listener.Before. 290 callerModule := c.callerModuleInstance() 291 listener := listeners[index] 292 hostModule := hostModuleFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque) 293 def := hostModule.FunctionDefinition(wasm.Index(index)) 294 listener.Before(ctx, callerModule, def, s, c.stackIterator(true)) 295 // Call into the Go function. 296 f.Call(ctx, s) 297 // Call Listener.After. 298 listener.After(ctx, callerModule, def, s) 299 // Back to the native code. 300 c.execCtx.exitCode = wazevoapi.ExitCodeOK 301 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 302 case wazevoapi.ExitCodeCallGoModuleFunction: 303 index := wazevoapi.GoFunctionIndexFromExitCode(ec) 304 f := hostModuleGoFuncFromOpaque[api.GoModuleFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque) 305 mod := c.callerModuleInstance() 306 f.Call(ctx, mod, goCallStackView(c.execCtx.stackPointerBeforeGoCall)) 307 // Back to the native code. 308 c.execCtx.exitCode = wazevoapi.ExitCodeOK 309 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 310 case wazevoapi.ExitCodeCallGoModuleFunctionWithListener: 311 index := wazevoapi.GoFunctionIndexFromExitCode(ec) 312 f := hostModuleGoFuncFromOpaque[api.GoModuleFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque) 313 listeners := hostModuleListenersSliceFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque) 314 s := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 315 // Call Listener.Before. 316 callerModule := c.callerModuleInstance() 317 listener := listeners[index] 318 hostModule := hostModuleFromOpaque(c.execCtx.goFunctionCallCalleeModuleContextOpaque) 319 def := hostModule.FunctionDefinition(wasm.Index(index)) 320 listener.Before(ctx, callerModule, def, s, c.stackIterator(true)) 321 // Call into the Go function. 322 f.Call(ctx, callerModule, s) 323 // Call Listener.After. 324 listener.After(ctx, callerModule, def, s) 325 // Back to the native code. 326 c.execCtx.exitCode = wazevoapi.ExitCodeOK 327 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 328 case wazevoapi.ExitCodeCallListenerBefore: 329 stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 330 index := stack[0] 331 mod := c.callerModuleInstance() 332 listener := mod.Engine.(*moduleEngine).listeners[index] 333 def := mod.Source.FunctionDefinition(wasm.Index(index)) 334 listener.Before(ctx, mod, def, stack[1:], c.stackIterator(false)) 335 c.execCtx.exitCode = wazevoapi.ExitCodeOK 336 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 337 case wazevoapi.ExitCodeCallListenerAfter: 338 stack := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 339 index := stack[0] 340 mod := c.callerModuleInstance() 341 listener := mod.Engine.(*moduleEngine).listeners[index] 342 def := mod.Source.FunctionDefinition(wasm.Index(index)) 343 listener.After(ctx, mod, def, stack[1:]) 344 c.execCtx.exitCode = wazevoapi.ExitCodeOK 345 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 346 case wazevoapi.ExitCodeCheckModuleExitCode: 347 // Note: this operation must be done in Go, not native code. The reason is that 348 // native code cannot be preempted and that means it can block forever if there are not 349 // enough OS threads (which we don't have control over). 350 if err := m.FailIfClosed(); err != nil { 351 panic(err) 352 } 353 c.execCtx.exitCode = wazevoapi.ExitCodeOK 354 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 355 case wazevoapi.ExitCodeRefFunc: 356 mod := c.callerModuleInstance() 357 s := goCallStackView(c.execCtx.stackPointerBeforeGoCall) 358 funcIndex := s[0] 359 ref := mod.Engine.FunctionInstanceReference(wasm.Index(funcIndex)) 360 s[0] = uint64(ref) 361 c.execCtx.exitCode = wazevoapi.ExitCodeOK 362 afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))) 363 case wazevoapi.ExitCodeUnreachable: 364 panic(wasmruntime.ErrRuntimeUnreachable) 365 case wazevoapi.ExitCodeMemoryOutOfBounds: 366 panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) 367 case wazevoapi.ExitCodeTableOutOfBounds: 368 panic(wasmruntime.ErrRuntimeInvalidTableAccess) 369 case wazevoapi.ExitCodeIndirectCallNullPointer: 370 panic(wasmruntime.ErrRuntimeInvalidTableAccess) 371 case wazevoapi.ExitCodeIndirectCallTypeMismatch: 372 panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch) 373 case wazevoapi.ExitCodeIntegerOverflow: 374 panic(wasmruntime.ErrRuntimeIntegerOverflow) 375 case wazevoapi.ExitCodeIntegerDivisionByZero: 376 panic(wasmruntime.ErrRuntimeIntegerDivideByZero) 377 case wazevoapi.ExitCodeInvalidConversionToInteger: 378 panic(wasmruntime.ErrRuntimeInvalidConversionToInteger) 379 default: 380 panic("BUG") 381 } 382 } 383 } 384 385 func (c *callEngine) callerModuleInstance() *wasm.ModuleInstance { 386 return moduleInstanceFromOpaquePtr(c.execCtx.callerModuleContextPtr) 387 } 388 389 func opaqueViewFromPtr(ptr uintptr) []byte { 390 var opaque []byte 391 sh := (*reflect.SliceHeader)(unsafe.Pointer(&opaque)) 392 sh.Data = ptr 393 sh.Len = 24 394 sh.Cap = 24 395 return opaque 396 } 397 398 const callStackCeiling = uintptr(50000000) // in uint64 (8 bytes) == 400000000 bytes in total == 400mb. 399 400 func (c *callEngine) growStackWithGuarded() (newSP uintptr, err error) { 401 if wazevoapi.StackGuardCheckEnabled { 402 wazevoapi.CheckStackGuardPage(c.stack) 403 } 404 newSP, err = c.growStack() 405 if err != nil { 406 return 407 } 408 if wazevoapi.StackGuardCheckEnabled { 409 c.execCtx.stackBottomPtr = &c.stack[wazevoapi.StackGuardCheckGuardPageSize] 410 } 411 return 412 } 413 414 // growStack grows the stack, and returns the new stack pointer. 415 func (c *callEngine) growStack() (newSP uintptr, err error) { 416 currentLen := uintptr(len(c.stack)) 417 if callStackCeiling < currentLen { 418 err = wasmruntime.ErrRuntimeStackOverflow 419 return 420 } 421 422 newLen := 2*currentLen + c.execCtx.stackGrowRequiredSize + 16 // Stack might be aligned to 16 bytes, so add 16 bytes just in case. 423 newStack := make([]byte, newLen) 424 425 relSp := c.stackTop - uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)) 426 427 // Copy the existing contents in the previous Go-allocated stack into the new one. 428 var prevStackAligned, newStackAligned []byte 429 { 430 sh := (*reflect.SliceHeader)(unsafe.Pointer(&prevStackAligned)) 431 sh.Data = c.stackTop - relSp 432 sh.Len = int(relSp) 433 sh.Cap = int(relSp) 434 } 435 newTop := alignedStackTop(newStack) 436 { 437 newSP = newTop - relSp 438 sh := (*reflect.SliceHeader)(unsafe.Pointer(&newStackAligned)) 439 sh.Data = newSP 440 sh.Len = int(relSp) 441 sh.Cap = int(relSp) 442 } 443 copy(newStackAligned, prevStackAligned) 444 445 c.stack = newStack 446 c.stackTop = newTop 447 c.execCtx.stackBottomPtr = &newStack[0] 448 return 449 } 450 451 func (c *callEngine) stackIterator(onHostCall bool) experimental.StackIterator { 452 c.stackIteratorImpl.reset(c, onHostCall) 453 return &c.stackIteratorImpl 454 } 455 456 // stackIterator implements experimental.StackIterator. 457 type stackIterator struct { 458 retAddrs []uintptr 459 retAddrCursor int 460 eng *engine 461 pc uint64 462 463 currentDef *wasm.FunctionDefinition 464 } 465 466 func (si *stackIterator) reset(c *callEngine, onHostCall bool) { 467 if onHostCall { 468 si.retAddrs = append(si.retAddrs[:0], uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress))) 469 } else { 470 si.retAddrs = si.retAddrs[:0] 471 } 472 si.retAddrs = unwindStack(uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.stackTop, si.retAddrs) 473 si.retAddrs = si.retAddrs[:len(si.retAddrs)-1] // the last return addr is the trampoline, so we skip it. 474 si.retAddrCursor = 0 475 si.eng = c.parent.parent.parent 476 } 477 478 // Next implements the same method as documented on experimental.StackIterator. 479 func (si *stackIterator) Next() bool { 480 if si.retAddrCursor >= len(si.retAddrs) { 481 return false 482 } 483 484 addr := si.retAddrs[si.retAddrCursor] 485 cm := si.eng.compiledModuleOfAddr(addr) 486 if cm != nil { 487 index := cm.functionIndexOf(addr) 488 def := cm.module.FunctionDefinition(cm.module.ImportFunctionCount + index) 489 si.currentDef = def 490 si.retAddrCursor++ 491 si.pc = uint64(addr) 492 return true 493 } 494 return false 495 } 496 497 // ProgramCounter implements the same method as documented on experimental.StackIterator. 498 func (si *stackIterator) ProgramCounter() experimental.ProgramCounter { 499 return experimental.ProgramCounter(si.pc) 500 } 501 502 // Function implements the same method as documented on experimental.StackIterator. 503 func (si *stackIterator) Function() experimental.InternalFunction { 504 return si 505 } 506 507 // Definition implements the same method as documented on experimental.InternalFunction. 508 func (si *stackIterator) Definition() api.FunctionDefinition { 509 return si.currentDef 510 } 511 512 // SourceOffsetForPC implements the same method as documented on experimental.InternalFunction. 513 func (si *stackIterator) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 { 514 upc := uintptr(pc) 515 cm := si.eng.compiledModuleOfAddr(upc) 516 return cm.getSourceOffset(upc) 517 }