Skip to content

Commit

Permalink
support defining Ruby's function as an async
Browse files Browse the repository at this point in the history
  • Loading branch information
hmsk committed Oct 31, 2024
1 parent 9af9e42 commit 54f7238
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 16 deletions.
36 changes: 20 additions & 16 deletions ext/quickjsrb/quickjsrb.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,30 +412,34 @@ static VALUE vm_m_evalCode(VALUE r_self, VALUE r_code)
}
}

static VALUE vm_m_defineGlobalFunction(VALUE r_self, VALUE r_name)
static VALUE vm_m_defineGlobalFunction(int argc, VALUE *argv, VALUE r_self)
{
rb_need_block();

VALUE r_name;
VALUE r_flags;
VALUE r_block;
rb_scan_args(argc, argv, "10*&", &r_name, &r_flags, &r_block);

const char *asyncKeyword =
RTEST(rb_funcall(r_flags, rb_intern("include?"), 1, ID2SYM(rb_intern("async")))) ? "async " : "";

VMData *data;
TypedData_Get_Struct(r_self, VMData, &vm_type, data);

if (rb_block_given_p())
{
VALUE r_proc = rb_block_proc();
rb_hash_aset(data->defined_functions, r_name, r_proc);
char *funcName = StringValueCStr(r_name);
rb_hash_aset(data->defined_functions, r_name, r_block);
char *funcName = StringValueCStr(r_name);

const char *template = "globalThis['%s'] = (...args) => __quickjsrb.runRubyMethod('%s', args);\n";
int length = snprintf(NULL, 0, template, funcName, funcName);
char *result = (char *)malloc(length + 1);
snprintf(result, length + 1, template, funcName, funcName);
const char *template = "globalThis['%s'] = %s(...args) => __quickjsrb.runRubyMethod('%s', args);\n";
int length = snprintf(NULL, 0, template, funcName, asyncKeyword, funcName);
char *result = (char *)malloc(length + 1);
snprintf(result, length + 1, template, funcName, asyncKeyword, funcName);

JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
JSValue j_codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);

free(result);
JS_FreeValue(data->context, j_codeResult);
return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);
}
free(result);
JS_FreeValue(data->context, j_codeResult);
return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);

return Qnil;
}
Expand Down Expand Up @@ -507,7 +511,7 @@ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
rb_define_alloc_func(r_class_vm, vm_alloc);
rb_define_method(r_class_vm, "initialize", vm_m_initialize, -1);
rb_define_method(r_class_vm, "eval_code", vm_m_evalCode, 1);
rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, 1);
rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, -1);
rb_define_method(r_class_vm, "import", vm_m_import, -1);
rb_define_method(r_class_vm, "logs", vm_m_logs, 0);
r_define_log_class(r_class_vm);
Expand Down
7 changes: 7 additions & 0 deletions test/quickjs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,13 @@ class GlobalFunction < QuickjsVmTest
@vm.define_function("infinite") { loop {} }
assert_raise_with_message(Quickjs::InterruptedError, /Ruby runtime got timeout/) { @vm.eval_code("infinite();") }
end

test "async keyword configures" do
@vm.define_function "unblocked", :async do
'asynchronous return'
end
assert_equal(@vm.eval_code("const awaited = await unblocked().then((result) => result + '!'); awaited;"), 'asynchronous return!')
end
end

class Import < QuickjsVmTest
Expand Down

0 comments on commit 54f7238

Please sign in to comment.