Files
wazero/internal/gojs/goarch/wasm.go
Crypt Keeper efc72de1e6 gojs: implements timeout events (#984)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-12-31 08:26:32 +08:00

174 lines
4.6 KiB
Go

// Package goarch isolates code from runtime.GOARCH=wasm in a way that avoids
// cyclic dependencies when re-used from other packages.
package goarch
import (
"context"
"encoding/binary"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/gojs/custom"
"github.com/tetratelabs/wazero/internal/gojs/util"
"github.com/tetratelabs/wazero/internal/wasm"
)
// StubFunction stubs functions not used in Go's main source tree.
// This traps (unreachable opcode) to ensure the function is never called.
func StubFunction(name string) *wasm.HostFunc {
return &wasm.HostFunc{
ExportNames: []string{name},
Name: name,
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
ParamNames: []string{"sp"},
Code: &wasm.Code{IsHostFunction: true, Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}},
}
}
var le = binary.LittleEndian
type Stack interface {
// Name is the function name being invoked.
Name() string
Param(i int) uint64
// ParamBytes reads a byte slice, given its memory offset and length (stack
// positions i, i+1)
ParamBytes(mem api.Memory, i int) []byte
// ParamString reads a string, given its memory offset and length (stack
// positions i, i+1)
ParamString(mem api.Memory, i int) string
ParamUint32(i int) uint32
// Refresh the stack from the current stack pointer (SP).
//
// Note: This is needed prior to storing a value when in an operation that
// can trigger a Go event handler.
Refresh(api.Module)
SetResult(i int, v uint64)
SetResultBool(i int, v bool)
SetResultI32(i int, v int32)
SetResultI64(i int, v int64)
SetResultUint32(i int, v uint32)
}
func NewStack(name string, mem api.Memory, sp uint32) Stack {
names := custom.NameSection[name]
s := &stack{name: name, paramCount: len(names.ParamNames), resultCount: len(names.ResultNames)}
s.refresh(mem, sp)
return s
}
type stack struct {
name string
paramCount, resultCount int
buf []byte
}
// Name implements Stack.Name
func (s *stack) Name() string {
return s.name
}
// Param implements Stack.Param
func (s *stack) Param(i int) (res uint64) {
pos := i << 3
res = le.Uint64(s.buf[pos:])
return
}
// ParamBytes implements Stack.ParamBytes
func (s *stack) ParamBytes(mem api.Memory, i int) (res []byte) {
offset := s.ParamUint32(i)
byteCount := s.ParamUint32(i + 1)
return util.MustRead(mem, s.name, i, offset, byteCount)
}
// ParamString implements Stack.ParamString
func (s *stack) ParamString(mem api.Memory, i int) string {
return string(s.ParamBytes(mem, i)) // safe copy of guest memory
}
// ParamUint32 implements Stack.ParamUint32
func (s *stack) ParamUint32(i int) uint32 {
return uint32(s.Param(i))
}
// Refresh implements Stack.Refresh
func (s *stack) Refresh(mod api.Module) {
s.refresh(mod.Memory(), GetSP(mod))
}
func (s *stack) refresh(mem api.Memory, sp uint32) {
count := uint32(s.paramCount + s.resultCount)
buf, ok := mem.Read(sp+8, count<<3)
if !ok {
panic("out of memory reading stack")
}
s.buf = buf
}
// SetResult implements Stack.SetResult
func (s *stack) SetResult(i int, v uint64) {
pos := (s.paramCount + i) << 3
le.PutUint64(s.buf[pos:], v)
}
// SetResultBool implements Stack.SetResultBool
func (s *stack) SetResultBool(i int, v bool) {
if v {
s.SetResultUint32(i, 1)
} else {
s.SetResultUint32(i, 0)
}
}
// SetResultI32 implements Stack.SetResultI32
func (s *stack) SetResultI32(i int, v int32) {
s.SetResult(i, uint64(v))
}
// SetResultI64 implements Stack.SetResultI64
func (s *stack) SetResultI64(i int, v int64) {
s.SetResult(i, uint64(v))
}
// SetResultUint32 implements Stack.SetResultUint32
func (s *stack) SetResultUint32(i int, v uint32) {
s.SetResult(i, uint64(v))
}
// GetSP gets the stack pointer, which is needed prior to storing a value when
// in an operation that can trigger a Go event handler.
//
// See https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L210-L213
func GetSP(mod api.Module) uint32 {
// Cheat by reading global[0] directly instead of through a function proxy.
// https://github.com/golang/go/blob/go1.19/src/runtime/rt0_js_wasm.s#L87-L90
return uint32(mod.(*wasm.CallContext).GlobalVal(0))
}
func NewFunc(name string, goFunc Func) *wasm.HostFunc {
return util.NewFunc(name, (&stackFunc{name: name, f: goFunc}).Call)
}
type Func func(context.Context, api.Module, Stack)
type stackFunc struct {
name string
f Func
}
// Call implements the same method as defined on api.GoModuleFunction.
func (f *stackFunc) Call(ctx context.Context, mod api.Module, wasmStack []uint64) {
s := NewStack(f.name, mod.Memory(), uint32(wasmStack[0]))
f.f(ctx, mod, s)
}