summaryrefslogtreecommitdiffstats
path: root/src/syscall/js/func.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:25:22 +0000
commitf6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch)
tree7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/syscall/js/func.go
parentInitial commit. (diff)
downloadgolang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz
golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/syscall/js/func.go')
-rw-r--r--src/syscall/js/func.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/syscall/js/func.go b/src/syscall/js/func.go
new file mode 100644
index 0000000..53a4d79
--- /dev/null
+++ b/src/syscall/js/func.go
@@ -0,0 +1,105 @@
+// 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.
+
+//go:build js && wasm
+
+package js
+
+import "sync"
+
+var (
+ funcsMu sync.Mutex
+ funcs = make(map[uint32]func(Value, []Value) any)
+ nextFuncID uint32 = 1
+)
+
+// 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 function to be used by JavaScript.
+//
+// The Go function fn is called 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.
+//
+// Invoking the wrapped Go function from JavaScript will
+// pause the event loop and spawn a new goroutine.
+// Other wrapped functions which are triggered during a call from Go to JavaScript
+// get executed on the same goroutine.
+//
+// As a consequence, if one wrapped function blocks, JavaScript's event loop
+// is blocked until that function returns. Hence, calling any async JavaScript
+// API, which requires the event loop, like fetch (http.Client), will cause an
+// immediate deadlock. Therefore a blocking function should explicitly start a
+// new goroutine.
+//
+// Func.Release must be called to free up resources when the function will not be invoked any more.
+func FuncOf(fn func(this Value, args []Value) any) 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.
+// It is allowed to call Release while the function is still running.
+func (c Func) Release() {
+ funcsMu.Lock()
+ delete(funcs, c.id)
+ funcsMu.Unlock()
+}
+
+// setEventHandler is defined in the runtime package.
+func setEventHandler(fn func() bool)
+
+func init() {
+ setEventHandler(handleEvent)
+}
+
+// handleEvent retrieves the pending event (window._pendingEvent) and calls the js.Func on it.
+// It returns true if an event was handled.
+func handleEvent() bool {
+ // Retrieve the event from js
+ cb := jsGo.Get("_pendingEvent")
+ if cb.IsNull() {
+ return false
+ }
+ jsGo.Set("_pendingEvent", Null())
+
+ id := uint32(cb.Get("id").Int())
+ if id == 0 { // zero indicates deadlock
+ select {}
+ }
+
+ // Retrieve the associated js.Func
+ funcsMu.Lock()
+ f, ok := funcs[id]
+ funcsMu.Unlock()
+ if !ok {
+ Global().Get("console").Call("error", "call to released function")
+ return true
+ }
+
+ // Call the js.Func with arguments
+ 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)
+
+ // Return the result to js
+ cb.Set("result", result)
+ return true
+}