quickjs 0.2.1 → 0.2.2

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: 7afae8229fda495f4a4c40c5c3ec1527bbb5491f23cd46ac1cf7d94badaa6a8b
4
- data.tar.gz: 62abbf8909cabf1e5d22b7228eda4fe3bd2193122573ad38b6491a7e60c142e2
3
+ metadata.gz: 0c2a481a2ea280bc0d4a9c7778b62035991bd08293ddef98b4824a40a6a6ccd9
4
+ data.tar.gz: a40b2f11813f2f82db4c5b9e0931199013671e0983e80566f0b6645709deece7
5
5
  SHA512:
6
- metadata.gz: 6fad5187a6672299187de72faf8ccb3cc5402858c65a12edb808ae5759c1665ec90c75cbc34194b9d81e28c4ca5b8616de9174cbac060ba74da4509a28b21d85
7
- data.tar.gz: dd8505b9ac5459fa2a2859cb1108b27861efcd59f234290ec43c4e2dc80e8ee61ea1ae34dba2f013036b495bcf6e9013f0fad0822a5249a43f7eba7cd3864251
6
+ metadata.gz: f509a918c4e5ff7d19bdaa8873ee62ede64cfd98e405a7ea6f4eb00a724fc79fc2cd928e2388d0f8e07dcd455834ba1f60ec2a9990b4781bd0012ec5f72a9c16
7
+ data.tar.gz: b7604b888bc62fe12f6a5b30387891108a24cf1de55e77934cabeeab20fe9ae27f494098448a32a975e9fde99cee9a9af5bdd58c258a0bafb60adc125cedf206
data/README.md CHANGED
@@ -114,10 +114,21 @@ vm = Quickjs::VM.new(timeout_msec: 1_000)
114
114
 
115
115
  ```rb
116
116
  vm = Quickjs::VM.new
117
+
118
+ # Equivalent to `import { default: aliasedDefault, member: member } from './exports.esm.js';`
117
119
  vm.import({ default: 'aliasedDefault', member: 'member' }, from: File.read('exports.esm.js'))
118
120
 
119
121
  vm.eval_code("aliasedDefault()") #=> Exported `default` of the ESM is called
120
122
  vm.eval_code("member()") #=> Exported `member` of the ESM is called
123
+
124
+ # import { member, defaultMember } from './exports.esm.js';
125
+ vm.import(['member', 'defaultMember'], from: File.read('exports.esm.js'))
126
+
127
+ # import DefaultExport from './exports.esm.js';
128
+ vm.import('DefaultExport', from: File.read('exports.esm.js'))
129
+
130
+ # import * as all from './exports.esm.js';
131
+ vm.import('* as all', from: File.read('exports.esm.js'))
121
132
  ```
122
133
 
123
134
  #### `Quickjs::VM#define_function`: 💎 Define a global function for JS by Ruby
@@ -131,6 +142,20 @@ end
131
142
  vm.eval_code("greetingTo('Rick')") #=> 'Hello! Rick'
132
143
  ```
133
144
 
145
+ #### `Quickjs::VM#logs`: 💾 Capture console logs
146
+
147
+ All logs by `console.(log|info|debug|warn|error)` on VM are recorded and inspectable.
148
+
149
+ ```rb
150
+ vm = Quickjs::VM.new
151
+ vm.eval_code('console.log("log me", null)')
152
+
153
+ vm.logs #=> Array of Quickjs::VM::Log
154
+ vm.logs.last.severity #=> :info
155
+ vm.logs.last.to_s #=> 'log me null'
156
+ vm,logs.last.raw #=> ['log me', nil]
157
+ ```
158
+
134
159
  ## License
135
160
 
136
161
  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).
@@ -34,7 +34,7 @@
34
34
  #include <time.h>
35
35
  #if defined(__APPLE__)
36
36
  #include <malloc/malloc.h>
37
- #elif defined(__linux__)
37
+ #elif defined(__linux__) || defined(__GLIBC__)
38
38
  #include <malloc.h>
39
39
  #elif defined(__FreeBSD__)
40
40
  #include <malloc_np.h>
@@ -151,7 +151,7 @@ static size_t js_trace_malloc_usable_size(const void *ptr)
151
151
  return _msize((void *)ptr);
152
152
  #elif defined(EMSCRIPTEN)
153
153
  return 0;
154
- #elif defined(__linux__)
154
+ #elif defined(__linux__) || defined(__GLIBC__)
155
155
  return malloc_usable_size((void *)ptr);
156
156
  #else
157
157
  /* change this to `return 0;` if compilation fails */
@@ -78,6 +78,10 @@ typedef sig_t sighandler_t;
78
78
  #include "list.h"
79
79
  #include "quickjs-libc.h"
80
80
 
81
+ #if !defined(PATH_MAX)
82
+ #define PATH_MAX 4096
83
+ #endif
84
+
81
85
  /* TODO:
82
86
  - add socket calls
83
87
  */
@@ -1090,7 +1094,7 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,
1090
1094
  int64_t pos;
1091
1095
  if (!f)
1092
1096
  return JS_EXCEPTION;
1093
- #if defined(__linux__)
1097
+ #if defined(__linux__) || defined(__GLIBC__)
1094
1098
  pos = ftello(f);
1095
1099
  #else
1096
1100
  pos = ftell(f);
@@ -1113,7 +1117,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,
1113
1117
  return JS_EXCEPTION;
1114
1118
  if (JS_ToInt32(ctx, &whence, argv[1]))
1115
1119
  return JS_EXCEPTION;
1116
- #if defined(__linux__)
1120
+ #if defined(__linux__) || defined(__GLIBC__)
1117
1121
  ret = fseeko(f, pos, whence);
1118
1122
  #else
1119
1123
  ret = fseek(f, pos, whence);
@@ -34,7 +34,7 @@
34
34
  #include <math.h>
35
35
  #if defined(__APPLE__)
36
36
  #include <malloc/malloc.h>
37
- #elif defined(__linux__)
37
+ #elif defined(__linux__) || defined(__GLIBC__)
38
38
  #include <malloc.h>
39
39
  #elif defined(__FreeBSD__)
40
40
  #include <malloc_np.h>
@@ -1681,7 +1681,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
1681
1681
  rt->stack_size = JS_DEFAULT_STACK_SIZE;
1682
1682
  JS_UpdateStackTop(rt);
1683
1683
 
1684
- rt->current_exception = JS_NULL;
1684
+ rt->current_exception = JS_UNINITIALIZED;
1685
1685
 
1686
1686
  return rt;
1687
1687
  fail:
@@ -1708,7 +1708,7 @@ static size_t js_def_malloc_usable_size(const void *ptr)
1708
1708
  return _msize((void *)ptr);
1709
1709
  #elif defined(EMSCRIPTEN)
1710
1710
  return 0;
1711
- #elif defined(__linux__)
1711
+ #elif defined(__linux__) || defined(__GLIBC__)
1712
1712
  return malloc_usable_size((void *)ptr);
1713
1713
  #else
1714
1714
  /* change this to `return 0;` if compilation fails */
@@ -6398,13 +6398,13 @@ JSValue JS_GetException(JSContext *ctx)
6398
6398
  JSValue val;
6399
6399
  JSRuntime *rt = ctx->rt;
6400
6400
  val = rt->current_exception;
6401
- rt->current_exception = JS_NULL;
6401
+ rt->current_exception = JS_UNINITIALIZED;
6402
6402
  return val;
6403
6403
  }
6404
6404
 
6405
6405
  JS_BOOL JS_HasException(JSContext *ctx)
6406
6406
  {
6407
- return !JS_IsNull(ctx->rt->current_exception);
6407
+ return !JS_IsUninitialized(ctx->rt->current_exception);
6408
6408
  }
6409
6409
 
6410
6410
  static void dbuf_put_leb128(DynBuf *s, uint32_t v)
@@ -15321,7 +15321,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
15321
15321
 
15322
15322
  if (is_exception_pending) {
15323
15323
  ex_obj = ctx->rt->current_exception;
15324
- ctx->rt->current_exception = JS_NULL;
15324
+ ctx->rt->current_exception = JS_UNINITIALIZED;
15325
15325
  res = -1;
15326
15326
  } else {
15327
15327
  ex_obj = JS_UNDEFINED;
@@ -18674,7 +18674,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
18674
18674
  JS_IteratorClose(ctx, sp[-1], TRUE);
18675
18675
  } else {
18676
18676
  *sp++ = rt->current_exception;
18677
- rt->current_exception = JS_NULL;
18677
+ rt->current_exception = JS_UNINITIALIZED;
18678
18678
  pc = b->byte_code_buf + pos;
18679
18679
  goto restart;
18680
18680
  }
@@ -46151,8 +46151,10 @@ static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
46151
46151
  if (JS_IsException(ret))
46152
46152
  return JS_EXCEPTION;
46153
46153
  res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
