Skip to content

Commit

Permalink
Use quickjs builtin functionality for parse and stringify, update doc (
Browse files Browse the repository at this point in the history
  • Loading branch information
andrjohns authored May 25, 2024
1 parent 8102779 commit eb9602c
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 31 deletions.
68 changes: 68 additions & 0 deletions R/JSContext.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
#' Assess validity of JS code without evaluating
#'
#' @name JSContext-method-validate
#' @aliases validate
#'
#' @usage validate(code_string)
#'
#' @description
#' Checks whether JS code string is valid code in the current context
#'
#' @param code_string The JS code to check
#' @return A boolean indicating whether code is valid
#'
#' @examples
#' \dontrun{
#' ctx <- JSContext$new()
#' ctx$validate("1 + 2")
#' }
validate <- NULL

#' Evaluate JS string or file in the current context
#'
#' @name JSContext-method-source
#' @aliases source
#'
#' @usage source(file = NULL, code = NULL)
#'
#' @description
#' Evaluate a provided JavaScript file or string within the initialised context.
#' Note that this method should only be used for initialising functions or values
#' within the context, no values are returned from this function. See the `$call()`
#' method for returning values.
#'
#' @param file A path to the JavaScript file to load
#' @param code A single string of JavaScript to evaluate
#' @return No return value, called for side effects
#'
#' @examples
#' \dontrun{
#' ctx <- JSContext$new()
#' ctx$source(file = "path/to/file.js")
#' ctx$source(code = "1 + 2")
#' }
source <- NULL

#' Call a JS function in the current context
#'
#' @name JSContext-method-call
#' @aliases call
#'
#' @usage call(function_name, ...)
#'
#' @description Call a specified function in the JavaScript context with the
#' provided arguments.
#'
#' @param function_name The function to be called
#' @param ... The arguments to be passed to the function
#' @return The result of calling the specified function,
#' the return type is mapped from JS to R using `jsonlite::fromJSON()`
#'
#' @examples
#' \dontrun{
#' ctx <- JSContext$new()
#' ctx$source(code = "function add(a, b) { return a + b; }")
#' ctx$call("add", 1, 2)
#' }
call <- 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)
Expand Down
8 changes: 5 additions & 3 deletions R/qjs.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ qjs_source <- function(ctx_ptr, code_string) {
}

qjs_call <- function(ctx_ptr, function_name, ...) {
parse_return(.Call(`qjs_call_`, ctx_ptr, function_name, list(...)))
args_json <- lapply(list(...), jsonlite::toJSON, auto_unbox = TRUE)
parse_return(.Call(`qjs_call_`, ctx_ptr, function_name, args_json))
}

