Skip to content

Commit

Permalink
don't throw error when an exception instance is just returned
Browse files Browse the repository at this point in the history
  • Loading branch information
hmsk committed Nov 8, 2024
1 parent 5831e6a commit e4fa086
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 30 deletions.
87 changes: 58 additions & 29 deletions ext/quickjsrb/quickjsrb.c
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
#include "quickjsrb.h"

JSValue to_js_value(JSContext *ctx, VALUE r_value)
JSValue j_error_from_ruby_error(JSContext *ctx, VALUE r_error)
{
if (RTEST(rb_funcall(
r_value,
rb_intern("is_a?"),
1, rb_const_get(rb_cClass, rb_intern("Exception")))))
{
JSValue j_error = JS_NewError(ctx);
VALUE r_str = rb_funcall(r_value, rb_intern("message"), 0, NULL);
char *exceptionMessage = StringValueCStr(r_str);
VALUE r_exception_name = rb_funcall(rb_funcall(r_value, rb_intern("class"), 0, NULL), rb_intern("name"), 0, NULL);
char *exceptionName = StringValueCStr(r_exception_name);
JS_SetPropertyStr(ctx, j_error, "name", JS_NewString(ctx, exceptionName));
JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, exceptionMessage));
return JS_Throw(ctx, j_error);
}
JSValue j_error = JS_NewError(ctx); // may wanna have custom error class to determine in JS' end

VALUE r_object_id = rb_funcall(r_error, rb_intern("object_id"), 0, NULL);
int objectId = NUM2INT(r_object_id);
JS_SetPropertyStr(ctx, j_error, "rb_object_id", JS_NewInt32(ctx, objectId));

VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0, NULL);
const char *errorMessage = StringValueCStr(r_exception_message);
JS_SetPropertyStr(ctx, j_error, "message", JS_NewString(ctx, errorMessage));

return j_error;
}

JSValue to_js_value(JSContext *ctx, VALUE r_value)
{
switch (TYPE(r_value))
{
case T_NIL:
Expand Down Expand Up @@ -72,6 +72,13 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
}
default:
{
if (TYPE(r_value) == T_OBJECT && RTEST(rb_funcall(
r_value,
rb_intern("is_a?"),
1, rb_const_get(rb_cClass, rb_intern("Exception")))))
{
return j_error_from_ruby_error(ctx, r_value);
}
VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
char *str = StringValueCStr(r_inspect_str);

Expand All @@ -80,7 +87,8 @@ JSValue to_js_value(JSContext *ctx, VALUE r_value)
}
}

VALUE r_try_json_parse(VALUE r_str) {
VALUE r_try_json_parse(VALUE r_str)
{
return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, r_str);
}

Expand Down Expand Up @@ -125,6 +133,23 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
return Qnil;
}

if (JS_IsError(ctx, j_val))
{
JSValue j_errorOriginalRubyObjectId = JS_GetPropertyStr(ctx, j_val, "rb_object_id");
int errorOriginalRubyObjectId = 0;
if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT)
{
JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
if (errorOriginalRubyObjectId > 0)
{
// may be nice if cover the case of object is missing
return rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId));
}
}
// will support other errors
}

JSValue j_global = JS_GetGlobalObject(ctx);
JSValue j_jsonClass = JS_GetPropertyStr(ctx, j_global, "JSON");
JSValue j_stringifyFunc = JS_GetPropertyStr(ctx, j_jsonClass, "stringify");
Expand All @@ -139,15 +164,19 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
JS_FreeValue(ctx, j_jsonClass);
JS_FreeValue(ctx, j_global);

if (rb_funcall(r_str, rb_intern("=="), 1, rb_str_new2("undefined"))) {
if (rb_funcall(r_str, rb_intern("=="), 1, rb_str_new2("undefined")))
{
return QUICKJSRB_SYM(undefinedId);
}

int couldntParse;
VALUE r_result = rb_protect(r_try_json_parse, r_str, &couldntParse);
if (couldntParse) {
if (couldntParse)
{
return Qnil;
} else {
}
else
{
return r_result;
}
}
Expand All @@ -162,10 +191,12 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
{
JSValue j_errorOriginalRubyObjectId = JS_GetPropertyStr(ctx, j_exceptionVal, "rb_object_id");
int errorOriginalRubyObjectId = 0;
if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT) {
if (JS_VALUE_GET_NORM_TAG(j_errorOriginalRubyObjectId) == JS_TAG_INT)
{
JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
JS_FreeValue(ctx, j_errorOriginalRubyObjectId);
if (errorOriginalRubyObjectId > 0) {
if (errorOriginalRubyObjectId > 0)
{
JS_FreeValue(ctx, j_exceptionVal);
// may be nice if cover the case of object is missing
rb_exc_raise(rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId)));
Expand Down Expand Up @@ -274,12 +305,7 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
if (sadnessHappened)
{
VALUE r_error = rb_errinfo();

JSValue j_error = JS_NewError(ctx); // may wanna have custom error class to determine in JS' end
VALUE r_exception_message = rb_funcall(r_error, rb_intern("message"), 0, NULL);
VALUE r_exception_name = rb_funcall(r_error, rb_intern("object_id"), 0, NULL);
JS_SetPropertyStr(ctx, j_error, "rb_object_id", to_js_value(ctx, r_exception_name));
JS_SetPropertyStr(ctx, j_error, "message", to_js_value(ctx, r_exception_message));
JSValue j_error = j_error_from_ruby_error(ctx, r_error);
return JS_Throw(ctx, j_error);
}
else
Expand Down Expand Up @@ -531,9 +557,12 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
char *import_name = StringValueCStr(r_import_name);
VALUE r_default_exposure = rb_ary_entry(r_import_settings, 1);
char *globalize;
if (RTEST(r_custom_exposure)) {
if (RTEST(r_custom_exposure))
{
globalize = StringValueCStr(r_custom_exposure);
} else {
}
else
{
globalize = StringValueCStr(r_default_exposure);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/quickjs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def eval_code(code, overwrite_opts = {})
def _with_timeout(msec, proc, args)
Timeout.timeout(msec / 1_000.0) { proc.call(*args) }
rescue Timeout::Error
Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
raise Quickjs::InterruptedError.new('Ruby runtime got timeout', nil)
rescue
raise
end
Expand Down
8 changes: 8 additions & 0 deletions test/quickjs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ class GlobalFunction < QuickjsVmTest
assert_equal(@vm.eval_code("get_obj()"), { 'a' => 1 })
end

test "returns original exception" do
@vm.define_function("get_exception") { IOError.new("yo") }

exception = @vm.eval_code("get_exception()")
assert_equal(exception.class, IOError)
assert_equal(exception.message, 'yo')
end

test "returns inspected string for otherwise" do
@vm.define_function("get_class") { Class.new }

Expand Down

0 comments on commit e4fa086

Please sign in to comment.