quickjs 0.1.6 → 0.1.8
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 +1 -1
- data/ext/quickjsrb/{quickjsrb-runtime-state.c → procs.c} +5 -17
- data/ext/quickjsrb/{quickjsrb-runtime-state.h → procs.h} +4 -8
- data/ext/quickjsrb/quickjsrb.c +127 -63
- data/ext/quickjsrb/quickjsrb.h +3 -0
- data/lib/quickjs/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 558b76a18b29f977c09990bec267141fb9feae1f04ddd4265971c7c24e823b9b
|
4
|
+
data.tar.gz: b2dbe1a4a121e92e7f1792690fe755ce021091a4ff402a773b9853c885256dcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 338bc63e7590c7043cf86c7045f3e93c8269110d3daf49be87ef6477cd99086700a6c0a26dcdc8cf7eb2ce43acc025c419812eeb23ab0b155b59ddd335454db5
|
7
|
+
data.tar.gz: cb075876cdc10e081b95c40882acbe7ed272dacca56050564888a8df5a68c058c31fa61d9151bddf794d6846c1f00052ae74e3d439c493761bf47154f6fcfdc2
|
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
@@ -1,4 +1,4 @@
|
|
1
|
-
#include "
|
1
|
+
#include "procs.h"
|
2
2
|
|
3
3
|
unsigned int hash(const char *key) {
|
4
4
|
unsigned long int value = 0;
|
@@ -8,7 +8,7 @@ unsigned int hash(const char *key) {
|
|
8
8
|
for (; i < key_len; ++i) {
|
9
9
|
value = value * 37 + key[i];
|
10
10
|
}
|
11
|
-
return value %
|
11
|
+
return value % MAX_NUM_OF_PROCS;
|
12
12
|
}
|
13
13
|
|
14
14
|
ProcEntry *create_proc_entry(const char *key, VALUE proc) {
|
@@ -21,8 +21,8 @@ ProcEntry *create_proc_entry(const char *key, VALUE proc) {
|
|
21
21
|
|
22
22
|
ProcEntryMap *create_proc_entries() {
|
23
23
|
ProcEntryMap *entryMap = malloc(sizeof(ProcEntryMap));
|
24
|
-
entryMap->entries = malloc(sizeof(ProcEntry *) *
|
25
|
-
for (int i = 0; i <
|
24
|
+
entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROCS);
|
25
|
+
for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
|
26
26
|
entryMap->entries[i] = NULL;
|
27
27
|
}
|
28
28
|
return entryMap;
|
@@ -66,7 +66,7 @@ VALUE get_proc(ProcEntryMap *entryMap, const char *key) {
|
|
66
66
|
}
|
67
67
|
|
68
68
|
void free_proc_entry_map(ProcEntryMap *entryMap) {
|
69
|
-
for (int i = 0; i <
|
69
|
+
for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
|
70
70
|
ProcEntry *entry = entryMap->entries[i];
|
71
71
|
while (entry != NULL) {
|
72
72
|
ProcEntry *temp = entry;
|
@@ -78,15 +78,3 @@ void free_proc_entry_map(ProcEntryMap *entryMap) {
|
|
78
78
|
free(entryMap->entries);
|
79
79
|
free(entryMap);
|
80
80
|
}
|
81
|
-
|
82
|
-
QuickjsrbRuntimeState *create_quickjsrb_runtime_state() {
|
83
|
-
QuickjsrbRuntimeState *state = malloc(sizeof(QuickjsrbRuntimeState));
|
84
|
-
state->procs = create_proc_entries();
|
85
|
-
|
86
|
-
return state;
|
87
|
-
}
|
88
|
-
|
89
|
-
void free_quickjsrb_runtime_state(QuickjsrbRuntimeState *state) {
|
90
|
-
free_proc_entry_map(state->procs);
|
91
|
-
free(state);
|
92
|
-
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#include "ruby.h"
|
2
2
|
|
3
|
-
#define
|
3
|
+
#define MAX_NUM_OF_PROCS 100
|
4
4
|
|
5
5
|
typedef struct ProcEntry {
|
6
6
|
char *key;
|
@@ -12,11 +12,7 @@ typedef struct ProcEntryMap {
|
|
12
12
|
ProcEntry **entries;
|
13
13
|
} ProcEntryMap;
|
14
14
|
|
15
|
-
|
16
|
-
ProcEntryMap *procs;
|
17
|
-
} QuickjsrbRuntimeState;
|
18
|
-
|
19
|
-
QuickjsrbRuntimeState *create_quickjsrb_runtime_state();
|
20
|
-
VALUE get_proc(ProcEntryMap *entryMap, const char *key);
|
15
|
+
ProcEntryMap *create_proc_entries();
|
21
16
|
void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc);
|
22
|
-
|
17
|
+
VALUE get_proc(ProcEntryMap *entryMap, const char *key);
|
18
|
+
void free_proc_entry_map(ProcEntryMap *entryMap);
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
@@ -1,64 +1,42 @@
|
|
1
1
|
#include "quickjsrb.h"
|
2
|
-
#include "quickjsrb-runtime-state.h"
|
3
2
|
|
4
|
-
struct
|
3
|
+
typedef struct EvalTime {
|
4
|
+
clock_t limit;
|
5
|
+
clock_t started_at;
|
6
|
+
} EvalTime;
|
7
|
+
|
8
|
+
typedef struct VMData {
|
5
9
|
char alive;
|
6
10
|
struct JSContext *context;
|
7
|
-
struct
|
8
|
-
|
11
|
+
struct ProcEntryMap *procs;
|
12
|
+
struct EvalTime *eval_time;
|
13
|
+
} VMData;
|
9
14
|
|
10
|
-
void
|
15
|
+
void vm_free(void* data)
|
11
16
|
{
|
12
17
|
free(data);
|
13
18
|
}
|
14
19
|
|
15
|
-
size_t
|
20
|
+
size_t vm_size(const void* data)
|
16
21
|
{
|
17
22
|
return sizeof(data);
|
18
23
|
}
|
19
24
|
|
20
|
-
static const rb_data_type_t
|
21
|
-
.wrap_struct_name = "
|
25
|
+
static const rb_data_type_t vm_type = {
|
26
|
+
.wrap_struct_name = "vm",
|
22
27
|
.function = {
|
23
28
|
.dmark = NULL,
|
24
|
-
.dfree =
|
25
|
-
.dsize =
|
29
|
+
.dfree = vm_free,
|
30
|
+
.dsize = vm_size,
|
26
31
|
},
|
27
32
|
.data = NULL,
|
28
33
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
29
34
|
};
|
30
35
|
|
31
|
-
VALUE
|
36
|
+
VALUE vm_alloc(VALUE self)
|
32
37
|
{
|
33
|
-
|
34
|
-
|
35
|
-
return TypedData_Make_Struct(self, struct qvmdata, &qvm_type, data);
|
36
|
-
}
|
37
|
-
|
38
|
-
static JSValue js_quickjsrb_call_global (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
39
|
-
struct qvmdata *data = JS_GetContextOpaque(ctx);
|
40
|
-
JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
|
41
|
-
const char *funcName = JS_ToCString(ctx, maybeFuncName);
|
42
|
-
|
43
|
-
VALUE proc = get_proc(data->state->procs, funcName);
|
44
|
-
if (proc == Qnil) {
|
45
|
-
return JS_NewString(ctx, "missing");
|
46
|
-
}
|
47
|
-
JS_FreeValue(ctx, maybeFuncName);
|
48
|
-
|
49
|
-
// TODO: support multiple args
|
50
|
-
JSValue maybeString;
|
51
|
-
VALUE r_array = rb_ary_new2(argc);
|
52
|
-
for (int i = 1; i < argc; ++i) {
|
53
|
-
maybeString = JS_ToString(ctx, argv[i]);
|
54
|
-
const char *msg = JS_ToCString(ctx, maybeString);
|
55
|
-
rb_ary_push(r_array, rb_str_new2(msg));
|
56
|
-
}
|
57
|
-
JS_FreeValue(ctx, maybeString);
|
58
|
-
|
59
|
-
VALUE r_result = rb_funcall(proc, rb_intern("call"), argc - 1, r_array);
|
60
|
-
char *result = StringValueCStr(r_result);
|
61
|
-
return JS_NewString(ctx, result);
|
38
|
+
VMData *data;
|
39
|
+
return TypedData_Make_Struct(self, VMData, &vm_type, data);
|
62
40
|
}
|
63
41
|
|
64
42
|
VALUE rb_mQuickjs;
|
@@ -68,7 +46,63 @@ const char *nanId = "NaN";
|
|
68
46
|
const char *featureStdId = "feature_std";
|
69
47
|
const char *featureOsId = "feature_os";
|
70
48
|
|
71
|
-
|
49
|
+
JSValue to_js_value(JSContext *ctx, VALUE r_value) {
|
50
|
+
switch (TYPE(r_value)) {
|
51
|
+
case T_NIL:
|
52
|
+
return JS_NULL;
|
53
|
+
case T_FIXNUM:
|
54
|
+
case T_FLOAT: {
|
55
|
+
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
|
56
|
+
char *str = StringValueCStr(r_str);
|
57
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
58
|
+
JSValue numberClass = JS_GetPropertyStr(ctx, global, "Number");
|
59
|
+
JSValue j_str = JS_NewString(ctx, str);
|
60
|
+
JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
|
61
|
+
JS_FreeValue(ctx, global);
|
62
|
+
JS_FreeValue(ctx, numberClass);
|
63
|
+
|
64
|
+
return stringified;
|
65
|
+
}
|
66
|
+
case T_STRING: {
|
67
|
+
char *str = StringValueCStr(r_value);
|
68
|
+
|
69
|
+
return JS_NewString(ctx, str);
|
70
|
+
}
|
71
|
+
case T_SYMBOL: {
|
72
|
+
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
|
73
|
+
char *str = StringValueCStr(r_str);
|
74
|
+
|
75
|
+
return JS_NewString(ctx, str);
|
76
|
+
}
|
77
|
+
case T_TRUE:
|
78
|
+
return JS_TRUE;
|
79
|
+
case T_FALSE:
|
80
|
+
return JS_FALSE;
|
81
|
+
case T_HASH:
|
82
|
+
case T_ARRAY: {
|
83
|
+
VALUE r_json_str = rb_funcall(r_value, rb_intern("to_json"), 0, NULL);
|
84
|
+
char *str = StringValueCStr(r_json_str);
|
85
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
86
|
+
JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
|
87
|
+
JSValue parseFunc = JS_GetPropertyStr(ctx, jsonClass, "parse");
|
88
|
+
JSValue j_str = JS_NewString(ctx, str);
|
89
|
+
JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
|
90
|
+
JS_FreeValue(ctx, global);
|
91
|
+
JS_FreeValue(ctx, parseFunc);
|
92
|
+
JS_FreeValue(ctx, jsonClass);
|
93
|
+
|
94
|
+
return stringified;
|
95
|
+
}
|
96
|
+
default: {
|
97
|
+
VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
|
98
|
+
char *str = StringValueCStr(r_inspect_str);
|
99
|
+
|
100
|
+
return JS_NewString(ctx, str);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
|
72
106
|
switch(JS_VALUE_GET_NORM_TAG(jsv)) {
|
73
107
|
case JS_TAG_INT: {
|
74
108
|
int int_res = 0;
|
@@ -94,7 +128,7 @@ VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
|
|
94
128
|
case JS_TAG_OBJECT: {
|
95
129
|
int promiseState = JS_PromiseState(ctx, jsv);
|
96
130
|
if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
|
97
|
-
return to_rb_value(js_std_await(ctx, jsv), ctx);
|
131
|
+
return to_rb_value(js_std_await(ctx, jsv), ctx); // should have timeout
|
98
132
|
} else if (promiseState == JS_PROMISE_REJECTED) {
|
99
133
|
return to_rb_value(JS_Throw(ctx, JS_PromiseResult(ctx, jsv)), ctx);
|
100
134
|
}
|
@@ -159,7 +193,22 @@ VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
|
|
159
193
|
}
|
160
194
|
}
|
161
195
|
|
162
|
-
|
196
|
+
static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv) {
|
197
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
198
|
+
JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
|
199
|
+
const char *funcName = JS_ToCString(ctx, maybeFuncName);
|
200
|
+
JS_FreeValue(ctx, maybeFuncName);
|
201
|
+
|
202
|
+
VALUE proc = get_proc(data->procs, funcName);
|
203
|
+
if (proc == Qnil) { // Shouldn't happen
|
204
|
+
return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
|
205
|
+
}
|
206
|
+
|
207
|
+
VALUE r_result = rb_apply(proc, rb_intern("call"), to_rb_value(argv[1], ctx));
|
208
|
+
return to_js_value(ctx, r_result);
|
209
|
+
}
|
210
|
+
|
211
|
+
VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
|
163
212
|
{
|
164
213
|
VALUE r_opts;
|
165
214
|
rb_scan_args(argc, argv, ":", &r_opts);
|
@@ -171,14 +220,19 @@ VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
|
|
171
220
|
if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
|
172
221
|
VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
|
173
222
|
if (NIL_P(r_features)) r_features = rb_ary_new();
|
223
|
+
VALUE r_timeout_msec = rb_hash_aref(r_opts, ID2SYM(rb_intern("timeout_msec")));
|
224
|
+
if (NIL_P(r_timeout_msec)) r_timeout_msec= UINT2NUM(100);
|
174
225
|
|
175
|
-
|
176
|
-
TypedData_Get_Struct(self,
|
226
|
+
VMData *data;
|
227
|
+
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
177
228
|
|
178
|
-
QuickjsrbRuntimeState *state= create_quickjsrb_runtime_state();
|
179
229
|
JSRuntime *runtime = JS_NewRuntime();
|
180
230
|
data->context = JS_NewContext(runtime);
|
181
|
-
data->
|
231
|
+
data->procs = create_proc_entries();
|
232
|
+
|
233
|
+
EvalTime *eval_time = malloc(sizeof(EvalTime));
|
234
|
+
data->eval_time = eval_time;
|
235
|
+
data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
|
182
236
|
data->alive = 1;
|
183
237
|
JS_SetContextOpaque(data->context, data);
|
184
238
|
|
@@ -222,15 +276,24 @@ VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
|
|
222
276
|
return self;
|
223
277
|
}
|
224
278
|
|
225
|
-
|
279
|
+
static int interrupt_handler(JSRuntime *runtime, void *opaque) {
|
280
|
+
EvalTime *eval_time = opaque;
|
281
|
+
return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
|
282
|
+
}
|
283
|
+
|
284
|
+
VALUE vm_m_evalCode(VALUE self, VALUE r_code)
|
226
285
|
{
|
227
|
-
|
228
|
-
TypedData_Get_Struct(self,
|
286
|
+
VMData *data;
|
287
|
+
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
229
288
|
|
230
289
|
if (data->alive < 1) {
|
231
290
|
rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
|
232
291
|
return Qnil;
|
233
292
|
}
|
293
|
+
|
294
|
+
data->eval_time->started_at = clock();
|
295
|
+
JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
|
296
|
+
|
234
297
|
char *code = StringValueCStr(r_code);
|
235
298
|
JSValue codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
|
236
299
|
VALUE result = to_rb_value(codeResult, data->context);
|
@@ -239,19 +302,19 @@ VALUE qvm_m_evalCode(VALUE self, VALUE r_code)
|
|
239
302
|
return result;
|
240
303
|
}
|
241
304
|
|
242
|
-
VALUE
|
305
|
+
VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
243
306
|
{
|
244
307
|
rb_need_block();
|
245
308
|
|
246
|
-
|
247
|
-
TypedData_Get_Struct(self,
|
309
|
+
VMData *data;
|
310
|
+
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
248
311
|
|
249
312
|
if (rb_block_given_p()) {
|
250
313
|
VALUE proc = rb_block_proc();
|
251
314
|
|
252
315
|
char *funcName = StringValueCStr(r_name);
|
253
316
|
|
254
|
-
set_proc(data->
|
317
|
+
set_proc(data->procs, funcName, proc);
|
255
318
|
|
256
319
|
const char* template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
|
257
320
|
int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
|
@@ -268,14 +331,15 @@ VALUE qvm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
|
268
331
|
return Qnil;
|
269
332
|
}
|
270
333
|
|
271
|
-
VALUE
|
334
|
+
VALUE vm_m_dispose(VALUE self)
|
272
335
|
{
|
273
|
-
|
274
|
-
TypedData_Get_Struct(self,
|
336
|
+
VMData *data;
|
337
|
+
TypedData_Get_Struct(self, VMData, &vm_type, data);
|
275
338
|
|
276
339
|
JSRuntime *runtime = JS_GetRuntime(data->context);
|
340
|
+
JS_SetInterruptHandler(runtime, NULL, NULL);
|
277
341
|
js_std_free_handlers(runtime);
|
278
|
-
|
342
|
+
free_proc_entry_map(data->procs);
|
279
343
|
JS_FreeContext(data->context);
|
280
344
|
JS_FreeRuntime(runtime);
|
281
345
|
data->alive = 0;
|
@@ -295,9 +359,9 @@ Init_quickjsrb(void)
|
|
295
359
|
rb_define_const(valueClass, "NAN", ID2SYM(rb_intern(nanId)));
|
296
360
|
|
297
361
|
VALUE vmClass = rb_define_class_under(rb_mQuickjs, "VM", rb_cObject);
|
298
|
-
rb_define_alloc_func(vmClass,
|
299
|
-
rb_define_method(vmClass, "initialize",
|
300
|
-
rb_define_method(vmClass, "eval_code",
|
301
|
-
rb_define_method(vmClass, "define_function",
|
302
|
-
rb_define_method(vmClass, "dispose!",
|
362
|
+
rb_define_alloc_func(vmClass, vm_alloc);
|
363
|
+
rb_define_method(vmClass, "initialize", vm_m_initialize, -1);
|
364
|
+
rb_define_method(vmClass, "eval_code", vm_m_evalCode, 1);
|
365
|
+
rb_define_method(vmClass, "define_function", vm_m_defineGlobalFunction, 1);
|
366
|
+
rb_define_method(vmClass, "dispose!", vm_m_dispose, 0);
|
303
367
|
}
|
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.8
|
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-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -37,6 +37,8 @@ files:
|
|
37
37
|
- README.md
|
38
38
|
- Rakefile
|
39
39
|
- ext/quickjsrb/extconf.rb
|
40
|
+
- ext/quickjsrb/procs.c
|
41
|
+
- ext/quickjsrb/procs.h
|
40
42
|
- ext/quickjsrb/quickjs/LICENSE
|
41
43
|
- ext/quickjsrb/quickjs/cutils.c
|
42
44
|
- ext/quickjsrb/quickjs/cutils.h
|
@@ -62,8 +64,6 @@ files:
|
|
62
64
|
- ext/quickjsrb/quickjs/run-test262.c
|
63
65
|
- ext/quickjsrb/quickjs/unicode_gen.c
|
64
66
|
- ext/quickjsrb/quickjs/unicode_gen_def.h
|
65
|
-
- ext/quickjsrb/quickjsrb-runtime-state.c
|
66
|
-
- ext/quickjsrb/quickjsrb-runtime-state.h
|
67
67
|
- ext/quickjsrb/quickjsrb.c
|
68
68
|
- ext/quickjsrb/quickjsrb.h
|
69
69
|
- lib/quickjs.rb
|