quickjs 0.2.2 → 0.3.0

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: 0c2a481a2ea280bc0d4a9c7778b62035991bd08293ddef98b4824a40a6a6ccd9
4
- data.tar.gz: a40b2f11813f2f82db4c5b9e0931199013671e0983e80566f0b6645709deece7
3
+ metadata.gz: ecc8aa4eddd6aca97baf0e773c4880b4479b4ac4597e0edf811d256eeb61aeb9
4
+ data.tar.gz: 37c8eeef2cad6e0d7773e89759c7d2d1659219247883cfd1817f2603ffbf1543
5
5
  SHA512:
6
- metadata.gz: f509a918c4e5ff7d19bdaa8873ee62ede64cfd98e405a7ea6f4eb00a724fc79fc2cd928e2388d0f8e07dcd455834ba1f60ec2a9990b4781bd0012ec5f72a9c16
7
- data.tar.gz: b7604b888bc62fe12f6a5b30387891108a24cf1de55e77934cabeeab20fe9ae27f494098448a32a975e9fde99cee9a9af5bdd58c258a0bafb60adc125cedf206
6
+ metadata.gz: 2728bcbae59fae1138bdb5aef4e2f22290db93ce6c1e81f031e25c43108287a4f4d93d20afcfd9cb19a0e55593e1ccdadf1d83ebfbb430ee802368b73994cc2b
7
+ data.tar.gz: d06939ece851f9f4fb1311846826cad5c92e594511948ac9659bb190ebe6f905fdce15b4b9d16cf5df3b08bc5483a6f27d847f3ddf752aaa18bfcf74fec087e1
@@ -1,22 +1,22 @@
1
1
  #include "quickjsrb.h"
2
2
 
3
- JSValue to_js_value(JSContext *ctx, VALUE r_value)
3
+ JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
4
4
  {
5
- if (RTEST(rb_funcall(
6
- r_value,
7
- rb_intern("is_a?"),
8
- 1, rb_const_get(rb_cClass, rb_intern("Exception")))))
9
- {
10
- JSValue j_error = JS_NewError(ctx);
11
- VALUE r_str = rb_funcall(r_value, rb_intern("message"), 0, NULL);
12
- char *exceptionMessage = StringValueCStr(r_str);
13
- VALUE r_exception_name = rb_funcall(rb_funcall(r_value, rb_intern("class"), 0, NULL), rb_intern("name"), 0, NULL);
14
- char *exceptionName = StringValueCStr(r_exception_name);
15
- JS_SetPropertyStr(ctx, j_error, "name", JS_NewString(ctx, exceptionName));
16
- JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, exceptionMessage));
17
- return JS_Throw(ctx, j_error);
18
- }
5
+ JSValue j_error = JS_NewError(ctx); // may wanna have custom error class to determine in JS' end
6
+
7
+ VALUE r_object_id = rb_funcall(r_error, rb_intern("object_id"), 0, NULL);
8
+ int objectId = NUM2INT(r_object_id);
9
+ JS_SetPropertyStr(ctx, j_error, "rb_object_id", JS_NewInt32(ctx, objectId));
10
+
11
+ VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0, NULL);
12
+ const char *errorMessage = StringValueCStr(r_exception_message);
13
+ JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, errorMessage));
14
+
15
+ return j_error;
16
+ }
19
17
 
18
+ JSValue to_js_value(JSContext *ctx, VALUE r_value)
19
+ {
20
20
  switch (TYPE(r_value))
21
21
  {
22
22
  case T_NIL:
@@ -72,6 +72,13 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
72
72
  }
73
73
  default:
74
74
  {
75
+ if (TYPE(r_value) == T_OBJECT && RTEST(rb_funcall(
76
+ r_value,
77
+ rb_intern("is_a?"),
78
+ 1, rb_const_get(rb_cClass, rb_intern("Exception")))))
79
+ {
80
+ return j_error_from_ruby_error(ctx, r_value);
81
+ }
75
82
  VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
76
83
  char *str = StringValueCStr(r_inspect_str);
77
84
 
@@ -80,6 +87,11 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
80
87
  }
81
88
  }
82
89
 
90
+ VALUE r_try_json_parse(VALUE r_str)
91
+ {
92
+ return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
93
+ }
94
+
83
95
  VALUE to_rb_value(JSContext *ctx, JSValue j_val)
84
96
  {
85
97
  switch (JS_VALUE_GET_NORM_TAG(j_val))
@@ -121,6 +133,23 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
121
133
  return Qnil;
122
134
  }
123
135
 
