github.com/tetratelabs/wazero@v1.2.1/internal/gojs/goos/goos.go (about)

     1  // Package goos isolates code from runtime.GOOS=js in a way that avoids cyclic
     2  // dependencies when re-used from other packages.
     3  package goos
     4  
     5  import (
     6  	"context"
     7  	"encoding/binary"
     8  	"fmt"
     9  
    10  	"github.com/tetratelabs/wazero/api"
    11  	"github.com/tetratelabs/wazero/internal/gojs/goarch"
    12  	"github.com/tetratelabs/wazero/internal/gojs/util"
    13  	"github.com/tetratelabs/wazero/internal/wasm"
    14  )
    15  
    16  // Ref is used to identify a JavaScript value, since the value itself cannot
    17  // be passed to WebAssembly.
    18  //
    19  // The JavaScript value "undefined" is represented by the value 0.
    20  //
    21  // A JavaScript number (64-bit float, except 0 and NaN) is represented by its
    22  // IEEE 754 binary representation.
    23  //
    24  // All other values are represented as an IEEE 754 binary representation of NaN
    25  // with bits 0-31 used as an ID and bits 32-34 used to differentiate between
    26  // string, symbol, function and object.
    27  type Ref uint64
    28  
    29  const (
    30  	// predefined
    31  
    32  	IdValueNaN uint32 = iota
    33  	IdValueZero
    34  	IdValueNull
    35  	IdValueTrue
    36  	IdValueFalse
    37  	IdValueGlobal
    38  	IdJsGo
    39  
    40  	// The below are derived from analyzing `*_js.go` source.
    41  
    42  	IdObjectConstructor
    43  	IdArrayConstructor
    44  	IdJsProcess
    45  	IdJsfs
    46  	IdJsfsConstants
    47  	IdUint8ArrayConstructor
    48  	IdJsCrypto
    49  	IdJsDateConstructor
    50  	IdJsDate
    51  	IdHttpFetch
    52  	IdHttpHeaders
    53  	NextID
    54  )
    55  
    56  const (
    57  	RefValueUndefined = Ref(0)
    58  	RefValueNaN       = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueNaN)
    59  	RefValueZero      = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueZero)
    60  	RefValueNull      = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueNull)
    61  	RefValueTrue      = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueTrue)
    62  	RefValueFalse     = (NanHead|Ref(TypeFlagNone))<<32 | Ref(IdValueFalse)
    63  	RefValueGlobal    = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdValueGlobal)
    64  	RefJsGo           = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsGo)
    65  
    66  	RefObjectConstructor      = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdObjectConstructor)
    67  	RefArrayConstructor       = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdArrayConstructor)
    68  	RefJsProcess              = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsProcess)
    69  	RefJsfs                   = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfs)
    70  	RefJsfsConstants          = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsfsConstants)
    71  	RefUint8ArrayConstructor  = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdUint8ArrayConstructor)
    72  	RefJsCrypto               = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsCrypto)
    73  	RefJsDateConstructor      = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdJsDateConstructor)
    74  	RefJsDate                 = (NanHead|Ref(TypeFlagObject))<<32 | Ref(IdJsDate)
    75  	RefHttpFetch              = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdHttpFetch)
    76  	RefHttpHeadersConstructor = (NanHead|Ref(TypeFlagFunction))<<32 | Ref(IdHttpHeaders)
    77  )
    78  
    79  type TypeFlag byte
    80  
    81  // the type flags need to be in sync with gojs.js
    82  const (
    83  	TypeFlagNone TypeFlag = iota
    84  	TypeFlagObject
    85  	TypeFlagString
    86  	TypeFlagSymbol //nolint
    87  	TypeFlagFunction
    88  )
    89  
    90  func ValueRef(id uint32, typeFlag TypeFlag) Ref {
    91  	return (NanHead|Ref(typeFlag))<<32 | Ref(id)
    92  }
    93  
    94  var le = binary.LittleEndian
    95  
    96  // NanHead are the upper 32 bits of a Ref which are set if the value is not encoded as an IEEE 754 number (see above).
    97  const NanHead = 0x7FF80000
    98  
    99  func (ref Ref) ParseFloat() (v float64, ok bool) {
   100  	if (ref>>32)&NanHead != NanHead {
   101  		v = api.DecodeF64(uint64(ref))
   102  		ok = true
   103  	}
   104  	return
   105  }
   106  
   107  // GetLastEventArgs returns the arguments to the last event created by
   108  // custom.NameSyscallValueCall.
   109  type GetLastEventArgs func(context.Context) []interface{}
   110  
   111  type ValLoader func(context.Context, Ref) interface{}
   112  
   113  type Stack interface {
   114  	goarch.Stack
   115  
   116  	ParamRef(i int) Ref
   117  
   118  	ParamRefs(mem api.Memory, i int) []Ref
   119  
   120  	ParamVal(ctx context.Context, i int, loader ValLoader) interface{}
   121  
   122  	// ParamVals is used by functions whose final parameter is an arg array.
   123  	ParamVals(ctx context.Context, mem api.Memory, i int, loader ValLoader) []interface{}
   124  
   125  	SetResultRef(i int, v Ref)
   126  }
   127  
   128  type stack struct {
   129  	s goarch.Stack
   130  }
   131  
   132  // Name implements the same method as documented on goarch.Stack
   133  func (s *stack) Name() string {
   134  	return s.s.Name()
   135  }
   136  
   137  // Param implements the same method as documented on goarch.Stack
   138  func (s *stack) Param(i int) uint64 {
   139  	return s.s.Param(i)
   140  }
   141  
   142  // ParamBytes implements the same method as documented on goarch.Stack
   143  func (s *stack) ParamBytes(mem api.Memory, i int) []byte {
   144  	return s.s.ParamBytes(mem, i)
   145  }
   146  
   147  // ParamRef implements Stack.ParamRef
   148  func (s *stack) ParamRef(i int) Ref {
   149  	return Ref(s.s.Param(i))
   150  }
   151  
   152  // ParamRefs implements Stack.ParamRefs
   153  func (s *stack) ParamRefs(mem api.Memory, i int) []Ref {
   154  	offset := s.s.ParamUint32(i)
   155  	size := s.s.ParamUint32(i + 1)
   156  	byteCount := size << 3 // size * 8
   157  
   158  	result := make([]Ref, 0, size)
   159  
   160  	buf := util.MustRead(mem, s.Name(), i, offset, byteCount)
   161  	for pos := uint32(0); pos < byteCount; pos += 8 {
   162  		ref := Ref(le.Uint64(buf[pos:]))
   163  		result = append(result, ref)
   164  	}
   165  	return result
   166  }
   167  
   168  // ParamString implements the same method as documented on goarch.Stack
   169  func (s *stack) ParamString(mem api.Memory, i int) string {
   170  	return s.s.ParamString(mem, i)
   171  }
   172  
   173  // ParamInt32 implements the same method as documented on goarch.Stack
   174  func (s *stack) ParamInt32(i int) int32 {
   175  	return s.s.ParamInt32(i)
   176  }
   177  
   178  // ParamUint32 implements the same method as documented on goarch.Stack
   179  func (s *stack) ParamUint32(i int) uint32 {
   180  	return s.s.ParamUint32(i)
   181  }
   182  
   183  // ParamVal implements Stack.ParamVal
   184  func (s *stack) ParamVal(ctx context.Context, i int, loader ValLoader) interface{} {
   185  	ref := s.ParamRef(i)
   186  	return loader(ctx, ref)
   187  }
   188  
   189  // ParamVals implements Stack.ParamVals
   190  func (s *stack) ParamVals(ctx context.Context, mem api.Memory, i int, loader ValLoader) []interface{} {
   191  	offset := s.s.ParamUint32(i)
   192  	size := s.s.ParamUint32(i + 1)
   193  	byteCount := size << 3 // size * 8
   194  
   195  	result := make([]interface{}, 0, size)
   196  
   197  	buf := util.MustRead(mem, s.Name(), i, offset, byteCount)
   198  	for pos := uint32(0); pos < byteCount; pos += 8 {
   199  		ref := Ref(le.Uint64(buf[pos:]))
   200  		result = append(result, loader(ctx, ref))
   201  	}
   202  	return result
   203  }
   204  
   205  // Refresh implements the same method as documented on goarch.Stack
   206  func (s *stack) Refresh(mod api.Module) {
   207  	s.s.Refresh(mod)
   208  }
   209  
   210  // SetResult implements the same method as documented on goarch.Stack
   211  func (s *stack) SetResult(i int, v uint64) {
   212  	s.s.SetResult(i, v)
   213  }
   214  
   215  // SetResultBool implements the same method as documented on goarch.Stack
   216  func (s *stack) SetResultBool(i int, v bool) {
   217  	s.s.SetResultBool(i, v)
   218  }
   219  
   220  // SetResultI32 implements the same method as documented on goarch.Stack
   221  func (s *stack) SetResultI32(i int, v int32) {
   222  	s.s.SetResultI32(i, v)
   223  }
   224  
   225  // SetResultI64 implements the same method as documented on goarch.Stack
   226  func (s *stack) SetResultI64(i int, v int64) {
   227  	s.s.SetResultI64(i, v)
   228  }
   229  
   230  // SetResultRef implements Stack.SetResultRef
   231  func (s *stack) SetResultRef(i int, v Ref) {
   232  	s.s.SetResult(i, uint64(v))
   233  }
   234  
   235  // SetResultUint32 implements the same method as documented on goarch.Stack
   236  func (s *stack) SetResultUint32(i int, v uint32) {
   237  	s.s.SetResultUint32(i, v)
   238  }
   239  
   240  func NewFunc(name string, goFunc Func) *wasm.HostFunc {
   241  	sf := &stackFunc{name: name, f: goFunc}
   242  	return util.NewFunc(name, sf.Call)
   243  }
   244  
   245  type Func func(context.Context, api.Module, Stack)
   246  
   247  type stackFunc struct {
   248  	name string
   249  	f    Func
   250  }
   251  
   252  // Call implements the same method as defined on api.GoModuleFunction.
   253  func (f *stackFunc) Call(ctx context.Context, mod api.Module, wasmStack []uint64) {
   254  	s := NewStack(f.name, mod.Memory(), uint32(wasmStack[0]))
   255  	f.f(ctx, mod, s)
   256  }
   257  
   258  func NewStack(name string, mem api.Memory, sp uint32) *stack {
   259  	return &stack{goarch.NewStack(name, mem, sp)}
   260  }
   261  
   262  var Undefined = struct{ name string }{name: "undefined"}
   263  
   264  func ValueToUint32(arg interface{}) uint32 {
   265  	if arg == RefValueZero || arg == Undefined {
   266  		return 0
   267  	} else if u, ok := arg.(uint32); ok {
   268  		return u
   269  	}
   270  	return uint32(arg.(float64))
   271  }
   272  
   273  func ValueToInt32(arg interface{}) int32 {
   274  	if arg == RefValueZero || arg == Undefined {
   275  		return 0
   276  	} else if u, ok := arg.(int); ok {
   277  		return int32(u)
   278  	}
   279  	return int32(uint32(arg.(float64)))
   280  }
   281  
   282  // GetFunction allows getting a JavaScript property by name.
   283  type GetFunction interface {
   284  	Get(propertyKey string) interface{}
   285  }
   286  
   287  // ByteArray is a result of uint8ArrayConstructor which temporarily stores
   288  // binary data outside linear memory.
   289  //
   290  // Note: This is a wrapper because a slice is not hashable.
   291  type ByteArray struct {
   292  	slice []byte
   293  }
   294  
   295  func WrapByteArray(buf []byte) *ByteArray {
   296  	return &ByteArray{buf}
   297  }
   298  
   299  // Unwrap returns the underlying byte slice
   300  func (a *ByteArray) Unwrap() []byte {
   301  	return a.slice
   302  }
   303  
   304  // Get implements GetFunction
   305  func (a *ByteArray) Get(propertyKey string) interface{} {
   306  	switch propertyKey {
   307  	case "byteLength":
   308  		return uint32(len(a.slice))
   309  	}
   310  	panic(fmt.Sprintf("TODO: get byteArray.%s", propertyKey))
   311  }