mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1243001 part 6. Implement Promise::AppendNativeHandler in the SPIDERMONKEY_PROMISE world. r=peterv
This patch introduces a fake IDL interface just to get the benefits of cycle collection for the JS-to-C++ link we now need for PromiseNativeHandler (because the SpiderMonkey Promise somehow needs to point to the PromiseNativeHandler). Now in practice a bunch of our PromiseNativeHandlers are not cycle collected. That kinda freaks me out, but spot-checking a few suggests they do not in fact leak (either because they don't form cycles or because the Promise they're observing always settles and then releases them). Either way, that's a problem that exists with or without this patch...
This commit is contained in:
parent
3366af6c39
commit
fbe6dcf99f
@ -969,6 +969,10 @@ DOMInterfaces = {
|
||||
'concrete': False,
|
||||
},
|
||||
|
||||
'PromiseNativeHandler': {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'PropertyNodeList': {
|
||||
'headerFile': 'HTMLPropertiesCollection.h',
|
||||
},
|
||||
|
@ -698,10 +698,108 @@ Promise::MaybeReject(JSContext* aCx,
|
||||
}
|
||||
}
|
||||
|
||||
#define SLOT_NATIVEHANDLER 0
|
||||
#define SLOT_NATIVEHANDLER_TASK 1
|
||||
|
||||
enum class NativeHandlerTask : int32_t {
|
||||
Resolve,
|
||||
Reject
|
||||
};
|
||||
|
||||
static bool
|
||||
NativeHandlerCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
JS::Rooted<JS::Value> v(aCx,
|
||||
js::GetFunctionNativeReserved(&args.callee(),
|
||||
SLOT_NATIVEHANDLER));
|
||||
MOZ_ASSERT(v.isObject());
|
||||
|
||||
PromiseNativeHandler* handler;
|
||||
if (NS_FAILED(UNWRAP_OBJECT(PromiseNativeHandler, &v.toObject(),
|
||||
handler))) {
|
||||
return Throw(aCx, NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
v = js::GetFunctionNativeReserved(&args.callee(), SLOT_NATIVEHANDLER_TASK);
|
||||
NativeHandlerTask task = static_cast<NativeHandlerTask>(v.toInt32());
|
||||
|
||||
if (task == NativeHandlerTask::Resolve) {
|
||||
handler->ResolvedCallback(aCx, args.get(0));
|
||||
} else {
|
||||
MOZ_ASSERT(task == NativeHandlerTask::Reject);
|
||||
handler->RejectedCallback(aCx, args.get(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreateNativeHandlerFunction(JSContext* aCx, JS::Handle<JSObject*> aHolder,
|
||||
NativeHandlerTask aTask)
|
||||
{
|
||||
JSFunction* func = js::NewFunctionWithReserved(aCx, NativeHandlerCallback,
|
||||
/* nargs = */ 1,
|
||||
/* flags = */ 0, nullptr);
|
||||
if (!func) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
|
||||
|
||||
JS::ExposeObjectToActiveJS(aHolder);
|
||||
js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER,
|
||||
JS::ObjectValue(*aHolder));
|
||||
js::SetFunctionNativeReserved(obj, SLOT_NATIVEHANDLER_TASK,
|
||||
JS::Int32Value(static_cast<int32_t>(aTask)));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable)
|
||||
{
|
||||
// XXXbz Implementation coming up in the next diff.
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
// Our API doesn't allow us to return a useful error. Not like this should
|
||||
// happen anyway.
|
||||
return;
|
||||
}
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
|
||||
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))) {
|
||||
// Again, no way to report errors.
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> resolveFunc(cx);
|
||||
resolveFunc =
|
||||
CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Resolve);
|
||||
if (NS_WARN_IF(!resolveFunc)) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> rejectFunc(cx);
|
||||
rejectFunc =
|
||||
CreateNativeHandlerFunction(cx, handlerWrapper, NativeHandlerTask::Reject);
|
||||
if (NS_WARN_IF(!rejectFunc)) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> promiseObj(cx, GetWrapper());
|
||||
if (NS_WARN_IF(!JS::AddPromiseReactions(cx, promiseObj, resolveFunc,
|
||||
rejectFunc))) {
|
||||
jsapi.ClearException();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
26
dom/promise/PromiseNativeHandler.cpp
Normal file
26
dom/promise/PromiseNativeHandler.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/* -*- 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
|
||||
|
||||
|
@ -30,6 +30,12 @@ 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
|
||||
|
@ -14,7 +14,8 @@ EXPORTS.mozilla.dom += [
|
||||
UNIFIED_SOURCES += [
|
||||
'Promise.cpp',
|
||||
'PromiseCallback.cpp',
|
||||
'PromiseDebugging.cpp'
|
||||
'PromiseDebugging.cpp',
|
||||
'PromiseNativeHandler.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
@ -65,4 +65,11 @@ interface _Promise {
|
||||
// Need to escape "Promise" so it's treated as an identifier.
|
||||
interface _Promise {
|
||||
};
|
||||
|
||||
// Hack to allow us to have JS owning and properly tracing/CCing/etc a
|
||||
// PromiseNativeHandler.
|
||||
[NoInterfaceObject,
|
||||
Exposed=(Window,Worker,System)]
|
||||
interface PromiseNativeHandler {
|
||||
};
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
Loading…
Reference in New Issue
Block a user