quickjs 0.1.13 → 0.2.1

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: 12a5b713531ad393cb5d16486516f3288eb2b9c88931b8db5fa440a4701393df
4
- data.tar.gz: c780fe075e29223e90a4f0183b02506ed2209ed2f17dd33e9e734e753a9b0b3d
3
+ metadata.gz: 7afae8229fda495f4a4c40c5c3ec1527bbb5491f23cd46ac1cf7d94badaa6a8b
4
+ data.tar.gz: 62abbf8909cabf1e5d22b7228eda4fe3bd2193122573ad38b6491a7e60c142e2
5
5
  SHA512:
6
- metadata.gz: 524a227ede71099f8da470ae3178df0c63fab2613cb3e9a37ec0215d462dab2fbbb52f69449273cf58ffc58758dbab8743be624c75ce0818335e86b312e49bda
7
- data.tar.gz: 9602f54b31a71163ce054aaad3b6da4b5e00d1866b63e98617971d3279ba3a5cef9c8aa2ade1e130dd4499c121c62cc94a8a48dc3eba92a8eaf7bd1a2093ef56
6
+ metadata.gz: 6fad5187a6672299187de72faf8ccb3cc5402858c65a12edb808ae5759c1665ec90c75cbc34194b9d81e28c4ca5b8616de9174cbac060ba74da4509a28b21d85
7
+ data.tar.gz: dd8505b9ac5459fa2a2859cb1108b27861efcd59f234290ec43c4e2dc80e8ee61ea1ae34dba2f013036b495bcf6e9013f0fad0822a5249a43f7eba7cd3864251
data/README.md CHANGED
@@ -36,7 +36,10 @@ Quickjs.eval_code('const obj = {}; obj.missingKey;') # => :undefined (Quickjs::V
36
36
  Quickjs.eval_code("Number('whatever')") #=> :NaN (Quickjs::Value::NAN)
37
37
  ```
38
38
 
39
- #### Limit resources
39
+ <details>
40
+ <summary>Options</summary>
41
+
42
+ #### Resources
40
43
 
41
44
  ```rb
42
45
  # 1GB memory limit
@@ -46,20 +49,21 @@ Quickjs.eval_code(code, { memory_limit: 1024 ** 3 })
46
49
  Quickjs.eval_code(code, { max_stack_size: 1024 ** 2 })
47
50
  ```
48
51
 
49
- #### Enable built-in modules
52
+ #### Built-in modules
53
+
54
+ To enable [std module](https://bellard.org/quickjs/quickjs.html#std-module) and [os module](https://bellard.org/quickjs/quickjs.html#os-module) selectively.
50
55
 
51
56
  ```rb
52
57
  # enable std module
53
- # https://bellard.org/quickjs/quickjs.html#std-module
54
58
  Quickjs.eval_code(code, { features: [Quickjs::MODULE_STD] })
55
59
 
56
60
  # enable os module
57
- # https://bellard.org/quickjs/quickjs.html#os-module
58
61
  Quickjs.eval_code(code, { features: [Quickjs::MODULE_OS] })
59
62
 
60
- # enable timeout features `setTimeout`, `clearTimeout`
63
+ # enable timeout features `setTimeout`, `clearTimeout` from os module specifically
61
64
  Quickjs.eval_code(code, { features: [Quickjs::FEATURES_TIMEOUT] })
62
65
  ```
66
+ </details>
63
67
 
64
68
  ### `Quickjs::VM`: Maintain a consistent VM/runtime
65
69
 
@@ -71,7 +75,10 @@ vm.eval_code('a.b = "d";')
71
75
  vm.eval_code('a.b;') #=> "d"
72
76
  ```
73
77
 
74
- #### Config VM
78
+ <details>
79
+ <summary>Config VM/runtime</summary>
80
+
81
+ #### Resources
75
82
 
76
83
  ```rb
77
84
  vm = Quickjs::VM.new(
@@ -80,31 +87,40 @@ vm = Quickjs::VM.new(
80
87
  )
81
88
  ```
82
89
 
