-
Notifications
You must be signed in to change notification settings - Fork 9
/
runtime.go
181 lines (153 loc) · 4.78 KB
/
runtime.go
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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}
}