mirror of https://github.com/golang/go.git
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/122375 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
7951d90bc6
commit
e97ef4127f
|
|
@ -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 @@
|
||||||
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 @@
|
||||||
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 (
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
||||||
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 {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue