Bug 1315756 - Do not allocate throwawayCapability in AsyncFunctionAwait. r=till

This commit is contained in:
Tooru Fujisawa 2016-11-09 03:27:51 +09:00
Родитель 5b812dee62
Коммит 367ecab382
4 изменённых файлов: 167 добавлений и 121 удалений

Просмотреть файл

@ -173,6 +173,7 @@ enum ReactionRecordSlots {
#define REACTION_FLAG_RESOLVED 0x1
#define REACTION_FLAG_FULFILLED 0x2
#define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4
#define REACTION_FLAG_AWAIT 0x8
// ES2016, 25.4.1.2.
class PromiseReactionRecord : public NativeObject
@ -199,6 +200,15 @@ class PromiseReactionRecord : public NativeObject
flags |= REACTION_FLAG_FULFILLED;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
}
void setIsAwait() {
int32_t flags = this->flags();
flags |= REACTION_FLAG_AWAIT;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
}
bool isAwait() {
int32_t flags = this->flags();
return flags & REACTION_FLAG_AWAIT;
}
Value handler() {
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
uint32_t slot = targetState() == JS::PromiseState::Fulfilled
@ -808,6 +818,35 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat
return true;
}
static MOZ_MUST_USE bool
AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
MutableHandleValue rval)
{
MOZ_ASSERT(reaction->isAwait());
RootedValue handlerVal(cx, reaction->handler());
RootedValue argument(cx, reaction->handlerArg());
Rooted<PromiseObject*> resultPromise(cx, &reaction->promise()->as<PromiseObject>());
RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator));
int32_t handlerNum = int32_t(handlerVal.toNumber());
MOZ_ASSERT(handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED
|| handlerNum == PROMISE_HANDLER_AWAIT_REJECTED);
// Await's handlers don't return a value, nor throw exception.
// They fail only on OOM.
if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED) {
if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument))
return false;
} else {
if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument))
return false;
}
rval.setUndefined();
return true;
}
// ES2016, 25.4.2.1.
/**
* Callback triggering the fulfill/reject reaction for a resolved Promise,
@ -844,6 +883,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
// Steps 1-2.
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
if (reaction->isAwait())
return AwaitPromiseReactionJob(cx, reaction, args.rval());
// Step 3.
RootedValue handlerVal(cx, reaction->handler());
@ -860,32 +901,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
// Step 4.
if (handlerNum == PROMISE_HANDLER_IDENTITY) {
handlerResult = argument;
} else if (handlerNum == PROMISE_HANDLER_THROWER) {
} else {
// Step 5.
MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER);
resolutionMode = RejectMode;
handlerResult = argument;
} else {
MOZ_ASSERT(handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED
|| handlerNum == PROMISE_HANDLER_AWAIT_REJECTED);
Rooted<PromiseObject*> promiseObj(cx, &reaction->promise()->as<PromiseObject>());
MOZ_ASSERT(PromiseHasAnyFlag(*promiseObj, PROMISE_FLAG_AWAIT));
RootedValue generatorVal(cx, promiseObj->getFixedSlot(PromiseSlot_AwaitGenerator));
// Step 6.
// Optimized for await.
bool result;
if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED)
result = AsyncFunctionAwaitedFulfilled(cx, argument, generatorVal, &handlerResult);
else
result = AsyncFunctionAwaitedRejected(cx, argument, generatorVal, &handlerResult);
if (!result) {
resolutionMode = RejectMode;
if (!cx->isExceptionPending() || !GetAndClearException(cx, &handlerResult))
return false;
}
}
} else {
// Step 6.
@ -2088,19 +2108,58 @@ js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleVal
return resultPromise;
}
static MOZ_MUST_USE bool PerformPromiseThenImpl(JSContext* cx, Handle<PromiseObject*> promise,
HandleValue onFulfilled,
HandleValue onRejected,
HandleObject resultPromise,
HandleObject resolve,
HandleObject reject);
static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx,
Handle<PromiseObject*> promise,
Handle<PromiseReactionRecord*> reaction);
// Some async/await functions are implemented here instead of
// js/src/builtin/AsyncFunction.cpp, to call Promise internal functions.
// Async Functions proposal 1.1.8 and 1.2.14 step 1.
MOZ_MUST_USE PromiseObject*
js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
{
// Step 1.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
if (!promise)
return nullptr;
AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC);
promise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
return promise;
}
// Async Functions proposal 2.2 steps 3.f, 3.g.
MOZ_MUST_USE bool
js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
{
// Step 3.f.
RootedValue exc(cx);
if (!GetAndClearException(cx, &exc))
return false;
if (!RejectMaybeWrappedPromise(cx, resultPromise, exc))
return false;
// Step 3.g.
return true;
}
// Async Functions proposal 2.2 steps 3.d-e, 3.g.
MOZ_MUST_USE bool
js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
{
// Steps 3.d-e.
if (!ResolvePromiseInternal(cx, resultPromise, value))
return false;
// Step 3.g.
return true;
}
// Async Functions proposal 2.3 steps 2-8.
// Implemented here instead of js/src/builtin/AsyncFunction.cpp
// to call Promise internal functions
bool
js::AsyncFunctionAwait(JSContext* cx, HandleValue generatorVal, HandleValue value,
MutableHandleValue rval)
MOZ_MUST_USE bool
js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
{
// Step 2.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
@ -2115,24 +2174,22 @@ js::AsyncFunctionAwait(JSContext* cx, HandleValue generatorVal, HandleValue valu
RootedValue onFulfilled(cx, Int32Value(PROMISE_HANDLER_AWAIT_FULFILLED));
RootedValue onRejected(cx, Int32Value(PROMISE_HANDLER_AWAIT_REJECTED));
// Step 7.
Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectWithDefaultResolution(cx));
if (!resultPromise)
RootedObject incumbentGlobal(cx);
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
return false;
// Step 6 (reordered).
AddPromiseFlags(*resultPromise, PROMISE_FLAG_AWAIT);
resultPromise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
// Steps 6-7.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise,
onFulfilled, onRejected,
nullptr, nullptr,
incumbentGlobal));
if (!reaction)
return false;
reaction->setIsAwait();
// Step 8.
if (!PerformPromiseThenImpl(cx, promise, onFulfilled, onRejected, resultPromise,
nullptr, nullptr))
{
return false;
}
rval.setObject(*resultPromise);
return true;
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
// ES2016, 25.4.5.3.
@ -2201,15 +2258,6 @@ PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue on
if (!IsCallable(onRejected))
onRejected = Int32Value(PROMISE_HANDLER_THROWER);
return PerformPromiseThenImpl(cx, promise, onFulfilled, onRejected, resultPromise,
resolve, reject);
}
static MOZ_MUST_USE bool
PerformPromiseThenImpl(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled,
HandleValue onRejected, HandleObject resultPromise,
HandleObject resolve, HandleObject reject)
{
RootedObject incumbentGlobal(cx);
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
return false;
@ -2222,6 +2270,13 @@ PerformPromiseThenImpl(JSContext* cx, Handle<PromiseObject*> promise, HandleValu
if (!reaction)
return false;
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
static MOZ_MUST_USE bool
PerformPromiseThenWithReaction(JSContext* cx, Handle<PromiseObject*> promise,
Handle<PromiseReactionRecord*> reaction)
{
JS::PromiseState state = promise->state();
int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
if (state == JS::PromiseState::Pending) {
@ -2534,7 +2589,7 @@ PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> v
bool
PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
{
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_AWAIT));
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
if (state() != JS::PromiseState::Pending)
return true;
@ -2557,7 +2612,7 @@ PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
bool
PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
{
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_AWAIT));
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
if (state() != JS::PromiseState::Pending)
return true;

Просмотреть файл

@ -31,7 +31,7 @@ enum PromiseSlots {
#define PROMISE_FLAG_REPORTED 0x8
#define PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION 0x10
#define PROMISE_FLAG_DEFAULT_REJECT_FUNCTION 0x20
#define PROMISE_FLAG_AWAIT 0x40
#define PROMISE_FLAG_ASYNC 0x40
class AutoSetNewObjectMetadata;
@ -119,9 +119,17 @@ MOZ_MUST_USE JSObject*
OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled,
HandleValue onRejected);
bool
AsyncFunctionAwait(JSContext* cx, HandleValue generatorVal, HandleValue value,
MutableHandleValue rval);
MOZ_MUST_USE PromiseObject*
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);
MOZ_MUST_USE bool
AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
MOZ_MUST_USE bool
AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise);
MOZ_MUST_USE bool
AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
/**
* A PromiseTask represents a task that can be dispatched to a helper thread

Просмотреть файл

@ -47,7 +47,8 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
return true;
}
static bool AsyncFunctionStart(JSContext* cx, HandleValue generatorVal, MutableHandleValue rval);
static MOZ_MUST_USE bool AsyncFunctionStart(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal);
#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
@ -72,11 +73,21 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
for (size_t i = 0, len = argc; i < len; i++)
args2[i].set(args[i]);
if (Call(cx, unwrappedVal, thisValue, args2, &generatorVal)) {
// Steps 3, 5.
return AsyncFunctionStart(cx, generatorVal, args.rval());
// Step 1.
Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx, generatorVal));
if (!resultPromise)
return false;
// Step 3.
if (!AsyncFunctionStart(cx, resultPromise, generatorVal))
return false;
// Step 5.
args.rval().setObject(*resultPromise);
return true;
}
// Step 4.
// Steps 1, 4.
RootedValue exc(cx);
if (!GetAndClearException(cx, &exc))
return false;
@ -129,47 +140,19 @@ js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
return wrapped;
}
// Async Functions proposal 2.2 steps 3.f, 3.g.
static bool
AsyncFunctionThrown(JSContext* cx, MutableHandleValue rval)
{
// Step 3.f.
RootedValue exc(cx);
if (!GetAndClearException(cx, &exc))
return false;
RootedObject rejectPromise(cx, PromiseObject::unforgeableReject(cx, exc));
if (!rejectPromise)
return false;
// Step 3.g.
rval.setObject(*rejectPromise);
return true;
}
// Async Functions proposal 2.2 steps 3.d-e, 3.g.
static bool
AsyncFunctionReturned(JSContext* cx, HandleValue value, MutableHandleValue rval)
{
// Steps 3.d-e.
RootedObject resolveObj(cx, PromiseObject::unforgeableResolve(cx, value));
if (!resolveObj)
return false;
// Step 3.g.
rval.setObject(*resolveObj);
return true;
}
enum class ResumeKind {
Normal,
Throw
};
// Async Functions proposal 2.2 steps 3.f, 3.g.
// Async Functions proposal 2.2 steps 3.d-e, 3.g.
// Implemented in js/src/builtin/Promise.cpp
// Async Functions proposal 2.2 steps 3-8, 2.4 steps 2-7, 2.5 steps 2-7.
static bool
AsyncFunctionResume(JSContext* cx, HandleValue generatorVal, ResumeKind kind,
HandleValue valueOrReason, MutableHandleValue rval)
AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal,
ResumeKind kind, HandleValue valueOrReason)
{
// Execution context switching is handled in generator.
HandlePropertyName funName = kind == ResumeKind::Normal
@ -179,7 +162,7 @@ AsyncFunctionResume(JSContext* cx, HandleValue generatorVal, ResumeKind kind,
args[0].set(valueOrReason);
RootedValue result(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
return AsyncFunctionThrown(cx, rval);
return AsyncFunctionThrown(cx, resultPromise);
RootedObject resultObj(cx, &result.toObject());
RootedValue doneVal(cx);
@ -190,41 +173,41 @@ AsyncFunctionResume(JSContext* cx, HandleValue generatorVal, ResumeKind kind,
return false;
if (doneVal.toBoolean())
return AsyncFunctionReturned(cx, value, rval);
return AsyncFunctionReturned(cx, resultPromise, value);
return AsyncFunctionAwait(cx, generatorVal, value, rval);
return AsyncFunctionAwait(cx, resultPromise, value);
}
// Async Functions proposal 2.2 steps 3-8.
static bool
AsyncFunctionStart(JSContext* cx, HandleValue generatorVal, MutableHandleValue rval)
static MOZ_MUST_USE bool
AsyncFunctionStart(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal)
{
return AsyncFunctionResume(cx, generatorVal, ResumeKind::Normal, UndefinedHandleValue, rval);
return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Normal, UndefinedHandleValue);
}
// Async Functions proposal 2.3 steps 1-8.
// Implemented in js/src/builtin/Promise.cpp
// Async Functions proposal 2.4.
bool
js::AsyncFunctionAwaitedFulfilled(JSContext* cx, HandleValue value, HandleValue generatorVal,
MutableHandleValue rval)
MOZ_MUST_USE bool
js::AsyncFunctionAwaitedFulfilled(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue value)
{
// Step 1 (implicit).
// Steps 2-7.
return AsyncFunctionResume(cx, generatorVal, ResumeKind::Normal, value, rval);
return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Normal, value);
}
// Async Functions proposal 2.5.
bool
js::AsyncFunctionAwaitedRejected(JSContext* cx, HandleValue reason, HandleValue generatorVal,
MutableHandleValue rval)
MOZ_MUST_USE bool
js::AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason)
{
// Step 1 (implicit).
// Step 2-7.
return AsyncFunctionResume(cx, generatorVal, ResumeKind::Throw, reason, rval);
return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Throw, reason);
}
JSFunction*

Просмотреть файл

@ -24,13 +24,13 @@ IsWrappedAsyncFunction(JSFunction* fun);
JSObject*
WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped);
bool
AsyncFunctionAwaitedFulfilled(JSContext* cx, HandleValue value, HandleValue generatorVal,
MutableHandleValue rval);
MOZ_MUST_USE bool
AsyncFunctionAwaitedFulfilled(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue value);
bool
AsyncFunctionAwaitedRejected(JSContext* cx, HandleValue reason, HandleValue generatorVal,
MutableHandleValue rval);
MOZ_MUST_USE bool
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason);
} // namespace js