// Utility for debugger
+ function PromiseHasRejectHandlerRecursive(promise) {
+ var queue = GET_PRIVATE(promise, promiseOnReject);
+ if (IS_UNDEFINED(queue)) return false;
+ // Do a depth first search for a reject handler that's not
+ // the default PromiseIdRejectHandler.
+ for (var i = 0; i < queue.length; i += 2) {
+ if (queue[i] != PromiseIdRejectHandler) return true;
+ if (PromiseHasRejectHandlerRecursive(queue[i + 1])) return true;
+ }
+ return false;
+ }
+
PromiseHasRejectHandler = function PromiseHasRejectHandler() {
// Mark promise as already having triggered a reject event.
SET_PRIVATE(this, promiseDebug, true);
- var queue = GET_PRIVATE(this, promiseOnReject);
- return !IS_UNDEFINED(queue) && queue.length > 0;
+ return PromiseHasRejectHandlerRecursive(this);
};
// -------------------------------------------------------------------
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug --allow-natives-syntax
+
+// Test debug events when we only listen to uncaught exceptions and
+// there is a catch handler for the to-be-rejected Promise.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var expected_events = 2;
+var log = [];
+
+var resolve, reject;
+var p0 = new Promise(function(res, rej) { resolve = res; reject = rej; });
+var p1 = p0.then(function() {
+ log.push("p0.then");
+ return Promise.reject(new Error("123"));
+});
+var p2 = p1.then(function() {
+ log.push("p1.then");
+});
+
+var q = new Promise(function(res, rej) {
+ log.push("resolve q");
+ res();
+});
+
+q.then(function() {
+ log.push("resolve p");
+ resolve();
+})
+
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Exception) {
+ expected_events--;
+ assertTrue(expected_events >= 0);
+ assertTrue(event_data.uncaught());
+ assertTrue(event_data.promise() instanceof Promise);
+ if (expected_events == 1) {
+ // p1 is rejected, uncaught except for its default reject handler.
+ assertEquals(0, exec_state.frameCount());
+ assertSame(p1, event_data.promise());
+ } else {
+ // p2 is rejected by p1's default reject handler.
+ assertEquals(0, exec_state.frameCount());
+ assertSame(p2, event_data.promise());
+ }
+ }
+ } catch (e) {
+ %AbortJS(e + "\n" + e.stack);
+ }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+ function checkResult() {
+ try {
+ assertTrue(iteration < 10);
+ if (expected_events === 0) {
+ assertEquals(["resolve q", "end main", "resolve p", "p0.then"], log);
+ } else {
+ testDone(iteration + 1);
+ }
+ } catch (e) {
+ %AbortJS(e + "\n" + e.stack);
+ }
+ }
+
+ // Run testDone through the Object.observe processing loop.
+ var dummy = {};
+ Object.observe(dummy, checkResult);
+ dummy.dummy = dummy;
+}
+
+testDone(0);
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug --allow-natives-syntax
+
+// Test debug events when we only listen to uncaught exceptions and
+// there is a catch handler for the to-be-rejected Promise.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var expected_events = 2;
+var log = [];
+
+var resolve, reject;
+var p0 = new Promise(function(res, rej) { resolve = res; reject = rej; });
+var p1 = p0.then(function() {
+ log.push("p0.then");
+ throw new Error("123"); // event
+});
+var p2 = p1.then(function() {
+ log.push("p1.then");
+});
+
+var q = new Promise(function(res, rej) {
+ log.push("resolve q");
+ res();
+});
+
+q.then(function() {
+ log.push("resolve p");
+ resolve();
+})
+
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Exception) {
+ expected_events--;
+ assertTrue(expected_events >= 0);
+ assertTrue(event_data.uncaught());
+ assertTrue(event_data.promise() instanceof Promise);
+ if (expected_events == 1) {
+ // p1 is rejected, uncaught except for its default reject handler.
+ assertTrue(
+ exec_state.frame(0).sourceLineText().indexOf("// event") > 0);
+ assertSame(p1, event_data.promise());
+ } else {
+ // p2 is rejected by p1's default reject handler.
+ assertEquals(0, exec_state.frameCount());
+ assertSame(p2, event_data.promise());
+ }
+ }
+ } catch (e) {
+ %AbortJS(e + "\n" + e.stack);
+ }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+ function checkResult() {
+ try {
+ assertTrue(iteration < 10);
+ if (expected_events === 0) {
+ assertEquals(["resolve q", "end main", "resolve p", "p0.then"], log);
+ } else {
+ testDone(iteration + 1);
+ }
+ } catch (e) {
+ %AbortJS(e + "\n" + e.stack);
+ }
+ }
+
+ // Run testDone through the Object.observe processing loop.
+ var dummy = {};
+ Object.observe(dummy, checkResult);
+ dummy.dummy = dummy;
+}
+
+testDone(0);