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:
yulia 2020-12-02 13:32:06 +00:00
parent da7e2ac90e
commit eb82ac67e4
9 changed files with 520 additions and 18 deletions

View File

@ -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);
}

View File

@ -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 = &currentModule->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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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) +

View File

@ -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),