90
+ #### Built-in modules
91
+
92
+ To enable [std module](https://bellard.org/quickjs/quickjs.html#std-module) and [os module](https://bellard.org/quickjs/quickjs.html#os-module) selectively.
93
+
83
94
  ```rb
84
95
  # enable std module
85
- # https://bellard.org/quickjs/quickjs.html#std-module
86
- vm = Quickjs::VM.new(
87
- features: [::Quickjs::MODULE_STD],
88
- )
96
+ vm = Quickjs::VM.new(features: [::Quickjs::MODULE_STD])
89
97
 
90
98
  # enable os module
91
- # https://bellard.org/quickjs/quickjs.html#os-module
92
- vm = Quickjs::VM.new(
93
- features: [::Quickjs::MODULE_OS],
94
- )
99
+ vm = Quickjs::VM.new(features: [::Quickjs::MODULE_OS])
100
+
101
+ # enable timeout features `setTimeout`, `clearTimeout`
102
+ vm = Quickjs::VM.new(features: [::Quickjs::FEATURES_TIMEOUT])
103
+ ```
95
104
 
105
+ #### VM timeout
106
+
107
+ ```rb
96
108
  # `eval_code` will be interrupted after 1 sec (default: 100 msec)
97
- vm = Quickjs::VM.new(
98
- timeout_msec: 1_000,
99
- )
109
+ vm = Quickjs::VM.new(timeout_msec: 1_000)
110
+ ```
111
+ </details>
100
112
 
101
- # enable timeout features `setTimeout`, `clearTimeout`
102
- vm = Quickjs::VM.new(
103
- features: [::Quickjs::FEATURES_TIMEOUT],
104
- )
113
+ #### `Quickjs::VM#import`: 🔌 Import ESM from a source code
114
+
115
+ ```rb
116
+ vm = Quickjs::VM.new
117
+ vm.import({ default: 'aliasedDefault', member: 'member' }, from: File.read('exports.esm.js'))
118
+
119
+ vm.eval_code("aliasedDefault()") #=> Exported `default` of the ESM is called
120
+ vm.eval_code("member()") #=> Exported `member` of the ESM is called
105
121
  ```
106
122
 
107
- #### ⚡️ Define a global function for JS by Ruby
123
+ #### `Quickjs::VM#define_function`: 💎 Define a global function for JS by Ruby
108
124
 
109
125
  ```rb
110
126
  vm = Quickjs::VM.new
@@ -191,7 +191,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
191
191
  if (promiseState != -1)
192
192
  {
193
193
  VALUE r_error_message = rb_str_new2("cannot translate a Promise to Ruby. await within JavaScript's end");
194
- rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
194
+ rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
195
195
  return Qnil;
196
196
  }
197
197
 
@@ -229,43 +229,35 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
229
229
  JS_FreeValue(ctx, j_errorClassMessage);
230
230
  JS_FreeValue(ctx, j_errorClassName);
231
231
 
232
- VALUE r_error_message = rb_sprintf("%s: %s", errorClassName, errorClassMessage);
233
- VALUE r_error_class = rb_eRuntimeError;
232
+ VALUE r_error_class, r_error_message = rb_str_new2(errorClassMessage);
234
233
 
235
234
  if (strcmp(errorClassName, "SyntaxError") == 0)
236
235
  {
237
236
  r_error_class = rb_cQuickjsSyntaxError;
238
- r_error_message = rb_str_new2(errorClassMessage);
239
237
  }
240
238
  else if (strcmp(errorClassName, "TypeError") == 0)
241
239
  {
242
240
  r_error_class = rb_cQuickjsTypeError;
243
- r_error_message = rb_str_new2(errorClassMessage);
244
241
  }
245
242
  else if (strcmp(errorClassName, "ReferenceError") == 0)
246
243
  {
247
244
  r_error_class = rb_cQuickjsReferenceError;
248
- r_error_message = rb_str_new2(errorClassMessage);
249
245
  }
250
246
  else if (strcmp(errorClassName, "RangeError") == 0)
251
247
  {
252
248
  r_error_class = rb_cQuickjsRangeError;
253
- r_error_message = rb_str_new2(errorClassMessage);
254
249
  }
255
250
  else if (strcmp(errorClassName, "EvalError") == 0)
256
251
  {
257
252
  r_error_class = rb_cQuickjsEvalError;
258
- r_error_message = rb_str_new2(errorClassMessage);
259
253
  }
260
254
  else if (strcmp(errorClassName, "URIError") == 0)
261
255
  {
262
256
  r_error_class = rb_cQuickjsURIError;
263
- r_error_message = rb_str_new2(errorClassMessage);
264
257
  }
265
258
  else if (strcmp(errorClassName, "AggregateError") == 0)
266
259
  {
267
260
  r_error_class = rb_cQuickjsAggregateError;
268
- r_error_message = rb_str_new2(errorClassMessage);
269
261
  }
270
262
  else if (strcmp(errorClassName, "InternalError") == 0 && strstr(errorClassMessage, "interrupted") != NULL)
271
263
  {
@@ -275,13 +267,16 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
275
267
  else if (strcmp(errorClassName, "Quickjs::InterruptedError") == 0)
276
268
  {
277
269
  r_error_class = rb_cQuickjsInterruptedError;
278
- r_error_message = rb_str_new2(errorClassMessage);
270
+ }
271
+ else
272
+ {
273
+ r_error_class = rb_cQuickjsRuntimeError;
279
274
  }
280
275
  JS_FreeCString(ctx, errorClassName);
281
276
  JS_FreeCString(ctx, errorClassMessage);
282
277
  JS_FreeValue(ctx, j_exceptionVal);
283
278
 
284
- rb_exc_raise(rb_exc_new_str(r_error_class, r_error_message));
279
+ rb_exc_raise(rb_funcall(r_error_class, rb_intern("new"), 2, r_error_message, rb_str_new2(errorClassName)));
285
280
  }
286
281
  else // exception without Error object
287
282
  {
@@ -290,7 +285,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
290
285
 
291
286
  JS_FreeCString(ctx, errorMessage);
292
287
  JS_FreeValue(ctx, j_exceptionVal);
293
- rb_exc_raise(rb_exc_new_str(rb_cQuickjsRuntimeError, r_error_message));
288
+ rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
294
289
  }
295
290
  return Qnil;
296
291
  }
@@ -490,7 +485,7 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
490
485
  JS_FreeValue(data->context, j_returnedValue);
491
486
  JS_FreeValue(data->context, j_awaitedResult);
492
487
  VALUE r_error_message = rb_str_new2("An unawaited Promise was returned to the top-level");
493
- rb_exc_raise(rb_exc_new_str(rb_cQuickjsNoAwaitError, r_error_message));
488
+ rb_exc_raise(rb_funcall(rb_cQuickjsNoAwaitError, rb_intern("new"), 2, r_error_message, Qnil));
494
489
  return Qnil;
495
490
  }
496
491
  else
@@ -530,22 +525,17 @@ static VALUE vm_m_defineGlobalFunction(VALUE r_self, VALUE r_name)
530
525
  return Qnil;
531
526
  }
532
527
 
533
- // WISH: vm.import('hey', from: '...source...') imports just default
534
- // WISH: vm.import('{ member, member2 }', from: '...source...')
535
- // WISH: vm.import('{ member as aliasedName }', from: '...source...')
536
- // WISH: vm.import('defaultMember, { member }', from: '...source...')
537
- // WISH: vm.import('* as all', from: '...source...')
538
528
  static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
539
529
  {
540
530
  VALUE r_import_string, r_opts;
541
531
  rb_scan_args(argc, argv, "10:", &r_import_string, &r_opts);
542
532
  if (NIL_P(r_opts))
543
533
  r_opts = rb_hash_new();
544
- VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from")));
534
+ VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from"))); // TODO: Use kwargs instead
545
535
  if (NIL_P(r_from))
546
536
  {
547
537
  VALUE r_error_message = rb_str_new2("missing import source");
548
- rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, r_error_message));
538
+ rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
549
539
  return Qnil;
550
540
  }
551
541
 
@@ -553,16 +543,25 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
553
543
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
554
544
 
555
545
  char *source = StringValueCStr(r_from);
556
- char *import_name = StringValueCStr(r_import_string);
557
546
  JSValue func = JS_Eval(data->context, source, strlen(source), "mmmodule", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
558
547
  js_module_set_import_meta(data->context, func, TRUE, FALSE);
559
548
  JS_FreeValue(data->context, func);
560
549
 
561
- const char *importAndGlobalizeModule = "import * as %s from 'mmmodule';\n"
562
- "globalThis['%s'] = %s;\n";
563
- int length = snprintf(NULL, 0, importAndGlobalizeModule, import_name, import_name, import_name);
550
+ VALUE r_import_settings = rb_funcall(
551
+ rb_const_get(rb_cClass, rb_intern("Quickjs")),
552
+ rb_intern("_build_import"),
553
+ 1,
554
+ r_import_string);
555
+ VALUE r_import_name = rb_ary_entry(r_import_settings, 0);
556
+ char *import_name = StringValueCStr(r_import_name);
557
+ VALUE r_globalize = rb_ary_entry(r_import_settings, 1);
558
+ char *globalize = StringValueCStr(r_globalize);
559
+
560
+ const char *importAndGlobalizeModule = "import %s from 'mmmodule';\n"
561
+ "%s\n";
562
+ int length = snprintf(NULL, 0, importAndGlobalizeModule, import_name, globalize);
564
563
  char *result = (char *)malloc(length + 1);
565
- snprintf(result, length + 1, importAndGlobalizeModule, import_name, import_name, import_name);
564
+ snprintf(result, length + 1, importAndGlobalizeModule, import_name, globalize);
566
565
 
567
566
  JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
568
567
  free(result);
@@ -605,8 +604,15 @@ static VALUE vm_m_to_s(VALUE r_self)
605
604
  return rb_funcall(r_ary, rb_intern("join"), 1, rb_str_new2(" "));
606
605
  }
607
606
 
608
- RUBY_FUNC_EXPORTED void
609
- Init_quickjsrb(void)
607
+ VALUE vm_m_initialize_quickjs_error(VALUE self, VALUE r_message, VALUE r_js_name)
608
+ {
609
+ rb_call_super(1, &r_message);
610
+ rb_iv_set(self, "@js_name", r_js_name);
611
+
612
+ return self;
613
+ }
614
+
615
+ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
610
616
  {
611
617
  VALUE rb_mQuickjs = rb_define_module("Quickjs");
612
618
  rb_define_const(rb_mQuickjs, "MODULE_STD", ID2SYM(rb_intern(featureStdId)));
@@ -632,6 +638,8 @@ Init_quickjsrb(void)
632
638
  rb_define_method(rb_cQuickjsVMLog, "inspect", vm_m_to_s, 0);
633
639
 
634
640
  rb_cQuickjsRuntimeError = rb_define_class_under(rb_mQuickjs, "RuntimeError", rb_eRuntimeError);
641
+ rb_define_method(rb_cQuickjsRuntimeError, "initialize", vm_m_initialize_quickjs_error, 2);
642
+ rb_define_attr(rb_cQuickjsRuntimeError, "js_name", 1, 0);
635
643
 
636
644
  rb_cQuickjsSyntaxError = rb_define_class_under(rb_mQuickjs, "SyntaxError", rb_cQuickjsRuntimeError);
637
645
  rb_cQuickjsTypeError = rb_define_class_under(rb_mQuickjs, "TypeError", rb_cQuickjsRuntimeError);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.1.13"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/quickjs.rb CHANGED
@@ -17,9 +17,36 @@ module Quickjs
17
17
  def _with_timeout(msec, proc, args)
18
18
  Timeout.timeout(msec / 1_000.0) { proc.call(*args) }
19
19
  rescue Timeout::Error
20
- Quickjs::InterruptedError.new('Ruby runtime got timeout')
20
+ Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
21
21
  rescue => e
22
22
  e
23
23
  end
24
24
  module_function :_with_timeout
25
+
26
+ def _build_import(imported)
27
+ code_define_global = ->(name) { "globalThis['#{name}'] = #{name};" }
28
+ case imported
29
+ in String if matched = imported.match(/\* as (.+)/)
30
+ [imported, code_define_global.call(matched[1])]
31
+ in String
32
+ [imported, code_define_global.call(imported)]
33
+ in [*all] if all.all? {|e| e.is_a? String }
34
+ [
35
+ imported.join(', ').yield_self{|s| '{ %s }' % s },
36
+ imported.map(&code_define_global).join("\n")
37
+ ]
38
+ in { ** }
39
+ imports, aliases = imported.to_a.map do |imp|
40
+ ["#{imp[0]} as #{imp[1]}", imp[1].to_s]
41
+ end.transpose
42
+
43
+ [
44
+ imports.join(', ').yield_self{|s| '{ %s }' % s },
45
+ aliases.map(&code_define_global).join("\n")
46
+ ]
47
+ else
48
+ raise 'Unsupported importing style'
49
+ end
50
+ end
51
+ module_function :_build_import
25
52
  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.13
4
+ version: 0.2.1
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-02 00:00:00.000000000 Z
11
+ date: 2024-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json