136
+ if (JS_IsError(ctx, j_val))
137
+ {
138
+ JSValue j_errorOriginalRubyObjectId = JS_GetPropertyStr(ctx, j_val, "rb_object_id");
139
+ int errorOriginalRubyObjectId = 0;
140
+ if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT)
141
+ {
142
+ JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
143
+ JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
144
+ if (errorOriginalRubyObjectId > 0)
145
+ {
146
+ // may be nice if cover the case of object is missing
147
+ return rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId));
148
+ }
149
+ }
150
+ // will support other errors
151
+ }
152
+
124
153
  JSValue j_global = JS_GetGlobalObject(ctx);
125
154
  JSValue j_jsonClass = JS_GetPropertyStr(ctx, j_global, "JSON");
126
155
  JSValue j_stringifyFunc = JS_GetPropertyStr(ctx, j_jsonClass, "stringify");
@@ -135,7 +164,21 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
135
164
  JS_FreeValue(ctx, j_jsonClass);
136
165
  JS_FreeValue(ctx, j_global);
137
166
 
138
- return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
167
+ if (rb_funcall(r_str, rb_intern("=="), 1, rb_str_new2("undefined")))
168
+ {
169
+ return QUICKJSRB_SYM(undefinedId);
170
+ }
171
+
172
+ int couldntParse;
173
+ VALUE r_result = rb_protect(r_try_json_parse, r_str, &couldntParse);
174
+ if (couldntParse)
175
+ {
176
+ return Qnil;
177
+ }
178
+ else
179
+ {
180
+ return r_result;
181
+ }
139
182
  }
140
183
  case JS_TAG_NULL:
141
184
  return Qnil;
@@ -146,6 +189,21 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
146
189
  JSValue j_exceptionVal = JS_GetException(ctx);
147
190
  if (JS_IsError(ctx, j_exceptionVal))
148
191
  {
192
+ JSValue j_errorOriginalRubyObjectId = JS_GetPropertyStr(ctx, j_exceptionVal, "rb_object_id");
193
+ int errorOriginalRubyObjectId = 0;
194
+ if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT)
195
+ {
196
+ JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
197
+ JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
198
+ if (errorOriginalRubyObjectId > 0)
199
+ {
200
+ JS_FreeValue(ctx, j_exceptionVal);
201
+ // may be nice if cover the case of object is missing
202
+ rb_exc_raise(rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId)));
203
+ return Qnil;
204
+ }
205
+ }
206
+
149
207
  JSValue j_errorClassName = JS_GetPropertyStr(ctx, j_exceptionVal, "name");
150
208
  const char *errorClassName = JS_ToCString(ctx, j_errorClassName);
151
209
 
@@ -211,6 +269,18 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
211
269
  }
212
270
  }
213
271
 
272
+ static VALUE r_try_call_proc(VALUE r_try_args)
273
+ {
274
+ return rb_funcall(
275
+ rb_const_get(rb_cClass, rb_intern("Quickjs")),
276
+ rb_intern("_with_timeout"),
277
+ 3,
278
+ RARRAY_AREF(r_try_args, 2), // timeout
279
+ RARRAY_AREF(r_try_args, 0), // proc
280
+ RARRAY_AREF(r_try_args, 1) // args for proc
281
+ );
282
+ }
283
+
214
284
  static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv)
215
285
  {
216
286
  VMData *data = JS_GetContextOpaque(ctx);
@@ -223,17 +293,27 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
223
293
  { // Shouldn't happen
224
294
  return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
225
295
  }
226
- JS_FreeCString(ctx, funcName);
227
296
 
228
- VALUE r_result = rb_funcall(
229
- rb_const_get(rb_cClass, rb_intern("Quickjs")),
230
- rb_intern("_with_timeout"),
231
- 3,
232
- ULONG2NUM(data->eval_time->limit * 1000 / CLOCKS_PER_SEC),
233
- r_proc,
234
- to_rb_value(ctx, argv[1]));
297
+ VALUE r_call_args = rb_ary_new();
298
+ rb_ary_push(r_call_args, r_proc);
299
+ rb_ary_push(r_call_args, to_rb_value(ctx, argv[1]));
300
+ rb_ary_push(r_call_args, ULONG2NUM(data->eval_time->limit * 1000 / CLOCKS_PER_SEC));
235
301
 
236
- return to_js_value(ctx, r_result);
302
+ int sadnessHappened;
303
+ VALUE r_result = rb_protect(r_try_call_proc, r_call_args, &sadnessHappened);
304
+ JSValue j_result;
305
+ if (sadnessHappened)
306
+ {
307
+ VALUE r_error = rb_errinfo();
308
+ JSValue j_error = j_error_from_ruby_error(ctx, r_error);
309
+ return JS_Throw(ctx, j_error);
310
+ }
311
+ else
312
+ {
313
+ j_result = to_js_value(ctx, r_result);
314
+ }
315
+ JS_FreeCString(ctx, funcName);
316
+ return j_result;
237
317
  }
238
318
 
239
319
  static JSValue js_quickjsrb_log(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv)
@@ -412,30 +492,34 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
412
492
  }
413
493
  }
414
494
 
