diff --git a/NAMESPACE b/NAMESPACE index 13eeaa1..d3ecf83 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,9 +2,10 @@ export(JSContext) export(cxxflags) +export(from_json) export(ldflags) export(qjs_eval) -export(qjs_passthrough) export(quickjs_version) +export(to_json) importFrom(jsonlite,fromJSON) useDynLib(QuickJSR, .registration = TRUE) diff --git a/R/qjs.R b/R/qjs.R index 97a6c11..747c3a3 100644 --- a/R/qjs.R +++ b/R/qjs.R @@ -36,15 +36,26 @@ qjs_validate <- function(ctx_ptr, function_name) { .Call(`qjs_validate_`, ctx_ptr, function_name) } -#' qjs_passthrough +#' to_json #' -#' Test function to pass through arguments +#' Use the QuickJS C API to convert an R object to a JSON string #' -#' @param args Args to pass through -#' @param json Whether to return a JSON string -#' @return The input argument unchanged +#' @param arg Argument to convert to JSON +#' @return JSON string #' #' @export -qjs_passthrough <- function(args, json = TRUE) { - .Call(`qjs_passthrough_`, args, json) +to_json <- function(arg) { + .Call(`to_json_`, arg) +} + +#' from_json +#' +#' Use the QuickJS C API to convert a JSON string to an R object +#' +#' @param json JSON string to convert to an R object +#' @return R object +#' +#' @export +from_json <- function(json) { + .Call(`from_json_`, json) } diff --git a/inst/include/quickjsr.hpp b/inst/include/quickjsr.hpp index 68c52ef..90c4ef2 100644 --- a/inst/include/quickjsr.hpp +++ b/inst/include/quickjsr.hpp @@ -4,5 +4,7 @@ #include #include #include +#include +#include #endif diff --git a/inst/include/quickjsr/JSON_to_JSValue.hpp b/inst/include/quickjsr/JSON_to_JSValue.hpp new file mode 100644 index 0000000..b8efe73 --- /dev/null +++ b/inst/include/quickjsr/JSON_to_JSValue.hpp @@ -0,0 +1,30 @@ +#ifndef QUICKJSR_JSON_TO_JSVALUE_HPP +#define QUICKJSR_JSON_TO_JSVALUE_HPP + +#include +#include + +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); + + 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; +} + +} // namespace quickjsr +#endif diff --git a/inst/include/quickjsr/JSValue_to_JSON.hpp b/inst/include/quickjsr/JSValue_to_JSON.hpp index 6004835..44121e6 100644 --- a/inst/include/quickjsr/JSValue_to_JSON.hpp +++ b/inst/include/quickjsr/JSValue_to_JSON.hpp @@ -6,7 +6,7 @@ namespace quickjsr { -std::string JS_ValToJSON(JSContext* ctx, JSValue* val) { +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"); diff --git a/inst/tinytest/test_data_conversion.R b/inst/tinytest/test_data_conversion.R index 85db9a5..8749937 100644 --- a/inst/tinytest/test_data_conversion.R +++ b/inst/tinytest/test_data_conversion.R @@ -3,7 +3,7 @@ # - The outputs are returned as JSON strings # - If the conversion is consistent, the output should match jsonlite's conversion expect_eq_jsonlite <- function(x) { - expect_equal(qjs_passthrough(x), as.character(jsonlite::toJSON(x))) + expect_equal(to_json(x), as.character(jsonlite::toJSON(x))) } expect_eq_jsonlite(1) expect_eq_jsonlite(1:3) @@ -31,7 +31,8 @@ expect_eq_jsonlite(list(list(a = "e", b = "f"), list(c = "g", d = "h"))) # Test that the full round-trip conversion is consistent. expect_eq_jsonlite_full <- function(x) { - expect_equal(qjs_passthrough(x, FALSE), jsonlite::fromJSON(jsonlite::toJSON(x))) + x_json <- jsonlite::toJSON(x) + expect_equal(from_json(x_json), jsonlite::fromJSON(x_json)) } expect_eq_jsonlite_full(1) expect_eq_jsonlite_full(1:3) diff --git a/man/from_json.Rd b/man/from_json.Rd new file mode 100644 index 0000000..08364a9 --- /dev/null +++ b/man/from_json.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/qjs.R +\name{from_json} +\alias{from_json} +\title{from_json} +\usage{ +from_json(json) +} +\arguments{ +\item{json}{JSON string to convert to an R object} +} +\value{ +R object +} +\description{ +Use the QuickJS C API to convert a JSON string to an R object +} diff --git a/man/qjs_passthrough.Rd b/man/qjs_passthrough.Rd deleted file mode 100644 index deaa199..0000000 --- a/man/qjs_passthrough.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/qjs.R -\name{qjs_passthrough} -\alias{qjs_passthrough} -\title{qjs_passthrough} -\usage{ -qjs_passthrough(args, json = TRUE) -} -\arguments{ -\item{args}{Args to pass through} - -\item{json}{Whether to return a JSON string} -} -\value{ -The input argument unchanged -} -\description{ -Test function to pass through arguments -} diff --git a/man/to_json.Rd b/man/to_json.Rd new file mode 100644 index 0000000..a5d3787 --- /dev/null +++ b/man/to_json.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/qjs.R +\name{to_json} +\alias{to_json} +\title{to_json} +\usage{ +to_json(arg) +} +\arguments{ +\item{arg}{Argument to convert to JSON} +} +\value{ +JSON string +} +\description{ +Use the QuickJS C API to convert an R object to a JSON string +} diff --git a/src/init.cpp b/src/init.cpp index 1db9edf..e1e3563 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,7 +10,8 @@ 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 qjs_passthrough_(SEXP args_, SEXP json_rtn_); + SEXP to_json_(SEXP arg_); + SEXP from_json_(SEXP json_); static const R_CallMethodDef CallEntries[] = { @@ -19,7 +20,8 @@ extern "C" { {"qjs_eval_", (DL_FUNC) &qjs_eval_, 1}, {"qjs_source_", (DL_FUNC) &qjs_source_, 2}, {"qjs_validate_", (DL_FUNC) &qjs_validate_, 2}, - {"qjs_passthrough_", (DL_FUNC) &qjs_passthrough_, 2}, + {"to_json_", (DL_FUNC) &to_json_, 1}, + {"from_json_", (DL_FUNC) &from_json_, 1}, {NULL, NULL, 0} }; diff --git a/src/quickjsr.cpp b/src/quickjsr.cpp index 1f9e714..e8cac67 100644 --- a/src/quickjsr.cpp +++ b/src/quickjsr.cpp @@ -90,7 +90,7 @@ extern "C" SEXP qjs_call_(SEXP ctx_ptr_, SEXP function_name_, SEXP args_json_) { js_std_dump_error(ctx); result = "Error!"; } else { - result = quickjsr::JS_ValToJSON(ctx, &result_js); + result = quickjsr::JSValue_to_JSON(ctx, &result_js); } JS_FreeValue(ctx, result_js); @@ -114,7 +114,7 @@ extern "C" SEXP qjs_eval_(SEXP eval_string_) { js_std_dump_error(ctx); result = "Error!"; } else { - result = quickjsr::JS_ValToJSON(ctx, &val); + result = quickjsr::JSValue_to_JSON(ctx, &val); } JS_FreeValue(ctx, val); @@ -125,44 +125,35 @@ extern "C" SEXP qjs_eval_(SEXP eval_string_) { END_CPP11 } -extern "C" SEXP qjs_passthrough_(SEXP args_, SEXP json_rtn_) { +extern "C" SEXP to_json_(SEXP arg_) { BEGIN_CPP11 JSRuntime* rt = JS_NewRuntime(); JSContext* ctx = JS_NewContext(rt); - std::string function_string = "function passthrough(x) { return x; }"; - JSValue tmp = JS_Eval(ctx, function_string.c_str(), function_string.size(), "", 0); - bool failed = JS_IsException(tmp); - JS_FreeValue(ctx, tmp); - if (failed) { - js_std_dump_error(ctx); - return cpp11::as_sexp("Error!"); - } - std::string wrapped_name = "passthrough"; - JSValue global = JS_GetGlobalObject(ctx); - JSValue function_wrapper = JS_GetPropertyStr(ctx, global, wrapped_name.c_str()); - JSValue args[] = { quickjsr::SEXP_to_JSValue(ctx, args_) }; - JSValue result_js = JS_Call(ctx, function_wrapper, global, 1, args); + JSValue arg = quickjsr::SEXP_to_JSValue(ctx, arg_); + std::string result = quickjsr::JSValue_to_JSON(ctx, &arg); - SEXP result; - if (JS_IsException(result_js)) { - js_std_dump_error(ctx); - result = cpp11::as_sexp("Error!"); - } else { - if (cpp11::as_cpp(json_rtn_)) { - result = cpp11::as_sexp(quickjsr::JS_ValToJSON(ctx, &result_js)); - } else { - result = quickjsr::JSValue_to_SEXP(ctx, result_js); - } - } - - JS_FreeValue(ctx, result_js); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, function_wrapper); - JS_FreeValue(ctx, global); + JS_FreeValue(ctx, arg); JS_FreeContext(ctx); JS_FreeRuntime(rt); return cpp11::as_sexp(result); END_CPP11 } + +extern "C" SEXP from_json_(SEXP json_) { + BEGIN_CPP11 + JSRuntime* rt = JS_NewRuntime(); + JSContext* ctx = JS_NewContext(rt); + + std::string json = cpp11::as_cpp(json_); + JSValue result = quickjsr::JSON_to_JSValue(ctx, json); + SEXP rtn = quickjsr::JSValue_to_SEXP(ctx, result); + + JS_FreeValue(ctx, result); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + return rtn; + END_CPP11 +}