GeezerVison is showing github.com — src: refactor embedded entrypoint loading · nodejs/node@18593b7
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/main/check_syntax.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -75,5 +75,5 @@ async function checkSyntax(source, filename) { | 75 | 75 | return; | 76 | 76 | } | 77 | 77 |
| 78 | | - wrapSafe(filename, source); | | 78 | + wrapSafe(filename, source, undefined, 'commonjs'); | 79 | 79 | } |
Collapse file Copy file name to clipboard +103-2Lines changed: 103 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | | 2 | + | | 3 | +// This main script is currently only run when LoadEnvironment() | | 4 | +// is run with a non-null StartExecutionCallback or a UTF8 | | 5 | +// main script. Effectively there are two cases where this happens: | | 6 | +// 1. It's a single-executable application *loading* a main script | | 7 | +// bundled into the executable. This is currently done from | | 8 | +// NodeMainInstance::Run(). | | 9 | +// 2. It's an embedder application and LoadEnvironment() is invoked | | 10 | +// as described above. | | 11 | + | 2 | 12 | const { | 3 | 13 | prepareMainThreadExecution, | 4 | 14 | } = require('internal/process/pre_execution'); | 5 | | -const { isExperimentalSeaWarningNeeded } = internalBinding('sea'); | | 15 | +const { isExperimentalSeaWarningNeeded, isSea } = internalBinding('sea'); | 6 | 16 | const { emitExperimentalWarning } = require('internal/util'); | 7 | | -const { embedderRequire, embedderRunCjs } = require('internal/util/embedding'); | | 17 | +const { emitWarningSync } = require('internal/process/warning'); | | 18 | +const { BuiltinModule: { normalizeRequirableId } } = require('internal/bootstrap/realm'); | | 19 | +const { Module } = require('internal/modules/cjs/loader'); | | 20 | +const { compileFunctionForCJSLoader } = internalBinding('contextify'); | | 21 | +const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache'); | | 22 | + | | 23 | +const { codes: { | | 24 | + ERR_UNKNOWN_BUILTIN_MODULE, | | 25 | +} } = require('internal/errors'); | 8 | 26 |
| | 27 | +// Don't expand process.argv[1] because in a single-executable application or an | | 28 | +// embedder application, the user main script isn't necessarily provided via the | | 29 | +// command line (e.g. it could be provided via an API or bundled into the executable). | 9 | 30 | prepareMainThreadExecution(false, true); | 10 | 31 |
| | 32 | +const isLoadingSea = isSea(); | 11 | 33 | if (isExperimentalSeaWarningNeeded()) { | 12 | 34 | emitExperimentalWarning('Single executable application'); | 13 | 35 | } | 14 | 36 |
| | 37 | +// This is roughly the same as: | | 38 | +// | | 39 | +// const mod = new Module(filename); | | 40 | +// mod._compile(content, filename); | | 41 | +// | | 42 | +// but the code has been duplicated because currently there is no way to set the | | 43 | +// value of require.main to module. | | 44 | +// | | 45 | +// TODO(RaisinTen): Find a way to deduplicate this. | | 46 | +function embedderRunCjs(content) { | | 47 | + // The filename of the module (used for CJS module lookup) | | 48 | + // is always the same as the location of the executable itself | | 49 | + // at the time of the loading (which means it changes depending | | 50 | + // on where the executable is in the file system). | | 51 | + const filename = process.execPath; | | 52 | + const customModule = new Module(filename, null); | | 53 | + | | 54 | + const { | | 55 | + function: compiledWrapper, | | 56 | + cachedDataRejected, | | 57 | + sourceMapURL, | | 58 | + } = compileFunctionForCJSLoader( | | 59 | + content, | | 60 | + filename, | | 61 | + isLoadingSea, // is_sea_main | | 62 | + false, // should_detect_module, ESM should be supported differently for embedded code | | 63 | + ); | | 64 | + // Cache the source map for the module if present. | | 65 | + if (sourceMapURL) { | | 66 | + maybeCacheSourceMap( | | 67 | + filename, | | 68 | + content, | | 69 | + customModule, | | 70 | + false, // isGeneratedSource | | 71 | + undefined, // sourceURL, TODO(joyeecheung): should be extracted by V8 | | 72 | + sourceMapURL, | | 73 | + ); | | 74 | + } | | 75 | + | | 76 | + // cachedDataRejected is only set if cache from SEA is used. | | 77 | + if (cachedDataRejected !== false && isLoadingSea) { | | 78 | + emitWarningSync('Code cache data rejected.'); | | 79 | + } | | 80 | + | | 81 | + // Patch the module to make it look almost like a regular CJS module | | 82 | + // instance. | | 83 | + customModule.filename = process.execPath; | | 84 | + customModule.paths = Module._nodeModulePaths(process.execPath); | | 85 | + embedderRequire.main = customModule; | | 86 | + | | 87 | + return compiledWrapper( | | 88 | + customModule.exports, // exports | | 89 | + embedderRequire, // require | | 90 | + customModule, // module | | 91 | + process.execPath, // __filename | | 92 | + customModule.path, // __dirname | | 93 | + ); | | 94 | +} | | 95 | + | | 96 | +let warnedAboutBuiltins = false; | | 97 | + | | 98 | +function embedderRequire(id) { | | 99 | + const normalizedId = normalizeRequirableId(id); | | 100 | + if (!normalizedId) { | | 101 | + if (isLoadingSea && !warnedAboutBuiltins) { | | 102 | + emitWarningSync( | | 103 | + 'Currently the require() provided to the main script embedded into ' + | | 104 | + 'single-executable applications only supports loading built-in modules.\n' + | | 105 | + 'To load a module from disk after the single executable application is ' + | | 106 | + 'launched, use require("module").createRequire().\n' + | | 107 | + 'Support for bundled module loading or virtual file systems are under ' + | | 108 | + 'discussions in https://github.com/nodejs/single-executable'); | | 109 | + warnedAboutBuiltins = true; | | 110 | + } | | 111 | + throw new ERR_UNKNOWN_BUILTIN_MODULE(id); | | 112 | + } | | 113 | + return require(normalizedId); | | 114 | +} | | 115 | + | 15 | 116 | return [process, embedderRequire, embedderRunCjs]; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +4-13Lines changed: 4 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1350,11 +1350,10 @@ function loadESMFromCJS(mod, filename) { | 1350 | 1350 | * Wraps the given content in a script and runs it in a new context. | 1351 | 1351 | * @param {string} filename The name of the file being loaded | 1352 | 1352 | * @param {string} content The content of the file being loaded | 1353 | | - * @param {Module} cjsModuleInstance The CommonJS loader instance | 1354 | | - * @param {object} codeCache The SEA code cache | | 1353 | + * @param {Module|undefined} cjsModuleInstance The CommonJS loader instance | 1355 | 1354 | * @param {'commonjs'|undefined} format Intended format of the module. | 1356 | 1355 | */ | 1357 | | -function wrapSafe(filename, content, cjsModuleInstance, codeCache, format) { | | 1356 | +function wrapSafe(filename, content, cjsModuleInstance, format) { | 1358 | 1357 | assert(format !== 'module'); // ESM should be handled in loadESMFromCJS(). | 1359 | 1358 | const hostDefinedOptionId = vm_dynamic_import_default_internal; | 1360 | 1359 | const importModuleDynamically = vm_dynamic_import_default_internal; | @@ -1385,16 +1384,8 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache, format) { | 1385 | 1384 | }; | 1386 | 1385 | } | 1387 | 1386 |
| 1388 | | - const isMain = !!(cjsModuleInstance && cjsModuleInstance[kIsMainSymbol]); | 1389 | 1387 | const shouldDetectModule = (format !== 'commonjs' && getOptionValue('--experimental-detect-module')); | 1390 | | - const result = compileFunctionForCJSLoader(content, filename, isMain, shouldDetectModule); | 1391 | | - | 1392 | | - // cachedDataRejected is only set for cache coming from SEA. | 1393 | | - if (codeCache && | 1394 | | - result.cachedDataRejected !== false && | 1395 | | - internalBinding('sea').isSea()) { | 1396 | | - process.emitWarning('Code cache data rejected.'); | 1397 | | - } | | 1388 | + const result = compileFunctionForCJSLoader(content, filename, false /* is_sea_main */, shouldDetectModule); | 1398 | 1389 |
| 1399 | 1390 | // Cache the source map for the module if present. | 1400 | 1391 | if (result.sourceMapURL) { | @@ -1423,7 +1414,7 @@ Module.prototype._compile = function(content, filename, format) { | 1423 | 1414 |
| 1424 | 1415 | let compiledWrapper; | 1425 | 1416 | if (format !== 'module') { | 1426 | | - const result = wrapSafe(filename, content, this, undefined, format); | | 1417 | + const result = wrapSafe(filename, content, this, format); | 1427 | 1418 | compiledWrapper = result.function; | 1428 | 1419 | if (result.canParseAsESM) { | 1429 | 1420 | format = 'module'; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -176,7 +176,7 @@ translators.set('module', function moduleStrategy(url, source, isMain) { | 176 | 176 | * @param {boolean} isMain - Whether the module is the entrypoint | 177 | 177 | */ | 178 | 178 | function loadCJSModule(module, source, url, filename, isMain) { | 179 | | - const compileResult = compileFunctionForCJSLoader(source, filename, isMain, false); | | 179 | + const compileResult = compileFunctionForCJSLoader(source, filename, false /* is_sea_main */, false); | 180 | 180 |
| 181 | 181 | const { function: compiledWrapper, sourceMapURL } = compileResult; | 182 | 182 | // Cache the source map for the cjs module if present. | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/util/embedding.js -51Lines changed: 0 additions & 51 deletions Collapse file Copy file name to clipboardExpand all lines: src/node.cc +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -308,6 +308,14 @@ std::optional<StartExecutionCallbackInfo> CallbackInfoFromArray( | 308 | 308 | CHECK(process_obj->IsObject()); | 309 | 309 | CHECK(require_fn->IsFunction()); | 310 | 310 | CHECK(runcjs_fn->IsFunction()); | | 311 | + // TODO(joyeecheung): some support for running ESM as an entrypoint | | 312 | + // is needed. The simplest API would be to add a run_esm to | | 313 | + // StartExecutionCallbackInfo which compiles, links (to builtins) | | 314 | + // and evaluates a SourceTextModule. | | 315 | + // TODO(joyeecheung): the env pointer should be part of | | 316 | + // StartExecutionCallbackInfo, otherwise embedders are forced to use | | 317 | + // lambdas to pass it into the callback, which can make the code | | 318 | + // difficult to read. | 311 | 319 | node::StartExecutionCallbackInfo info{process_obj.As<Object>(), | 312 | 320 | require_fn.As<Function>(), | 313 | 321 | runcjs_fn.As<Function>()}; | |
Collapse file Copy file name to clipboardExpand all lines: src/node_contextify.cc +34-21Lines changed: 34 additions & 21 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1441,12 +1441,17 @@ static std::vector<std::string_view> throws_only_in_cjs_error_messages = { | 1441 | 1441 | "await is only valid in async functions and " | 1442 | 1442 | "the top level bodies of modules"}; | 1443 | 1443 |
| 1444 | | -static MaybeLocal<Function> CompileFunctionForCJSLoader(Environment* env, | 1445 | | - Local<Context> context, | 1446 | | - Local<String> code, | 1447 | | - Local<String> filename, | 1448 | | - bool* cache_rejected, | 1449 | | - bool is_cjs_scope) { | | 1444 | +// If cached_data is provided, it would be used for the compilation and | | 1445 | +// the on-disk compilation cache from NODE_COMPILE_CACHE (if configured) | | 1446 | +// would be ignored. | | 1447 | +static MaybeLocal<Function> CompileFunctionForCJSLoader( | | 1448 | + Environment* env, | | 1449 | + Local<Context> context, | | 1450 | + Local<String> code, | | 1451 | + Local<String> filename, | | 1452 | + bool* cache_rejected, | | 1453 | + bool is_cjs_scope, | | 1454 | + ScriptCompiler::CachedData* cached_data) { | 1450 | 1455 | Isolate* isolate = context->GetIsolate(); | 1451 | 1456 | EscapableHandleScope scope(isolate); | 1452 | 1457 |
| @@ -1464,20 +1469,7 @@ static MaybeLocal<Function> CompileFunctionForCJSLoader(Environment* env, | 1464 | 1469 | false, // is WASM | 1465 | 1470 | false, // is ES Module | 1466 | 1471 | hdo); | 1467 | | - ScriptCompiler::CachedData* cached_data = nullptr; | 1468 | 1472 |
| 1469 | | -#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION | 1470 | | - if (sea::IsSingleExecutable()) { | 1471 | | - sea::SeaResource sea = sea::FindSingleExecutableResource(); | 1472 | | - if (sea.use_code_cache()) { | 1473 | | - std::string_view data = sea.code_cache.value(); | 1474 | | - cached_data = new ScriptCompiler::CachedData( | 1475 | | - reinterpret_cast<const uint8_t*>(data.data()), | 1476 | | - static_cast<int>(data.size()), | 1477 | | - v8::ScriptCompiler::CachedData::BufferNotOwned); | 1478 | | - } | 1479 | | - } | 1480 | | -#endif | 1481 | 1473 | ScriptCompiler::Source source(code, origin, cached_data); | 1482 | 1474 | ScriptCompiler::CompileOptions options; | 1483 | 1475 | if (cached_data == nullptr) { | @@ -1532,6 +1524,7 @@ static void CompileFunctionForCJSLoader( | 1532 | 1524 | CHECK(args[3]->IsBoolean()); | 1533 | 1525 | Local<String> code = args[0].As<String>(); | 1534 | 1526 | Local<String> filename = args[1].As<String>(); | | 1527 | + bool is_sea_main = args[2].As<Boolean>()->Value(); | 1535 | 1528 | bool should_detect_module = args[3].As<Boolean>()->Value(); | 1536 | 1529 |
| 1537 | 1530 | Isolate* isolate = args.GetIsolate(); | @@ -1544,11 +1537,31 @@ static void CompileFunctionForCJSLoader( | 1544 | 1537 | Local<Value> cjs_exception; | 1545 | 1538 | Local<Message> cjs_message; | 1546 | 1539 |
| | 1540 | + ScriptCompiler::CachedData* cached_data = nullptr; | | 1541 | +#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION | | 1542 | + if (is_sea_main) { | | 1543 | + sea::SeaResource sea = sea::FindSingleExecutableResource(); | | 1544 | + // Use the "main" field in SEA config for the filename. | | 1545 | + Local<Value> filename_from_sea; | | 1546 | + if (!ToV8Value(context, sea.code_path).ToLocal(&filename_from_sea)) { | | 1547 | + return; | | 1548 | + } | | 1549 | + filename = filename_from_sea.As<String>(); | | 1550 | + if (sea.use_code_cache()) { | | 1551 | + std::string_view data = sea.code_cache.value(); | | 1552 | + cached_data = new ScriptCompiler::CachedData( | | 1553 | + reinterpret_cast<const uint8_t*>(data.data()), | | 1554 | + static_cast<int>(data.size()), | | 1555 | + v8::ScriptCompiler::CachedData::BufferNotOwned); | | 1556 | + } | | 1557 | + } | | 1558 | +#endif | | 1559 | + | 1547 | 1560 | { | 1548 | 1561 | ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env()); | 1549 | 1562 | TryCatchScope try_catch(env); | 1550 | 1563 | if (!CompileFunctionForCJSLoader( | 1551 | | - env, context, code, filename, &cache_rejected, true) | | 1564 | + env, context, code, filename, &cache_rejected, true, cached_data) | 1552 | 1565 | .ToLocal(&fn)) { | 1553 | 1566 | CHECK(try_catch.HasCaught()); | 1554 | 1567 | CHECK(!try_catch.HasTerminated()); | @@ -1703,7 +1716,7 @@ static void ContainsModuleSyntax(const FunctionCallbackInfo<Value>& args) { | 1703 | 1716 | TryCatchScope try_catch(env); | 1704 | 1717 | ShouldNotAbortOnUncaughtScope no_abort_scope(env); | 1705 | 1718 | if (CompileFunctionForCJSLoader( | 1706 | | - env, context, code, filename, &cache_rejected, cjs_var) | | 1719 | + env, context, code, filename, &cache_rejected, cjs_var, nullptr) | 1707 | 1720 | .ToLocal(&fn)) { | 1708 | 1721 | args.GetReturnValue().Set(false); | 1709 | 1722 | return; | |
Collapse file Copy file name to clipboardExpand all lines: src/node_main_instance.cc +1-14Lines changed: 1 addition & 14 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -103,20 +103,7 @@ ExitCode NodeMainInstance::Run() { | 103 | 103 |
| 104 | 104 | void NodeMainInstance::Run(ExitCode* exit_code, Environment* env) { | 105 | 105 | if (*exit_code == ExitCode::kNoFailure) { | 106 | | - bool runs_sea_code = false; | 107 | | -#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION | 108 | | - if (sea::IsSingleExecutable()) { | 109 | | - sea::SeaResource sea = sea::FindSingleExecutableResource(); | 110 | | - if (!sea.use_snapshot()) { | 111 | | - runs_sea_code = true; | 112 | | - std::string_view code = sea.main_code_or_snapshot; | 113 | | - LoadEnvironment(env, code); | 114 | | - } | 115 | | - } | 116 | | -#endif | 117 | | - // Either there is already a snapshot main function from SEA, or it's not | 118 | | - // a SEA at all. | 119 | | - if (!runs_sea_code) { | | 106 | + if (!sea::MaybeLoadSingleExecutableApplication(env)) { | 120 | 107 | LoadEnvironment(env, StartExecutionCallback{}); | 121 | 108 | } | 122 | 109 |
| |
|