Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use RAII for runtime/context #73

Merged
merged 7 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 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,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) {
Expand All @@ -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)
}
Expand All @@ -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",
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_Containers.hpp>

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

#include <cpp11/external_pointer.hpp>
#include <quickjs-libc.h>
#include <quickjs_helpers.hpp>

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<JS_RtCtxContainer>;

struct JS_ValContainer {
public:
RtCtxXPtr rt_ctx;
JSValue val;

template <typename JSValT>
JS_ValContainer(RtCtxXPtr in_rt_ctx, JSValT&& in_val)
: rt_ctx(in_rt_ctx), val(std::forward<JSValT>(in_val)) {}

~JS_ValContainer() {
JS_FreeValue(rt_ctx->ctx, val);
}
};

} // namespace quickjsr

#endif
119 changes: 35 additions & 84 deletions src/quickjsr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,80 @@
#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 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<int>(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), "<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);
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<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());

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
Expand All @@ -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), "<input>", 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), "<input>", 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), "<input>");
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), "<input>"));
return quickjsr::JSValue_to_SEXP(rt_ctx->ctx, result.val);
END_CPP11
}

Expand Down
Loading