aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/syscall/js
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2019-01-18 19:04:36 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-01-18 19:04:36 +0000
commit4f4a855d82a889cebcfca150a7a43909bcb6a346 (patch)
treef12bae0781920fa34669fe30b6f4615a86d9fb80 /libgo/go/syscall/js
parent225220d668dafb8262db7012bced688acbe63b33 (diff)
downloadgcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.zip
gcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.tar.gz
gcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.tar.bz2
libgo: update to Go1.12beta2
Reviewed-on: https://go-review.googlesource.com/c/158019 gotools/: * Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release. (GOTOOLS_TEST_TIMEOUT): Increase to 600. (check-runtime): Export LD_LIBRARY_PATH before computing GOARCH and GOOS. (check-vet): Copy golang.org/x/tools into check-vet-dir. * Makefile.in: Regenerate. gcc/testsuite/: * go.go-torture/execute/names-1.go: Stop using debug/xcoff, which is no longer externally visible. From-SVN: r268084
Diffstat (limited to 'libgo/go/syscall/js')
-rw-r--r--libgo/go/syscall/js/callback.go122
-rw-r--r--libgo/go/syscall/js/func.go92
-rw-r--r--libgo/go/syscall/js/js.go64
-rw-r--r--libgo/go/syscall/js/js_test.go120
-rw-r--r--libgo/go/syscall/js/typedarray.go2
5 files changed, 238 insertions, 162 deletions
diff --git a/libgo/go/syscall/js/callback.go b/libgo/go/syscall/js/callback.go
deleted file mode 100644
index 9d57307..0000000
--- a/libgo/go/syscall/js/callback.go
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build js,wasm
-
-package js
-
-import "sync"
-
-var (
- pendingCallbacks = Global().Get("Array").New()
- makeCallbackHelper = Global().Get("Go").Get("_makeCallbackHelper")
- makeEventCallbackHelper = Global().Get("Go").Get("_makeEventCallbackHelper")
-)
-
-var (
- callbacksMu sync.Mutex
- callbacks = make(map[uint32]func([]Value))
- nextCallbackID uint32 = 1
-)
-
-// Callback is a Go function that got wrapped for use as a JavaScript callback.
-type Callback struct {
- Value // the JavaScript function that queues the callback for execution
- id uint32
-}
-
-// NewCallback returns a wrapped callback function.
-//
-// Invoking the callback in JavaScript will queue the Go function fn for execution.
-// This execution happens asynchronously on a special goroutine that handles all callbacks and preserves
-// the order in which the callbacks got called.
-// As a consequence, if one callback blocks this goroutine, other callbacks will not be processed.
-// A blocking callback should therefore explicitly start a new goroutine.
-//
-// Callback.Release must be called to free up resources when the callback will not be used any more.
-func NewCallback(fn func(args []Value)) Callback {
- callbackLoopOnce.Do(func() {
- go callbackLoop()
- })
-
- callbacksMu.Lock()
- id := nextCallbackID
- nextCallbackID++
- callbacks[id] = fn
- callbacksMu.Unlock()
- return Callback{
- Value: makeCallbackHelper.Invoke(id, pendingCallbacks, jsGo),
- id: id,
- }
-}
-
-type EventCallbackFlag int
-
-const (
- // PreventDefault can be used with NewEventCallback to call event.preventDefault synchronously.
- PreventDefault EventCallbackFlag = 1 << iota
- // StopPropagation can be used with NewEventCallback to call event.stopPropagation synchronously.
- StopPropagation
- // StopImmediatePropagation can be used with NewEventCallback to call event.stopImmediatePropagation synchronously.
- StopImmediatePropagation
-)
-
-// NewEventCallback returns a wrapped callback function, just like NewCallback, but the callback expects to have
-// exactly one argument, the event. Depending on flags, it will synchronously call event.preventDefault,
-// event.stopPropagation and/or event.stopImmediatePropagation before queuing the Go function fn for execution.
-func NewEventCallback(flags EventCallbackFlag, fn func(event Value)) Callback {
- c := NewCallback(func(args []Value) {
- fn(args[0])
- })
- return Callback{
- Value: makeEventCallbackHelper.Invoke(
- flags&PreventDefault != 0,
- flags&StopPropagation != 0,
- flags&StopImmediatePropagation != 0,
- c,
- ),
- id: c.id,
- }
-}
-
-// Release frees up resources allocated for the callback.
-// The callback must not be invoked after calling Release.
-func (c Callback) Release() {
- callbacksMu.Lock()
- delete(callbacks, c.id)
- callbacksMu.Unlock()
-}
-
-var callbackLoopOnce sync.Once
-
-func callbackLoop() {
- for !jsGo.Get("_callbackShutdown").Bool() {
- sleepUntilCallback()
- for {
- cb := pendingCallbacks.Call("shift")
- if cb == Undefined() {
- break
- }
-
- id := uint32(cb.Get("id").Int())
- callbacksMu.Lock()
- f, ok := callbacks[id]
- callbacksMu.Unlock()
- if !ok {
- Global().Get("console").Call("error", "call to closed callback")
- continue
- }
-
- argsObj := cb.Get("args")
- args := make([]Value, argsObj.Length())
- for i := range args {
- args[i] = argsObj.Index(i)
- }
- f(args)
- }
- }
-}
-
-// sleepUntilCallback is defined in the runtime package
-func sleepUntilCallback()
diff --git a/libgo/go/syscall/js/func.go b/libgo/go/syscall/js/func.go
new file mode 100644
index 0000000..6b7f39b
--- /dev/null
+++ b/libgo/go/syscall/js/func.go
@@ -0,0 +1,92 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package js
+
+import "sync"
+
+var (
+ funcsMu sync.Mutex
+ funcs = make(map[uint32]func(Value, []Value) interface{})
+ nextFuncID uint32 = 1
+)
+
+var _ Wrapper = Func{} // Func must implement Wrapper
+
+// Func is a wrapped Go function to be called by JavaScript.
+type Func struct {
+ Value // the JavaScript function that invokes the Go function
+ id uint32
+}
+
+// FuncOf returns a wrapped function.
+//
+// Invoking the JavaScript function will synchronously call the Go function fn with the value of JavaScript's
+// "this" keyword and the arguments of the invocation.
+// The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.
+//
+// A wrapped function triggered during a call from Go to JavaScript gets executed on the same goroutine.
+// A wrapped function triggered by JavaScript's event loop gets executed on an extra goroutine.
+// Blocking operations in the wrapped function will block the event loop.
+// As a consequence, if one wrapped function blocks, other wrapped funcs will not be processed.
+// A blocking function should therefore explicitly start a new goroutine.
+//
+// Func.Release must be called to free up resources when the function will not be used any more.
+func FuncOf(fn func(this Value, args []Value) interface{}) Func {
+ funcsMu.Lock()
+ id := nextFuncID
+ nextFuncID++
+ funcs[id] = fn
+ funcsMu.Unlock()
+ return Func{
+ id: id,
+ Value: jsGo.Call("_makeFuncWrapper", id),
+ }
+}
+
+// Release frees up resources allocated for the function.
+// The function must not be invoked after calling Release.
+func (c Func) Release() {
+ funcsMu.Lock()
+ delete(funcs, c.id)
+ funcsMu.Unlock()
+}
+
+// setEventHandler is defined in the runtime package.
+func setEventHandler(fn func())
+
+func init() {
+ setEventHandler(handleEvent)
+}
+
+func handleEvent() {
+ cb := jsGo.Get("_pendingEvent")
+ if cb == Null() {
+ return
+ }
+ jsGo.Set("_pendingEvent", Null())
+
+ id := uint32(cb.Get("id").Int())
+ if id == 0 { // zero indicates deadlock
+ select {}
+ }
+ funcsMu.Lock()
+ f, ok := funcs[id]
+ funcsMu.Unlock()
+ if !ok {
+ Global().Get("console").Call("error", "call to released function")
+ return
+ }
+
+ this := cb.Get("this")
+ argsObj := cb.Get("args")
+ args := make([]Value, argsObj.Length())
+ for i := range args {
+ args[i] = argsObj.Index(i)
+ }
+ result := f(this, args)
+ cb.Set("result", result)
+}
diff --git a/libgo/go/syscall/js/js.go b/libgo/go/syscall/js/js.go
index 336586ca..0893db0 100644
--- a/libgo/go/syscall/js/js.go
+++ b/libgo/go/syscall/js/js.go
@@ -16,19 +16,32 @@ 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.
+// Wrapper is implemented by types that are backed by a JavaScript value.
+type Wrapper interface {
+ // JSValue returns a JavaScript value associated with an object.
+ JSValue() Value
+}
+
+// Value represents a JavaScript value. The zero value is the JavaScript value "undefined".
type Value struct {
ref ref
}
+// JSValue implements Wrapper interface.
+func (v Value) JSValue() Value {
+ return v
+}
+
func makeValue(v ref) Value {
return Value{ref: v}
}
@@ -38,6 +51,9 @@ func predefValue(id uint32) Value {
}
func floatValue(f float64) Value {
+ if f == 0 {
+ return valueZero
+ }
if f != f {
return valueNaN
}
@@ -56,8 +72,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)
@@ -90,21 +107,21 @@ func Global() Value {
// | ---------------------- | ---------------------- |
// | js.Value | [its value] |
// | js.TypedArray | typed array |
-// | js.Callback | function |
+// | js.Func | function |
// | nil | null |
// | bool | boolean |
// | integers and floats | number |
// | string | string |
// | []interface{} | new array |
// | map[string]interface{} | new object |
+//
+// Panics if x is not one of the expected types.
func ValueOf(x interface{}) Value {
switch x := x.(type) {
- case Value:
+ case Value: // should precede Wrapper to avoid a loop
return x
- case TypedArray:
- return x.Value
- case Callback:
- return x.Value
+ case Wrapper:
+ return x.JSValue()
case nil:
return valueNull
case bool:
@@ -318,13 +335,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))
}
@@ -350,6 +372,26 @@ func (v Value) Bool() bool {
}
}
+// Truthy returns the JavaScript "truthiness" of the value v. In JavaScript,
+// false, 0, "", null, undefined, and NaN are "falsy", and everything else is
+// "truthy". See https://developer.mozilla.org/en-US/docs/Glossary/Truthy.
+func (v Value) Truthy() bool {
+ switch v.Type() {
+ case TypeUndefined, TypeNull:
+ return false
+ case TypeBoolean:
+ return v.Bool()
+ case TypeNumber:
+ return v.ref != valueNaN.ref && v.ref != valueZero.ref
+ case TypeString:
+ return v.String() != ""
+ case TypeSymbol, TypeFunction, TypeObject:
+ return true
+ default:
+ panic("bad type")
+ }
+}
+
// String returns the value v converted to string according to JavaScript type conversions.
func (v Value) String() string {
str, length := valuePrepareString(v.ref)
diff --git a/libgo/go/syscall/js/js_test.go b/libgo/go/syscall/js/js_test.go
index 9cc931a..c14d2cc 100644
--- a/libgo/go/syscall/js/js_test.go
+++ b/libgo/go/syscall/js/js_test.go
@@ -4,6 +4,15 @@
// +build js,wasm
+// To run these tests:
+//
+// - Install Node
+// - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find
+// "go_js_wasm_exec").
+// - GOOS=js GOARCH=wasm go test
+//
+// See -exec in "go help test", and "go help run" for details.
+
package js_test
import (
@@ -19,10 +28,19 @@ var dummys = js.Global().Call("eval", `({
someInt: 42,
someFloat: 42.123,
someArray: [41, 42, 43],
+ someDate: new Date(),
add: function(a, b) {
return a + b;
},
+ zero: 0,
+ stringZero: "0",
NaN: NaN,
+ emptyObj: {},
+ emptyArray: [],
+ Infinity: Infinity,
+ NegInfinity: -Infinity,
+ objNumber0: new Number(0),
+ objBooleanFalse: new Boolean(false),
})`)
func TestBool(t *testing.T) {
@@ -74,6 +92,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 +258,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,51 +293,89 @@ func TestValueOf(t *testing.T) {
}
}
-func TestCallback(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 TestFuncOf(t *testing.T) {
c := make(chan struct{})
- cb := js.NewCallback(func(args []js.Value) {
+ cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if got := args[0].Int(); got != 42 {
t.Errorf("got %#v, want %#v", got, 42)
}
c <- struct{}{}
+ return nil
})
defer cb.Release()
js.Global().Call("setTimeout", cb, 0, 42)
<-c
}
-func TestEventCallback(t *testing.T) {
- for _, name := range []string{"preventDefault", "stopPropagation", "stopImmediatePropagation"} {
- c := make(chan struct{})
- var flags js.EventCallbackFlag
- switch name {
- case "preventDefault":
- flags = js.PreventDefault
- case "stopPropagation":
- flags = js.StopPropagation
- case "stopImmediatePropagation":
- flags = js.StopImmediatePropagation
- }
- cb := js.NewEventCallback(flags, func(event js.Value) {
- c <- struct{}{}
+func TestInvokeFunction(t *testing.T) {
+ called := false
+ cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ called = true
+ return 42
})
- defer cb.Release()
-
- event := js.Global().Call("eval", fmt.Sprintf("({ called: false, %s: function() { this.called = true; } })", name))
- cb.Invoke(event)
- if !event.Get("called").Bool() {
- t.Errorf("%s not called", name)
- }
-
- <-c
+ defer cb2.Release()
+ return cb2.Invoke()
+ })
+ defer cb.Release()
+ if got := cb.Invoke().Int(); got != 42 {
+ t.Errorf("got %#v, want %#v", got, 42)
+ }
+ if !called {
+ t.Error("function not called")
}
}
-func ExampleNewCallback() {
- var cb js.Callback
- cb = js.NewCallback(func(args []js.Value) {
+func ExampleFuncOf() {
+ var cb js.Func
+ cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fmt.Println("button clicked")
- cb.Release() // release the callback if the button will not be clicked again
+ cb.Release() // release the function if the button will not be clicked again
+ return nil
})
js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb)
}
+
+// See
+// - https://developer.mozilla.org/en-US/docs/Glossary/Truthy
+// - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953
+// - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
+func TestTruthy(t *testing.T) {
+ want := true
+ for _, key := range []string{
+ "someBool", "someString", "someInt", "someFloat", "someArray", "someDate",
+ "stringZero", // "0" is truthy
+ "add", // functions are truthy
+ "emptyObj", "emptyArray", "Infinity", "NegInfinity",
+ // All objects are truthy, even if they're Number(0) or Boolean(false).
+ "objNumber0", "objBooleanFalse",
+ } {
+ if got := dummys.Get(key).Truthy(); got != want {
+ t.Errorf("%s: got %#v, want %#v", key, got, want)
+ }
+ }
+
+ want = false
+ if got := dummys.Get("zero").Truthy(); got != want {
+ t.Errorf("got %#v, want %#v", got, want)
+ }
+ if got := dummys.Get("NaN").Truthy(); got != want {
+ t.Errorf("got %#v, want %#v", got, want)
+ }
+ if got := js.ValueOf("").Truthy(); got != want {
+ t.Errorf("got %#v, want %#v", got, want)
+ }
+ if got := js.Null().Truthy(); got != want {
+ t.Errorf("got %#v, want %#v", got, want)
+ }
+ if got := js.Undefined().Truthy(); got != want {
+ t.Errorf("got %#v, want %#v", got, want)
+ }
+}
diff --git a/libgo/go/syscall/js/typedarray.go b/libgo/go/syscall/js/typedarray.go
index afa1548..aa56cf6 100644
--- a/libgo/go/syscall/js/typedarray.go
+++ b/libgo/go/syscall/js/typedarray.go
@@ -22,6 +22,8 @@ var (
float64Array = Global().Get("Float64Array")
)
+var _ Wrapper = TypedArray{} // TypedArray must implement Wrapper
+
// TypedArray represents a JavaScript typed array.
type TypedArray struct {
Value