quickjs 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/ext/quickjsrb/extconf.rb +0 -1
- data/ext/quickjsrb/quickjsrb.c +61 -19
- data/ext/quickjsrb/quickjsrb.h +1 -2
- data/lib/quickjs/version.rb +1 -1
- metadata +2 -4
- data/ext/quickjsrb/procs.c +0 -80
- data/ext/quickjsrb/procs.h +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0461f98dbe3171e88609b060aa9fb414ba881b0977d983d6ec7897726a0559c
|
4
|
+
data.tar.gz: 84ed4cf19c1d5191e9cd57b789cc3138691f7ad2059b515a2ce4280051105c46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c98d7daf116e5513a19b99e4ef387f3b1b60389c3bb9f91822cc1d437bf87cc72041cd97c7cdf2a8a8cb55a86df93a3cf7fe472733017ae4a315831397a56cd5
|
7
|
+
data.tar.gz: f177d0e31b8d6499fcf922bd85dc594aa2acd0bf8d93d696256c4a3a7a1595eeb874df7e14e8447694a3045b3f2b9d8279633550e535fc8f257311c613c043b6
|
data/README.md
CHANGED
@@ -89,6 +89,11 @@ vm = Quickjs::VM.new(
|
|
89
89
|
vm = Quickjs::VM.new(
|
90
90
|
features: [::Quickjs::MODULE_OS],
|
91
91
|
)
|
92
|
+
|
93
|
+
# `eval_code` will be interrupted after 1 sec (default: 100 msec)
|
94
|
+
vm = Quickjs::VM.new(
|
95
|
+
timeout_msec: 1_000,
|
96
|
+
)
|
92
97
|
```
|
93
98
|
|
94
99
|
#### Dispose VM explicitly
|
data/ext/quickjsrb/extconf.rb
CHANGED
data/ext/quickjsrb/quickjsrb.c
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
#include "quickjsrb.h"
|
2
2
|
|
3
|
+
typedef struct EvalTime {
|
4
|
+
clock_t limit;
|
5
|
+
clock_t started_at;
|
6
|
+
} EvalTime;
|
7
|
+
|
3
8
|
typedef struct VMData {
|
4
9
|
char alive;
|
5
10
|
struct JSContext *context;
|
6
|
-
|
7
|
-
struct
|
11
|
+
VALUE defined_functions;
|
12
|
+
struct EvalTime *eval_time;
|
8
13
|
} VMData;
|
9
14
|
|
10
|
-
void vm_free(void*
|
15
|
+
static void vm_free(void* ptr)
|
11
16
|
{
|
12
|
-
|
17
|
+
VMData *data = (VMData *)ptr;
|
18
|
+
free(data->eval_time);
|
19
|
+
xfree(ptr);
|
13
20
|
}
|
14
21
|
|
15
22
|
size_t vm_size(const void* data)
|
@@ -17,10 +24,16 @@ size_t vm_size(const void* data)
|
|
17
24
|
return sizeof(data);
|
18
25
|
}
|
19
26
|
|
27
|
+
static void vm_mark(void *ptr)
|
28
|
+
{
|
29
|
+
VMData *data = (VMData *)ptr;
|
30
|
+
rb_gc_mark_movable(data->defined_functions);
|
31
|
+
}
|
32
|
+
|
20
33
|
static const rb_data_type_t vm_type = {
|
21
|
-
.wrap_struct_name = "
|
34
|
+
.wrap_struct_name = "quickjsvm",
|
22
35
|
.function = {
|
23
|
-
.dmark =
|
36
|
+
.dmark = vm_mark,
|
24
37
|
.dfree = vm_free,
|
25
38
|
.dsize = vm_size,
|
26
39
|
},
|
@@ -28,10 +41,16 @@ static const rb_data_type_t vm_type = {
|
|
28
41
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
29
42
|
};
|
30
43
|
|
31
|
-
VALUE vm_alloc(VALUE self)
|
44
|
+
static VALUE vm_alloc(VALUE self)
|
32
45
|
{
|
33
46
|
VMData *data;
|
34
|
-
|
47
|
+
VALUE obj = TypedData_Make_Struct(self, VMData, &vm_type, data);
|
48
|
+
data->defined_functions = rb_hash_new();
|
49
|
+
|
50
|
+
EvalTime *eval_time = malloc(sizeof(EvalTime));
|
51
|
+
data->eval_time = eval_time;
|
52
|
+
|
53
|
+
return obj;
|
35
54
|
}
|
36
55
|
|
37
56
|
VALUE rb_mQuickjs;
|
@@ -55,6 +74,7 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value) {
|
|
55
74
|
JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
|
56
75
|
JS_FreeValue(ctx, global);
|
57
76
|
JS_FreeValue(ctx, numberClass);
|
77
|
+
JS_FreeValue(ctx, j_str);
|
58
78
|
|
59
79
|
return stringified;
|
60
80
|
}
|
@@ -83,8 +103,9 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value) {
|
|
83
103
|
JSValue j_str = JS_NewString(ctx, str);
|
84
104
|
JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
|
85
105
|
JS_FreeValue(ctx, global);
|
86
|
-
JS_FreeValue(ctx, parseFunc);
|
87
106
|
JS_FreeValue(ctx, jsonClass);
|
107
|
+
JS_FreeValue(ctx, parseFunc);
|
108
|
+
JS_FreeValue(ctx, j_str);
|
88
109
|
|
89
110
|
return stringified;
|
90
111
|
}
|
@@ -118,14 +139,23 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
|
|
118
139
|
case JS_TAG_STRING: {
|
119
140
|
JSValue maybeString = JS_ToString(ctx, jsv);
|
120
141
|
const char *msg = JS_ToCString(ctx, maybeString);
|
142
|
+
JS_FreeValue(ctx, maybeString);
|
121
143
|
return rb_str_new2(msg);
|
122
144
|
}
|
123
145
|
case JS_TAG_OBJECT: {
|
124
146
|
int promiseState = JS_PromiseState(ctx, jsv);
|
125
147
|
if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
|
126
|
-
|
148
|
+
JSValue awaited = js_std_await(ctx, jsv);
|
149
|
+
VALUE rb_awaited = to_rb_value(awaited, ctx); // TODO: should have timeout
|
150
|
+
JS_FreeValue(ctx, awaited);
|
151
|
+
return rb_awaited;
|
127
152
|
} else if (promiseState == JS_PROMISE_REJECTED) {
|
128
|
-
|
153
|
+
JSValue promiseResult = JS_PromiseResult(ctx, jsv);
|
154
|
+
JSValue throw = JS_Throw(ctx, promiseResult);
|
155
|
+
JS_FreeValue(ctx, promiseResult);
|
156
|
+
VALUE rb_errored = to_rb_value(throw, ctx);
|
157
|
+
JS_FreeValue(ctx, throw);
|
158
|
+
return rb_errored;
|
129
159
|
}
|
130
160
|
|
131
161
|
JSValue global = JS_GetGlobalObject(ctx);
|
@@ -194,16 +224,17 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
|
|
194
224
|
const char *funcName = JS_ToCString(ctx, maybeFuncName);
|
195
225
|
JS_FreeValue(ctx, maybeFuncName);
|
196
226
|
|
197
|
-
VALUE proc =
|
227
|
+
VALUE proc = rb_hash_aref(data->defined_functions, rb_str_new2(funcName));
|
198
228
|
if (proc == Qnil) { // Shouldn't happen
|
199
229
|
return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
|
200
230
|
}
|
201
231
|
|
232
|
+
// TODO: cover timeout for calling proc
|
202
233
|
VALUE r_result = rb_apply(proc, rb_intern("call"), to_rb_value(argv[1], ctx));
|
203
234
|
return to_js_value(ctx, r_result);
|
204
235
|
}
|
205
236
|
|
206
|
-
VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
|
237
|
+
static VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
|
207
238
|
{
|
208
239
|
VALUE r_opts;
|
209
240
|
rb_scan_args(argc, argv, ":", &r_opts);
|
@@ -215,13 +246,15 @@ VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
|
|
215
246
|
if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
|
216
247
|
VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
|
217
248
|
if (NIL_P(r_features)) r_features = rb_ary_new();
|
249
|
+
VALUE r_timeout_msec = rb_hash_aref(r_opts, ID2SYM(rb_intern("timeout_msec")));
|
250
|
+
if (NIL_P(r_timeout_msec)) r_timeout_msec= UINT2NUM(100);
|
218
251
|
|
219
252
|
VMData *data;
|
220
253
|
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
221
254
|
|
222
255
|
JSRuntime *runtime = JS_NewRuntime();
|
223
256
|
data->context = JS_NewContext(runtime);
|
224
|
-
data->
|
257
|
+
data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
|
225
258
|
data->alive = 1;
|
226
259
|
JS_SetContextOpaque(data->context, data);
|
227
260
|
|
@@ -265,7 +298,12 @@ VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
|
|
265
298
|
return self;
|
266
299
|
}
|
267
300
|
|
268
|
-
|
301
|
+
static int interrupt_handler(JSRuntime *runtime, void *opaque) {
|
302
|
+
EvalTime *eval_time = opaque;
|
303
|
+
return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
|
304
|
+
}
|
305
|
+
|
306
|
+
static VALUE vm_m_evalCode(VALUE self, VALUE r_code)
|
269
307
|
{
|
270
308
|
VMData *data;
|
271
309
|
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
@@ -274,6 +312,10 @@ VALUE vm_m_evalCode(VALUE self, VALUE r_code)
|
|
274
312
|
rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
|
275
313
|
return Qnil;
|
276
314
|
}
|
315
|
+
|
316
|
+
data->eval_time->started_at = clock();
|
317
|
+
JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
|
318
|
+
|
277
319
|
char *code = StringValueCStr(r_code);
|
278
320
|
JSValue codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
|
279
321
|
VALUE result = to_rb_value(codeResult, data->context);
|
@@ -282,7 +324,7 @@ VALUE vm_m_evalCode(VALUE self, VALUE r_code)
|
|
282
324
|
return result;
|
283
325
|
}
|
284
326
|
|
285
|
-
VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
327
|
+
static VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
286
328
|
{
|
287
329
|
rb_need_block();
|
288
330
|
|
@@ -294,7 +336,7 @@ VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
|
294
336
|
|
295
337
|
char *funcName = StringValueCStr(r_name);
|
296
338
|
|
297
|
-
|
339
|
+
rb_hash_aset(data->defined_functions, r_name, proc);
|
298
340
|
|
299
341
|
const char* template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
|
300
342
|
int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
|
@@ -311,14 +353,14 @@ VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
|
311
353
|
return Qnil;
|
312
354
|
}
|
313
355
|
|
314
|
-
VALUE vm_m_dispose(VALUE self)
|
356
|
+
static VALUE vm_m_dispose(VALUE self)
|
315
357
|
{
|
316
358
|
VMData *data;
|
317
359
|
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
318
360
|
|
319
361
|
JSRuntime *runtime = JS_GetRuntime(data->context);
|
362
|
+
JS_SetInterruptHandler(runtime, NULL, NULL);
|
320
363
|
js_std_free_handlers(runtime);
|
321
|
-
free_proc_entry_map(data->procs);
|
322
364
|
JS_FreeContext(data->context);
|
323
365
|
JS_FreeRuntime(runtime);
|
324
366
|
data->alive = 0;
|
data/ext/quickjsrb/quickjsrb.h
CHANGED
data/lib/quickjs/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quickjs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hmsk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -37,8 +37,6 @@ files:
|
|
37
37
|
- README.md
|
38
38
|
- Rakefile
|
39
39
|
- ext/quickjsrb/extconf.rb
|
40
|
-
- ext/quickjsrb/procs.c
|
41
|
-
- ext/quickjsrb/procs.h
|
42
40
|
- ext/quickjsrb/quickjs/LICENSE
|
43
41
|
- ext/quickjsrb/quickjs/cutils.c
|
44
42
|
- ext/quickjsrb/quickjs/cutils.h
|
data/ext/quickjsrb/procs.c
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
#include "procs.h"
|
2
|
-
|
3
|
-
unsigned int hash(const char *key) {
|
4
|
-
unsigned long int value = 0;
|
5
|
-
unsigned int i = 0;
|
6
|
-
unsigned int key_len = strlen(key);
|
7
|
-
|
8
|
-
for (; i < key_len; ++i) {
|
9
|
-
value = value * 37 + key[i];
|
10
|
-
}
|
11
|
-
return value % MAX_NUM_OF_PROCS;
|
12
|
-
}
|
13
|
-
|
14
|
-
ProcEntry *create_proc_entry(const char *key, VALUE proc) {
|
15
|
-
ProcEntry *entry = malloc(sizeof(ProcEntry));
|
16
|
-
entry->key = strdup(key);
|
17
|
-
entry->proc = proc;
|
18
|
-
entry->next = NULL;
|
19
|
-
return entry;
|
20
|
-
}
|
21
|
-
|
22
|
-
ProcEntryMap *create_proc_entries() {
|
23
|
-
ProcEntryMap *entryMap = malloc(sizeof(ProcEntryMap));
|
24
|
-
entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROCS);
|
25
|
-
for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
|
26
|
-
entryMap->entries[i] = NULL;
|
27
|
-
}
|
28
|
-
return entryMap;
|
29
|
-
}
|
30
|
-
|
31
|
-
void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc) {
|
32
|
-
unsigned int slot = hash(key);
|
33
|
-
ProcEntry *entry = entryMap->entries[slot];
|
34
|
-
|
35
|
-
if (entry == NULL) {
|
36
|
-
entryMap->entries[slot] = create_proc_entry(key, proc);
|
37
|
-
return;
|
38
|
-
}
|
39
|
-
|
40
|
-
ProcEntry *prev;
|
41
|
-
while (entry != NULL) {
|
42
|
-
if (strcmp(entry->key, key) == 0) {
|
43
|
-
entry->proc = proc;
|
44
|
-
return;
|
45
|
-
}
|
46
|
-
prev = entry;
|
47
|
-
entry = prev->next;
|
48
|
-
}
|
49
|
-
|
50
|
-
prev->next = create_proc_entry(key, proc);
|
51
|
-
}
|
52
|
-
|
53
|
-
VALUE get_proc(ProcEntryMap *entryMap, const char *key) {
|
54
|
-
unsigned int slot = hash(key);
|
55
|
-
|
56
|
-
ProcEntry *entry = entryMap->entries[slot];
|
57
|
-
|
58
|
-
while (entry != NULL) {
|
59
|
-
if (strcmp(entry->key, key) == 0) {
|
60
|
-
return entry->proc;
|
61
|
-
}
|
62
|
-
entry = entry->next;
|
63
|
-
}
|
64
|
-
|
65
|
-
return Qnil;
|
66
|
-
}
|
67
|
-
|
68
|
-
void free_proc_entry_map(ProcEntryMap *entryMap) {
|
69
|
-
for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
|
70
|
-
ProcEntry *entry = entryMap->entries[i];
|
71
|
-
while (entry != NULL) {
|
72
|
-
ProcEntry *temp = entry;
|
73
|
-
entry = entry->next;
|
74
|
-
free(temp->key);
|
75
|
-
free(temp);
|
76
|
-
}
|
77
|
-
}
|
78
|
-
free(entryMap->entries);
|
79
|
-
free(entryMap);
|
80
|
-
}
|
data/ext/quickjsrb/procs.h
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#include "ruby.h"
|
2
|
-
|
3
|
-
#define MAX_NUM_OF_PROCS 100
|
4
|
-
|
5
|
-
typedef struct ProcEntry {
|
6
|
-
char *key;
|
7
|
-
VALUE proc;
|
8
|
-
struct ProcEntry *next;
|
9
|
-
} ProcEntry;
|
10
|
-
|
11
|
-
typedef struct ProcEntryMap {
|
12
|
-
ProcEntry **entries;
|
13
|
-
} ProcEntryMap;
|
14
|
-
|
15
|
-
ProcEntryMap *create_proc_entries();
|
16
|
-
void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc);
|
17
|
-
VALUE get_proc(ProcEntryMap *entryMap, const char *key);
|
18
|
-
void free_proc_entry_map(ProcEntryMap *entryMap);
|