415
- static VALUE vm_m_defineGlobalFunction(VALUE r_self, VALUE r_name)
495
+ static VALUE vm_m_defineGlobalFunction(int argc, VALUE *argv, VALUE r_self)
416
496
  {
417
497
  rb_need_block();
418
498
 
499
+ VALUE r_name;
500
+ VALUE r_flags;
501
+ VALUE r_block;
502
+ rb_scan_args(argc, argv, "10*&", &r_name, &r_flags, &r_block);
503
+
504
+ const char *asyncKeyword =
505
+ RTEST(rb_funcall(r_flags, rb_intern("include?"), 1, ID2SYM(rb_intern("async")))) ? "async " : "";
506
+
419
507
  VMData *data;
420
508
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
421
509
 
422
- if (rb_block_given_p())
423
- {
424
- VALUE r_proc = rb_block_proc();
425
- rb_hash_aset(data->defined_functions, r_name, r_proc);
426
- char *funcName = StringValueCStr(r_name);
510
+ rb_hash_aset(data->defined_functions, r_name, r_block);
511
+ char *funcName = StringValueCStr(r_name);
427
512
 
428
- const char *template = "globalThis['%s'] = (...args) => __quickjsrb.runRubyMethod('%s', args);\n";
429
- int length = snprintf(NULL, 0, template, funcName, funcName);
430
- char *result = (char *)malloc(length + 1);
431
- snprintf(result, length + 1, template, funcName, funcName);
513
+ const char *template = "globalThis['%s'] = %s(...args) => __quickjsrb.runRubyMethod('%s', args);\n";
514
+ int length = snprintf(NULL, 0, template, funcName, asyncKeyword, funcName);
515
+ char *result = (char *)malloc(length + 1);
516
+ snprintf(result, length + 1, template, funcName, asyncKeyword, funcName);
432
517
 
433
- JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
518
+ JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
434
519
 
435
- free(result);
436
- JS_FreeValue(data->context, j_codeResult);
437
- return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);
438
- }
520
+ free(result);
521
+ JS_FreeValue(data->context, j_codeResult);
522
+ return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);
439
523
 
440
524
  return Qnil;
441
525
  }
@@ -446,13 +530,14 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
446
530
  rb_scan_args(argc, argv, "10:", &r_import_string, &r_opts);
447
531
  if (NIL_P(r_opts))
448
532
  r_opts = rb_hash_new();
449
- VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from"))); // TODO: Use kwargs instead
533
+ VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from")));
450
534
  if (NIL_P(r_from))
451
535
  {
452
536
  VALUE r_error_message = rb_str_new2("missing import source");
453
537
  rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
454
538
  return Qnil;
455
539
  }
540
+ VALUE r_custom_exposure = rb_hash_aref(r_opts, ID2SYM(rb_intern("code_to_expose")));
456
541
 
457
542
  VMData *data;
458
543
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
@@ -470,8 +555,16 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
470
555
  r_import_string);
471
556
  VALUE r_import_name = rb_ary_entry(r_import_settings, 0);
472
557
  char *import_name = StringValueCStr(r_import_name);
473
- VALUE r_globalize = rb_ary_entry(r_import_settings, 1);
474
- char *globalize = StringValueCStr(r_globalize);
558
+ VALUE r_default_exposure = rb_ary_entry(r_import_settings, 1);
559
+ char *globalize;
560
+ if (RTEST(r_custom_exposure))
561
+ {
562
+ globalize = StringValueCStr(r_custom_exposure);
563
+ }
564
+ else
565
+ {
566
+ globalize = StringValueCStr(r_default_exposure);
567
+ }
475
568
 
476
569
  const char *importAndGlobalizeModule = "import %s from '%s';\n"
477
570
  "%s\n";
@@ -507,7 +600,7 @@ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
507
600
  rb_define_alloc_func(r_class_vm, vm_alloc);
508
601
  rb_define_method(r_class_vm, "initialize", vm_m_initialize, -1);
509
602
  rb_define_method(r_class_vm, "eval_code", vm_m_evalCode, 1);
510
- rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, 1);
603
+ rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, -1);
511
604
  rb_define_method(r_class_vm, "import", vm_m_import, -1);
512
605
  rb_define_method(r_class_vm, "logs", vm_m_logs, 0);
513
606
  r_define_log_class(r_class_vm);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/quickjs.rb CHANGED
@@ -16,9 +16,9 @@ module Quickjs
16
16
  def _with_timeout(msec, proc, args)
17
17
  Timeout.timeout(msec / 1_000.0) { proc.call(*args) }
18
18
  rescue Timeout::Error
19
- Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
20
- rescue => e
21
- e
19
+ raise Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
20
+ rescue
21
+ raise
22
22
  end
23
23
  module_function :_with_timeout
24
24
 
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.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hmsk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-29 00:00:00.000000000 Z
11
+ date: 2024-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json