github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/gojs/goarch/wasm.go (about) 1 // Package goarch isolates code from runtime.GOARCH=wasm in a way that avoids 2 // cyclic dependencies when re-used from other packages. 3 package goarch 4 5 import ( 6 "context" 7 "encoding/binary" 8 9 "github.com/bananabytelabs/wazero/api" 10 "github.com/bananabytelabs/wazero/internal/gojs/custom" 11 "github.com/bananabytelabs/wazero/internal/gojs/util" 12 "github.com/bananabytelabs/wazero/internal/wasm" 13 ) 14 15 // StubFunction stubs functions not used in Go's main source tree. 16 // This traps (unreachable opcode) to ensure the function is never called. 17 func StubFunction(name string) *wasm.HostFunc { 18 return &wasm.HostFunc{ 19 ExportName: name, 20 Name: name, 21 ParamTypes: []wasm.ValueType{wasm.ValueTypeI32}, 22 ParamNames: []string{"sp"}, 23 Code: wasm.Code{GoFunc: api.GoModuleFunc(func(ctx context.Context, _ api.Module, stack []uint64) {})}, 24 } 25 } 26 27 var le = binary.LittleEndian 28 29 type Stack interface { 30 // Name is the function name being invoked. 31 Name() string 32 33 Param(i int) uint64 34 35 // ParamBytes reads a byte slice, given its memory offset and length (stack 36 // positions i, i+1) 37 ParamBytes(mem api.Memory, i int) []byte 38 39 // ParamString reads a string, given its memory offset and length (stack 40 // positions i, i+1) 41 ParamString(mem api.Memory, i int) string 42 43 ParamInt32(i int) int32 44 45 ParamUint32(i int) uint32 46 47 // Refresh the stack from the current stack pointer (SP). 48 // 49 // Note: This is needed prior to storing a value when in an operation that 50 // can trigger a Go event handler. 51 Refresh(api.Module) 52 53 SetResult(i int, v uint64) 54 55 SetResultBool(i int, v bool) 56 57 SetResultI32(i int, v int32) 58 59 SetResultI64(i int, v int64) 60 61 SetResultUint32(i int, v uint32) 62 } 63 64 func NewStack(name string, mem api.Memory, sp uint32) Stack { 65 names := custom.NameSection[name] 66 s := &stack{name: name, paramCount: len(names.ParamNames), resultCount: len(names.ResultNames)} 67 s.refresh(mem, sp) 68 return s 69 } 70 71 type stack struct { 72 name string 73 paramCount, resultCount int 74 buf []byte 75 } 76 77 // Name implements Stack.Name 78 func (s *stack) Name() string { 79 return s.name 80 } 81 82 // Param implements Stack.Param 83 func (s *stack) Param(i int) (res uint64) { 84 pos := i << 3 85 res = le.Uint64(s.buf[pos:]) 86 return 87 } 88 89 // ParamBytes implements Stack.ParamBytes 90 func (s *stack) ParamBytes(mem api.Memory, i int) (res []byte) { 91 offset := s.ParamUint32(i) 92 byteCount := s.ParamUint32(i + 1) 93 return util.MustRead(mem, s.name, i, offset, byteCount) 94 } 95 96 // ParamString implements Stack.ParamString 97 func (s *stack) ParamString(mem api.Memory, i int) string { 98 return string(s.ParamBytes(mem, i)) // safe copy of guest memory 99 } 100 101 // ParamInt32 implements Stack.ParamInt32 102 func (s *stack) ParamInt32(i int) int32 { 103 return int32(s.Param(i)) 104 } 105 106 // ParamUint32 implements Stack.ParamUint32 107 func (s *stack) ParamUint32(i int) uint32 { 108 return uint32(s.Param(i)) 109 } 110 111 // Refresh implements Stack.Refresh 112 func (s *stack) Refresh(mod api.Module) { 113 s.refresh(mod.Memory(), GetSP(mod)) 114 } 115 116 func (s *stack) refresh(mem api.Memory, sp uint32) { 117 count := uint32(s.paramCount + s.resultCount) 118 buf, ok := mem.Read(sp+8, count<<3) 119 if !ok { 120 panic("out of memory reading stack") 121 } 122 s.buf = buf 123 } 124 125 // SetResult implements Stack.SetResult 126 func (s *stack) SetResult(i int, v uint64) { 127 pos := (s.paramCount + i) << 3 128 le.PutUint64(s.buf[pos:], v) 129 } 130 131 // SetResultBool implements Stack.SetResultBool 132 func (s *stack) SetResultBool(i int, v bool) { 133 if v { 134 s.SetResultUint32(i, 1) 135 } else { 136 s.SetResultUint32(i, 0) 137 } 138 } 139 140 // SetResultI32 implements Stack.SetResultI32 141 func (s *stack) SetResultI32(i int, v int32) { 142 s.SetResult(i, uint64(v)) 143 } 144 145 // SetResultI64 implements Stack.SetResultI64 146 func (s *stack) SetResultI64(i int, v int64) { 147 s.SetResult(i, uint64(v)) 148 } 149 150 // SetResultUint32 implements Stack.SetResultUint32 151 func (s *stack) SetResultUint32(i int, v uint32) { 152 s.SetResult(i, uint64(v)) 153 } 154 155 // GetSP gets the stack pointer, which is needed prior to storing a value when 156 // in an operation that can trigger a Go event handler. 157 // 158 // See https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L210-L213 159 func GetSP(mod api.Module) uint32 { 160 // Cheat by reading global[0] directly instead of through a function proxy. 161 // https://github.com/golang/go/blob/go1.20/src/runtime/rt0_js_wasm.s#L87-L90 162 return uint32(mod.(*wasm.ModuleInstance).GlobalVal(0)) 163 } 164 165 func NewFunc(name string, goFunc Func) *wasm.HostFunc { 166 return util.NewFunc(name, (&stackFunc{name: name, f: goFunc}).Call) 167 } 168 169 type Func func(context.Context, api.Module, Stack) 170 171 type stackFunc struct { 172 name string 173 f Func 174 } 175 176 // Call implements the same method as defined on api.GoModuleFunction. 177 func (f *stackFunc) Call(ctx context.Context, mod api.Module, wasmStack []uint64) { 178 s := NewStack(f.name, mod.Memory(), uint32(wasmStack[0])) 179 f.f(ctx, mod, s) 180 }