Skip to content

Commit

Permalink
Const ref passing and automatic destructors throughout
Browse files Browse the repository at this point in the history
  • Loading branch information
andrjohns committed May 15, 2024
1 parent 0da34b0 commit 31c29ba
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 96 deletions.
17 changes: 4 additions & 13 deletions inst/include/quickjsr/JSON_to_JSValue.hpp
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
#ifndef QUICKJSR_JSON_TO_JSVALUE_HPP
#define QUICKJSR_JSON_TO_JSVALUE_HPP

#include <quickjsr/wrapper_classes.hpp>
#include <cpp11.hpp>
#include <quickjs-libc.h>

namespace quickjsr {

JSValue JSON_to_JSValue(JSContext* ctx, const std::string& json) {
JSValue global = JS_GetGlobalObject(ctx);
JSValue json_obj = JS_GetPropertyStr(ctx, global, "JSON");
JSValue parse = JS_GetPropertyStr(ctx, json_obj, "parse");

JSValue json_str = JS_NewString(ctx, json.c_str());
JSValue result = JS_Call(ctx, parse, global, 1, &json_str);
JSValueWrapper JSON_to_JSValue(const std::string& json) {
JSValueWrapper result = current_context->Parse(json);

if (JS_IsException(result)) {
js_std_dump_error(ctx);
js_std_dump_error(current_context->ctx);
}

JS_FreeValue(ctx, json_str);
JS_FreeValue(ctx, parse);
JS_FreeValue(ctx, json_obj);
JS_FreeValue(ctx, global);

return result;
}

Expand Down
32 changes: 14 additions & 18 deletions inst/include/quickjsr/JSValue_to_Cpp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,46 @@
#define QUICKJSR_JSVALUE_TO_CPP_HPP

#include <quickjsr/type_traits.hpp>
#include <quickjsr/wrapper_classes.hpp>
#include <type_traits>
#include <string>
#include <vector>
#include <quickjs-libc.h>

namespace quickjsr {
template <typename T, enable_if_type_t<double, T>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
T JSValue_to_Cpp(const JSValueWrapper& val) {
double res;
JS_ToFloat64(ctx, &res, val);
JS_ToFloat64(current_context->ctx, &res, val);
return res;
}

template <typename T, enable_if_type_t<int32_t, T>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
T JSValue_to_Cpp(const JSValueWrapper& val) {
int32_t res;
JS_ToInt32(ctx, &res, val);
JS_ToInt32(current_context->ctx, &res, val);
return res;
}

template <typename T, enable_if_type_t<bool, T>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
return static_cast<bool>(JS_ToBool(ctx, val));
T JSValue_to_Cpp(const JSValueWrapper& val) {
return static_cast<bool>(JS_ToBool(current_context->ctx, val));
}

template <typename T, enable_if_type_t<std::string, T>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
const char* res_str = JS_ToCString(ctx, val);
std::string result = res_str;
JS_FreeCString(ctx, res_str);
return result;
T JSValue_to_Cpp(const JSValueWrapper& val) {
return JS_ToCString(current_context->ctx, val);
}

template <typename T, std::enable_if_t<is_std_vector<T>::value>* = nullptr>
T JSValue_to_Cpp(JSContext* ctx, JSValue val) {
T JSValue_to_Cpp(const JSValueWrapper& val) {
T res;
uint32_t len;
JSValue arr_len = JS_GetPropertyStr(ctx, val, "length");
JS_ToUint32(ctx, &len, arr_len);
JS_FreeValue(ctx, arr_len);
JSValueWrapper arr_len = JS_GetPropertyStr(current_context->ctx, val, "length");
JS_ToUint32(current_context->ctx, &len, arr_len);
for (uint32_t i = 0; i < len; i++) {
JSValue elem = JS_GetPropertyUint32(ctx, val, i);
res.push_back(JSValue_to_Cpp<value_type_t<T>>(ctx, elem));
JS_FreeValue(ctx, elem);
JSValueWrapper elem = JS_GetPropertyUint32(current_context->ctx, val, i);
res.push_back(JSValue_to_Cpp<value_type_t<T>>(elem));
}
return res;
}
Expand Down
18 changes: 5 additions & 13 deletions inst/include/quickjsr/JSValue_to_JSON.hpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
#ifndef QUICKJSR_JSVALUE_TO_JSON_HPP
#define QUICKJSR_JSVALUE_TO_JSON_HPP

#include <quickjsr/wrapper_classes.hpp>
#include <cpp11.hpp>
#include <quickjs-libc.h>

namespace quickjsr {

std::string JSValue_to_JSON(JSContext* ctx, JSValue* val) {
JSValue global = JS_GetGlobalObject(ctx);
JSValue json = JS_GetPropertyStr(ctx, global, "JSON");
JSValue stringify = JS_GetPropertyStr(ctx, json, "stringify");

JSValue result_js = JS_Call(ctx, stringify, global, 1, val);
std::string JSValue_to_JSON(JSValueWrapper& val) {
JSValueWrapper result_js = current_context->Stringify(&val);
std::string result;
if (JS_IsException(result_js)) {
js_std_dump_error(ctx);
js_std_dump_error(current_context->ctx);
result = "Error!";
} else {
result = JSValue_to_Cpp<std::string>(ctx, result_js);
result = JSValue_to_Cpp<std::string>(result_js);
}

JS_FreeValue(ctx, result_js);
JS_FreeValue(ctx, stringify);
JS_FreeValue(ctx, json);
JS_FreeValue(ctx, global);

return result;
}

Expand Down
30 changes: 15 additions & 15 deletions inst/include/quickjsr/JSValue_to_SEXP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,46 @@
#include <cpp11.hpp>
#include <quickjs-libc.h>
#include <quickjsr/JSValue_to_Cpp.hpp>
#include <quickjsr/wrapper_classes.hpp>

namespace quickjsr {
// Forward declaration to allow for recursive calls
SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val);
SEXP JSValue_to_SEXP(const JSValueWrapper& val);

SEXP JSValue_to_SEXP_scalar(JSContext* ctx, JSValue val) {
SEXP JSValue_to_SEXP_scalar(const JSValueWrapper& val) {
if (JS_IsBool(val)) {
return cpp11::as_sexp(JSValue_to_Cpp<bool>(ctx, val));
return cpp11::as_sexp(JSValue_to_Cpp<bool>(val));
}
if (JS_IsNumber(val)) {
return cpp11::as_sexp(JSValue_to_Cpp<double>(ctx, val));
return cpp11::as_sexp(JSValue_to_Cpp<double>(val));
}
if (JS_IsString(val)) {
return cpp11::as_sexp(JSValue_to_Cpp<std::string>(ctx, val));
return cpp11::as_sexp(JSValue_to_Cpp<std::string>(val));
}
return cpp11::as_sexp("Unsupported type");
}

SEXP JSValue_to_SEXP_vector(JSContext* ctx, JSValue val) {
JSValue elem = JS_GetPropertyUint32(ctx, val, 0);
SEXP JSValue_to_SEXP_vector(const JSValueWrapper& val) {
JSValueWrapper elem = JS_GetPropertyUint32(current_context->ctx, val, 0);
SEXP rtn;
if (JS_IsBool(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<bool>>(ctx, val));
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<bool>>(val));
} else if (JS_IsNumber(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<double>>(ctx, val));
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<double>>(val));
} else if (JS_IsString(elem)) {
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<std::string>>(ctx, val));
rtn = cpp11::as_sexp(JSValue_to_Cpp<std::vector<std::string>>(val));
} else {
rtn = cpp11::as_sexp("Unsupported type");
}
JS_FreeValue(ctx, elem);
return rtn;
}

SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val) {
if (JS_IsArray(ctx, val)) {
return JSValue_to_SEXP_vector(ctx, val);
SEXP JSValue_to_SEXP(const JSValueWrapper& val) {
if (JS_IsArray(current_context->ctx, val)) {
return JSValue_to_SEXP_vector(val);
}
// TODO: Implement array and object conversion
return JSValue_to_SEXP_scalar(ctx, val);
return JSValue_to_SEXP_scalar(val);
}

} // namespace quickjsr
Expand Down
45 changes: 25 additions & 20 deletions inst/include/quickjsr/SEXP_to_JSValue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,69 @@

#include <cpp11.hpp>
#include <quickjs-libc.h>
#include <quickjsr/wrapper_classes.hpp>

namespace quickjsr {
// Forward declaration to allow for recursive calls
JSValue SEXP_to_JSValue(JSContext* ctx, SEXP x, bool auto_unbox);
JSValue SEXP_to_JSValue_impl(SEXP x, bool auto_unbox);

JSValue SEXP_to_JSValue_elem(JSContext* ctx, SEXP x, int i, bool auto_unbox) {
JSValue SEXP_to_JSValue_elem(SEXP x, int i, bool auto_unbox) {
switch(TYPEOF(x)) {
case REALSXP:
return JS_NewFloat64(ctx, REAL(x)[i]);
return JS_NewFloat64(current_context->ctx, REAL(x)[i]);
case INTSXP:
return JS_NewInt32(ctx, INTEGER(x)[i]);
return JS_NewInt32(current_context->ctx, INTEGER(x)[i]);
case LGLSXP:
return JS_NewBool(ctx, LOGICAL(x)[i]);
return JS_NewBool(current_context->ctx, LOGICAL(x)[i]);
case STRSXP:
return JS_NewString(ctx, CHAR(STRING_ELT(x, i)));
return JS_NewString(current_context->ctx, CHAR(STRING_ELT(x, i)));
case VECSXP:
return SEXP_to_JSValue(ctx, VECTOR_ELT(x, i), auto_unbox);
return SEXP_to_JSValue_impl(VECTOR_ELT(x, i), auto_unbox);
default:
return JS_UNDEFINED;
}
}

JSValue SEXP_to_JSValue_array(JSContext* ctx, SEXP x, bool auto_unbox) {
JSValue arr = JS_NewArray(ctx);
JSValue SEXP_to_JSValue_array(SEXP x, bool auto_unbox) {
JSValue arr = JS_NewArray(current_context->ctx);
for (int i = 0; i < Rf_length(x); i++) {
JSValue val = SEXP_to_JSValue_elem(ctx, x, i, auto_unbox);
JS_SetPropertyUint32(ctx, arr, i, val);
JSValue val = SEXP_to_JSValue_elem(x, i, auto_unbox);
JS_SetPropertyUint32(current_context->ctx, arr, i, val);
}
return arr;
}

JSValue SEXP_to_JSValue_object(JSContext* ctx, SEXP x, bool auto_unbox) {
JSValue obj = JS_NewObject(ctx);
JSValue SEXP_to_JSValue_object(SEXP x, bool auto_unbox) {
JSValue obj = JS_NewObject(current_context->ctx);
for (int i = 0; i < Rf_length(x); i++) {
SEXP name = STRING_ELT(Rf_getAttrib(x, R_NamesSymbol), i);
JSValue val = SEXP_to_JSValue_elem(ctx, x, i, auto_unbox);
JS_SetPropertyStr(ctx, obj, CHAR(name), val);
JSValue val = SEXP_to_JSValue_elem(x, i, auto_unbox);
JS_SetPropertyStr(current_context->ctx, obj, CHAR(name), val);
}
return obj;
}

JSValue SEXP_to_JSValue(JSContext* ctx, SEXP x, bool auto_unbox = false) {
JSValue SEXP_to_JSValue_impl(SEXP x, bool auto_unbox = false) {
// Following jsonlite conventions:
// - R list with names is an object, otherwise an array
if (TYPEOF(x) == VECSXP) {
if (Rf_getAttrib(x, R_NamesSymbol) != R_NilValue) {
return SEXP_to_JSValue_object(ctx, x, auto_unbox);
return SEXP_to_JSValue_object(x, auto_unbox);
} else {
return SEXP_to_JSValue_array(ctx, x, auto_unbox);
return SEXP_to_JSValue_array(x, auto_unbox);
}
}
if (Rf_length(x) == 1 && auto_unbox) {
return SEXP_to_JSValue_elem(ctx, x, 0, true);
return SEXP_to_JSValue_elem(x, 0, true);
} else {
return SEXP_to_JSValue_array(ctx, x, true);
return SEXP_to_JSValue_array(x, true);
}
}

JSValueWrapper SEXP_to_JSValue(SEXP x, bool auto_unbox = false) {
return SEXP_to_JSValue_impl(x, auto_unbox);
}

} // namespace quickjsr

#endif
22 changes: 20 additions & 2 deletions inst/include/quickjsr/wrapper_classes.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#ifndef QUICKJSR_WRAPPER_CLASSES_HPP
#define QUICKJSR_WRAPPER_CLASSES_HPP

#include <string>
#include <quickjs-libc.h>
#include <string>

namespace quickjsr {

Expand All @@ -19,13 +19,22 @@ struct JSRuntimeContextWrapper {
JSRuntime* rt;
JSContext* ctx;
JSValue global_obj;
JSValue JSON;
JSValue stringify;
JSValue parse;

JSRuntimeContextWrapper(int stack_size = -1) :
rt(InitRuntime(stack_size)),
ctx(JS_NewContext(rt)),
global_obj(JS_GetGlobalObject(ctx)) { }
global_obj(JS_GetGlobalObject(ctx)),
JSON(JS_GetPropertyStr(ctx, global_obj, "JSON")),
stringify(JS_GetPropertyStr(ctx, JSON, "stringify")),
parse(JS_GetPropertyStr(ctx, JSON, "parse")) { }

~JSRuntimeContextWrapper() {
JS_FreeValue(ctx, parse);
JS_FreeValue(ctx, stringify);
JS_FreeValue(ctx, JSON);
JS_FreeValue(ctx, global_obj);
JS_FreeContext(ctx);
js_std_free_handlers(rt);
Expand All @@ -41,6 +50,15 @@ struct JSRuntimeContextWrapper {
JS_FreeValue(ctx, fun);
return res;
}
JSValue Stringify(JSValue* val) {
return JS_Call(ctx, stringify, global_obj, 1, val);
}
JSValue Parse(const std::string& json) {
JSValue json_str = JS_NewString(ctx, json.c_str());
JSValue res = JS_Call(ctx, parse, global_obj, 1, &json_str);
JS_FreeValue(ctx, json_str);
return res;
}
};

JSRuntimeContextWrapper* current_context = nullptr;
Expand Down
5 changes: 5 additions & 0 deletions inst/tinytest/test_JSContext.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ expect_equal(jsc$call("mult_test", 10, 15), 150)
jsc2 <- JSContext$new()
jsc2$source(code = "function add_test_2(x, y) { return x + y + 2; }")
expect_equal(jsc2$call("add_test_2", 1, 2), 5)

expect_equal(jsc$call("add_test", 1, 2), 3)
expect_equal(jsc$call("add_test", 1, "a"), "1a")

expect_equal(jsc2$call("add_test_2", 1, 2), 5)
Loading

0 comments on commit 31c29ba

Please sign in to comment.