Bug 1299887 Use a PromiseNativeHandler shim to ensure real PromiseNativeHandlers are released when the Promise settles. r=bz

This commit is contained in:
Ben Kelly 2016-09-06 14:38:13 -07:00
parent 2d66e62e03
commit e12fcacd53
4 changed files with 62 additions and 35 deletions

View File

@ -740,7 +740,7 @@ NativeHandlerCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
SLOT_NATIVEHANDLER));
MOZ_ASSERT(v.isObject());
PromiseNativeHandler* handler;
PromiseNativeHandler* handler = nullptr;
if (NS_FAILED(UNWRAP_OBJECT(PromiseNativeHandler, &v.toObject(),
handler))) {
return Throw(aCx, NS_ERROR_UNEXPECTED);
@ -781,6 +781,59 @@ CreateNativeHandlerFunction(JSContext* aCx, JS::Handle<JSObject*> aHolder,
return obj;
}
namespace {
class PromiseNativeHandlerShim final : public PromiseNativeHandler
{
RefPtr<PromiseNativeHandler> mInner;
~PromiseNativeHandlerShim()
{
}
public:
explicit PromiseNativeHandlerShim(PromiseNativeHandler* aInner)
: mInner(aInner)
{
MOZ_ASSERT(mInner);
}
void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
mInner->ResolvedCallback(aCx, aValue);
mInner = nullptr;
}
void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
mInner->RejectedCallback(aCx, aValue);
mInner = nullptr;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aWrapper)
{
return PromiseNativeHandlerBinding::Wrap(aCx, this, aGivenProto, aWrapper);
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeHandlerShim)
};
NS_IMPL_CYCLE_COLLECTION(PromiseNativeHandlerShim, mInner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeHandlerShim)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeHandlerShim)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeHandlerShim)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
} // anonymous namespace
void
Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
{
@ -793,11 +846,18 @@ Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
return;
}
// The self-hosted promise js may keep the object we pass to it alive
// for quite a while depending on when GC runs. Therefore, pass a shim
// object instead. The shim will free its inner PromiseNativeHandler
// after the promise has settled just like our previous c++ promises did.
RefPtr<PromiseNativeHandlerShim> shim =
new PromiseNativeHandlerShim(aRunnable);
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> handlerWrapper(cx);
// Note: PromiseNativeHandler is NOT wrappercached. So we can't use
// ToJSValue here, because it will try to do XPConnect wrapping on it, sadly.
if (NS_WARN_IF(!aRunnable->WrapObject(cx, nullptr, &handlerWrapper))) {
if (NS_WARN_IF(!shim->WrapObject(cx, nullptr, &handlerWrapper))) {
// Again, no way to report errors.
jsapi.ClearException();
return;

View File

@ -1,26 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/PromiseBinding.h"
namespace mozilla {
namespace dom {
#ifdef SPIDERMONKEY_PROMISE
bool
PromiseNativeHandler::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aWrapper)
{
return PromiseNativeHandlerBinding::Wrap(aCx, this, aGivenProto, aWrapper);
}
#endif // SPIDERMONKEY_PROMISE
} // namespace dom
} // namespace mozilla

View File

@ -30,12 +30,6 @@ public:
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
#ifdef SPIDERMONKEY_PROMISE
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aWrapper);
#endif // SPIDERMONKEY_PROMISE
};
} // namespace dom

View File

@ -15,7 +15,6 @@ UNIFIED_SOURCES += [
'Promise.cpp',
'PromiseCallback.cpp',
'PromiseDebugging.cpp',
'PromiseNativeHandler.cpp',
]
LOCAL_INCLUDES += [