quickjs 0.1.7 → 0.1.9

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: 9a639b7afafb5c155725cba8d7927b7f4e146e21369a65a3902e8fe23ef1e3dd
4
- data.tar.gz: b0e098ae6c193e761f456642cdcaf5396251785d5bc56b5e807fd540a0615f87
3
+ metadata.gz: f0461f98dbe3171e88609b060aa9fb414ba881b0977d983d6ec7897726a0559c
4
+ data.tar.gz: 84ed4cf19c1d5191e9cd57b789cc3138691f7ad2059b515a2ce4280051105c46
5
5
  SHA512:
6
- metadata.gz: 9ad93ee1c056d68409daeba63515b8afa8555150044e0543a97bb2f3cb64abf5b8f5a5b1fce75e952721b0277a7807ce521289b70b63157cc489032da7da8f7c
7
- data.tar.gz: b36a5f2e7402156e94403c5110ec1acc6141d657b236562b18b57c63c68fd966d4ef25096e3b515f9633093bee2e2a89f65578b4426e0c4184197078c9c2c578
6
+ metadata.gz: c98d7daf116e5513a19b99e4ef387f3b1b60389c3bb9f91822cc1d437bf87cc72041cd97c7cdf2a8a8cb55a86df93a3cf7fe472733017ae4a315831397a56cd5
7
+ data.tar.gz: f177d0e31b8d6499fcf922bd85dc594aa2acd0bf8d93d696256c4a3a7a1595eeb874df7e14e8447694a3045b3f2b9d8279633550e535fc8f257311c613c043b6
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,6 @@ $srcs = [
11
11
  'cutils.c',
12
12
  'quickjs.c',
13
13
  'quickjs-libc.c',
14
- 'procs.c',
15
14
  'quickjsrb.c',
16
15
  ]
17
16
 
@@ -1,15 +1,22 @@
1
1
  #include "quickjsrb.h"
2
2
 
3
+ typedef struct EvalTime {
4
+ clock_t limit;
5
+ clock_t started_at;
6
+ } EvalTime;
7
+
3
8
  typedef struct VMData {
4
9
  char alive;
5
10
  struct JSContext *context;
6
- struct QuickjsrbRuntimeState *state;
7
- struct ProcEntryMap *procs;
11
+ VALUE defined_functions;
12
+ struct EvalTime *eval_time;
8
13
  } VMData;
9
14
 
10
- void vm_free(void* data)
15
+ static void vm_free(void* ptr)
11
16
  {
12
- free(data);
17
+ VMData *data = (VMData *)ptr;
18
+ free(data->eval_time);
19
+ xfree(ptr);
13
20
  }
14
21
 
15
22
  size_t vm_size(const void* data)
@@ -17,10 +24,16 @@ size_t vm_size(const void* data)
17
24
  return sizeof(data);
18
25
  }
19
26
 
27
+ static void vm_mark(void *ptr)
28
+ {
29
+ VMData *data = (VMData *)ptr;
30
+ rb_gc_mark_movable(data->defined_functions);
31
+ }
32
+
20
33
  static const rb_data_type_t vm_type = {
21
- .wrap_struct_name = "vm",
34
+ .wrap_struct_name = "quickjsvm",
22
35
  .function = {
23
- .dmark = NULL,
36
+ .dmark = vm_mark,
24
37
  .dfree = vm_free,
25
38
  .dsize = vm_size,
26
39
  },
@@ -28,10 +41,16 @@ static const rb_data_type_t vm_type = {
28
41
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
29
42
  };
30
43
 
31
- VALUE vm_alloc(VALUE self)
44
+ static VALUE vm_alloc(VALUE self)
32
45
  {
33
46
  VMData *data;
34
- return TypedData_Make_Struct(self, VMData, &vm_type, data);
47
+ VALUE obj = TypedData_Make_Struct(self, VMData, &vm_type, data);
48
+ data->defined_functions = rb_hash_new();
49
+
50
+ EvalTime *eval_time = malloc(sizeof(EvalTime));
51
+ data->eval_time = eval_time;
52
+
53
+ return obj;
35
54
  }
36
55
 
37
56
  VALUE rb_mQuickjs;
@@ -55,6 +74,7 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value) {
55
74
  JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
56
75
  JS_FreeValue(ctx, global);
57
76
  JS_FreeValue(ctx, numberClass);
77
+ JS_FreeValue(ctx, j_str);
58
78
 
59
79
  return stringified;
60
80
  }
@@ -83,8 +103,9 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value) {
83
103
  JSValue j_str = JS_NewString(ctx, str);
84
104
  JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
85
105
  JS_FreeValue(ctx, global);
86
- JS_FreeValue(ctx, parseFunc);
87
106
  JS_FreeValue(ctx, jsonClass);
107
+ JS_FreeValue(ctx, parseFunc);
108
+ JS_FreeValue(ctx, j_str);
88
109
 
89
110
  return stringified;
90
111
  }
