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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8eba12c1c34fab81270517de72da6a8b3c9b561d521a42b80c14411c23555684
4
- data.tar.gz: 4bced848c986a41c787306e799b40622d97dff999994b64e615cd3b1c79e586e
3
+ metadata.gz: 81c0e4a716d292ddbfd58f559f37ddb1d86adb7059779f29bfa0a2b8a8180214
4
+ data.tar.gz: f45ff31a1913c4ab807ddd5d8f08c17c9bc58236b09308f31f5adbf5d9241ffe
5
5
  SHA512:
6
- metadata.gz: 3598ef1f8b5bcbeedf916c101387477b8318e6550a1aa52b6fa4ddb269e261447768428eb4b644e2a0a9cc64ae084830c511cb077b7e25e292eb5d8e84a96460
7
- data.tar.gz: 686a93a0a00d747c99d8a0a0f0072a2d0451cc6bfdeeeb512f02817288636d7192a2e2dda52d0e9bf05b92ebb1930f503ca7ce2251c8419ca568c66f825bca1f
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).
@@ -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 self)
51
+ static VALUE vm_alloc(VALUE r_self)
52
52
  {
53
53
  VMData *data;
54
- VALUE obj = TypedData_Make_Struct(self, VMData, &vm_type, data);
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 global = JS_GetGlobalObject(ctx);
85
- JSValue numberClass = JS_GetPropertyStr(ctx, global, "Number");
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 stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
88
- JS_FreeValue(ctx, global);
89
- JS_FreeValue(ctx, numberClass);
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 stringified;
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 global = JS_GetGlobalObject(ctx);
117
- JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
118
- JSValue parseFunc = JS_GetPropertyStr(ctx, jsonClass, "parse");
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 stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
121
- JS_FreeValue(ctx, global);
122
- JS_FreeValue(ctx, jsonClass);
123
- JS_FreeValue(ctx, parseFunc);
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 stringified;
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(JSValue jsv, JSContext *ctx)
152
+ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
139
153
  {
140
- switch (JS_VALUE_GET_NORM_TAG(jsv))
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, jsv);
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(jsv))
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, jsv);
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, jsv) > 0 ? Qtrue : Qfalse;
174
+ return JS_ToBool(ctx, j_val) > 0 ? Qtrue : Qfalse;
161
175
  }
162
176
  case JS_TAG_STRING:
163
177
  {
164
- JSValue maybeString = JS_ToString(ctx, jsv);
165
- const char *msg = JS_ToCString(ctx, maybeString);
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 rb_str_new2(msg);
181
+ return r_str;
169
182
  }
170
183
  case JS_TAG_OBJECT:
171
184
  {
172
- int promiseState = JS_PromiseState(ctx, jsv);
173
- if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING)
185
+ int promiseState = JS_PromiseState(ctx, j_val);
186
+ if (promiseState != -1)
174
187
  {
175
- JSValue awaited = js_std_await(ctx, jsv);
176
- VALUE rb_awaited = to_rb_value(awaited, ctx); // TODO: should have timeout
177
- JS_FreeValue(ctx, awaited);
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 global = JS_GetGlobalObject(ctx);
191
- JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
192
- JSValue stringifyFunc = JS_GetPropertyStr(ctx, jsonClass, "stringify");
193
- JSValue strigified = JS_Call(ctx, stringifyFunc, jsonClass, 1, &jsv);
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, strigified);
196
- VALUE rbString = rb_str_new2(msg);
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, global);
200
- JS_FreeValue(ctx, strigified);
201
- JS_FreeValue(ctx, stringifyFunc);
202
- JS_FreeValue(ctx, jsonClass);
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, rbString);
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 exceptionVal = JS_GetException(ctx);
213
- if (JS_IsError(ctx, exceptionVal))
215
+ JSValue j_exceptionVal = JS_GetException(ctx);
216
+ if (JS_IsError(ctx, j_exceptionVal))
214
217
  {
215
- JSValue jsErrorClassName = JS_GetPropertyStr(ctx, exceptionVal, "name");
216
- const char *errorClassName = JS_ToCString(ctx, jsErrorClassName);
218
+ JSValue j_errorClassName = JS_GetPropertyStr(ctx, j_exceptionVal, "name");
219
+ const char *errorClassName = JS_ToCString(ctx, j_errorClassName);
217
220
 
218
- JSValue jsErrorClassMessage = JS_GetPropertyStr(ctx, exceptionVal, "message");
219
- const char *errorClassMessage = JS_ToCString(ctx, jsErrorClassMessage);
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, jsErrorClassMessage);
222
- JS_FreeValue(ctx, jsErrorClassName);
224
+ JS_FreeValue(ctx, j_errorClassMessage);
225
+ JS_FreeValue(ctx, j_errorClassName);
223
226
 
