quickjs 0.2.3 → 0.4.0

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: 5f57f31bd8921f897f9d1c81d1c50d24774bbfba5d31a99528b138e82d3fad58
4
- data.tar.gz: 28216a7b1711fe08ad3e2a5b021abebaadd2b5d3808584ff259d0129abf0adf6
3
+ metadata.gz: 49e231625e712ca98ffcab031b747d11392608eb840b2cafa6881173a5be9ac8
4
+ data.tar.gz: '099960b9c78f6f7a75990386186a0b149102d73ccbbbb7a1db7e52e120c57833'
5
5
  SHA512:
6
- metadata.gz: 355b53297b3c27269b71dca93e057ecfe02eac70755b5bd336b212092fb6da3c14ff3b2dd2f77214a62891b28d860e33f961329374ddef485e168dea41b50097
7
- data.tar.gz: 7ff16986a8ef0efeff790389805fa6d36f6b03b5983e8f8131ff33f8369514344cc9f9b9f103f036ad305441e06dfdfdacd125bdb8b061967337235d4913c525
6
+ metadata.gz: 3cad539bfe3b3a7e254ef4c1a2919c7a12afd29e67e5729acc2f8356896898eef71f1f46c35a62e351b1ac03ba0b8e9fe357c4f69781d5b8a203525068e22261
7
+ data.tar.gz: d4fcdfe872d91b0fd86bcccd9e58262b233ee99d31a529ba288583e2953ea191f4b9269dacbac9a47256f5d2111f737741fce4d5d26cba5e4fcc97bc450be96b
@@ -1,22 +1,22 @@
1
1
  #include "quickjsrb.h"
2
2
 
3
- JSValue to_js_value(JSContext *ctx, VALUE r_value)
3
+ JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
4
4
  {
5
- if (RTEST(rb_funcall(
6
- r_value,
7
- rb_intern("is_a?"),
8
- 1, rb_const_get(rb_cClass, rb_intern("Exception")))))
9
- {
10
- JSValue j_error = JS_NewError(ctx);
11
- VALUE r_str = rb_funcall(r_value, rb_intern("message"), 0, NULL);
12
- char *exceptionMessage = StringValueCStr(r_str);
13
- VALUE r_exception_name = rb_funcall(rb_funcall(r_value, rb_intern("class"), 0, NULL), rb_intern("name"), 0, NULL);
14
- char *exceptionName = StringValueCStr(r_exception_name);
15
- JS_SetPropertyStr(ctx, j_error, "name", JS_NewString(ctx, exceptionName));
16
- JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, exceptionMessage));
17
- return JS_Throw(ctx, j_error);
18
- }
5
+ JSValue j_error = JS_NewError(ctx); // may wanna have custom error class to determine in JS' end
19
6
 
7
+ VALUE r_object_id = rb_funcall(r_error, rb_intern("object_id"), 0, NULL);
8
+ int objectId = NUM2INT(r_object_id);
9
+ JS_SetPropertyStr(ctx, j_error, "rb_object_id", JS_NewInt32(ctx, objectId));
10
+
11
+ VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0, NULL);
12
+ const char *errorMessage = StringValueCStr(r_exception_message);
13
+ JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, errorMessage));
14
+
15
+ return j_error;
16
+ }
17
+
18
+ JSValue to_js_value(JSContext *ctx, VALUE r_value)
19
+ {
20
20
  switch (TYPE(r_value))
21
21
  {
22
22
  case T_NIL:
@@ -72,6 +72,13 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
72
72
  }
73
73
  default:
74
74
  {
75
+ if (TYPE(r_value) == T_OBJECT && RTEST(rb_funcall(
76
+ r_value,
77
+ rb_intern("is_a?"),
78
+ 1, rb_const_get(rb_cClass, rb_intern("Exception")))))
79
+ {
80
+ return j_error_from_ruby_error(ctx, r_value);
81
+ }
75
82
  VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
76
83
  char *str = StringValueCStr(r_inspect_str);
77
84
 
@@ -80,6 +87,28 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
80
87
  }
81
88
  }