@@ -118,14 +139,23 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
118
139
  case JS_TAG_STRING: {
119
140
  JSValue maybeString = JS_ToString(ctx, jsv);
120
141
  const char *msg = JS_ToCString(ctx, maybeString);
142
+ JS_FreeValue(ctx, maybeString);
121
143
  return rb_str_new2(msg);
122
144
  }
123
145
  case JS_TAG_OBJECT: {
124
146
  int promiseState = JS_PromiseState(ctx, jsv);
125
147
  if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
126
- return to_rb_value(js_std_await(ctx, jsv), ctx);
148
+ JSValue awaited = js_std_await(ctx, jsv);
149
+ VALUE rb_awaited = to_rb_value(awaited, ctx); // TODO: should have timeout
150
+ JS_FreeValue(ctx, awaited);
151
+ return rb_awaited;
127
152
  } else if (promiseState == JS_PROMISE_REJECTED) {
128
- return to_rb_value(JS_Throw(ctx, JS_PromiseResult(ctx, jsv)), ctx);
153
+ JSValue promiseResult = JS_PromiseResult(ctx, jsv);
154
+ JSValue throw = JS_Throw(ctx, promiseResult);
155
+ JS_FreeValue(ctx, promiseResult);
156
+ VALUE rb_errored = to_rb_value(throw, ctx);
157
+ JS_FreeValue(ctx, throw);
158
+ return rb_errored;
129
159
  }
130
160
 
131
161
  JSValue global = JS_GetGlobalObject(ctx);
@@ -194,16 +224,17 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
194
224
  const char *funcName = JS_ToCString(ctx, maybeFuncName);
195
225
  JS_FreeValue(ctx, maybeFuncName);
196
226
 
197
- VALUE proc = get_proc(data->procs, funcName);
227
+ VALUE proc = rb_hash_aref(data->defined_functions, rb_str_new2(funcName));
198
228
  if (proc == Qnil) { // Shouldn't happen
199
229
  return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
200
230
  }
201
231
 
232
+ // TODO: cover timeout for calling proc
202
233
  VALUE r_result = rb_apply(proc, rb_intern("call"), to_rb_value(argv[1], ctx));
203
234
  return to_js_value(ctx, r_result);
204
235
  }
205
236
 
206
- VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
237
+ static VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
207
238
  {
208
239
  VALUE r_opts;
209
240
  rb_scan_args(argc, argv, ":", &r_opts);
@@ -215,13 +246,15 @@ VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
215
246
  if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
216
247
  VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
217
248
  if (NIL_P(r_features)) r_features = rb_ary_new();
249
+ VALUE r_timeout_msec = rb_hash_aref(r_opts, ID2SYM(rb_intern("timeout_msec")));
250
+ if (NIL_P(r_timeout_msec)) r_timeout_msec= UINT2NUM(100);
218
251
 
219
252
  VMData *data;
220
253
  TypedData_Get_Struct(self, VMData, &vm_type, data);
221
254
 
222
255
  JSRuntime *runtime = JS_NewRuntime();
223
256
  data->context = JS_NewContext(runtime);
224
- data->procs = create_proc_entries();
257
+ data->eval_time->limit = (clock_t)(CLOCKS_PER_SEC * NUM2UINT(r_timeout_msec) / 1000);
225
258
  data->alive = 1;
226
259
  JS_SetContextOpaque(data->context, data);
227
260
 
@@ -265,7 +298,12 @@ VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
265
298
  return self;
266
299
  }
267
300
 
268
- VALUE vm_m_evalCode(VALUE self, VALUE r_code)
301
+ static int interrupt_handler(JSRuntime *runtime, void *opaque) {
302
+ EvalTime *eval_time = opaque;
303
+ return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
304
+ }
305
+
306
+ static VALUE vm_m_evalCode(VALUE self, VALUE r_code)
269
307
  {
270
308
  VMData *data;
271
309
  TypedData_Get_Struct(self, VMData, &vm_type, data);
@@ -274,6 +312,10 @@ VALUE vm_m_evalCode(VALUE self, VALUE r_code)
274
312
  rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
275
313
  return Qnil;
276
314
  }
315
+
316
+ data->eval_time->started_at = clock();
317
+ JS_SetInterruptHandler(JS_GetRuntime(data->context), interrupt_handler, data->eval_time);
318
+
277
319
  char *code = StringValueCStr(r_code);
278
320
  JSValue codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
279
321
  VALUE result = to_rb_value(codeResult, data->context);
@@ -282,7 +324,7 @@ VALUE vm_m_evalCode(VALUE self, VALUE r_code)
282
324
  return result;
283
325
  }
284
326
 
285
- VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
327
+ static VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
286
328
  {
287
329
  rb_need_block();
288
330
 
@@ -294,7 +336,7 @@ VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
294
336
 
295
337
  char *funcName = StringValueCStr(r_name);
296
338
 
297
- set_proc(data->procs, funcName, proc);
339
+ rb_hash_aset(data->defined_functions, r_name, proc);
298
340
 
299
341
  const char* template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
300
342
  int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
@@ -311,14 +353,14 @@ VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
311
353
  return Qnil;
312
354
  }