46154
- if (res < 0)
46154
+ if (res < 0) {
46155
+ JS_FreeValue(ctx, ret);
46155
46156
  return JS_EXCEPTION;
46157
+ }
46156
46158
  if (res) {
46157
46159
  if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
46158
46160
  if (!js_same_value(ctx, desc.value, ret)) {
@@ -1,79 +1,5 @@
1
1
  #include "quickjsrb.h"
2
2
 
3
- typedef struct EvalTime
4
- {
5
- clock_t limit;
6
- clock_t started_at;
7
- } EvalTime;
8
-
9
- typedef struct VMData
10
- {
11
- struct JSContext *context;
12
- VALUE defined_functions;
13
- struct EvalTime *eval_time;
14
- VALUE logs;
15
- } VMData;
16
-
17
- static void vm_free(void *ptr)
18
- {
19
- VMData *data = (VMData *)ptr;
20
- free(data->eval_time);
21
-
22
- JSRuntime *runtime = JS_GetRuntime(data->context);
23
- JS_SetInterruptHandler(runtime, NULL, NULL);
24
- js_std_free_handlers(runtime);
25
- JS_FreeContext(data->context);
26
- JS_FreeRuntime(runtime);
27
-
28
- xfree(ptr);
29
- }
30
-
31
- size_t vm_size(const void *data)
32
- {
33
- return sizeof(VMData);
34
- }
35
-
36
- static void vm_mark(void *ptr)
37
- {
38
- VMData *data = (VMData *)ptr;
39
- rb_gc_mark_movable(data->defined_functions);
40
- rb_gc_mark_movable(data->logs);
41
- }
42
-
43
- static const rb_data_type_t vm_type = {
44
- .wrap_struct_name = "quickjsvm",
45
- .function = {
46
- .dmark = vm_mark,
47
- .dfree = vm_free,
48
- .dsize = vm_size,
49
- },
50
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
51
- };
52
-
53
- static VALUE vm_alloc(VALUE r_self)
54
- {
55
- VMData *data;
56
- VALUE obj = TypedData_Make_Struct(r_self, VMData, &vm_type, data);
57
- data->defined_functions = rb_hash_new();
58
- data->logs = rb_ary_new();
59
-
60
- EvalTime *eval_time = malloc(sizeof(EvalTime));
61
- data->eval_time = eval_time;
62
-
63
- JSRuntime *runtime = JS_NewRuntime();
64
- data->context = JS_NewContext(runtime);
65
-
66
- return obj;
67
- }
68
-
69
- VALUE rb_cQuickjsVMLog, rb_cQuickjsSyntaxError, rb_cQuickjsRuntimeError, rb_cQuickjsInterruptedError, rb_cQuickjsNoAwaitError, rb_cQuickjsTypeError, rb_cQuickjsReferenceError, rb_cQuickjsRangeError, rb_cQuickjsEvalError, rb_cQuickjsURIError, rb_cQuickjsAggregateError;
70
- const char *undefinedId = "undefined";
71
- const char *nanId = "NaN";
72
-
73
- const char *featureStdId = "feature_std";
74
- const char *featureOsId = "feature_os";
75
- const char *featureOsTimeoutId = "feature_os_timeout";
76
-
77
3
  JSValue to_js_value(JSContext *ctx, VALUE r_value)
78
4
  {
79
5
  if (RTEST(rb_funcall(
@@ -168,7 +94,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
168
94
  {
169
95
  if (JS_VALUE_IS_NAN(j_val))
170
96
  {
171
- return ID2SYM(rb_intern(nanId));
97
+ return QUICKJSRB_SYM(nanId);
172
98
  }
173
99
  double double_res;
174
100
  JS_ToFloat64(ctx, &double_res, j_val);
@@ -191,7 +117,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
191
117
  if (promiseState != -1)
192
118
  {
193
119
  VALUE r_error_message = rb_str_new2("cannot translate a Promise to Ruby. await within JavaScript's end");
194
- rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
120
+ rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
195
121
  return Qnil;
196
122
  }
197
123
 
@@ -204,17 +130,17 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
204
130
  VALUE r_str = rb_str_new2(msg);
205
131
  JS_FreeCString(ctx, msg);
206
132
 
207
- JS_FreeValue(ctx, j_global);
208
133
  JS_FreeValue(ctx, j_strigified);
209
134
  JS_FreeValue(ctx, j_stringifyFunc);
210
135
  JS_FreeValue(ctx, j_jsonClass);
136
+ JS_FreeValue(ctx, j_global);
211
137
 
212
138
  return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
213
139
  }
214
140
  case JS_TAG_NULL:
215
141
  return Qnil;
216
142
  case JS_TAG_UNDEFINED:
217
- return ID2SYM(rb_intern(undefinedId));
143
+ return QUICKJSRB_SYM(undefinedId);
218
144
  case JS_TAG_EXCEPTION:
219
145
  {
220
146
  JSValue j_exceptionVal = JS_GetException(ctx);
@@ -230,47 +156,22 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
230
156
  JS_FreeValue(ctx, j_errorClassName);
231
157
 
232
158
  VALUE r_error_class, r_error_message = rb_str_new2(errorClassMessage);
233
-
234
- if (strcmp(errorClassName, "SyntaxError") == 0)
159
+ if (is_native_error_name(errorClassName))
235
160
  {
236
- r_error_class = rb_cQuickjsSyntaxError;
237
- }
238
- else if (strcmp(errorClassName, "TypeError") == 0)
239
- {
240
- r_error_class = rb_cQuickjsTypeError;
241
- }
242
- else if (strcmp(errorClassName, "ReferenceError") == 0)
243
- {
244
- r_error_class = rb_cQuickjsReferenceError;
245
- }
246
- else if (strcmp(errorClassName, "RangeError") == 0)
247
- {
248
- r_error_class = rb_cQuickjsRangeError;
249
- }
250
- else if (strcmp(errorClassName, "EvalError") == 0)
251
- {
252
- r_error_class = rb_cQuickjsEvalError;
253
- }
254
- else if (strcmp(errorClassName, "URIError") == 0)
255
- {
256
- r_error_class = rb_cQuickjsURIError;
257
- }
258
- else if (strcmp(errorClassName, "AggregateError") == 0)
259
- {
260
- r_error_class = rb_cQuickjsAggregateError;
161
+ r_error_class = QUICKJSRB_ERROR_FOR(errorClassName);
261
162
  }
262
163
  else if (strcmp(errorClassName, "InternalError") == 0 && strstr(errorClassMessage, "interrupted") != NULL)
263
164
  {
264
- r_error_class = rb_cQuickjsInterruptedError;
165
+ r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
265
166
  r_error_message = rb_str_new2("Code evaluation is interrupted by the timeout or something");
266
167
  }
267
168
  else if (strcmp(errorClassName, "Quickjs::InterruptedError") == 0)
268
169
  {
269
- r_error_class = rb_cQuickjsInterruptedError;
170
+ r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
270
171
  }
271
172
  else
272
173
  {
273
- r_error_class = rb_cQuickjsRuntimeError;
174
+ r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR);
274
175
  }
275
176
  JS_FreeCString(ctx, errorClassName);
276
177
  JS_FreeCString(ctx, errorClassMessage);
@@ -285,7 +186,7 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
285
186
 
286
187
  JS_FreeCString(ctx, errorMessage);
287
188
  JS_FreeValue(ctx, j_exceptionVal);
288
- rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
189
+ rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
289
190
  }
290
191
  return Qnil;
291
192
  }
@@ -342,8 +243,10 @@ static JSValue js_quickjsrb_log(JSContext *ctx, JSValueConst _this, int _argc, J
342
243
  const char *severity = JS_ToCString(ctx, j_severity);
343
244
  JS_FreeValue(ctx, j_severity);
344
245
 
345
- VALUE r_log = rb_funcall(rb_cQuickjsVMLog, rb_intern("new"), 0);
246
+ VALUE r_log_class = rb_const_get(rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern("VM")), rb_intern("Log"));
247
+ VALUE r_log = rb_funcall(r_log_class, rb_intern("new"), 0);
346
248
  rb_iv_set(r_log, "@severity", ID2SYM(rb_intern(severity)));
249
+ JS_FreeCString(ctx, severity);
347
250
 
348
251
  VALUE r_row = rb_ary_new();
349
252
  int i;
@@ -351,22 +254,28 @@ static JSValue js_quickjsrb_log(JSContext *ctx, JSValueConst _this, int _argc, J
351
254
  int count;
352
255
  JS_ToInt32(ctx, &count, j_length);
353
256
  JS_FreeValue(ctx, j_length);
257
+
354
258
  for (i = 0; i < count; i++)
355
259
  {
260
+ VALUE r_loghash = rb_hash_new();
356
261
  JSValue j_logged = JS_GetPropertyUint32(ctx, argv[1], i);
262
+ if (JS_VALUE_GET_NORM_TAG(j_logged) == JS_TAG_OBJECT && JS_PromiseState(ctx, j_logged) != -1)
263
+ {
264
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("raw")), rb_str_new2("Promise"));
265
+ }
266
+ else
267
+ {
268
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("raw")), to_rb_value(ctx, j_logged));
269
+ }
357
270
  const char *body = JS_ToCString(ctx, j_logged);
358
- VALUE r_loghash = rb_hash_new();
359
- rb_hash_aset(r_loghash, ID2SYM(rb_intern("c")), rb_str_new2(body));
360
- rb_hash_aset(r_loghash, ID2SYM(rb_intern("raw")), to_rb_value(ctx, j_logged));
361
- rb_ary_push(r_row, r_loghash);
362
271
  JS_FreeValue(ctx, j_logged);
272
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("c")), rb_str_new2(body));
363
273
  JS_FreeCString(ctx, body);
