quickjs 0.1.10 → 0.1.11
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 +10 -1
- data/ext/quickjsrb/quickjsrb.c +150 -118
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +14 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81c0e4a716d292ddbfd58f559f37ddb1d86adb7059779f29bfa0a2b8a8180214
|
4
|
+
data.tar.gz: f45ff31a1913c4ab807ddd5d8f08c17c9bc58236b09308f31f5adbf5d9241ffe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24c0349dc9e911936932a78e3c7aa0ac75ea36eb776258ae8d8d20d215eaa79a2d79a64c0b1eb0878182033eee8945f7e3f65dd1272e5290a332ac07bde7b7a9
|
7
|
+
data.tar.gz: e7e472520ece228398482ea753157ffc4bf08b2f446b56dd2439b27a9c1006ffc9439ca5d1eb27015e6fa19b68e5663d82339c082b82fa085235e8c8108b5d3b
|
data/README.md
CHANGED
@@ -56,6 +56,9 @@ Quickjs.eval_code(code, { features: [Quickjs::MODULE_STD] })
|
|
56
56
|
# enable os module
|
57
57
|
# https://bellard.org/quickjs/quickjs.html#os-module
|
58
58
|
Quickjs.eval_code(code, { features: [Quickjs::MODULE_OS] })
|
59
|
+
|
60
|
+
# enable timeout features `setTimeout`, `clearTimeout`
|
61
|
+
Quickjs.eval_code(code, { features: [Quickjs::FEATURES_TIMEOUT] })
|
59
62
|
```
|
60
63
|
|
61
64
|
### `Quickjs::VM`: Maintain a consistent VM/runtime
|
@@ -94,9 +97,14 @@ vm = Quickjs::VM.new(
|
|
94
97
|
vm = Quickjs::VM.new(
|
95
98
|
timeout_msec: 1_000,
|
96
99
|
)
|
100
|
+
|
101
|
+
# enable timeout features `setTimeout`, `clearTimeout`
|
102
|
+
vm = Quickjs::VM.new(
|
103
|
+
features: [::Quickjs::FEATURES_TIMEOUT],
|
104
|
+
)
|
97
105
|
```
|
98
106
|
|
99
|
-
#### Define a global function for JS
|
107
|
+
#### ⚡️ Define a global function for JS by Ruby
|
100
108
|
|
101
109
|
```rb
|
102
110
|
vm = Quickjs::VM.new
|
@@ -110,4 +118,5 @@ vm.eval_code("greetingTo('Rick')") #=> 'Hello! Rick'
|
|
110
118
|
## License
|
111
119
|
|
112
120
|
Every file in `ext/quickjsrb/quickjs` is licensed under [the MIT License Copyright 2017-2021 by Fabrice Bellard and Charlie Goron](/ext/quickjsrb/quickjs/LICENSE).
|
121
|
+
|
113
122
|
For otherwise, [the MIT License, Copyright 2024 by Kengo Hamasaki](/LICENSE).
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
@@ -48,10 +48,10 @@ static const rb_data_type_t vm_type = {
|
|
48
48
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
49
49
|
};
|
50
50
|
|
51
|
-
static VALUE vm_alloc(VALUE
|
51
|
+
static VALUE vm_alloc(VALUE r_self)
|
52
52
|
{
|
53
53
|
VMData *data;
|
54
|
-
VALUE obj = TypedData_Make_Struct(
|
54
|
+
VALUE obj = TypedData_Make_Struct(r_self, VMData, &vm_type, data);
|
55
55
|
data->defined_functions = rb_hash_new();
|
56
56
|
|
57
57
|
EvalTime *eval_time = malloc(sizeof(EvalTime));
|
@@ -69,9 +69,23 @@ const char *nanId = "NaN";
|
|
69
69
|
|
70
70
|
const char *featureStdId = "feature_std";
|
71
71
|
const char *featureOsId = "feature_os";
|
72
|
+
const char *featureOsTimeoutId = "feature_os_timeout";
|
72
73
|
|
73
74
|
JSValue to_js_value(JSContext *ctx, VALUE r_value)
|
74
75
|
{
|
76
|
+
if (RTEST(rb_funcall(
|
77
|
+
r_value,
|
78
|
+
rb_intern("is_a?"),
|
79
|
+
1, rb_const_get(rb_cClass, rb_intern("Exception")))))
|
80
|
+
{
|
81
|
+
VALUE r_str = rb_funcall(r_value, rb_intern("message"), 0, NULL);
|
82
|
+
char *str = StringValueCStr(r_str);
|
83
|
+
JSValue j_error = JS_NewError(ctx);
|
84
|
+
JSValue j_str = JS_NewString(ctx, str);
|
85
|
+
JS_SetPropertyStr(ctx, j_error, "message", j_str);
|
86
|
+
return JS_Throw(ctx, j_error);
|
87
|
+
}
|
88
|
+
|
75
89
|
switch (TYPE(r_value))
|
76
90
|
{
|
77
91
|
case T_NIL:
|
@@ -81,15 +95,15 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
|
|
81
95
|
{
|
82
96
|
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
|
83
97
|
char *str = StringValueCStr(r_str);
|
84
|
-
JSValue
|
85
|
-
JSValue
|
98
|
+
JSValue j_global = JS_GetGlobalObject(ctx);
|
99
|
+
JSValue j_numberClass = JS_GetPropertyStr(ctx, j_global, "Number");
|
86
100
|
JSValue j_str = JS_NewString(ctx, str);
|
87
|
-
JSValue
|
88
|
-
JS_FreeValue(ctx,
|
89
|
-
JS_FreeValue(ctx,
|
101
|
+
JSValue j_stringified = JS_Call(ctx, j_numberClass, JS_UNDEFINED, 1, &j_str);
|
102
|
+
JS_FreeValue(ctx, j_global);
|
103
|
+
JS_FreeValue(ctx, j_numberClass);
|
90
104
|
JS_FreeValue(ctx, j_str);
|
91
105
|
|
92
|
-
return
|
106
|
+
return j_stringified;
|
93
107
|
}
|
94
108
|
case T_STRING:
|
95
109
|
{
|
@@ -113,17 +127,17 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
|
|
113
127
|
{
|
114
128
|
VALUE r_json_str = rb_funcall(r_value, rb_intern("to_json"), 0, NULL);
|
115
129
|
char *str = StringValueCStr(r_json_str);
|
116
|
-
JSValue
|
117
|
-
JSValue
|
118
|
-
JSValue
|
130
|
+
JSValue j_global = JS_GetGlobalObject(ctx);
|
131
|
+
JSValue j_jsonClass = JS_GetPropertyStr(ctx, j_global, "JSON");
|
132
|
+
JSValue j_parseFunc = JS_GetPropertyStr(ctx, j_jsonClass, "parse");
|
119
133
|
JSValue j_str = JS_NewString(ctx, str);
|
120
|
-
JSValue
|
121
|
-
JS_FreeValue(ctx,
|
122
|
-
JS_FreeValue(ctx,
|
123
|
-
JS_FreeValue(ctx,
|
134
|
+
JSValue j_stringified = JS_Call(ctx, j_parseFunc, j_jsonClass, 1, &j_str);
|
135
|
+
JS_FreeValue(ctx, j_global);
|
136
|
+
JS_FreeValue(ctx, j_jsonClass);
|
137
|
+
JS_FreeValue(ctx, j_parseFunc);
|
124
138
|
JS_FreeValue(ctx, j_str);
|
125
139
|
|
126
|
-
return
|
140
|
+
return j_stringified;
|
127
141
|
}
|
128
142
|
default:
|
129
143
|
{
|
@@ -135,73 +149,62 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
|
|
135
149
|
}
|
136
150
|
}
|
137
151
|
|
138
|
-
VALUE to_rb_value(
|
152
|
+
VALUE to_rb_value(JSContext *ctx, JSValue j_val)
|
139
153
|
{
|
140
|
-
switch (JS_VALUE_GET_NORM_TAG(
|
154
|
+
switch (JS_VALUE_GET_NORM_TAG(j_val))
|
141
155
|
{
|
142
156
|
case JS_TAG_INT:
|
143
157
|
{
|
144
158
|
int int_res = 0;
|
145
|
-
JS_ToInt32(ctx, &int_res,
|
159
|
+
JS_ToInt32(ctx, &int_res, j_val);
|
146
160
|
return INT2NUM(int_res);
|
147
161
|
}
|
148
162
|
case JS_TAG_FLOAT64:
|
149
163
|
{
|
150
|
-
if (JS_VALUE_IS_NAN(
|
164
|
+
if (JS_VALUE_IS_NAN(j_val))
|
151
165
|
{
|
152
166
|
return ID2SYM(rb_intern(nanId));
|
153
167
|
}
|
154
168
|
double double_res;
|
155
|
-
JS_ToFloat64(ctx, &double_res,
|
169
|
+
JS_ToFloat64(ctx, &double_res, j_val);
|
156
170
|
return DBL2NUM(double_res);
|
157
171
|
}
|
158
172
|
case JS_TAG_BOOL:
|
159
173
|
{
|
160
|
-
return JS_ToBool(ctx,
|
174
|
+
return JS_ToBool(ctx, j_val) > 0 ? Qtrue : Qfalse;
|
161
175
|
}
|
162
176
|
case JS_TAG_STRING:
|
163
177
|
{
|
164
|
-
|
165
|
-
|
166
|
-
JS_FreeValue(ctx, maybeString);
|
178
|
+
const char *msg = JS_ToCString(ctx, j_val);
|
179
|
+
VALUE r_str = rb_str_new2(msg);
|
167
180
|
JS_FreeCString(ctx, msg);
|
168
|
-
return
|
181
|
+
return r_str;
|
169
182
|
}
|
170
183
|
case JS_TAG_OBJECT:
|
171
184
|
{
|
172
|
-
int promiseState = JS_PromiseState(ctx,
|
173
|
-
if (promiseState
|
185
|
+
int promiseState = JS_PromiseState(ctx, j_val);
|
186
|
+
if (promiseState != -1)
|
174
187
|
{
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
return rb_awaited;
|
179
|
-
}
|
180
|
-
else if (promiseState == JS_PROMISE_REJECTED)
|
181
|
-
{
|
182
|
-
JSValue promiseResult = JS_PromiseResult(ctx, jsv);
|
183
|
-
JSValue throw = JS_Throw(ctx, promiseResult);
|
184
|
-
JS_FreeValue(ctx, promiseResult);
|
185
|
-
VALUE rb_errored = to_rb_value(throw, ctx);
|
186
|
-
JS_FreeValue(ctx, throw);
|
187
|
-
return rb_errored;
|
188
|
+
VALUE r_error_message = rb_str_new2("cannot translate a Promise to Ruby. await within JavaScript's end");
|
189
|
+
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
|
190
|
+
return Qnil;
|
188
191
|
}
|
189
192
|
|
190
|
-
JSValue
|
191
|
-
JSValue
|
192
|
-
JSValue
|
193
|
-
JSValue
|
193
|
+
JSValue j_global = JS_GetGlobalObject(ctx);
|
194
|
+
JSValue j_jsonClass = JS_GetPropertyStr(ctx, j_global, "JSON");
|
195
|
+
JSValue j_stringifyFunc = JS_GetPropertyStr(ctx, j_jsonClass, "stringify");
|
196
|
+
JSValue j_strigified = JS_Call(ctx, j_stringifyFunc, j_jsonClass, 1, &j_val);
|
194
197
|
|
195
|
-
const char *msg = JS_ToCString(ctx,
|
196
|
-
VALUE
|
198
|
+
const char *msg = JS_ToCString(ctx, j_strigified);
|
199
|
+
VALUE r_str = rb_str_new2(msg);
|
197
200
|
JS_FreeCString(ctx, msg);
|
198
201
|
|
199
|
-
JS_FreeValue(ctx,
|
200
|
-
JS_FreeValue(ctx,
|
201
|
-
JS_FreeValue(ctx,
|
202
|
-
JS_FreeValue(ctx,
|
202
|
+
JS_FreeValue(ctx, j_global);
|
203
|
+
JS_FreeValue(ctx, j_strigified);
|
204
|
+
JS_FreeValue(ctx, j_stringifyFunc);
|
205
|
+
JS_FreeValue(ctx, j_jsonClass);
|
203
206
|
|
204
|
-
return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1,
|
207
|
+
return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
|
205
208
|
}
|
206
209
|
case JS_TAG_NULL:
|
207
210
|
return Qnil;
|
@@ -209,47 +212,47 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx)
|
|
209
212
|
return ID2SYM(rb_intern(undefinedId));
|
210
213
|
case JS_TAG_EXCEPTION:
|
211
214
|
{
|
212
|
-
JSValue
|
213
|
-
if (JS_IsError(ctx,
|
215
|
+
JSValue j_exceptionVal = JS_GetException(ctx);
|
216
|
+
if (JS_IsError(ctx, j_exceptionVal))
|
214
217
|
{
|
215
|
-
JSValue
|
216
|
-
const char *errorClassName = JS_ToCString(ctx,
|
218
|
+
JSValue j_errorClassName = JS_GetPropertyStr(ctx, j_exceptionVal, "name");
|
219
|
+
const char *errorClassName = JS_ToCString(ctx, j_errorClassName);
|
217
220
|
|
218
|
-
JSValue
|
219
|
-
const char *errorClassMessage = JS_ToCString(ctx,
|
221
|
+
JSValue j_errorClassMessage = JS_GetPropertyStr(ctx, j_exceptionVal, "message");
|
222
|
+
const char *errorClassMessage = JS_ToCString(ctx, j_errorClassMessage);
|
220
223
|
|
221
|
-
JS_FreeValue(ctx,
|
222
|
-
JS_FreeValue(ctx,
|
224
|
+
JS_FreeValue(ctx, j_errorClassMessage);
|
225
|
+
JS_FreeValue(ctx, j_errorClassName);
|
223
226
|
|
224
|
-
VALUE
|
227
|
+
VALUE r_error_message = rb_sprintf("%s: %s", errorClassName, errorClassMessage);
|
225
228
|
JS_FreeCString(ctx, errorClassName);
|
226
229
|
JS_FreeCString(ctx, errorClassMessage);
|
227
|
-
JS_FreeValue(ctx,
|
228
|
-
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError,
|
230
|
+
JS_FreeValue(ctx, j_exceptionVal);
|
231
|
+
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
|
229
232
|
}
|
230
233
|
else
|
231
234
|
{
|
232
|
-
const char *errorMessage = JS_ToCString(ctx,
|
235
|
+
const char *errorMessage = JS_ToCString(ctx, j_exceptionVal);
|
236
|
+
VALUE r_error_message = rb_sprintf("%s", errorMessage);
|
233
237
|
|
234
|
-
VALUE rb_errorMessage = rb_sprintf("%s", errorMessage);
|
235
238
|
JS_FreeCString(ctx, errorMessage);
|
236
|
-
JS_FreeValue(ctx,
|
237
|
-
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError,
|
239
|
+
JS_FreeValue(ctx, j_exceptionVal);
|
240
|
+
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
|
238
241
|
}
|
239
242
|
return Qnil;
|
240
243
|
}
|
241
244
|
case JS_TAG_BIG_INT:
|
242
245
|
{
|
243
|
-
JSValue
|
244
|
-
JSValue
|
246
|
+
JSValue j_toStringFunc = JS_GetPropertyStr(ctx, j_val, "toString");
|
247
|
+
JSValue j_strigified = JS_Call(ctx, j_toStringFunc, j_val, 0, NULL);
|
245
248
|
|
246
|
-
const char *msg = JS_ToCString(ctx,
|
247
|
-
VALUE
|
248
|
-
JS_FreeValue(ctx,
|
249
|
-
JS_FreeValue(ctx,
|
249
|
+
const char *msg = JS_ToCString(ctx, j_strigified);
|
250
|
+
VALUE r_str = rb_str_new2(msg);
|
251
|
+
JS_FreeValue(ctx, j_toStringFunc);
|
252
|
+
JS_FreeValue(ctx, j_strigified);
|
250
253
|
JS_FreeCString(ctx, msg);
|
251
254
|
|
252
|
-
return rb_funcall(
|
255
|
+
return rb_funcall(r_str, rb_intern("to_i"), 0, NULL);
|
253
256
|
}
|
254
257
|
case JS_TAG_BIG_FLOAT:
|
255
258
|
case JS_TAG_BIG_DECIMAL:
|
@@ -262,35 +265,41 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx)
|
|
262
265
|
static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv)
|
263
266
|
{
|
264
267
|
VMData *data = JS_GetContextOpaque(ctx);
|
265
|
-
JSValue
|
266
|
-
const char *funcName = JS_ToCString(ctx,
|
267
|
-
JS_FreeValue(ctx,
|
268
|
+
JSValue j_maybeFuncName = JS_ToString(ctx, argv[0]);
|
269
|
+
const char *funcName = JS_ToCString(ctx, j_maybeFuncName);
|
270
|
+
JS_FreeValue(ctx, j_maybeFuncName);
|
268
271
|
|
269
|
-
VALUE
|
270
|
-
if (
|
272
|
+
VALUE r_proc = rb_hash_aref(data->defined_functions, rb_str_new2(funcName));
|
273
|
+
if (r_proc == Qnil)
|
271
274
|
{ // Shouldn't happen
|
272
275
|
return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
|
273
276
|
}
|
274
277
|
JS_FreeCString(ctx, funcName);
|
275
278
|
|
276
|
-
|
277
|
-
|
279
|
+
VALUE r_result = rb_funcall(
|
280
|
+
rb_const_get(rb_cClass, rb_intern("Quickjs")),
|
281
|
+
rb_intern("_with_timeout"),
|
282
|
+
3,
|
283
|
+
ULONG2NUM(data->eval_time->limit * 1000 / CLOCKS_PER_SEC),
|
284
|
+
r_proc,
|
285
|
+
to_rb_value(ctx, argv[1]));
|
286
|
+
|
278
287
|
return to_js_value(ctx, r_result);
|
279
288
|
}
|
280
289
|
|
281
|
-
static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE
|
290
|
+
static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
|
282
291
|
{
|
283
292
|
VALUE r_opts;
|
284
293
|
rb_scan_args(argc, argv, ":", &r_opts);
|
285
294
|
if (NIL_P(r_opts))
|
286
295
|
r_opts = rb_hash_new();
|
287
296
|
|
288
|
-
VALUE
|
289
|
-
if (NIL_P(
|
290
|
-
|
291
|
-
VALUE
|
292
|
-
if (NIL_P(
|
293
|
-
|
297
|
+
VALUE r_memory_limit = rb_hash_aref(r_opts, ID2SYM(rb_intern("memory_limit")));
|
298
|
+
if (NIL_P(r_memory_limit))
|
299
|
+
r_memory_limit = UINT2NUM(1024 * 1024 * 128);
|
300
|
+
VALUE r_max_stack_size = rb_hash_aref(r_opts, ID2SYM(rb_intern("max_stack_size")));
|
301
|
+
if (NIL_P(r_max_stack_size))
|
302
|
+
r_max_stack_size = UINT2NUM(1024 * 1024 * 4);
|
294
303
|
VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
|
295
304
|
if (NIL_P(r_features))
|
296
305
|
r_features = rb_ary_new();
|
@@ -299,14 +308,14 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE self)
|
|
299
308
|
r_timeout_msec = UINT2NUM(100);
|
300
309
|
|
301
310
|
VMData *data;
|
302
|
-
TypedData_Get_Struct(
|
311
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
303
312
|
|
304
313
|
data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
|
305
314
|
JS_SetContextOpaque(data->context, data);
|
306
315
|
JSRuntime *runtime = JS_GetRuntime(data->context);
|
307
316
|
|
308
|
-
JS_SetMemoryLimit(runtime, NUM2UINT(
|
309
|
-
JS_SetMaxStackSize(runtime, NUM2UINT(
|
317
|
+
JS_SetMemoryLimit(runtime, NUM2UINT(r_memory_limit));
|
318
|
+
JS_SetMaxStackSize(runtime, NUM2UINT(r_max_stack_size));
|
310
319
|
|
311
320
|
JS_AddIntrinsicBigFloat(data->context);
|
312
321
|
JS_AddIntrinsicBigDecimal(data->context);
|
@@ -322,8 +331,8 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE self)
|
|
322
331
|
js_init_module_std(data->context, "std");
|
323
332
|
const char *enableStd = "import * as std from 'std';\n"
|
324
333
|
"globalThis.std = std;\n";
|
325
|
-
JSValue
|
326
|
-
JS_FreeValue(data->context,
|
334
|
+
JSValue j_stdEval = JS_Eval(data->context, enableStd, strlen(enableStd), "<vm>", JS_EVAL_TYPE_MODULE);
|
335
|
+
JS_FreeValue(data->context, j_stdEval);
|
327
336
|
}
|
328
337
|
|
329
338
|
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsId)))))
|
@@ -331,20 +340,29 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE self)
|
|
331
340
|
js_init_module_os(data->context, "os");
|
332
341
|
const char *enableOs = "import * as os from 'os';\n"
|
333
342
|
"globalThis.os = os;\n";
|
334
|
-
JSValue
|
335
|
-
JS_FreeValue(data->context,
|
343
|
+
JSValue j_osEval = JS_Eval(data->context, enableOs, strlen(enableOs), "<vm>", JS_EVAL_TYPE_MODULE);
|
344
|
+
JS_FreeValue(data->context, j_osEval);
|
345
|
+
}
|
346
|
+
else if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsTimeoutId)))))
|
347
|
+
{
|
348
|
+
js_init_module_os(data->context, "_os"); // Better if this is limited just only for setTimeout and clearTimeout
|
349
|
+
const char *enableTimeout = "import * as _os from '_os';\n"
|
350
|
+
"globalThis.setTimeout = _os.setTimeout;\n"
|
351
|
+
"globalThis.clearTimeout = _os.clearTimeout;\n";
|
352
|
+
JSValue j_timeoutEval = JS_Eval(data->context, enableTimeout, strlen(enableTimeout), "<vm>", JS_EVAL_TYPE_MODULE);
|
353
|
+
JS_FreeValue(data->context, j_timeoutEval);
|
336
354
|
}
|
337
355
|
|
338
356
|
const char *setupGlobalRuby = "globalThis.__ruby = {};\n";
|
339
|
-
JSValue
|
340
|
-
JS_FreeValue(data->context,
|
357
|
+
JSValue j_rubyEval = JS_Eval(data->context, setupGlobalRuby, strlen(setupGlobalRuby), "<vm>", JS_EVAL_TYPE_MODULE);
|
358
|
+
JS_FreeValue(data->context, j_rubyEval);
|
341
359
|
|
342
|
-
JSValue
|
343
|
-
JSValue
|
344
|
-
JS_SetPropertyStr(data->context,
|
345
|
-
JS_FreeValue(data->context,
|
360
|
+
JSValue j_global = JS_GetGlobalObject(data->context);
|
361
|
+
JSValue j_func = JS_NewCFunction(data->context, js_quickjsrb_call_global, "rubyGlobal", 2);
|
362
|
+
JS_SetPropertyStr(data->context, j_global, "rubyGlobal", j_func);
|
363
|
+
JS_FreeValue(data->context, j_global);
|
346
364
|
|
347
|
-
return
|
365
|
+
return r_self;
|
348
366
|
}
|
349
367
|
|
350
368
|
static int interrupt_handler(JSRuntime *runtime, void *opaque)
|
@@ -353,46 +371,59 @@ static int interrupt_handler(JSRuntime *runtime, void *opaque)
|
|
353
371
|
return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
|
354
372
|
}
|
355
373
|
|
356
|
-
static VALUE vm_m_evalCode(VALUE
|
374
|
+
static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
|
357
375
|
{
|
358
376
|
VMData *data;
|
359
|
-
TypedData_Get_Struct(
|
377
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
360
378
|
|
361
379
|
data->eval_time->started_at = clock();
|
362
380
|
JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
|
363
381
|
|
364
382
|
char *code = StringValueCStr(r_code);
|
365
|
-
JSValue
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
383
|
+
JSValue j_codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_ASYNC);
|
384
|
+
JSValue j_awaitedResult = js_std_await(data->context, j_codeResult);
|
385
|
+
JSValue j_returnedValue = JS_GetPropertyStr(data->context, j_awaitedResult, "value");
|
386
|
+
// Do this by rescuing to_rb_value
|
387
|
+
if (JS_VALUE_GET_NORM_TAG(j_returnedValue) == JS_TAG_OBJECT && JS_PromiseState(data->context, j_returnedValue) != -1)
|
388
|
+
{
|
389
|
+
JS_FreeValue(data->context, j_returnedValue);
|
390
|
+
JS_FreeValue(data->context, j_awaitedResult);
|
391
|
+
VALUE r_error_message = rb_str_new2("An unawaited Promise was returned to the top-level");
|
392
|
+
rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
|
393
|
+
return Qnil;
|
394
|
+
}
|
395
|
+
else
|
396
|
+
{
|
397
|
+
VALUE result = to_rb_value(data->context, j_returnedValue);
|
398
|
+
JS_FreeValue(data->context, j_returnedValue);
|
399
|
+
JS_FreeValue(data->context, j_awaitedResult);
|
400
|
+
return result;
|
401
|
+
}
|
370
402
|
}
|
371
403
|
|
372
|
-
static VALUE vm_m_defineGlobalFunction(VALUE
|
404
|
+
static VALUE vm_m_defineGlobalFunction(VALUE r_self, VALUE r_name)
|
373
405
|
{
|
374
406
|
rb_need_block();
|
375
407
|
|
376
408
|
VMData *data;
|
377
|
-
TypedData_Get_Struct(
|
409
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
378
410
|
|
379
411
|
if (rb_block_given_p())
|
380
412
|
{
|
381
|
-
VALUE
|
382
|
-
|
413
|
+
VALUE r_proc = rb_block_proc();
|
414
|
+
rb_hash_aset(data->defined_functions, r_name, r_proc);
|
383
415
|
char *funcName = StringValueCStr(r_name);
|
384
416
|
|
385
|
-
|
386
|
-
|
387
|
-
const char *template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
|
417
|
+
const char *template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\n"
|
418
|
+
"globalThis['%s'] = globalThis.__ruby['%s'];\n";
|
388
419
|
int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
|
389
420
|
char *result = (char *)malloc(length + 1);
|
390
421
|
snprintf(result, length + 1, template, funcName, funcName, funcName, funcName);
|
391
422
|
|
392
|
-
JSValue
|
423
|
+
JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
|
393
424
|
|
394
425
|
free(result);
|
395
|
-
JS_FreeValue(data->context,
|
426
|
+
JS_FreeValue(data->context, j_codeResult);
|
396
427
|
return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);
|
397
428
|
}
|
398
429
|
|
@@ -405,6 +436,7 @@ Init_quickjsrb(void)
|
|
405
436
|
rb_mQuickjs = rb_define_module("Quickjs");
|
406
437
|
rb_define_const(rb_mQuickjs, "MODULE_STD", ID2SYM(rb_intern(featureStdId)));
|
407
438
|
rb_define_const(rb_mQuickjs, "MODULE_OS", ID2SYM(rb_intern(featureOsId)));
|
439
|
+
rb_define_const(rb_mQuickjs, "FEATURES_TIMEOUT", ID2SYM(rb_intern(featureOsTimeoutId)));
|
408
440
|
|
409
441
|
VALUE valueClass = rb_define_class_under(rb_mQuickjs, "Value", rb_cObject);
|
410
442
|
rb_define_const(valueClass, "UNDEFINED", ID2SYM(rb_intern(undefinedId)));
|
data/lib/quickjs/version.rb
CHANGED
data/lib/quickjs.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "timeout"
|
3
4
|
require "json"
|
4
5
|
require_relative "quickjs/version"
|
5
6
|
require_relative "quickjs/quickjsrb"
|
6
7
|
|
7
8
|
module Quickjs
|
8
9
|
def eval_code(code, overwrite_opts = {})
|
9
|
-
Quickjs::VM.new(**overwrite_opts)
|
10
|
+
vm = Quickjs::VM.new(**overwrite_opts)
|
11
|
+
res = vm.eval_code(code)
|
12
|
+
vm = nil
|
13
|
+
res
|
10
14
|
end
|
11
15
|
module_function :eval_code
|
16
|
+
|
17
|
+
def _with_timeout(msec, proc, args)
|
18
|
+
Timeout.timeout(msec / 1_000.0) { proc.call(*args) }
|
19
|
+
rescue Timeout::Error
|
20
|
+
RuntimeError.new('interrupted')
|
21
|
+
rescue => e
|
22
|
+
e
|
23
|
+
end
|
24
|
+
module_function :_with_timeout
|
12
25
|
end
|
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.11
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|