Bug 911216 - Part 10: Support debugger hooks for creation and settling of promises. r=shu
authorTill Schneidereit <till@tillschneidereit.net>
Wed, 10 Feb 2016 23:09:14 +0100
changeset 289985 87c4e3921c4c419001c3ae554ab4249d3ee13c0a
parent 289984 33ad12d6ff452fa3f5f12f623a8837024706ab52
child 289986 e947c9941fe17266770e9f56f283f0d7628b2b65
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs911216
milestone48.0a1
Bug 911216 - Part 10: Support debugger hooks for creation and settling of promises. r=shu
js/src/builtin/Promise.cpp
js/src/builtin/Promise.js
js/src/builtin/TestingFunctions.cpp
js/src/vm/Debugger.cpp
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "builtin/Promise.h"
 
 #include "jscntxt.h"
 
 #include "gc/Heap.h"
+#include "js/Debug.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static const JSFunctionSpec promise_methods[] = {
@@ -160,16 +161,18 @@ PromiseObject::create(JSContext* cx, Han
         args.setCallee(rejectVal);
         args.setThis(UndefinedValue());
         args[0].set(exceptionVal);
 
         if (!Invoke(cx, args))
             return nullptr;
     }
 
+    JS::dbg::onNewPromise(cx, promise);
+
     // Step 11.
     return promise;
 }
 
 namespace js {
 
 // ES6, 25.4.3.1.
 bool
--- a/js/src/builtin/Promise.js
+++ b/js/src/builtin/Promise.js
@@ -143,16 +143,18 @@ function ResolvePromise(promise, valueOr
 
     // Step 6.
     UnsafeSetReservedSlot(promise, PROMISE_STATE_SLOT, state);
 
     // Also null out the resolve/reject functions so they can be GC'd.
     UnsafeSetReservedSlot(promise, PROMISE_RESOLVE_FUNCTION_SLOT, null);
     UnsafeSetReservedSlot(promise, PROMISE_REJECT_FUNCTION_SLOT, null);
 
+    _dbg_onPromiseSettled(promise);
+
     // Step 7.
     return TriggerPromiseReactions(reactions, valueOrReason);
 }
 
 // Used to verify that an object is a PromiseCapability record.
 var PromiseCapabilityRecordProto = {__proto__: null};
 
 // ES6, 25.4.1.5.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -18,16 +18,18 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJS.h"
 #include "asmjs/Wasm.h"
 #include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmTextToBinary.h"
+#include "builtin/Promise.h"
+#include "builtin/SelfHostingDefines.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitFrameIterator.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
@@ -1327,16 +1329,38 @@ OOMTest(JSContext* cx, unsigned argc, Va
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
+#ifdef SPIDERMONKEY_PROMISE
+static bool
+SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "settlePromiseNow", 1))
+        return false;
+    if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
+        JS_ReportError(cx, "first argument must be a Promise object");
+        return false;
+    }
+
+    RootedNativeObject promise(cx, &args[0].toObject().as<NativeObject>());
+    promise->setReservedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_FULFILLED));
+    promise->setReservedSlot(PROMISE_RESULT_SLOT, UndefinedValue());
+
+    JS::dbg::onPromiseSettled(cx, promise);
+    return true;
+}
+
+#else
+
 static const js::Class FakePromiseClass = {
     "Promise", JSCLASS_IS_ANONYMOUS
 };
 
 static bool
 MakeFakePromise(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1360,16 +1384,17 @@ SettleFakePromise(JSContext* cx, unsigne
         JS_ReportError(cx, "first argument must be a (fake) Promise object");
         return false;
     }
 
     RootedObject promise(cx, &args[0].toObject());
     JS::dbg::onPromiseSettled(cx, promise);
     return true;
 }
+#endif // SPIDERMONKEY_PROMISE
 
 static unsigned finalizeCount = 0;
 
 static void
 finalize_counter_finalize(JSFreeOp* fop, JSObject* obj)
 {
     ++finalizeCount;
 }
@@ -3542,28 +3567,37 @@ static const JSFunctionSpecWithHelp Test
 "  Test that the passed function behaves correctly under OOM conditions by\n"
 "  repeatedly executing it and simulating allocation failure at successive\n"
 "  allocations until the function completes without seeing a failure.\n"
 "  By default this tests that an exception is raised if execution fails, but\n"
 "  this can be disabled by passing false as the optional second parameter.\n"
 "  This is also disabled when --fuzzing-safe is specified."),
 #endif
 