274
+ rb_ary_push(r_row, r_loghash);
364
275
  }
365
276
 
366
277
  rb_iv_set(r_log, "@row", r_row);
367
278
  rb_ary_push(data->logs, r_log);
368
- JS_FreeCString(ctx, severity);
369
-
370
279
  return JS_UNDEFINED;
371
280
  }
372
281
 
@@ -408,7 +317,7 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
408
317
  JS_SetModuleLoaderFunc(runtime, NULL, js_module_loader, NULL);
409
318
  js_std_init_handlers(runtime);
410
319
 
411
- if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureStdId)))))
320
+ if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featureStdId))))
412
321
  {
413
322
  js_init_module_std(data->context, "std");
414
323
  const char *enableStd = "import * as std from 'std';\n"
@@ -417,7 +326,7 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
417
326
  JS_FreeValue(data->context, j_stdEval);
418
327
  }
419
328
 
420
- if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsId)))))
329
+ if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featureOsId))))
421
330
  {
422
331
  js_init_module_os(data->context, "os");
423
332
  const char *enableOs = "import * as os from 'os';\n"
@@ -425,13 +334,19 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
425
334
  JSValue j_osEval = JS_Eval(data->context, enableOs, strlen(enableOs), "<vm>", JS_EVAL_TYPE_MODULE);
426
335
  JS_FreeValue(data->context, j_osEval);
427
336
  }
