]> Cypherpunks.ru repositories - gostls13.git/commitdiff
syscall/js: make zero js.Value represent "undefined"
authorRichard Musiol <mail@richard-musiol.de>
Thu, 18 Oct 2018 13:53:38 +0000 (15:53 +0200)
committerRichard Musiol <neelance@gmail.com>
Thu, 18 Oct 2018 15:20:30 +0000 (15:20 +0000)
This commit changes the encoding of js.Value so that the zero js.Value
represents the JavaScript value "undefined". This is what users
intuitively expect.

Specifically, the encodings of "undefined" and the number zero have
been swapped.

Fixes #27592.

Change-Id: Icfc832c8cdf7a8a78bd69d20e00a04dbed0ccd10
Reviewed-on: https://go-review.googlesource.com/c/143137
Run-TryBot: Richard Musiol <neelance@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
misc/wasm/wasm_exec.js
src/syscall/js/js.go
src/syscall/js/js_test.go

index 815b3fbeff465601aae35d5f6cc1f7a5aa66a420..bd9754e53a354f982084fd4eaf43800a0ab33738 100644 (file)
@@ -95,6 +95,9 @@
 
                        const loadValue = (addr) => {
                                const f = mem().getFloat64(addr, true);
+                               if (f === 0) {
+                                       return undefined;
+                               }
                                if (!isNaN(f)) {
                                        return f;
                                }
                                                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);
                        this._inst = instance;
                        this._values = [ // TODO: garbage collection
                                NaN,
-                               undefined,
+                               0,
                                null,
                                true,
                                false,
                }
 
                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();
                                }
index 336586ca2dd1831d9e695d2d5fb2d9fd5a172fd7..9d826c38864f49a58c1aadbed77f8b4326f5a8c2 100644 (file)
@@ -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))
 }
 
index 9cc931a31d38d99b3e465508fcc717410e555f70..ed39fe3714187a394e04673846e2c28ff877d8e3 100644 (file)
@@ -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) {