Repository navigation
Expand file tree
/
Copy pathjsfunc_jsc.go
More file actions
95 lines (87 loc) · 2.26 KB
/
Copy pathjsfunc_jsc.go
File metadata and controls
95 lines (87 loc) · 2.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//go:build !goja && !qjswasm
package ramune
import (
"fmt"
"sync/atomic"
"unsafe"
)
// JSFunc wraps a JavaScript function reference, allowing it to be called from Go.
// Created automatically when a JS function is passed as an argument to a GoFunc callback.
// Call Close() when done to release the JS reference.
type JSFunc struct {
noCopy noCopy
rt *Runtime
ptr uintptr // JSObjectRef (protected)
closed atomic.Bool
}
func (r *Runtime) newJSFunc(ptr uintptr) *JSFunc {
r.jsValueProtect(r.ctx, ptr)
r.unprotectMu.Lock()
if r.protectedPtrs == nil {
r.protectedPtrs = make(map[uintptr]int)
}
r.protectedPtrs[ptr]++
r.unprotectMu.Unlock()
return &JSFunc{rt: r, ptr: ptr}
}
// Call invokes the JavaScript function with the given arguments.
// Returns the result converted to a Go value (bool, float64, string, nil,
// map[string]any, or []any).
// Safe to call from any goroutine and from within GoFunc callbacks.
func (f *JSFunc) Call(args ...any) (any, error) {
if f == nil || f.closed.Load() {
return nil, ErrNilValue
}
var result any
var err error
f.rt.dispatch(func() {
jsArgs := make([]uintptr, len(args))
for i, arg := range args {
jsVal, e := f.rt.goToJS(arg)
if e != nil {
err = fmt.Errorf("ramune: JSFunc.Call arg %d: %w", i, e)
return
}
jsArgs[i] = jsVal
}
var exc uintptr
ret := f.rt.jsObjectCallAsFunction(
f.rt.ctx, f.ptr, 0,
uint64(len(jsArgs)), jsArgs,
uintptr(unsafe.Pointer(&exc)),
)
if ret == 0 {
if exc != 0 {
msg, stack := f.rt.getExceptionInfo(exc)
err = &JSError{Context: "JSFunc.Call", Message: msg, Stack: stack}
return
}
err = &JSError{Context: "JSFunc.Call", Message: "JavaScript exception occurred"}
return
}
result = f.rt.jsToGoCallback(ret)
})
return result, err
}
// Close releases the JavaScript function reference.
// Safe to call multiple times or on nil.
func (f *JSFunc) Close() error {
if f == nil || f.closed.Swap(true) {
return nil
}
if f.rt.closed.Load() {
f.ptr = 0
return nil
}
f.rt.unprotectMu.Lock()
f.rt.unprotectQueue = append(f.rt.unprotectQueue, f.ptr)
if f.rt.protectedPtrs != nil {
f.rt.protectedPtrs[f.ptr]--
if f.rt.protectedPtrs[f.ptr] <= 0 {
delete(f.rt.protectedPtrs, f.ptr)
}
}
f.rt.unprotectMu.Unlock()
f.ptr = 0
return nil
}