428
- else if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsTimeoutId)))))
337
+ else if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, QUICKJSRB_SYM(featureOsTimeoutId))))
429
338
  {
430
- js_init_module_os(data->context, "_os"); // Better if this is limited just only for setTimeout and clearTimeout
431
- const char *enableTimeout = "import * as _os from '_os';\n"
432
- "globalThis.setTimeout = _os.setTimeout;\n"
433
- "globalThis.clearTimeout = _os.clearTimeout;\n";
339
+ char *filename = random_string();
340
+ js_init_module_os(data->context, filename); // Better if this is limited just only for setTimeout and clearTimeout
341
+ const char *enableTimeoutTemplate = "import * as _os from '%s';\n"
342
+ "globalThis.setTimeout = _os.setTimeout;\n"
343
+ "globalThis.clearTimeout = _os.clearTimeout;\n";
344
+ int length = snprintf(NULL, 0, enableTimeoutTemplate, filename);
345
+ char *enableTimeout = (char *)malloc(length + 1);
346
+ snprintf(enableTimeout, length + 1, enableTimeoutTemplate, filename);
347
+
434
348
  JSValue j_timeoutEval = JS_Eval(data->context, enableTimeout, strlen(enableTimeout), "<vm>", JS_EVAL_TYPE_MODULE);
349
+ free(enableTimeout);
435
350
  JS_FreeValue(data->context, j_timeoutEval);
436
351
  }
437
352
 
@@ -477,7 +392,7 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
477
392
 
478
393
  char *code = StringValueCStr(r_code);
479
394
  JSValue j_codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_ASYNC);
480
- JSValue j_awaitedResult = js_std_await(data->context, j_codeResult);
395
+ JSValue j_awaitedResult = js_std_await(data->context, j_codeResult); // This frees j_codeResult
481
396
  JSValue j_returnedValue = JS_GetPropertyStr(data->context, j_awaitedResult, "value");
482
397
  // Do this by rescuing to_rb_value
483
398
  if (JS_VALUE_GET_NORM_TAG(j_returnedValue) == JS_TAG_OBJECT && JS_PromiseState(data->context, j_returnedValue) != -1)
@@ -485,7 +400,7 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
485
400
  JS_FreeValue(data->context, j_returnedValue);
486
401
  JS_FreeValue(data->context, j_awaitedResult);
487
402
  VALUE r_error_message = rb_str_new2("An unawaited Promise was returned to the top-level");
488
- rb_exc_raise(rb_funcall(rb_cQuickjsNoAwaitError, rb_intern("new"), 2, r_error_message, Qnil));
403
+ rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_NO_AWAIT_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
489
404
  return Qnil;
490
405
  }
491
406
  else
@@ -535,15 +450,16 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
535
450
  if (NIL_P(r_from))
536
451
  {
537
452
  VALUE r_error_message = rb_str_new2("missing import source");
538
- rb_exc_raise(rb_funcall(rb_cQuickjsRuntimeError, rb_intern("new"), 2, r_error_message, Qnil));
453
+ rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
539
454
  return Qnil;
540
455
  }
541
456
 
542
457
  VMData *data;
543
458
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
544
459
 
460
+ char *filename = random_string();
545
461
  char *source = StringValueCStr(r_from);