+#ifdef SPIDERMONKEY_PROMISE
+    JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
+"settlePromiseNow(promise)",
+"  'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
+"  with a value of `undefined` and causes the firing of any onPromiseSettled\n"
+"  hooks set on Debugger instances that are observing the given promise's\n"
+"  global as a debuggee."),
+#else
     JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0,
 "makeFakePromise()",
 "  Create an object whose [[Class]] name is 'Promise' and call\n"
 "  JS::dbg::onNewPromise on it before returning it. It doesn't actually have\n"
 "  any of the other behavior associated with promises."),
 
     JS_FN_HELP("settleFakePromise", SettleFakePromise, 1, 0,
 "settleFakePromise(promise)",
 "  'Settle' a 'promise' created by makeFakePromise(). This doesn't have any\n"
 "  observable effects outside of firing any onPromiseSettled hooks set on\n"
 "  Debugger instances that are observing the given promise's global as a\n"
 "  debuggee."),
+#endif // SPIDERMONKEY_PROMISE
 
     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
 "makeFinalizeObserver()",
 "  Get a special object whose finalization increases the counter returned\n"
 "  by the finalizeCount function."),
 
     JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
 "finalizeCount()",
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -9142,18 +9142,22 @@ static inline void
 AssertIsPromise(JSContext* cx, HandleObject promise)
 {
     MOZ_ASSERT(promise);
     assertSameCompartment(cx, promise);
     MOZ_ASSERT(strcmp(promise->getClass()->name, "Promise") == 0);
 }
 
 JS_PUBLIC_API(void)
-JS::dbg::onNewPromise(JSContext* cx, HandleObject promise)
-{
+JS::dbg::onNewPromise(JSContext* cx, HandleObject promise_)
+{
+    RootedObject promise(cx, promise_);
+    if (IsWrapper(promise))
+        promise = UncheckedUnwrap(promise);
+    AutoCompartment ac(cx, promise);
     AssertIsPromise(cx, promise);
     Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
 }
 
 JS_PUBLIC_API(void)
 JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise)
 {
     AssertIsPromise(cx, promise);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1934,16 +1934,34 @@ intrinsic_ModuleNamespaceExports(JSConte
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     RootedModuleNamespaceObject namespace_(cx, &args[0].toObject().as<ModuleNamespaceObject>());
     args.rval().setObject(namespace_->exports());
     return true;
 }
 
+/**
+ * Intrinsic used to tell the debugger about settled promises.
+ *
+ * This is invoked both when resolving and rejecting promises, after the
+ * resulting state has been set on the promise, and it's up to the debugger
+ * to act on this signal in whichever way it wants.
+ */
+static bool
+intrinsic_onPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+    RootedObject promise(cx, &args[0].toObject());
+    JS::dbg::onPromiseSettled(cx, promise);
+    args.rval().setUndefined();
+    return true;
+}
+
 // The self-hosting global isn't initialized with the normal set of builtins.
 // Instead, individual C++-implemented functions that're required by
 // self-hosted code are defined as global functions. Accessing these
 // functions via a content compartment's builtins would be unsafe, because
 // content script might have changed the builtins' prototypes' members.
 // Installing the whole set of builtins in the self-hosting compartment, OTOH,
 // would be wasteful: it increases memory usage and initialization time for
 // self-hosting compartment.
@@ -2261,16 +2279,18 @@ static const JSFunctionSpec intrinsic_fu
           intrinsic_InstantiateModuleFunctionDeclarations, 1, 0),
     JS_FN("SetModuleEvaluated", intrinsic_SetModuleEvaluated, 1, 0),
     JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0),
     JS_FN("IsModuleNamespace", intrinsic_IsInstanceOfBuiltin<ModuleNamespaceObject>, 1, 0),
     JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0),
     JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
     JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
 
+    JS_FN("_dbg_onPromiseSettled", intrinsic_onPromiseSettled, 1, 0),
+
     JS_FS_END
 };
 
 void
 js::FillSelfHostingCompileOptions(CompileOptions& options)
 {
     /*
      * In self-hosting mode, scripts use JSOP_GETINTRINSIC instead of