diff --git a/R/JSContext.R b/R/JSContext.R index 2c7e258..cf34470 100644 --- a/R/JSContext.R +++ b/R/JSContext.R @@ -66,18 +66,18 @@ source <- NULL call <- NULL #' Get a variable from the current context -#' +#' #' @name JSContext-method-get #' @aliases get -#' +#' #' @usage get(var_name) -#' +#' #' @description #' Get the value of a variable from the current context -#' +#' #' @param var_name The name of the variable to retrieve #' @return The value of the variable -#' +#' #' @examples #' \dontrun{ #' ctx <- JSContext$new() @@ -87,19 +87,19 @@ call <- NULL get <- NULL #' Assign a value to a variable in the current context -#' +#' #' @name JSContext-method-assign #' @aliases assign -#' +#' #' @usage assign(var_name, value) #' #' @description #' Assign a value to a variable in the current context -#' +#' #' @param var_name The name of the variable to assign #' @param value The value to assign to the variable #' @return No return value, called for side effects -#' +#' #' @examples #' \dontrun{ #' ctx <- JSContext$new() @@ -110,14 +110,12 @@ assign <- NULL new_JSContext <- function(stack_size = NULL) { stack_size_int = ifelse(is.null(stack_size), -1, stack_size) - rt_and_ctx = qjs_context(stack_size_int) ContextList = list( - runtime = rt_and_ctx$runtime_ptr, - context = rt_and_ctx$context_ptr + runtime_context_ptr = qjs_context(stack_size_int) ) ContextList$validate <- function(code_string) { - qjs_validate(ContextList$context, code_string) + qjs_validate(ContextList$runtime_context_ptr, code_string) } ContextList$source <- function(file = NULL, code = NULL) { @@ -127,10 +125,10 @@ new_JSContext <- function(stack_size = NULL) { warning("Both a filepath and code string cannot be provided,", " code will be ignored!", call. = FALSE) } - eval_success <- qjs_source(ContextList$context, + eval_success <- qjs_source(ContextList$runtime_context_ptr, input = normalizePath(file), is_file = TRUE) } else if (!is.null(code)) { - eval_success <- qjs_source(ContextList$context, input = code, is_file = FALSE) + eval_success <- qjs_source(ContextList$runtime_context_ptr, input = code, is_file = FALSE) } else { stop("No JS code provided!", call. = FALSE) } @@ -141,13 +139,13 @@ new_JSContext <- function(stack_size = NULL) { invisible(NULL) } ContextList$call <- function(function_name, ...) { - qjs_call(ContextList$context, function_name, ...) + qjs_call(ContextList$runtime_context_ptr, function_name, ...) } ContextList$get <- function(var_name) { - qjs_get(ContextList$context, var_name) + qjs_get(ContextList$runtime_context_ptr, var_name) } ContextList$assign <- function(var_name, value) { - qjs_assign(ContextList$context, var_name, value) + qjs_assign(ContextList$runtime_context_ptr, var_name, value) } structure( class = "JSContext", diff --git a/inst/include/quickjsr.hpp b/inst/include/quickjsr.hpp index 05b626e..809e8f5 100644 --- a/inst/include/quickjsr.hpp +++ b/inst/include/quickjsr.hpp @@ -7,5 +7,6 @@ #include #include #include +#include #endif diff --git a/inst/include/quickjsr/JS_Containers.hpp b/inst/include/quickjsr/JS_Containers.hpp new file mode 100644 index 0000000..3021650 --- /dev/null +++ b/inst/include/quickjsr/JS_Containers.hpp @@ -0,0 +1,43 @@ +#ifndef QUICKJSR_JS_CONTAINERS_HPP +#define QUICKJSR_JS_CONTAINERS_HPP + +#include +#include +#include + +namespace quickjsr { + +struct JS_RtCtxContainer { + public: + JSRuntime* rt; + JSContext* ctx; + + JS_RtCtxContainer(int stack_size = 0) + : rt(JS_NewCustomRuntime(stack_size)), ctx(JS_NewCustomContext(rt)) {} + + ~JS_RtCtxContainer() { + JS_FreeContext(ctx); + js_std_free_handlers(rt); + JS_FreeRuntime(rt); + } +}; + +using RtCtxXPtr = cpp11::external_pointer; + +struct JS_ValContainer { + public: + RtCtxXPtr rt_ctx; + JSValue val; + + template + JS_ValContainer(RtCtxXPtr in_rt_ctx, JSValT&& in_val) + : rt_ctx(in_rt_ctx), val(std::forward(in_val)) {} + + ~JS_ValContainer() { + JS_FreeValue(rt_ctx->ctx, val); + } +}; + +} // namespace quickjsr + +#endif diff --git a/src/quickjsr.cpp b/src/quickjsr.cpp index c4c8442..fcc1472 100644 --- a/src/quickjsr.cpp +++ b/src/quickjsr.cpp @@ -4,42 +4,29 @@ #include #include -void JS_FreeRuntimeStdHandlers(JSRuntime* rt) { - js_std_free_handlers(rt); - JS_FreeRuntime(rt); -} - -// Register the cpp11 external pointer types with the correct cleanup/finaliser functions -using ContextXPtr = cpp11::external_pointer; -using RuntimeXPtr = cpp11::external_pointer; +using quickjsr::JS_RtCtxContainer; +using quickjsr::JS_ValContainer; +using quickjsr::RtCtxXPtr; extern "C" { SEXP qjs_context_(SEXP stack_size_) { BEGIN_CPP11 int stack_size = Rf_isInteger(stack_size_) ? INTEGER_ELT(stack_size_, 0) : static_cast(REAL_ELT(stack_size_, 0)); - - RuntimeXPtr rt(quickjsr::JS_NewCustomRuntime(stack_size)); - ContextXPtr ctx(quickjsr::JS_NewCustomContext(rt.get())); - - cpp11::writable::list result; - using cpp11::literals::operator""_nm; - result.push_back({"runtime_ptr"_nm = rt}); - result.push_back({"context_ptr"_nm = ctx}); - - return cpp11::as_sexp(result); + RtCtxXPtr rt(new JS_RtCtxContainer(stack_size)); + return cpp11::as_sexp(rt); END_CPP11 } SEXP qjs_source_(SEXP ctx_ptr_, SEXP input_, SEXP is_file_) { BEGIN_CPP11 - JSContext* ctx = ContextXPtr(ctx_ptr_).get(); + RtCtxXPtr rt_ctx(ctx_ptr_); int ret; const char* input = Rf_translateCharUTF8(STRING_ELT(input_, 0)); if (LOGICAL_ELT(is_file_, 0)) { - ret = quickjsr::eval_file(ctx, input, -1); + ret = quickjsr::eval_file(rt_ctx->ctx, input, -1); } else { - ret = quickjsr::eval_buf(ctx, input, strlen(input), "", JS_EVAL_TYPE_GLOBAL); + ret = quickjsr::eval_buf(rt_ctx->ctx, input, strlen(input), "", JS_EVAL_TYPE_GLOBAL); } return cpp11::as_sexp(!ret); END_CPP11 @@ -47,63 +34,50 @@ extern "C" { SEXP qjs_validate_(SEXP ctx_ptr_, SEXP code_string_) { BEGIN_CPP11 - JSContext* ctx = ContextXPtr(ctx_ptr_).get(); + RtCtxXPtr rt_ctx(ctx_ptr_); const char* code_string = Rf_translateCharUTF8(STRING_ELT(code_string_, 0)); - JSValue val = JS_Eval(ctx, code_string, strlen(code_string), "", JS_EVAL_FLAG_COMPILE_ONLY); - bool failed = JS_IsException(val); - JS_FreeValue(ctx, val); - return cpp11::as_sexp(!failed); + JS_ValContainer val(rt_ctx, JS_Eval(rt_ctx->ctx, code_string, strlen(code_string), "", JS_EVAL_FLAG_COMPILE_ONLY)); + return cpp11::as_sexp(!JS_IsException(val.val)); END_CPP11 } SEXP qjs_call_(SEXP ctx_ptr_, SEXP fun_name_, SEXP args_list_) { BEGIN_CPP11 - JSContext* ctx = ContextXPtr(ctx_ptr_).get(); + RtCtxXPtr rt_ctx(ctx_ptr_); int64_t n_args = Rf_xlength(args_list_); std::vector args(n_args); for (int64_t i = 0; i < n_args; i++) { - args[i] = quickjsr::SEXP_to_JSValue(ctx, VECTOR_ELT(args_list_, i), true); + args[i] = quickjsr::SEXP_to_JSValue(rt_ctx->ctx, VECTOR_ELT(args_list_, i), true); } - JSValue global = JS_GetGlobalObject(ctx); - JSValue fun = quickjsr::JS_GetPropertyRecursive(ctx, global, Rf_translateCharUTF8(STRING_ELT(fun_name_, 0))); - JSValue result_js = JS_Call(ctx, fun, global, args.size(), args.data()); - - SEXP result = quickjsr::JSValue_to_SEXP(ctx, result_js); + JS_ValContainer global(rt_ctx, JS_GetGlobalObject(rt_ctx->ctx)); + JS_ValContainer fun(rt_ctx, quickjsr::JS_GetPropertyRecursive(rt_ctx->ctx, global.val, Rf_translateCharUTF8(STRING_ELT(fun_name_, 0)))); + JS_ValContainer result_js(rt_ctx, JS_Call(rt_ctx->ctx, fun.val, global.val, args.size(), args.data())); - JS_FreeValue(ctx, result_js); for (auto&& arg : args) { - JS_FreeValue(ctx, arg); + JS_FreeValue(rt_ctx->ctx, arg); } - JS_FreeValue(ctx, fun); - JS_FreeValue(ctx, global); - return result; + return quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result_js.val); END_CPP11 } SEXP qjs_get_(SEXP ctx_ptr_, SEXP js_obj_name) { BEGIN_CPP11 - JSContext* ctx = ContextXPtr(ctx_ptr_).get(); - JSValue global = JS_GetGlobalObject(ctx); - JSValue result = quickjsr::JS_GetPropertyRecursive(ctx, global, Rf_translateCharUTF8(STRING_ELT(js_obj_name, 0))); - SEXP rtn = quickjsr::JSValue_to_SEXP(ctx, result); - - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, global); - - return rtn; + RtCtxXPtr rt_ctx(ctx_ptr_); + JS_ValContainer global(rt_ctx, JS_GetGlobalObject(rt_ctx->ctx)); + JS_ValContainer result(rt_ctx, quickjsr::JS_GetPropertyRecursive(rt_ctx->ctx, global.val, Rf_translateCharUTF8(STRING_ELT(js_obj_name, 0)))); + return quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result.val); END_CPP11 } SEXP qjs_assign_(SEXP ctx_ptr_, SEXP js_obj_name_, SEXP value_) { BEGIN_CPP11 - JSContext* ctx = ContextXPtr(ctx_ptr_).get(); - JSValue global = JS_GetGlobalObject(ctx); - JSValue value = quickjsr::SEXP_to_JSValue(ctx, value_, true); - int result = quickjsr::JS_SetPropertyRecursive(ctx, global, Rf_translateCharUTF8(STRING_ELT(js_obj_name_, 0)), value); - JS_FreeValue(ctx, global); + RtCtxXPtr rt_ctx(ctx_ptr_); + JS_ValContainer global(rt_ctx, JS_GetGlobalObject(rt_ctx->ctx)); + JS_ValContainer value(rt_ctx, quickjsr::SEXP_to_JSValue(rt_ctx->ctx, value_, true)); + int result = quickjsr::JS_SetPropertyRecursive(rt_ctx->ctx, global.val, Rf_translateCharUTF8(STRING_ELT(js_obj_name_, 0)), value.val); return cpp11::as_sexp(result); END_CPP11 @@ -112,50 +86,27 @@ extern "C" { SEXP qjs_eval_(SEXP eval_string_) { BEGIN_CPP11 const char* eval_string = Rf_translateCharUTF8(STRING_ELT(eval_string_, 0)); - JSRuntime* rt = quickjsr::JS_NewCustomRuntime(0); - JSContext* ctx = quickjsr::JS_NewCustomContext(rt); - - JSValue val = JS_Eval(ctx, eval_string, strlen(eval_string), "", JS_EVAL_TYPE_GLOBAL); - SEXP result = quickjsr::JSValue_to_SEXP(ctx, val); - - JS_FreeValue(ctx, val); - JS_FreeContext(ctx); - JS_FreeRuntimeStdHandlers(rt); - - return result; + RtCtxXPtr rt_ctx(new JS_RtCtxContainer()); + JS_ValContainer val(rt_ctx, JS_Eval(rt_ctx->ctx, eval_string, strlen(eval_string), "", JS_EVAL_TYPE_GLOBAL)); + return quickjsr::JSValue_to_SEXP(rt_ctx->ctx, val.val); END_CPP11 } SEXP to_json_(SEXP arg_, SEXP auto_unbox_) { BEGIN_CPP11 - JSRuntime* rt = quickjsr::JS_NewCustomRuntime(0); - JSContext* ctx = quickjsr::JS_NewCustomContext(rt); - - JSValue arg = quickjsr::SEXP_to_JSValue(ctx, arg_, LOGICAL_ELT(auto_unbox_, 0)); - std::string result = quickjsr::JSValue_to_JSON(ctx, arg); - - JS_FreeValue(ctx, arg); - JS_FreeContext(ctx); - JS_FreeRuntimeStdHandlers(rt); - - return cpp11::as_sexp(result); + RtCtxXPtr rt_ctx(new JS_RtCtxContainer()); + JS_ValContainer arg(rt_ctx, quickjsr::SEXP_to_JSValue(rt_ctx->ctx, arg_, LOGICAL_ELT(auto_unbox_, 0))); + return cpp11::as_sexp(quickjsr::JSValue_to_JSON(rt_ctx->ctx, arg.val)); END_CPP11 } SEXP from_json_(SEXP json_) { BEGIN_CPP11 - JSRuntime* rt = quickjsr::JS_NewCustomRuntime(0); - JSContext* ctx = quickjsr::JS_NewCustomContext(rt); + RtCtxXPtr rt_ctx(new JS_RtCtxContainer()); const char* json = Rf_translateCharUTF8(STRING_ELT(json_, 0)); - JSValue result = JS_ParseJSON(ctx, json, strlen(json), ""); - SEXP rtn = quickjsr::JSValue_to_SEXP(ctx, result); - - JS_FreeValue(ctx, result); - JS_FreeContext(ctx); - JS_FreeRuntimeStdHandlers(rt); - - return rtn; + JS_ValContainer result(rt_ctx, JS_ParseJSON(rt_ctx->ctx, json, strlen(json), "")); + return quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result.val); END_CPP11 }