mirror of https://github.com/golang/go.git
syscall/js: use stable references to JavaScript values
This commit changes how JavaScript values are referenced by Go code. After this change, a JavaScript value is always represented by the same ref, even if passed multiple times from JavaScript to Go. This allows Go's == operator to work as expected on js.Value (strict equality). Additionally, the performance of some operations of the syscall/js package got improved by saving additional roundtrips to JavaScript code. Fixes #25802. Change-Id: Ide6ffe66c6aa1caf5327a2d3ddbe48fe7c180461 Reviewed-on: https://go-review.googlesource.com/120561 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
d89efc3c06
commit
8997ec1c4e
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
let outputBuf = "";
|
let outputBuf = "";
|
||||||
global.fs = {
|
global.fs = {
|
||||||
constants: {},
|
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused
|
||||||
writeSync(fd, buf) {
|
writeSync(fd, buf) {
|
||||||
outputBuf += decoder.decode(buf);
|
outputBuf += decoder.decode(buf);
|
||||||
const nl = outputBuf.lastIndexOf("\n");
|
const nl = outputBuf.lastIndexOf("\n");
|
||||||
|
|
@ -81,21 +81,72 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadValue = (addr) => {
|
const loadValue = (addr) => {
|
||||||
|
const f = mem().getFloat64(addr, true);
|
||||||
|
if (!isNaN(f)) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
const id = mem().getUint32(addr, true);
|
const id = mem().getUint32(addr, true);
|
||||||
return this._values[id];
|
return this._values[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const storeValue = (addr, v) => {
|
const storeValue = (addr, v) => {
|
||||||
if (v === undefined) {
|
if (typeof v === "number") {
|
||||||
mem().setUint32(addr, 0, true);
|
if (isNaN(v)) {
|
||||||
|
mem().setUint32(addr + 4, 0x7FF80000, true); // NaN
|
||||||
|
mem().setUint32(addr, 0, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mem().setFloat64(addr, v, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (v === null) {
|
|
||||||
mem().setUint32(addr, 1, true);
|
mem().setUint32(addr + 4, 0x7FF80000, true); // NaN
|
||||||
|
|
||||||
|
switch (v) {
|
||||||
|
case undefined:
|
||||||
|
mem().setUint32(addr, 1, true);
|
||||||
|
return;
|
||||||
|
case null:
|
||||||
|
mem().setUint32(addr, 2, true);
|
||||||
|
return;
|
||||||
|
case true:
|
||||||
|
mem().setUint32(addr, 3, true);
|
||||||
|
return;
|
||||||
|
case false:
|
||||||
|
mem().setUint32(addr, 4, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof v === "string") {
|
||||||
|
let ref = this._stringRefs.get(v);
|
||||||
|
if (ref === undefined) {
|
||||||
|
ref = this._values.length;
|
||||||
|
this._values.push(v);
|
||||||
|
this._stringRefs.set(v, ref);
|
||||||
|
}
|
||||||
|
mem().setUint32(addr, ref, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._values.push(v);
|
|
||||||
mem().setUint32(addr, this._values.length - 1, true);
|
if (typeof v === "symbol") {
|
||||||
|
let ref = this._symbolRefs.get(v);
|
||||||
|
if (ref === undefined) {
|
||||||
|
ref = this._values.length;
|
||||||
|
this._values.push(v);
|
||||||
|
this._symbolRefs.set(v, ref);
|
||||||
|
}
|
||||||
|
mem().setUint32(addr, ref, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ref = v[this._refProp];
|
||||||
|
if (ref === undefined) {
|
||||||
|
ref = this._values.length;
|
||||||
|
this._values.push(v);
|
||||||
|
v[this._refProp] = ref;
|
||||||
|
}
|
||||||
|
mem().setUint32(addr, ref, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSlice = (addr) => {
|
const loadSlice = (addr) => {
|
||||||
|
|
@ -109,8 +160,7 @@
|
||||||
const len = getInt64(addr + 8);
|
const len = getInt64(addr + 8);
|
||||||
const a = new Array(len);
|
const a = new Array(len);
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const id = mem().getUint32(array + i * 4, true);
|
a[i] = loadValue(array + i * 8);
|
||||||
a[i] = this._values[id];
|
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
@ -173,21 +223,6 @@
|
||||||
crypto.getRandomValues(loadSlice(sp + 8));
|
crypto.getRandomValues(loadSlice(sp + 8));
|
||||||
},
|
},
|
||||||
|
|
||||||
// func boolVal(value bool) ref
|
|
||||||
"syscall/js.boolVal": (sp) => {
|
|
||||||
storeValue(sp + 16, mem().getUint8(sp + 8) !== 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// func intVal(value int) ref
|
|
||||||
"syscall/js.intVal": (sp) => {
|
|
||||||
storeValue(sp + 16, getInt64(sp + 8));
|
|
||||||
},
|
|
||||||
|
|
||||||
// func floatVal(value float64) ref
|
|
||||||
"syscall/js.floatVal": (sp) => {
|
|
||||||
storeValue(sp + 16, mem().getFloat64(sp + 8, true));
|
|
||||||
},
|
|
||||||
|
|
||||||
// func stringVal(value string) ref
|
// func stringVal(value string) ref
|
||||||
"syscall/js.stringVal": (sp) => {
|
"syscall/js.stringVal": (sp) => {
|
||||||
storeValue(sp + 24, loadString(sp + 8));
|
storeValue(sp + 24, loadString(sp + 8));
|
||||||
|
|
@ -220,10 +255,10 @@
|
||||||
const m = Reflect.get(v, loadString(sp + 16));
|
const m = Reflect.get(v, loadString(sp + 16));
|
||||||
const args = loadSliceOfValues(sp + 32);
|
const args = loadSliceOfValues(sp + 32);
|
||||||
storeValue(sp + 56, Reflect.apply(m, v, args));
|
storeValue(sp + 56, Reflect.apply(m, v, args));
|
||||||
mem().setUint8(sp + 60, 1);
|
mem().setUint8(sp + 64, 1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
storeValue(sp + 56, err);
|
storeValue(sp + 56, err);
|
||||||
mem().setUint8(sp + 60, 0);
|
mem().setUint8(sp + 64, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -233,10 +268,10 @@
|
||||||
const v = loadValue(sp + 8);
|
const v = loadValue(sp + 8);
|
||||||
const args = loadSliceOfValues(sp + 16);
|
const args = loadSliceOfValues(sp + 16);
|
||||||
storeValue(sp + 40, Reflect.apply(v, undefined, args));
|
storeValue(sp + 40, Reflect.apply(v, undefined, args));
|
||||||
mem().setUint8(sp + 44, 1);
|
mem().setUint8(sp + 48, 1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
storeValue(sp + 40, err);
|
storeValue(sp + 40, err);
|
||||||
mem().setUint8(sp + 44, 0);
|
mem().setUint8(sp + 48, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -246,28 +281,13 @@
|
||||||
const v = loadValue(sp + 8);
|
const v = loadValue(sp + 8);
|
||||||
const args = loadSliceOfValues(sp + 16);
|
const args = loadSliceOfValues(sp + 16);
|
||||||
storeValue(sp + 40, Reflect.construct(v, args));
|
storeValue(sp + 40, Reflect.construct(v, args));
|
||||||
mem().setUint8(sp + 44, 1);
|
mem().setUint8(sp + 48, 1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
storeValue(sp + 40, err);
|
storeValue(sp + 40, err);
|
||||||
mem().setUint8(sp + 44, 0);
|
mem().setUint8(sp + 48, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// func valueFloat(v ref) float64
|
|
||||||
"syscall/js.valueFloat": (sp) => {
|
|
||||||
mem().setFloat64(sp + 16, parseFloat(loadValue(sp + 8)), true);
|
|
||||||
},
|
|
||||||
|
|
||||||
// func valueInt(v ref) int
|
|
||||||
"syscall/js.valueInt": (sp) => {
|
|
||||||
setInt64(sp + 16, parseInt(loadValue(sp + 8)));
|
|
||||||
},
|
|
||||||
|
|
||||||
// func valueBool(v ref) bool
|
|
||||||
"syscall/js.valueBool": (sp) => {
|
|
||||||
mem().setUint8(sp + 16, !!loadValue(sp + 8));
|
|
||||||
},
|
|
||||||
|
|
||||||
// func valueLength(v ref) int
|
// func valueLength(v ref) int
|
||||||
"syscall/js.valueLength": (sp) => {
|
"syscall/js.valueLength": (sp) => {
|
||||||
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
||||||
|
|
@ -288,7 +308,7 @@
|
||||||
|
|
||||||
// func valueInstanceOf(v ref, t ref) bool
|
// func valueInstanceOf(v ref, t ref) bool
|
||||||
"syscall/js.valueInstanceOf": (sp) => {
|
"syscall/js.valueInstanceOf": (sp) => {
|
||||||
mem().setUint8(sp + 16, loadValue(sp + 8) instanceof loadValue(sp + 12));
|
mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
|
||||||
},
|
},
|
||||||
|
|
||||||
"debug": (value) => {
|
"debug": (value) => {
|
||||||
|
|
@ -301,8 +321,11 @@
|
||||||
async run(instance) {
|
async run(instance) {
|
||||||
this._inst = instance;
|
this._inst = instance;
|
||||||
this._values = [ // TODO: garbage collection
|
this._values = [ // TODO: garbage collection
|
||||||
|
NaN,
|
||||||
undefined,
|
undefined,
|
||||||
null,
|
null,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
global,
|
global,
|
||||||
this._inst.exports.mem,
|
this._inst.exports.mem,
|
||||||
() => { // resolveCallbackPromise
|
() => { // resolveCallbackPromise
|
||||||
|
|
@ -312,6 +335,9 @@
|
||||||
setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous
|
setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
this._stringRefs = new Map();
|
||||||
|
this._symbolRefs = new Map();
|
||||||
|
this._refProp = Symbol();
|
||||||
this.exited = false;
|
this.exited = false;
|
||||||
|
|
||||||
const mem = new DataView(this._inst.exports.mem.buffer)
|
const mem = new DataView(this._inst.exports.mem.buffer)
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,12 @@ runtime/asm_wasm.s: [wasm] rt0_go: use of 8(SP) points beyond argument frame
|
||||||
// Calling WebAssembly import. No write from Go assembly.
|
// Calling WebAssembly import. No write from Go assembly.
|
||||||
runtime/sys_wasm.s: [wasm] nanotime: RET without writing to 8-byte ret+0(FP)
|
runtime/sys_wasm.s: [wasm] nanotime: RET without writing to 8-byte ret+0(FP)
|
||||||
runtime/sys_wasm.s: [wasm] scheduleCallback: RET without writing to 4-byte ret+8(FP)
|
runtime/sys_wasm.s: [wasm] scheduleCallback: RET without writing to 4-byte ret+8(FP)
|
||||||
syscall/js/js_js.s: [wasm] boolVal: RET without writing to 4-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] stringVal: RET without writing to 8-byte ret+16(FP)
|
||||||
syscall/js/js_js.s: [wasm] intVal: RET without writing to 4-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] valueGet: RET without writing to 8-byte ret+24(FP)
|
||||||
syscall/js/js_js.s: [wasm] floatVal: RET without writing to 4-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] valueIndex: RET without writing to 8-byte ret+16(FP)
|
||||||
syscall/js/js_js.s: [wasm] stringVal: RET without writing to 4-byte ret+16(FP)
|
syscall/js/js_js.s: [wasm] valueCall: RET without writing to 8-byte ret+48(FP)
|
||||||
syscall/js/js_js.s: [wasm] valueGet: RET without writing to 4-byte ret+24(FP)
|
syscall/js/js_js.s: [wasm] valueInvoke: RET without writing to 8-byte ret+32(FP)
|
||||||
syscall/js/js_js.s: [wasm] valueIndex: RET without writing to 4-byte ret+16(FP)
|
syscall/js/js_js.s: [wasm] valueNew: RET without writing to 8-byte ret+32(FP)
|
||||||
syscall/js/js_js.s: [wasm] valueCall: RET without writing to 4-byte ret+48(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueInvoke: RET without writing to 4-byte ret+32(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueNew: RET without writing to 4-byte ret+32(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueFloat: RET without writing to 8-byte ret+8(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueInt: RET without writing to 8-byte ret+8(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueBool: RET without writing to 1-byte ret+8(FP)
|
|
||||||
syscall/js/js_js.s: [wasm] valueLength: RET without writing to 8-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] valueLength: RET without writing to 8-byte ret+8(FP)
|
||||||
syscall/js/js_js.s: [wasm] valuePrepareString: RET without writing to 4-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] valuePrepareString: RET without writing to 8-byte ret+8(FP)
|
||||||
syscall/js/js_js.s: [wasm] valueInstanceOf: RET without writing to 1-byte ret+8(FP)
|
syscall/js/js_js.s: [wasm] valueInstanceOf: RET without writing to 1-byte ret+16(FP)
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,14 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly itself.
|
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly.
|
||||||
type ref uint32
|
// 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.
|
// Value represents a JavaScript value.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
|
|
@ -27,6 +33,17 @@ func makeValue(v ref) Value {
|
||||||
return Value{ref: v}
|
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.
|
// Error wraps a JavaScript error.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
// Value is the underlying JavaScript error value.
|
// Value is the underlying JavaScript error value.
|
||||||
|
|
@ -39,11 +56,14 @@ func (e Error) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
valueUndefined = makeValue(0)
|
valueNaN = predefValue(0)
|
||||||
valueNull = makeValue(1)
|
valueUndefined = predefValue(1)
|
||||||
valueGlobal = makeValue(2)
|
valueNull = predefValue(2)
|
||||||
memory = makeValue(3) // WebAssembly linear memory
|
valueTrue = predefValue(3)
|
||||||
resolveCallbackPromise = makeValue(4) // function that the callback helper uses to resume the execution of Go's WebAssembly code
|
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".
|
// Undefined returns the JavaScript value "undefined".
|
||||||
|
|
@ -73,35 +93,39 @@ func ValueOf(x interface{}) Value {
|
||||||
case nil:
|
case nil:
|
||||||
return valueNull
|
return valueNull
|
||||||
case bool:
|
case bool:
|
||||||
return makeValue(boolVal(x))
|
if x {
|
||||||
|
return valueTrue
|
||||||
|
} else {
|
||||||
|
return valueFalse
|
||||||
|
}
|
||||||
case int:
|
case int:
|
||||||
return makeValue(intVal(x))
|
return floatValue(float64(x))
|
||||||
case int8:
|
case int8:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case int16:
|
case int16:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case int32:
|
case int32:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case int64:
|
case int64:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uint:
|
case uint:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uint8:
|
case uint8:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uint16:
|
case uint16:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uint32:
|
case uint32:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uint64:
|
case uint64:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case uintptr:
|
case uintptr:
|
||||||
return makeValue(intVal(int(x)))
|
return floatValue(float64(x))
|
||||||
case unsafe.Pointer:
|
case unsafe.Pointer:
|
||||||
return makeValue(intVal(int(uintptr(x))))
|
return floatValue(float64(uintptr(x)))
|
||||||
case float32:
|
case float32:
|
||||||
return makeValue(floatVal(float64(x)))
|
return floatValue(float64(x))
|
||||||
case float64:
|
case float64:
|
||||||
return makeValue(floatVal(x))
|
return floatValue(x)
|
||||||
case string:
|
case string:
|
||||||
return makeValue(stringVal(x))
|
return makeValue(stringVal(x))
|
||||||
case []byte:
|
case []byte:
|
||||||
|
|
@ -114,12 +138,6 @@ func ValueOf(x interface{}) Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolVal(x bool) ref
|
|
||||||
|
|
||||||
func intVal(x int) ref
|
|
||||||
|
|
||||||
func floatVal(x float64) ref
|
|
||||||
|
|
||||||
func stringVal(x string) ref
|
func stringVal(x string) ref
|
||||||
|
|
||||||
// Get returns the JavaScript property p of value v.
|
// Get returns the JavaScript property p of value v.
|
||||||
|
|
@ -201,27 +219,35 @@ func (v Value) New(args ...interface{}) Value {
|
||||||
|
|
||||||
func valueNew(v ref, args []ref) (ref, bool)
|
func valueNew(v ref, args []ref) (ref, bool)
|
||||||
|
|
||||||
// Float returns the value v converted to float64 according to JavaScript type conversions (parseFloat).
|
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 {
|
func (v Value) Float() float64 {
|
||||||
return valueFloat(v.ref)
|
if !v.isNumber() {
|
||||||
|
panic("syscall/js: not a number")
|
||||||
|
}
|
||||||
|
return *(*float64)(unsafe.Pointer(&v.ref))
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueFloat(v ref) float64
|
// Int returns the value v truncated to an int. It panics if v is not a JavaScript number.
|
||||||
|
|
||||||
// Int returns the value v converted to int according to JavaScript type conversions (parseInt).
|
|
||||||
func (v Value) Int() int {
|
func (v Value) Int() int {
|
||||||
return valueInt(v.ref)
|
return int(v.Float())
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueInt(v ref) int
|
// Bool returns the value v as a bool. It panics if v is not a JavaScript boolean.
|
||||||
|
|
||||||
// Bool returns the value v converted to bool according to JavaScript type conversions.
|
|
||||||
func (v Value) Bool() bool {
|
func (v Value) Bool() bool {
|
||||||
return valueBool(v.ref)
|
switch v.ref {
|
||||||
|
case valueTrue.ref:
|
||||||
|
return true
|
||||||
|
case valueFalse.ref:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic("syscall/js: not a boolean")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueBool(v ref) bool
|
|
||||||
|
|
||||||
// String returns the value v converted to string according to JavaScript type conversions.
|
// String returns the value v converted to string according to JavaScript type conversions.
|
||||||
func (v Value) String() string {
|
func (v Value) String() string {
|
||||||
str, length := valuePrepareString(v.ref)
|
str, length := valuePrepareString(v.ref)
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,6 @@
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
TEXT ·boolVal(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·intVal(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·floatVal(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·stringVal(SB), NOSPLIT, $0
|
TEXT ·stringVal(SB), NOSPLIT, $0
|
||||||
CallImport
|
CallImport
|
||||||
RET
|
RET
|
||||||
|
|
@ -48,18 +36,6 @@ TEXT ·valueNew(SB), NOSPLIT, $0
|
||||||
CallImport
|
CallImport
|
||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT ·valueFloat(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·valueInt(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·valueBool(SB), NOSPLIT, $0
|
|
||||||
CallImport
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·valueLength(SB), NOSPLIT, $0
|
TEXT ·valueLength(SB), NOSPLIT, $0
|
||||||
CallImport
|
CallImport
|
||||||
RET
|
RET
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ package js_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -21,6 +22,7 @@ var dummys = js.Global().Call("eval", `({
|
||||||
add: function(a, b) {
|
add: function(a, b) {
|
||||||
return a + b;
|
return a + b;
|
||||||
},
|
},
|
||||||
|
NaN: NaN,
|
||||||
})`)
|
})`)
|
||||||
|
|
||||||
func TestBool(t *testing.T) {
|
func TestBool(t *testing.T) {
|
||||||
|
|
@ -33,6 +35,9 @@ func TestBool(t *testing.T) {
|
||||||
if got := dummys.Get("otherBool").Bool(); got != want {
|
if got := dummys.Get("otherBool").Bool(); got != want {
|
||||||
t.Errorf("got %#v, want %#v", got, want)
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
|
if dummys.Get("someBool") != dummys.Get("someBool") {
|
||||||
|
t.Errorf("same value not equal")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
func TestString(t *testing.T) {
|
||||||
|
|
@ -45,6 +50,9 @@ func TestString(t *testing.T) {
|
||||||
if got := dummys.Get("otherString").String(); got != want {
|
if got := dummys.Get("otherString").String(); got != want {
|
||||||
t.Errorf("got %#v, want %#v", got, want)
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
|
if dummys.Get("someString") != dummys.Get("someString") {
|
||||||
|
t.Errorf("same value not equal")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInt(t *testing.T) {
|
func TestInt(t *testing.T) {
|
||||||
|
|
@ -57,6 +65,9 @@ func TestInt(t *testing.T) {
|
||||||
if got := dummys.Get("otherInt").Int(); got != want {
|
if got := dummys.Get("otherInt").Int(); got != want {
|
||||||
t.Errorf("got %#v, want %#v", got, want)
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
|
if dummys.Get("someInt") != dummys.Get("someInt") {
|
||||||
|
t.Errorf("same value not equal")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntConversion(t *testing.T) {
|
func TestIntConversion(t *testing.T) {
|
||||||
|
|
@ -87,6 +98,23 @@ func TestFloat(t *testing.T) {
|
||||||
if got := dummys.Get("otherFloat").Float(); got != want {
|
if got := dummys.Get("otherFloat").Float(); got != want {
|
||||||
t.Errorf("got %#v, want %#v", got, want)
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
|
if dummys.Get("someFloat") != dummys.Get("someFloat") {
|
||||||
|
t.Errorf("same value not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObject(t *testing.T) {
|
||||||
|
if dummys.Get("someArray") != dummys.Get("someArray") {
|
||||||
|
t.Errorf("same value not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNaN(t *testing.T) {
|
||||||
|
want := js.ValueOf(math.NaN())
|
||||||
|
got := dummys.Get("NaN")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUndefined(t *testing.T) {
|
func TestUndefined(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue