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 +4 -4
- data/README.md +38 -22
- data/ext/quickjsrb/quickjsrb.c +36 -28
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +28 -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: 7afae8229fda495f4a4c40c5c3ec1527bbb5491f23cd46ac1cf7d94badaa6a8b
|
4
|
+
data.tar.gz: 62abbf8909cabf1e5d22b7228eda4fe3bd2193122573ad38b6491a7e60c142e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
####
|
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
|
-
|
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
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
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
|
-
|
99
|
-
|
109
|
+
vm = Quickjs::VM.new(timeout_msec: 1_000)
|
110
|
+
```
|
111
|
+
</details>
|
100
112
|
|
101
|
-
#
|
102
|
-
|
103
|
-
|
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
|
-
####
|
123
|
+
#### `Quickjs::VM#define_function`: 💎 Define a global function for JS by Ruby
|
108
124
|
|
109
125
|
```rb
|
110
126
|
vm = Quickjs::VM.new
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
@@ -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(
|
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 =
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
562
|
-
|
563
|
-
|
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,
|
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
|
-
|
609
|
-
|
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);
|
data/lib/quickjs/version.rb
CHANGED
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
|
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-
|
11
|
+
date: 2024-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|