github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 // +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 "unsafe" 16 ) 17 18 // ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly. 19 // 20 // The JavaScript value "undefined" is represented by the value 0. 21 // A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation. 22 // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as 23 // an ID and bits 32-33 used to differentiate between string, symbol, function and object. 24 type ref uint64 25 26 // 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). 27 const nanHead = 0x7FF80000 28 29 // Wrapper is implemented by types that are backed by a JavaScript value. 30 type Wrapper interface { 31 // JSValue returns a JavaScript value associated with an object. 32 JSValue() Value 33 } 34 35 // Value represents a JavaScript value. The zero value is the JavaScript value "undefined". 36 type Value struct { 37 ref ref 38 } 39 40 // JSValue implements Wrapper interface. 41 func (v Value) JSValue() Value { 42 return v 43 } 44 45 func makeValue(v ref) Value { 46 return Value{ref: v} 47 } 48 49 func predefValue(id uint32) Value { 50 return Value{ref: nanHead<<32 | ref(id)} 51 } 52 53 func floatValue(f float64) Value { 54 if f == 0 { 55 return valueZero 56 } 57 if f != f { 58 return valueNaN 59 } 60 return Value{ref: *(*ref)(unsafe.Pointer(&f))} 61 } 62 63 // Error wraps a JavaScript error. 64 type Error struct { 65 // Value is the underlying JavaScript error value. 66 Value 67 } 68 69 // Error implements the error interface. 70 func (e Error) Error() string { 71 return "JavaScript error: " + e.Get("message").String() 72 } 73 74 var ( 75 valueUndefined = Value{ref: 0} 76 valueNaN = predefValue(0) 77 valueZero = predefValue(1) 78 valueNull = predefValue(2) 79 valueTrue = predefValue(3) 80 valueFalse = predefValue(4) 81 valueGlobal = predefValue(5) 82 memory = predefValue(6) // WebAssembly linear memory 83 jsGo = predefValue(7) // instance of the Go class in JavaScript 84 85 objectConstructor = valueGlobal.Get("Object") 86 arrayConstructor = valueGlobal.Get("Array") 87 ) 88 89 // Undefined returns the JavaScript value "undefined". 90 func Undefined() Value { 91 return valueUndefined 92 } 93 94 // Null returns the JavaScript value "null". 95 func Null() Value { 96 return valueNull 97 } 98 99 // Global returns the JavaScript global object, usually "window" or "global". 100 func Global() Value { 101 return valueGlobal 102 } 103 104 // ValueOf returns x as a JavaScript value: 105 // 106 // | Go | JavaScript | 107 // | ---------------------- | ---------------------- | 108 // | js.Value | [its value] | 109 // | js.TypedArray | typed array | 110 // | js.Func | function | 111 // | nil | null | 112 // | bool | boolean | 113 // | integers and floats | number | 114 // | string | string | 115 // | []interface{} | new array | 116 // | map[string]interface{} | new object | 117 // 118 // Panics if x is not one of the expected types. 119 func ValueOf(x interface{}) Value { 120 switch x := x.(type) { 121 case Value: // should precede Wrapper to avoid a loop 122 return x 123 case Wrapper: 124 return x.JSValue() 125 case nil: 126 return valueNull 127 case bool: 128 if x { 129 return valueTrue 130 } else { 131 return valueFalse 132 } 133 case int: 134 return floatValue(float64(x)) 135 case int8: 136 return floatValue(float64(x)) 137 case int16: 138 return floatValue(float64(x)) 139 case int32: 140 return floatValue(float64(x)) 141 case int64: 142 return floatValue(float64(x)) 143 case uint: 144 return floatValue(float64(x)) 145 case uint8: 146 return floatValue(float64(x)) 147 case uint16: 148 return floatValue(float64(x)) 149 case uint32: 150 return floatValue(float64(x)) 151 case uint64: 152 return floatValue(float64(x)) 153 case uintptr: 154 return floatValue(float64(x)) 155 case unsafe.Pointer: 156 return floatValue(float64(uintptr(x))) 157 case float32: 158 return floatValue(float64(x)) 159 case float64: 160 return floatValue(x) 161 case string: 162 return makeValue(stringVal(x)) 163 case []interface{}: 164 a := arrayConstructor.New(len(x)) 165 for i, s := range x { 166 a.SetIndex(i, s) 167 } 168 return a 169 case map[string]interface{}: 170 o := objectConstructor.New() 171 for k, v := range x { 172 o.Set(k, v) 173 } 174 return o 175 default: 176 panic("ValueOf: invalid value") 177 } 178 } 179 180 func stringVal(x string) ref 181 182 // Type represents the JavaScript type of a Value. 183 type Type int 184 185 const ( 186 TypeUndefined Type = iota 187 TypeNull 188 TypeBoolean 189 TypeNumber 190 TypeString 191 TypeSymbol 192 TypeObject 193 TypeFunction 194 ) 195 196 func (t Type) String() string { 197 switch t { 198 case TypeUndefined: 199 return "undefined" 200 case TypeNull: 201 return "null" 202 case TypeBoolean: 203 return "boolean" 204 case TypeNumber: 205 return "number" 206 case TypeString: 207 return "string" 208 case TypeSymbol: 209 return "symbol" 210 case TypeObject: 211 return "object" 212 case TypeFunction: 213 return "function" 214 default: 215 panic("bad type") 216 } 217 } 218 219 // Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator, 220 // except that it returns TypeNull instead of TypeObject for null. 221 func (v Value) Type() Type { 222 switch v.ref { 223 case valueUndefined.ref: 224 return TypeUndefined 225 case valueNull.ref: 226 return TypeNull 227 case valueTrue.ref, valueFalse.ref: 228 return TypeBoolean 229 } 230 if v.isNumber() { 231 return TypeNumber 232 } 233 typeFlag := v.ref >> 32 & 3 234 switch typeFlag { 235 case 1: 236 return TypeString 237 case 2: 238 return TypeSymbol 239 case 3: 240 return TypeFunction 241 default: 242 return TypeObject 243 } 244 } 245 246 // Get returns the JavaScript property p of value v. 247 func (v Value) Get(p string) Value { 248 return makeValue(valueGet(v.ref, p)) 249 } 250 251 func valueGet(v ref, p string) ref 252 253 // Set sets the JavaScript property p of value v to ValueOf(x). 254 func (v Value) Set(p string, x interface{}) { 255 valueSet(v.ref, p, ValueOf(x).ref) 256 } 257 258 func valueSet(v ref, p string, x ref) 259 260 // Index returns JavaScript index i of value v. 261 func (v Value) Index(i int) Value { 262 return makeValue(valueIndex(v.ref, i)) 263 } 264 265 func valueIndex(v ref, i int) ref 266 267 // SetIndex sets the JavaScript index i of value v to ValueOf(x). 268 func (v Value) SetIndex(i int, x interface{}) { 269 valueSetIndex(v.ref, i, ValueOf(x).ref) 270 } 271 272 func valueSetIndex(v ref, i int, x ref) 273 274 func makeArgs(args []interface{}) []ref { 275 argVals := make([]ref, len(args)) 276 for i, arg := range args { 277 argVals[i] = ValueOf(arg).ref 278 } 279 return argVals 280 } 281 282 // Length returns the JavaScript property "length" of v. 283 func (v Value) Length() int { 284 return valueLength(v.ref) 285 } 286 287 func valueLength(v ref) int 288 289 // Call does a JavaScript call to the method m of value v with the given arguments. 290 // It panics if v has no method m. 291 // The arguments get mapped to JavaScript values according to the ValueOf function. 292 func (v Value) Call(m string, args ...interface{}) Value { 293 res, ok := valueCall(v.ref, m, makeArgs(args)) 294 if !ok { 295 if vType := v.Type(); vType != TypeObject && vType != TypeFunction { // check here to avoid overhead in success case 296 panic(&ValueError{"Value.Call", vType}) 297 } 298 if propType := v.Get(m).Type(); propType != TypeFunction { 299 panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String()) 300 } 301 panic(Error{makeValue(res)}) 302 } 303 return makeValue(res) 304 } 305 306 func valueCall(v ref, m string, args []ref) (ref, bool) 307 308 // Invoke does a JavaScript call of the value v with the given arguments. 309 // It panics if v is not a function. 310 // The arguments get mapped to JavaScript values according to the ValueOf function. 311 func (v Value) Invoke(args ...interface{}) Value { 312 res, ok := valueInvoke(v.ref, makeArgs(args)) 313 if !ok { 314 if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case 315 panic(&ValueError{"Value.Invoke", vType}) 316 } 317 panic(Error{makeValue(res)}) 318 } 319 return makeValue(res) 320 } 321 322 func valueInvoke(v ref, args []ref) (ref, bool) 323 324 // New uses JavaScript's "new" operator with value v as constructor and the given arguments. 325 // It panics if v is not a function. 326 // The arguments get mapped to JavaScript values according to the ValueOf function. 327 func (v Value) New(args ...interface{}) Value { 328 res, ok := valueNew(v.ref, makeArgs(args)) 329 if !ok { 330 panic(Error{makeValue(res)}) 331 } 332 return makeValue(res) 333 } 334 335 func valueNew(v ref, args []ref) (ref, bool) 336 337 func (v Value) isNumber() bool { 338 return v.ref == valueZero.ref || 339 v.ref == valueNaN.ref || 340 (v.ref != valueUndefined.ref && v.ref>>32&nanHead != nanHead) 341 } 342 343 func (v Value) float(method string) float64 { 344 if !v.isNumber() { 345 panic(&ValueError{method, v.Type()}) 346 } 347 if v.ref == valueZero.ref { 348 return 0 349 } 350 return *(*float64)(unsafe.Pointer(&v.ref)) 351 } 352 353 // Float returns the value v as a float64. It panics if v is not a JavaScript number. 354 func (v Value) Float() float64 { 355 return v.float("Value.Float") 356 } 357 358 // Int returns the value v truncated to an int. It panics if v is not a JavaScript number. 359 func (v Value) Int() int { 360 return int(v.float("Value.Int")) 361 } 362 363 // Bool returns the value v as a bool. It panics if v is not a JavaScript boolean. 364 func (v Value) Bool() bool { 365 switch v.ref { 366 case valueTrue.ref: 367 return true 368 case valueFalse.ref: 369 return false 370 default: 371 panic(&ValueError{"Value.Bool", v.Type()}) 372 } 373 } 374 375 // Truthy returns the JavaScript "truthiness" of the value v. In JavaScript, 376 // false, 0, "", null, undefined, and NaN are "falsy", and everything else is 377 // "truthy". See https://developer.mozilla.org/en-US/docs/Glossary/Truthy. 378 func (v Value) Truthy() bool { 379 switch v.Type() { 380 case TypeUndefined, TypeNull: 381 return false 382 case TypeBoolean: 383 return v.Bool() 384 case TypeNumber: 385 return v.ref != valueNaN.ref && v.ref != valueZero.ref 386 case TypeString: 387 return v.String() != "" 388 case TypeSymbol, TypeFunction, TypeObject: 389 return true 390 default: 391 panic("bad type") 392 } 393 } 394 395 // String returns the value v converted to string according to JavaScript type conversions. 396 func (v Value) String() string { 397 str, length := valuePrepareString(v.ref) 398 b := make([]byte, length) 399 valueLoadString(str, b) 400 return string(b) 401 } 402 403 func valuePrepareString(v ref) (ref, int) 404 405 func valueLoadString(v ref, b []byte) 406 407 // InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. 408 func (v Value) InstanceOf(t Value) bool { 409 return valueInstanceOf(v.ref, t.ref) 410 } 411 412 func valueInstanceOf(v ref, t ref) bool 413 414 // A ValueError occurs when a Value method is invoked on 415 // a Value that does not support it. Such cases are documented 416 // in the description of each method. 417 type ValueError struct { 418 Method string 419 Type Type 420 } 421 422 func (e *ValueError) Error() string { 423 return "syscall/js: call of " + e.Method + " on " + e.Type.String() 424 }