quickjs 0.1.6 → 0.1.8

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: 6fa772eb6185fa2bc744d7bb7b0cdb2024ccaf2d2ce34cb221139aaf2e819371
4
- data.tar.gz: de4b5651b61eaaafe975ba56ae24b954c28efc9ea0ba2a6fb34f149b62d4aa58
3
+ metadata.gz: 558b76a18b29f977c09990bec267141fb9feae1f04ddd4265971c7c24e823b9b
4
+ data.tar.gz: b2dbe1a4a121e92e7f1792690fe755ce021091a4ff402a773b9853c885256dcc
5
5
  SHA512:
6
- metadata.gz: 1095f8d04c0c90756506b8bd16a519b207b3b2a0eda64abecec8fb541a0a5a5c0e11240abce18b03e4ee4d7b4c3a53138513218f12afa4d7e46462f3a93680ef
7
- data.tar.gz: 3237ad5b23217199900665605629c5b69c0333ea5951f3a43cf2e26ac65d6a718ff94f262fdaeb5bf0c3b132e60cb0561d60a121045a26bb201bbfd63e8e3a9c
6
+ metadata.gz: 338bc63e7590c7043cf86c7045f3e93c8269110d3daf49be87ef6477cd99086700a6c0a26dcdc8cf7eb2ce43acc025c419812eeb23ab0b155b59ddd335454db5
7
+ data.tar.gz: cb075876cdc10e081b95c40882acbe7ed272dacca56050564888a8df5a68c058c31fa61d9151bddf794d6846c1f00052ae74e3d439c493761bf47154f6fcfdc2
data/README.md CHANGED
@@ -89,6 +89,11 @@ vm = Quickjs::VM.new(
89
89
  vm = Quickjs::VM.new(
90
90
  features: [::Quickjs::MODULE_OS],
91
91
  )
92
+
93
+ # `eval_code` will be interrupted after 1 sec (default: 100 msec)
94
+ vm = Quickjs::VM.new(
95
+ timeout_msec: 1_000,
96
+ )
92
97
  ```
93
98
 
94
99
  #### Dispose VM explicitly
@@ -11,7 +11,7 @@ $srcs = [
11
11
  'cutils.c',
12
12
  'quickjs.c',
13
13
  'quickjs-libc.c',
14
- 'quickjsrb-runtime-state.c',
14
+ 'procs.c',
15
15
  'quickjsrb.c',
16
16
  ]
17
17
 
@@ -1,4 +1,4 @@
1
- #include "quickjsrb-runtime-state.h"
1
+ #include "procs.h"
2
2
 
3
3
  unsigned int hash(const char *key) {
4
4
  unsigned long int value = 0;
@@ -8,7 +8,7 @@ unsigned int hash(const char *key) {
8
8
  for (; i < key_len; ++i) {
9
9
  value = value * 37 + key[i];
10
10
  }
11
- return value % MAX_NUM_OF_PROC;
11
+ return value % MAX_NUM_OF_PROCS;
12
12
  }
13
13
 
14
14
  ProcEntry *create_proc_entry(const char *key, VALUE proc) {
@@ -21,8 +21,8 @@ ProcEntry *create_proc_entry(const char *key, VALUE proc) {
21
21
 
22
22
  ProcEntryMap *create_proc_entries() {
23
23
  ProcEntryMap *entryMap = malloc(sizeof(ProcEntryMap));
24
- entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROC);
25
- for (int i = 0; i < MAX_NUM_OF_PROC; ++i) {
24
+ entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROCS);
25
+ for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
26
26
  entryMap->entries[i] = NULL;
27
27
  }
28
28
  return entryMap;
@@ -66,7 +66,7 @@ VALUE get_proc(ProcEntryMap *entryMap, const char *key) {
66
66
  }
67
67
 
68
68
  void free_proc_entry_map(ProcEntryMap *entryMap) {
69
- for (int i = 0; i < MAX_NUM_OF_PROC; ++i) {
69
+ for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
70
70
  ProcEntry *entry = entryMap->entries[i];
71
71
  while (entry != NULL) {
72
72
  ProcEntry *temp = entry;
@@ -78,15 +78,3 @@ void free_proc_entry_map(ProcEntryMap *entryMap) {
78
78
  free(entryMap->entries);
79
79
  free(entryMap);
80
80
  }
81
-
82
- QuickjsrbRuntimeState *create_quickjsrb_runtime_state() {
83
- QuickjsrbRuntimeState *state = malloc(sizeof(QuickjsrbRuntimeState));
84
- state->procs = create_proc_entries();
85
-
86
- return state;
87
- }
88
-
89
- void free_quickjsrb_runtime_state(QuickjsrbRuntimeState *state) {
90
- free_proc_entry_map(state->procs);
91
- free(state);
92
- }
@@ -1,6 +1,6 @@
1
1
  #include "ruby.h"
2
2
 
3
- #define MAX_NUM_OF_PROC 100
3
+ #define MAX_NUM_OF_PROCS 100
4
4
 
5
5
  typedef struct ProcEntry {
6
6
  char *key;
@@ -12,11 +12,7 @@ typedef struct ProcEntryMap {
12
12
  ProcEntry **entries;
13
13
  } ProcEntryMap;
14
14
 
15
- typedef struct QuickjsrbRuntimeState {
16
- ProcEntryMap *procs;
17
- } QuickjsrbRuntimeState;
18
-
19
- QuickjsrbRuntimeState *create_quickjsrb_runtime_state();
20
- VALUE get_proc(ProcEntryMap *entryMap, const char *key);
15
+ ProcEntryMap *create_proc_entries();
21
16
  void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc);
22
- void free_quickjsrb_runtime_state(QuickjsrbRuntimeState *state);
17
+ VALUE get_proc(ProcEntryMap *entryMap, const char *key);
18
+ void free_proc_entry_map(ProcEntryMap *entryMap);
@@ -1,64 +1,42 @@
1
1
  #include "quickjsrb.h"
2
- #include "quickjsrb-runtime-state.h"
3
2
 
4
- struct qvmdata {
3
+ typedef struct EvalTime {
4
+ clock_t limit;
5
+ clock_t started_at;
6
+ } EvalTime;
7
+
8
+ typedef struct VMData {
5
9
  char alive;
6
10
  struct JSContext *context;
7
- struct QuickjsrbRuntimeState *state;
8
- };
11
+ struct ProcEntryMap *procs;
12
+ struct EvalTime *eval_time;
13
+ } VMData;
9
14
 
10
- void qvm_free(void* data)
15
+ void vm_free(void* data)
11
16
  {
12
17
  free(data);
13
18
  }
14
19
 
15
- size_t qvm_size(const void* data)
20
+ size_t vm_size(const void* data)
16
21
  {
17
22
  return sizeof(data);
18
23
  }
19
24
 
20
- static const rb_data_type_t qvm_type = {
21
- .wrap_struct_name = "qvm",
25
+ static const rb_data_type_t vm_type = {
26
+ .wrap_struct_name = "vm",
22
27
  .function = {
23
28
  .dmark = NULL,
24
- .dfree = qvm_free,
25
- .dsize = qvm_size,
29
+ .dfree = vm_free,
30
+ .dsize = vm_size,
26
31
  },
27
32
  .data = NULL,
28
33
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
29
34
  };
30
35
 
31
- VALUE qvm_alloc(VALUE self)
36
+ VALUE vm_alloc(VALUE self)
32
37
  {
33
- struct qvmdata *data;
34
-
35
- return TypedData_Make_Struct(self, struct qvmdata, &qvm_type, data);
36
- }
37
-
38
- static JSValue js_quickjsrb_call_global (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
39
- struct qvmdata *data = JS_GetContextOpaque(ctx);
40
- JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
41
- const char *funcName = JS_ToCString(ctx, maybeFuncName);
42
-
43
- VALUE proc = get_proc(data->state->procs, funcName);
44
- if (proc == Qnil) {
45
- return JS_NewString(ctx, "missing");
46
- }
47
- JS_FreeValue(ctx, maybeFuncName);
48
-
49
- // TODO: support multiple args
50
- JSValue maybeString;
51
- VALUE r_array = rb_ary_new2(argc);
52
- for (int i = 1; i < argc; ++i) {
53
- maybeString = JS_ToString(ctx, argv[i]);
54
- const char *msg = JS_ToCString(ctx, maybeString);
55
- rb_ary_push(r_array, rb_str_new2(msg));
56
- }
57
- JS_FreeValue(ctx, maybeString);
58
-
59
- VALUE r_result = rb_funcall(proc, rb_intern("call"), argc - 1, r_array);
60
- char *result = StringValueCStr(r_result);
61
- return JS_NewString(ctx, result);
38
+ VMData *data;
39
+ return TypedData_Make_Struct(self, VMData, &vm_type, data);
62
40
  }
63
41
 
64
42
  VALUE rb_mQuickjs;
@@ -68,7 +46,63 @@ const char *nanId = "NaN";
68
46
  const char *featureStdId = "feature_std";
69
47
  const char *featureOsId = "feature_os";
70
48
 
71
- VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
49
+ JSValue to_js_value(JSContext *ctx, VALUE r_value) {
50
+ switch (TYPE(r_value)) {
51
+ case T_NIL:
52
+ return JS_NULL;
53
+ case T_FIXNUM:
54
+ case T_FLOAT: {
55
+ VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
56
+ char *str = StringValueCStr(r_str);
57
+ JSValue global = JS_GetGlobalObject(ctx);
58
+ JSValue numberClass = JS_GetPropertyStr(ctx, global, "Number");
59
+ JSValue j_str = JS_NewString(ctx, str);
60
+ JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
61
+ JS_FreeValue(ctx, global);
62
+ JS_FreeValue(ctx, numberClass);
63
+
64
+ return stringified;
65
+ }
66
+ case T_STRING: {
67
+ char *str = StringValueCStr(r_value);
68
+
69
+ return JS_NewString(ctx, str);
70
+ }
71
+ case T_SYMBOL: {
72
+ VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
73
+ char *str = StringValueCStr(r_str);
74
+
75
+ return JS_NewString(ctx, str);
76
+ }
77
+ case T_TRUE:
78
+ return JS_TRUE;
79
+ case T_FALSE:
80
+ return JS_FALSE;
81
+ case T_HASH:
82
+ case T_ARRAY: {
83
+ VALUE r_json_str = rb_funcall(r_value, rb_intern("to_json"), 0, NULL);
84
+ char *str = StringValueCStr(r_json_str);
85
+ JSValue global = JS_GetGlobalObject(ctx);
86
+ JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
87
+ JSValue parseFunc = JS_GetPropertyStr(ctx, jsonClass, "parse");
88
+ JSValue j_str = JS_NewString(ctx, str);
89
+ JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
90
+ JS_FreeValue(ctx, global);
91
+ JS_FreeValue(ctx, parseFunc);
92
+ JS_FreeValue(ctx, jsonClass);
93
+
94
+ return stringified;
95
+ }
96
+ default: {
97
+ VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
98
+ char *str = StringValueCStr(r_inspect_str);
99
+
100
+ return JS_NewString(ctx, str);
101
+ }
102
+ }
103
+ }
104
+
105
+ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
72
106
  switch(JS_VALUE_GET_NORM_TAG(jsv)) {
73
107
  case JS_TAG_INT: {
74
108
  int int_res = 0;
@@ -94,7 +128,7 @@ VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
94
128
  case JS_TAG_OBJECT: {
95
129
  int promiseState = JS_PromiseState(ctx, jsv);
96
130
  if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
97
- return to_rb_value(js_std_await(ctx, jsv), ctx);
131
+ return to_rb_value(js_std_await(ctx, jsv), ctx); // should have timeout
98
132
  } else if (promiseState == JS_PROMISE_REJECTED) {
99
133
  return to_rb_value(JS_Throw(ctx, JS_PromiseResult(ctx, jsv)), ctx);
100
134
  }
@@ -159,7 +193,22 @@ VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
159
193
  }
160
194
  }
161
195
 
162
- VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
196
+ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv) {
197
+ VMData *data = JS_GetContextOpaque(ctx);
198
+ JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
199
+ const char *funcName = JS_ToCString(ctx, maybeFuncName);
200
+ JS_FreeValue(ctx, maybeFuncName);
201
+
202
+ VALUE proc = get_proc(data->procs, funcName);
203
+ if (proc == Qnil) { // Shouldn't happen
204
+ return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
205
+ }
206
+
207
+ VALUE r_result = rb_apply(proc, rb_intern("call"), to_rb_value(argv[1], ctx));
208
+ return to_js_value(ctx, r_result);
209
+ }
210
+
211
+ VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
163
212
  {
164
213
  VALUE r_opts;
165
214
  rb_scan_args(argc, argv, ":", &r_opts);
@@ -171,14 +220,19 @@ VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
171
220
  if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
172
221
  VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
173
222
  if (NIL_P(r_features)) r_features = rb_ary_new();
223
+ VALUE r_timeout_msec = rb_hash_aref(r_opts, ID2SYM(rb_intern("timeout_msec")));
224
+ if (NIL_P(r_timeout_msec)) r_timeout_msec= UINT2NUM(100);
174
225
 
175
- struct qvmdata *data;
176
- TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
226
+ VMData *data;
227
+ TypedData_Get_Struct(self, VMData, &vm_type, data);
177
228
 
178
- QuickjsrbRuntimeState *state= create_quickjsrb_runtime_state();
179
229
  JSRuntime *runtime = JS_NewRuntime();
180
230
  data->context = JS_NewContext(runtime);
181
- data->state = state;
231
+ data->procs = create_proc_entries();
232
+
233
+ EvalTime *eval_time = malloc(sizeof(EvalTime));
234
+ data->eval_time = eval_time;
235
+ data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
182
236
  data->alive = 1;
183
237
  JS_SetContextOpaque(data->context, data);
184
238
 
@@ -222,15 +276,24 @@ VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
222
276
  return self;
223
277
  }
224
278
 
225
- VALUE qvm_m_evalCode(VALUE self, VALUE r_code)
279
+ static int interrupt_handler(JSRuntime *runtime, void *opaque) {
280
+ EvalTime *eval_time = opaque;
281
+ return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
282
+ }
283
+
284
+ VALUE vm_m_evalCode(VALUE self, VALUE r_code)
226
285
  {
227
- struct qvmdata *data;
228
- TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
286
+ VMData *data;
287
+ TypedData_Get_Struct(self, VMData, &vm_type, data);
229
288
 
230
289
  if (data->alive < 1) {
231
290
  rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
232
291
  return Qnil;
233
292
  }
293
+
294
+ data->eval_time->started_at = clock();
295
+ JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
296
+
234
297
  char *code = StringValueCStr(r_code);
235
298
  JSValue codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
236
299
  VALUE result = to_rb_value(codeResult, data->context);
@@ -239,19 +302,19 @@ VALUE qvm_m_evalCode(VALUE self, VALUE r_code)
239
302
  return result;
240
303
  }
241
304
 
242
- VALUE qvm_m_defineGlobalFunction(VALUE self, VALUE r_name)
305
+ VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
243
306
  {
244
307
  rb_need_block();
245
308
 
246
- struct qvmdata *data;
247
- TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
309
+ VMData *data;
310
+ TypedData_Get_Struct(self, VMData, &vm_type, data);
248
311
 
249
312
  if (rb_block_given_p()) {
250
313
  VALUE proc = rb_block_proc();
251
314
 
252
315
  char *funcName = StringValueCStr(r_name);
253
316
 
254
- set_proc(data->state->procs, funcName, proc);
317
+ set_proc(data->procs, funcName, proc);
255
318
 
256
319
  const char* template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
257
320
  int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
@@ -268,14 +331,15 @@ VALUE qvm_m_defineGlobalFunction(VALUE self, VALUE r_name)
268
331
  return Qnil;
269
332
  }
270
333
 
271
- VALUE qvm_m_dispose(VALUE self)
334
+ VALUE vm_m_dispose(VALUE self)
272
335
  {
273
- struct qvmdata *data;
274
- TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
336
+ VMData *data;
337
+ TypedData_Get_Struct(self, VMData, &vm_type, data);
275
338
 
276
339
  JSRuntime *runtime = JS_GetRuntime(data->context);
340
+ JS_SetInterruptHandler(runtime, NULL, NULL);
277
341
  js_std_free_handlers(runtime);
278
- free_quickjsrb_runtime_state(data->state);
342
+ free_proc_entry_map(data->procs);
279
343
  JS_FreeContext(data->context);
280
344
  JS_FreeRuntime(runtime);
281
345
  data->alive = 0;
@@ -295,9 +359,9 @@ Init_quickjsrb(void)
295
359
  rb_define_const(valueClass, "NAN", ID2SYM(rb_intern(nanId)));
296
360
 
297
361
  VALUE vmClass = rb_define_class_under(rb_mQuickjs, "VM", rb_cObject);
298
- rb_define_alloc_func(vmClass, qvm_alloc);
299
- rb_define_method(vmClass, "initialize", qvm_m_initialize, -1);
300
- rb_define_method(vmClass, "eval_code", qvm_m_evalCode, 1);
301
- rb_define_method(vmClass, "define_function", qvm_m_defineGlobalFunction, 1);
302
- rb_define_method(vmClass, "dispose!", qvm_m_dispose, 0);
362
+ rb_define_alloc_func(vmClass, vm_alloc);
363
+ rb_define_method(vmClass, "initialize", vm_m_initialize, -1);
364
+ rb_define_method(vmClass, "eval_code", vm_m_evalCode, 1);
365
+ rb_define_method(vmClass, "define_function", vm_m_defineGlobalFunction, 1);
366
+ rb_define_method(vmClass, "dispose!", vm_m_dispose, 0);
303
367
  }
@@ -10,5 +10,8 @@
10
10
  #include <stdint.h>
11
11
  #include <stdio.h>
12
12
  #include <string.h>
13
+ #include <time.h>
14
+
15
+ #include "procs.h"
13
16
 
14
17
  #endif /* QUICKJSRB_H */
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.8"
5
5
  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.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - hmsk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-08 00:00:00.000000000 Z
11
+ date: 2024-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -37,6 +37,8 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - ext/quickjsrb/extconf.rb
40
+ - ext/quickjsrb/procs.c
41
+ - ext/quickjsrb/procs.h
40
42
  - ext/quickjsrb/quickjs/LICENSE
41
43
  - ext/quickjsrb/quickjs/cutils.c
42
44
  - ext/quickjsrb/quickjs/cutils.h
@@ -62,8 +64,6 @@ files:
62
64
  - ext/quickjsrb/quickjs/run-test262.c
63
65
  - ext/quickjsrb/quickjs/unicode_gen.c
64
66
  - ext/quickjsrb/quickjs/unicode_gen_def.h
65
- - ext/quickjsrb/quickjsrb-runtime-state.c
66
- - ext/quickjsrb/quickjsrb-runtime-state.h
67
67
  - ext/quickjsrb/quickjsrb.c
68
68
  - ext/quickjsrb/quickjsrb.h
69
69
  - lib/quickjs.rb