quickjs 0.12.0 → 0.13.0.pre
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/Rakefile +8 -1
- data/ext/quickjsrb/extconf.rb +6 -0
- data/ext/quickjsrb/quickjsrb.c +121 -6
- data/ext/quickjsrb/quickjsrb.h +32 -32
- data/ext/quickjsrb/quickjsrb_file.c +332 -0
- data/ext/quickjsrb/quickjsrb_file.h +16 -0
- data/ext/quickjsrb/vendor/polyfill-encoding.min.js +1 -0
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +8 -0
- data/polyfills/package-lock.json +2 -2
- data/polyfills/package.json +1 -1
- data/polyfills/rolldown.config.mjs +8 -0
- data/polyfills/src/encoding.js +231 -0
- data/sig/quickjs.rbs +69 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f27d3afc594731cc4df53661889a9787626121923ece35c544b581ef3f52722c
|
|
4
|
+
data.tar.gz: d3869683f9134339d1d2e44617845f8ece614e1503926841fb9d4ad6de3dc7ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b5cfcad98315e8821644aa233ea86213f855900506767ccce01cb0c1a653326456e3d00c453d0821117e31eee543ec440ed1e4583dd6b420a62e49ffcb53e080
|
|
7
|
+
data.tar.gz: 7df6cc20b91fce4c14e2a2b96b31bb447ee50eba03498a99e336c8b35d62164a05874ed7753509a4d0c1958761f96c5c85f4de66c6301b79eecbe208dbcf8212
|
data/Rakefile
CHANGED
|
@@ -71,4 +71,11 @@ end
|
|
|
71
71
|
|
|
72
72
|
task 'release:guard_clean' => 'polyfills:version:check'
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
namespace :rbs do
|
|
75
|
+
desc 'Validate RBS type definitions'
|
|
76
|
+
task :validate do
|
|
77
|
+
sh RbConfig.ruby, '-S', 'rbs', '-I', 'sig', 'validate'
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
task default: %i[clobber compile test rbs:validate]
|
data/ext/quickjsrb/extconf.rb
CHANGED
|
@@ -13,7 +13,9 @@ $srcs = [
|
|
|
13
13
|
'quickjs-libc.c',
|
|
14
14
|
'polyfill-intl-en.min.c',
|
|
15
15
|
'polyfill-file.min.c',
|
|
16
|
+
'polyfill-encoding.min.c',
|
|
16
17
|
'quickjsrb.c',
|
|
18
|
+
'quickjsrb_file.c',
|
|
17
19
|
]
|
|
18
20
|
|
|
19
21
|
append_cflags('-I$(srcdir)/quickjs')
|
|
@@ -66,6 +68,10 @@ polyfill-file.min.js:
|
|
|
66
68
|
$(COPY) $(srcdir)/vendor/$@ $@
|
|
67
69
|
polyfill-file.min.c: ./qjsc polyfill-file.min.js
|
|
68
70
|
./qjsc -fno-string-normalize -fno-eval -fno-proxy -fno-module-loader -c -M polyfill/file.so,file -m -o $@ polyfill-file.min.js
|
|
71
|
+
polyfill-encoding.min.js:
|
|
72
|
+
$(COPY) $(srcdir)/vendor/$@ $@
|
|
73
|
+
polyfill-encoding.min.c: ./qjsc polyfill-encoding.min.js
|
|
74
|
+
./qjsc -fno-string-normalize -fno-eval -fno-proxy -fno-module-loader -c -M polyfill/encoding.so,encoding -m -o $@ polyfill-encoding.min.js
|
|
69
75
|
COMPILE_POLYFILL
|
|
70
76
|
conf
|
|
71
77
|
end
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
#include "quickjsrb.h"
|
|
2
|
+
#include "quickjsrb_file.h"
|
|
3
|
+
|
|
4
|
+
const char *featureStdId = "feature_std";
|
|
5
|
+
const char *featureOsId = "feature_os";
|
|
6
|
+
const char *featureTimeoutId = "feature_timeout";
|
|
7
|
+
const char *featurePolyfillIntlId = "feature_polyfill_intl";
|
|
8
|
+
const char *featurePolyfillFileId = "feature_polyfill_file";
|
|
9
|
+
const char *featurePolyfillEncodingId = "feature_polyfill_encoding";
|
|
10
|
+
|
|
11
|
+
const char *undefinedId = "undefined";
|
|
12
|
+
const char *nanId = "NaN";
|
|
13
|
+
|
|
14
|
+
const char *native_errors[] = {
|
|
15
|
+
"SyntaxError",
|
|
16
|
+
"TypeError",
|
|
17
|
+
"ReferenceError",
|
|
18
|
+
"RangeError",
|
|
19
|
+
"EvalError",
|
|
20
|
+
"URIError",
|
|
21
|
+
"AggregateError"};
|
|
22
|
+
const int num_native_errors = sizeof(native_errors) / sizeof(native_errors[0]);
|
|
23
|
+
|
|
24
|
+
static int dispatch_log(VMData *data, const char *severity, VALUE r_row);
|
|
2
25
|
|
|
3
26
|
JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
|
|
4
27
|
{
|
|
@@ -10,7 +33,7 @@ JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
|
|
|
10
33
|
|
|
11
34
|
// Keep the error alive in VMData to prevent GC before find_ruby_error retrieves it
|
|
12
35
|
VMData *data = JS_GetContextOpaque(ctx);
|
|
13
|
-
rb_hash_aset(data->
|
|
36
|
+
rb_hash_aset(data->alive_objects, r_object_id, r_error);
|
|
14
37
|
|
|
15
38
|
VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0);
|
|
16
39
|
const char *errorMessage = StringValueCStr(r_exception_message);
|
|
@@ -68,6 +91,12 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
|
|
|
68
91
|
}
|
|
69
92
|
default:
|
|
70
93
|
{
|
|
94
|
+
if (rb_obj_is_kind_of(r_value, rb_cFile))
|
|
95
|
+
{
|
|
96
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
|
97
|
+
if (!JS_IsUndefined(data->j_file_proxy_creator))
|
|
98
|
+
return quickjsrb_file_to_js(ctx, r_value);
|
|
99
|
+
}
|
|
71
100
|
if (TYPE(r_value) == T_OBJECT && RTEST(rb_funcall(
|
|
72
101
|
r_value,
|
|
73
102
|
rb_intern("is_a?"),
|
|
@@ -95,8 +124,8 @@ VALUE find_ruby_error(JSContext *ctx, JSValue j_error)
|
|
|
95
124
|
{
|
|
96
125
|
VMData *data = JS_GetContextOpaque(ctx);
|
|
97
126
|
VALUE r_key = INT2NUM(errorOriginalRubyObjectId);
|
|
98
|
-
VALUE r_error = rb_hash_aref(data->
|
|
99
|
-
rb_hash_delete(data->
|
|
127
|
+
VALUE r_error = rb_hash_aref(data->alive_objects, r_key);
|
|
128
|
+
rb_hash_delete(data->alive_objects, r_key);
|
|
100
129
|
return r_error;
|
|
101
130
|
}
|
|
102
131
|
}
|
|
@@ -177,6 +206,36 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
|
|
|
177
206
|
}
|
|
178
207
|
// will support other errors like just returning an instance of Error
|
|
179
208
|
}
|
|
209
|
+
|
|
210
|
+
// Check for Ruby object proxy (e.g., File proxy with rb_object_id on target)
|
|
211
|
+
{
|
|
212
|
+
JSValue j_rb_id = JS_GetPropertyStr(ctx, j_val, "rb_object_id");
|
|
213
|
+
if (JS_VALUE_GET_NORM_TAG(j_rb_id) == JS_TAG_INT || JS_VALUE_GET_NORM_TAG(j_rb_id) == JS_TAG_FLOAT64)
|
|
214
|
+
{
|
|
215
|
+
int64_t object_id;
|
|
216
|
+
JS_ToInt64(ctx, &object_id, j_rb_id);
|
|
217
|
+
JS_FreeValue(ctx, j_rb_id);
|
|
218
|
+
if (object_id > 0)
|
|
219
|
+
{
|
|
220
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
|
221
|
+
VALUE r_obj = rb_hash_aref(data->alive_objects, LONG2NUM(object_id));
|
|
222
|
+
if (!NIL_P(r_obj) && !rb_obj_is_kind_of(r_obj, rb_eException))
|
|
223
|
+
return r_obj;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else
|
|
227
|
+
{
|
|
228
|
+
JS_FreeValue(ctx, j_rb_id);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// JS File → Quickjs::File
|
|
233
|
+
{
|
|
234
|
+
VALUE r_maybe_file = quickjsrb_try_convert_js_file(ctx, j_val);
|
|
235
|
+
if (!NIL_P(r_maybe_file))
|
|
236
|
+
return r_maybe_file;
|
|
237
|
+
}
|
|
238
|
+
|
|
180
239
|
VALUE r_str = to_r_json(ctx, j_val);
|
|
181
240
|
|
|
182
241
|
if (rb_funcall(r_str, rb_intern("=="), 1, rb_str_new2("undefined")))
|
|
@@ -227,7 +286,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
|
|
|
227
286
|
|
|
228
287
|
VMData *data = JS_GetContextOpaque(ctx);
|
|
229
288
|
VALUE r_headline = rb_str_new2(headline);
|
|
230
|
-
|
|
289
|
+
dispatch_log(data, "error", rb_ary_new3(1, r_log_body_new(r_headline, r_headline)));
|
|
231
290
|
|
|
232
291
|
JS_FreeValue(ctx, j_errorClassMessage);
|
|
233
292
|
JS_FreeValue(ctx, j_errorClassName);
|
|
@@ -269,7 +328,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
|
|
|
269
328
|
|
|
270
329
|
VMData *data = JS_GetContextOpaque(ctx);
|
|
271
330
|
VALUE r_headline = rb_str_new2(headline);
|
|
272
|
-
|
|
331
|
+
dispatch_log(data, "error", rb_ary_new3(1, r_log_body_new(r_headline, r_headline)));
|
|
273
332
|
|
|
274
333
|
free(headline);
|
|
275
334
|
|
|
@@ -418,6 +477,43 @@ static JSValue js_quickjsrb_set_timeout(JSContext *ctx, JSValueConst _this, int
|
|
|
418
477
|
return JS_UNDEFINED;
|
|
419
478
|
}
|
|
420
479
|
|
|
480
|
+
static VALUE r_try_call_listener(VALUE r_args)
|
|
481
|
+
{
|
|
482
|
+
VALUE r_listener = RARRAY_AREF(r_args, 0);
|
|
483
|
+
VALUE r_log = RARRAY_AREF(r_args, 1);
|
|
484
|
+
return rb_funcall(r_listener, rb_intern("call"), 1, r_log);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
static int dispatch_log(VMData *data, const char *severity, VALUE r_row)
|
|
488
|
+
{
|
|
489
|
+
VALUE r_log = r_log_new(severity, r_row);
|
|
490
|
+
if (!NIL_P(data->log_listener))
|
|
491
|
+
{
|
|
492
|
+
VALUE r_args = rb_ary_new3(2, data->log_listener, r_log);
|
|
493
|
+
int error;
|
|
494
|
+
rb_protect(r_try_call_listener, r_args, &error);
|
|
495
|
+
if (error)
|
|
496
|
+
return error;
|
|
497
|
+
}
|
|
498
|
+
else
|
|
499
|
+
{
|
|
500
|
+
rb_ary_push(data->logs, r_log);
|
|
501
|
+
}
|
|
502
|
+
return 0;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static VALUE vm_m_on_log(VALUE r_self)
|
|
506
|
+
{
|
|
507
|
+
rb_need_block();
|
|
508
|
+
|
|
509
|
+
VMData *data;
|
|
510
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
511
|
+
|
|
512
|
+
data->log_listener = rb_block_proc();
|
|
513
|
+
|
|
514
|
+
return Qnil;
|
|
515
|
+
}
|
|
516
|
+
|
|
421
517
|
static JSValue js_quickjsrb_log(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv, const char *severity)
|
|
422
518
|
{
|
|
423
519
|
VMData *data = JS_GetContextOpaque(ctx);
|
|
@@ -467,7 +563,14 @@ static JSValue js_quickjsrb_log(JSContext *ctx, JSValueConst _this, int argc, JS
|
|
|
467
563
|
rb_ary_push(r_row, r_log_body_new(r_raw, r_c));
|
|
468
564
|
}
|
|
469
565
|
|
|
470
|
-
|
|
566
|
+
int error = dispatch_log(data, severity, r_row);
|
|
567
|
+
if (error)
|
|
568
|
+
{
|
|
569
|
+
VALUE r_error = rb_errinfo();
|
|
570
|
+
rb_set_errinfo(Qnil);
|
|
571
|
+
JSValue j_error = j_error_from_ruby_error(ctx, r_error);
|
|
572
|
+
return JS_Throw(ctx, j_error);
|
|
573
|
+
}
|
|
471
574
|
return JS_UNDEFINED;
|
|
472
575
|
}
|
|
473
576
|
|
|
@@ -566,6 +669,15 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
|
|
|
566
669
|
JSValue j_polyfillFileObject = JS_ReadObject(data->context, &qjsc_polyfill_file_min, qjsc_polyfill_file_min_size, JS_READ_OBJ_BYTECODE);
|
|
567
670
|
JSValue j_polyfillFileResult = JS_EvalFunction(data->context, j_polyfillFileObject);
|
|
568
671
|
JS_FreeValue(data->context, j_polyfillFileResult);
|
|
672
|
+
|
|
673
|
+
quickjsrb_init_file_proxy(data);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featurePolyfillEncodingId))))
|
|
677
|
+
{
|
|
678
|
+
JSValue j_polyfillEncodingObject = JS_ReadObject(data->context, &qjsc_polyfill_encoding_min, qjsc_polyfill_encoding_min_size, JS_READ_OBJ_BYTECODE);
|
|
679
|
+
JSValue j_polyfillEncodingResult = JS_EvalFunction(data->context, j_polyfillEncodingObject);
|
|
680
|
+
JS_FreeValue(data->context, j_polyfillEncodingResult);
|
|
569
681
|
}
|
|
570
682
|
|
|
571
683
|
JSValue j_console = JS_NewObject(data->context);
|
|
@@ -740,6 +852,8 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
|
|
|
740
852
|
|
|
741
853
|
static VALUE vm_m_logs(VALUE r_self)
|
|
742
854
|
{
|
|
855
|
+
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "Quickjs::VM#logs is deprecated; use Quickjs::VM#on_log instead");
|
|
856
|
+
|
|
743
857
|
VMData *data;
|
|
744
858
|
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
745
859
|
|
|
@@ -762,5 +876,6 @@ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
|
|
|
762
876
|
rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, -1);
|
|
763
877
|
rb_define_method(r_class_vm, "import", vm_m_import, -1);
|
|
764
878
|
rb_define_method(r_class_vm, "logs", vm_m_logs, 0);
|
|
879
|
+
rb_define_method(r_class_vm, "on_log", vm_m_on_log, 0);
|
|
765
880
|
r_define_log_class(r_class_vm);
|
|
766
881
|
}
|
data/ext/quickjsrb/quickjsrb.h
CHANGED
|
@@ -16,24 +16,21 @@ extern const uint32_t qjsc_polyfill_intl_en_min_size;
|
|
|
16
16
|
extern const uint8_t qjsc_polyfill_intl_en_min;
|
|
17
17
|
extern const uint32_t qjsc_polyfill_file_min_size;
|
|
18
18
|
extern const uint8_t qjsc_polyfill_file_min;
|
|
19
|
+
extern const uint32_t qjsc_polyfill_encoding_min_size;
|
|
20
|
+
extern const uint8_t qjsc_polyfill_encoding_min;
|
|
19
21
|
|
|
20
|
-
const char *featureStdId
|
|
21
|
-
const char *featureOsId
|
|
22
|
-
const char *featureTimeoutId
|
|
23
|
-
const char *featurePolyfillIntlId
|
|
24
|
-
const char *featurePolyfillFileId
|
|
22
|
+
extern const char *featureStdId;
|
|
23
|
+
extern const char *featureOsId;
|
|
24
|
+
extern const char *featureTimeoutId;
|
|
25
|
+
extern const char *featurePolyfillIntlId;
|
|
26
|
+
extern const char *featurePolyfillFileId;
|
|
27
|
+
extern const char *featurePolyfillEncodingId;
|
|
25
28
|
|
|
26
|
-
const char *undefinedId
|
|
27
|
-
const char *nanId
|
|
29
|
+
extern const char *undefinedId;
|
|
30
|
+
extern const char *nanId;
|
|
28
31
|
|
|
29
|
-
const char *native_errors[]
|
|
30
|
-
|
|
31
|
-
"TypeError",
|
|
32
|
-
"ReferenceError",
|
|
33
|
-
"RangeError",
|
|
34
|
-
"EvalError",
|
|
35
|
-
"URIError",
|
|
36
|
-
"AggregateError"};
|
|
32
|
+
extern const char *native_errors[];
|
|
33
|
+
extern const int num_native_errors;
|
|
37
34
|
|
|
38
35
|
#define QUICKJSRB_SYM(id) \
|
|
39
36
|
(VALUE) { ID2SYM(rb_intern(id)) }
|
|
@@ -52,7 +49,9 @@ typedef struct VMData
|
|
|
52
49
|
VALUE defined_functions;
|
|
53
50
|
struct EvalTime *eval_time;
|
|
54
51
|
VALUE logs;
|
|
55
|
-
VALUE
|
|
52
|
+
VALUE log_listener;
|
|
53
|
+
VALUE alive_objects;
|
|
54
|
+
JSValue j_file_proxy_creator;
|
|
56
55
|
} VMData;
|
|
57
56
|
|
|
58
57
|
static void vm_free(void *ptr)
|
|
@@ -60,6 +59,9 @@ static void vm_free(void *ptr)
|
|
|
60
59
|
VMData *data = (VMData *)ptr;
|
|
61
60
|
free(data->eval_time);
|
|
62
61
|
|
|
62
|
+
if (!JS_IsUndefined(data->j_file_proxy_creator))
|
|
63
|
+
JS_FreeValue(data->context, data->j_file_proxy_creator);
|
|
64
|
+
|
|
63
65
|
JSRuntime *runtime = JS_GetRuntime(data->context);
|
|
64
66
|
JS_SetInterruptHandler(runtime, NULL, NULL);
|
|
65
67
|
js_std_free_handlers(runtime);
|
|
@@ -69,7 +71,7 @@ static void vm_free(void *ptr)
|
|
|
69
71
|
xfree(ptr);
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
size_t vm_size(const void *data)
|
|
74
|
+
static size_t vm_size(const void *data)
|
|
73
75
|
{
|
|
74
76
|
return sizeof(VMData);
|
|
75
77
|
}
|
|
@@ -79,7 +81,8 @@ static void vm_mark(void *ptr)
|
|
|
79
81
|
VMData *data = (VMData *)ptr;
|
|
80
82
|
rb_gc_mark_movable(data->defined_functions);
|
|
81
83
|
rb_gc_mark_movable(data->logs);
|
|
82
|
-
rb_gc_mark_movable(data->
|
|
84
|
+
rb_gc_mark_movable(data->log_listener);
|
|
85
|
+
rb_gc_mark_movable(data->alive_objects);
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
static void vm_compact(void *ptr)
|
|
@@ -87,7 +90,8 @@ static void vm_compact(void *ptr)
|
|
|
87
90
|
VMData *data = (VMData *)ptr;
|
|
88
91
|
data->defined_functions = rb_gc_location(data->defined_functions);
|
|
89
92
|
data->logs = rb_gc_location(data->logs);
|
|
90
|
-
data->
|
|
93
|
+
data->log_listener = rb_gc_location(data->log_listener);
|
|
94
|
+
data->alive_objects = rb_gc_location(data->alive_objects);
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
static const rb_data_type_t vm_type = {
|
|
@@ -107,7 +111,9 @@ static VALUE vm_alloc(VALUE r_self)
|
|
|
107
111
|
VALUE obj = TypedData_Make_Struct(r_self, VMData, &vm_type, data);
|
|
108
112
|
data->defined_functions = rb_hash_new();
|
|
109
113
|
data->logs = rb_ary_new();
|
|
110
|
-
data->
|
|
114
|
+
data->log_listener = Qnil;
|
|
115
|
+
data->alive_objects = rb_hash_new();
|
|
116
|
+
data->j_file_proxy_creator = JS_UNDEFINED;
|
|
111
117
|
|
|
112
118
|
EvalTime *eval_time = malloc(sizeof(EvalTime));
|
|
113
119
|
data->eval_time = eval_time;
|
|
@@ -132,17 +138,12 @@ static char *random_string()
|
|
|
132
138
|
|
|
133
139
|
static bool is_native_error_name(const char *error_name)
|
|
134
140
|
{
|
|
135
|
-
|
|
136
|
-
int numStrings = sizeof(native_errors) / sizeof(native_errors[0]);
|
|
137
|
-
for (int i = 0; i < numStrings; i++)
|
|
141
|
+
for (int i = 0; i < num_native_errors; i++)
|
|
138
142
|
{
|
|
139
143
|
if (strcmp(native_errors[i], error_name) == 0)
|
|
140
|
-
|
|
141
|
-
is_native_error = true;
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
+
return true;
|
|
144
145
|
}
|
|
145
|
-
return
|
|
146
|
+
return false;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
// Constants
|
|
@@ -154,6 +155,7 @@ static void r_define_constants(VALUE r_parent_class)
|
|
|
154
155
|
rb_define_const(r_parent_class, "FEATURE_TIMEOUT", QUICKJSRB_SYM(featureTimeoutId));
|
|
155
156
|
rb_define_const(r_parent_class, "POLYFILL_INTL", QUICKJSRB_SYM(featurePolyfillIntlId));
|
|
156
157
|
rb_define_const(r_parent_class, "POLYFILL_FILE", QUICKJSRB_SYM(featurePolyfillFileId));
|
|
158
|
+
rb_define_const(r_parent_class, "POLYFILL_ENCODING", QUICKJSRB_SYM(featurePolyfillEncodingId));
|
|
157
159
|
|
|
158
160
|
VALUE rb_cQuickjsValue = rb_define_class_under(r_parent_class, "Value", rb_cObject);
|
|
159
161
|
rb_define_const(rb_cQuickjsValue, "UNDEFINED", QUICKJSRB_SYM(undefinedId));
|
|
@@ -225,7 +227,7 @@ static VALUE r_log_body_new(VALUE r_raw, VALUE r_c)
|
|
|
225
227
|
#define QUICKJSRB_ERROR_FOR(name) \
|
|
226
228
|
(VALUE) { rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern(name)) }
|
|
227
229
|
|
|
228
|
-
VALUE vm_m_initialize_quickjs_error(VALUE self, VALUE r_message, VALUE r_js_name)
|
|
230
|
+
static VALUE vm_m_initialize_quickjs_error(VALUE self, VALUE r_message, VALUE r_js_name)
|
|
229
231
|
{
|
|
230
232
|
rb_call_super(1, &r_message);
|
|
231
233
|
rb_iv_set(self, "@js_name", r_js_name);
|
|
@@ -239,9 +241,7 @@ static void r_define_exception_classes(VALUE r_parent_class)
|
|
|
239
241
|
rb_define_method(r_runtime_error, "initialize", vm_m_initialize_quickjs_error, 2);
|
|
240
242
|
rb_define_attr(r_runtime_error, "js_name", 1, 0);
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
int numStrings = sizeof(native_errors) / sizeof(native_errors[0]);
|
|
244
|
-
for (int i = 0; i < numStrings; i++)
|
|
244
|
+
for (int i = 0; i < num_native_errors; i++)
|
|
245
245
|
{
|
|
246
246
|
rb_define_class_under(r_parent_class, native_errors[i], r_runtime_error);
|
|
247
247
|
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#include "quickjsrb.h"
|
|
2
|
+
#include "quickjsrb_file.h"
|
|
3
|
+
|
|
4
|
+
static VALUE r_find_alive_rb_file(JSContext *ctx, JSValue j_handle)
|
|
5
|
+
{
|
|
6
|
+
int64_t handle;
|
|
7
|
+
JS_ToInt64(ctx, &handle, j_handle);
|
|
8
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
|
9
|
+
return rb_hash_aref(data->alive_objects, LONG2NUM(handle));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static JSValue js_ruby_file_name(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
13
|
+
{
|
|
14
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
15
|
+
if (NIL_P(r_file))
|
|
16
|
+
return JS_UNDEFINED;
|
|
17
|
+
|
|
18
|
+
VALUE r_path = rb_funcall(r_file, rb_intern("path"), 0);
|
|
19
|
+
VALUE r_basename = rb_funcall(rb_cFile, rb_intern("basename"), 1, r_path);
|
|
20
|
+
return JS_NewString(ctx, StringValueCStr(r_basename));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static JSValue js_ruby_file_size(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
24
|
+
{
|
|
25
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
26
|
+
if (NIL_P(r_file))
|
|
27
|
+
return JS_UNDEFINED;
|
|
28
|
+
|
|
29
|
+
VALUE r_size = rb_funcall(r_file, rb_intern("size"), 0);
|
|
30
|
+
return JS_NewInt64(ctx, NUM2LONG(r_size));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static JSValue js_ruby_file_type(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
34
|
+
{
|
|
35
|
+
return JS_NewString(ctx, "");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static JSValue js_ruby_file_last_modified(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
39
|
+
{
|
|
40
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
41
|
+
if (NIL_P(r_file))
|
|
42
|
+
return JS_UNDEFINED;
|
|
43
|
+
|
|
44
|
+
VALUE r_mtime = rb_funcall(r_file, rb_intern("mtime"), 0);
|
|
45
|
+
VALUE r_epoch_f = rb_funcall(r_mtime, rb_intern("to_f"), 0);
|
|
46
|
+
double epoch_ms = NUM2DBL(r_epoch_f) * 1000.0;
|
|
47
|
+
return JS_NewFloat64(ctx, epoch_ms);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static JSValue js_ruby_file_text(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
51
|
+
{
|
|
52
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
53
|
+
if (NIL_P(r_file))
|
|
54
|
+
return JS_UNDEFINED;
|
|
55
|
+
|
|
56
|
+
JSValue promise, resolving_funcs[2];
|
|
57
|
+
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
58
|
+
if (JS_IsException(promise))
|
|
59
|
+
return JS_EXCEPTION;
|
|
60
|
+
|
|
61
|
+
rb_funcall(r_file, rb_intern("rewind"), 0);
|
|
62
|
+
VALUE r_content = rb_funcall(r_file, rb_intern("read"), 0);
|
|
63
|
+
JSValue j_content = JS_NewString(ctx, StringValueCStr(r_content));
|
|
64
|
+
|
|
65
|
+
JSValue ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, &j_content);
|
|
66
|
+
JS_FreeValue(ctx, j_content);
|
|
67
|
+
JS_FreeValue(ctx, ret);
|
|
68
|
+
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
69
|
+
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
70
|
+
|
|
71
|
+
return promise;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static JSValue js_ruby_file_array_buffer(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
75
|
+
{
|
|
76
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
77
|
+
if (NIL_P(r_file))
|
|
78
|
+
return JS_UNDEFINED;
|
|
79
|
+
|
|
80
|
+
JSValue promise, resolving_funcs[2];
|
|
81
|
+
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
82
|
+
if (JS_IsException(promise))
|
|
83
|
+
return JS_EXCEPTION;
|
|
84
|
+
|
|
85
|
+
rb_funcall(r_file, rb_intern("rewind"), 0);
|
|
86
|
+
VALUE r_content = rb_funcall(r_file, rb_intern("read"), 0);
|
|
87
|
+
rb_funcall(r_content, rb_intern("force_encoding"), 1, rb_str_new_cstr("BINARY"));
|
|
88
|
+
long len = RSTRING_LEN(r_content);
|
|
89
|
+
const char *ptr = RSTRING_PTR(r_content);
|
|
90
|
+
|
|
91
|
+
JSValue j_buf = JS_NewArrayBufferCopy(ctx, (const uint8_t *)ptr, len);
|
|
92
|
+
|
|
93
|
+
JSValue ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, &j_buf);
|
|
94
|
+
JS_FreeValue(ctx, j_buf);
|
|
95
|
+
JS_FreeValue(ctx, ret);
|
|
96
|
+
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
97
|
+
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
98
|
+
|
|
99
|
+
return promise;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static JSValue js_ruby_file_slice(JSContext *ctx, JSValueConst _this, int argc, JSValueConst *argv)
|
|
103
|
+
{
|
|
104
|
+
VALUE r_file = r_find_alive_rb_file(ctx, argv[0]);
|
|
105
|
+
if (NIL_P(r_file))
|
|
106
|
+
return JS_UNDEFINED;
|
|
107
|
+
|
|
108
|
+
VALUE r_size = rb_funcall(r_file, rb_intern("size"), 0);
|
|
109
|
+
long file_size = NUM2LONG(r_size);
|
|
110
|
+
|
|
111
|
+
long start = 0;
|
|
112
|
+
if (argc > 1 && !JS_IsUndefined(argv[1]))
|
|
113
|
+
{
|
|
114
|
+
int64_t s;
|
|
115
|
+
JS_ToInt64(ctx, &s, argv[1]);
|
|
116
|
+
start = (long)s;
|
|
117
|
+
if (start < 0)
|
|
118
|
+
start = file_size + start;
|
|
119
|
+
if (start < 0)
|
|
120
|
+
start = 0;
|
|
121
|
+
if (start > file_size)
|
|
122
|
+
start = file_size;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
long end = file_size;
|
|
126
|
+
if (argc > 2 && !JS_IsUndefined(argv[2]))
|
|
127
|
+
{
|
|
128
|
+
int64_t e;
|
|
129
|
+
JS_ToInt64(ctx, &e, argv[2]);
|
|
130
|
+
end = (long)e;
|
|
131
|
+
if (end < 0)
|
|
132
|
+
end = file_size + end;
|
|
133
|
+
if (end < 0)
|
|
134
|
+
end = 0;
|
|
135
|
+
if (end > file_size)
|
|
136
|
+
end = file_size;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const char *content_type = "";
|
|
140
|
+
if (argc > 3 && JS_IsString(argv[3]))
|
|
141
|
+
content_type = JS_ToCString(ctx, argv[3]);
|
|
142
|
+
|
|
143
|
+
long len = end > start ? end - start : 0;
|
|
144
|
+
|
|
145
|
+
rb_funcall(r_file, rb_intern("rewind"), 0);
|
|
146
|
+
if (start > 0)
|
|
147
|
+
rb_funcall(r_file, rb_intern("seek"), 1, LONG2NUM(start));
|
|
148
|
+
VALUE r_bytes = rb_funcall(r_file, rb_intern("read"), 1, LONG2NUM(len));
|
|
149
|
+
rb_funcall(r_bytes, rb_intern("force_encoding"), 1, rb_str_new_cstr("BINARY"));
|
|
150
|
+
|
|
151
|
+
JSValue j_buf = JS_NewArrayBufferCopy(ctx, (const uint8_t *)RSTRING_PTR(r_bytes), RSTRING_LEN(r_bytes));
|
|
152
|
+
JSValue j_global = JS_GetGlobalObject(ctx);
|
|
153
|
+
JSValue j_uint8_ctor = JS_GetPropertyStr(ctx, j_global, "Uint8Array");
|
|
154
|
+
JSValue j_uint8 = JS_CallConstructor(ctx, j_uint8_ctor, 1, &j_buf);
|
|
155
|
+
|
|
156
|
+
JSValue j_parts = JS_NewArray(ctx);
|
|
157
|
+
JS_SetPropertyUint32(ctx, j_parts, 0, j_uint8);
|
|
158
|
+
|
|
159
|
+
JSValue j_opts = JS_NewObject(ctx);
|
|
160
|
+
JS_SetPropertyStr(ctx, j_opts, "type", JS_NewString(ctx, content_type));
|
|
161
|
+
|
|
162
|
+
JSValue j_blob_ctor = JS_GetPropertyStr(ctx, j_global, "Blob");
|
|
163
|
+
JSValueConst blob_args[2] = {j_parts, j_opts};
|
|
164
|
+
JSValue j_blob = JS_CallConstructor(ctx, j_blob_ctor, 2, blob_args);
|
|
165
|
+
|
|
166
|
+
if (argc > 3 && JS_IsString(argv[3]))
|
|
167
|
+
JS_FreeCString(ctx, content_type);
|
|
168
|
+
|
|
169
|
+
JS_FreeValue(ctx, j_buf);
|
|
170
|
+
JS_FreeValue(ctx, j_uint8_ctor);
|
|
171
|
+
JS_FreeValue(ctx, j_parts);
|
|
172
|
+
JS_FreeValue(ctx, j_opts);
|
|
173
|
+
JS_FreeValue(ctx, j_blob_ctor);
|
|
174
|
+
JS_FreeValue(ctx, j_global);
|
|
175
|
+
|
|
176
|
+
return j_blob;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void quickjsrb_init_file_proxy(VMData *data)
|
|
180
|
+
{
|
|
181
|
+
const char *factory_src =
|
|
182
|
+
"(function(getName, getSize, getType, getLastModified, getText, getArrayBuffer, getSlice) {\n"
|
|
183
|
+
" return function(handle) {\n"
|
|
184
|
+
" var target = Object.create(File.prototype);\n"
|
|
185
|
+
" Object.defineProperty(target, 'rb_object_id', { value: handle, enumerable: false });\n"
|
|
186
|
+
" return new Proxy(target, {\n"
|
|
187
|
+
" getPrototypeOf: function() { return File.prototype; },\n"
|
|
188
|
+
" get: function(target, prop, receiver) {\n"
|
|
189
|
+
" if (prop === 'name') return getName(handle);\n"
|
|
190
|
+
" if (prop === 'size') return getSize(handle);\n"
|
|
191
|
+
" if (prop === 'type') return getType(handle);\n"
|
|
192
|
+
" if (prop === 'lastModified') return getLastModified(handle);\n"
|
|
193
|
+
" if (prop === 'text') return function() { return getText(handle); };\n"
|
|
194
|
+
" if (prop === 'arrayBuffer') return function() { return getArrayBuffer(handle); };\n"
|
|
195
|
+
" if (prop === 'slice') return function(start, end, contentType) { return getSlice(handle, start, end, contentType); };\n"
|
|
196
|
+
" if (prop === Symbol.toStringTag) return 'File';\n"
|
|
197
|
+
" if (prop === 'toString') return function() { return '[object File]'; };\n"
|
|
198
|
+
" return Reflect.get(target, prop, receiver);\n"
|
|
199
|
+
" }\n"
|
|
200
|
+
" });\n"
|
|
201
|
+
" };\n"
|
|
202
|
+
"})";
|
|
203
|
+
JSValue j_factory_fn = JS_Eval(data->context, factory_src, strlen(factory_src), "<file-proxy>", JS_EVAL_TYPE_GLOBAL);
|
|
204
|
+
|
|
205
|
+
JSValue j_helpers[7];
|
|
206
|
+
j_helpers[0] = JS_NewCFunction(data->context, js_ruby_file_name, "__rb_file_name", 1);
|
|
207
|
+
j_helpers[1] = JS_NewCFunction(data->context, js_ruby_file_size, "__rb_file_size", 1);
|
|
208
|
+
j_helpers[2] = JS_NewCFunction(data->context, js_ruby_file_type, "__rb_file_type", 1);
|
|
209
|
+
j_helpers[3] = JS_NewCFunction(data->context, js_ruby_file_last_modified, "__rb_file_last_modified", 1);
|
|
210
|
+
j_helpers[4] = JS_NewCFunction(data->context, js_ruby_file_text, "__rb_file_text", 1);
|
|
211
|
+
j_helpers[5] = JS_NewCFunction(data->context, js_ruby_file_array_buffer, "__rb_file_array_buffer", 1);
|
|
212
|
+
j_helpers[6] = JS_NewCFunction(data->context, js_ruby_file_slice, "__rb_file_slice", 4);
|
|
213
|
+
|
|
214
|
+
data->j_file_proxy_creator = JS_Call(data->context, j_factory_fn, JS_UNDEFINED, 7, j_helpers);
|
|
215
|
+
|
|
216
|
+
JS_FreeValue(data->context, j_factory_fn);
|
|
217
|
+
for (int i = 0; i < 7; i++)
|
|
218
|
+
JS_FreeValue(data->context, j_helpers[i]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
JSValue quickjsrb_file_to_js(JSContext *ctx, VALUE r_file)
|
|
222
|
+
{
|
|
223
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
|
224
|
+
VALUE r_object_id = rb_funcall(r_file, rb_intern("object_id"), 0);
|
|
225
|
+
rb_hash_aset(data->alive_objects, r_object_id, r_file);
|
|
226
|
+
JSValue j_handle = JS_NewInt64(ctx, NUM2LONG(r_object_id));
|
|
227
|
+
JSValue j_proxy = JS_Call(ctx, data->j_file_proxy_creator, JS_UNDEFINED, 1, &j_handle);
|
|
228
|
+
JS_FreeValue(ctx, j_handle);
|
|
229
|
+
return j_proxy;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static VALUE r_extract_blob_content(JSContext *ctx, JSValue j_val)
|
|
233
|
+
{
|
|
234
|
+
JSValue j_ab_fn = JS_GetPropertyStr(ctx, j_val, "arrayBuffer");
|
|
235
|
+
JSValue j_promise = JS_Call(ctx, j_ab_fn, j_val, 0, NULL);
|
|
236
|
+
JS_FreeValue(ctx, j_ab_fn);
|
|
237
|
+
|
|
238
|
+
JSContext *ctx2;
|
|
239
|
+
while (JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx2) > 0)
|
|
240
|
+
{
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
VALUE r_content = rb_str_new("", 0);
|
|
244
|
+
if (JS_PromiseState(ctx, j_promise) == JS_PROMISE_FULFILLED)
|
|
245
|
+
{
|
|
246
|
+
JSValue j_result = JS_PromiseResult(ctx, j_promise);
|
|
247
|
+
size_t buf_len;
|
|
248
|
+
uint8_t *buf = JS_GetArrayBuffer(ctx, &buf_len, j_result);
|
|
249
|
+
if (buf)
|
|
250
|
+
r_content = rb_str_new((const char *)buf, buf_len);
|
|
251
|
+
JS_FreeValue(ctx, j_result);
|
|
252
|
+
}
|
|
253
|
+
JS_FreeValue(ctx, j_promise);
|
|
254
|
+
|
|
255
|
+
rb_funcall(r_content, rb_intern("force_encoding"), 1, rb_str_new_cstr("BINARY"));
|
|
256
|
+
return r_content;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static void r_populate_blob_attrs(JSContext *ctx, JSValue j_val, VALUE r_obj)
|
|
260
|
+
{
|
|
261
|
+
JSValue j_size = JS_GetPropertyStr(ctx, j_val, "size");
|
|
262
|
+
int64_t size_val;
|
|
263
|
+
JS_ToInt64(ctx, &size_val, j_size);
|
|
264
|
+
rb_iv_set(r_obj, "@size", LONG2NUM(size_val));
|
|
265
|
+
JS_FreeValue(ctx, j_size);
|
|
266
|
+
|
|
267
|
+
JSValue j_type = JS_GetPropertyStr(ctx, j_val, "type");
|
|
268
|
+
const char *type_str = JS_ToCString(ctx, j_type);
|
|
269
|
+
rb_iv_set(r_obj, "@type", rb_str_new_cstr(type_str ? type_str : ""));
|
|
270
|
+
JS_FreeCString(ctx, type_str);
|
|
271
|
+
JS_FreeValue(ctx, j_type);
|
|
272
|
+
|
|
273
|
+
rb_iv_set(r_obj, "@content", r_extract_blob_content(ctx, j_val));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
VALUE quickjsrb_try_convert_js_file(JSContext *ctx, JSValue j_val)
|
|
277
|
+
{
|
|
278
|
+
JSValue j_global = JS_GetGlobalObject(ctx);
|
|
279
|
+
|
|
280
|
+
// Check File first (File extends Blob, so instanceof Blob is also true for Files)
|
|
281
|
+
JSValue j_file_ctor = JS_GetPropertyStr(ctx, j_global, "File");
|
|
282
|
+
if (!JS_IsUndefined(j_file_ctor) && !JS_IsException(j_file_ctor))
|
|
283
|
+
{
|
|
284
|
+
int is_file = JS_IsInstanceOf(ctx, j_val, j_file_ctor);
|
|
285
|
+
JS_FreeValue(ctx, j_file_ctor);
|
|
286
|
+
if (is_file > 0)
|
|
287
|
+
{
|
|
288
|
+
JS_FreeValue(ctx, j_global);
|
|
289
|
+
|
|
290
|
+
VALUE r_quickjs_mod = rb_const_get(rb_cClass, rb_intern("Quickjs"));
|
|
291
|
+
VALUE r_file = rb_funcall(rb_const_get(r_quickjs_mod, rb_intern("File")), rb_intern("new"), 0);
|
|
292
|
+
r_populate_blob_attrs(ctx, j_val, r_file);
|
|
293
|
+
|
|
294
|
+
JSValue j_name = JS_GetPropertyStr(ctx, j_val, "name");
|
|
295
|
+
const char *name_str = JS_ToCString(ctx, j_name);
|
|
296
|
+
rb_iv_set(r_file, "@name", rb_str_new_cstr(name_str ? name_str : ""));
|
|
297
|
+
JS_FreeCString(ctx, name_str);
|
|
298
|
+
JS_FreeValue(ctx, j_name);
|
|
299
|
+
|
|
300
|
+
JSValue j_last_modified = JS_GetPropertyStr(ctx, j_val, "lastModified");
|
|
301
|
+
double last_modified_val;
|
|
302
|
+
JS_ToFloat64(ctx, &last_modified_val, j_last_modified);
|
|
303
|
+
rb_iv_set(r_file, "@last_modified", DBL2NUM(last_modified_val));
|
|
304
|
+
JS_FreeValue(ctx, j_last_modified);
|
|
305
|
+
|
|
306
|
+
return r_file;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else
|
|
310
|
+
{
|
|
311
|
+
JS_FreeValue(ctx, j_file_ctor);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
JSValue j_blob_ctor = JS_GetPropertyStr(ctx, j_global, "Blob");
|
|
315
|
+
JS_FreeValue(ctx, j_global);
|
|
316
|
+
if (JS_IsUndefined(j_blob_ctor) || JS_IsException(j_blob_ctor))
|
|
317
|
+
{
|
|
318
|
+
JS_FreeValue(ctx, j_blob_ctor);
|
|
319
|
+
return Qnil;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
int is_blob = JS_IsInstanceOf(ctx, j_val, j_blob_ctor);
|
|
323
|
+
JS_FreeValue(ctx, j_blob_ctor);
|
|
324
|
+
if (is_blob <= 0)
|
|
325
|
+
return Qnil;
|
|
326
|
+
|
|
327
|
+
VALUE r_quickjs_mod = rb_const_get(rb_cClass, rb_intern("Quickjs"));
|
|
328
|
+
VALUE r_blob = rb_funcall(rb_const_get(r_quickjs_mod, rb_intern("Blob")), rb_intern("new"), 0);
|
|
329
|
+
r_populate_blob_attrs(ctx, j_val, r_blob);
|
|
330
|
+
|
|
331
|
+
return r_blob;
|
|
332
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifndef QUICKJSRB_FILE_H
|
|
2
|
+
#define QUICKJSRB_FILE_H 1
|
|
3
|
+
|
|
4
|
+
// This header is included by quickjsrb.c (which already includes quickjsrb.h)
|
|
5
|
+
// and quickjsrb_file.c (which includes quickjsrb.h before this header).
|
|
6
|
+
// So we rely on VMData, JSValue, etc. being already defined.
|
|
7
|
+
|
|
8
|
+
void quickjsrb_init_file_proxy(VMData *data);
|
|
9
|
+
JSValue quickjsrb_file_to_js(JSContext *ctx, VALUE r_file);
|
|
10
|
+
|
|
11
|
+
// Check if a JS value is a File instance and convert to Quickjs::File
|
|
12
|
+
// Returns Qnil if not a File
|
|
13
|
+
VALUE quickjsrb_try_convert_js_file(JSContext *ctx, JSValue j_val);
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
#endif /* QUICKJSRB_FILE_H */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){var e=class{get encoding(){return`utf-8`}encode(e=``){let t=String(e),n=[];for(let e=0;e<t.length;e++){let r=t.charCodeAt(e);if(r>=55296&&r<=56319&&e+1<t.length){let n=t.charCodeAt(e+1);n>=56320&&n<=57343&&(r=(r-55296)*1024+(n-56320)+65536,e++)}r<=127?n.push(r):r<=2047?n.push(192|r>>6,128|r&63):r<=65535?n.push(224|r>>12,128|r>>6&63,128|r&63):n.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63)}return new Uint8Array(n)}encodeInto(e,t){let n=String(e),r=0,i=0;for(let e=0;e<n.length;e++){let a=n.charCodeAt(e);if(a>=55296&&a<=56319&&e+1<n.length){let o=n.charCodeAt(e+1);if(o>=56320&&o<=57343){if(i+4>t.length)break;a=(a-55296)*1024+(o-56320)+65536,t[i++]=240|a>>18,t[i++]=128|a>>12&63,t[i++]=128|a>>6&63,t[i++]=128|a&63,r+=2,e++;continue}}let o;if(o=a<=127?1:a<=2047?2:3,i+o>t.length)break;o===1?t[i++]=a:o===2?(t[i++]=192|a>>6,t[i++]=128|a&63):(t[i++]=224|a>>12,t[i++]=128|a>>6&63,t[i++]=128|a&63),r++}return{read:r,written:i}}};let t=[`unicode-1-1-utf-8`,`unicode11utf8`,`unicode20utf8`,`utf-8`,`utf8`,`x-unicode20utf8`];function n(e){let n=e.trim().toLowerCase();return t.includes(n)?`utf-8`:null}var r=class{#e;#t;#n;constructor(e=`utf-8`,t={}){let r=n(String(e));if(!r)throw RangeError(`The "${e}" encoding is not supported.`);this.#e=r,this.#t=!!t.fatal,this.#n=!!t.ignoreBOM}get encoding(){return this.#e}get fatal(){return this.#t}get ignoreBOM(){return this.#n}decode(e,t={}){if(e==null)return``;let n;if(e instanceof ArrayBuffer)n=new Uint8Array(e);else if(ArrayBuffer.isView(e))n=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);else throw TypeError(`The provided value is not of type '(ArrayBuffer or ArrayBufferView)'`);let r=0;return!this.#n&&n.length>=3&&n[0]===239&&n[1]===187&&n[2]===191&&(r=3),i(n,r,this.#t)}};function i(e,t,n){let r=``,i=t;for(;i<e.length;){let t=e[i],a,o;if(t<=127)a=t,o=1;else if((t&224)==192){if(i+1>=e.length||(e[i+1]&192)!=128){if(n)throw TypeError(`The encoded data was not valid.`);r+=`�`,i++;continue}a=(t&31)<<6|e[i+1]&63,o=2}else if((t&240)==224){if(i+2>=e.length||(e[i+1]&192)!=128||(e[i+2]&192)!=128){if(n)throw TypeError(`The encoded data was not valid.`);r+=`�`,i++;continue}a=(t&15)<<12|(e[i+1]&63)<<6|e[i+2]&63,o=3}else if((t&248)==240){if(i+3>=e.length||(e[i+1]&192)!=128||(e[i+2]&192)!=128||(e[i+3]&192)!=128){if(n)throw TypeError(`The encoded data was not valid.`);r+=`�`,i++;continue}a=(t&7)<<18|(e[i+1]&63)<<12|(e[i+2]&63)<<6|e[i+3]&63,o=4}else{if(n)throw TypeError(`The encoded data was not valid.`);r+=`�`,i++;continue}a<=65535?r+=String.fromCharCode(a):(a-=65536,r+=String.fromCharCode(55296+(a>>10),56320+(a&1023))),i+=o}return r}globalThis.TextEncoder=e,globalThis.TextDecoder=r})();
|
data/lib/quickjs/version.rb
CHANGED
data/lib/quickjs.rb
CHANGED
|
@@ -5,6 +5,14 @@ require_relative "quickjs/version"
|
|
|
5
5
|
require_relative "quickjs/quickjsrb"
|
|
6
6
|
|
|
7
7
|
module Quickjs
|
|
8
|
+
class Blob
|
|
9
|
+
attr_reader :size, :type, :content
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class File < Blob
|
|
13
|
+
attr_reader :name, :last_modified
|
|
14
|
+
end
|
|
15
|
+
|
|
8
16
|
def eval_code(code, overwrite_opts = {})
|
|
9
17
|
vm = Quickjs::VM.new(**overwrite_opts)
|
|
10
18
|
res = vm.eval_code(code)
|
data/polyfills/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickjs-rb-polyfills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0.pre",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "quickjs-rb-polyfills",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.13.0.pre",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@formatjs/intl-datetimeformat": "^7.2.1",
|
|
12
12
|
"@formatjs/intl-getcanonicallocales": "^3.2.1",
|
data/polyfills/package.json
CHANGED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// WHATWG TextEncoder/TextDecoder polyfill for QuickJS
|
|
2
|
+
// Spec: https://encoding.spec.whatwg.org/
|
|
3
|
+
|
|
4
|
+
class TextEncoder {
|
|
5
|
+
get encoding() {
|
|
6
|
+
return "utf-8";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
encode(input = "") {
|
|
10
|
+
const str = String(input);
|
|
11
|
+
const bytes = [];
|
|
12
|
+
for (let i = 0; i < str.length; i++) {
|
|
13
|
+
let code = str.charCodeAt(i);
|
|
14
|
+
|
|
15
|
+
if (code >= 0xd800 && code <= 0xdbff && i + 1 < str.length) {
|
|
16
|
+
const next = str.charCodeAt(i + 1);
|
|
17
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
18
|
+
code = (code - 0xd800) * 0x400 + (next - 0xdc00) + 0x10000;
|
|
19
|
+
i++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (code <= 0x7f) {
|
|
24
|
+
bytes.push(code);
|
|
25
|
+
} else if (code <= 0x7ff) {
|
|
26
|
+
bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
|
27
|
+
} else if (code <= 0xffff) {
|
|
28
|
+
bytes.push(
|
|
29
|
+
0xe0 | (code >> 12),
|
|
30
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
31
|
+
0x80 | (code & 0x3f)
|
|
32
|
+
);
|
|
33
|
+
} else {
|
|
34
|
+
bytes.push(
|
|
35
|
+
0xf0 | (code >> 18),
|
|
36
|
+
0x80 | ((code >> 12) & 0x3f),
|
|
37
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
38
|
+
0x80 | (code & 0x3f)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return new Uint8Array(bytes);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
encodeInto(source, destination) {
|
|
46
|
+
const str = String(source);
|
|
47
|
+
let read = 0;
|
|
48
|
+
let written = 0;
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < str.length; i++) {
|
|
51
|
+
let code = str.charCodeAt(i);
|
|
52
|
+
|
|
53
|
+
if (code >= 0xd800 && code <= 0xdbff && i + 1 < str.length) {
|
|
54
|
+
const next = str.charCodeAt(i + 1);
|
|
55
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
56
|
+
if (written + 4 > destination.length) break;
|
|
57
|
+
code = (code - 0xd800) * 0x400 + (next - 0xdc00) + 0x10000;
|
|
58
|
+
destination[written++] = 0xf0 | (code >> 18);
|
|
59
|
+
destination[written++] = 0x80 | ((code >> 12) & 0x3f);
|
|
60
|
+
destination[written++] = 0x80 | ((code >> 6) & 0x3f);
|
|
61
|
+
destination[written++] = 0x80 | (code & 0x3f);
|
|
62
|
+
read += 2;
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let byteCount;
|
|
69
|
+
if (code <= 0x7f) byteCount = 1;
|
|
70
|
+
else if (code <= 0x7ff) byteCount = 2;
|
|
71
|
+
else byteCount = 3;
|
|
72
|
+
|
|
73
|
+
if (written + byteCount > destination.length) break;
|
|
74
|
+
|
|
75
|
+
if (byteCount === 1) {
|
|
76
|
+
destination[written++] = code;
|
|
77
|
+
} else if (byteCount === 2) {
|
|
78
|
+
destination[written++] = 0xc0 | (code >> 6);
|
|
79
|
+
destination[written++] = 0x80 | (code & 0x3f);
|
|
80
|
+
} else {
|
|
81
|
+
destination[written++] = 0xe0 | (code >> 12);
|
|
82
|
+
destination[written++] = 0x80 | ((code >> 6) & 0x3f);
|
|
83
|
+
destination[written++] = 0x80 | (code & 0x3f);
|
|
84
|
+
}
|
|
85
|
+
read++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { read, written };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const UTF8_LABELS = [
|
|
93
|
+
"unicode-1-1-utf-8", "unicode11utf8", "unicode20utf8",
|
|
94
|
+
"utf-8", "utf8", "x-unicode20utf8",
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
function normalizeEncodingLabel(label) {
|
|
98
|
+
const normalized = label.trim().toLowerCase();
|
|
99
|
+
if (UTF8_LABELS.includes(normalized)) return "utf-8";
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class TextDecoder {
|
|
104
|
+
#encoding;
|
|
105
|
+
#fatal;
|
|
106
|
+
#ignoreBOM;
|
|
107
|
+
|
|
108
|
+
constructor(label = "utf-8", options = {}) {
|
|
109
|
+
const normalized = normalizeEncodingLabel(String(label));
|
|
110
|
+
if (!normalized) {
|
|
111
|
+
throw new RangeError(`The "${label}" encoding is not supported.`);
|
|
112
|
+
}
|
|
113
|
+
this.#encoding = normalized;
|
|
114
|
+
this.#fatal = Boolean(options.fatal);
|
|
115
|
+
this.#ignoreBOM = Boolean(options.ignoreBOM);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get encoding() {
|
|
119
|
+
return this.#encoding;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get fatal() {
|
|
123
|
+
return this.#fatal;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get ignoreBOM() {
|
|
127
|
+
return this.#ignoreBOM;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
decode(input, options = {}) {
|
|
131
|
+
if (input === undefined || input === null) return "";
|
|
132
|
+
|
|
133
|
+
let bytes;
|
|
134
|
+
if (input instanceof ArrayBuffer) {
|
|
135
|
+
bytes = new Uint8Array(input);
|
|
136
|
+
} else if (ArrayBuffer.isView(input)) {
|
|
137
|
+
bytes = new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
|
|
138
|
+
} else {
|
|
139
|
+
throw new TypeError(
|
|
140
|
+
"The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let start = 0;
|
|
145
|
+
if (
|
|
146
|
+
!this.#ignoreBOM &&
|
|
147
|
+
bytes.length >= 3 &&
|
|
148
|
+
bytes[0] === 0xef &&
|
|
149
|
+
bytes[1] === 0xbb &&
|
|
150
|
+
bytes[2] === 0xbf
|
|
151
|
+
) {
|
|
152
|
+
start = 3;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return decodeUTF8(bytes, start, this.#fatal);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function decodeUTF8(bytes, start, fatal) {
|
|
160
|
+
let str = "";
|
|
161
|
+
let i = start;
|
|
162
|
+
while (i < bytes.length) {
|
|
163
|
+
const b = bytes[i];
|
|
164
|
+
let code, byteLen;
|
|
165
|
+
|
|
166
|
+
if (b <= 0x7f) {
|
|
167
|
+
code = b;
|
|
168
|
+
byteLen = 1;
|
|
169
|
+
} else if ((b & 0xe0) === 0xc0) {
|
|
170
|
+
if (i + 1 >= bytes.length || (bytes[i + 1] & 0xc0) !== 0x80) {
|
|
171
|
+
if (fatal) throw new TypeError("The encoded data was not valid.");
|
|
172
|
+
str += "\ufffd";
|
|
173
|
+
i++;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
code = ((b & 0x1f) << 6) | (bytes[i + 1] & 0x3f);
|
|
177
|
+
byteLen = 2;
|
|
178
|
+
} else if ((b & 0xf0) === 0xe0) {
|
|
179
|
+
if (
|
|
180
|
+
i + 2 >= bytes.length ||
|
|
181
|
+
(bytes[i + 1] & 0xc0) !== 0x80 ||
|
|
182
|
+
(bytes[i + 2] & 0xc0) !== 0x80
|
|
183
|
+
) {
|
|
184
|
+
if (fatal) throw new TypeError("The encoded data was not valid.");
|
|
185
|
+
str += "\ufffd";
|
|
186
|
+
i++;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
code =
|
|
190
|
+
((b & 0x0f) << 12) |
|
|
191
|
+
((bytes[i + 1] & 0x3f) << 6) |
|
|
192
|
+
(bytes[i + 2] & 0x3f);
|
|
193
|
+
byteLen = 3;
|
|
194
|
+
} else if ((b & 0xf8) === 0xf0) {
|
|
195
|
+
if (
|
|
196
|
+
i + 3 >= bytes.length ||
|
|
197
|
+
(bytes[i + 1] & 0xc0) !== 0x80 ||
|
|
198
|
+
(bytes[i + 2] & 0xc0) !== 0x80 ||
|
|
199
|
+
(bytes[i + 3] & 0xc0) !== 0x80
|
|
200
|
+
) {
|
|
201
|
+
if (fatal) throw new TypeError("The encoded data was not valid.");
|
|
202
|
+
str += "\ufffd";
|
|
203
|
+
i++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
code =
|
|
207
|
+
((b & 0x07) << 18) |
|
|
208
|
+
((bytes[i + 1] & 0x3f) << 12) |
|
|
209
|
+
((bytes[i + 2] & 0x3f) << 6) |
|
|
210
|
+
(bytes[i + 3] & 0x3f);
|
|
211
|
+
byteLen = 4;
|
|
212
|
+
} else {
|
|
213
|
+
if (fatal) throw new TypeError("The encoded data was not valid.");
|
|
214
|
+
str += "\ufffd";
|
|
215
|
+
i++;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (code <= 0xffff) {
|
|
220
|
+
str += String.fromCharCode(code);
|
|
221
|
+
} else {
|
|
222
|
+
code -= 0x10000;
|
|
223
|
+
str += String.fromCharCode(0xd800 + (code >> 10), 0xdc00 + (code & 0x3ff));
|
|
224
|
+
}
|
|
225
|
+
i += byteLen;
|
|
226
|
+
}
|
|
227
|
+
return str;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
globalThis.TextEncoder = TextEncoder;
|
|
231
|
+
globalThis.TextDecoder = TextDecoder;
|
data/sig/quickjs.rbs
CHANGED
|
@@ -1,4 +1,72 @@
|
|
|
1
1
|
module Quickjs
|
|
2
2
|
VERSION: String
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
MODULE_STD: Symbol
|
|
5
|
+
MODULE_OS: Symbol
|
|
6
|
+
FEATURE_TIMEOUT: Symbol
|
|
7
|
+
POLYFILL_INTL: Symbol
|
|
8
|
+
POLYFILL_FILE: Symbol
|
|
9
|
+
POLYFILL_HTML_BASE64: Symbol
|
|
10
|
+
|
|
11
|
+
def self.eval_code: (String code, ?Hash[Symbol, untyped] overwrite_opts) -> untyped
|
|
12
|
+
|
|
13
|
+
class Value
|
|
14
|
+
UNDEFINED: Symbol
|
|
15
|
+
NAN: Symbol
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class VM
|
|
19
|
+
def initialize: (?features: Array[Symbol], ?memory_limit: Integer, ?max_stack_size: Integer, ?timeout_msec: Integer) -> void
|
|
20
|
+
|
|
21
|
+
def eval_code: (String code) -> untyped
|
|
22
|
+
|
|
23
|
+
def define_function: (String | Symbol name, *Symbol flags) { (*untyped) -> untyped } -> Symbol
|
|
24
|
+
|
|
25
|
+
def import: (String | Array[String] | Hash[Symbol, String] imported, from: String, ?code_to_expose: String?) -> true
|
|
26
|
+
|
|
27
|
+
def logs: () -> Array[Log]
|
|
28
|
+
|
|
29
|
+
class Log
|
|
30
|
+
attr_reader severity: Symbol
|
|
31
|
+
|
|
32
|
+
def raw: () -> Array[untyped]
|
|
33
|
+
|
|
34
|
+
def to_s: () -> String
|
|
35
|
+
|
|
36
|
+
def inspect: () -> String
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class RuntimeError < ::RuntimeError
|
|
41
|
+
def initialize: (String message, String? js_name) -> void
|
|
42
|
+
|
|
43
|
+
attr_reader js_name: String?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class SyntaxError < RuntimeError
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class TypeError < RuntimeError
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class ReferenceError < RuntimeError
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class RangeError < RuntimeError
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class EvalError < RuntimeError
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class URIError < RuntimeError
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class AggregateError < RuntimeError
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class InterruptedError < RuntimeError
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class NoAwaitError < RuntimeError
|
|
71
|
+
end
|
|
4
72
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: quickjs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0.pre
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- hmsk
|
|
@@ -78,6 +78,9 @@ files:
|
|
|
78
78
|
- ext/quickjsrb/quickjs/unicode_gen_def.h
|
|
79
79
|
- ext/quickjsrb/quickjsrb.c
|
|
80
80
|
- ext/quickjsrb/quickjsrb.h
|
|
81
|
+
- ext/quickjsrb/quickjsrb_file.c
|
|
82
|
+
- ext/quickjsrb/quickjsrb_file.h
|
|
83
|
+
- ext/quickjsrb/vendor/polyfill-encoding.min.js
|
|
81
84
|
- ext/quickjsrb/vendor/polyfill-file.min.js
|
|
82
85
|
- ext/quickjsrb/vendor/polyfill-intl-en.min.js
|
|
83
86
|
- lib/quickjs.rb
|
|
@@ -85,6 +88,7 @@ files:
|
|
|
85
88
|
- polyfills/package-lock.json
|
|
86
89
|
- polyfills/package.json
|
|
87
90
|
- polyfills/rolldown.config.mjs
|
|
91
|
+
- polyfills/src/encoding.js
|
|
88
92
|
- polyfills/src/file.js
|
|
89
93
|
- polyfills/src/intl-en.js
|
|
90
94
|
- sig/quickjs.rbs
|