Skip to content

Commit fb22c7f

Browse files
authored
src: separate module.hasAsyncGraph and module.hasTopLevelAwait
Clarify the names - hasAsyncGraph means either the module or its dependencies contains top-level await; hasTopLevelAwait means the module itself contains top-level await. Theoratically the former can be inferred from iterating over the dependencies but for the built-in loader it's currently not fully reliable until we eliminate async linking. Also remove the hasTopLevelAwait method - we can simply put it on the module wrap for source text modules right after compilation. PR-URL: #59675 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 45a8b55 commit fb22c7f

File tree

5 files changed

+18
-34
lines changed

5 files changed

+18
-34
lines changed

lib/internal/modules/esm/loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ class ModuleLoader {
387387
if (!job.module) {
388388
assert.fail(getRaceMessage(filename, parentFilename));
389389
}
390-
if (job.module.async) {
390+
if (job.module.hasAsyncGraph) {
391391
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
392392
}
393393
const status = job.module.getStatus();

lib/internal/modules/esm/module_job.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,13 +327,13 @@ class ModuleJob extends ModuleJobBase {
327327
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
328328
// fully synchronous instead.
329329
if (status === kUninstantiated) {
330-
this.module.async = this.module.instantiateSync();
330+
this.module.hasAsyncGraph = this.module.instantiateSync();
331331
status = this.module.getStatus();
332332
}
333333
if (status === kInstantiated || status === kErrored) {
334334
const filename = urlToFilename(this.url);
335335
const parentFilename = urlToFilename(parent?.filename);
336-
this.module.async ??= this.module.isGraphAsync();
336+
this.module.hasAsyncGraph ??= this.module.isGraphAsync();
337337

338338
if (this.module.async && !getOptionValue('--experimental-print-required-tla')) {
339339
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
@@ -370,7 +370,7 @@ class ModuleJob extends ModuleJobBase {
370370
try {
371371
await this.module.evaluate(timeout, breakOnSigint);
372372
} catch (e) {
373-
explainCommonJSGlobalLikeNotDefinedError(e, this.module.url, this.module.hasTopLevelAwait());
373+
explainCommonJSGlobalLikeNotDefinedError(e, this.module.url, this.module.hasTopLevelAwait);
374374
throw e;
375375
}
376376
return { __proto__: null, module: this.module };
@@ -490,24 +490,25 @@ class ModuleJobSync extends ModuleJobBase {
490490
debug('ModuleJobSync.runSync()', this.module);
491491
assert(this.phase === kEvaluationPhase);
492492
// TODO(joyeecheung): add the error decoration logic from the async instantiate.
493-
this.module.async = this.module.instantiateSync();
493+
this.module.hasAsyncGraph = this.module.instantiateSync();
494494
// If --experimental-print-required-tla is true, proceeds to evaluation even
495495
// if it's async because we want to search for the TLA and help users locate
496496
// them.
497497
// TODO(joyeecheung): track the asynchroniticy using v8::Module::HasTopLevelAwait()
498498
// and we'll be able to throw right after compilation of the modules, using acron
499-
// to find and print the TLA.
499+
// to find and print the TLA. This requires the linking to be synchronous in case
500+
// it runs into cached asynchronous modules that are not yet fetched.
500501
const parentFilename = urlToFilename(parent?.filename);
501502
const filename = urlToFilename(this.url);
502-
if (this.module.async && !getOptionValue('--experimental-print-required-tla')) {
503+
if (this.module.hasAsyncGraph && !getOptionValue('--experimental-print-required-tla')) {
503504
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
504505
}
505506
setHasStartedUserESMExecution();
506507
try {
507508
const namespace = this.module.evaluateSync(filename, parentFilename);
508509
return { __proto__: null, module: this.module, namespace };
509510
} catch (e) {
510-
explainCommonJSGlobalLikeNotDefinedError(e, this.module.url, this.module.hasTopLevelAwait());
511+
explainCommonJSGlobalLikeNotDefinedError(e, this.module.url, this.module.hasTopLevelAwait);
511512
throw e;
512513
}
513514
}

src/env_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
V(gid_string, "gid") \
209209
V(groups_string, "groups") \
210210
V(has_regexp_groups_string, "hasRegExpGroups") \
211+
V(has_top_level_await_string, "hasTopLevelAwait") \
211212
V(hash_string, "hash") \
212213
V(h2_string, "h2") \
213214
V(handle_string, "handle") \

src/module_wrap.cc

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ using errors::TryCatchScope;
2222
using node::contextify::ContextifyContext;
2323
using v8::Array;
2424
using v8::ArrayBufferView;
25+
using v8::Boolean;
2526
using v8::Context;
2627
using v8::Data;
2728
using v8::EscapableHandleScope;
@@ -414,6 +415,13 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
414415
return;
415416
}
416417

418+
if (that->Set(context,
419+
realm->env()->has_top_level_await_string(),
420+
Boolean::New(isolate, module->HasTopLevelAwait()))
421+
.IsNothing()) {
422+
return;
423+
}
424+
417425
if (that->Set(context,
418426
realm->env()->source_url_string(),
419427
module->GetUnboundModuleScript()->GetSourceURL())
@@ -999,27 +1007,6 @@ void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
9991007
args.GetReturnValue().Set(module->IsGraphAsync());
10001008
}
10011009

1002-
void ModuleWrap::HasTopLevelAwait(const FunctionCallbackInfo<Value>& args) {
1003-
Isolate* isolate = args.GetIsolate();
1004-
ModuleWrap* obj;
1005-
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
1006-
1007-
Local<Module> module = obj->module_.Get(isolate);
1008-
1009-
// Check if module is valid
1010-
if (module.IsEmpty()) {
1011-
args.GetReturnValue().Set(false);
1012-
return;
1013-
}
1014-
1015-
// For source text modules, check if the graph is async
1016-
// For synthetic modules, it's always false
1017-
bool has_top_level_await =
1018-
module->IsSourceTextModule() && module->IsGraphAsync();
1019-
1020-
args.GetReturnValue().Set(has_top_level_await);
1021-
}
1022-
10231010
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
10241011
Isolate* isolate = args.GetIsolate();
10251012
ModuleWrap* obj;
@@ -1443,8 +1430,6 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
14431430
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
14441431
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
14451432
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
1446-
SetProtoMethodNoSideEffect(
1447-
isolate, tpl, "hasTopLevelAwait", HasTopLevelAwait);
14481433
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
14491434
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
14501435
isolate_data->set_module_wrap_constructor_template(tpl);
@@ -1507,7 +1492,6 @@ void ModuleWrap::RegisterExternalReferences(
15071492
registry->Register(GetStatus);
15081493
registry->Register(GetError);
15091494
registry->Register(IsGraphAsync);
1510-
registry->Register(HasTopLevelAwait);
15111495

15121496
registry->Register(CreateRequiredModuleFacade);
15131497

src/module_wrap.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ class ModuleWrap : public BaseObject {
119119
v8::Local<v8::Module> module,
120120
v8::Local<v8::Object> meta);
121121

122-
static void HasTopLevelAwait(const v8::FunctionCallbackInfo<v8::Value>& args);
123-
124122
v8::Local<v8::Context> context() const;
125123
v8::Maybe<bool> CheckUnsettledTopLevelAwait();
126124

0 commit comments

Comments
 (0)