82
89
 
90
+ VALUE find_ruby_error(JSContext *ctx, JSValue j_error)
91
+ {
92
+ JSValue j_errorOriginalRubyObjectId = JS_GetPropertyStr(ctx, j_error, "rb_object_id");
93
+ int errorOriginalRubyObjectId = 0;
94
+ if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT)
95
+ {
96
+ JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
97
+ JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
98
+ if (errorOriginalRubyObjectId > 0)
99
+ {
100
+ // may be nice if cover the case of object is missing
101
+ return rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId));
102
+ }
103
+ }
104
+ return Qnil;
105
+ }
106
+
107
+ VALUE r_try_json_parse(VALUE r_str)
108
+ {
109
+ return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
110
+ }
111
+
83
112
  VALUE to_rb_value(JSContext *ctx, JSValue j_val)
84
113
  {
85
114
  switch (JS_VALUE_GET_NORM_TAG(j_val))
@@ -121,6 +150,16 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
121
150
  return Qnil;
122
151
  }
123
152
 
153
+ if (JS_IsError(ctx, j_val))
154
+ {
155
+ VALUE r_maybe_ruby_error = find_ruby_error(ctx, j_val);
156
+ if (!NIL_P(r_maybe_ruby_error))
157
+ {
158
+ return r_maybe_ruby_error;
159
+ }
160
+ // will support other errors like just returning an instance of Error
161
+ }
162
+
124
163
  JSValue j_global = JS_GetGlobalObject(ctx);
125
164
  JSValue j_jsonClass = JS_GetPropertyStr(ctx, j_global, "JSON");
126
165
  JSValue j_stringifyFunc = JS_GetPropertyStr(ctx, j_jsonClass, "stringify");
@@ -135,7 +174,21 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
135
174
  JS_FreeValue(ctx, j_jsonClass);
136
175
  JS_FreeValue(ctx, j_global);
137
176
 
138
- return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
177
+ if (rb_funcall(r_str, rb_intern("=="), 1, rb_str_new2("undefined")))
178
+ {
179
+ return QUICKJSRB_SYM(undefinedId);
180
+ }
181
+
182
+ int couldntParse;
183
+ VALUE r_result = rb_protect(r_try_json_parse, r_str, &couldntParse);
184
+ if (couldntParse)
185
+ {
186
+ return Qnil;
187
+ }
188
+ else
189
+ {
190
+ return r_result;
191
+ }
139
192
  }
140
193
  case JS_TAG_NULL:
141
194
  return Qnil;
@@ -146,14 +199,44 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
146
199
  JSValue j_exceptionVal = JS_GetException(ctx);
147
200
  if (JS_IsError(ctx, j_exceptionVal))
