wa-lang.org/wazero@v1.0.2/internal/gojs/js.go (about)

     1  package gojs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"wa-lang.org/wazero/api"
     8  )
     9  
    10  // ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly.
    11  //
    12  // The JavaScript value "undefined" is represented by the value 0.
    13  // A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation.
    14  // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as
    15  // an ID and bits 32-34 used to differentiate between string, symbol, function and object.
    16  type ref uint64
    17  
    18  const (
    19  	parameterSp   = "sp"
    20  	functionDebug = "debug"
    21  )
    22  
    23  // jsFn is a jsCall.call function, configured via jsVal.addFunction.
    24  type jsFn interface {
    25  	invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error)
    26  }
    27  
    28  type jsGet interface {
    29  	get(ctx context.Context, propertyKey string) interface{}
    30  }
    31  
    32  // jsCall allows calling a method/function by name.
    33  type jsCall interface {
    34  	call(ctx context.Context, mod api.Module, this ref, method string, args ...interface{}) (interface{}, error)
    35  }
    36  
    37  // 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).
    38  const nanHead = 0x7FF80000
    39  
    40  type typeFlag byte
    41  
    42  const (
    43  	// the type flags need to be in sync with gojs.js
    44  	typeFlagNone typeFlag = iota
    45  	typeFlagObject
    46  	typeFlagString
    47  	typeFlagSymbol // nolint
    48  	typeFlagFunction
    49  )
    50  
    51  func valueRef(id uint32, typeFlag typeFlag) ref {
    52  	return (nanHead|ref(typeFlag))<<32 | ref(id)
    53  }
    54  
    55  func newJsVal(ref ref, name string) *jsVal {
    56  	return &jsVal{ref: ref, name: name, properties: map[string]interface{}{}, functions: map[string]jsFn{}}
    57  }
    58  
    59  // jsVal corresponds to a generic js.Value in go, when `GOOS=js`.
    60  type jsVal struct {
    61  	// ref when is the constant reference used for built-in values, such as
    62  	// objectConstructor.
    63  	ref
    64  	name       string
    65  	properties map[string]interface{}
    66  	functions  map[string]jsFn
    67  }
    68  
    69  func (v *jsVal) addProperties(properties map[string]interface{}) *jsVal {
    70  	for k, val := range properties {
    71  		v.properties[k] = val
    72  	}
    73  	return v
    74  }
    75  
    76  func (v *jsVal) addFunction(method string, fn jsFn) *jsVal {
    77  	v.functions[method] = fn
    78  	// If fn returns an error, js.Call does a type lookup to verify it is a
    79  	// function.
    80  	// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L389
    81  	v.properties[method] = fn
    82  	return v
    83  }
    84  
    85  // get implements jsGet.get
    86  func (v *jsVal) get(_ context.Context, propertyKey string) interface{} {
    87  	if v, ok := v.properties[propertyKey]; ok {
    88  		return v
    89  	}
    90  	panic(fmt.Sprintf("TODO: get %s.%s", v.name, propertyKey))
    91  }
    92  
    93  // call implements jsCall.call
    94  func (v *jsVal) call(ctx context.Context, mod api.Module, this ref, method string, args ...interface{}) (interface{}, error) {
    95  	if v, ok := v.functions[method]; ok {
    96  		return v.invoke(ctx, mod, args...)
    97  	}
    98  	panic(fmt.Sprintf("TODO: call %s.%s", v.name, method))
    99  }
   100  
   101  // byteArray is a result of uint8ArrayConstructor which temporarily stores
   102  // binary data outside linear memory.
   103  //
   104  // Note: This is a wrapper because a slice is not hashable.
   105  type byteArray struct {
   106  	slice []byte
   107  }
   108  
   109  // get implements jsGet.get
   110  func (a *byteArray) get(_ context.Context, propertyKey string) interface{} {
   111  	switch propertyKey {
   112  	case "byteLength":
   113  		return uint32(len(a.slice))
   114  	}
   115  	panic(fmt.Sprintf("TODO: get byteArray.%s", propertyKey))
   116  }
   117  
   118  // objectArray is a result of arrayConstructor typically used to pass
   119  // indexed arguments.
   120  //
   121  // Note: This is a wrapper because a slice is not hashable.
   122  type objectArray struct {
   123  	slice []interface{}
   124  }
   125  
   126  // object is a result of objectConstructor typically used to pass named
   127  // arguments.
   128  //
   129  // Note: This is a wrapper because a map is not hashable.
   130  type object struct {
   131  	properties map[string]interface{}
   132  }