summaryrefslogtreecommitdiff
path: root/libgo/go/syscall/js/js.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/syscall/js/js.go')
-rw-r--r--libgo/go/syscall/js/js.go164
1 files changed, 127 insertions, 37 deletions
diff --git a/libgo/go/syscall/js/js.go b/libgo/go/syscall/js/js.go
index 7300d2c7695..8a043991711 100644
--- a/libgo/go/syscall/js/js.go
+++ b/libgo/go/syscall/js/js.go
@@ -12,6 +12,7 @@
package js
import (
+ "runtime"
"unsafe"
)
@@ -20,7 +21,7 @@ import (
// 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.
+// an ID and bits 32-34 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 encoded as an IEEE 754 number (see above).
@@ -33,21 +34,45 @@ type Wrapper interface {
}
// Value represents a JavaScript value. The zero value is the JavaScript value "undefined".
+// Values can be checked for equality with the Equal method.
type Value struct {
- ref ref
+ _ [0]func() // uncomparable; to make == not compile
+ ref ref // identifies a JavaScript value, see ref type
+ gcPtr *ref // used to trigger the finalizer when the Value is not referenced any more
}
+const (
+ // the type flags need to be in sync with wasm_exec.js
+ typeFlagNone = iota
+ typeFlagObject
+ typeFlagString
+ typeFlagSymbol
+ typeFlagFunction
+)
+
// JSValue implements Wrapper interface.
func (v Value) JSValue() Value {
return v
}
-func makeValue(v ref) Value {
- return Value{ref: v}
+func makeValue(r ref) Value {
+ var gcPtr *ref
+ typeFlag := (r >> 32) & 7
+ if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone {
+ gcPtr = new(ref)
+ *gcPtr = r
+ runtime.SetFinalizer(gcPtr, func(p *ref) {
+ finalizeRef(*p)
+ })
+ }
+
+ return Value{ref: r, gcPtr: gcPtr}
}
-func predefValue(id uint32) Value {
- return Value{ref: nanHead<<32 | ref(id)}
+func finalizeRef(r ref)
+
+func predefValue(id uint32, typeFlag byte) Value {
+ return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)}
}
func floatValue(f float64) Value {
@@ -73,28 +98,48 @@ func (e Error) Error() string {
var (
valueUndefined = Value{ref: 0}
- valueNaN = predefValue(0)
- valueZero = predefValue(1)
- valueNull = predefValue(2)
- valueTrue = predefValue(3)
- valueFalse = predefValue(4)
- valueGlobal = predefValue(5)
- jsGo = predefValue(6) // instance of the Go class in JavaScript
+ valueNaN = predefValue(0, typeFlagNone)
+ valueZero = predefValue(1, typeFlagNone)
+ valueNull = predefValue(2, typeFlagNone)
+ valueTrue = predefValue(3, typeFlagNone)
+ valueFalse = predefValue(4, typeFlagNone)
+ valueGlobal = predefValue(5, typeFlagObject)
+ jsGo = predefValue(6, typeFlagObject) // instance of the Go class in JavaScript
objectConstructor = valueGlobal.Get("Object")
arrayConstructor = valueGlobal.Get("Array")
)
+// Equal reports whether v and w are equal according to JavaScript's === operator.
+func (v Value) Equal(w Value) bool {
+ return v.ref == w.ref && v.ref != valueNaN.ref
+}
+
// Undefined returns the JavaScript value "undefined".
func Undefined() Value {
return valueUndefined
}
+// IsUndefined reports whether v is the JavaScript value "undefined".
+func (v Value) IsUndefined() bool {
+ return v.ref == valueUndefined.ref
+}
+
// Null returns the JavaScript value "null".
func Null() Value {
return valueNull
}
+// IsNull reports whether v is the JavaScript value "null".
+func (v Value) IsNull() bool {
+ return v.ref == valueNull.ref
+}
+
+// IsNaN reports whether v is the JavaScript value "NaN".
+func (v Value) IsNaN() bool {
+ return v.ref == valueNaN.ref
+}
+
// Global returns the JavaScript global object, usually "window" or "global".
func Global() Value {
return valueGlobal
@@ -232,16 +277,18 @@ func (v Value) Type() Type {
if v.isNumber() {
return TypeNumber
}
- typeFlag := v.ref >> 32 & 3
+ typeFlag := (v.ref >> 32) & 7
switch typeFlag {
- case 1:
+ case typeFlagObject:
+ return TypeObject
+ case typeFlagString:
return TypeString
- case 2:
+ case typeFlagSymbol:
return TypeSymbol
- case 3:
+ case typeFlagFunction:
return TypeFunction
default:
- return TypeObject
+ panic("bad type flag")
}
}
@@ -251,7 +298,9 @@ func (v Value) Get(p string) Value {
if vType := v.Type(); !vType.isObject() {
panic(&ValueError{"Value.Get", vType})
}
- return makeValue(valueGet(v.ref, p))
+ r := makeValue(valueGet(v.ref, p))
+ runtime.KeepAlive(v)
+ return r
}
func valueGet(v ref, p string) ref
@@ -262,18 +311,35 @@ func (v Value) Set(p string, x interface{}) {
if vType := v.Type(); !vType.isObject() {
panic(&ValueError{"Value.Set", vType})
}
- valueSet(v.ref, p, ValueOf(x).ref)
+ xv := ValueOf(x)
+ valueSet(v.ref, p, xv.ref)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(xv)
}
func valueSet(v ref, p string, x ref)
+// Delete deletes the JavaScript property p of value v.
+// It panics if v is not a JavaScript object.
+func (v Value) Delete(p string) {
+ if vType := v.Type(); !vType.isObject() {
+ panic(&ValueError{"Value.Delete", vType})
+ }
+ valueDelete(v.ref, p)
+ runtime.KeepAlive(v)
+}
+
+func valueDelete(v ref, p string)
+
// Index returns JavaScript index i of value v.
// It panics if v is not a JavaScript object.
func (v Value) Index(i int) Value {
if vType := v.Type(); !vType.isObject() {
panic(&ValueError{"Value.Index", vType})
}
- return makeValue(valueIndex(v.ref, i))
+ r := makeValue(valueIndex(v.ref, i))
+ runtime.KeepAlive(v)
+ return r
}
func valueIndex(v ref, i int) ref
@@ -284,17 +350,23 @@ func (v Value) SetIndex(i int, x interface{}) {
if vType := v.Type(); !vType.isObject() {
panic(&ValueError{"Value.SetIndex", vType})
}
- valueSetIndex(v.ref, i, ValueOf(x).ref)
+ xv := ValueOf(x)
+ valueSetIndex(v.ref, i, xv.ref)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(xv)
}
func valueSetIndex(v ref, i int, x ref)
-func makeArgs(args []interface{}) []ref {
- argVals := make([]ref, len(args))
+func makeArgs(args []interface{}) ([]Value, []ref) {
+ argVals := make([]Value, len(args))
+ argRefs := make([]ref, len(args))
for i, arg := range args {
- argVals[i] = ValueOf(arg).ref
+ v := ValueOf(arg)
+ argVals[i] = v
+ argRefs[i] = v.ref
}
- return argVals
+ return argVals, argRefs
}
// Length returns the JavaScript property "length" of v.
@@ -303,7 +375,9 @@ func (v Value) Length() int {
if vType := v.Type(); !vType.isObject() {
panic(&ValueError{"Value.SetIndex", vType})
}
- return valueLength(v.ref)
+ r := valueLength(v.ref)
+ runtime.KeepAlive(v)
+ return r
}
func valueLength(v ref) int
@@ -312,7 +386,10 @@ func valueLength(v ref) int
// It panics if v has no method m.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) Call(m string, args ...interface{}) Value {
- res, ok := valueCall(v.ref, m, makeArgs(args))
+ argVals, argRefs := makeArgs(args)
+ res, ok := valueCall(v.ref, m, argRefs)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(argVals)
if !ok {
if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case
panic(&ValueError{"Value.Call", vType})
@@ -331,7 +408,10 @@ func valueCall(v ref, m string, args []ref) (ref, bool)
// It panics if v is not a JavaScript function.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) Invoke(args ...interface{}) Value {
- res, ok := valueInvoke(v.ref, makeArgs(args))
+ argVals, argRefs := makeArgs(args)
+ res, ok := valueInvoke(v.ref, argRefs)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(argVals)
if !ok {
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
panic(&ValueError{"Value.Invoke", vType})
@@ -347,7 +427,10 @@ func valueInvoke(v ref, args []ref) (ref, bool)
// It panics if v is not a JavaScript function.
// The arguments get mapped to JavaScript values according to the ValueOf function.
func (v Value) New(args ...interface{}) Value {
- res, ok := valueNew(v.ref, makeArgs(args))
+ argVals, argRefs := makeArgs(args)
+ res, ok := valueNew(v.ref, argRefs)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(argVals)
if !ok {
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
panic(&ValueError{"Value.Invoke", vType})
@@ -362,7 +445,7 @@ func valueNew(v ref, args []ref) (ref, bool)
func (v Value) isNumber() bool {
return v.ref == valueZero.ref ||
v.ref == valueNaN.ref ||
- (v.ref != valueUndefined.ref && v.ref>>32&nanHead != nanHead)
+ (v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead)
}
func (v Value) float(method string) float64 {
@@ -427,15 +510,15 @@ func (v Value) Truthy() bool {
func (v Value) String() string {
switch v.Type() {
case TypeString:
- return jsString(v.ref)
+ return jsString(v)
case TypeUndefined:
return "<undefined>"
case TypeNull:
return "<null>"
case TypeBoolean:
- return "<boolean: " + jsString(v.ref) + ">"
+ return "<boolean: " + jsString(v) + ">"
case TypeNumber:
- return "<number: " + jsString(v.ref) + ">"
+ return "<number: " + jsString(v) + ">"
case TypeSymbol:
return "<symbol>"
case TypeObject:
@@ -447,10 +530,12 @@ func (v Value) String() string {
}
}
-func jsString(v ref) string {
- str, length := valuePrepareString(v)
+func jsString(v Value) string {
+ str, length := valuePrepareString(v.ref)
+ runtime.KeepAlive(v)
b := make([]byte, length)
valueLoadString(str, b)
+ finalizeRef(str)
return string(b)
}
@@ -460,7 +545,10 @@ func valueLoadString(v ref, b []byte)
// InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator.
func (v Value) InstanceOf(t Value) bool {
- return valueInstanceOf(v.ref, t.ref)
+ r := valueInstanceOf(v.ref, t.ref)
+ runtime.KeepAlive(v)
+ runtime.KeepAlive(t)
+ return r
}
func valueInstanceOf(v ref, t ref) bool
@@ -482,6 +570,7 @@ func (e *ValueError) Error() string {
// CopyBytesToGo panics if src is not an Uint8Array.
func CopyBytesToGo(dst []byte, src Value) int {
n, ok := copyBytesToGo(dst, src.ref)
+ runtime.KeepAlive(src)
if !ok {
panic("syscall/js: CopyBytesToGo: expected src to be an Uint8Array")
}
@@ -495,6 +584,7 @@ func copyBytesToGo(dst []byte, src ref) (int, bool)
// CopyBytesToJS panics if dst is not an Uint8Array.
func CopyBytesToJS(dst Value, src []byte) int {
n, ok := copyBytesToJS(dst.ref, src)
+ runtime.KeepAlive(dst)
if !ok {
panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array")
}