// Package wasmdebug contains utilities used to give consistent search keys between stack traces and error messages.// Note: This is named wasmdebug to avoid conflicts with the normal go module.// Note: This only imports "api" as importing "wasm" would create a cyclic dependency.
package wasmdebugimport ()// FuncName returns the naming convention of "moduleName.funcName".//// - moduleName is the possibly empty name the module was instantiated with.// - funcName is the name in the Custom Name section.// - funcIdx is the position in the function index, prefixed with// imported functions.//// Note: "moduleName.$funcIdx" is used when the funcName is empty, as commonly// the case in TinyGo.func (, string, uint32) string {varstrings.Builder// Start module.function .WriteString() .WriteByte('.')if == "" { .WriteByte('$') .WriteString(strconv.Itoa(int())) } else { .WriteString() }return .String()}// signature returns a formatted signature similar to how it is defined in Go.//// * paramTypes should be from wasm.FunctionType// * resultTypes should be from wasm.FunctionType// TODO: add paramNamesfunc signature( string, []api.ValueType, []api.ValueType) string {varstrings.Builder .WriteString()// Start params .WriteByte('(') := len()switch {case0:case1: .WriteString(api.ValueTypeName([0]))default: .WriteString(api.ValueTypeName([0]))for , := range [1:] { .WriteByte(',') .WriteString(api.ValueTypeName()) } } .WriteByte(')')// Start results := len()switch {case0:case1: .WriteByte(' ') .WriteString(api.ValueTypeName([0]))default: // As this is used for errors, don't panic if there are multiple returns, even if that's invalid! .WriteByte(' ') .WriteByte('(') .WriteString(api.ValueTypeName([0]))for , := range [1:] { .WriteByte(',') .WriteString(api.ValueTypeName()) } .WriteByte(')') }return .String()}// ErrorBuilder helps build consistent errors, particularly adding a WASM stack trace.//// AddFrame should be called beginning at the frame that panicked until no more frames exist. Once done, call Format.typeErrorBuilderinterface {// AddFrame adds the next frame. // // * funcName should be from FuncName // * paramTypes should be from wasm.FunctionType // * resultTypes should be from wasm.FunctionType // * sources is the source code information for this frame and can be empty. // // Note: paramTypes and resultTypes are present because signature misunderstanding, mismatch or overflow are common.AddFrame(funcName string, paramTypes, resultTypes []api.ValueType, sources []string)// FromRecovered returns an error with the wasm stack trace appended to it.FromRecovered(recovered interface{}) error}func () ErrorBuilder {return &stackTrace{}}type stackTrace struct {// frameCount is the number of stack frame currently pushed into lines. frameCount int// lines contains the stack trace and possibly the inlined source code information. lines []string}// GoRuntimeErrorTracePrefix is the prefix coming before the Go runtime stack trace included in the face of runtime.Error.// This is exported for testing purpose.constGoRuntimeErrorTracePrefix = "Go runtime stack trace:"func ( *stackTrace) ( interface{}) error {iffalse {debug.PrintStack() }if , := .(*sys.ExitError); { // Don't wrap an exit error!return } := strings.Join(.lines, "\n\t")// If the error was internal, don't mention it was recovered.if , := .(*wasmruntime.Error); {returnfmt.Errorf("wasm error: %w\nwasm stack trace:\n\t%s", , ) }// If we have a runtime.Error, something severe happened which should include the stack trace. This could be // a nil pointer from wazero or a user-defined function from HostModuleBuilder.if , := .(runtime.Error); {returnfmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s\n\n%s\n%s", , , GoRuntimeErrorTracePrefix, debug.Stack()) }// At this point we expect the error was from a function defined by HostModuleBuilder that intentionally called panic.if , := .(error); { // e.g. panic(errors.New("whoops"))returnfmt.Errorf("%w (recovered by wazero)\nwasm stack trace:\n\t%s", , ) } else { // e.g. panic("whoops")returnfmt.Errorf("%v (recovered by wazero)\nwasm stack trace:\n\t%s", , ) }}// MaxFrames is the maximum number of frames to include in the stack trace.constMaxFrames = 30// AddFrame implements ErrorBuilder.AddFramefunc ( *stackTrace) ( string, , []api.ValueType, []string) {if .frameCount == MaxFrames {return } .frameCount++ := signature(, , ) .lines = append(.lines, )for , := range { .lines = append(.lines, "\t"+) }if .frameCount == MaxFrames { .lines = append(.lines, "... maybe followed by omitted frames") }}
The pages are generated with Goldsv0.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.