mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
Issue #2089 - Turn AsyncGeneratorResumeNext recursion into iteration to avoid stack overflow
Based-on: m-c 1416809
This commit is contained in:
parent
6e2201d3a8
commit
55eacd43a4
3 changed files with 188 additions and 125 deletions
|
|
@ -3008,47 +3008,21 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
|
|||
return true;
|
||||
}
|
||||
|
||||
enum class ResumeNextKind {
|
||||
Enqueue, Reject, Resolve
|
||||
};
|
||||
|
||||
static MOZ_MUST_USE bool
|
||||
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
|
||||
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
ResumeNextKind kind, HandleValue valueOrException = UndefinedHandleValue,
|
||||
bool done = false);
|
||||
|
||||
// Async Iteration proposal 11.4.3.3.
|
||||
MOZ_MUST_USE bool
|
||||
js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
HandleValue value, bool done)
|
||||
{
|
||||
// Step 1 (implicit).
|
||||
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
|
||||
|
||||
// Step 4.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
RootedObject resultPromise(cx, request->promise());
|
||||
|
||||
asyncGenObj->cacheRequest(request);
|
||||
|
||||
// Step 6.
|
||||
RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
|
||||
if (!resultObj)
|
||||
return false;
|
||||
|
||||
RootedValue resultValue(cx, ObjectValue(*resultObj));
|
||||
|
||||
// Step 7.
|
||||
if (!ResolvePromiseInternal(cx, resultPromise, resultValue))
|
||||
return false;
|
||||
|
||||
// Step 8.
|
||||
if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
|
||||
return false;
|
||||
|
||||
// Step 9.
|
||||
return true;
|
||||
return AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Resolve, value, done);
|
||||
}
|
||||
|
||||
// Async Iteration proposal 11.4.3.4.
|
||||
|
|
@ -3056,123 +3030,188 @@ MOZ_MUST_USE bool
|
|||
js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
HandleValue exception)
|
||||
{
|
||||
// Step 1 (implicit).
|
||||
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
|
||||
|
||||
// Step 4.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
RootedObject resultPromise(cx, request->promise());
|
||||
|
||||
asyncGenObj->cacheRequest(request);
|
||||
|
||||
// Step 6.
|
||||
if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
|
||||
return false;
|
||||
|
||||
// Step 7.
|
||||
if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
|
||||
return false;
|
||||
|
||||
// Step 8.
|
||||
return true;
|
||||
return AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Reject, exception);
|
||||
}
|
||||
|
||||
// Async Iteration proposal 11.4.3.5.
|
||||
static MOZ_MUST_USE bool
|
||||
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
|
||||
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
|
||||
ResumeNextKind kind,
|
||||
HandleValue valueOrException_ /* = UndefinedHandleValue */,
|
||||
bool done /* = false */)
|
||||
{
|
||||
// Step 1 (implicit).
|
||||
RootedValue valueOrException(cx, valueOrException_);
|
||||
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isExecuting());
|
||||
while (true) {
|
||||
switch (kind) {
|
||||
case ResumeNextKind::Enqueue:
|
||||
// No further action required.
|
||||
break;
|
||||
case ResumeNextKind::Reject: {
|
||||
// 11.4.3.4 AsyncGeneratorReject ( generator, exception )
|
||||
HandleValue exception = valueOrException;
|
||||
|
||||
// Step 4.
|
||||
if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn())
|
||||
return true;
|
||||
// Step 1 (implicit).
|
||||
|
||||
// Steps 5-6.
|
||||
if (asyncGenObj->isQueueEmpty())
|
||||
return true;
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
|
||||
|
||||
// Steps 7-8.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
// Step 4.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Step 9.
|
||||
CompletionKind completionKind = request->completionKind();
|
||||
// Step 5.
|
||||
RootedObject resultPromise(cx, request->promise());
|
||||
|
||||
// Step 10.
|
||||
if (completionKind != CompletionKind::Normal) {
|
||||
// Step 10.a.
|
||||
if (asyncGenObj->isSuspendedStart())
|
||||
asyncGenObj->setCompleted();
|
||||
asyncGenObj->cacheRequest(request);
|
||||
|
||||
// Step 10.b.
|
||||
if (asyncGenObj->isCompleted()) {
|
||||
RootedValue value(cx, request->completionValue());
|
||||
// Step 6.
|
||||
if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
|
||||
return false;
|
||||
|
||||
// Step 10.b.i.
|
||||
if (completionKind == CompletionKind::Return) {
|
||||
// Steps 10.b.i.1.
|
||||
asyncGenObj->setAwaitingReturn();
|
||||
// Steps 7-8.
|
||||
break;
|
||||
}
|
||||
case ResumeNextKind::Resolve: {
|
||||
// 11.4.3.3 AsyncGeneratorResolve ( generator, value, done )
|
||||
HandleValue value = valueOrException;
|
||||
|
||||
// Steps 10.b.i.4-6 (reordered).
|
||||
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled));
|
||||
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnRejected));
|
||||
// Step 1 (implicit).
|
||||
|
||||
// Steps 10.b.i.2-3, 7-10.
|
||||
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncGenerator(asyncGenObj);
|
||||
};
|
||||
return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra);
|
||||
}
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
|
||||
|
||||
// Step 10.b.ii.1.
|
||||
MOZ_ASSERT(completionKind == CompletionKind::Throw);
|
||||
// Step 4.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Steps 10.b.ii.2-3.
|
||||
return AsyncGeneratorReject(cx, asyncGenObj, value);
|
||||
// Step 5.
|
||||
RootedObject resultPromise(cx, request->promise());
|
||||
|
||||
asyncGenObj->cacheRequest(request);
|
||||
|
||||
// Step 6.
|
||||
RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
|
||||
if (!resultObj)
|
||||
return false;
|
||||
|
||||
RootedValue resultValue(cx, ObjectValue(*resultObj));
|
||||
|
||||
// Step 7.
|
||||
if (!ResolvePromiseInternal(cx, resultPromise, resultValue))
|
||||
return false;
|
||||
|
||||
// Steps 8-9.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (asyncGenObj->isCompleted()) {
|
||||
// Step 11.
|
||||
return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true);
|
||||
|
||||
// Step 1 (implicit).
|
||||
|
||||
// Steps 2-3.
|
||||
MOZ_ASSERT(!asyncGenObj->isExecuting());
|
||||
|
||||
// Step 4.
|
||||
if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn())
|
||||
return true;
|
||||
|
||||
// Steps 5-6.
|
||||
if (asyncGenObj->isQueueEmpty())
|
||||
return true;
|
||||
|
||||
// Steps 7-8.
|
||||
Rooted<AsyncGeneratorRequest*> request(
|
||||
cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj));
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Step 9.
|
||||
CompletionKind completionKind = request->completionKind();
|
||||
|
||||
// Step 10.
|
||||
if (completionKind != CompletionKind::Normal) {
|
||||
// Step 10.a.
|
||||
if (asyncGenObj->isSuspendedStart())
|
||||
asyncGenObj->setCompleted();
|
||||
|
||||
// Step 10.b.
|
||||
if (asyncGenObj->isCompleted()) {
|
||||
RootedValue value(cx, request->completionValue());
|
||||
|
||||
// Step 10.b.i.
|
||||
if (completionKind == CompletionKind::Return) {
|
||||
// Steps 10.b.i.1.
|
||||
asyncGenObj->setAwaitingReturn();
|
||||
|
||||
// Steps 10.b.i.4-6 (reordered).
|
||||
static constexpr int32_t ResumeNextReturnFulfilled =
|
||||
PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled;
|
||||
static constexpr int32_t ResumeNextReturnRejected =
|
||||
PromiseHandlerAsyncGeneratorResumeNextReturnRejected;
|
||||
|
||||
RootedValue onFulfilled(cx, Int32Value(ResumeNextReturnFulfilled));
|
||||
RootedValue onRejected(cx, Int32Value(ResumeNextReturnRejected));
|
||||
|
||||
// Steps 10.b.i.2-3, 7-10.
|
||||
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncGenerator(asyncGenObj);
|
||||
};
|
||||
return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra);
|
||||
}
|
||||
|
||||
// Step 10.b.ii.1.
|
||||
MOZ_ASSERT(completionKind == CompletionKind::Throw);
|
||||
|
||||
// Steps 10.b.ii.2-3.
|
||||
kind = ResumeNextKind::Reject;
|
||||
valueOrException.set(value);
|
||||
// |done| is unused for ResumeNextKind::Reject.
|
||||
continue;
|
||||
}
|
||||
} else if (asyncGenObj->isCompleted()) {
|
||||
// Step 11.
|
||||
kind = ResumeNextKind::Resolve;
|
||||
valueOrException.setUndefined();
|
||||
done = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Step 12.
|
||||
MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield());
|
||||
|
||||
// Step 16 (reordered).
|
||||
asyncGenObj->setExecuting();
|
||||
|
||||
RootedValue argument(cx, request->completionValue());
|
||||
|
||||
if (completionKind == CompletionKind::Return) {
|
||||
// 11.4.3.7 AsyncGeneratorYield step 8.b-e.
|
||||
// Since we don't have the place that handles return from yield
|
||||
// inside the generator, handle the case here, with extra state
|
||||
// State_AwaitingYieldReturn.
|
||||
asyncGenObj->setAwaitingYieldReturn();
|
||||
|
||||
static constexpr int32_t YieldReturnAwaitedFulfilled =
|
||||
PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled;
|
||||
static constexpr int32_t YieldReturnAwaitedRejected =
|
||||
PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected;
|
||||
|
||||
RootedValue onFulfilled(cx, Int32Value(YieldReturnAwaitedFulfilled));
|
||||
RootedValue onRejected(cx, Int32Value(YieldReturnAwaitedRejected));
|
||||
|
||||
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncGenerator(asyncGenObj);
|
||||
};
|
||||
return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra);
|
||||
}
|
||||
|
||||
// Steps 13-15, 17-21.
|
||||
return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument);
|
||||
}
|
||||
|
||||
// Step 12.
|
||||
MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield());
|
||||
|
||||
// Step 16 (reordered).
|
||||
asyncGenObj->setExecuting();
|
||||
|
||||
RootedValue argument(cx, request->completionValue());
|
||||
|
||||
if (completionKind == CompletionKind::Return) {
|
||||
// 11.4.3.7 AsyncGeneratorYield step 8.b-e.
|
||||
// Since we don't have the place that handles return from yield
|
||||
// inside the generator, handle the case here, with extra state
|
||||
// State_AwaitingYieldReturn.
|
||||
asyncGenObj->setAwaitingYieldReturn();
|
||||
|
||||
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled));
|
||||
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected));
|
||||
|
||||
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
|
||||
reaction->setIsAsyncGenerator(asyncGenObj);
|
||||
};
|
||||
return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra);
|
||||
}
|
||||
|
||||
// Steps 13-15, 17-21.
|
||||
return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument);
|
||||
}
|
||||
|
||||
// Async Iteration proposal 11.4.3.6.
|
||||
|
|
@ -3221,7 +3260,7 @@ js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
|
|||
// Step 7.
|
||||
if (!asyncGenObj->isExecuting()) {
|
||||
// Step 8.
|
||||
if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
|
||||
if (!AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Enqueue))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
12
js/src/jit-test/tests/auto-regress/bug1416809-1.js
Normal file
12
js/src/jit-test/tests/auto-regress/bug1416809-1.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// https://tc39.github.io/proposal-async-iteration
|
||||
|
||||
// Recursion between:
|
||||
// 11.4.3.3 AsyncGeneratorResolve, step 8
|
||||
// 11.4.3.5 AsyncGeneratorResumeNext, step 11.
|
||||
|
||||
var asyncIter = async function*(){ yield; }();
|
||||
asyncIter.next();
|
||||
|
||||
for (var i = 0; i < 20000; i++) {
|
||||
asyncIter.next();
|
||||
}
|
||||
12
js/src/jit-test/tests/auto-regress/bug1416809-2.js
Normal file
12
js/src/jit-test/tests/auto-regress/bug1416809-2.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// https://tc39.github.io/proposal-async-iteration
|
||||
|
||||
// Recursion between:
|
||||
// 11.4.3.4 AsyncGeneratorReject, step 7.
|
||||
// 11.4.3.5 AsyncGeneratorResumeNext, step 10.b.ii.2.
|
||||
|
||||
var asyncIter = async function*(){ yield; }();
|
||||
asyncIter.next();
|
||||
|
||||
for (var i = 0; i < 20000; i++) {
|
||||
asyncIter.throw();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue