github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/syscall/js/js.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build js && wasm 6 7 // Package js gives access to the WebAssembly host environment when using the js/wasm architecture. 8 // Its API is based on JavaScript semantics. 9 // 10 // This package is EXPERIMENTAL. Its current scope is only to allow tests to run, but not yet to provide a 11 // comprehensive API for users. It is exempt from the Go compatibility promise. 12 package js 13 14 import ( 15 "runtime" 16 "unsafe" 17 ) 18 19 // ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly. 20 // 21 // The JavaScript value "undefined" is represented by the value 0. 22 // A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation. 23 // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as 24 // an ID and bits 32-34 used to differentiate between string, symbol, function and object. 25 type ref uint64 26 27 // 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). 28 const nanHead = 0x7FF80000 29 30 // Value represents a JavaScript value. The zero value is the JavaScript value "undefined". 31 // Values can be checked for equality with the Equal method. 32 type Value struct { 33 _ [0]func() // uncomparable; to make == not compile 34 ref ref // identifies a JavaScript value, see ref type 35 gcPtr *ref // used to trigger the finalizer when the Value is not referenced any more 36 } 37 38 const ( 39 // the type flags need to be in sync with wasm_exec.js 40 typeFlagNone = iota 41 typeFlagObject 42 typeFlagString 43 typeFlagSymbol 44 typeFlagFunction 45 ) 46 47 func makeValue(r ref) Value { 48 var gcPtr *ref 49 typeFlag := (r >> 32) & 7 50 if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone { 51 gcPtr = new(ref) 52 *gcPtr = r 53 runtime.SetFinalizer(gcPtr, func(p *ref) { 54 finalizeRef(*p) 55 }) 56 } 57 58 return Value{ref: r, gcPtr: gcPtr} 59 } 60 61 func finalizeRef(r ref) 62 63 func predefValue(id uint32, typeFlag byte) Value { 64 return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)} 65 } 66 67 func floatValue(f float64) Value { 68 if f == 0 { 69 return valueZero 70 } 71 if f != f { 72 return valueNaN 73 } 74 return Value{ref: *(*ref)(unsafe.Pointer(&f))} 75 } 76 77 // Error wraps a JavaScript error. 78 type Error struct { 79 // Value is the underlying JavaScript error value. 80 Value 81 } 82 83 // Error implements the error interface. 84 func (e Error) Error() string { 85 return "JavaScript error: " + e.Get("message").String() 86 } 87 88 var ( 89 valueUndefined = Value{ref: 0} 90 valueNaN = predefValue(0, typeFlagNone) 91 valueZero = predefValue(1, typeFlagNone) 92 valueNull = predefValue(2, typeFlagNone) 93 valueTrue = predefValue(3, typeFlagNone) 94 valueFalse = predefValue(4, typeFlagNone) 95 valueGlobal = predefValue(5, typeFlagObject) 96 jsGo = predefValue(6, typeFlagObject) // instance of the Go class in JavaScript 97 98 objectConstructor = valueGlobal.Get("Object") 99 arrayConstructor = valueGlobal.Get("Array") 100 ) 101 102 // Equal reports whether v and w are equal according to JavaScript's === operator. 103 func (v Value) Equal(w Value) bool { 104 return v.ref == w.ref && v.ref != valueNaN.ref 105 } 106 107 // Undefined returns the JavaScript value "undefined". 108 func Undefined() Value { 109 return valueUndefined 110 } 111 112 // IsUndefined reports whether v is the JavaScript value "undefined". 113 func (v Value) IsUndefined() bool { 114 return v.ref == valueUndefined.ref 115 } 116 117 // Null returns the JavaScript value "null". 118 func Null() Value { 119 return valueNull 120 } 121 122 // IsNull reports whether v is the JavaScript value "null". 123 func (v Value) IsNull() bool { 124 return v.ref == valueNull.ref 125 } 126 127 // IsNaN reports whether v is the JavaScript value "NaN". 128 func (v Value) IsNaN() bool { 129 return v.ref == valueNaN.ref 130 } 131 132 // Global returns the JavaScript global object, usually "window" or "global". 133 func Global() Value { 134 return valueGlobal 135 } 136 137 // ValueOf returns x as a JavaScript value: 138 // 139 // | Go | JavaScript | 140 // | ---------------------- | ---------------------- | 141 // | js.Value | [its value] | 142 // | js.Func | function | 143 // | nil | null | 144 // | bool | boolean | 145 // | integers and floats | number | 146 // | string | string | 147 // | []interface{} | new array | 148 // | map[string]interface{} | new object | 149 // 150 // Panics if x is not one of the expected types. 151 func ValueOf(x interface{}) Value { 152 switch x := x.(type) { 153 case Value: 154 return x 155 case Func: 156 return x.Value 157 case nil: 158 return valueNull 159 case bool: 160 if x { 161 return valueTrue 162 } else { 163 return valueFalse 164 } 165 case int: 166 return floatValue(float64(x)) 167 case int8: 168 return floatValue(float64(x)) 169 case int16: 170 return floatValue(float64(x)) 171 case int32: 172 return floatValue(float64(x)) 173 case int64: 174 return floatValue(float64(x)) 175 case uint: 176 return floatValue(float64(x)) 177 case uint8: 178 return floatValue(float64(x)) 179 case uint16: 180 return floatValue(float64(x)) 181 case uint32: 182 return floatValue(float64(x)) 183 case uint64: 184 return floatValue(float64(x)) 185 case uintptr: 186 return floatValue(float64(x)) 187 case unsafe.Pointer: 188 return floatValue(float64(uintptr(x))) 189 case float32: 190 return floatValue(float64(x)) 191 case float64: 192 return floatValue(x) 193 case string: 194 return makeValue(stringVal(x)) 195 case []interface{}: 196 a := arrayConstructor.New(len(x)) 197 for i, s := range x { 198 a.SetIndex(i, s) 199 } 200 return a 201 case map[string]interface{}: 202 o := objectConstructor.New() 203 for k, v := range x { 204 o.Set(k, v) 205 } 206 return o 207 default: 208 panic("ValueOf: invalid value") 209 } 210 } 211 212 func stringVal(x string) ref 213 214 // Type represents the JavaScript type of a Value. 215 type Type int 216 217 const ( 218 TypeUndefined Type = iota 219 TypeNull 220 TypeBoolean 221 TypeNumber 222 TypeString 223 TypeSymbol 224 TypeObject 225 TypeFunction 226 ) 227 228 func (t Type) String() string { 229 switch t { 230 case TypeUndefined: 231 return "undefined" 232 case TypeNull: 233 return "null" 234 case TypeBoolean: 235 return "boolean" 236 case TypeNumber: 237 return "number" 238 case TypeString: 239 return "string" 240 case TypeSymbol: 241 return "symbol" 242 case TypeObject: 243 return "object" 244 case TypeFunction: 245 return "function" 246 default: 247 panic("bad type") 248 } 249 } 250 251 func (t Type) isObject() bool { 252 return t == TypeObject || t == TypeFunction 253 } 254 255 // Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator, 256 // except that it returns TypeNull instead of TypeObject for null. 257 func (v Value) Type() Type { 258 switch v.ref { 259 case valueUndefined.ref: 260 return TypeUndefined 261 case valueNull.ref: 262 return TypeNull 263 case valueTrue.ref, valueFalse.ref: 264 return TypeBoolean 265 } 266 if v.isNumber() { 267 return TypeNumber 268 } 269 typeFlag := (v.ref >> 32) & 7 270 switch typeFlag { 271 case typeFlagObject: 272 return TypeObject 273 case typeFlagString: 274 return TypeString 275 case typeFlagSymbol: 276 return TypeSymbol 277 case typeFlagFunction: 278 return TypeFunction 279 default: 280 panic("bad type flag") 281 } 282 } 283 284 // Get returns the JavaScript property p of value v. 285 // It panics if v is not a JavaScript object. 286 func (v Value) Get(p string) Value { 287 if vType := v.Type(); !vType.isObject() { 288 panic(&ValueError{"Value.Get", vType}) 289 } 290 r := makeValue(valueGet(v.ref, p)) 291 runtime.KeepAlive(v) 292 return r 293 } 294 295 func valueGet(v ref, p string) ref 296 297 // Set sets the JavaScript property p of value v to ValueOf(x). 298 // It panics if v is not a JavaScript object. 299 func (v Value) Set(p string, x interface{}) { 300 if vType := v.Type(); !vType.isObject() { 301 panic(&ValueError{"Value.Set", vType}) 302 } 303 xv := ValueOf(x) 304 valueSet(v.ref, p, xv.ref) 305 runtime.KeepAlive(v) 306 runtime.KeepAlive(xv) 307 } 308 309 func valueSet(v ref, p string, x ref) 310 311 // Delete deletes the JavaScript property p of value v. 312 // It panics if v is not a JavaScript object. 313 func (v Value) Delete(p string) { 314 if vType := v.Type(); !vType.isObject() { 315 panic(&ValueError{"Value.Delete", vType}) 316 } 317 valueDelete(v.ref, p) 318 runtime.KeepAlive(v) 319 } 320 321 func valueDelete(v ref, p string) 322 323 // Index returns JavaScript index i of value v. 324 // It panics if v is not a JavaScript object. 325 func (v Value) Index(i int) Value { 326 if vType := v.Type(); !vType.isObject() { 327 panic(&ValueError{"Value.Index", vType}) 328 } 329 r := makeValue(valueIndex(v.ref, i)) 330 runtime.KeepAlive(v) 331 return r 332 } 333 334 func valueIndex(v ref, i int) ref 335 336 // SetIndex sets the JavaScript index i of value v to ValueOf(x). 337 // It panics if v is not a JavaScript object. 338 func (v Value) SetIndex(i int, x interface{}) { 339 if vType := v.Type(); !vType.isObject() { 340 panic(&ValueError{"Value.SetIndex", vType}) 341 } 342 xv := ValueOf(x) 343 valueSetIndex(v.ref, i, xv.ref) 344 runtime.KeepAlive(v) 345 runtime.KeepAlive(xv) 346 } 347 348 func valueSetIndex(v ref, i int, x ref) 349 350 func makeArgs(args []interface{}) ([]Value, []ref) { 351 argVals := make([]Value, len(args)) 352 argRefs := make([]ref, len(args)) 353 for i, arg := range args { 354 v := ValueOf(arg) 355 argVals[i] = v 356 argRefs[i] = v.ref 357 } 358 return argVals, argRefs 359 } 360 361 // Length returns the JavaScript property "length" of v. 362 // It panics if v is not a JavaScript object. 363 func (v Value) Length() int { 364 if vType := v.Type(); !vType.isObject() { 365 panic(&ValueError{"Value.SetIndex", vType}) 366 } 367 r := valueLength(v.ref) 368 runtime.KeepAlive(v) 369 return r 370 } 371 372 func valueLength(v ref) int 373 374 // Call does a JavaScript call to the method m of value v with the given arguments. 375 // It panics if v has no method m. 376 // The arguments get mapped to JavaScript values according to the ValueOf function. 377 func (v Value) Call(m string, args ...interface{}) Value { 378 argVals, argRefs := makeArgs(args) 379 res, ok := valueCall(v.ref, m, argRefs) 380 runtime.KeepAlive(v) 381 runtime.KeepAlive(argVals) 382 if !ok { 383 if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case 384 panic(&ValueError{"Value.Call", vType}) 385 } 386 if propType := v.Get(m).Type(); propType != TypeFunction { 387 panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String()) 388 } 389 panic(Error{makeValue(res)}) 390 } 391 return makeValue(res) 392 } 393 394 func valueCall(v ref, m string, args []ref) (ref, bool) 395 396 // Invoke does a JavaScript call of the value v with the given arguments. 397 // It panics if v is not a JavaScript function. 398 // The arguments get mapped to JavaScript values according to the ValueOf function. 399 func (v Value) Invoke(args ...interface{}) Value { 400 argVals, argRefs := makeArgs(args) 401 res, ok := valueInvoke(v.ref, argRefs) 402 runtime.KeepAlive(v) 403 runtime.KeepAlive(argVals) 404 if !ok { 405 if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case 406 panic(&ValueError{"Value.Invoke", vType}) 407 } 408 panic(Error{makeValue(res)}) 409 } 410 return makeValue(res) 411 } 412 413 func valueInvoke(v ref, args []ref) (ref, bool) 414 415 // New uses JavaScript's "new" operator with value v as constructor and the given arguments. 416 // It panics if v is not a JavaScript function. 417 // The arguments get mapped to JavaScript values according to the ValueOf function. 418 func (v Value) New(args ...interface{}) Value { 419 argVals, argRefs := makeArgs(args) 420 res, ok := valueNew(v.ref, argRefs) 421 runtime.KeepAlive(v) 422 runtime.KeepAlive(argVals) 423 if !ok { 424 if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case 425 panic(&ValueError{"Value.Invoke", vType}) 426 } 427 panic(Error{makeValue(res)}) 428 } 429 return makeValue(res) 430 } 431 432 func valueNew(v ref, args []ref) (ref, bool) 433 434 func (v Value) isNumber() bool { 435 return v.ref == valueZero.ref || 436 v.ref == valueNaN.ref || 437 (v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead) 438 } 439 440 func (v Value) float(method string) float64 { 441 if !v.isNumber() { 442 panic(&ValueError{method, v.Type()}) 443 } 444 if v.ref == valueZero.ref { 445 return 0 446 } 447 return *(*float64)(unsafe.Pointer(&v.ref)) 448 } 449 450 // Float returns the value v as a float64. 451 // It panics if v is not a JavaScript number. 452 func (v Value) Float() float64 { 453 return v.float("Value.Float") 454 } 455 456 // Int returns the value v truncated to an int. 457 // It panics if v is not a JavaScript number. 458 func (v Value) Int() int { 459 return int(v.float("Value.Int")) 460 } 461 462 // Bool returns the value v as a bool. 463 // It panics if v is not a JavaScript boolean. 464 func (v Value) Bool() bool { 465 switch v.ref { 466 case valueTrue.ref: 467 return true 468 case valueFalse.ref: 469 return false 470 default: 471 panic(&ValueError{"Value.Bool", v.Type()}) 472 } 473 } 474 475 // Truthy returns the JavaScript "truthiness" of the value v. In JavaScript, 476 // false, 0, "", null, undefined, and NaN are "falsy", and everything else is 477 // "truthy". See https://developer.mozilla.org/en-US/docs/Glossary/Truthy. 478 func (v Value) Truthy() bool { 479 switch v.Type() { 480 case TypeUndefined, TypeNull: 481 return false 482 case TypeBoolean: 483 return v.Bool() 484 case TypeNumber: 485 return v.ref != valueNaN.ref && v.ref != valueZero.ref 486 case TypeString: 487 return v.String() != "" 488 case TypeSymbol, TypeFunction, TypeObject: 489 return true 490 default: 491 panic("bad type") 492 } 493 } 494 495 // String returns the value v as a string. 496 // String is a special case because of Go's String method convention. Unlike the other getters, 497 // it does not panic if v's Type is not TypeString. Instead, it returns a string of the form "<T>" 498 // or "<T: V>" where T is v's type and V is a string representation of v's value. 499 func (v Value) String() string { 500 switch v.Type() { 501 case TypeString: 502 return jsString(v) 503 case TypeUndefined: 504 return "<undefined>" 505 case TypeNull: 506 return "<null>" 507 case TypeBoolean: 508 return "<boolean: " + jsString(v) + ">" 509 case TypeNumber: 510 return "<number: " + jsString(v) + ">" 511 case TypeSymbol: 512 return "<symbol>" 513 case TypeObject: 514 return "<object>" 515 case TypeFunction: 516 return "<function>" 517 default: 518 panic("bad type") 519 } 520 } 521 522 func jsString(v Value) string { 523 str, length := valuePrepareString(v.ref) 524 runtime.KeepAlive(v) 525 b := make([]byte, length) 526 valueLoadString(str, b) 527 finalizeRef(str) 528 return string(b) 529 } 530 531 func valuePrepareString(v ref) (ref, int) 532 533 func valueLoadString(v ref, b []byte) 534 535 // InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. 536 func (v Value) InstanceOf(t Value) bool { 537 r := valueInstanceOf(v.ref, t.ref) 538 runtime.KeepAlive(v) 539 runtime.KeepAlive(t) 540 return r 541 } 542 543 func valueInstanceOf(v ref, t ref) bool 544 545 // A ValueError occurs when a Value method is invoked on 546 // a Value that does not support it. Such cases are documented 547 // in the description of each method. 548 type ValueError struct { 549 Method string 550 Type Type 551 } 552 553 func (e *ValueError) Error() string { 554 return "syscall/js: call of " + e.Method + " on " + e.Type.String() 555 } 556 557 // CopyBytesToGo copies bytes from src to dst. 558 // It panics if src is not an Uint8Array or Uint8ClampedArray. 559 // It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. 560 func CopyBytesToGo(dst []byte, src Value) int { 561 n, ok := copyBytesToGo(dst, src.ref) 562 runtime.KeepAlive(src) 563 if !ok { 564 panic("syscall/js: CopyBytesToGo: expected src to be an Uint8Array or Uint8ClampedArray") 565 } 566 return n 567 } 568 569 func copyBytesToGo(dst []byte, src ref) (int, bool) 570 571 // CopyBytesToJS copies bytes from src to dst. 572 // It panics if dst is not an Uint8Array or Uint8ClampedArray. 573 // It returns the number of bytes copied, which will be the minimum of the lengths of src and dst. 574 func CopyBytesToJS(dst Value, src []byte) int { 575 n, ok := copyBytesToJS(dst.ref, src) 576 runtime.KeepAlive(dst) 577 if !ok { 578 panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array or Uint8ClampedArray") 579 } 580 return n 581 } 582 583 func copyBytesToJS(dst ref, src []byte) (int, bool)