mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1170760 part 1. Introduce a PromiseCapability struct. r=baku,efaust
This commit is contained in:
parent
090da7d0a5
commit
3630c9e53b
@ -319,6 +319,108 @@ private:
|
|||||||
RefPtr<Promise> mNextPromise;
|
RefPtr<Promise> mNextPromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A struct implementing
|
||||||
|
// <http://www.ecma-international.org/ecma-262/6.0/#sec-promisecapability-records>.
|
||||||
|
// While the spec holds on to these in some places, in practice those places
|
||||||
|
// don't actually need everything from this struct, so we explicitly grab
|
||||||
|
// members from it as needed in those situations. That allows us to make this a
|
||||||
|
// stack-only struct and keep the rooting simple.
|
||||||
|
//
|
||||||
|
// We also add an optimization for the (common) case when we discover that the
|
||||||
|
// Promise constructor we're supposed to use is in fact the canonical Promise
|
||||||
|
// constructor. In that case we will just set mNativePromise in our
|
||||||
|
// PromiseCapability and not set mPromise/mResolve/mReject; the correct
|
||||||
|
// callbacks will be the standard Promise ones, and we don't really want to
|
||||||
|
// synthesize JSFunctions for them in that situation.
|
||||||
|
struct MOZ_STACK_CLASS Promise::PromiseCapability
|
||||||
|
{
|
||||||
|
explicit PromiseCapability(JSContext* aCx)
|
||||||
|
: mPromise(aCx)
|
||||||
|
, mResolve(aCx)
|
||||||
|
, mReject(aCx)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Take an exception on aCx and try to convert it into a promise rejection.
|
||||||
|
// Note that this can result in a new exception being thrown on aCx, or an
|
||||||
|
// exception getting thrown on aRv. On entry to this method, aRv is assumed
|
||||||
|
// to not be a failure. This should only be called if NewPromiseCapability
|
||||||
|
// succeeded on this PromiseCapability.
|
||||||
|
void RejectWithException(JSContext* aCx, ErrorResult& aRv);
|
||||||
|
|
||||||
|
// Return a JS::Value representing the promise. This should only be called if
|
||||||
|
// NewPromiseCapability succeeded on this PromiseCapability. It makes no
|
||||||
|
// guarantees about compartments (e.g. in the mNativePromise case it's in the
|
||||||
|
// compartment of the reflector, but in the mPromise case it might be in the
|
||||||
|
// compartment of some cross-compartment wrapper for a reflector).
|
||||||
|
JS::Value PromiseValue() const;
|
||||||
|
|
||||||
|
// All the JS::Value fields of this struct are actually objects, but for our
|
||||||
|
// purposes it's simpler to store them as JS::Value.
|
||||||
|
|
||||||
|
// [[Promise]].
|
||||||
|
JS::Rooted<JS::Value> mPromise;
|
||||||
|
// [[Resolve]]. Value in the context compartment.
|
||||||
|
JS::Rooted<JS::Value> mResolve;
|
||||||
|
// [[Reject]]. Value in the context compartment.
|
||||||
|
JS::Rooted<JS::Value> mReject;
|
||||||
|
// If mNativePromise is non-null, we should use it, not mPromise.
|
||||||
|
RefPtr<Promise> mNativePromise;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// We don't want to allow creation of temporaries of this type, ever.
|
||||||
|
PromiseCapability(const PromiseCapability&) = delete;
|
||||||
|
PromiseCapability(PromiseCapability&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
Promise::PromiseCapability::RejectWithException(JSContext* aCx,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
// This method basically implements
|
||||||
|
// http://www.ecma-international.org/ecma-262/6.0/#sec-ifabruptrejectpromise
|
||||||
|
// or at least the parts of it that happen if we have an abrupt completion.
|
||||||
|
|
||||||
|
MOZ_ASSERT(!aRv.Failed());
|
||||||
|
MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
|
||||||
|
"NewPromiseCapability didn't succeed");
|
||||||
|
|
||||||
|
JS::Rooted<JS::Value> exn(aCx);
|
||||||
|
if (!JS_GetPendingException(aCx, &exn)) {
|
||||||
|
// This is an uncatchable exception, so can't be converted into a rejection.
|
||||||
|
// Just rethrow that on aRv.
|
||||||
|
aRv.ThrowUncatchableException();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_ClearPendingException(aCx);
|
||||||
|
|
||||||
|
// If we have a native promise, just reject it without trying to call out into
|
||||||
|
// JS.
|
||||||
|
if (mNativePromise) {
|
||||||
|
mNativePromise->MaybeRejectInternal(aCx, exn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Rooted<JS::Value> ignored(aCx);
|
||||||
|
if (!JS::Call(aCx, JS::UndefinedHandleValue, mReject, JS::HandleValueArray(exn),
|
||||||
|
&ignored)) {
|
||||||
|
aRv.NoteJSContextException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value
|
||||||
|
Promise::PromiseCapability::PromiseValue() const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mNativePromise || !mPromise.isUndefined(),
|
||||||
|
"NewPromiseCapability didn't succeed");
|
||||||
|
|
||||||
|
if (mNativePromise) {
|
||||||
|
return JS::ObjectValue(*mNativePromise->GetWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mPromise;
|
||||||
|
}
|
||||||
|
|
||||||
// Promise
|
// Promise
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
|
||||||
|
@ -212,6 +212,8 @@ public:
|
|||||||
DispatchToMicroTask(nsIRunnable* aRunnable);
|
DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
struct PromiseCapability;
|
||||||
|
|
||||||
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
||||||
// that from inside this class too, somehow.
|
// that from inside this class too, somehow.
|
||||||
explicit Promise(nsIGlobalObject* aGlobal);
|
explicit Promise(nsIGlobalObject* aGlobal);
|
||||||
|
Loading…
Reference in New Issue
Block a user