mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
lib,src: iterate module requests of a module wrap in JS
Avoid repetitively calling into JS callback from C++ in `ModuleWrap::Link`. This removes the convoluted callback style of the internal `ModuleWrap` link step. PR-URL: https://github.com/nodejs/node/pull/52058 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
1 parent
9a032cf6e2
commit
9b2b6abb62
7 files changed
+211
-241
No files matched your search
@@ -319,7 +319,7 @@ class ModuleLoader {
|
||||
* @param {object} importAttributes import attributes from the import statement.
|
||||
* @returns {ModuleJobBase}
|
||||
*/
|
||||
getModuleWrapForRequire(specifier, parentURL, importAttributes) {
|
||||
getModuleJobForRequire(specifier, parentURL, importAttributes) {
|
||||
assert(getOptionValue('--experimental-require-module'));
|
||||
|
||||
if (canParse(specifier)) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
Array,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSome,
|
||||
FunctionPrototype,
|
||||
ObjectSetPrototypeOf,
|
||||
@@ -87,31 +87,8 @@ class ModuleJob extends ModuleJobBase {
|
||||
this.modulePromise = PromiseResolve(this.modulePromise);
|
||||
}
|
||||
|
||||
// Wait for the ModuleWrap instance being linked with all dependencies.
|
||||
const link = async () => {
|
||||
this.module = await this.modulePromise;
|
||||
assert(this.module instanceof ModuleWrap);
|
||||
|
||||
// Explicitly keeping track of dependency jobs is needed in order
|
||||
// to flatten out the dependency graph below in `_instantiate()`,
|
||||
// so that circular dependencies can't cause a deadlock by two of
|
||||
// these `link` callbacks depending on each other.
|
||||
const dependencyJobs = [];
|
||||
const promises = this.module.link(async (specifier, attributes) => {
|
||||
const job = await this.#loader.getModuleJob(specifier, url, attributes);
|
||||
debug(`async link() ${this.url} -> ${specifier}`, job);
|
||||
ArrayPrototypePush(dependencyJobs, job);
|
||||
return job.modulePromise;
|
||||
});
|
||||
|
||||
if (promises !== undefined) {
|
||||
await SafePromiseAllReturnVoid(promises);
|
||||
}
|
||||
|
||||
return SafePromiseAllReturnArrayLike(dependencyJobs);
|
||||
};
|
||||
// Promise for the list of all dependencyJobs.
|
||||
this.linked = link();
|
||||
this.linked = this._link();
|
||||
// This promise is awaited later anyway, so silence
|
||||
// 'unhandled rejection' warnings.
|
||||
PromisePrototypeThen(this.linked, undefined, noop);
|
||||
@@ -121,6 +98,49 @@ class ModuleJob extends ModuleJobBase {
|
||||
this.instantiated = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates the module requests and links with the loader.
|
||||
* @returns {Promise<ModuleJob[]>} Dependency module jobs.
|
||||
*/
|
||||
async _link() {
|
||||
this.module = await this.modulePromise;
|
||||
assert(this.module instanceof ModuleWrap);
|
||||
|
||||
const moduleRequests = this.module.getModuleRequests();
|
||||
// Explicitly keeping track of dependency jobs is needed in order
|
||||
// to flatten out the dependency graph below in `_instantiate()`,
|
||||
// so that circular dependencies can't cause a deadlock by two of
|
||||
// these `link` callbacks depending on each other.
|
||||
// Create an ArrayLike to avoid calling into userspace with `.then`
|
||||
// when returned from the async function.
|
||||
const dependencyJobs = Array(moduleRequests.length);
|
||||
ObjectSetPrototypeOf(dependencyJobs, null);
|
||||
|
||||
// Specifiers should be aligned with the moduleRequests array in order.
|
||||
const specifiers = Array(moduleRequests.length);
|
||||
const modulePromises = Array(moduleRequests.length);
|
||||
// Iterate with index to avoid calling into userspace with `Symbol.iterator`.
|
||||
for (let idx = 0; idx < moduleRequests.length; idx++) {
|
||||
const { specifier, attributes } = moduleRequests[idx];
|
||||
|
||||
const dependencyJobPromise = this.#loader.getModuleJob(
|
||||
specifier, this.url, attributes,
|
||||
);
|
||||
const modulePromise = PromisePrototypeThen(dependencyJobPromise, (job) => {
|
||||
debug(`async link() ${this.url} -> ${specifier}`, job);
|
||||
dependencyJobs[idx] = job;
|
||||
return job.modulePromise;
|
||||
});
|
||||
modulePromises[idx] = modulePromise;
|
||||
specifiers[idx] = specifier;
|
||||
}
|
||||
|
||||
const modules = await SafePromiseAllReturnArrayLike(modulePromises);
|
||||
this.module.link(specifiers, modules);
|
||||
|
||||
return dependencyJobs;
|
||||
}
|
||||
|
||||
instantiate() {
|
||||
if (this.instantiated === undefined) {
|
||||
this.instantiated = this._instantiate();
|
||||
@@ -277,18 +297,20 @@ class ModuleJobSync extends ModuleJobBase {
|
||||
super(url, importAttributes, moduleWrap, isMain, inspectBrk, true);
|
||||
assert(this.module instanceof ModuleWrap);
|
||||
this.#loader = loader;
|
||||
const moduleRequests = this.module.getModuleRequestsSync();
|
||||
const linked = [];
|
||||
const moduleRequests = this.module.getModuleRequests();
|
||||
// Specifiers should be aligned with the moduleRequests array in order.
|
||||
const specifiers = Array(moduleRequests.length);
|
||||
const modules = Array(moduleRequests.length);
|
||||
const jobs = Array(moduleRequests.length);
|
||||
for (let i = 0; i < moduleRequests.length; ++i) {
|
||||
const { 0: specifier, 1: attributes } = moduleRequests[i];
|
||||
const job = this.#loader.getModuleWrapForRequire(specifier, url, attributes);
|
||||
const isLast = (i === moduleRequests.length - 1);
|
||||
// TODO(joyeecheung): make the resolution callback deal with both promisified
|
||||
// an raw module wraps, then we don't need to wrap it with a promise here.
|
||||
this.module.cacheResolvedWrapsSync(specifier, PromiseResolve(job.module), isLast);
|
||||
ArrayPrototypePush(linked, job);
|
||||
const { specifier, attributes } = moduleRequests[i];
|
||||
const job = this.#loader.getModuleJobForRequire(specifier, url, attributes);
|
||||
specifiers[i] = specifier;
|
||||
modules[i] = job.module;
|
||||
jobs[i] = job;
|
||||
}
|
||||
this.linked = linked;
|
||||
this.module.link(specifiers, modules);
|
||||
this.linked = jobs;
|
||||
}
|
||||
|
||||
get modulePromise() {
|
||||
|
||||
+59
-37
@@ -1,17 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
Array,
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeForEach,
|
||||
ArrayPrototypeIndexOf,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypeSome,
|
||||
ObjectDefineProperty,
|
||||
ObjectFreeze,
|
||||
ObjectGetPrototypeOf,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectSetPrototypeOf,
|
||||
PromiseResolve,
|
||||
PromisePrototypeThen,
|
||||
ReflectApply,
|
||||
SafePromiseAllReturnVoid,
|
||||
SafePromiseAllReturnArrayLike,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
@@ -294,44 +298,62 @@ class SourceTextModule extends Module {
|
||||
importModuleDynamically,
|
||||
});
|
||||
|
||||
this[kLink] = async (linker) => {
|
||||
this.#statusOverride = 'linking';
|
||||
|
||||
const promises = this[kWrap].link(async (identifier, attributes) => {
|
||||
const module = await linker(identifier, this, { attributes, assert: attributes });
|
||||
if (!isModule(module)) {
|
||||
throw new ERR_VM_MODULE_NOT_MODULE();
|
||||
}
|
||||
if (module.context !== this.context) {
|
||||
throw new ERR_VM_MODULE_DIFFERENT_CONTEXT();
|
||||
}
|
||||
if (module.status === 'errored') {
|
||||
throw new ERR_VM_MODULE_LINK_FAILURE(`request for '${identifier}' resolved to an errored module`, module.error);
|
||||
}
|
||||
if (module.status === 'unlinked') {
|
||||
await module[kLink](linker);
|
||||
}
|
||||
return module[kWrap];
|
||||
});
|
||||
|
||||
try {
|
||||
if (promises !== undefined) {
|
||||
await SafePromiseAllReturnVoid(promises);
|
||||
}
|
||||
} catch (e) {
|
||||
this.#error = e;
|
||||
throw e;
|
||||
} finally {
|
||||
this.#statusOverride = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
this[kDependencySpecifiers] = undefined;
|
||||
}
|
||||
|
||||
async [kLink](linker) {
|
||||
this.#statusOverride = 'linking';
|
||||
|
||||
const moduleRequests = this[kWrap].getModuleRequests();
|
||||
// Iterates the module requests and links with the linker.
|
||||
// Specifiers should be aligned with the moduleRequests array in order.
|
||||
const specifiers = Array(moduleRequests.length);
|
||||
const modulePromises = Array(moduleRequests.length);
|
||||
// Iterates with index to avoid calling into userspace with `Symbol.iterator`.
|
||||
for (let idx = 0; idx < moduleRequests.length; idx++) {
|
||||
const { specifier, attributes } = moduleRequests[idx];
|
||||
|
||||
const linkerResult = linker(specifier, this, {
|
||||
attributes,
|
||||
assert: attributes,
|
||||
});
|
||||
const modulePromise = PromisePrototypeThen(
|
||||
PromiseResolve(linkerResult), async (module) => {
|
||||
if (!isModule(module)) {
|
||||
throw new ERR_VM_MODULE_NOT_MODULE();
|
||||
}
|
||||
if (module.context !== this.context) {
|
||||
throw new ERR_VM_MODULE_DIFFERENT_CONTEXT();
|
||||
}
|
||||
if (module.status === 'errored') {
|
||||
throw new ERR_VM_MODULE_LINK_FAILURE(`request for '${specifier}' resolved to an errored module`, module.error);
|
||||
}
|
||||
if (module.status === 'unlinked') {
|
||||
await module[kLink](linker);
|
||||
}
|
||||
return module[kWrap];
|
||||
});
|
||||
modulePromises[idx] = modulePromise;
|
||||
specifiers[idx] = specifier;
|
||||
}
|
||||
|
||||
try {
|
||||
const modules = await SafePromiseAllReturnArrayLike(modulePromises);
|
||||
this[kWrap].link(specifiers, modules);
|
||||
} catch (e) {
|
||||
this.#error = e;
|
||||
throw e;
|
||||
} finally {
|
||||
this.#statusOverride = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
get dependencySpecifiers() {
|
||||
validateInternalField(this, kDependencySpecifiers, 'SourceTextModule');
|
||||
this[kDependencySpecifiers] ??= ObjectFreeze(this[kWrap].getStaticDependencySpecifiers());
|
||||
// TODO(legendecas): add a new getter to expose the import attributes as the value type
|
||||
// of [[RequestedModules]] is changed in https://tc39.es/proposal-import-attributes/#table-cyclic-module-fields.
|
||||
this[kDependencySpecifiers] ??= ObjectFreeze(
|
||||
ArrayPrototypeMap(this[kWrap].getModuleRequests(), (request) => request.specifier));
|
||||
return this[kDependencySpecifiers];
|
||||
}
|
||||
|
||||
@@ -393,10 +415,10 @@ class SyntheticModule extends Module {
|
||||
context,
|
||||
identifier,
|
||||
});
|
||||
}
|
||||
|
||||
this[kLink] = () => this[kWrap].link(() => {
|
||||
assert.fail('link callback should not be called');
|
||||
});
|
||||
[kLink]() {
|
||||
/** nothing to do for synthetic modules */
|
||||
}
|
||||
|
||||
setExport(name, value) {
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
V(args_string, "args") \
|
||||
V(asn1curve_string, "asn1Curve") \
|
||||
V(async_ids_stack_string, "async_ids_stack") \
|
||||
V(attributes_string, "attributes") \
|
||||
V(base_string, "base") \
|
||||
V(bits_string, "bits") \
|
||||
V(block_list_string, "blockList") \
|
||||
@@ -307,6 +308,7 @@
|
||||
V(sni_context_string, "sni_context") \
|
||||
V(source_string, "source") \
|
||||
V(source_map_url_string, "sourceMapURL") \
|
||||
V(specifier_string, "specifier") \
|
||||
V(stack_string, "stack") \
|
||||
V(standard_name_string, "standardName") \
|
||||
V(start_time_string, "startTime") \
|
||||
@@ -384,6 +386,7 @@
|
||||
V(js_transferable_constructor_template, v8::FunctionTemplate) \
|
||||
V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \
|
||||
V(message_port_constructor_template, v8::FunctionTemplate) \
|
||||
V(module_wrap_constructor_template, v8::FunctionTemplate) \
|
||||
V(microtask_queue_ctor_template, v8::FunctionTemplate) \
|
||||
V(pipe_constructor_template, v8::FunctionTemplate) \
|
||||
V(promise_wrap_template, v8::ObjectTemplate) \
|
||||
|
||||
+82
-152
@@ -399,136 +399,105 @@ static Local<Object> createImportAttributesContainer(
|
||||
Local<FixedArray> raw_attributes,
|
||||
const int elements_per_attribute) {
|
||||
CHECK_EQ(raw_attributes->Length() % elements_per_attribute, 0);
|
||||
Local<Object> attributes =
|
||||
Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0);
|
||||
size_t num_attributes = raw_attributes->Length() / elements_per_attribute;
|
||||
std::vector<Local<v8::Name>> names(num_attributes);
|
||||
std::vector<Local<v8::Value>> values(num_attributes);
|
||||
|
||||
for (int i = 0; i < raw_attributes->Length(); i += elements_per_attribute) {
|
||||
attributes
|
||||
->Set(realm->context(),
|
||||
raw_attributes->Get(realm->context(), i).As<String>(),
|
||||
raw_attributes->Get(realm->context(), i + 1).As<Value>())
|
||||
.ToChecked();
|
||||
int idx = i / elements_per_attribute;
|
||||
names[idx] = raw_attributes->Get(realm->context(), i).As<v8::Name>();
|
||||
values[idx] = raw_attributes->Get(realm->context(), i + 1).As<Value>();
|
||||
}
|
||||
|
||||
return attributes;
|
||||
return Object::New(
|
||||
isolate, v8::Null(isolate), names.data(), values.data(), num_attributes);
|
||||
}
|
||||
|
||||
void ModuleWrap::GetModuleRequestsSync(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
static Local<Array> createModuleRequestsContainer(
|
||||
Realm* realm, Isolate* isolate, Local<FixedArray> raw_requests) {
|
||||
std::vector<Local<Value>> requests(raw_requests->Length());
|
||||
|
||||
Local<Object> that = args.This();
|
||||
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
||||
|
||||
CHECK(!obj->linked_);
|
||||
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
Local<FixedArray> module_requests = module->GetModuleRequests();
|
||||
const int module_requests_length = module_requests->Length();
|
||||
|
||||
std::vector<Local<Value>> requests;
|
||||
requests.reserve(module_requests_length);
|
||||
// call the dependency resolve callbacks
|
||||
for (int i = 0; i < module_requests_length; i++) {
|
||||
for (int i = 0; i < raw_requests->Length(); i++) {
|
||||
Local<ModuleRequest> module_request =
|
||||
module_requests->Get(realm->context(), i).As<ModuleRequest>();
|
||||
Local<FixedArray> raw_attributes = module_request->GetImportAssertions();
|
||||
std::vector<Local<Value>> request = {
|
||||
module_request->GetSpecifier(),
|
||||
createImportAttributesContainer(realm, isolate, raw_attributes, 3),
|
||||
};
|
||||
requests.push_back(Array::New(isolate, request.data(), request.size()));
|
||||
}
|
||||
raw_requests->Get(realm->context(), i).As<ModuleRequest>();
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(isolate, requests.data(), requests.size()));
|
||||
}
|
||||
|
||||
void ModuleWrap::CacheResolvedWrapsSync(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
CHECK_EQ(args.Length(), 3);
|
||||
CHECK(args[0]->IsString());
|
||||
CHECK(args[1]->IsPromise());
|
||||
CHECK(args[2]->IsBoolean());
|
||||
|
||||
ModuleWrap* dependent;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&dependent, args.This());
|
||||
|
||||
Utf8Value specifier(isolate, args[0]);
|
||||
dependent->resolve_cache_[specifier.ToString()].Reset(isolate,
|
||||
args[1].As<Promise>());
|
||||
|
||||
if (args[2].As<v8::Boolean>()->Value()) {
|
||||
dependent->linked_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
|
||||
Local<Object> that = args.This();
|
||||
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
||||
|
||||
if (obj->linked_)
|
||||
return;
|
||||
obj->linked_ = true;
|
||||
|
||||
Local<Function> resolver_arg = args[0].As<Function>();
|
||||
|
||||
Local<Context> mod_context = obj->context();
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
|
||||
Local<FixedArray> module_requests = module->GetModuleRequests();
|
||||
const int module_requests_length = module_requests->Length();
|
||||
MaybeStackBuffer<Local<Value>, 16> promises(module_requests_length);
|
||||
|
||||
// call the dependency resolve callbacks
|
||||
for (int i = 0; i < module_requests_length; i++) {
|
||||
Local<ModuleRequest> module_request =
|
||||
module_requests->Get(realm->context(), i).As<ModuleRequest>();
|
||||
Local<String> specifier = module_request->GetSpecifier();
|
||||
Utf8Value specifier_utf8(realm->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
// Contains the import assertions for this request in the form:
|
||||
// [key1, value1, source_offset1, key2, value2, source_offset2, ...].
|
||||
Local<FixedArray> raw_attributes = module_request->GetImportAssertions();
|
||||
Local<Object> attributes =
|
||||
createImportAttributesContainer(realm, isolate, raw_attributes, 3);
|
||||
|
||||
Local<Value> argv[] = {
|
||||
Local<v8::Name> names[] = {
|
||||
realm->isolate_data()->specifier_string(),
|
||||
realm->isolate_data()->attributes_string(),
|
||||
};
|
||||
Local<Value> values[] = {
|
||||
specifier,
|
||||
attributes,
|
||||
};
|
||||
DCHECK_EQ(arraysize(names), arraysize(values));
|
||||
|
||||
MaybeLocal<Value> maybe_resolve_return_value =
|
||||
resolver_arg->Call(mod_context, that, arraysize(argv), argv);
|
||||
if (maybe_resolve_return_value.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
Local<Value> resolve_return_value =
|
||||
maybe_resolve_return_value.ToLocalChecked();
|
||||
if (!resolve_return_value->IsPromise()) {
|
||||
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
||||
realm, "request for '%s' did not return promise", specifier_std);
|
||||
return;
|
||||
}
|
||||
Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
|
||||
obj->resolve_cache_[specifier_std].Reset(isolate, resolve_promise);
|
||||
Local<Object> request = Object::New(
|
||||
isolate, v8::Null(isolate), names, values, arraysize(names));
|
||||
|
||||
promises[i] = resolve_promise;
|
||||
requests[i] = request;
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(isolate, promises.out(), promises.length()));
|
||||
return Array::New(isolate, requests.data(), requests.size());
|
||||
}
|
||||
|
||||
void ModuleWrap::GetModuleRequests(const FunctionCallbackInfo<Value>& args) {
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Object> that = args.This();
|
||||
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, that);
|
||||
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
args.GetReturnValue().Set(createModuleRequestsContainer(
|
||||
realm, isolate, module->GetModuleRequests()));
|
||||
}
|
||||
|
||||
// moduleWrap.link(specifiers, moduleWraps)
|
||||
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Local<Context> context = realm->context();
|
||||
|
||||
ModuleWrap* dependent;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&dependent, args.This());
|
||||
|
||||
CHECK_EQ(args.Length(), 2);
|
||||
|
||||
Local<Array> specifiers = args[0].As<Array>();
|
||||
Local<Array> modules = args[1].As<Array>();
|
||||
CHECK_EQ(specifiers->Length(), modules->Length());
|
||||
|
||||
std::vector<v8::Global<Value>> specifiers_buffer;
|
||||
if (FromV8Array(context, specifiers, &specifiers_buffer).IsNothing()) {
|
||||
return;
|
||||
}
|
||||
std::vector<v8::Global<Value>> modules_buffer;
|
||||
if (FromV8Array(context, modules, &modules_buffer).IsNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < specifiers->Length(); i++) {
|
||||
Local<String> specifier_str =
|
||||
specifiers_buffer[i].Get(isolate).As<String>();
|
||||
Local<Object> module_object = modules_buffer[i].Get(isolate).As<Object>();
|
||||
|
||||
CHECK(
|
||||
realm->isolate_data()->module_wrap_constructor_template()->HasInstance(
|
||||
module_object));
|
||||
|
||||
Utf8Value specifier(isolate, specifier_str);
|
||||
dependent->resolve_cache_[specifier.ToString()].Reset(isolate,
|
||||
module_object);
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -788,29 +757,6 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
|
||||
args.GetReturnValue().Set(module->GetStatus());
|
||||
}
|
||||
|
||||
void ModuleWrap::GetStaticDependencySpecifiers(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
||||
|
||||
Local<Module> module = obj->module_.Get(realm->isolate());
|
||||
|
||||
Local<FixedArray> module_requests = module->GetModuleRequests();
|
||||
int count = module_requests->Length();
|
||||
|
||||
MaybeStackBuffer<Local<Value>, 16> specifiers(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Local<ModuleRequest> module_request =
|
||||
module_requests->Get(realm->context(), i).As<ModuleRequest>();
|
||||
specifiers[i] = module_request->GetSpecifier();
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(realm->isolate(), specifiers.out(), count));
|
||||
}
|
||||
|
||||
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
ModuleWrap* obj;
|
||||
@@ -848,16 +794,8 @@ MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
Local<Promise> resolve_promise =
|
||||
Local<Object> module_object =
|
||||
dependent->resolve_cache_[specifier_std].Get(isolate);
|
||||
|
||||
if (resolve_promise->State() != Promise::kFulfilled) {
|
||||
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
||||
env, "request for '%s' is not yet fulfilled", specifier_std);
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
Local<Object> module_object = resolve_promise->Result().As<Object>();
|
||||
if (module_object.IsEmpty() || !module_object->IsObject()) {
|
||||
THROW_ERR_VM_MODULE_LINK_FAILURE(
|
||||
env, "request for '%s' did not return an object", specifier_std);
|
||||
@@ -1084,9 +1022,7 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
ModuleWrap::kInternalFieldCount);
|
||||
|
||||
SetProtoMethod(isolate, tpl, "link", Link);
|
||||
SetProtoMethod(isolate, tpl, "getModuleRequestsSync", GetModuleRequestsSync);
|
||||
SetProtoMethod(
|
||||
isolate, tpl, "cacheResolvedWrapsSync", CacheResolvedWrapsSync);
|
||||
SetProtoMethod(isolate, tpl, "getModuleRequests", GetModuleRequests);
|
||||
SetProtoMethod(isolate, tpl, "instantiateSync", InstantiateSync);
|
||||
SetProtoMethod(isolate, tpl, "evaluateSync", EvaluateSync);
|
||||
SetProtoMethod(isolate, tpl, "getNamespaceSync", GetNamespaceSync);
|
||||
@@ -1098,12 +1034,8 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
|
||||
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
|
||||
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
|
||||
SetProtoMethodNoSideEffect(isolate,
|
||||
tpl,
|
||||
"getStaticDependencySpecifiers",
|
||||
GetStaticDependencySpecifiers);
|
||||
|
||||
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
|
||||
isolate_data->set_module_wrap_constructor_template(tpl);
|
||||
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
@@ -1141,8 +1073,7 @@ void ModuleWrap::RegisterExternalReferences(
|
||||
registry->Register(New);
|
||||
|
||||
registry->Register(Link);
|
||||
registry->Register(GetModuleRequestsSync);
|
||||
registry->Register(CacheResolvedWrapsSync);
|
||||
registry->Register(GetModuleRequests);
|
||||
registry->Register(InstantiateSync);
|
||||
registry->Register(EvaluateSync);
|
||||
registry->Register(GetNamespaceSync);
|
||||
@@ -1153,7 +1084,6 @@ void ModuleWrap::RegisterExternalReferences(
|
||||
registry->Register(GetNamespace);
|
||||
registry->Register(GetStatus);
|
||||
registry->Register(GetError);
|
||||
registry->Register(GetStaticDependencySpecifiers);
|
||||
|
||||
registry->Register(SetImportModuleDynamicallyCallback);
|
||||
registry->Register(SetInitializeImportMetaObjectCallback);
|
||||
|
||||
+2
-7
@@ -98,9 +98,7 @@ class ModuleWrap : public BaseObject {
|
||||
~ModuleWrap() override;
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetModuleRequestsSync(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void CacheResolvedWrapsSync(
|
||||
static void GetModuleRequests(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void InstantiateSync(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void EvaluateSync(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
@@ -112,8 +110,6 @@ class ModuleWrap : public BaseObject {
|
||||
static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetStaticDependencySpecifiers(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void SetImportModuleDynamicallyCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
@@ -133,10 +129,9 @@ class ModuleWrap : public BaseObject {
|
||||
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
|
||||
|
||||
v8::Global<v8::Module> module_;
|
||||
std::unordered_map<std::string, v8::Global<v8::Promise>> resolve_cache_;
|
||||
std::unordered_map<std::string, v8::Global<v8::Object>> resolve_cache_;
|
||||
contextify::ContextifyContext* contextify_context_ = nullptr;
|
||||
bool synthetic_ = false;
|
||||
bool linked_ = false;
|
||||
int module_hash_;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,23 +5,21 @@ const assert = require('assert');
|
||||
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
const { ModuleWrap } = internalBinding('module_wrap');
|
||||
const { getPromiseDetails, isPromise } = internalBinding('util');
|
||||
const setTimeoutAsync = require('util').promisify(setTimeout);
|
||||
|
||||
const foo = new ModuleWrap('foo', undefined, 'export * from "bar";', 0, 0);
|
||||
const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
|
||||
|
||||
(async () => {
|
||||
const promises = foo.link(() => setTimeoutAsync(1000).then(() => bar));
|
||||
assert.strictEqual(promises.length, 1);
|
||||
assert(isPromise(promises[0]));
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
assert.strictEqual(getPromiseDetails(promises[0])[1], bar);
|
||||
const moduleRequests = foo.getModuleRequests();
|
||||
assert.strictEqual(moduleRequests.length, 1);
|
||||
assert.strictEqual(moduleRequests[0].specifier, 'bar');
|
||||
|
||||
foo.link(['bar'], [bar]);
|
||||
foo.instantiate();
|
||||
|
||||
assert.strictEqual(await foo.evaluate(-1, false), undefined);
|
||||
assert.strictEqual(foo.getNamespace().five, 5);
|
||||
|
||||
// Check that the module requests are the same after linking, instantiate, and evaluation.
|
||||
assert.deepStrictEqual(moduleRequests, foo.getModuleRequests());
|
||||
})().then(common.mustCall());
|
||||
Reference in new issue
Block a user