Skip to content

Commit a8ef8b8

Browse files
committed
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.
1 parent b1898c5 commit a8ef8b8

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;
@@ -395,6 +396,13 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
395396
return;
396397
}
397398

399+
if (that->Set(context,
400+
realm->env()->has_top_level_await_string(),
401+
Boolean::New(isolate, module->HasTopLevelAwait()))
402+
.IsNothing()) {
403+
return;
404+
}
405+
398406
if (that->Set(context,
399407
realm->env()->source_url_string(),
400408
module->GetUnboundModuleScript()->GetSourceURL())
@@ -948,27 +956,6 @@ void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
948956
args.GetReturnValue().Set(module->IsGraphAsync());
949957
}
950958

951-
void ModuleWrap::HasTopLevelAwait(const FunctionCallbackInfo<Value>& args) {
952-
Isolate* isolate = args.GetIsolate();
953-
ModuleWrap* obj;
954-
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
955-
956-
Local<Module> module = obj->module_.Get(isolate);
957-
958-
// Check if module is valid
959-
if (module.IsEmpty()) {
960-
args.GetReturnValue().Set(false);
961-
return;
962-
}
963-
964-
// For source text modules, check if the graph is async
965-
// For synthetic modules, it's always false
966-
bool has_top_level_await =
967-
module->IsSourceTextModule() && module->IsGraphAsync();
968-
969-
args.GetReturnValue().Set(has_top_level_await);
970-
}
971-
972959
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
973960
Isolate* isolate = args.GetIsolate();
974961
ModuleWrap* obj;
@@ -1392,8 +1379,6 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
13921379
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
13931380
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
13941381
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
1395-
SetProtoMethodNoSideEffect(
1396-
isolate, tpl, "hasTopLevelAwait", HasTopLevelAwait);
13971382
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
13981383
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
13991384
isolate_data->set_module_wrap_constructor_template(tpl);
@@ -1456,7 +1441,6 @@ void ModuleWrap::RegisterExternalReferences(
14561441
registry->Register(GetStatus);
14571442
registry->Register(GetError);
14581443
registry->Register(IsGraphAsync);
1459-
registry->Register(HasTopLevelAwait);
14601444

14611445
registry->Register(CreateRequiredModuleFacade);
14621446

src/module_wrap.h

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

119-
static void HasTopLevelAwait(const v8::FunctionCallbackInfo<v8::Value>& args);
120-
121119
v8::Local<v8::Context> context() const;
122120
v8::Maybe<bool> CheckUnsettledTopLevelAwait();
123121

0 commit comments

Comments
 (0)