Skip to content

Commit

Permalink
Use RAII for runtime/context
Browse files Browse the repository at this point in the history
  • Loading branch information
andrjohns committed Jul 17, 2024
1 parent 3f21228 commit bbe9a68
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 82 deletions.
35 changes: 17 additions & 18 deletions R/JSContext.R
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -110,14 +110,13 @@ 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)
rt_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) {
Expand All @@ -127,10 +126,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)
}
Expand All @@ -141,13 +140,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",
Expand Down
1 change: 1 addition & 0 deletions inst/include/quickjsr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
#include <quickjsr/JSValue_to_JSON.hpp>
#include <quickjsr/type_traits.hpp>
#include <quickjsr/JS_PropertyRecursive.hpp>
#include <quickjsr/JS_RtCtxContainer.hpp>

#endif
28 changes: 28 additions & 0 deletions inst/include/quickjsr/JS_RtCtxContainer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef QUICKJSR_JS_RTCTXCONTAINER_HPP
#define QUICKJSR_JS_RTCTXCONTAINER_HPP

#include <quickjs-libc.h>
#include <quickjs_helpers.hpp>

namespace quickjsr {

class JS_RtCtxContainer {
public:
JSRuntime* rt;
JSContext* ctx;

JS_RtCtxContainer(int stack_size = 0) {
rt = quickjsr::JS_NewCustomRuntime(stack_size);
ctx = quickjsr::JS_NewCustomContext(rt);
}

~JS_RtCtxContainer() {
JS_FreeContext(ctx);
js_std_free_handlers(rt);
JS_FreeRuntime(rt);
}
};

} // namespace quickjsr

#endif
107 changes: 43 additions & 64 deletions src/quickjsr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,94 @@
#include <quickjs_helpers.hpp>
#include <quickjsr.hpp>

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<JSContext, JS_FreeContext>;
using RuntimeXPtr = cpp11::external_pointer<JSRuntime, JS_FreeRuntimeStdHandlers>;
using quickjsr::JS_RtCtxContainer;
using RtCtxXPtr = cpp11::external_pointer<JS_RtCtxContainer>;

extern "C" {
SEXP qjs_context_(SEXP stack_size_) {
BEGIN_CPP11
int stack_size = Rf_isInteger(stack_size_) ? INTEGER_ELT(stack_size_, 0)
: static_cast<int>(REAL_ELT(stack_size_, 0));

RuntimeXPtr rt(quickjsr::JS_NewCustomRuntime(stack_size));
ContextXPtr ctx(quickjsr::JS_NewCustomContext(rt.get()));
RtCtxXPtr rt(new JS_RtCtxContainer(stack_size));

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);
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), "<input>", JS_EVAL_TYPE_GLOBAL);
ret = quickjsr::eval_buf(rt_ctx->ctx, input, strlen(input), "<input>", JS_EVAL_TYPE_GLOBAL);
}
return cpp11::as_sexp(!ret);
END_CPP11
}

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);
JSValue val = JS_Eval(rt_ctx->ctx, code_string, strlen(code_string), "", JS_EVAL_FLAG_COMPILE_ONLY);
bool failed = JS_IsException(val);
JS_FreeValue(ctx, val);
JS_FreeValue(rt_ctx->ctx, val);
return cpp11::as_sexp(!failed);
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<JSValue> 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());
JSValue global = JS_GetGlobalObject(rt_ctx->ctx);
JSValue fun = quickjsr::JS_GetPropertyRecursive(rt_ctx->ctx, global, Rf_translateCharUTF8(STRING_ELT(fun_name_, 0)));
JSValue result_js = JS_Call(rt_ctx->ctx, fun, global, args.size(), args.data());

SEXP result = quickjsr::JSValue_to_SEXP(ctx, result_js);
SEXP result = quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result_js);

JS_FreeValue(ctx, result_js);
JS_FreeValue(rt_ctx->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);
JS_FreeValue(rt_ctx->ctx, fun);
JS_FreeValue(rt_ctx->ctx, global);