546
- JSValue func = JS_Eval(data->context, source, strlen(source), "mmmodule", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
462
+ JSValue func = JS_Eval(data->context, source, strlen(source), filename, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
547
463
  js_module_set_import_meta(data->context, func, TRUE, FALSE);
548
464
  JS_FreeValue(data->context, func);
549
465
 
@@ -557,11 +473,11 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
557
473
  VALUE r_globalize = rb_ary_entry(r_import_settings, 1);
558
474
  char *globalize = StringValueCStr(r_globalize);
559
475
 
560
- const char *importAndGlobalizeModule = "import %s from 'mmmodule';\n"
476
+ const char *importAndGlobalizeModule = "import %s from '%s';\n"
561
477
  "%s\n";
562
- int length = snprintf(NULL, 0, importAndGlobalizeModule, import_name, globalize);
478
+ int length = snprintf(NULL, 0, importAndGlobalizeModule, import_name, filename, globalize);
563
479
  char *result = (char *)malloc(length + 1);
564
- snprintf(result, length + 1, importAndGlobalizeModule, import_name, globalize);
480
+ snprintf(result, length + 1, importAndGlobalizeModule, import_name, filename, globalize);
565
481
 
566
482
  JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
567
483
  free(result);
@@ -570,7 +486,7 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
570
486
  return Qtrue;
571
487
  }
572
488
 
573
- static VALUE vm_m_getLogs(VALUE r_self)
489
+ static VALUE vm_m_logs(VALUE r_self)
574
490
  {
575
491
  VMData *data;
576
492
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
@@ -578,77 +494,21 @@ static VALUE vm_m_getLogs(VALUE r_self)
578
494
  return data->logs;
579
495
  }
580
496
 
581
- static VALUE pick_raw(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE blockarg)
582
- {
583
- return rb_hash_aref(block_arg, ID2SYM(rb_intern("raw")));
584
- }
585
-
586
- static VALUE vm_m_raw(VALUE r_self)
587
- {
588
- VALUE row = rb_iv_get(r_self, "@row");
589
- VALUE r_ary = rb_block_call(row, rb_intern("map"), 0, NULL, pick_raw, Qnil);
590
-
591
- return r_ary;
592
- }
593
-
594
- static VALUE pick_c(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE blockarg)
595
- {
596
- return rb_hash_aref(block_arg, ID2SYM(rb_intern("c")));
597
- }
598
-
599
- static VALUE vm_m_to_s(VALUE r_self)
600
- {
601
- VALUE row = rb_iv_get(r_self, "@row");
602
- VALUE r_ary = rb_block_call(row, rb_intern("map"), 0, NULL, pick_c, Qnil);
603
-
604
- return rb_funcall(r_ary, rb_intern("join"), 1, rb_str_new2(" "));
605
- }
606
-
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
497
  RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
616
498
  {
617
- VALUE rb_mQuickjs = rb_define_module("Quickjs");
618
- rb_define_const(rb_mQuickjs, "MODULE_STD", ID2SYM(rb_intern(featureStdId)));
619
- rb_define_const(rb_mQuickjs, "MODULE_OS", ID2SYM(rb_intern(featureOsId)));
620
- rb_define_const(rb_mQuickjs, "FEATURES_TIMEOUT", ID2SYM(rb_intern(featureOsTimeoutId)));
621
-
622
- VALUE rb_cQuickjsValue = rb_define_class_under(rb_mQuickjs, "Value", rb_cObject);
623
- rb_define_const(rb_cQuickjsValue, "UNDEFINED", ID2SYM(rb_intern(undefinedId)));
624
- rb_define_const(rb_cQuickjsValue, "NAN", ID2SYM(rb_intern(nanId)));
625
-
626
- VALUE rb_cQuickjsVM = rb_define_class_under(rb_mQuickjs, "VM", rb_cObject);
627
- rb_define_alloc_func(rb_cQuickjsVM, vm_alloc);
628
- rb_define_method(rb_cQuickjsVM, "initialize", vm_m_initialize, -1);
629
- rb_define_method(rb_cQuickjsVM, "eval_code", vm_m_evalCode, 1);
630
- rb_define_method(rb_cQuickjsVM, "define_function", vm_m_defineGlobalFunction, 1);
631
- rb_define_method(rb_cQuickjsVM, "import", vm_m_import, -1);
632
- rb_define_method(rb_cQuickjsVM, "logs", vm_m_getLogs, 0);
633
-
634
- rb_cQuickjsVMLog = rb_define_class_under(rb_cQuickjsVM, "Log", rb_cObject);
635
- rb_define_attr(rb_cQuickjsVMLog, "severity", 1, 0);
636
- rb_define_method(rb_cQuickjsVMLog, "raw", vm_m_raw, 0);
637
- rb_define_method(rb_cQuickjsVMLog, "to_s", vm_m_to_s, 0);
638
- rb_define_method(rb_cQuickjsVMLog, "inspect", vm_m_to_s, 0);
639
-
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);
643
-
644
- rb_cQuickjsSyntaxError = rb_define_class_under(rb_mQuickjs, "SyntaxError", rb_cQuickjsRuntimeError);
645
- rb_cQuickjsTypeError = rb_define_class_under(rb_mQuickjs, "TypeError", rb_cQuickjsRuntimeError);
646
- rb_cQuickjsRangeError = rb_define_class_under(rb_mQuickjs, "RangeError", rb_cQuickjsRuntimeError);
647
- rb_cQuickjsReferenceError = rb_define_class_under(rb_mQuickjs, "ReferenceError", rb_cQuickjsRuntimeError);
648
- rb_cQuickjsURIError = rb_define_class_under(rb_mQuickjs, "URIError", rb_cQuickjsRuntimeError);
649
- rb_cQuickjsEvalError = rb_define_class_under(rb_mQuickjs, "EvalError", rb_cQuickjsRuntimeError);
650
- rb_cQuickjsAggregateError = rb_define_class_under(rb_mQuickjs, "AggregateError", rb_cQuickjsRuntimeError);
651
-
652
- rb_cQuickjsInterruptedError = rb_define_class_under(rb_mQuickjs, "InterruptedError", rb_cQuickjsRuntimeError);
653
- rb_cQuickjsNoAwaitError = rb_define_class_under(rb_mQuickjs, "NoAwaitError", rb_cQuickjsRuntimeError);
499
+ rb_require("json");
500
+ rb_require("securerandom");
501
+
502
+ VALUE r_module_quickjs = rb_define_module("Quickjs");
503
+ r_define_constants(r_module_quickjs);
504
+ r_define_exception_classes(r_module_quickjs);
505
+
506
+ VALUE r_class_vm = rb_define_class_under(r_module_quickjs, "VM", rb_cObject);
507
+ rb_define_alloc_func(r_class_vm, vm_alloc);
508
+ rb_define_method(r_class_vm, "initialize", vm_m_initialize, -1);
509
+ 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);
511
+ rb_define_method(r_class_vm, "import", vm_m_import, -1);
512
+ rb_define_method(r_class_vm, "logs", vm_m_logs, 0);
513
+ r_define_log_class(r_class_vm);
654
514
  }
