Mypal/js/src/builtin/Module.js

641 lines
21 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_UNINSTANTIATED &&
newStatus <= MODULE_STATUS_EVALUATED_ERROR,
"Bad new module status in ModuleSetStatus");
// Note that under OOM conditions we can fail the module instantiation
// process even after modules have been marked as instantiated.
assert((module.status <= MODULE_STATUS_INSTANTIATED &&
newStatus === MODULE_STATUS_UNINSTANTIATED) ||
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 a resolved binding record: { module, bindingName }
//
// There are two failure cases:
//
// - If no definition was found or the request is found to be circular, *null*
// is returned.
//
// - If the request is found to be ambiguous, the string `"ambiguous"` is
// returned.
//
function ModuleResolveExport(exportName, resolveSet = [])
{
if (!IsObject(this) || !IsModule(this)) {
return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet,
"ModuleResolveExport");
}
// Step 1
let module = this;
// Step 2
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 null;
}
}
// Step 3
_DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName});
// Step 4
let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i];
if (exportName === e.exportName)
return {module, bindingName: e.localName};
}
// Step 5
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);
return callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
}
}
// Step 6
if (exportName === "default") {
// A default export cannot be provided by an export *.
return null;
}
// Step 7
let starResolution = null;
// Step 8
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 === "ambiguous")
return resolution;
if (resolution !== null) {
if (starResolution === null) {
starResolution = resolution;
} else {
if (resolution.module !== starResolution.module ||
resolution.bindingName !== starResolution.bindingName)
{
return "ambiguous";
}
}
}
}
// Step 9
return starResolution;
}
function IsResolvedBinding(resolution)
{
assert(resolution === "ambiguous" || typeof resolution === "object",
"Bad module resolution result");
return typeof resolution === "object" && resolution !== null;
}
// 15.2.1.18 GetModuleNamespace(module)
function GetModuleNamespace(module)
{
// Step 1
assert(IsModule(module), "GetModuleNamespace called with non-module");
// Steps 2-3
assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED_ERROR,
"Bad module status in GetModuleNamespace");
// Step 4
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 (IsResolvedBinding(resolution))
_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(IsResolvedBinding(binding), "Failed to resolve binding");
AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
}
return ns;
}
function GetModuleEnvironment(module)
{
assert(IsModule(module), "Non-module passed to GetModuleEnvironment");
assert(module.status >= MODULE_STATUS_INSTANTIATING,
"Attempt to access module environement before instantation");
let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT);
assert(IsModuleEnvironment(env),
"Module environment slot contains unexpected value");
return env;
}
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;
}
function HandleModuleInstantiationFailure(module)
{
// Reset the module to the "uninstantiated" state. Don't reset the
// environment slot as the environment object will be required by any
// possible future instantiation attempt.
ModuleSetStatus(module, MODULE_STATUS_UNINSTANTIATED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, undefined);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, undefined);
}
// 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 {
InnerModuleInstantiation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_INSTANTIATING,
"Expected instantiating status during failed instantiation");
HandleModuleInstantiationFailure(m);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
HandleModuleInstantiationFailure(module);
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Expected uninstantiated status after failed instantiation");
throw error;
}
// Step 6
assert(module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED ||
module.status === MODULE_STATUS_EVALUATED_ERROR,
"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 InnerModuleInstantiation(module, stack, index)
function InnerModuleInstantiation(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 ||
module.status === MODULE_STATUS_EVALUATED_ERROR)
{
return index;
}
// Step 3
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Bad module status in ModuleDeclarationInstantiation");
// Step 4
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
// Steps 5-7
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 8
_DefineDataProperty(stack, stack.length, module);
// Step 9
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
index = InnerModuleInstantiation(requiredModule, stack, index);
assert(requiredModule.status === MODULE_STATUS_INSTANTIATING ||
requiredModule.status === MODULE_STATUS_INSTANTIATED ||
requiredModule.status === MODULE_STATUS_EVALUATED ||
requiredModule.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad required module status after InnerModuleInstantiation");
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 10
ModuleDeclarationEnvironmentSetup(module);
// Steps 11-12
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 13
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);
if (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber);
}
}
// Steps 5-6
// Note that we have already created the environment by this point.
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 (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
}
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
}
}
InstantiateModuleFunctionDeclarations(module);
}
function ThrowResolutionError(module, resolution, kind, name, line, column)
{
assert(module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ThrowResolutionError");
assert(kind === "import" || kind === "indirectExport",
"Unexpected kind in ThrowResolutionError");
assert(line > 0,
"Line number should be present for all imports and indirect exports");
let ambiguous = resolution === "ambiguous";
let errorNumber;
if (kind === "import")
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_IMPORT : JSMSG_MISSING_IMPORT;
else
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT : JSMSG_MISSING_INDIRECT_EXPORT;
let message = GetErrorMessage(errorNumber) + ": " + name;
let error = CreateModuleSyntaxError(module, line, column, message);
throw error;
}
function GetModuleEvaluationError(module)
{
assert(IsObject(module) && IsModule(module),
"Non-module passed to GetModuleEvaluationError");
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status in GetModuleEvaluationError");
return UnsafeGetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT);
}
function RecordModuleEvaluationError(module, error)
{
// Set the module's EvaluationError slot to indicate a failed module
// evaluation.
assert(IsObject(module) && IsModule(module),
"Non-module passed to RecordModuleEvaluationError");
if (module.status === MODULE_STATUS_EVALUATED_ERROR) {
// It would be nice to assert that |error| is the same as the one we
// previously recorded, but that's not always true in the case of out of
// memory and over-recursion errors.
return;
}
ModuleSetStatus(module, MODULE_STATUS_EVALUATED_ERROR);
UnsafeSetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT, 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_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED &&
module.status !== MODULE_STATUS_EVALUATED_ERROR)
{
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");
RecordModuleEvaluationError(m, error);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
RecordModuleEvaluationError(module, error);
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status 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_EVALUATED_ERROR)
throw GetModuleEvaluationError(module);
if (module.status === MODULE_STATUS_EVALUATED)
return index;
// Step 3
if (module.status === MODULE_STATUS_EVALUATING)
return index;
// Step 4
assert(module.status === MODULE_STATUS_INSTANTIATED,
"Bad module status in InnerModuleEvaluation");
// 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;
}