148
201
  {
202
+ VALUE r_maybe_ruby_error = find_ruby_error(ctx, j_exceptionVal);
203
+ if (!NIL_P(r_maybe_ruby_error))
204
+ {
205
+ JS_FreeValue(ctx, j_exceptionVal);
206
+ rb_exc_raise(r_maybe_ruby_error);
207
+ return Qnil;
208
+ }
209
+
149
210
  JSValue j_errorClassName = JS_GetPropertyStr(ctx, j_exceptionVal, "name");
150
211
  const char *errorClassName = JS_ToCString(ctx, j_errorClassName);
151
212
 
152
213
  JSValue j_errorClassMessage = JS_GetPropertyStr(ctx, j_exceptionVal, "message");
153
214
  const char *errorClassMessage = JS_ToCString(ctx, j_errorClassMessage);
154
215
 
216
+ JSValue j_stackTrace = JS_GetPropertyStr(ctx, j_exceptionVal, "stack");
217
+ const char *stackTrace = JS_ToCString(ctx, j_stackTrace);
218
+ const char *headlineTemplate = "Uncaught %s: %s\n%s";
219
+ int length = snprintf(NULL, 0, headlineTemplate, errorClassName, errorClassMessage, stackTrace);
220
+ char *headline = (char *)malloc(length + 1);
221
+ snprintf(headline, length + 1, headlineTemplate, errorClassName, errorClassMessage, stackTrace);
222
+
223
+ VMData *data = JS_GetContextOpaque(ctx);
224
+ VALUE r_log_class = rb_const_get(rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern("VM")), rb_intern("Log"));
225
+ VALUE r_log = rb_funcall(r_log_class, rb_intern("new"), 0);
226
+ rb_iv_set(r_log, "@severity", ID2SYM(rb_intern("error")));
227
+ VALUE r_row = rb_ary_new();
228
+ VALUE r_loghash = rb_hash_new();
229
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("raw")), rb_str_new2(headline));
230
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("c")), rb_str_new2(headline));
231
+ rb_ary_push(r_row, r_loghash);
232
+ rb_iv_set(r_log, "@row", r_row);
233
+ rb_ary_push(data->logs, r_log);
234
+
155
235
  JS_FreeValue(ctx, j_errorClassMessage);
156
236
  JS_FreeValue(ctx, j_errorClassName);
237
+ JS_FreeValue(ctx, j_stackTrace);
238
+ JS_FreeCString(ctx, stackTrace);
239
+ free(headline);
157
240
 
158
241
  VALUE r_error_class, r_error_message = rb_str_new2(errorClassMessage);
159
242
  if (is_native_error_name(errorClassName))
@@ -165,11 +248,6 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
165
248
  r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
166
249
  r_error_message = rb_str_new2("Code evaluation is interrupted by the timeout or something");
167
250
  }
168
- else if (strcmp(errorClassName, "InternalError") == 0 && strstr(errorClassMessage, "Ruby") != NULL)
169
- {
170
- r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_RUBY_FUNCTION_ERROR);
171
- }
172
-
173
251
  else if (strcmp(errorClassName, "Quickjs::InterruptedError") == 0)
174
252
  {
175
253
  r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
@@ -187,8 +265,26 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
187
265
  else // exception without Error object
188
266
  {
189
267
  const char *errorMessage = JS_ToCString(ctx, j_exceptionVal);
190
- VALUE r_error_message = rb_sprintf("%s", errorMessage);
268
+ const char *headlineTemplate = "Uncaught '%s'";
269
+ int length = snprintf(NULL, 0, headlineTemplate, errorMessage);
270
+ char *headline = (char *)malloc(length + 1);
271
+ snprintf(headline, length + 1, headlineTemplate, errorMessage);
272
+
273
+ VMData *data = JS_GetContextOpaque(ctx);
274
+ VALUE r_log_class = rb_const_get(rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern("VM")), rb_intern("Log"));
275
+ VALUE r_log = rb_funcall(r_log_class, rb_intern("new"), 0);
276
+ rb_iv_set(r_log, "@severity", ID2SYM(rb_intern("error")));
277
+ VALUE r_row = rb_ary_new();
278
+ VALUE r_loghash = rb_hash_new();
279
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("raw")), rb_str_new2(headline));
280
+ rb_hash_aset(r_loghash, ID2SYM(rb_intern("c")), rb_str_new2(headline));
281
+ rb_ary_push(r_row, r_loghash);
282
+ rb_iv_set(r_log, "@row", r_row);
283
+ rb_ary_push(data->logs, r_log);
284
+
285
+ free(headline);
191
286
 
287
+ VALUE r_error_message = rb_sprintf("%s", errorMessage);
192
288
  JS_FreeCString(ctx, errorMessage);
193
289
  JS_FreeValue(ctx, j_exceptionVal);
194
290
  rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
@@ -251,7 +347,9 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
251
347
  JSValue j_result;
