Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/internal/bootstrap/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,6 @@ class BuiltinModule {
this.setExport('default', builtin.exports);
});
// Ensure immediate sync execution to capture exports now
this.module.link([]);
this.module.instantiate();
this.module.evaluate(-1, false);
return this.module;
Expand Down
9 changes: 5 additions & 4 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,14 @@ class ModuleLoader {
if (!job.module) {
assert.fail(getRaceMessage(filename, parentFilename));
}
if (job.module.hasAsyncGraph) {
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
}
const status = job.module.getStatus();
debug('Module status', job, status);
// hasAsyncGraph is available after module been instantiated.
if (status >= kInstantiated && job.module.hasAsyncGraph) {
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
}
if (status === kEvaluated) {
return { wrap: job.module, namespace: job.module.getNamespaceSync(filename, parentFilename) };
return { wrap: job.module, namespace: job.module.getNamespace() };
} else if (status === kInstantiated) {
// When it's an async job cached by another import request,
// which has finished linking but has not started its
Expand Down
25 changes: 17 additions & 8 deletions lib/internal/modules/esm/module_job.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,17 +324,21 @@ class ModuleJob extends ModuleJobBase {
let status = this.module.getStatus();

debug('ModuleJob.runSync()', status, this.module);
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
// fully synchronous instead.
if (status === kUninstantiated) {
this.module.hasAsyncGraph = this.module.instantiateSync();
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
// fully synchronous instead.
if (this.module.getModuleRequests().length > 0) {
const filename = urlToFilename(this.url);
const parentFilename = urlToFilename(parent?.filename);
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
}
this.module.link([]);
this.module.instantiate();
status = this.module.getStatus();
}
if (status === kInstantiated || status === kErrored) {
const filename = urlToFilename(this.url);
const parentFilename = urlToFilename(parent?.filename);
this.module.hasAsyncGraph ??= this.module.isGraphAsync();

if (this.module.hasAsyncGraph && !getOptionValue('--experimental-print-required-tla')) {
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
}
Expand All @@ -345,14 +349,19 @@ class ModuleJob extends ModuleJobBase {
}
throw this.module.getError();
} else if (status === kEvaluating || status === kEvaluated) {
if (this.module.hasAsyncGraph) {
const filename = urlToFilename(this.url);
const parentFilename = urlToFilename(parent?.filename);
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
}
// kEvaluating can show up when this is being used to deal with CJS <-> CJS cycles.
// Allow it for now, since we only need to ban ESM <-> CJS cycles which would be
// detected earlier during the linking phase, though the CJS handling in the ESM
// loader won't be able to emit warnings on pending circular exports like what
// the CJS loader does.
// TODO(joyeecheung): remove the re-invented require() in the ESM loader and
// always handle CJS using the CJS loader to eliminate the quirks.
return { __proto__: null, module: this.module, namespace: this.module.getNamespaceSync() };
return { __proto__: null, module: this.module, namespace: this.module.getNamespace() };
}
assert.fail(`Unexpected module status ${status}.`);
}
Expand Down Expand Up @@ -472,7 +481,7 @@ class ModuleJobSync extends ModuleJobBase {
}
return { __proto__: null, module: this.module };
} else if (status === kInstantiated) {
// The evaluation may have been canceled because instantiateSync() detected TLA first.
// The evaluation may have been canceled because instantiate() detected TLA first.
// But when it is imported again, it's fine to re-evaluate it asynchronously.
const timeout = -1;
const breakOnSigint = false;
Expand All @@ -490,7 +499,7 @@ class ModuleJobSync extends ModuleJobBase {
debug('ModuleJobSync.runSync()', this.module);
assert(this.phase === kEvaluationPhase);
// TODO(joyeecheung): add the error decoration logic from the async instantiate.
this.module.hasAsyncGraph = this.module.instantiateSync();
this.module.instantiate();
// If --experimental-print-required-tla is true, proceeds to evaluation even
// if it's async because we want to search for the TLA and help users locate
// them.
Expand Down
1 change: 0 additions & 1 deletion lib/internal/vm/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ class SyntheticModule extends Module {
identifier,
});
// A synthetic module does not have dependencies.
this[kWrap].link([]);
this[kWrap].instantiate();
}

Expand Down
146 changes: 49 additions & 97 deletions src/module_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ using v8::ObjectTemplate;
using v8::PrimitiveArray;
using v8::Promise;
using v8::PromiseRejectEvent;
using v8::PropertyCallbackInfo;
using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::String;
Expand Down Expand Up @@ -158,6 +159,8 @@ ModuleWrap::ModuleWrap(Realm* realm,

if (!synthetic_evaluation_step->IsUndefined()) {
synthetic_ = true;
// Synthetic modules have no dependencies.
linked_ = true;
}
MakeWeak();
module_.SetWeak();
Expand Down Expand Up @@ -240,7 +243,7 @@ Maybe<bool> ModuleWrap::CheckUnsettledTopLevelAwait() {
return Just(true);
}

if (!module->IsGraphAsync()) { // There is no TLA, no need to check.
if (!HasAsyncGraph()) { // There is no TLA, no need to check.
return Just(true);
}

Expand All @@ -263,6 +266,16 @@ Maybe<bool> ModuleWrap::CheckUnsettledTopLevelAwait() {
return Just(false);
}

bool ModuleWrap::HasAsyncGraph() {
if (!has_async_graph_.has_value()) {
Isolate* isolate = env()->isolate();
HandleScope scope(isolate);

has_async_graph_ = module_.Get(isolate)->IsGraphAsync();
}
return has_async_graph_.value();
}

Local<PrimitiveArray> ModuleWrap::GetHostDefinedOptions(
Isolate* isolate, Local<Symbol> id_symbol) {
Local<PrimitiveArray> host_defined_options =
Expand Down Expand Up @@ -680,25 +693,28 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Context> context = obj->context();
Local<Module> module = obj->module_.Get(isolate);
Environment* env = realm->env();

if (!obj->IsLinked()) {
THROW_ERR_VM_MODULE_LINK_FAILURE(realm->env(), "module is not linked");
THROW_ERR_VM_MODULE_LINK_FAILURE(env, "module is not linked");
return;
}

TryCatchScope try_catch(realm->env());
USE(module->InstantiateModule(
context, ResolveModuleCallback, ResolveSourceCallback));
{
TryCatchScope try_catch(env);
USE(module->InstantiateModule(
context, ResolveModuleCallback, ResolveSourceCallback));

if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(realm->env(),
try_catch.Exception(),
try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow();
return;
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(env,
try_catch.Exception(),
try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow();
return;
}
}
}

Expand Down Expand Up @@ -783,37 +799,6 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
}
}

void ModuleWrap::InstantiateSync(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate();
ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Context> context = obj->context();
Local<Module> module = obj->module_.Get(isolate);
Environment* env = realm->env();

{
TryCatchScope try_catch(env);
USE(module->InstantiateModule(
context, ResolveModuleCallback, ResolveSourceCallback));

if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(env,
try_catch.Exception(),
try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow();
return;
}
}

// TODO(joyeecheung): record Module::HasTopLevelAwait() in every ModuleWrap
// and infer the asynchronicity from a module's children during linking.
args.GetReturnValue().Set(module->IsGraphAsync());
}

Maybe<void> ThrowIfPromiseRejected(Realm* realm, Local<Promise> promise) {
Isolate* isolate = realm->isolate();
Local<Context> context = realm->context();
Expand Down Expand Up @@ -879,7 +864,7 @@ void ModuleWrap::EvaluateSync(const FunctionCallbackInfo<Value>& args) {
return;
}

if (module->IsGraphAsync()) {
if (obj->HasAsyncGraph()) {
CHECK(env->options()->print_required_tla);
auto stalled_messages =
std::get<1>(module->GetStalledTopLevelAwaitMessages(isolate));
Expand All @@ -901,52 +886,15 @@ void ModuleWrap::EvaluateSync(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(module->GetModuleNamespace());
}

void ModuleWrap::GetNamespaceSync(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate();
ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Module> module = obj->module_.Get(isolate);

switch (module->GetStatus()) {
case Module::Status::kUninstantiated:
case Module::Status::kInstantiating:
return realm->env()->ThrowError(
"Cannot get namespace, module has not been instantiated");
case Module::Status::kInstantiated:
case Module::Status::kEvaluating:
case Module::Status::kEvaluated:
case Module::Status::kErrored:
break;
}

if (module->IsGraphAsync()) {
return THROW_ERR_REQUIRE_ASYNC_MODULE(realm->env(), args[0], args[1]);
}
Local<Value> result = module->GetModuleNamespace();
args.GetReturnValue().Set(result);
}

void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate();
ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

Local<Module> module = obj->module_.Get(isolate);

switch (module->GetStatus()) {
case Module::Status::kUninstantiated:
case Module::Status::kInstantiating:
return realm->env()->ThrowError(
"cannot get namespace, module has not been instantiated");
case Module::Status::kInstantiated:
case Module::Status::kEvaluating:
case Module::Status::kEvaluated:
case Module::Status::kErrored:
break;
default:
UNREACHABLE();
if (module->GetStatus() < Module::kInstantiated) {
return THROW_ERR_MODULE_NOT_INSTANTIATED(realm->env());
}

Local<Value> result = module->GetModuleNamespace();
Expand Down Expand Up @@ -997,23 +945,28 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(module->GetStatus());
}

void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

Local<Module> module = obj->module_.Get(isolate);

args.GetReturnValue().Set(module->IsGraphAsync());
args.GetReturnValue().Set(module->GetException());
}

void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
void ModuleWrap::HasAsyncGraph(Local<Name> property,
const PropertyCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Environment* env = Environment::GetCurrent(isolate);
ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

Local<Module> module = obj->module_.Get(isolate);
args.GetReturnValue().Set(module->GetException());
if (module->GetStatus() < Module::kInstantiated) {
return THROW_ERR_MODULE_NOT_INSTANTIATED(env);
}

args.GetReturnValue().Set(obj->HasAsyncGraph());
}

// static
Expand Down Expand Up @@ -1417,10 +1370,8 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,

SetProtoMethod(isolate, tpl, "link", Link);
SetProtoMethod(isolate, tpl, "getModuleRequests", GetModuleRequests);
SetProtoMethod(isolate, tpl, "instantiateSync", InstantiateSync);
SetProtoMethod(isolate, tpl, "evaluateSync", EvaluateSync);
SetProtoMethod(isolate, tpl, "getNamespaceSync", GetNamespaceSync);
SetProtoMethod(isolate, tpl, "instantiate", Instantiate);
SetProtoMethod(isolate, tpl, "evaluateSync", EvaluateSync);
SetProtoMethod(isolate, tpl, "evaluate", Evaluate);
SetProtoMethod(isolate, tpl, "setExport", SetSyntheticExport);
SetProtoMethod(isolate, tpl, "setModuleSourceObject", SetModuleSourceObject);
Expand All @@ -1429,9 +1380,12 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
isolate, tpl, "createCachedData", CreateCachedData);
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);

tpl->InstanceTemplate()->SetLazyDataProperty(
FIXED_ONE_BYTE_STRING(isolate, "hasAsyncGraph"), HasAsyncGraph);

isolate_data->set_module_wrap_constructor_template(tpl);

SetMethod(isolate,
Expand Down Expand Up @@ -1479,9 +1433,7 @@ void ModuleWrap::RegisterExternalReferences(

registry->Register(Link);
registry->Register(GetModuleRequests);
registry->Register(InstantiateSync);
registry->Register(EvaluateSync);
registry->Register(GetNamespaceSync);
registry->Register(Instantiate);
registry->Register(Evaluate);
registry->Register(SetSyntheticExport);
Expand All @@ -1491,7 +1443,7 @@ void ModuleWrap::RegisterExternalReferences(
registry->Register(GetNamespace);
registry->Register(GetStatus);
registry->Register(GetError);
registry->Register(IsGraphAsync);
registry->Register(HasAsyncGraph);

registry->Register(CreateRequiredModuleFacade);

Expand Down
Loading
Loading