From a3e8069db8f406498f3abccecbeaac49553fae15 Mon Sep 17 00:00:00 2001 From: Jeroen Ooms Date: Sat, 5 Oct 2024 17:27:32 +0200 Subject: [PATCH] Attempt 2 --- R/callback.R | 10 +++++++ src/bindings.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/R/callback.R b/R/callback.R index 790c064..461fc85 100644 --- a/R/callback.R +++ b/R/callback.R @@ -50,3 +50,13 @@ no_jumps <- function(...){ structure("User interruption during R evaluation", class = 'cb_error') }) } + +read_file_string <- function(name, ...){ + no_jumps({ + path <- normalizePath(name, mustWork = FALSE) + if(!file.exists(path)){ + stop(sprintf("File '%s' not found", path)) + } + rawToChar(readBin(path, raw(), file.info(path)$size)) + }) +} diff --git a/src/bindings.cpp b/src/bindings.cpp index a654791..752c7fa 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -66,6 +66,75 @@ static void fatal_cb(const char* location, const char* message){ REprintf("V8 FATAL ERROR in %s: %s", location, message); } +static v8::Local compile_module(std::string src, v8::Local context); + +static v8::MaybeLocal ResolveModuleCallback(v8::Local context, + v8::Local specifier, + v8::Local import_attributes, + v8::Local referrer) { + v8::String::Utf8Value name(isolate, specifier); + Rf_warningcall_immediate(R_NilValue, "Failed to import '%s': (Modules are not yet supported)", ToCString(name)); + return compile_module("", context); +} + +static v8::MaybeLocal ResolveDynamicModuleCallback( + v8::Local context, + v8::Local host_defined_options, + v8::Local resource_name, + v8::Local specifier, + v8::Local import_assertions) { + v8::Local resolver = v8::Promise::Resolver::New(context).ToLocalChecked(); + v8::MaybeLocal promise(resolver->GetPromise()); + v8::String::Utf8Value name(context->GetIsolate(), specifier); + Rcpp::Function r_readlines = Rcpp::Environment::namespace_env("V8")["read_file_string"]; + Rcpp::CharacterVector out = r_readlines(Rcpp::String(*name)); + v8::Local source_text(ToJSString(std::string(out.at(0)).c_str())); + if(out.inherits("cb_error")){ + resolver->Reject(context, source_text).FromMaybe(false); //R error reading file + return promise; + } + if(source_text.IsEmpty()){ + resolver->Reject(context, ToJSString("Failed to read Module source code.")).FromMaybe(false); + return promise; + } + v8::ScriptOrigin origin(specifier, 0, 0, false, -1, v8::Local(), false, false, true); + v8::ScriptCompiler::Source source(source_text, origin); + v8::Local module; + if (!v8::ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)){ + resolver->Reject(context, ToJSString("Failure at CompileModule")).FromMaybe(false); + return promise; + } + if(!module->InstantiateModule(context, ResolveModuleCallback).FromMaybe(false)){ + resolver->Reject(context, ToJSString("Failure at InstantiateModule")).FromMaybe(false); + return promise; + } + v8::Local retValue; + if (!module->Evaluate(context).ToLocal(&retValue)) { + resolver->Reject(context, ToJSString("Failure evaluating")).FromMaybe(false); + return promise; + } + resolver->Resolve(context, module->GetModuleNamespace()).FromMaybe(false); + return promise; +} + + +/* Helper fun that compiles JavaScript source code */ +static v8::Local compile_module(std::string src, v8::Local context){ + v8::Local source_text = ToJSString(src.c_str()); + if(source_text.IsEmpty()) + throw std::runtime_error("Failed to load JavaScript source. Check memory/stack limits."); + v8::ScriptOrigin origin(ToJSString( "main.mjs"), 0, 0, false, -1, + v8::Local(), false, false, true); + v8::ScriptCompiler::Source source(source_text, origin); + v8::Local module; + if (!v8::ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) + throw std::runtime_error("Failed to run CompileModule() source. Check memory/stack limits."); + if(!module->InstantiateModule(context, ResolveModuleCallback).FromMaybe(false)) + throw std::runtime_error("Failed to run InstantiateModule() source. Check memory/stack limits."); + return module; +} + + // [[Rcpp::init]] void start_v8_isolate(void *dll){ #ifdef V8_ICU_DATA_PATH @@ -103,6 +172,8 @@ void start_v8_isolate(void *dll){ uintptr_t CurrentStackPosition = reinterpret_cast(__builtin_frame_address(0)); isolate->SetStackLimit(CurrentStackPosition - kWorkerMaxStackSize); #endif + // setting this callback enables dynamic imports + isolate->SetHostImportModuleDynamicallyCallback(ResolveDynamicModuleCallback); } /* Helper fun that compiles JavaScript source code */