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 }