return result;
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);
RtCtxXPtr rt_ctx(ctx_ptr_);
JSValue global = JS_GetGlobalObject(rt_ctx->ctx);
JSValue result = quickjsr::JS_GetPropertyRecursive(rt_ctx->ctx, global, Rf_translateCharUTF8(STRING_ELT(js_obj_name, 0)));
SEXP rtn = quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result);

JS_FreeValue(ctx, result);
JS_FreeValue(ctx, global);
JS_FreeValue(rt_ctx->ctx, result);
JS_FreeValue(rt_ctx->ctx, global);

return rtn;
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_);
JSValue global = JS_GetGlobalObject(rt_ctx->ctx);
JSValue value = quickjsr::SEXP_to_JSValue(rt_ctx->ctx, value_, true);
int result = quickjsr::JS_SetPropertyRecursive(rt_ctx->ctx, global, Rf_translateCharUTF8(STRING_ELT(js_obj_name_, 0)), value);
JS_FreeValue(rt_ctx->ctx, global);

return cpp11::as_sexp(result);
END_CPP11
Expand All @@ -112,48 +100,39 @@ 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);
JS_RtCtxContainer rt_ctx;

JSValue val = JS_Eval(ctx, eval_string, strlen(eval_string), "<input>", JS_EVAL_TYPE_GLOBAL);
SEXP result = quickjsr::JSValue_to_SEXP(ctx, val);
JSValue val = JS_Eval(rt_ctx.ctx, eval_string, strlen(eval_string), "<input>", JS_EVAL_TYPE_GLOBAL);
SEXP result = quickjsr::JSValue_to_SEXP(rt_ctx.ctx, val);

JS_FreeValue(ctx, val);
JS_FreeContext(ctx);
JS_FreeRuntimeStdHandlers(rt);
JS_FreeValue(rt_ctx.ctx, val);

return result;
END_CPP11
}

SEXP to_json_(SEXP arg_, SEXP auto_unbox_) {
BEGIN_CPP11
JSRuntime* rt = quickjsr::JS_NewCustomRuntime(0);
JSContext* ctx = quickjsr::JS_NewCustomContext(rt);
JS_RtCtxContainer rt_ctx;

JSValue arg = quickjsr::SEXP_to_JSValue(ctx, arg_, LOGICAL_ELT(auto_unbox_, 0));
std::string result = quickjsr::JSValue_to_JSON(ctx, arg);
JSValue arg = quickjsr::SEXP_to_JSValue(rt_ctx.ctx, arg_, LOGICAL_ELT(auto_unbox_, 0));
std::string result = quickjsr::JSValue_to_JSON(rt_ctx.ctx, arg);

JS_FreeValue(ctx, arg);
JS_FreeContext(ctx);
JS_FreeRuntimeStdHandlers(rt);
JS_FreeValue(rt_ctx.ctx, arg);

return cpp11::as_sexp(result);
END_CPP11
}

SEXP from_json_(SEXP json_) {
BEGIN_CPP11
JSRuntime* rt = quickjsr::JS_NewCustomRuntime(0);
JSContext* ctx = quickjsr::JS_NewCustomContext(rt);
JS_RtCtxContainer rt_ctx;

const char* json = Rf_translateCharUTF8(STRING_ELT(json_, 0));
JSValue result = JS_ParseJSON(ctx, json, strlen(json), "<input>");
SEXP rtn = quickjsr::JSValue_to_SEXP(ctx, result);
JSValue result = JS_ParseJSON(rt_ctx.ctx, json, strlen(json), "<input>");
SEXP rtn = quickjsr::JSValue_to_SEXP(rt_ctx.ctx, result);

JS_FreeValue(ctx, result);
JS_FreeContext(ctx);
JS_FreeRuntimeStdHandlers(rt);
JS_FreeValue(rt_ctx.ctx, result);

return rtn;
END_CPP11
Expand Down

0 comments on commit bbe9a68

Please sign in to comment.