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  }