diff options
author | Ian Lance Taylor <iant@golang.org> | 2019-01-18 19:04:36 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2019-01-18 19:04:36 +0000 |
commit | 4f4a855d82a889cebcfca150a7a43909bcb6a346 (patch) | |
tree | f12bae0781920fa34669fe30b6f4615a86d9fb80 /libgo/go/syscall/js | |
parent | 225220d668dafb8262db7012bced688acbe63b33 (diff) | |
download | gcc-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.go | 122 | ||||
-rw-r--r-- | libgo/go/syscall/js/func.go | 92 | ||||
-rw-r--r-- | libgo/go/syscall/js/js.go | 64 | ||||
-rw-r--r-- | libgo/go/syscall/js/js_test.go | 120 | ||||
-rw-r--r-- | libgo/go/syscall/js/typedarray.go | 2 |
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 |