]> Cypherpunks.ru repositories - gostls13.git/blob - src/syscall/js/func.go
cc9497236450bb829f90dce982b577eef9b7fd54
[gostls13.git] / src / syscall / js / func.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build js && wasm
6
7 package js
8
9 import "sync"
10
11 var (
12         funcsMu    sync.Mutex
13         funcs             = make(map[uint32]func(Value, []Value) any)
14         nextFuncID uint32 = 1
15 )
16
17 // Func is a wrapped Go function to be called by JavaScript.
18 type Func struct {
19         Value // the JavaScript function that invokes the Go function
20         id    uint32
21 }
22
23 // FuncOf returns a function to be used by JavaScript.
24 //
25 // The Go function fn is called with the value of JavaScript's "this" keyword and the
26 // arguments of the invocation. The return value of the invocation is
27 // the result of the Go function mapped back to JavaScript according to ValueOf.
28 //
29 // Invoking the wrapped Go function from JavaScript will
30 // pause the event loop and spawn a new goroutine.
31 // Other wrapped functions which are triggered during a call from Go to JavaScript
32 // get executed on the same goroutine.
33 //
34 // As a consequence, if one wrapped function blocks, JavaScript's event loop
35 // is blocked until that function returns. Hence, calling any async JavaScript
36 // API, which requires the event loop, like fetch (http.Client), will cause an
37 // immediate deadlock. Therefore a blocking function should explicitly start a
38 // new goroutine.
39 //
40 // Func.Release must be called to free up resources when the function will not be invoked any more.
41 func FuncOf(fn func(this Value, args []Value) any) Func {
42         funcsMu.Lock()
43         id := nextFuncID
44         nextFuncID++
45         funcs[id] = fn
46         funcsMu.Unlock()
47         return Func{
48                 id:    id,
49                 Value: jsGo.Call("_makeFuncWrapper", id),
50         }
51 }
52
53 // Release frees up resources allocated for the function.
54 // The function must not be invoked after calling Release.
55 // It is allowed to call Release while the function is still running.
56 func (c Func) Release() {
57         funcsMu.Lock()
58         delete(funcs, c.id)
59         funcsMu.Unlock()
60 }
61
62 // setEventHandler is defined in the runtime package.
63 func setEventHandler(fn func())
64
65 func init() {
66         setEventHandler(handleEvent)
67 }
68
69 func handleEvent() {
70         cb := jsGo.Get("_pendingEvent")
71         if cb.IsNull() {
72                 return
73         }
74         jsGo.Set("_pendingEvent", Null())
75
76         id := uint32(cb.Get("id").Int())
77         if id == 0 { // zero indicates deadlock
78                 select {}
79         }
80         funcsMu.Lock()
81         f, ok := funcs[id]
82         funcsMu.Unlock()
83         if !ok {
84                 Global().Get("console").Call("error", "call to released function")
85                 return
86         }
87
88         this := cb.Get("this")
89         argsObj := cb.Get("args")
90         args := make([]Value, argsObj.Length())
91         for i := range args {
92                 args[i] = argsObj.Index(i)
93         }
94         result := f(this, args)
95         cb.Set("result", result)
96 }