package quickjs import ( "runtime/cgo" "sync" "sync/atomic" "unsafe" ) /* #include #include "bridge.h" */ import "C" type funcEntry struct { ctx *Context fn func(ctx *Context, this Value, args []Value) Value asyncFn func(ctx *Context, this Value, promise Value, args []Value) Value } var funcPtrLen int64 var funcPtrLock sync.Mutex var funcPtrStore = make(map[int64]funcEntry) var funcPtrClassID C.JSClassID func init() { C.JS_NewClassID(&funcPtrClassID) } func storeFuncPtr(v funcEntry) int64 { id := atomic.AddInt64(&funcPtrLen, 1) - 1 if id >= 9223372036854775807 { id = 0 } funcPtrLock.Lock() defer funcPtrLock.Unlock() funcPtrStore[id] = v return id } func restoreFuncPtr(ptr int64) funcEntry { funcPtrLock.Lock() defer funcPtrLock.Unlock() return funcPtrStore[ptr] } func freeFuncPtr(ptr int64) { funcPtrLock.Lock() defer funcPtrLock.Unlock() delete(funcPtrStore, ptr) } func freeFuncPtrs(ptrs []int64) { funcPtrLock.Lock() defer funcPtrLock.Unlock() for _, ptr := range ptrs { delete(funcPtrStore, ptr) } } //export goProxy func goProxy(ctx *C.JSContext, thisVal C.JSValueConst, argc C.int, argv *C.JSValueConst) C.JSValue { // https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices refs := unsafe.Slice(argv, argc) // Go 1.17 and later id := C.int64_t(0) C.JS_ToInt64(ctx, &id, refs[0]) entry := restoreFuncPtr(int64(id)) args := make([]Value, len(refs)-1) for i := 0; i < len(args); i++ { args[i].ctx = entry.ctx args[i].ref = refs[1+i] } result := entry.fn(entry.ctx, Value{ctx: entry.ctx, ref: thisVal}, args) return result.ref } //export goAsyncProxy func goAsyncProxy(ctx *C.JSContext, thisVal C.JSValueConst, argc C.int, argv *C.JSValueConst) C.JSValue { // https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices refs := unsafe.Slice(argv, argc) // Go 1.17 and later id := C.int64_t(0) C.JS_ToInt64(ctx, &id, refs[0]) entry := restoreFuncPtr(int64(id)) args := make([]Value, len(refs)-1) for i := 0; i < len(args); i++ { args[i].ctx = entry.ctx args[i].ref = refs[1+i] } promise := args[0] result := entry.asyncFn(entry.ctx, Value{ctx: entry.ctx, ref: thisVal}, promise, args[1:]) return result.ref } //export goInterruptHandler func goInterruptHandler(rt *C.JSRuntime, handlerArgs unsafe.Pointer) C.int { handlerArgsStruct := (*C.handlerArgs)(handlerArgs) hFn := cgo.Handle(handlerArgsStruct.fn) hFnValue := hFn.Value().(InterruptHandler) // defer hFn.Delete() return C.int(hFnValue()) }