Commit e97ef412 authored by Richard Musiol's avatar Richard Musiol Committed by Brad Fitzpatrick

syscall/js: add Value.Type

This commits adds Value.Type(), which returns the JavaScript type of
a Value.

The implementation uses two previously unused bits of the NaN payload
to encode type information.

Change-Id: I568609569983791d50d35b8d80c44f3472203511
Reviewed-on: https://go-review.googlesource.com/122375Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 7951d90b
...@@ -97,9 +97,11 @@ ...@@ -97,9 +97,11 @@
} }
const storeValue = (addr, v) => { const storeValue = (addr, v) => {
const nanHead = 0x7FF80000;
if (typeof v === "number") { if (typeof v === "number") {
if (isNaN(v)) { if (isNaN(v)) {
mem().setUint32(addr + 4, 0x7FF80000, true); // NaN mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 0, true); mem().setUint32(addr, 0, true);
return; return;
} }
...@@ -107,19 +109,21 @@ ...@@ -107,19 +109,21 @@
return; return;
} }
mem().setUint32(addr + 4, 0x7FF80000, true); // NaN
switch (v) { switch (v) {
case undefined: case undefined:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 1, true); mem().setUint32(addr, 1, true);
return; return;
case null: case null:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 2, true); mem().setUint32(addr, 2, true);
return; return;
case true: case true:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 3, true); mem().setUint32(addr, 3, true);
return; return;
case false: case false:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 4, true); mem().setUint32(addr, 4, true);
return; return;
} }
...@@ -130,6 +134,19 @@ ...@@ -130,6 +134,19 @@
this._values.push(v); this._values.push(v);
this._refs.set(v, ref); this._refs.set(v, ref);
} }
let typeFlag = 0;
switch (typeof v) {
case "string":
typeFlag = 1;
break;
case "symbol":
typeFlag = 2;
break;
case "function":
typeFlag = 3;
break;
}
mem().setUint32(addr + 4, nanHead | typeFlag, true);
mem().setUint32(addr, ref, true); mem().setUint32(addr, ref, true);
} }
......
...@@ -17,11 +17,11 @@ import ( ...@@ -17,11 +17,11 @@ import (
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly. // 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. // 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 // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as
// used as an ID. // an ID and bits 32-33 used to differentiate between string, symbol, function and object.
type ref uint64 type ref uint64
// nanHead are the upper 32 bits of a ref 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 a JavaScript number or NaN itself.
const nanHead = 0x7FF80000 const nanHead = 0x7FF80000
// Value represents a JavaScript value. // Value represents a JavaScript value.
...@@ -145,6 +145,70 @@ func ValueOf(x interface{}) Value { ...@@ -145,6 +145,70 @@ func ValueOf(x interface{}) Value {
func stringVal(x string) ref func stringVal(x string) ref
// Type represents the JavaScript type of a Value.
type Type int
const (
TypeUndefined Type = iota
TypeNull
TypeBoolean
TypeNumber
TypeString
TypeSymbol
TypeObject
TypeFunction
)
func (t Type) String() string {
switch t {
case TypeUndefined:
return "undefined"
case TypeNull:
return "null"
case TypeBoolean:
return "boolean"
case TypeNumber:
return "number"
case TypeString:
return "string"
case TypeSymbol:
return "symbol"
case TypeObject:
return "object"
case TypeFunction:
return "function"
default:
panic("bad type")
}
}
// Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator,
// except that it returns TypeNull instead of TypeObject for null.
func (v Value) Type() Type {
switch v.ref {
case valueUndefined.ref:
return TypeUndefined
case valueNull.ref:
return TypeNull
case valueTrue.ref, valueFalse.ref:
return TypeBoolean
}
if v.isNumber() {
return TypeNumber
}
typeFlag := v.ref >> 32 & 3
switch typeFlag {
case 1:
return TypeString
case 2:
return TypeSymbol
case 3:
return TypeFunction
default:
return TypeObject
}
}
// Get returns the JavaScript property p of value v. // Get returns the JavaScript property p of value v.
func (v Value) Get(p string) Value { func (v Value) Get(p string) Value {
return makeValue(valueGet(v.ref, p)) return makeValue(valueGet(v.ref, p))
...@@ -225,7 +289,7 @@ func (v Value) New(args ...interface{}) Value { ...@@ -225,7 +289,7 @@ func (v Value) New(args ...interface{}) Value {
func valueNew(v ref, args []ref) (ref, bool) func valueNew(v ref, args []ref) (ref, bool)
func (v Value) isNumber() bool { func (v Value) isNumber() bool {
return v.ref>>32 != nanHead || v.ref == valueNaN.ref return v.ref>>32&nanHead != nanHead || v.ref == valueNaN.ref
} }
// Float returns the value v as a float64. It panics if v is not a JavaScript number. // Float returns the value v as a float64. It panics if v is not a JavaScript number.
......
...@@ -221,6 +221,33 @@ func TestInstanceOf(t *testing.T) { ...@@ -221,6 +221,33 @@ func TestInstanceOf(t *testing.T) {
} }
} }
func TestType(t *testing.T) {
if got, want := js.Undefined().Type(), js.TypeUndefined; got != want {
t.Errorf("got %s, want %s", got, want)
}
if got, want := js.Null().Type(), js.TypeNull; got != want {
t.Errorf("got %s, want %s", got, want)
}
if got, want := js.ValueOf(true).Type(), js.TypeBoolean; 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)
}
if got, want := js.ValueOf("test").Type(), js.TypeString; got != want {
t.Errorf("got %s, want %s", got, want)
}
if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want {
t.Errorf("got %s, want %s", got, want)
}
if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want {
t.Errorf("got %s, want %s", got, want)
}
if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want {
t.Errorf("got %s, want %s", got, want)
}
}
func TestCallback(t *testing.T) { func TestCallback(t *testing.T) {
c := make(chan struct{}) c := make(chan struct{})
cb := js.NewCallback(func(args []js.Value) { cb := js.NewCallback(func(args []js.Value) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment