Skip to content

Commit

Permalink
format quickjsrb.c
Browse files Browse the repository at this point in the history
  • Loading branch information
hmsk committed Jul 15, 2024
1 parent c15765a commit c125aa4
Showing 1 changed file with 134 additions and 97 deletions.
231 changes: 134 additions & 97 deletions ext/quickjsrb/quickjsrb.c
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
#include "quickjsrb.h"

typedef struct EvalTime {
typedef struct EvalTime
{
clock_t limit;
clock_t started_at;
} EvalTime;

typedef struct VMData {
typedef struct VMData
{
char alive;
struct JSContext *context;
VALUE defined_functions;
struct EvalTime *eval_time;
} VMData;

static void vm_free(void* ptr)
static void vm_free(void *ptr)
{
VMData *data = (VMData *)ptr;
free(data->eval_time);
xfree(ptr);
}

size_t vm_size(const void* data)
size_t vm_size(const void *data)
{
return sizeof(data);
}
Expand All @@ -31,14 +33,14 @@ static void vm_mark(void *ptr)
}

static const rb_data_type_t vm_type = {
.wrap_struct_name = "quickjsvm",
.function = {
.dmark = vm_mark,
.dfree = vm_free,
.dsize = vm_size,
},
.data = NULL,
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
.wrap_struct_name = "quickjsvm",
.function = {
.dmark = vm_mark,
.dfree = vm_free,
.dsize = vm_size,
},
.data = NULL,
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};

static VALUE vm_alloc(VALUE self)
Expand All @@ -60,96 +62,114 @@ const char *nanId = "NaN";
const char *featureStdId = "feature_std";
const char *featureOsId = "feature_os";

JSValue to_js_value(JSContext *ctx, VALUE r_value) {
switch (TYPE(r_value)) {
case T_NIL:
return JS_NULL;
case T_FIXNUM:
case T_FLOAT: {
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
char *str = StringValueCStr(r_str);
JSValue global = JS_GetGlobalObject(ctx);
JSValue numberClass = JS_GetPropertyStr(ctx, global, "Number");
JSValue j_str = JS_NewString(ctx, str);
JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, numberClass);
JS_FreeValue(ctx, j_str);

return stringified;
}
case T_STRING: {
char *str = StringValueCStr(r_value);
JSValue to_js_value(JSContext *ctx, VALUE r_value)
{
switch (TYPE(r_value))
{
case T_NIL:
return JS_NULL;
case T_FIXNUM:
case T_FLOAT:
{
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
char *str = StringValueCStr(r_str);
JSValue global = JS_GetGlobalObject(ctx);
JSValue numberClass = JS_GetPropertyStr(ctx, global, "Number");
JSValue j_str = JS_NewString(ctx, str);
JSValue stringified = JS_Call(ctx, numberClass, JS_UNDEFINED, 1, &j_str);
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, numberClass);
JS_FreeValue(ctx, j_str);

return JS_NewString(ctx, str);
}
case T_SYMBOL: {
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
char *str = StringValueCStr(r_str);
return stringified;
}
case T_STRING:
{
char *str = StringValueCStr(r_value);

return JS_NewString(ctx, str);
}
case T_TRUE:
return JS_TRUE;
case T_FALSE:
return JS_FALSE;
case T_HASH:
case T_ARRAY: {
VALUE r_json_str = rb_funcall(r_value, rb_intern("to_json"), 0, NULL);
char *str = StringValueCStr(r_json_str);
JSValue global = JS_GetGlobalObject(ctx);
JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
JSValue parseFunc = JS_GetPropertyStr(ctx, jsonClass, "parse");
JSValue j_str = JS_NewString(ctx, str);
JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, jsonClass);
JS_FreeValue(ctx, parseFunc);
JS_FreeValue(ctx, j_str);

return stringified;
}
default: {
VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
char *str = StringValueCStr(r_inspect_str);
return JS_NewString(ctx, str);
}
case T_SYMBOL:
{
VALUE r_str = rb_funcall(r_value, rb_intern("to_s"), 0, NULL);
char *str = StringValueCStr(r_str);

return JS_NewString(ctx, str);
}
return JS_NewString(ctx, str);
}
case T_TRUE:
return JS_TRUE;
case T_FALSE:
return JS_FALSE;
case T_HASH:
case T_ARRAY:
{
VALUE r_json_str = rb_funcall(r_value, rb_intern("to_json"), 0, NULL);
char *str = StringValueCStr(r_json_str);
JSValue global = JS_GetGlobalObject(ctx);
JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
JSValue parseFunc = JS_GetPropertyStr(ctx, jsonClass, "parse");
JSValue j_str = JS_NewString(ctx, str);
JSValue stringified = JS_Call(ctx, parseFunc, jsonClass, 1, &j_str);
JS_FreeValue(ctx, global);
JS_FreeValue(ctx, jsonClass);
JS_FreeValue(ctx, parseFunc);
JS_FreeValue(ctx, j_str);

return stringified;
}
default:
{
VALUE r_inspect_str = rb_funcall(r_value, rb_intern("inspect"), 0, NULL);
char *str = StringValueCStr(r_inspect_str);

return JS_NewString(ctx, str);
}
}
}

VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
switch(JS_VALUE_GET_NORM_TAG(jsv)) {
case JS_TAG_INT: {
VALUE to_rb_value(JSValue jsv, JSContext *ctx)
{
switch (JS_VALUE_GET_NORM_TAG(jsv))
{
case JS_TAG_INT:
{
int int_res = 0;
JS_ToInt32(ctx, &int_res, jsv);
return INT2NUM(int_res);
}
case JS_TAG_FLOAT64: {
if (JS_VALUE_IS_NAN(jsv)) {
case JS_TAG_FLOAT64:
{
if (JS_VALUE_IS_NAN(jsv))
{
return ID2SYM(rb_intern(nanId));
}
double double_res;
JS_ToFloat64(ctx, &double_res, jsv);
return DBL2NUM(double_res);
}
case JS_TAG_BOOL: {
case JS_TAG_BOOL:
{
return JS_ToBool(ctx, jsv) > 0 ? Qtrue : Qfalse;
}
case JS_TAG_STRING: {
case JS_TAG_STRING:
{
JSValue maybeString = JS_ToString(ctx, jsv);
const char *msg = JS_ToCString(ctx, maybeString);
JS_FreeValue(ctx, maybeString);
return rb_str_new2(msg);
}
case JS_TAG_OBJECT: {
case JS_TAG_OBJECT:
{
int promiseState = JS_PromiseState(ctx, jsv);
if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING)
{
JSValue awaited = js_std_await(ctx, jsv);
VALUE rb_awaited = to_rb_value(awaited, ctx); // TODO: should have timeout
JS_FreeValue(ctx, awaited);
return rb_awaited;
} else if (promiseState == JS_PROMISE_REJECTED) {
}
else if (promiseState == JS_PROMISE_REJECTED)
{
JSValue promiseResult = JS_PromiseResult(ctx, jsv);
JSValue throw = JS_Throw(ctx, promiseResult);
JS_FreeValue(ctx, promiseResult);
Expand Down Expand Up @@ -177,9 +197,11 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
return Qnil;
case JS_TAG_UNDEFINED:
return ID2SYM(rb_intern(undefinedId));
case JS_TAG_EXCEPTION: {
case JS_TAG_EXCEPTION:
{
JSValue exceptionVal = JS_GetException(ctx);
if (JS_IsError(ctx, exceptionVal)) {
if (JS_IsError(ctx, exceptionVal))
{
JSValue jsErrorClassName = JS_GetPropertyStr(ctx, exceptionVal, "name");
const char *errorClassName = JS_ToCString(ctx, jsErrorClassName);

Expand All @@ -190,7 +212,9 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
JS_FreeValue(ctx, jsErrorClassName);

rb_raise(rb_eRuntimeError, "%s: %s", errorClassName, errorClassMessage);
} else {
}
else
{
const char *errorMessage = JS_ToCString(ctx, exceptionVal);

rb_raise(rb_eRuntimeError, "%s", errorMessage);
Expand All @@ -199,7 +223,8 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
JS_FreeValue(ctx, exceptionVal);
return Qnil;
}
case JS_TAG_BIG_INT: {
case JS_TAG_BIG_INT:
{
JSValue toStringFunc = JS_GetPropertyStr(ctx, jsv, "toString");
JSValue strigified = JS_Call(ctx, toStringFunc, jsv, 0, NULL);

Expand All @@ -218,14 +243,16 @@ VALUE to_rb_value(JSValue jsv, JSContext *ctx) {
}
}

static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv) {
static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int _argc, JSValueConst *argv)
{
VMData *data = JS_GetContextOpaque(ctx);
JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
const char *funcName = JS_ToCString(ctx, maybeFuncName);
JS_FreeValue(ctx, maybeFuncName);

VALUE proc = rb_hash_aref(data->defined_functions, rb_str_new2(funcName));
if (proc == Qnil) { // Shouldn't happen
if (proc == Qnil)
{ // Shouldn't happen
return JS_ThrowReferenceError(ctx, "Proc `%s` is not defined", funcName);
}

Expand All @@ -234,20 +261,25 @@ static JSValue js_quickjsrb_call_global(JSContext *ctx, JSValueConst _this, int
return to_js_value(ctx, r_result);
}

static VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE r_opts;
rb_scan_args(argc, argv, ":", &r_opts);
if (NIL_P(r_opts)) r_opts = rb_hash_new();
if (NIL_P(r_opts))
r_opts = rb_hash_new();

VALUE r_memoryLimit = rb_hash_aref(r_opts, ID2SYM(rb_intern("memory_limit")));
if (NIL_P(r_memoryLimit)) r_memoryLimit = UINT2NUM(1024 * 1024 * 128);
if (NIL_P(r_memoryLimit))
r_memoryLimit = UINT2NUM(1024 * 1024 * 128);
VALUE r_maxStackSize = rb_hash_aref(r_opts, ID2SYM(rb_intern("max_stack_size")));
if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
if (NIL_P(r_maxStackSize))
r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
if (NIL_P(r_features)) r_features = rb_ary_new();
if (NIL_P(r_features))
r_features = rb_ary_new();
VALUE r_timeout_msec = rb_hash_aref(r_opts, ID2SYM(rb_intern("timeout_msec")));
if (NIL_P(r_timeout_msec)) r_timeout_msec= UINT2NUM(100);
if (NIL_P(r_timeout_msec))
r_timeout_msec = UINT2NUM(100);

VMData *data;
TypedData_Get_Struct(self, VMData, &vm_type, data);
Expand All @@ -270,18 +302,20 @@ static VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
JS_SetModuleLoaderFunc(runtime, NULL, js_module_loader, NULL);
js_std_init_handlers(runtime);

if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureStdId))))) {
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureStdId)))))
{
js_init_module_std(data->context, "std");
const char *enableStd = "import * as std from 'std';\n"
"globalThis.std = std;\n";
"globalThis.std = std;\n";
JSValue stdEval = JS_Eval(data->context, enableStd, strlen(enableStd), "<vm>", JS_EVAL_TYPE_MODULE);
JS_FreeValue(data->context, stdEval);
}

if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsId))))) {
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsId)))))
{
js_init_module_os(data->context, "os");
const char *enableOs = "import * as os from 'os';\n"
"globalThis.os = os;\n";
"globalThis.os = os;\n";
JSValue osEval = JS_Eval(data->context, enableOs, strlen(enableOs), "<vm>", JS_EVAL_TYPE_MODULE);
JS_FreeValue(data->context, osEval);
}
Expand All @@ -298,17 +332,19 @@ static VALUE vm_m_initialize(int argc, VALUE* argv, VALUE self)
return self;
}

static int interrupt_handler(JSRuntime *runtime, void *opaque) {
EvalTime *eval_time = opaque;
return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
static int interrupt_handler(JSRuntime *runtime, void *opaque)
{
EvalTime *eval_time = opaque;
return clock() >= eval_time->started_at + eval_time->limit ? 1 : 0;
}

static VALUE vm_m_evalCode(VALUE self, VALUE r_code)
{
VMData *data;
TypedData_Get_Struct(self, VMData, &vm_type, data);

if (data->alive < 1) {
if (data->alive < 1)
{
rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
return Qnil;
}
Expand All @@ -331,16 +367,17 @@ static VALUE vm_m_defineGlobalFunction(VALUE self, VALUE r_name)
VMData *data;
TypedData_Get_Struct(self, VMData, &vm_type, data);

if (rb_block_given_p()) {
if (rb_block_given_p())
{
VALUE proc = rb_block_proc();

char *funcName = StringValueCStr(r_name);

rb_hash_aset(data->defined_functions, r_name, proc);

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

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

0 comments on commit c125aa4

Please sign in to comment.