|
8 | 8 |
|
9 | 9 | #include "js_native_api.h"
|
10 | 10 | #include "v8-api.h"
|
| 11 | +#include "v8-module-loader.h" |
11 | 12 |
|
12 | 13 | #ifdef ANDROID
|
13 | 14 | #include <android/log.h>
|
@@ -3272,6 +3273,110 @@ napi_status napi_run_script_source(napi_env env, napi_value script,
|
3272 | 3273 | env, v8impl::JsValueFromV8LocalValue(source_with_comment), result);
|
3273 | 3274 | }
|
3274 | 3275 |
|
| 3276 | +// ES Module support |
| 3277 | +napi_status NAPI_CDECL napi_run_script_as_module(napi_env env, |
| 3278 | + napi_value script, |
| 3279 | + const char* source_url, |
| 3280 | + napi_value* result) { |
| 3281 | + NAPI_PREAMBLE(env); |
| 3282 | + CHECK_ARG(env, script); |
| 3283 | + CHECK_ARG(env, source_url); |
| 3284 | + CHECK_ARG(env, result); |
| 3285 | + |
| 3286 | + v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script); |
| 3287 | + |
| 3288 | + if (!v8_script->IsString()) { |
| 3289 | + return napi_set_last_error(env, napi_string_expected); |
| 3290 | + } |
| 3291 | + |
| 3292 | + v8::Local<v8::Context> context = env->context(); |
| 3293 | + v8::Isolate* isolate = env->isolate; |
| 3294 | + |
| 3295 | + v8::TryCatch module_try_catch(isolate); |
| 3296 | + |
| 3297 | + // Initialize ES module system on first use |
| 3298 | + static bool es_module_initialized = false; |
| 3299 | + if (!es_module_initialized) { |
| 3300 | + v8impl::InitializeESModuleSystem(isolate); |
| 3301 | + es_module_initialized = true; |
| 3302 | + } |
| 3303 | + |
| 3304 | + // Create script origin for ES module |
| 3305 | + v8::ScriptOrigin origin( |
| 3306 | + isolate, |
| 3307 | + v8::String::NewFromUtf8(isolate, source_url, v8::NewStringType::kNormal).ToLocalChecked(), |
| 3308 | + 0, 0, false, -1, v8::Local<v8::Value>(), false, false, |
| 3309 | + true // is_module = true for ES modules |
| 3310 | + ); |
| 3311 | + |
| 3312 | + v8::ScriptCompiler::Source source(v8_script.As<v8::String>(), origin); |
| 3313 | + |
| 3314 | + // Compile as ES module |
| 3315 | + v8::MaybeLocal<v8::Module> maybe_module = v8::ScriptCompiler::CompileModule( |
| 3316 | + isolate, &source, v8::ScriptCompiler::kNoCompileOptions); |
| 3317 | + |
| 3318 | + if (maybe_module.IsEmpty()) { |
| 3319 | + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_module, napi_generic_failure); |
| 3320 | + return napi_generic_failure; |
| 3321 | + } |
| 3322 | + |
| 3323 | + v8::Local<v8::Module> module = maybe_module.ToLocalChecked(); |
| 3324 | + |
| 3325 | + // Register the module in our module registry for resolution |
| 3326 | + // Use the source_url as the module path |
| 3327 | + std::string modulePath = source_url; |
| 3328 | + |
| 3329 | + // Safe Global handle management: Clear any existing entry first |
| 3330 | + auto it = v8impl::g_moduleRegistry.find(modulePath); |
| 3331 | + if (it != v8impl::g_moduleRegistry.end()) { |
| 3332 | + // Clear the existing Global handle before replacing it |
| 3333 | + it->second.Reset(); |
| 3334 | + } |
| 3335 | + |
| 3336 | + v8impl::g_moduleRegistry[modulePath].Reset(isolate, module); |
| 3337 | + |
| 3338 | + // Check for pending exception from compilation |
| 3339 | + if (module_try_catch.HasCaught()) { |
| 3340 | + // Log the exception to console to debug |
| 3341 | + v8::Local<v8::Value> exception = module_try_catch.Exception(); |
| 3342 | + v8::Local<v8::Message> message = module_try_catch.Message(); |
| 3343 | + if (!message.IsEmpty()) { |
| 3344 | + v8::String::Utf8Value error(isolate, message->Get()); |
| 3345 | + fprintf(stderr, "Error compiling module: %s\n", *error); |
| 3346 | + } else { |
| 3347 | + v8::String::Utf8Value error(isolate, exception); |
| 3348 | + fprintf(stderr, "Error compiling module: %s\n", *error); |
| 3349 | + } |
| 3350 | + return napi_set_last_error(env, napi_generic_failure); |
| 3351 | + } |
| 3352 | + |
| 3353 | + // Use our ES module resolver |
| 3354 | + v8::TryCatch instantiate_try_catch(isolate); |
| 3355 | + if (!module->InstantiateModule(context, &v8impl::ResolveModuleCallback).FromMaybe(false)) { |
| 3356 | + if (instantiate_try_catch.HasCaught()) { |
| 3357 | + // Store the exception in env->last_exception instead of throwing |
| 3358 | + v8::Local<v8::Value> exception = instantiate_try_catch.Exception(); |
| 3359 | + v8::String::Utf8Value error(isolate, exception); |
| 3360 | + |
| 3361 | + if (!env->last_exception.IsEmpty()) { |
| 3362 | + env->last_exception.Reset(); |
| 3363 | + } |
| 3364 | + env->last_exception.Reset(env->isolate, instantiate_try_catch.Exception()); |
| 3365 | + } |
| 3366 | + return napi_set_last_error(env, napi_generic_failure); |
| 3367 | + } |
| 3368 | + |
| 3369 | + // Evaluate the module |
| 3370 | + v8::MaybeLocal<v8::Value> maybe_result = module->Evaluate(context); |
| 3371 | + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_result, napi_generic_failure); |
| 3372 | + |
| 3373 | + // Get the module namespace as the result |
| 3374 | + v8::Local<v8::Value> namespace_obj = module->GetModuleNamespace(); |
| 3375 | + *result = v8impl::JsValueFromV8LocalValue(namespace_obj); |
| 3376 | + |
| 3377 | + return GET_RETURN_STATUS(env); |
| 3378 | +} |
| 3379 | + |
3275 | 3380 | napi_status NAPI_CDECL napi_add_finalizer(napi_env env, napi_value js_object,
|
3276 | 3381 | void* finalize_data,
|
3277 | 3382 | napi_finalize finalize_cb,
|
|
0 commit comments