Mypal/js/src/builtin/Module.js

649 lines
22 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function CallModuleResolveHook(module, specifier, expectedMinimumStatus)
{
let requestedModule = HostResolveImportedModule(module, specifier);
if (requestedModule.state < expectedMinimumStatus)
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
return requestedModule;
}
// 15.2.1.16.2 GetExportedNames(exportStarSet)
function ModuleGetExportedNames(exportStarSet = [])
{
if (!IsObject(this) || !IsModule(this)) {
return callFunction(CallModuleMethodIfWrapped, this, exportStarSet,
"ModuleGetExportedNames");
}
// Step 1
let module = this;
// Step 2
if (callFunction(ArrayIncludes, exportStarSet, module))
return [];
// Step 3
_DefineDataProperty(exportStarSet, exportStarSet.length, module);
// Step 4
let exportedNames = [];
let namesCount = 0;
// Step 5
let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i];
_DefineDataProperty(exportedNames, namesCount++, e.exportName);
}
// Step 6
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
_DefineDataProperty(exportedNames, namesCount++, e.exportName);
}
// Step 7
let starExportEntries = module.starExportEntries;
for (let i = 0; i < starExportEntries.length; i++) {
let e = starExportEntries[i];
let requestedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATE_INSTANTIATED);
let starNames = callFunction(requestedModule.getExportedNames, requestedModule,
exportStarSet);
for (let j = 0; j < starNames.length; j++) {
let n = starNames[j];
if (n !== "default" && !callFunction(ArrayIncludes, exportedNames, n))
_DefineDataProperty(exportedNames, namesCount++, n);
}
}
return exportedNames;
}
function ModuleSetStatus(module, newStatus)
{
assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED,
"Bad new module status in ModuleSetStatus");
if (newStatus !== MODULE_STATUS_ERRORED)
assert(newStatus > module.status, "New module status inconsistent with current status");
UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus);
}
// 15.2.1.16.3 ResolveExport(exportName, resolveSet)
//
// Returns an object describing the location of the resolved export or
// indicating a failure.
//
// On success this returns: { resolved: true, module, bindingName }
//
// There are three failure cases:
//
// - The resolution failure can be blamed on a particular module.
// Returns: { resolved: false, module, ambiguous: false }
//
// - No culprit can be determined and the resolution failure was due to star
// export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: true }
//
// - No culprit can be determined and the resolution failure was not due to
// star export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: false }
//
function ModuleResolveExport(exportName, resolveSet = [])
{
if (!IsObject(this) || !IsModule(this)) {
return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet,
"ModuleResolveExport");
}
// Step 1
let module = this;
// Step 2
assert(module.status !== MODULE_STATUS_ERRORED, "Bad module status in ResolveExport");
// Step 3
for (let i = 0; i < resolveSet.length; i++) {
let r = resolveSet[i];
if (r.module === module && r.exportName === exportName) {
// This is a circular import request.
return {resolved: false, module: null, ambiguous: false};
}
}
// Step 4
_DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName});
// Step 5
let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i];
if (exportName === e.exportName)
return {resolved: true, module, bindingName: e.localName};
}
// Step 6
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
if (exportName === e.exportName) {
let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
return resolution;
}
}
// Step 7
if (exportName === "default") {
// A default export cannot be provided by an export *.
return {resolved: false, module: null, ambiguous: false};
}
// Step 8
let starResolution = null;
// Step 9
let starExportEntries = module.starExportEntries;
for (let i = 0; i < starExportEntries.length; i++) {
let e = starExportEntries[i];
let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, exportName,
resolveSet);
if (!resolution.resolved && (resolution.module || resolution.ambiguous))
return resolution;
if (resolution.resolved) {
if (starResolution === null) {
starResolution = resolution;
} else {
if (resolution.module !== starResolution.module ||
resolution.bindingName !== starResolution.bindingName)
{
return {resolved: false, module: null, ambiguous: true};
}
}
}
}
// Step 10
if (starResolution !== null)
return starResolution;
return {resolved: false, module: null, ambiguous: false};
}
// 15.2.1.18 GetModuleNamespace(module)
function GetModuleNamespace(module)
{
// Step 1
assert(IsModule(module), "GetModuleNamespace called with non-module");
// Step 2
assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
module.status !== MODULE_STATUS_ERRORED,
"Bad module status in GetModuleNamespace");
// Step 3
let namespace = module.namespace;
if (typeof namespace === "undefined") {
let exportedNames = callFunction(module.getExportedNames, module);
let unambiguousNames = [];
for (let i = 0; i < exportedNames.length; i++) {
let name = exportedNames[i];
let resolution = callFunction(module.resolveExport, module, name);
if (resolution.resolved)
_DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
}
namespace = ModuleNamespaceCreate(module, unambiguousNames);
}
// Step 4
return namespace;
}
// 9.4.6.13 ModuleNamespaceCreate(module, exports)
function ModuleNamespaceCreate(module, exports)
{
callFunction(std_Array_sort, exports);
let ns = NewModuleNamespace(module, exports);
// Pre-compute all bindings now rather than calling ResolveExport() on every
// access.
for (let i = 0; i < exports.length; i++) {
let name = exports[i];
let binding = callFunction(module.resolveExport, module, name);
assert(binding.resolved, "Failed to resolve binding");
AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
}
return ns;
}
function GetModuleEnvironment(module)
{
assert(IsModule(module), "Non-module passed to GetModuleEnvironment");
// Check for a previous failed attempt to instantiate this module. This can
// only happen due to a bug in the module loader.
if (module.status === MODULE_STATUS_ERRORED)
ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status);
let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT);
assert(env === undefined || IsModuleEnvironment(env),
"Module environment slot contains unexpected value");
return env;
}
function RecordModuleError(module, error)
{
// Set the module's status to 'errored' to indicate a failed module
// instantiation and record the exception. The environment slot is also
// reset to 'undefined'.
assert(IsObject(module) && IsModule(module), "Non-module passed to RecordModuleError");
ModuleSetStatus(module, MODULE_STATUS_ERRORED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT, error);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined);
}
function CountArrayValues(array, value)
{
let count = 0;
for (let i = 0; i < array.length; i++) {
if (array[i] === value)
count++;
}
return count;
}
function ArrayContains(array, value)
{
for (let i = 0; i < array.length; i++) {
if (array[i] === value)
return true;
}
return false;
}
// 15.2.1.16.4 ModuleInstantiate()
function ModuleInstantiate()
{
if (!IsObject(this) || !IsModule(this))
return callFunction(CallModuleMethodIfWrapped, this, "ModuleInstantiate");
// Step 1
let module = this;
// Step 2
if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_EVALUATING)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
// Step 3
let stack = [];
// Steps 4-5
try {
InnerModuleDeclarationInstantiation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_INSTANTIATING ||
m.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
RecordModuleError(m, error);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack.
assert(error === "out of memory",
"Stack must contain module unless we hit OOM");
RecordModuleError(module, error);
}
assert(module.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed instantiation");
throw error;
}
// Step 6
assert(module.status == MODULE_STATUS_INSTANTIATED ||
module.status == MODULE_STATUS_EVALUATED,
"Bad module status after successful instantiation");
// Step 7
assert(stack.length === 0,
"Stack should be empty after successful instantiation");
// Step 8
return undefined;
}
_SetCanonicalName(ModuleInstantiate, "ModuleInstantiate");
// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index)
function InnerModuleDeclarationInstantiation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED)
{
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
// Step 4
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Bad module status in ModuleDeclarationInstantiation");
// Steps 5
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
// Step 6-8
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
_DefineDataProperty(stack, stack.length, module);
// Step 10
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED);
index = InnerModuleDeclarationInstantiation(requiredModule, stack, index);
assert(requiredModule.status === MODULE_STATUS_INSTANTIATING ||
requiredModule.status === MODULE_STATUS_INSTANTIATED ||
requiredModule.status === MODULE_STATUS_EVALUATED,
"Bad required module status after InnerModuleDeclarationInstantiation");
assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) ===
ArrayContains(stack, requiredModule),
"Required module should be in the stack iff it is currently being instantiated");
assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
if (requiredModule.status === MODULE_STATUS_INSTANTIATING) {
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
std_Math_min(module.dfsAncestorIndex,
requiredModule.dfsAncestorIndex));
}
}
// Step 11
ModuleDeclarationEnvironmentSetup(module);
// Steps 12-13
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 14
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
requiredModule = callFunction(std_Array_pop, stack);
ModuleSetStatus(requiredModule, MODULE_STATUS_INSTANTIATED);
} while (requiredModule !== module);
}
// Step 15
return index;
}
// 15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup(module)
function ModuleDeclarationEnvironmentSetup(module)
{
// Step 1
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
let resolution = callFunction(module.resolveExport, module, e.exportName);
assert(resolution.resolved || resolution.module,
"Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup");
if (!resolution.resolved) {
return ResolutionError(resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber)
}
}
// Steps 5-6
CreateModuleEnvironment(module);
let env = GetModuleEnvironment(module);
// Step 8
let importEntries = module.importEntries;
for (let i = 0; i < importEntries.length; i++) {
let imp = importEntries[i];
let importedModule = CallModuleResolveHook(module, imp.moduleRequest,
MODULE_STATUS_INSTANTIATING);
if (imp.importName === "*") {
let namespace = GetModuleNamespace(importedModule);
CreateNamespaceBinding(env, imp.localName, namespace);
} else {
let resolution = callFunction(importedModule.resolveExport, importedModule,
imp.importName);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
if (!resolution.resolved) {
return ResolutionError(resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
}
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
}
}
InstantiateModuleFunctionDeclarations(module);
}
// 15.2.1.16.4.3 ResolutionError(module)
function ResolutionError(resolution, kind, name, line, column)
{
let module = resolution.module;
assert(module !== null,
"Null module passed to ResolutionError");
assert(module.status === MODULE_STATUS_UNINSTANTIATED ||
module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ResolutionError");
assert(kind === "import" || kind === "indirectExport",
"Unexpected kind in ResolutionError");
assert(line > 0,
"Line number should be present for all imports and indirect exports");
let errorNumber;
if (kind === "import") {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT
: JSMSG_MISSING_IMPORT;
} else {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT
: JSMSG_MISSING_INDIRECT_EXPORT;
}
let message = GetErrorMessage(errorNumber) + ": " + name;
let error = CreateModuleSyntaxError(module, line, column, message);
RecordModuleError(module, error);
throw error;
}
// 15.2.1.16.5 ModuleEvaluate()
function ModuleEvaluate()
{
if (!IsObject(this) || !IsModule(this))
return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluate");
// Step 1
let module = this;
// Step 2
if (module.status !== MODULE_STATUS_ERRORED &&
module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
// Step 3
let stack = [];
// Steps 4-5
try {
InnerModuleEvaluation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_EVALUATING,
"Bad module status after failed evaluation");
RecordModuleError(m, error);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack.
assert(error === "out of memory",
"Stack must contain module unless we hit OOM");
RecordModuleError(module, error);
}
assert(module.status === MODULE_STATUS_ERRORED,
"Bad module status after failed evaluation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed evaluation");
throw error;
}
assert(module.status == MODULE_STATUS_EVALUATED,
"Bad module status after successful evaluation");
assert(stack.length === 0,
"Stack should be empty after successful evaluation");
return undefined;
}
_SetCanonicalName(ModuleEvaluate, "ModuleEvaluate");
// 15.2.1.16.5.1 InnerModuleEvaluation(module, stack, index)
function InnerModuleEvaluation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_EVALUATING ||
module.status === MODULE_STATUS_EVALUATED)
{
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
// Step 4
assert(module.status === MODULE_STATUS_INSTANTIATED,
"Bad module status in ModuleEvaluation");
// Step 5
ModuleSetStatus(module, MODULE_STATUS_EVALUATING);
// Steps 6-8
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
_DefineDataProperty(stack, stack.length, module);
// Step 10
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule =
CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
index = InnerModuleEvaluation(requiredModule, stack, index);
assert(requiredModule.status == MODULE_STATUS_EVALUATING ||
requiredModule.status == MODULE_STATUS_EVALUATED,
"Bad module status after InnerModuleEvaluation");
assert((requiredModule.status === MODULE_STATUS_EVALUATING) ===
ArrayContains(stack, requiredModule),
"Required module should be in the stack iff it is currently being evaluated");
assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
if (requiredModule.status === MODULE_STATUS_EVALUATING) {
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
std_Math_min(module.dfsAncestorIndex,
requiredModule.dfsAncestorIndex));
}
}
// Step 11
ExecuteModule(module);
// Step 12
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
// Step 13
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 14
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
requiredModule = callFunction(std_Array_pop, stack);
ModuleSetStatus(requiredModule, MODULE_STATUS_EVALUATED);
} while (requiredModule !== module);
}
// Step 15
return index;
}