Bug 1170760 part 6. Fix GetDependentPromise to deal with a situation when someone called then() and passed it the resolve/reject functions that come from a promise's constructor. r=baku

This commit is contained in:
Boris Zbarsky 2015-11-25 15:48:09 -05:00
Родитель 7e919bdf2d
Коммит 2296e841e7
4 изменённых файлов: 48 добавлений и 16 удалений

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

@ -115,11 +115,6 @@ private:
NS_DECL_OWNINGTHREAD;
};
enum {
SLOT_PROMISE = 0,
SLOT_DATA
};
/*
* Utilities for thenable callbacks.
*
@ -134,9 +129,9 @@ void
LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
JS::Handle<JSObject*> aRejectFunc)
{
js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
js::SetFunctionNativeReserved(aResolveFunc, Promise::SLOT_DATA,
JS::ObjectValue(*aRejectFunc));
js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
js::SetFunctionNativeReserved(aRejectFunc, Promise::SLOT_DATA,
JS::ObjectValue(*aResolveFunc));
}
@ -147,18 +142,22 @@ LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
bool
MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
{
JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
JS::Value otherFuncVal =
js::GetFunctionNativeReserved(aFunc, Promise::SLOT_DATA);
if (!otherFuncVal.isObject()) {
return false;
}
JSObject* otherFuncObj = &otherFuncVal.toObject();
MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj,
Promise::SLOT_DATA).isObject());
// Break both references.
js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
js::SetFunctionNativeReserved(aFunc, Promise::SLOT_DATA,
JS::UndefinedValue());
js::SetFunctionNativeReserved(otherFuncObj, Promise::SLOT_DATA,
JS::UndefinedValue());
return true;
}
@ -166,7 +165,8 @@ MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
Promise*
GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
{
JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc,
Promise::SLOT_PROMISE);
MOZ_ASSERT(promiseVal.isObject());
@ -700,6 +700,8 @@ Promise::JSCallbackThenableRejecter(JSContext* aCx,
/* static */ JSObject*
Promise::CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask)
{
// If this function ever changes, make sure to update
// WrapperPromiseCallback::GetDependentPromise.
JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
1 /* nargs */, 0 /* flags */,
nullptr);

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

@ -214,6 +214,11 @@ public:
static void
DispatchToMicroTask(nsIRunnable* aRunnable);
enum JSCallbackSlots {
SLOT_PROMISE = 0,
SLOT_DATA
};
protected:
struct PromiseCapability;

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

@ -9,6 +9,8 @@
#include "mozilla/dom/PromiseNativeHandler.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
namespace mozilla {
namespace dom {
@ -309,6 +311,32 @@ WrapperPromiseCallback::Call(JSContext* aCx,
return NS_OK;
}
Promise*
WrapperPromiseCallback::GetDependentPromise()
{
// Per spec, various algorithms like all() and race() are actually implemented
// in terms of calling then() but passing it the resolve/reject functions that
// are passed as arguments to function passed to the Promise constructor.
// That will cause the promise in question to hold on to a
// WrapperPromiseCallback, but the dependent promise should really be the one
// whose constructor those functions came from, not the about-to-be-ignored
// return value of "then". So try to determine whether we're in that case and
// if so go ahead and dig the dependent promise out of the function we have.
JSObject* callable = mCallback->Callable();
// Unwrap it, in case it's a cross-compartment wrapper. Our caller here is
// system, so it's really ok to just go and unwrap.
callable = js::UncheckedUnwrap(callable);
if (JS_IsNativeFunction(callable, Promise::JSCallback)) {
JS::Value promiseVal =
js::GetFunctionNativeReserved(callable, Promise::SLOT_PROMISE);
Promise* promise;
UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
return promise;
}
return mNextPromise;
}
// NativePromiseCallback
NS_IMPL_CYCLE_COLLECTION_INHERITED(NativePromiseCallback,

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

@ -57,10 +57,7 @@ public:
nsresult Call(JSContext* aCx,
JS::Handle<JS::Value> aValue) override;
Promise* GetDependentPromise() override
{
return mNextPromise;
}
Promise* GetDependentPromise() override;
WrapperPromiseCallback(Promise* aNextPromise, JS::Handle<JSObject*> aGlobal,
AnyCallback* aCallback);