qjs_validate <- function(ctx_ptr, function_name) {
Expand All @@ -41,11 +42,12 @@ qjs_validate <- function(ctx_ptr, function_name) {
#' Use the QuickJS C API to convert an R object to a JSON string
#'
#' @param arg Argument to convert to JSON
#' @param auto_unbox Automatically unbox single element vectors
#' @return JSON string
#'
#' @export
to_json <- function(arg) {
.Call(`to_json_`, arg)
to_json <- function(arg, auto_unbox = FALSE) {
.Call(`to_json_`, arg, auto_unbox)
}

#' from_json
Expand Down
12 changes: 1 addition & 11 deletions inst/include/quickjsr/JSON_to_JSValue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,12 @@
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);
JSValue result = JS_ParseJSON(ctx, json.c_str(), json.size(), "<input>");

if (JS_IsException(result)) {
js_std_dump_error(ctx);
}

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

return result;
}

Expand Down
10 changes: 2 additions & 8 deletions inst/include/quickjsr/JSValue_to_JSON.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@

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");
std::string JSValue_to_JSON(JSContext* ctx, JSValue val) {

JSValue result_js = JS_Call(ctx, stringify, global, 1, val);
JSValue result_js = JS_JSONStringify(ctx, val, JS_UNDEFINED, JS_UNDEFINED);
std::string result;
if (JS_IsException(result_js)) {
js_std_dump_error(ctx);
Expand All @@ -21,9 +18,6 @@ std::string JSValue_to_JSON(JSContext* ctx, JSValue* val) {
}

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

return result;
}
Expand Down
34 changes: 34 additions & 0 deletions man/JSContext-method-call.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions man/JSContext-method-source.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions man/JSContext-method-validate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion man/to_json.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extern "C" {
SEXP qjs_validate_(SEXP ctx_ptr_, SEXP code_string_);
SEXP qjs_call_(SEXP ctx_ptr_, SEXP function_name_, SEXP args_json_);
SEXP qjs_eval_(SEXP eval_string_);
SEXP to_json_(SEXP arg_);
SEXP to_json_(SEXP arg_, SEXP auto_unbox_);
SEXP from_json_(SEXP json_);


Expand All @@ -20,7 +20,7 @@ extern "C" {
{"qjs_eval_", (DL_FUNC) &qjs_eval_, 1},
{"qjs_source_", (DL_FUNC) &qjs_source_, 2},
{"qjs_validate_", (DL_FUNC) &qjs_validate_, 2},
{"to_json_", (DL_FUNC) &to_json_, 1},
{"to_json_", (DL_FUNC) &to_json_, 2},
{"from_json_", (DL_FUNC) &from_json_, 1},
{NULL, NULL, 0}
};
Expand Down
13 changes: 7 additions & 6 deletions src/quickjsr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extern "C" SEXP qjs_call_(SEXP ctx_ptr_, SEXP fun_name_, SEXP args_list_) {
int n_args = Rf_length(args_list_);
std::vector<JSValue> args(n_args);
for (int i = 0; i < n_args; i++) {
args[i] = quickjsr::SEXP_to_JSValue(ctx, VECTOR_ELT(args_list_, i), true);
args[i] = quickjsr::JSON_to_JSValue(ctx, cpp11::as_cpp<std::string>(VECTOR_ELT(args_list_, i)));
}

JSValue global = JS_GetGlobalObject(ctx);
Expand All @@ -77,7 +77,7 @@ extern "C" SEXP qjs_call_(SEXP ctx_ptr_, SEXP fun_name_, SEXP args_list_) {
js_std_dump_error(ctx);
result = "Error!";
} else {
result = quickjsr::JSValue_to_JSON(ctx, &result_js);
result = quickjsr::JSValue_to_JSON(ctx, result_js);
}

JS_FreeValue(ctx, result_js);
Expand All @@ -103,7 +103,7 @@ extern "C" SEXP qjs_eval_(SEXP eval_string_) {
js_std_dump_error(ctx);
result = "Error!";
} else {
result = quickjsr::JSValue_to_JSON(ctx, &val);
result = quickjsr::JSValue_to_JSON(ctx, val);
}

JS_FreeValue(ctx, val);
Expand All @@ -114,13 +114,14 @@ extern "C" SEXP qjs_eval_(SEXP eval_string_) {
END_CPP11
}

extern "C" SEXP to_json_(SEXP arg_) {
extern "C" SEXP to_json_(SEXP arg_, SEXP auto_unbox_) {
BEGIN_CPP11
JSRuntime* rt = JS_NewRuntime();
JSContext* ctx = JS_NewContext(rt);

JSValue arg = quickjsr::SEXP_to_JSValue(ctx, arg_);
std::string result = quickjsr::JSValue_to_JSON(ctx, &arg);
JSValue arg = quickjsr::SEXP_to_JSValue(ctx, arg_,
cpp11::as_cpp<bool>(auto_unbox_));
std::string result = quickjsr::JSValue_to_JSON(ctx, arg);

JS_FreeValue(ctx, arg);
JS_FreeContext(ctx);
Expand Down

0 comments on commit eb9602c

Please sign in to comment.