@@ -24,18 +24,21 @@ using v8::Array;
2424using v8::ArrayBuffer;
2525using v8::BackingStore;
2626using v8::Context;
27+ using v8::Data;
2728using v8::Function;
2829using v8::FunctionCallbackInfo;
2930using v8::HandleScope;
3031using v8::Isolate;
3132using v8::Local;
3233using v8::LocalVector;
3334using v8::MaybeLocal;
35+ using v8::Module;
3436using v8::NewStringType;
3537using v8::Object;
3638using v8::ScriptCompiler;
3739using v8::ScriptOrigin;
3840using v8::String;
41+ using v8::UnboundModuleScript;
3942using v8::Value;
4043
4144namespace node {
@@ -542,7 +545,7 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
542545 " \" useCodeCache\" is redundant when \" useSnapshot\" is true\n " );
543546 }
544547
545- // TODO(joyeecheung): support ESM with useSnapshot and useCodeCache .
548+ // TODO(joyeecheung): support ESM with useSnapshot.
546549 if (result.main_format == ModuleFormat::kModule &&
547550 static_cast <bool >(result.flags & SeaFlags::kUseSnapshot )) {
548551 FPrintF (stderr,
@@ -551,14 +554,6 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
551554 return std::nullopt ;
552555 }
553556
554- if (result.main_format == ModuleFormat::kModule &&
555- static_cast <bool >(result.flags & SeaFlags::kUseCodeCache )) {
556- FPrintF (stderr,
557- " \" mainFormat\" : \" module\" is not supported when "
558- " \" useCodeCache\" is true\n " );
559- return std::nullopt ;
560- }
561-
562557 if (result.main_path .empty ()) {
563558 FPrintF (stderr,
564559 " \" main\" field of %s is not a non-empty string\n " ,
@@ -616,7 +611,8 @@ ExitCode GenerateSnapshotForSEA(const SeaConfig& config,
616611}
617612
618613std::optional<std::string> GenerateCodeCache (std::string_view main_path,
619- std::string_view main_script) {
614+ std::string_view main_script,
615+ ModuleFormat format) {
620616 RAIIIsolate raii_isolate (SnapshotBuilder::GetEmbeddedSnapshotData ());
621617 Isolate* isolate = raii_isolate.get ();
622618
@@ -647,34 +643,62 @@ std::optional<std::string> GenerateCodeCache(std::string_view main_path,
647643 return std::nullopt ;
648644 }
649645
650- LocalVector<String> parameters (
651- isolate,
652- {
653- FIXED_ONE_BYTE_STRING (isolate, " exports" ),
654- FIXED_ONE_BYTE_STRING (isolate, " require" ),
655- FIXED_ONE_BYTE_STRING (isolate, " module" ),
656- FIXED_ONE_BYTE_STRING (isolate, " __filename" ),
657- FIXED_ONE_BYTE_STRING (isolate, " __dirname" ),
658- });
659- ScriptOrigin script_origin (filename, 0 , 0 , true );
660- ScriptCompiler::Source script_source (content, script_origin);
661- MaybeLocal<Function> maybe_fn =
662- ScriptCompiler::CompileFunction (context,
663- &script_source,
664- parameters.size (),
665- parameters.data (),
666- 0 ,
667- nullptr );
668- Local<Function> fn;
669- if (!maybe_fn.ToLocal (&fn)) {
670- return std::nullopt ;
646+ std::unique_ptr<ScriptCompiler::CachedData> cache;
647+
648+ if (format == ModuleFormat::kModule ) {
649+ // Using empty host defined options is fine as it is not part of the cache
650+ // key and will be reset after deserialization.
651+ ScriptOrigin origin (filename,
652+ 0 , // line offset
653+ 0 , // column offset
654+ true , // is cross origin
655+ -1 , // script id
656+ Local<Value>(), // source map URL
657+ false , // is opaque
658+ false , // is WASM
659+ true , // is ES Module
660+ Local<Data>()); // host defined options
661+ ScriptCompiler::Source source (content, origin);
662+ Local<Module> module ;
663+ if (!ScriptCompiler::CompileModule (isolate, &source).ToLocal (&module )) {
664+ return std::nullopt ;
665+ }
666+ Local<UnboundModuleScript> unbound = module ->GetUnboundModuleScript ();
667+ cache.reset (ScriptCompiler::CreateCodeCache (unbound));
668+ } else {
669+ // TODO(RaisinTen): Using the V8 code cache prevents us from using
670+ // `import()` in the SEA code. Support it. Refs:
671+ // https://github.com/nodejs/node/pull/48191#discussion_r1213271430
672+ // TODO(joyeecheung): this likely has been fixed by
673+ // https://chromium-review.googlesource.com/c/v8/v8/+/5401780 - add a test
674+ // and update docs.
675+ LocalVector<String> parameters (
676+ isolate,
677+ {
678+ FIXED_ONE_BYTE_STRING (isolate, " exports" ),
679+ FIXED_ONE_BYTE_STRING (isolate, " require" ),
680+ FIXED_ONE_BYTE_STRING (isolate, " module" ),
681+ FIXED_ONE_BYTE_STRING (isolate, " __filename" ),
682+ FIXED_ONE_BYTE_STRING (isolate, " __dirname" ),
683+ });
684+ ScriptOrigin script_origin (filename, 0 , 0 , true );
685+ ScriptCompiler::Source script_source (content, script_origin);
686+ Local<Function> fn;
687+ if (!ScriptCompiler::CompileFunction (context,
688+ &script_source,
689+ parameters.size (),
690+ parameters.data (),
691+ 0 ,
692+ nullptr )
693+ .ToLocal (&fn)) {
694+ return std::nullopt ;
695+ }
696+ cache.reset (ScriptCompiler::CreateCodeCacheForFunction (fn));
671697 }
672698
673- // TODO(RaisinTen): Using the V8 code cache prevents us from using `import()`
674- // in the SEA code. Support it.
675- // Refs: https://github.com/nodejs/node/pull/48191#discussion_r1213271430
676- std::unique_ptr<ScriptCompiler::CachedData> cache{
677- ScriptCompiler::CreateCodeCacheForFunction (fn)};
699+ if (!cache) {
700+ return std::nullopt ;
701+ }
678702 std::string code_cache (cache->data , cache->data + cache->length );
679703 return code_cache;
680704}
@@ -728,7 +752,7 @@ ExitCode GenerateSingleExecutableBlob(
728752 std::string code_cache;
729753 if (static_cast <bool >(config.flags & SeaFlags::kUseCodeCache )) {
730754 std::optional<std::string> optional_code_cache =
731- GenerateCodeCache (config.main_path , main_script);
755+ GenerateCodeCache (config.main_path , main_script, config. main_format );
732756 if (!optional_code_cache.has_value ()) {
733757 FPrintF (stderr, " Cannot generate V8 code cache\n " );
734758 return ExitCode::kGenericUserError ;
0 commit comments