252
348
  if (sadnessHappened)
253
349
  {
254
- j_result = JS_ThrowInternalError(ctx, "unintentional error is raised while executing the function by Ruby: `%s`", funcName);
350
+ VALUE r_error = rb_errinfo();
351
+ JSValue j_error = j_error_from_ruby_error(ctx, r_error);
352
+ return JS_Throw(ctx, j_error);
255
353
  }
256
354
  else
257
355
  {
@@ -475,13 +573,14 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
475
573
  rb_scan_args(argc, argv, "10:", &r_import_string, &r_opts);
476
574
  if (NIL_P(r_opts))
477
575
  r_opts = rb_hash_new();
478
- VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from"))); // TODO: Use kwargs instead
576
+ VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from")));
479
577
  if (NIL_P(r_from))
480
578
  {
481
579
  VALUE r_error_message = rb_str_new2("missing import source");
482
580
  rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
483
581
  return Qnil;
484
582
  }
583
+ VALUE r_custom_exposure = rb_hash_aref(r_opts, ID2SYM(rb_intern("code_to_expose")));
485
584
 
486
585
  VMData *data;
487
586
  TypedData_Get_Struct(r_self, VMData, &vm_type, data);
@@ -499,8 +598,16 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
499
598
  r_import_string);
500
599
  VALUE r_import_name = rb_ary_entry(r_import_settings, 0);
501
600
  char *import_name = StringValueCStr(r_import_name);
502
- VALUE r_globalize = rb_ary_entry(r_import_settings, 1);
503
- char *globalize = StringValueCStr(r_globalize);
601
+ VALUE r_default_exposure = rb_ary_entry(r_import_settings, 1);
602
+ char *globalize;
603
+ if (RTEST(r_custom_exposure))
604
+ {
605
+ globalize = StringValueCStr(r_custom_exposure);
606
+ }
607
+ else
608
+ {
609
+ globalize = StringValueCStr(r_default_exposure);
610
+ }
504
611
 
505
612
  const char *importAndGlobalizeModule = "import %s from '%s';\n"
506
613
  "%s\n";
@@ -183,7 +183,6 @@ static VALUE r_define_log_class(VALUE r_parent_class)
183
183
  #define QUICKJSRB_ROOT_RUNTIME_ERROR "RuntimeError"
184
184
  #define QUICKJSRB_INTERRUPTED_ERROR "InterruptedError"
185
185
  #define QUICKJSRB_NO_AWAIT_ERROR "NoAwaitError"
186
- #define QUICKJSRB_RUBY_FUNCTION_ERROR "RubyFunctionError"
187
186
 
188
187
  #define QUICKJSRB_ERROR_FOR(name) \
189
188
  (VALUE) { rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern(name)) }
@@ -212,7 +211,6 @@ static void r_define_exception_classes(VALUE r_parent_class)
212
211
  // quickjsrb specific errors
213
212
  rb_define_class_under(r_parent_class, QUICKJSRB_INTERRUPTED_ERROR, r_runtime_error);
214
213
  rb_define_class_under(r_parent_class, QUICKJSRB_NO_AWAIT_ERROR, r_runtime_error);
215
- rb_define_class_under(r_parent_class, QUICKJSRB_RUBY_FUNCTION_ERROR, r_runtime_error);
216
214
  }
217
215
 
218
216
  #endif /* QUICKJSRB_H */
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quickjs
4
- VERSION = "0.2.3"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/quickjs.rb CHANGED
@@ -16,7 +16,7 @@ module Quickjs
16
16
  def _with_timeout(msec, proc, args)
17
17
  Timeout.timeout(msec / 1_000.0) { proc.call(*args) }
18
18
  rescue Timeout::Error
19
- Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
19
+ raise Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
20
20
  rescue
21
21
  raise
22
22
  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.2.3
4
+ version: 0.4.0
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-31 00:00:00.000000000 Z
11
+ date: 2024-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json