diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index 815b3fbeff465601aae35d5f6cc1f7a5aa66a420..bd9754e53a354f982084fd4eaf43800a0ab33738 100644 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -95,6 +95,9 @@ const loadValue = (addr) => { const f = mem().getFloat64(addr, true); + if (f === 0) { + return undefined; + } if (!isNaN(f)) { return f; } @@ -112,14 +115,18 @@ mem().setUint32(addr, 0, true); return; } + if (v === 0) { + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 1, true); + return; + } mem().setFloat64(addr, v, true); return; } switch (v) { case undefined: - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 1, true); + mem().setFloat64(addr, 0, true); return; case null: mem().setUint32(addr + 4, nanHead, true); @@ -334,7 +341,7 @@ this._inst = instance; this._values = [ // TODO: garbage collection NaN, - undefined, + 0, null, true, false, @@ -396,14 +403,14 @@ } static _makeCallbackHelper(id, pendingCallbacks, go) { - return function() { + return function () { pendingCallbacks.push({ id: id, args: arguments }); go._resolveCallbackPromise(); }; } static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { - return function(event) { + return function (event) { if (preventDefault) { event.preventDefault(); } diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index 336586ca2dd1831d9e695d2d5fb2d9fd5a172fd7..9d826c38864f49a58c1aadbed77f8b4326f5a8c2 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.go @@ -16,15 +16,17 @@ import ( ) // 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. +// +// The JavaScript value "undefined" is represented by the value 0. +// A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation. // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as // an ID and bits 32-33 used to differentiate between string, symbol, function and object. type ref uint64 -// nanHead are the upper 32 bits of a ref which are set if the value is not a JavaScript number or NaN itself. +// 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). const nanHead = 0x7FF80000 -// Value represents a JavaScript value. +// Value represents a JavaScript value. The zero value is the JavaScript value "undefined". type Value struct { ref ref } @@ -38,6 +40,9 @@ func predefValue(id uint32) Value { } func floatValue(f float64) Value { + if f == 0 { + return valueZero + } if f != f { return valueNaN } @@ -56,8 +61,9 @@ func (e Error) Error() string { } var ( + valueUndefined = Value{ref: 0} valueNaN = predefValue(0) - valueUndefined = predefValue(1) + valueZero = predefValue(1) valueNull = predefValue(2) valueTrue = predefValue(3) valueFalse = predefValue(4) @@ -318,13 +324,18 @@ func (v Value) New(args ...interface{}) Value { func valueNew(v ref, args []ref) (ref, bool) func (v Value) isNumber() bool { - return v.ref>>32&nanHead != nanHead || v.ref == valueNaN.ref + return v.ref == valueZero.ref || + v.ref == valueNaN.ref || + (v.ref != valueUndefined.ref && v.ref>>32&nanHead != nanHead) } func (v Value) float(method string) float64 { if !v.isNumber() { panic(&ValueError{method, v.Type()}) } + if v.ref == valueZero.ref { + return 0 + } return *(*float64)(unsafe.Pointer(&v.ref)) } diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 9cc931a31d38d99b3e465508fcc717410e555f70..ed39fe3714187a394e04673846e2c28ff877d8e3 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -22,6 +22,7 @@ var dummys = js.Global().Call("eval", `({ add: function(a, b) { return a + b; }, + zero: 0, NaN: NaN, })`) @@ -74,6 +75,9 @@ func TestInt(t *testing.T) { if dummys.Get("someInt") != dummys.Get("someInt") { t.Errorf("same value not equal") } + if got := dummys.Get("zero").Int(); got != 0 { + t.Errorf("got %#v, want %#v", got, 0) + } } func TestIntConversion(t *testing.T) { @@ -237,6 +241,9 @@ func TestType(t *testing.T) { if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want { t.Errorf("got %s, want %s", got, want) } + if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want { + t.Errorf("got %s, want %s", got, want) + } if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want { t.Errorf("got %s, want %s", got, want) } @@ -269,6 +276,13 @@ func TestValueOf(t *testing.T) { } } +func TestZeroValue(t *testing.T) { + var v js.Value + if v != js.Undefined() { + t.Error("zero js.Value is not js.Undefined()") + } +} + func TestCallback(t *testing.T) { c := make(chan struct{}) cb := js.NewCallback(func(args []js.Value) {