@@ -12,4 +12,205 @@
12
12
  #include <string.h>
13
13
  #include <time.h>
14
14
 
15
+ const char *featureStdId = "feature_std";
16
+ const char *featureOsId = "feature_os";
17
+ const char *featureOsTimeoutId = "feature_os_timeout";
18
+
19
+ const char *undefinedId = "undefined";
20
+ const char *nanId = "NaN";
21
+
22
+ const char *native_errors[] = {
23
+ "SyntaxError",
24
+ "TypeError",
25
+ "ReferenceError",
26
+ "RangeError",
27
+ "EvalError",
28
+ "URIError",
29
+ "AggregateError"};
30
+
31
+ #define QUICKJSRB_SYM(id) \
32
+ (VALUE) { ID2SYM(rb_intern(id)) }
33
+
34
+ // VM data structure
35
+
36
+ typedef struct EvalTime
37
+ {
38
+ clock_t limit;
39
+ clock_t started_at;
40
+ } EvalTime;
41
+
42
+ typedef struct VMData
43
+ {
44
+ struct JSContext *context;
45
+ VALUE defined_functions;
46
+ struct EvalTime *eval_time;
47
+ VALUE logs;
48
+ } VMData;
49
+
50
+ static void vm_free(void *ptr)
51
+ {
52
+ VMData *data = (VMData *)ptr;
53
+ free(data->eval_time);
54
+
55
+ JSRuntime *runtime = JS_GetRuntime(data->context);
56
+ JS_SetInterruptHandler(runtime, NULL, NULL);
57
+ js_std_free_handlers(runtime);
58
+ JS_FreeContext(data->context);
59
+ JS_FreeRuntime(runtime);
60
+
61
+ xfree(ptr);
62
+ }
63
+
64
+ size_t vm_size(const void *data)
65
+ {
66
+ return sizeof(VMData);
67
+ }
68
+
69
+ static void vm_mark(void *ptr)
70
+ {
71
+ VMData *data = (VMData *)ptr;
72
+ rb_gc_mark_movable(data->defined_functions);
73
+ rb_gc_mark_movable(data->logs);
74
+ }
75
+
76
+ static const rb_data_type_t vm_type = {
77
+ .wrap_struct_name = "quickjsvm",
78
+ .function = {
79
+ .dmark = vm_mark,
80
+ .dfree = vm_free,
81
+ .dsize = vm_size,
82
+ },
83
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
84
+ };
85
+
86
+ static VALUE vm_alloc(VALUE r_self)
87
+ {
88
+ VMData *data;
89
+ VALUE obj = TypedData_Make_Struct(r_self, VMData, &vm_type, data);
90
+ data->defined_functions = rb_hash_new();
91
+ data->logs = rb_ary_new();
92
+
93
+ EvalTime *eval_time = malloc(sizeof(EvalTime));
94
+ data->eval_time = eval_time;
95
+
96
+ JSRuntime *runtime = JS_NewRuntime();
97
+ data->context = JS_NewContext(runtime);
98
+
99
+ return obj;
100
+ }
101
+
102
+ // Utils
103
+
104
+ static char *random_string()
105
+ {
106
+ VALUE r_rand = rb_funcall(
107
+ rb_const_get(rb_cClass, rb_intern("SecureRandom")),
108
+ rb_intern("alphanumeric"),
109
+ 1,
110
+ INT2NUM(12));
111
+ return StringValueCStr(r_rand);
112
+ }
113
+
114
+ static bool is_native_error_name(const char *error_name)
115
+ {
116
+ bool is_native_error = false;
117
+ int numStrings = sizeof(native_errors) / sizeof(native_errors[0]);
118
+ for (int i = 0; i < numStrings; i++)
119
+ {
120
+ if (strcmp(native_errors[i], error_name) == 0)
121
+ {
122
+ is_native_error = true;
123
+ break;
124
+ }
125
+ }
126
+ return is_native_error;
127
+ }
128
+
129
+ // Constants
130
+
131
+ static void r_define_constants(VALUE r_parent_class)
132
+ {
133
+ rb_define_const(r_parent_class, "MODULE_STD", QUICKJSRB_SYM(featureStdId));
134
+ rb_define_const(r_parent_class, "MODULE_OS", QUICKJSRB_SYM(featureOsId));
135
+ rb_define_const(r_parent_class, "FEATURES_TIMEOUT", QUICKJSRB_SYM(featureOsTimeoutId));
136
+
137
+ VALUE rb_cQuickjsValue = rb_define_class_under(r_parent_class, "Value", rb_cObject);
138
+ rb_define_const(rb_cQuickjsValue, "UNDEFINED", QUICKJSRB_SYM(undefinedId));
139
+ rb_define_const(rb_cQuickjsValue, "NAN", QUICKJSRB_SYM(nanId));
140
+ }
141
+
142
+ // Log class
143
+
144
+ static VALUE r_proc_pick_raw(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE blockarg)
145
+ {
146
+ return rb_hash_aref(block_arg, ID2SYM(rb_intern("raw")));
147
+ }
148
+
149
+ static VALUE r_log_m_raw(VALUE r_self)
150
+ {
151
+ VALUE row = rb_iv_get(r_self, "@row");
152
+ VALUE r_ary = rb_block_call(row, rb_intern("map"), 0, NULL, r_proc_pick_raw, Qnil);
153
+
154
+ return r_ary;
155
+ }
156
+
157
+ static VALUE r_proc_pick_c(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE blockarg)
158
+ {
159
+ return rb_hash_aref(block_arg, ID2SYM(rb_intern("c")));
160
+ }
161
+
162
+ static VALUE r_log_m_to_s(VALUE r_self)
163
+ {
164
+ VALUE row = rb_iv_get(r_self, "@row");
165
+ VALUE r_ary = rb_block_call(row, rb_intern("map"), 0, NULL, r_proc_pick_c, Qnil);
166
+
167
+ return rb_funcall(r_ary, rb_intern("join"), 1, rb_str_new2(" "));
168
+ }
169
+
170
+ static VALUE r_define_log_class(VALUE r_parent_class)
171
+ {
172
+ VALUE r_log_class = rb_define_class_under(r_parent_class, "Log", rb_cObject);
173
+ rb_define_attr(r_log_class, "severity", 1, 0);
174
+ rb_define_method(r_log_class, "raw", r_log_m_raw, 0);
175
+ rb_define_method(r_log_class, "to_s", r_log_m_to_s, 0);
176
+ rb_define_method(r_log_class, "inspect", r_log_m_to_s, 0);
177
+
178
+ return r_log_class;
179
+ }
180
+
181
+ // Exceptions
182
+
183
+ #define QUICKJSRB_ROOT_RUNTIME_ERROR "RuntimeError"
184
+ #define QUICKJSRB_INTERRUPTED_ERROR "InterruptedError"
185
+ #define QUICKJSRB_NO_AWAIT_ERROR "NoAwaitError"
186
+
187
+ #define QUICKJSRB_ERROR_FOR(name) \
188
+ (VALUE) { rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern(name)) }
189
+
190
+ VALUE vm_m_initialize_quickjs_error(VALUE self, VALUE r_message, VALUE r_js_name)
191
+ {
192
+ rb_call_super(1, &r_message);
193
+ rb_iv_set(self, "@js_name", r_js_name);
194
+
195
+ return self;
196
+ }
197
+
198
+ static void r_define_exception_classes(VALUE r_parent_class)
199
+ {
200
+ VALUE r_runtime_error = rb_define_class_under(r_parent_class, QUICKJSRB_ROOT_RUNTIME_ERROR, rb_eRuntimeError);
201
+ rb_define_method(r_runtime_error, "initialize", vm_m_initialize_quickjs_error, 2);
202
+ rb_define_attr(r_runtime_error, "js_name", 1, 0);
203
+
204
+ // JS native errors
205
+ int numStrings = sizeof(native_errors) / sizeof(native_errors[0]);
206
+ for (int i = 0; i < numStrings; i++)
207
+ {
208
+ rb_define_class_under(r_parent_class, native_errors[i], r_runtime_error);
209
+ }
210
+
211
+ // quickjsrb specific errors
212
+ rb_define_class_under(r_parent_class, QUICKJSRB_INTERRUPTED_ERROR, r_runtime_error);
213
+ rb_define_class_under(r_parent_class, QUICKJSRB_NO_AWAIT_ERROR, r_runtime_error);
214
+ }
215
+
15
216
  #endif /* QUICKJSRB_H */
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
data/lib/quickjs.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "timeout"
4
- require "json"
5
4
  require_relative "quickjs/version"
6
5
  require_relative "quickjs/quickjsrb"
7
6
 
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.1
4
+ version: 0.2.2
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-15 00:00:00.000000000 Z
11
+ date: 2024-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: securerandom
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: A native wrapper to run QuickJS in Ruby
28
42
  email:
29
43
  - k.hamasaki@gmail.com