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 }