mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 07:40:42 +00:00
Bug 1519100 - Implement Top-level await specification; r=jonco
Tested in: https://phabricator.services.mozilla.com/D93530 Differential Revision: https://phabricator.services.mozilla.com/D93524
This commit is contained in:
parent
da7e2ac90e
commit
eb82ac67e4
@ -586,12 +586,33 @@ function ModuleEvaluate()
|
||||
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
|
||||
}
|
||||
|
||||
// Top-level Await Step 4
|
||||
if (module.status === MODULE_STATUS_EVALUATED) {
|
||||
module = GetAsyncCycleRoot(module);
|
||||
}
|
||||
|
||||
// Top-level Await Step 5
|
||||
if (module.topLevelCapability) {
|
||||
return module.topLevelCapability;
|
||||
}
|
||||
|
||||
let capability = CreateTopLevelCapability(module);
|
||||
|
||||
// Step 4
|
||||
let stack = [];
|
||||
|
||||
// Steps 5-6
|
||||
try {
|
||||
InnerModuleEvaluation(module, stack, 0);
|
||||
if (!module.asyncEvaluating) {
|
||||
ModuleTopLevelCapabilityResolve(module);
|
||||
}
|
||||
// Steps 7-8
|
||||
assert(module.status === MODULE_STATUS_EVALUATED,
|
||||
"Bad module status after successful evaluation");
|
||||
assert(stack.length === 0,
|
||||
"Stack should be empty after successful evaluation");
|
||||
|
||||
} catch (error) {
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
let m = stack[i];
|
||||
@ -607,17 +628,11 @@ function ModuleEvaluate()
|
||||
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
|
||||
"Bad module status after failed evaluation");
|
||||
|
||||
throw error;
|
||||
ModuleTopLevelCapabilityReject(module, error);
|
||||
}
|
||||
|
||||
// Steps 7-8
|
||||
assert(module.status === MODULE_STATUS_EVALUATED,
|
||||
"Bad module status after successful evaluation");
|
||||
assert(stack.length === 0,
|
||||
"Stack should be empty after successful evaluation");
|
||||
|
||||
// Step 9
|
||||
return undefined;
|
||||
return capability;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-innermoduleevaluation
|
||||
@ -648,6 +663,7 @@ function InnerModuleEvaluation(module, stack, index)
|
||||
// Steps 6-8
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT, 0);
|
||||
index++;
|
||||
|
||||
// Step 9
|
||||
@ -677,11 +693,32 @@ function InnerModuleEvaluation(module, stack, index)
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
|
||||
std_Math_min(module.dfsAncestorIndex,
|
||||
requiredModule.dfsAncestorIndex));
|
||||
} else {
|
||||
requiredModule = GetAsyncCycleRoot(requiredModule);
|
||||
assert(requiredModule.status === MODULE_STATUS_EVALUATED,
|
||||
`Bad module status in InnerModuleEvaluation: ${requiredModule.status}`);
|
||||
if (requiredModule.evaluationError) {
|
||||
throw GetModuleEvaluationError(module);
|
||||
}
|
||||
}
|
||||
if (requiredModule.asyncEvaluating) {
|
||||
UnsafeSetReservedSlot(module,
|
||||
MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT,
|
||||
module.pendingAsyncDependencies + 1);
|
||||
AppendAsyncParentModule(requiredModule, module);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 11
|
||||
ExecuteModule(module);
|
||||
if (module.pendingAsyncDependencies > 0) {
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_ASYNC_EVALUATING_SLOT, true);
|
||||
} else {
|
||||
if (module.async) {
|
||||
ExecuteAsyncModule(module);
|
||||
} else {
|
||||
// Step 11
|
||||
ExecuteModule(module);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 12
|
||||
assert(CountArrayValues(stack, module) === 1,
|
||||
@ -703,3 +740,17 @@ function InnerModuleEvaluation(module, stack, index)
|
||||
// Step 15
|
||||
return index;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-execute-async-module
|
||||
function ExecuteAsyncModule(module) {
|
||||
// Steps 1-3.
|
||||
assert(module.status == MODULE_STATUS_EVALUATING ||
|
||||
module.status == MODULE_STATUS_EVALUATED, "bad status for async module");
|
||||
assert(module.async, "module is not async");
|
||||
UnsafeSetReservedSlot(module, MODULE_OBJECT_ASYNC_EVALUATING_SLOT, true);
|
||||
|
||||
// Step 4-11 done in AsyncAwait opcode
|
||||
|
||||
ExecuteModule(module);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
#include "vm/List-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
@ -835,8 +836,13 @@ void ModuleObject::initFunctionDeclarations(
|
||||
*functionDeclarations() = std::move(decls);
|
||||
}
|
||||
|
||||
void ModuleObject::initAsyncSlots(JSContext* cx, bool isAsync) {
|
||||
bool ModuleObject::initAsyncSlots(JSContext* cx, bool isAsync,
|
||||
HandleObject asyncParentModulesList) {
|
||||
initReservedSlot(AsyncSlot, BooleanValue(isAsync));
|
||||
initReservedSlot(AsyncEvaluatingSlot, BooleanValue(false));
|
||||
initReservedSlot(AsyncParentModulesSlot,
|
||||
ObjectValue(*asyncParentModulesList));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModuleObject::initScriptSlots(HandleScript script) {
|
||||
@ -967,10 +973,73 @@ bool ModuleObject::isAsync() const {
|
||||
return getReservedSlot(AsyncSlot).toBoolean();
|
||||
}
|
||||
|
||||
bool ModuleObject::isAsyncEvaluating() const {
|
||||
return getReservedSlot(AsyncEvaluatingSlot).toBoolean();
|
||||
}
|
||||
|
||||
void ModuleObject::setAsyncEvaluating(bool isEvaluating) {
|
||||
return setReservedSlot(AsyncEvaluatingSlot, BooleanValue(isEvaluating));
|
||||
}
|
||||
|
||||
uint32_t ModuleObject::dfsIndex() const {
|
||||
return getReservedSlot(DFSIndexSlot).toInt32();
|
||||
}
|
||||
|
||||
uint32_t ModuleObject::dfsAncestorIndex() const {
|
||||
return getReservedSlot(DFSAncestorIndexSlot).toInt32();
|
||||
}
|
||||
|
||||
JSObject* ModuleObject::topLevelCapability() const {
|
||||
return &getReservedSlot(TopLevelCapabilitySlot).toObject();
|
||||
}
|
||||
|
||||
PromiseObject* ModuleObject::createTopLevelCapability(
|
||||
JSContext* cx, HandleModuleObject module) {
|
||||
MOZ_ASSERT(module->getReservedSlot(TopLevelCapabilitySlot).isUndefined());
|
||||
Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
|
||||
if (!resultPromise) {
|
||||
return nullptr;
|
||||
}
|
||||
module->setInitialTopLevelCapability(resultPromise);
|
||||
return resultPromise;
|
||||
}
|
||||
|
||||
void ModuleObject::setInitialTopLevelCapability(HandleObject promiseObj) {
|
||||
initReservedSlot(TopLevelCapabilitySlot, ObjectValue(*promiseObj));
|
||||
}
|
||||
|
||||
inline ListObject* ModuleObject::asyncParentModules() const {
|
||||
return &getReservedSlot(AsyncParentModulesSlot).toObject().as<ListObject>();
|
||||
}
|
||||
|
||||
bool ModuleObject::appendAsyncParentModule(JSContext* cx,
|
||||
HandleModuleObject self,
|
||||
HandleModuleObject parent) {
|
||||
Rooted<Value> parentValue(cx, ObjectValue(*parent));
|
||||
return self->asyncParentModules()->append(cx, parentValue);
|
||||
}
|
||||
|
||||
uint32_t ModuleObject::pendingAsyncDependencies() const {
|
||||
return getReservedSlot(PendingAsyncDependenciesSlot).toInt32();
|
||||
}
|
||||
|
||||
void ModuleObject::setPendingAsyncDependencies(uint32_t newValue) {
|
||||
return setReservedSlot(PendingAsyncDependenciesSlot, NumberValue(newValue));
|
||||
}
|
||||
|
||||
bool ModuleObject::hasTopLevelCapability() const {
|
||||
return !getReservedSlot(TopLevelCapabilitySlot).isUndefined();
|
||||
}
|
||||
|
||||
bool ModuleObject::hadEvaluationError() const {
|
||||
return status() == MODULE_STATUS_EVALUATED_ERROR;
|
||||
}
|
||||
|
||||
void ModuleObject::setEvaluationError(HandleValue newValue) {
|
||||
setReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_EVALUATED_ERROR));
|
||||
return setReservedSlot(EvaluationErrorSlot, newValue);
|
||||
}
|
||||
|
||||
Value ModuleObject::evaluationError() const {
|
||||
MOZ_ASSERT(hadEvaluationError());
|
||||
return getReservedSlot(EvaluationErrorSlot);
|
||||
@ -1053,7 +1122,8 @@ bool ModuleObject::instantiateFunctionDeclarations(JSContext* cx,
|
||||
bool ModuleObject::execute(JSContext* cx, HandleModuleObject self,
|
||||
MutableHandleValue rval) {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(self->status() == MODULE_STATUS_EVALUATING);
|
||||
MOZ_ASSERT(self->status() == MODULE_STATUS_EVALUATING ||
|
||||
self->status() == MODULE_STATUS_EVALUATED);
|
||||
if (!AssertFrozen(cx, self)) {
|
||||
return false;
|
||||
}
|
||||
@ -1158,6 +1228,13 @@ DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, DFSIndexSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, DFSAncestorIndexSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, async, AsyncSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, topLevelCapability,
|
||||
TopLevelCapabilitySlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncEvaluating, AsyncEvaluatingSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncParentModules,
|
||||
AsyncParentModulesSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, pendingAsyncDependencies,
|
||||
PendingAsyncDependenciesSlot)
|
||||
|
||||
/* static */
|
||||
bool GlobalObject::initModuleProto(JSContext* cx,
|
||||
@ -1175,6 +1252,11 @@ bool GlobalObject::initModuleProto(JSContext* cx,
|
||||
JS_PSG("dfsIndex", ModuleObject_dfsIndexGetter, 0),
|
||||
JS_PSG("dfsAncestorIndex", ModuleObject_dfsAncestorIndexGetter, 0),
|
||||
JS_PSG("async", ModuleObject_asyncGetter, 0),
|
||||
JS_PSG("topLevelCapability", ModuleObject_topLevelCapabilityGetter, 0),
|
||||
JS_PSG("asyncEvaluating", ModuleObject_asyncEvaluatingGetter, 0),
|
||||
JS_PSG("asyncParentModules", ModuleObject_asyncParentModulesGetter, 0),
|
||||
JS_PSG("pendingAsyncDependencies",
|
||||
ModuleObject_pendingAsyncDependenciesGetter, 0),
|
||||
JS_PS_END};
|
||||
|
||||
static const JSFunctionSpec protoFunctions[] = {
|
||||
@ -1420,6 +1502,15 @@ bool frontend::StencilModuleMetadata::initModule(
|
||||
}
|
||||
module->initFunctionDeclarations(std::move(functionDeclsCopy));
|
||||
|
||||
Rooted<ListObject*> asyncParentModulesList(cx, ListObject::create(cx));
|
||||
if (!asyncParentModulesList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!module->initAsyncSlots(cx, isAsync, asyncParentModulesList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
module->initImportExportData(
|
||||
requestedModulesObject, importEntriesObject, localExportEntriesObject,
|
||||
indirectExportEntriesObject, starExportEntriesObject);
|
||||
@ -1845,6 +1936,193 @@ JSObject* js::CallModuleResolveHook(JSContext* cx,
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-getasynccycleroot
|
||||
ModuleObject* js::GetAsyncCycleRoot(ModuleObject* module) {
|
||||
// Step 1.
|
||||
MOZ_ASSERT(module->status() == MODULE_STATUS_EVALUATED);
|
||||
|
||||
// Step 2.
|
||||
if (module->asyncParentModules()->empty()) {
|
||||
return module;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
ModuleObject* currentModule = module;
|
||||
while (currentModule->dfsIndex() > currentModule->dfsAncestorIndex()) {
|
||||
MOZ_ASSERT(!currentModule->asyncParentModules()->empty());
|
||||
ModuleObject* nextCycleModule = ¤tModule->asyncParentModules()
|
||||
->get(0)
|
||||
.toObject()
|
||||
.as<ModuleObject>();
|
||||
MOZ_ASSERT(nextCycleModule->dfsAncestorIndex() <=
|
||||
currentModule->dfsAncestorIndex());
|
||||
currentModule = nextCycleModule;
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
MOZ_ASSERT(currentModule->dfsIndex() == currentModule->dfsAncestorIndex());
|
||||
|
||||
// Step 5.
|
||||
return currentModule;
|
||||
}
|
||||
|
||||
bool js::AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JSFunction& func = args.callee().as<JSFunction>();
|
||||
|
||||
Rooted<ModuleObject*> module(
|
||||
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
|
||||
.toObject()
|
||||
.as<ModuleObject>());
|
||||
AsyncModuleExecutionFulfilled(cx, module);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool js::AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JSFunction& func = args.callee().as<JSFunction>();
|
||||
Rooted<ModuleObject*> module(
|
||||
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
|
||||
.toObject()
|
||||
.as<ModuleObject>());
|
||||
AsyncModuleExecutionRejected(cx, module, args.get(0));
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Top Level Await
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
|
||||
void js::AsyncModuleExecutionFulfilled(JSContext* cx,
|
||||
HandleModuleObject module) {
|
||||
// Step 1.
|
||||
MOZ_ASSERT(module->status() == MODULE_STATUS_EVALUATED);
|
||||
|
||||
// Step 2.
|
||||
if (!module->isAsyncEvaluating()) {
|
||||
MOZ_ASSERT(module->hadEvaluationError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
MOZ_ASSERT(!module->hadEvaluationError());
|
||||
|
||||
// Step 4.
|
||||
module->setAsyncEvaluating(false);
|
||||
|
||||
// Step 5.
|
||||
uint32_t length = module->asyncParentModules()->length();
|
||||
Rooted<ModuleObject*> m(cx);
|
||||
Rooted<ModuleObject*> cycleRoot(cx);
|
||||
RootedValue result(cx);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
m = &module->asyncParentModules()->get(i).toObject().as<ModuleObject>();
|
||||
|
||||
if (module->dfsIndex() != module->dfsAncestorIndex()) {
|
||||
MOZ_ASSERT(m->dfsAncestorIndex() <= module->dfsAncestorIndex());
|
||||
}
|
||||
|
||||
m->setPendingAsyncDependencies(m->pendingAsyncDependencies() - 1);
|
||||
|
||||
if (m->pendingAsyncDependencies() == 0 && !m->hadEvaluationError()) {
|
||||
MOZ_ASSERT(m->isAsyncEvaluating());
|
||||
|
||||
cycleRoot = GetAsyncCycleRoot(m);
|
||||
|
||||
if (cycleRoot->hadEvaluationError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m->isAsync()) {
|
||||
// Steps for ExecuteAsyncModule
|
||||
MOZ_ASSERT(m->status() == MODULE_STATUS_EVALUATING ||
|
||||
m->status() == MODULE_STATUS_EVALUATED);
|
||||
MOZ_ASSERT(m->isAsync());
|
||||
MOZ_ASSERT(m->isAsyncEvaluating());
|
||||
ModuleObject::execute(cx, m, &result);
|
||||
} else {
|
||||
if (ModuleObject::execute(cx, m, &result)) {
|
||||
AsyncModuleExecutionFulfilled(cx, m);
|
||||
} else {
|
||||
AsyncModuleExecutionRejected(cx, m, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (module->hasTopLevelCapability()) {
|
||||
MOZ_ASSERT(module->dfsIndex() == module->dfsAncestorIndex());
|
||||
ModuleObject::topLevelCapabilityResolve(cx, module);
|
||||
}
|
||||
|
||||
// Return undefined.
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
|
||||
void js::AsyncModuleExecutionRejected(JSContext* cx, HandleModuleObject module,
|
||||
HandleValue error) {
|
||||
// Step 1.
|
||||
MOZ_ASSERT(module->status() == MODULE_STATUS_EVALUATED);
|
||||
|
||||
// Step 2.
|
||||
if (!module->isAsyncEvaluating()) {
|
||||
MOZ_ASSERT(module->hadEvaluationError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
MOZ_ASSERT(!module->hadEvaluationError());
|
||||
|
||||
// Step 4.
|
||||
module->setEvaluationError(error);
|
||||
|
||||
// Step 5.
|
||||
module->setAsyncEvaluating(false);
|
||||
|
||||
// Step 6.
|
||||
uint32_t length = module->asyncParentModules()->length();
|
||||
Rooted<ModuleObject*> parent(cx);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
parent =
|
||||
&module->asyncParentModules()->get(i).toObject().as<ModuleObject>();
|
||||
if (module->dfsIndex() != module->dfsAncestorIndex()) {
|
||||
MOZ_ASSERT(parent->dfsAncestorIndex() == module->dfsAncestorIndex());
|
||||
}
|
||||
AsyncModuleExecutionRejected(cx, parent, error);
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
if (module->hasTopLevelCapability()) {
|
||||
MOZ_ASSERT(module->dfsIndex() == module->dfsAncestorIndex());
|
||||
ModuleObject::topLevelCapabilityReject(cx, module, error);
|
||||
}
|
||||
|
||||
// Return undefined.
|
||||
}
|
||||
|
||||
bool ModuleObject::topLevelCapabilityResolve(JSContext* cx,
|
||||
HandleModuleObject module) {
|
||||
RootedValue rval(cx);
|
||||
Rooted<PromiseObject*> promise(
|
||||
cx, &module->topLevelCapability()->as<PromiseObject>());
|
||||
return AsyncFunctionReturned(cx, promise, rval);
|
||||
}
|
||||
|
||||
bool ModuleObject::topLevelCapabilityReject(JSContext* cx,
|
||||
HandleModuleObject module,
|
||||
HandleValue error) {
|
||||
Rooted<PromiseObject*> promise(
|
||||
cx, &module->topLevelCapability()->as<PromiseObject>());
|
||||
if (!AsyncFunctionThrown(cx, promise, error)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(promise->state() == JS::PromiseState::Rejected);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script,
|
||||
HandleValue specifierArg) {
|
||||
RootedObject promiseConstructor(cx, JS::GetPromiseConstructor(cx));
|
||||
@ -2230,7 +2508,12 @@ XDRResult js::XDRModuleObject(XDRState<mode>* xdr,
|
||||
MOZ_TRY(xdr->codeUint8(&isAsyncModule));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
module->initAsyncSlots(cx, isAsyncModule == 1);
|
||||
Rooted<ListObject*> asyncParentModulesList(cx, ListObject::create(cx));
|
||||
if (!asyncParentModulesList) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
module->initAsyncSlots(cx, isAsyncModule == 1, asyncParentModulesList);
|
||||
}
|
||||
|
||||
modp.set(module);
|
||||
|
@ -19,7 +19,9 @@
|
||||
#include "js/Modules.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "vm/JSAtom.h"
|
||||
#include "vm/List.h"
|
||||
#include "vm/NativeObject.h"
|
||||
#include "vm/PromiseObject.h" // js::PromiseObject
|
||||
#include "vm/ProxyObject.h"
|
||||
|
||||
namespace js {
|
||||
@ -249,6 +251,10 @@ class ModuleObject : public NativeObject {
|
||||
DFSIndexSlot,
|
||||
DFSAncestorIndexSlot,
|
||||
AsyncSlot,
|
||||
AsyncEvaluatingSlot,
|
||||
TopLevelCapabilitySlot,
|
||||
AsyncParentModulesSlot,
|
||||
PendingAsyncDependenciesSlot,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
@ -262,6 +268,14 @@ class ModuleObject : public NativeObject {
|
||||
"DFSIndexSlot must match self-hosting define");
|
||||
static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
|
||||
"DFSAncestorIndexSlot must match self-hosting define");
|
||||
static_assert(AsyncEvaluatingSlot == MODULE_OBJECT_ASYNC_EVALUATING_SLOT,
|
||||
"AsyncEvaluatingSlot must match self-hosting define");
|
||||
static_assert(TopLevelCapabilitySlot ==
|
||||
MODULE_OBJECT_TOP_LEVEL_CAPABILITY_SLOT,
|
||||
"topLevelCapabilitySlot must match self-hosting define");
|
||||
static_assert(PendingAsyncDependenciesSlot ==
|
||||
MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT,
|
||||
"PendingAsyncDependenciesSlot must match self-hosting define");
|
||||
|
||||
static const JSClass class_;
|
||||
|
||||
@ -294,7 +308,8 @@ class ModuleObject : public NativeObject {
|
||||
ModuleEnvironmentObject* environment() const;
|
||||
ModuleNamespaceObject* namespace_();
|
||||
ModuleStatus status() const;
|
||||
bool isAsync() const;
|
||||
uint32_t dfsIndex() const;
|
||||
uint32_t dfsAncestorIndex() const;
|
||||
bool hadEvaluationError() const;
|
||||
Value evaluationError() const;
|
||||
JSObject* metaObject() const;
|
||||
@ -306,6 +321,28 @@ class ModuleObject : public NativeObject {
|
||||
ArrayObject& starExportEntries() const;
|
||||
IndirectBindingMap& importBindings();
|
||||
|
||||
static PromiseObject* createTopLevelCapability(JSContext* cx,
|
||||
HandleModuleObject module);
|
||||
bool isAsync() const;
|
||||
void setAsync(bool isAsync);
|
||||
bool isAsyncEvaluating() const;
|
||||
void setAsyncEvaluating(bool isEvaluating);
|
||||
void setEvaluationError(HandleValue newValue);
|
||||
void setPendingAsyncDependencies(uint32_t newValue);
|
||||
void setInitialTopLevelCapability(HandleObject promiseObj);
|
||||
bool hasTopLevelCapability() const;
|
||||
JSObject* topLevelCapability() const;
|
||||
ListObject* asyncParentModules() const;
|
||||
uint32_t pendingAsyncDependencies() const;
|
||||
|
||||
static bool appendAsyncParentModule(JSContext* cx, HandleModuleObject self,
|
||||
HandleModuleObject parent);
|
||||
|
||||
static bool topLevelCapabilityResolve(JSContext* cx,
|
||||
HandleModuleObject module);
|
||||
static bool topLevelCapabilityReject(JSContext* cx, HandleModuleObject module,
|
||||
HandleValue error);
|
||||
|
||||
static bool Instantiate(JSContext* cx, HandleModuleObject self);
|
||||
static bool Evaluate(JSContext* cx, HandleModuleObject self);
|
||||
|
||||
@ -332,7 +369,8 @@ class ModuleObject : public NativeObject {
|
||||
frontend::FunctionDeclarationVector* functionDeclarations();
|
||||
void initFunctionDeclarations(frontend::FunctionDeclarationVector&& decls);
|
||||
|
||||
void initAsyncSlots(JSContext* cx, bool isAsync);
|
||||
bool initAsyncSlots(JSContext* cx, bool isAsync,
|
||||
HandleObject asyncParentModulesList);
|
||||
|
||||
private:
|
||||
static const JSClassOps classOps_;
|
||||
@ -348,6 +386,24 @@ JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
|
||||
JSObject* CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate,
|
||||
HandleString specifier);
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-getasynccycleroot
|
||||
ModuleObject* GetAsyncCycleRoot(ModuleObject* module);
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
|
||||
void AsyncModuleExecutionFulfilled(JSContext* cx, HandleModuleObject module);
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
|
||||
void AsyncModuleExecutionRejected(JSContext* cx, HandleModuleObject module,
|
||||
HandleValue error);
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
|
||||
bool AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc,
|
||||
Value* vp);
|
||||
|
||||
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
|
||||
bool AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc,
|
||||
Value* vp);
|
||||
|
||||
JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script,
|
||||
HandleValue specifier);
|
||||
|
||||
|
@ -5702,7 +5702,13 @@ static MOZ_MUST_USE bool IsTopMostAsyncFunctionCall(JSContext* cx) {
|
||||
if (iter.done()) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(iter.isFunctionFrame());
|
||||
|
||||
if (!iter.isFunctionFrame() && iter.isModuleFrame()) {
|
||||
// The iterator is not a function frame, it is a module frame.
|
||||
// Ignore this optimization for now.
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(iter.calleeTemplate()->isAsync());
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -110,6 +110,9 @@
|
||||
#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4
|
||||
#define MODULE_OBJECT_DFS_INDEX_SLOT 14
|
||||
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 15
|
||||
#define MODULE_OBJECT_ASYNC_EVALUATING_SLOT 17
|
||||
#define MODULE_OBJECT_TOP_LEVEL_CAPABILITY_SLOT 18
|
||||
#define MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT 20
|
||||
|
||||
// rev b012019fea18f29737a67c36911340a3e25bfc63
|
||||
// 15.2.1.16 Cyclic Module Records
|
||||
|
@ -2909,8 +2909,11 @@ bool BytecodeEmitter::emitIteratorNext(
|
||||
const Maybe<uint32_t>& callSourceCoordOffset,
|
||||
IteratorKind iterKind /* = IteratorKind::Sync */,
|
||||
bool allowSelfHosted /* = false */) {
|
||||
MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
|
||||
".next() iteration is prohibited in self-hosted code because it "
|
||||
// TODO: migrate Module code to cpp, to avoid having the extra check here.
|
||||
MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting ||
|
||||
(sc->isModuleContext() && sc->asModuleContext()->isAsync()),
|
||||
".next() iteration is prohibited in non-module self-hosted code "
|
||||
"because it"
|
||||
"can run user-modifiable iteration code");
|
||||
|
||||
// [stack] ... NEXT ITER
|
||||
|
@ -225,6 +225,22 @@ AsyncFunctionGeneratorObject* AsyncFunctionGeneratorObject::create(
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSFunction* NewHandler(JSContext* cx, Native handler,
|
||||
JS::Handle<JSObject*> target) {
|
||||
cx->check(target);
|
||||
|
||||
JS::Handle<PropertyName*> funName = cx->names().empty;
|
||||
JS::Rooted<JSFunction*> handlerFun(
|
||||
cx, NewNativeFunction(cx, handler, 0, funName,
|
||||
gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
|
||||
if (!handlerFun) {
|
||||
return nullptr;
|
||||
}
|
||||
handlerFun->setExtendedSlot(FunctionExtended::MODULE_SLOT,
|
||||
JS::ObjectValue(*target));
|
||||
return handlerFun;
|
||||
}
|
||||
|
||||
AsyncFunctionGeneratorObject* AsyncFunctionGeneratorObject::create(
|
||||
JSContext* cx, HandleModuleObject module) {
|
||||
// TODO: Module is currently hitching a ride with
|
||||
@ -247,6 +263,23 @@ AsyncFunctionGeneratorObject* AsyncFunctionGeneratorObject::create(
|
||||
}
|
||||
obj->initFixedSlot(PROMISE_SLOT, ObjectValue(*resultPromise));
|
||||
|
||||
RootedObject onFulfilled(
|
||||
cx, NewHandler(cx, AsyncModuleExecutionFulfilledHandler, module));
|
||||
if (!onFulfilled) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject onRejected(
|
||||
cx, NewHandler(cx, AsyncModuleExecutionRejectedHandler, module));
|
||||
if (!onRejected) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS::AddPromiseReactionsIgnoringUnhandledRejection(
|
||||
cx, resultPromise, onFulfilled, onRejected)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Starts in the running state.
|
||||
obj->setResumeIndex(AbstractGeneratorObject::RESUME_INDEX_RUNNING);
|
||||
|
||||
|
@ -826,6 +826,9 @@ class FunctionExtended : public JSFunction {
|
||||
// asm.js module functions store their WasmModuleObject in the first slot.
|
||||
static const unsigned ASMJS_MODULE_SLOT = 0;
|
||||
|
||||
// Async module callback handlers store their ModuleObject in the first slot.
|
||||
static const unsigned MODULE_SLOT = 0;
|
||||
|
||||
static inline size_t offsetOfExtendedSlot(unsigned which) {
|
||||
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
|
||||
return offsetof(FunctionExtended, extendedSlots) +
|
||||
|
@ -1998,6 +1998,63 @@ static bool intrinsic_ExecuteModule(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return ModuleObject::execute(cx, module, args.rval());
|
||||
}
|
||||
|
||||
static bool intrinsic_GetAsyncCycleRoot(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
JSObject* result = js::GetAsyncCycleRoot(module);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool intrinsic_AppendAsyncParentModule(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
RootedModuleObject self(cx, &args[0].toObject().as<ModuleObject>());
|
||||
RootedModuleObject parent(cx, &args[1].toObject().as<ModuleObject>());
|
||||
return ModuleObject::appendAsyncParentModule(cx, self, parent);
|
||||
}
|
||||
|
||||
static bool intrinsic_CreateTopLevelCapability(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedModuleObject self(cx, &args[0].toObject().as<ModuleObject>());
|
||||
PromiseObject* result = ModuleObject::createTopLevelCapability(cx, self);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool intrinsic_ModuleTopLevelCapabilityResolve(JSContext* cx,
|
||||
unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
ModuleObject::topLevelCapabilityResolve(cx, module);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool intrinsic_ModuleTopLevelCapabilityReject(JSContext* cx,
|
||||
unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
HandleValue error = args[1];
|
||||
ModuleObject::topLevelCapabilityReject(cx, module, error);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool intrinsic_NewModuleNamespace(JSContext* cx, unsigned argc,
|
||||
Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
@ -2582,6 +2639,13 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("InstantiateModuleFunctionDeclarations",
|
||||
intrinsic_InstantiateModuleFunctionDeclarations, 1, 0),
|
||||
JS_FN("ExecuteModule", intrinsic_ExecuteModule, 1, 0),
|
||||
JS_FN("GetAsyncCycleRoot", intrinsic_GetAsyncCycleRoot, 1, 0),
|
||||
JS_FN("AppendAsyncParentModule", intrinsic_AppendAsyncParentModule, 2, 0),
|
||||
JS_FN("CreateTopLevelCapability", intrinsic_CreateTopLevelCapability, 1, 0),
|
||||
JS_FN("ModuleTopLevelCapabilityResolve",
|
||||
intrinsic_ModuleTopLevelCapabilityResolve, 1, 0),
|
||||
JS_FN("ModuleTopLevelCapabilityReject",
|
||||
intrinsic_ModuleTopLevelCapabilityReject, 2, 0),
|
||||
JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0),
|
||||
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4,
|
||||
0),
|
||||
|
Loading…
x
Reference in New Issue
Block a user