| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build js,wasm |
| |
| // Package js gives access to the WebAssembly host environment when using the js/wasm architecture. |
| // Its API is based on JavaScript semantics. |
| // |
| // This package is EXPERIMENTAL. Its current scope is only to allow tests to run, but not yet to provide a |
| // comprehensive API for users. It is exempt from the Go compatibility promise. |
| package js |
| |
| import ( |
| "unsafe" |
| ) |
| |
| // ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly. |
| // A JavaScript number (64-bit float, except NaN) is represented by its IEEE 754 binary representation. |
| // All other values are represented as an IEEE 754 binary representation of NaN with the low 32 bits |
| // used as an ID. |
| type ref uint64 |
| |
| // nanHead are the upper 32 bits of a ref if the value is not a JavaScript number or NaN itself. |
| const nanHead = 0x7FF80000 |
| |
| // Value represents a JavaScript value. |
| type Value struct { |
| ref ref |
| } |
| |
| func makeValue(v ref) Value { |
| return Value{ref: v} |
| } |
| |
| func predefValue(id uint32) Value { |
| return Value{ref: nanHead<<32 | ref(id)} |
| } |
| |
| func floatValue(f float64) Value { |
| if f != f { |
| return valueNaN |
| } |
| return Value{ref: *(*ref)(unsafe.Pointer(&f))} |
| } |
| |
| // Error wraps a JavaScript error. |
| type Error struct { |
| // Value is the underlying JavaScript error value. |
| Value |
| } |
| |
| // Error implements the error interface. |
| func (e Error) Error() string { |
| return "JavaScript error: " + e.Get("message").String() |
| } |
| |
| var ( |
| valueNaN = predefValue(0) |
| valueUndefined = predefValue(1) |
| valueNull = predefValue(2) |
| valueTrue = predefValue(3) |
| valueFalse = predefValue(4) |
| valueGlobal = predefValue(5) |
| memory = predefValue(6) // WebAssembly linear memory |
| resolveCallbackPromise = predefValue(7) // function that the callback helper uses to resume the execution of Go's WebAssembly code |
| ) |
| |
| // Undefined returns the JavaScript value "undefined". |
| func Undefined() Value { |
| return valueUndefined |
| } |
| |
| // Null returns the JavaScript value "null". |
| func Null() Value { |
| return valueNull |
| } |
| |
| // Global returns the JavaScript global object, usually "window" or "global". |
| func Global() Value { |
| return valueGlobal |
| } |
| |
| var uint8Array = valueGlobal.Get("Uint8Array") |
| |
| // ValueOf returns x as a JavaScript value. |
| func ValueOf(x interface{}) Value { |
| switch x := x.(type) { |
| case Value: |
| return x |
| case Callback: |
| return x.enqueueFn |
| case nil: |
| return valueNull |
| case bool: |
| if x { |
| return valueTrue |
| } else { |
| return valueFalse |
| } |
| case int: |
| return floatValue(float64(x)) |
| case int8: |
| return floatValue(float64(x)) |
| case int16: |
| return floatValue(float64(x)) |
| case int32: |
| return floatValue(float64(x)) |
| case int64: |
| return floatValue(float64(x)) |
| case uint: |
| return floatValue(float64(x)) |
| case uint8: |
| return floatValue(float64(x)) |
| case uint16: |
| return floatValue(float64(x)) |
| case uint32: |
| return floatValue(float64(x)) |
| case uint64: |
| return floatValue(float64(x)) |
| case uintptr: |
| return floatValue(float64(x)) |
| case unsafe.Pointer: |
| return floatValue(float64(uintptr(x))) |
| case float32: |
| return floatValue(float64(x)) |
| case float64: |
| return floatValue(x) |
| case string: |
| return makeValue(stringVal(x)) |
| case []byte: |
| if len(x) == 0 { |
| return uint8Array.New(memory.Get("buffer"), 0, 0) |
| } |
| return uint8Array.New(memory.Get("buffer"), unsafe.Pointer(&x[0]), len(x)) |
| default: |
| panic("invalid value") |
| } |
| } |
| |
| func stringVal(x string) ref |
| |
| // Get returns the JavaScript property p of value v. |
| func (v Value) Get(p string) Value { |
| return makeValue(valueGet(v.ref, p)) |
| } |
| |
| func valueGet(v ref, p string) ref |
| |
| // Set sets the JavaScript property p of value v to x. |
| func (v Value) Set(p string, x interface{}) { |
| valueSet(v.ref, p, ValueOf(x).ref) |
| } |
| |
| func valueSet(v ref, p string, x ref) |
| |
| // Index returns JavaScript index i of value v. |
| func (v Value) Index(i int) Value { |
| return makeValue(valueIndex(v.ref, i)) |
| } |
| |
| func valueIndex(v ref, i int) ref |
| |
| // SetIndex sets the JavaScript index i of value v to x. |
| func (v Value) SetIndex(i int, x interface{}) { |
| valueSetIndex(v.ref, i, ValueOf(x).ref) |
| } |
| |
| func valueSetIndex(v ref, i int, x ref) |
| |
| func makeArgs(args []interface{}) []ref { |
| argVals := make([]ref, len(args)) |
| for i, arg := range args { |
| argVals[i] = ValueOf(arg).ref |
| } |
| return argVals |
| } |
| |
| // Length returns the JavaScript property "length" of v. |
| func (v Value) Length() int { |
| return valueLength(v.ref) |
| } |
| |
| func valueLength(v ref) int |
| |
| // Call does a JavaScript call to the method m of value v with the given arguments. |
| // It panics if v has no method m. |
| func (v Value) Call(m string, args ...interface{}) Value { |
| res, ok := valueCall(v.ref, m, makeArgs(args)) |
| if !ok { |
| panic(Error{makeValue(res)}) |
| } |
| return makeValue(res) |
| } |
| |
| func valueCall(v ref, m string, args []ref) (ref, bool) |
| |
| // Invoke does a JavaScript call of the value v with the given arguments. |
| // It panics if v is not a function. |
| func (v Value) Invoke(args ...interface{}) Value { |
| res, ok := valueInvoke(v.ref, makeArgs(args)) |
| if !ok { |
| panic(Error{makeValue(res)}) |
| } |
| return makeValue(res) |
| } |
| |
| func valueInvoke(v ref, args []ref) (ref, bool) |
| |
| // New uses JavaScript's "new" operator with value v as constructor and the given arguments. |
| // It panics if v is not a function. |
| func (v Value) New(args ...interface{}) Value { |
| res, ok := valueNew(v.ref, makeArgs(args)) |
| if !ok { |
| panic(Error{makeValue(res)}) |
| } |
| return makeValue(res) |
| } |
| |
| func valueNew(v ref, args []ref) (ref, bool) |
| |
| func (v Value) isNumber() bool { |
| return v.ref>>32 != nanHead || v.ref == valueNaN.ref |
| } |
| |
| // Float returns the value v as a float64. It panics if v is not a JavaScript number. |
| func (v Value) Float() float64 { |
| if !v.isNumber() { |
| panic("syscall/js: not a number") |
| } |
| return *(*float64)(unsafe.Pointer(&v.ref)) |
| } |
| |
| // Int returns the value v truncated to an int. It panics if v is not a JavaScript number. |
| func (v Value) Int() int { |
| return int(v.Float()) |
| } |
| |
| // Bool returns the value v as a bool. It panics if v is not a JavaScript boolean. |
| func (v Value) Bool() bool { |
| switch v.ref { |
| case valueTrue.ref: |
| return true |
| case valueFalse.ref: |
| return false |
| default: |
| panic("syscall/js: not a boolean") |
| } |
| } |
| |
| // String returns the value v converted to string according to JavaScript type conversions. |
| func (v Value) String() string { |
| str, length := valuePrepareString(v.ref) |
| b := make([]byte, length) |
| valueLoadString(str, b) |
| return string(b) |
| } |
| |
| func valuePrepareString(v ref) (ref, int) |
| |
| func valueLoadString(v ref, b []byte) |
| |
| // InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. |
| func (v Value) InstanceOf(t Value) bool { |
| return valueInstanceOf(v.ref, t.ref) |
| } |
| |
| func valueInstanceOf(v ref, t ref) bool |