224
- VALUE rb_errorMessage = rb_sprintf("%s: %s", errorClassName, errorClassMessage);
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, exceptionVal);
228
- rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, rb_errorMessage));
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, exceptionVal);
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, exceptionVal);
237
- rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, rb_errorMessage));
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 toStringFunc = JS_GetPropertyStr(ctx, jsv, "toString");
244
- JSValue strigified = JS_Call(ctx, toStringFunc, jsv, 0, NULL);
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, strigified);
247
- VALUE rbString = rb_str_new2(msg);
248
- JS_FreeValue(ctx, toStringFunc);
249
- JS_FreeValue(ctx, strigified);
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(rbString, rb_intern("to_i"), 0, NULL);
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 maybeFuncName = JS_ToString(ctx, argv[0]);
266
- const char *funcName = JS_ToCString(ctx, maybeFuncName);
267
- JS_FreeValue(ctx, maybeFuncName);
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 proc = rb_hash_aref(data->defined_functions, rb_str_new2(funcName));
270
- if (proc == Qnil)
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
- // TODO: cover timeout for calling proc
277
- VALUE r_result = rb_apply(proc, rb_intern("call"), to_rb_value(argv[1], ctx));
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 self)
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 r_memoryLimit = rb_hash_aref(r_opts, ID2SYM(rb_intern("memory_limit")));
289
- if (NIL_P(r_memoryLimit))
290
- r_memoryLimit = UINT2NUM(1024 * 1024 * 128);
291
- VALUE r_maxStackSize = rb_hash_aref(r_opts, ID2SYM(rb_intern("max_stack_size")));
292
- if (NIL_P(r_maxStackSize))
293
- r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
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(self, VMData, &vm_type, data);
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(r_memoryLimit));
309
- JS_SetMaxStackSize(runtime, NUM2UINT(r_maxStackSize));
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 stdEval = JS_Eval(data->context, enableStd, strlen(enableStd), "<vm>", JS_EVAL_TYPE_MODULE);
326
- JS_FreeValue(data->context, stdEval);
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 osEval = JS_Eval(data->context, enableOs, strlen(enableOs), "<vm>", JS_EVAL_TYPE_MODULE);
335
- JS_FreeValue(data->context, osEval);
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 rubyEval = JS_Eval(data->context, setupGlobalRuby, strlen(setupGlobalRuby), "<vm>", JS_EVAL_TYPE_MODULE);
340
- JS_FreeValue(data->context, rubyEval);
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 global = JS_GetGlobalObject(data->context);
343
- JSValue func = JS_NewCFunction(data->context, js_quickjsrb_call_global, "rubyGlobal", 2);
344
- JS_SetPropertyStr(data->context, global, "rubyGlobal", func);
345
- JS_FreeValue(data->context, global);
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 self;
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 self, VALUE r_code)
374
+ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
357
375
  {
358
376
  VMData *data;
359
- TypedData_Get_Struct(self, VMData, &vm_type, data);
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 codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
366
- VALUE result = to_rb_value(codeResult, data->context);
367
-
368
- JS_FreeValue(data->context, codeResult);
369
- return result;
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 self, VALUE r_name)
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(self, VMData, &vm_type, data);
409
+ TypedData_Get_Struct(r_self, VMData, &vm_type, data);
378
410
 
379
411
  if (rb_block_given_p())
380
412
  {
381
- VALUE proc = rb_block_proc();
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
- rb_hash_aset(data->defined_functions, r_name, proc);
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 codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
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, codeResult);
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)));
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.1.10"
4
+ VERSION = "0.1.11"
5
5
  end
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).eval_code(code)
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.10
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-17 00:00:00.000000000 Z
11
+ date: 2024-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json