313
355
 
314
- VALUE vm_m_dispose(VALUE self)
356
+ static VALUE vm_m_dispose(VALUE self)
315
357
  {
316
358
  VMData *data;
317
359
  TypedData_Get_Struct(self, VMData, &vm_type, data);
318
360
 
319
361
  JSRuntime *runtime = JS_GetRuntime(data->context);
362
+ JS_SetInterruptHandler(runtime, NULL, NULL);
320
363
  js_std_free_handlers(runtime);
321
- free_proc_entry_map(data->procs);
322
364
  JS_FreeContext(data->context);
323
365
  JS_FreeRuntime(runtime);
324
366
  data->alive = 0;
@@ -10,7 +10,6 @@
10
10
  #include <stdint.h>
11
11
  #include <stdio.h>
12
12
  #include <string.h>
13
-
14
- #include "procs.h"
13
+ #include <time.h>
15
14
 
16
15
  #endif /* QUICKJSRB_H */
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.1.7"
4
+ VERSION = "0.1.9"
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.7
4
+ version: 0.1.9
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-09 00:00:00.000000000 Z
11
+ date: 2024-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -37,8 +37,6 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - ext/quickjsrb/extconf.rb
40
- - ext/quickjsrb/procs.c
41
- - ext/quickjsrb/procs.h
42
40
  - ext/quickjsrb/quickjs/LICENSE
43
41
  - ext/quickjsrb/quickjs/cutils.c
44
42
  - ext/quickjsrb/quickjs/cutils.h
@@ -1,80 +0,0 @@
1
- #include "procs.h"
2
-
3
- unsigned int hash(const char *key) {
4
- unsigned long int value = 0;
5
- unsigned int i = 0;
6
- unsigned int key_len = strlen(key);
7
-
8
- for (; i < key_len; ++i) {
9
- value = value * 37 + key[i];
10
- }
11
- return value % MAX_NUM_OF_PROCS;
12
- }
13
-
14
- ProcEntry *create_proc_entry(const char *key, VALUE proc) {
15
- ProcEntry *entry = malloc(sizeof(ProcEntry));
16
- entry->key = strdup(key);
17
- entry->proc = proc;
18
- entry->next = NULL;
19
- return entry;
20
- }
21
-
22
- ProcEntryMap *create_proc_entries() {
23
- ProcEntryMap *entryMap = malloc(sizeof(ProcEntryMap));
24
- entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROCS);
25
- for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
26
- entryMap->entries[i] = NULL;
27
- }
28
- return entryMap;
29
- }
30
-
31
- void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc) {
32
- unsigned int slot = hash(key);
33
- ProcEntry *entry = entryMap->entries[slot];
34
-
35
- if (entry == NULL) {
36
- entryMap->entries[slot] = create_proc_entry(key, proc);
37
- return;
38
- }
39
-
40
- ProcEntry *prev;
41
- while (entry != NULL) {
42
- if (strcmp(entry->key, key) == 0) {
43
- entry->proc = proc;
44
- return;
45
- }
46
- prev = entry;
47
- entry = prev->next;
48
- }
49
-
50
- prev->next = create_proc_entry(key, proc);
51
- }
52
-
53
- VALUE get_proc(ProcEntryMap *entryMap, const char *key) {
54
- unsigned int slot = hash(key);
55
-
56
- ProcEntry *entry = entryMap->entries[slot];
57
-
58
- while (entry != NULL) {
59
- if (strcmp(entry->key, key) == 0) {
60
- return entry->proc;
61
- }
62
- entry = entry->next;
63
- }
64
-
65
- return Qnil;
66
- }
67
-
68
- void free_proc_entry_map(ProcEntryMap *entryMap) {
69
- for (int i = 0; i < MAX_NUM_OF_PROCS; ++i) {
70
- ProcEntry *entry = entryMap->entries[i];
71
- while (entry != NULL) {
72
- ProcEntry *temp = entry;
73
- entry = entry->next;
74
- free(temp->key);
75
- free(temp);
76
- }
77
- }
78
- free(entryMap->entries);
79
- free(entryMap);
80
- }
@@ -1,18 +0,0 @@
1
- #include "ruby.h"
2
-
3
- #define MAX_NUM_OF_PROCS 100
4
-
5
- typedef struct ProcEntry {
6
- char *key;
7
- VALUE proc;
8
- struct ProcEntry *next;
9
- } ProcEntry;
10
-
11
- typedef struct ProcEntryMap {
12
- ProcEntry **entries;
13
- } ProcEntryMap;
14
-
15
- ProcEntryMap *create_proc_entries();
16
- void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc);
17
- VALUE get_proc(ProcEntryMap *entryMap, const char *key);
18
- void free_proc_entry_map(ProcEntryMap *entryMap);