Skip to content

Commit

Permalink
raise original Ruby's exception transparently instead of RubyFunction…
Browse files Browse the repository at this point in the history
…Error
  • Loading branch information
hmsk committed Nov 8, 2024
1 parent 3754c68 commit 7dc8335
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 11 deletions.
28 changes: 21 additions & 7 deletions ext/quickjsrb/quickjsrb.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
JSValue j_errorClassMessage = JS_GetPropertyStr(ctx, j_exceptionVal, "message");
const char *errorClassMessage = JS_ToCString(ctx, j_errorClassMessage);

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) {
JS_ToInt32(ctx, &errorOriginalRubyObjectId, j_errorOriginalRubyObjectId);
}

JS_FreeValue(ctx, j_errorClassMessage);
JS_FreeValue(ctx, j_errorClassName);

Expand All @@ -179,11 +185,6 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
r_error_message = rb_str_new2("Code evaluation is interrupted by the timeout or something");
}
else if (strcmp(errorClassName, "InternalError") == 0 && strstr(errorClassMessage, "Ruby") != NULL)
{
r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_RUBY_FUNCTION_ERROR);
}

else if (strcmp(errorClassName, "Quickjs::InterruptedError") == 0)
{
r_error_class = QUICKJSRB_ERROR_FOR(QUICKJSRB_INTERRUPTED_ERROR);
Expand All @@ -196,7 +197,13 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
JS_FreeCString(ctx, errorClassMessage);
JS_FreeValue(ctx, j_exceptionVal);

rb_exc_raise(rb_funcall(r_error_class, rb_intern("new"), 2, r_error_message, rb_str_new2(errorClassName)));
VALUE r_error;
if (errorOriginalRubyObjectId > 0) {
r_error = rb_funcall(rb_const_get(rb_cClass, rb_intern("ObjectSpace")), rb_intern("_id2ref"), 1, INT2NUM(errorOriginalRubyObjectId));
} else {
r_error = rb_funcall(r_error_class, rb_intern("new"), 2, r_error_message, rb_str_new2(errorClassName));
}
rb_exc_raise(r_error);
}
else // exception without Error object
{
Expand Down Expand Up @@ -265,7 +272,14 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
JSValue j_result;
if (sadnessHappened)
{
j_result = JS_ThrowInternalError(ctx, "unintentional error is raised while executing the function by Ruby: `%s`", funcName);
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));
return JS_Throw(ctx, j_error);
}
else
{
Expand Down
2 changes: 0 additions & 2 deletions ext/quickjsrb/quickjsrb.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ static VALUE r_define_log_class(VALUE r_parent_class)
#define QUICKJSRB_ROOT_RUNTIME_ERROR "RuntimeError"
#define QUICKJSRB_INTERRUPTED_ERROR "InterruptedError"
#define QUICKJSRB_NO_AWAIT_ERROR "NoAwaitError"
#define QUICKJSRB_RUBY_FUNCTION_ERROR "RubyFunctionError"

#define QUICKJSRB_ERROR_FOR(name) \
(VALUE) { rb_const_get(rb_const_get(rb_cClass, rb_intern("Quickjs")), rb_intern(name)) }
Expand Down Expand Up @@ -212,7 +211,6 @@ static void r_define_exception_classes(VALUE r_parent_class)
// quickjsrb specific errors
rb_define_class_under(r_parent_class, QUICKJSRB_INTERRUPTED_ERROR, r_runtime_error);
rb_define_class_under(r_parent_class, QUICKJSRB_NO_AWAIT_ERROR, r_runtime_error);
rb_define_class_under(r_parent_class, QUICKJSRB_RUBY_FUNCTION_ERROR, r_runtime_error);
}

#endif /* QUICKJSRB_H */
4 changes: 2 additions & 2 deletions test/quickjs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ class GlobalFunction < QuickjsVmTest
end

test "throws an internal error which will be converted to Quickjs::RubyFunctionError in JS world when Ruby function raises" do
@vm.define_function("errorable") { raise 'sady' }
@vm.define_function("errorable") { raise IOError, 'sad error happened within Ruby' }

assert_raise_with_message(Quickjs::RubyFunctionError, 'unintentional error is raised while executing the function by Ruby: `errorable`') { @vm.eval_code("errorable();") }
assert_raise_with_message(IOError, 'sad error happened within Ruby') { @vm.eval_code("errorable();") }
end
end

Expand Down

0 comments on commit 7dc8335

Please sign in to comment.