| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent fb356b3 commit 575ced8
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3953,8 +3953,8 @@ cases: | |||
| 3953 | 3953 | and generally can only happen during development of Node.js itself. | |
| 3954 | 3954 | * `12` **Invalid Debug Argument**: The `--inspect` and/or `--inspect-brk` | |
| 3955 | 3955 | options were set, but the port number chosen was invalid or unavailable. | |
| 3956 | - * `13` **Unfinished Top-Level Await**: `await` was used outside of a function | ||
| 3957 | - in the top-level code, but the passed `Promise` never resolved. | ||
| 3956 | + * `13` **Unsettled Top-Level Await**: `await` was used outside of a function | ||
| 3957 | + in the top-level code, but the passed `Promise` never settled. | ||
| 3958 | 3958 | * `14` **Snapshot Failure**: Node.js was started to build a V8 startup | |
| 3959 | 3959 | snapshot and it failed because certain requirements of the state of | |
| 3960 | 3960 | the application were not met. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,7 +32,7 @@ const { | |||
| 32 | 32 | ERR_METHOD_NOT_IMPLEMENTED, | |
| 33 | 33 | ERR_WORKER_UNSERIALIZABLE_ERROR, | |
| 34 | 34 | } = require('internal/errors').codes; | |
| 35 | - const { exitCodes: { kUnfinishedTopLevelAwait } } = internalBinding('errors'); | ||
| 35 | + const { exitCodes: { kUnsettledTopLevelAwait } } = internalBinding('errors'); | ||
| 36 | 36 | const { URL } = require('internal/url'); | |
| 37 | 37 | const { canParse: URLCanParse } = internalBinding('url'); | |
| 38 | 38 | const { receiveMessageOnPort } = require('worker_threads'); | |
@@ -615,7 +615,7 @@ class HooksProxy { | |||
| 615 | 615 | } while (response == null); | |
| 616 | 616 | debug('got sync response from worker', { method, args }); | |
| 617 | 617 | if (response.message.status === 'never-settle') { | |
| 618 | - process.exit(kUnfinishedTopLevelAwait); | ||
| 618 | + process.exit(kUnsettledTopLevelAwait); | ||
| 619 | 619 | } else if (response.message.status === 'exit') { | |
| 620 | 620 | process.exit(response.message.body); | |
| 621 | 621 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -182,7 +182,7 @@ class ModuleLoader { | |||
| 182 | 182 | } | |
| 183 | 183 | } | |
| 184 | 184 | ||
| 185 | - async eval(source, url) { | ||
| 185 | + async eval(source, url, isEntryPoint = false) { | ||
| 186 | 186 | const evalInstance = (url) => { | |
| 187 | 187 | const { ModuleWrap } = internalBinding('module_wrap'); | |
| 188 | 188 | const { registerModule } = require('internal/modules/esm/utils'); | |
@@ -201,7 +201,7 @@ class ModuleLoader { | |||
| 201 | 201 | const job = new ModuleJob( | |
| 202 | 202 | this, url, undefined, evalInstance, false, false); | |
| 203 | 203 | this.loadCache.set(url, undefined, job); | |
| 204 | - const { module } = await job.run(); | ||
| 204 | + const { module } = await job.run(isEntryPoint); | ||
| 205 | 205 | ||
| 206 | 206 | return { | |
| 207 | 207 | __proto__: null, | |
@@ -311,9 +311,9 @@ class ModuleLoader { | |||
| 311 | 311 | * module import. | |
| 312 | 312 | * @returns {Promise<ModuleExports>} | |
| 313 | 313 | */ | |
| 314 | - async import(specifier, parentURL, importAttributes) { | ||
| 314 | + async import(specifier, parentURL, importAttributes, isEntryPoint = false) { | ||
| 315 | 315 | const moduleJob = await this.getModuleJob(specifier, parentURL, importAttributes); | |
| 316 | - const { module } = await moduleJob.run(); | ||
| 316 | + const { module } = await moduleJob.run(isEntryPoint); | ||
| 317 | 317 | return module.getNamespace(); | |
| 318 | 318 | } | |
| 319 | 319 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,10 +17,15 @@ const { | |||
| 17 | 17 | StringPrototypeIncludes, | |
| 18 | 18 | StringPrototypeSplit, | |
| 19 | 19 | StringPrototypeStartsWith, | |
| 20 | + globalThis, | ||
| 20 | 21 | } = primordials; | |
| 21 | 22 | ||
| 22 | 23 | const { ModuleWrap } = internalBinding('module_wrap'); | |
| 23 | - | ||
| 24 | + const { | ||
| 25 | + privateSymbols: { | ||
| 26 | + entry_point_module_private_symbol, | ||
| 27 | + }, | ||
| 28 | + } = internalBinding('util'); | ||
| 24 | 29 | const { decorateErrorStack, kEmptyObject } = require('internal/util'); | |
| 25 | 30 | const { | |
| 26 | 31 | getSourceMapsEnabled, | |
@@ -213,8 +218,11 @@ class ModuleJob { | |||
| 213 | 218 | return { __proto__: null, module: this.module }; | |
| 214 | 219 | } | |
| 215 | 220 | ||
| 216 | - async run() { | ||
| 221 | + async run(isEntryPoint = false) { | ||
| 217 | 222 | await this.instantiate(); | |
| 223 | + if (isEntryPoint) { | ||
| 224 | + globalThis[entry_point_module_private_symbol] = this.module; | ||
| 225 | + } | ||
| 218 | 226 | const timeout = -1; | |
| 219 | 227 | const breakOnSigint = false; | |
| 220 | 228 | setHasStartedUserESMExecution(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | 4 | StringPrototypeEndsWith, | |
| 5 | + globalThis, | ||
| 5 | 6 | } = primordials; | |
| 6 | 7 | ||
| 7 | 8 | const { containsModuleSyntax } = internalBinding('contextify'); | |
@@ -16,9 +17,12 @@ const { | |||
| 16 | 17 | } = require('internal/process/execution'); | |
| 17 | 18 | const { | |
| 18 | 19 | triggerUncaughtException, | |
| 19 | - exitCodes: { kUnfinishedTopLevelAwait }, | ||
| 20 | 20 | } = internalBinding('errors'); | |
| 21 | - | ||
| 21 | + const { | ||
| 22 | + privateSymbols: { | ||
| 23 | + entry_point_promise_private_symbol, | ||
| 24 | + }, | ||
| 25 | + } = internalBinding('util'); | ||
| 22 | 26 | /** | |
| 23 | 27 | * Get the absolute path to the main entry point. | |
| 24 | 28 | * @param {string} main - Entry point path | |
@@ -102,20 +106,10 @@ function shouldUseESMLoader(mainPath) { | |||
| 102 | 106 | return type === 'module'; | |
| 103 | 107 | } | |
| 104 | 108 | ||
| 105 | - /** | ||
| 106 | - * Handle a Promise from running code that potentially does Top-Level Await. | ||
| 107 | - * In that case, it makes sense to set the exit code to a specific non-zero value | ||
| 108 | - * if the main code never finishes running. | ||
| 109 | - */ | ||
| 110 | - function handleProcessExit() { | ||
| 111 | - process.exitCode ??= kUnfinishedTopLevelAwait; | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | 109 | /** | |
| 115 | 110 | * @param {function(ModuleLoader):ModuleWrap|undefined} callback | |
| 116 | 111 | */ | |
| 117 | 112 | async function asyncRunEntryPointWithESMLoader(callback) { | |
| 118 | - process.on('exit', handleProcessExit); | ||
| 119 | 113 | const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); | |
| 120 | 114 | try { | |
| 121 | 115 | const userImports = getOptionValue('--import'); | |
@@ -137,8 +131,6 @@ async function asyncRunEntryPointWithESMLoader(callback) { | |||
| 137 | 131 | err, | |
| 138 | 132 | true, /* fromPromise */ | |
| 139 | 133 | ); | |
| 140 | - } finally { | ||
| 141 | - process.off('exit', handleProcessExit); | ||
| 142 | 134 | } | |
| 143 | 135 | } | |
| 144 | 136 | ||
@@ -152,6 +144,10 @@ async function asyncRunEntryPointWithESMLoader(callback) { | |||
| 152 | 144 | */ | |
| 153 | 145 | function runEntryPointWithESMLoader(callback) { | |
| 154 | 146 | const promise = asyncRunEntryPointWithESMLoader(callback); | |
| 147 | + // Register the promise - if by the time the event loop finishes running, this is | ||
| 148 | + // still unsettled, we'll search the graph from the entry point module and print | ||
| 149 | + // the location of any unsettled top-level await found. | ||
| 150 | + globalThis[entry_point_promise_private_symbol] = promise; | ||
| 155 | 151 | return promise; | |
| 156 | 152 | } | |
| 157 | 153 | ||
@@ -171,7 +167,7 @@ function executeUserEntryPoint(main = process.argv[1]) { | |||
| 171 | 167 | const mainURL = pathToFileURL(mainPath).href; | |
| 172 | 168 | ||
| 173 | 169 | runEntryPointWithESMLoader((cascadedLoader) => { | |
| 174 | - // Note that if the graph contains unfinished TLA, this may never resolve | ||
| 170 | + // Note that if the graph contains unsettled TLA, this may never resolve | ||
| 175 | 171 | // even after the event loop stops running. | |
| 176 | 172 | return cascadedLoader.import(mainURL, undefined, { __proto__: null }, true); | |
| 177 | 173 | }); | |
@@ -185,5 +181,4 @@ function executeUserEntryPoint(main = process.argv[1]) { | |||
| 185 | 181 | module.exports = { | |
| 186 | 182 | executeUserEntryPoint, | |
| 187 | 183 | runEntryPointWithESMLoader, | |
| 188 | - handleProcessExit, | ||
| 189 | 184 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -173,9 +173,6 @@ function wrapProcessMethods(binding) { | |||
| 173 | 173 | memoryUsage.rss = rss; | |
| 174 | 174 | ||
| 175 | 175 | function exit(code) { | |
| 176 | - const { handleProcessExit } = require('internal/modules/run_main'); | ||
| 177 | - process.off('exit', handleProcessExit); | ||
| 178 | - | ||
| 179 | 176 | if (arguments.length !== 0) { | |
| 180 | 177 | process.exitCode = code; | |
| 181 | 178 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,7 +73,20 @@ Maybe<ExitCode> SpinEventLoopInternal(Environment* env) { | |||
| 73 | 73 | ||
| 74 | 74 | env->PrintInfoForSnapshotIfDebug(); | |
| 75 | 75 | env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); }); | |
| 76 | - return EmitProcessExitInternal(env); | ||
| 76 | + Maybe<ExitCode> exit_code = EmitProcessExitInternal(env); | ||
| 77 | + if (exit_code.FromMaybe(ExitCode::kGenericUserError) != | ||
| 78 | + ExitCode::kNoFailure) { | ||
| 79 | + return exit_code; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + auto unsettled_tla = env->CheckUnsettledTopLevelAwait(); | ||
| 83 | + if (unsettled_tla.IsNothing()) { | ||
| 84 | + return Nothing<ExitCode>(); | ||
| 85 | + } | ||
| 86 | + if (!unsettled_tla.FromJust()) { | ||
| 87 | + return Just(ExitCode::kUnsettledTopLevelAwait); | ||
| 88 | + } | ||
| 89 | + return Just(ExitCode::kNoFailure); | ||
| 77 | 90 | } | |
| 78 | 91 | ||
| 79 | 92 | struct CommonEnvironmentSetup::Impl { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -371,6 +371,10 @@ inline void Environment::set_exiting(bool value) { | |||
| 371 | 371 | exit_info_[kExiting] = value ? 1 : 0; | |
| 372 | 372 | } | |
| 373 | 373 | ||
| 374 | + inline bool Environment::exiting() const { | ||
| 375 | + return exit_info_[kExiting] == 1; | ||
| 376 | + } | ||
| 377 | + | ||
| 374 | 378 | inline ExitCode Environment::exit_code(const ExitCode default_code) const { | |
| 375 | 379 | return exit_info_[kHasExitCode] == 0 | |
| 376 | 380 | ? default_code | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | #include "debug_utils-inl.h" | |
| 5 | 5 | #include "diagnosticfilename-inl.h" | |
| 6 | 6 | #include "memory_tracker-inl.h" | |
| 7 | + #include "module_wrap.h" | ||
| 7 | 8 | #include "node_buffer.h" | |
| 8 | 9 | #include "node_context_data.h" | |
| 9 | 10 | #include "node_contextify.h" | |
@@ -50,6 +51,7 @@ using v8::HeapSpaceStatistics; | |||
| 50 | 51 | using v8::Integer; | |
| 51 | 52 | using v8::Isolate; | |
| 52 | 53 | using v8::Local; | |
| 54 | + using v8::Maybe; | ||
| 53 | 55 | using v8::MaybeLocal; | |
| 54 | 56 | using v8::NewStringType; | |
| 55 | 57 | using v8::Number; | |
@@ -1228,6 +1230,41 @@ void Environment::AtExit(void (*cb)(void* arg), void* arg) { | |||
| 1228 | 1230 | at_exit_functions_.push_front(ExitCallback{cb, arg}); | |
| 1229 | 1231 | } | |
| 1230 | 1232 | ||
| 1233 | + Maybe<bool> Environment::CheckUnsettledTopLevelAwait() { | ||
| 1234 | + HandleScope scope(isolate_); | ||
| 1235 | + Local<Context> ctx = context(); | ||
| 1236 | + Local<Value> value; | ||
| 1237 | + | ||
| 1238 | + Local<Value> entry_point_promise; | ||
| 1239 | + if (!ctx->Global() | ||
| 1240 | + ->GetPrivate(ctx, entry_point_promise_private_symbol()) | ||
| 1241 | + .ToLocal(&entry_point_promise)) { | ||
| 1242 | + return v8::Nothing<bool>(); | ||
| 1243 | + } | ||
| 1244 | + if (!entry_point_promise->IsPromise()) { | ||
| 1245 | + return v8::Just(true); | ||
| 1246 | + } | ||
| 1247 | + if (entry_point_promise.As<Promise>()->State() != | ||
| 1248 | + Promise::PromiseState::kPending) { | ||
| 1249 | + return v8::Just(true); | ||
| 1250 | + } | ||
| 1251 | + | ||
| 1252 | + if (!ctx->Global() | ||
| 1253 | + ->GetPrivate(ctx, entry_point_module_private_symbol()) | ||
| 1254 | + .ToLocal(&value)) { | ||
| 1255 | + return v8::Nothing<bool>(); | ||
| 1256 | + } | ||
| 1257 | + if (!value->IsObject()) { | ||
| 1258 | + return v8::Just(true); | ||
| 1259 | + } | ||
| 1260 | + Local<Object> object = value.As<Object>(); | ||
| 1261 | + CHECK(BaseObject::IsBaseObject(isolate_data_, object)); | ||
| 1262 | + CHECK_EQ(object->InternalFieldCount(), | ||
| 1263 | + loader::ModuleWrap::kInternalFieldCount); | ||
| 1264 | + auto* wrap = BaseObject::FromJSObject<loader::ModuleWrap>(object); | ||
| 1265 | + return wrap->CheckUnsettledTopLevelAwait(); | ||
| 1266 | + } | ||
| 1267 | + | ||
| 1231 | 1268 | void Environment::RunAndClearInterrupts() { | |
| 1232 | 1269 | while (native_immediates_interrupts_.size() > 0) { | |
| 1233 | 1270 | NativeImmediateQueue queue; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -735,6 +735,7 @@ class Environment : public MemoryRetainer { | |||
| 735 | 735 | // a pseudo-boolean to indicate whether the exit code is undefined. | |
| 736 | 736 | inline AliasedInt32Array& exit_info(); | |
| 737 | 737 | inline void set_exiting(bool value); | |
| 738 | + bool exiting() const; | ||
| 738 | 739 | inline ExitCode exit_code(const ExitCode default_code) const; | |
| 739 | 740 | ||
| 740 | 741 | // This stores whether the --abort-on-uncaught-exception flag was passed | |
@@ -840,6 +841,7 @@ class Environment : public MemoryRetainer { | |||
| 840 | 841 | void AtExit(void (*cb)(void* arg), void* arg); | |
| 841 | 842 | void RunAtExitCallbacks(); | |
| 842 | 843 | ||
| 844 | + v8::Maybe<bool> CheckUnsettledTopLevelAwait(); | ||
| 843 | 845 | void RunWeakRefCleanup(); | |
| 844 | 846 | ||
| 845 | 847 | v8::MaybeLocal<v8::Value> RunSnapshotSerializeCallback() const; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments