182 lines
4.8 KiB
Go
182 lines
4.8 KiB
Go
|
package quickjs
|
||
|
|
||
|
/*
|
||
|
#include "bridge.h"
|
||
|
#include <time.h>
|
||
|
*/
|
||
|
import "C"
|
||
|
import (
|
||
|
"runtime"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
// Runtime represents a Javascript runtime corresponding to an object heap. Several runtimes can exist at the same time but they cannot exchange objects. Inside a given runtime, no multi-threading is supported.
|
||
|
type Runtime struct {
|
||
|
ref *C.JSRuntime
|
||
|
options *Options
|
||
|
}
|
||
|
|
||
|
type Options struct {
|
||
|
timeout uint64
|
||
|
memoryLimit uint64
|
||
|
gcThreshold uint64
|
||
|
maxStackSize uint64
|
||
|
canBlock bool
|
||
|
moduleImport bool
|
||
|
}
|
||
|
|
||
|
type Option func(*Options)
|
||
|
|
||
|
// WithExecuteTimeout will set the runtime's execute timeout; default is 0
|
||
|
func WithExecuteTimeout(timeout uint64) Option {
|
||
|
return func(o *Options) {
|
||
|
o.timeout = timeout
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithMemoryLimit will set the runtime memory limit; if not set, it will be unlimit.
|
||
|
func WithMemoryLimit(memoryLimit uint64) Option {
|
||
|
return func(o *Options) {
|
||
|
o.memoryLimit = memoryLimit
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithGCThreshold will set the runtime's GC threshold; use -1 to disable automatic GC.
|
||
|
func WithGCThreshold(gcThreshold uint64) Option {
|
||
|
return func(o *Options) {
|
||
|
o.gcThreshold = gcThreshold
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithMaxStackSize will set max runtime's stack size; default is 255
|
||
|
func WithMaxStackSize(maxStackSize uint64) Option {
|
||
|
return func(o *Options) {
|
||
|
o.maxStackSize = maxStackSize
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WithCanBlock will set the runtime's can block; default is true
|
||
|
func WithCanBlock(canBlock bool) Option {
|
||
|
return func(o *Options) {
|
||
|
o.canBlock = canBlock
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithModuleImport(moduleImport bool) Option {
|
||
|
return func(o *Options) {
|
||
|
o.moduleImport = moduleImport
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewRuntime creates a new quickjs runtime.
|
||
|
func NewRuntime(opts ...Option) Runtime {
|
||
|
runtime.LockOSThread() // prevent multiple quickjs runtime from being created
|
||
|
|
||
|
options := &Options{
|
||
|
timeout: 0,
|
||
|
memoryLimit: 0,
|
||
|
gcThreshold: 0,
|
||
|
maxStackSize: 0,
|
||
|
canBlock: true,
|
||
|
moduleImport: false,
|
||
|
}
|
||
|
for _, opt := range opts {
|
||
|
opt(options)
|
||
|
}
|
||
|
|
||
|
rt := Runtime{ref: C.JS_NewRuntime(), options: options}
|
||
|
|
||
|
if rt.options.timeout > 0 {
|
||
|
rt.SetExecuteTimeout(rt.options.timeout)
|
||
|
}
|
||
|
if rt.options.memoryLimit > 0 {
|
||
|
rt.SetMemoryLimit(rt.options.memoryLimit)
|
||
|
}
|
||
|
if rt.options.gcThreshold > 0 {
|
||
|
rt.SetGCThreshold(rt.options.gcThreshold)
|
||
|
}
|
||
|
if rt.options.maxStackSize > 0 {
|
||
|
rt.SetMaxStackSize(rt.options.maxStackSize)
|
||
|
}
|
||
|
if rt.options.canBlock {
|
||
|
C.JS_SetCanBlock(rt.ref, C.int(1))
|
||
|
}
|
||
|
return rt
|
||
|
}
|
||
|
|
||
|
// RunGC will call quickjs's garbage collector.
|
||
|
func (r Runtime) RunGC() {
|
||
|
C.JS_RunGC(r.ref)
|
||
|
}
|
||
|
|
||
|
// Close will free the runtime pointer.
|
||
|
func (r Runtime) Close() {
|
||
|
C.JS_FreeRuntime(r.ref)
|
||
|
}
|
||
|
|
||
|
// SetCanBlock will set the runtime's can block; default is true
|
||
|
func (r Runtime) SetCanBlock(canBlock bool) {
|
||
|
if canBlock {
|
||
|
C.JS_SetCanBlock(r.ref, C.int(1))
|
||
|
} else {
|
||
|
C.JS_SetCanBlock(r.ref, C.int(0))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SetMemoryLimit the runtime memory limit; if not set, it will be unlimit.
|
||
|
func (r Runtime) SetMemoryLimit(limit uint64) {
|
||
|
C.JS_SetMemoryLimit(r.ref, C.size_t(limit))
|
||
|
}
|
||
|
|
||
|
// SetGCThreshold the runtime's GC threshold; use -1 to disable automatic GC.
|
||
|
func (r Runtime) SetGCThreshold(threshold uint64) {
|
||
|
C.JS_SetGCThreshold(r.ref, C.size_t(threshold))
|
||
|
}
|
||
|
|
||
|
// SetMaxStackSize will set max runtime's stack size; default is 255
|
||
|
func (r Runtime) SetMaxStackSize(stack_size uint64) {
|
||
|
C.JS_SetMaxStackSize(r.ref, C.size_t(stack_size))
|
||
|
}
|
||
|
|
||
|
// SetExecuteTimeout will set the runtime's execute timeout; default is 0
|
||
|
func (r Runtime) SetExecuteTimeout(timeout uint64) {
|
||
|
C.SetExecuteTimeout(r.ref, C.time_t(timeout))
|
||
|
}
|
||
|
|
||
|
// NewContext creates a new JavaScript context.
|
||
|
// enable BigFloat/BigDecimal support and enable .
|
||
|
// enable operator overloading.
|
||
|
func (r Runtime) NewContext() *Context {
|
||
|
C.js_std_init_handlers(r.ref)
|
||
|
|
||
|
// create a new context (heap, global object and context stack
|
||
|
ctx_ref := C.JS_NewContext(r.ref)
|
||
|
|
||
|
C.JS_AddIntrinsicBigFloat(ctx_ref)
|
||
|
C.JS_AddIntrinsicBigDecimal(ctx_ref)
|
||
|
C.JS_AddIntrinsicOperators(ctx_ref)
|
||
|
C.JS_EnableBignumExt(ctx_ref, C.int(1))
|
||
|
|
||
|
// set the module loader for support dynamic import
|
||
|
if r.options.moduleImport {
|
||
|
C.JS_SetModuleLoaderFunc(r.ref, (*C.JSModuleNormalizeFunc)(unsafe.Pointer(nil)), (*C.JSModuleLoaderFunc)(C.js_module_loader), unsafe.Pointer(nil))
|
||
|
}
|
||
|
|
||
|
// import the 'std' and 'os' modules
|
||
|
C.js_init_module_std(ctx_ref, C.CString("std"))
|
||
|
C.js_init_module_os(ctx_ref, C.CString("os"))
|
||
|
|
||
|
// import setTimeout and clearTimeout from 'os' to globalThis
|
||
|
code := `
|
||
|
import { setTimeout, clearTimeout } from "os";
|
||
|
globalThis.setTimeout = setTimeout;
|
||
|
globalThis.clearTimeout = clearTimeout;
|
||
|
`
|
||
|
init_compile := C.JS_Eval(ctx_ref, C.CString(code), C.size_t(len(code)), C.CString("init.js"), C.JS_EVAL_TYPE_MODULE|C.JS_EVAL_FLAG_COMPILE_ONLY)
|
||
|
init_run := C.js_std_await(ctx_ref, C.JS_EvalFunction(ctx_ref, init_compile))
|
||
|
C.JS_FreeValue(ctx_ref, init_run)
|
||
|
// C.js_std_loop(ctx_ref)
|
||
|
|
||
|
return &Context{ref: ctx_ref, runtime: &r}
|
||
|
}
|