Revision: 22913
Author:   [email protected]
Date:     Wed Aug  6 09:41:52 2014 UTC
Log:      Trigger exception debug events on Promise reject.

[email protected], [email protected]
BUG=393913
LOG=Y

Review URL: https://codereview.chromium.org/440773004
http://code.google.com/p/v8/source/detail?r=22913

Added:
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises/async-task-event.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises/events.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reentry.js
/branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-after-resolve.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-all.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-late.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-uncaught.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-in-constructor.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-all.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-late.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-uncaught.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-invalid-reject.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-throw-in-reject.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-undefined-reject.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-all.js
/branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-late.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-uncaught.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-in-constructor.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-uncaught-all.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-uncaught-uncaught.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-with-throw-in-reject.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-with-undefined-reject.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/try-reject-in-constructor.js /branches/bleeding_edge/test/mjsunit/es6/debug-promises/try-throw-reject-in-constructor.js
 /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpoppromise.js
 /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromiserejectevent.js
 /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpushpromise.js
Deleted:
 /branches/bleeding_edge/test/mjsunit/es6/debug-promise-events.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-async-task-event.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-all.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-late.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-uncaught.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-reentry.js
/branches/bleeding_edge/test/mjsunit/es6/debug-promises-throw-in-constructor.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-throw-in-reject.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-uncaught-all.js
/branches/bleeding_edge/test/mjsunit/es6/debug-promises-uncaught-uncaught.js
 /branches/bleeding_edge/test/mjsunit/es6/debug-promises-undefined-reject.js
/branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromisehandleepilogue.js /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromisehandleprologue.js
Modified:
 /branches/bleeding_edge/src/debug.cc
 /branches/bleeding_edge/src/debug.h
 /branches/bleeding_edge/src/isolate.cc
 /branches/bleeding_edge/src/promise.js
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/mjsunit/mjsunit.status
 /branches/bleeding_edge/tools/generate-runtime-tests.py

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/async-task-event.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,61 @@
+// 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
+
+Debug = debug.Debug;
+
+var base_id = -1;
+var exception = null;
+var expected = [
+  "enqueue #1",
+  "willHandle #1",
+  "then #1",
+  "enqueue #2",
+  "didHandle #1",
+  "willHandle #2",
+  "then #2",
+  "enqueue #3",
+  "didHandle #2",
+  "willHandle #3",
+  "didHandle #3"
+];
+
+function assertLog(msg) {
+  print(msg);
+  assertTrue(expected.length > 0);
+  assertEquals(expected.shift(), msg);
+  if (!expected.length) {
+    Debug.setListener(null);
+  }
+}
+
+function listener(event, exec_state, event_data, data) {
+  if (event != Debug.DebugEvent.AsyncTaskEvent) return;
+  try {
+    if (base_id < 0)
+      base_id = event_data.id();
+    var id = event_data.id() - base_id + 1;
+    assertEquals("Promise.resolve", event_data.name());
+    assertLog(event_data.type() + " #" + id);
+  } catch (e) {
+    print(e + e.stack)
+    exception = e;
+  }
+}
+
+Debug.setListener(listener);
+
+var resolver;
+var p = new Promise(function(resolve, reject) {
+  resolver = resolve;
+});
+p.then(function() {
+  assertLog("then #1");
+}).then(function() {
+  assertLog("then #2");
+});
+resolver();
+
+assertNull(exception);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/events.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,124 @@
+// 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: --allow-natives-syntax --expose-debug-as debug
+
+Debug = debug.Debug;
+
+var eventsExpected = 16;
+var exception = null;
+var result = [];
+
+function updatePromise(promise, parentPromise, status, value) {
+  var i;
+  for (i = 0; i < result.length; ++i) {
+    if (result[i].promise === promise) {
+      result[i].parentPromise = parentPromise || result[i].parentPromise;
+      result[i].status = status || result[i].status;
+      result[i].value = value || result[i].value;
+      break;
+    }
+  }
+  assertTrue(i < result.length);
+}
+
+function listener(event, exec_state, event_data, data) {
+  if (event != Debug.DebugEvent.PromiseEvent) return;
+  try {
+    eventsExpected--;
+    assertTrue(event_data.promise().isPromise());
+    if (event_data.status() === 0) {
+      // New promise.
+      assertEquals("pending", event_data.promise().status());
+      result.push({ promise: event_data.promise().value(), status: 0 });
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    } else if (event_data.status() !== undefined) {
+      // Resolve/reject promise.
+      updatePromise(event_data.promise().value(),
+                    undefined,
+                    event_data.status(),
+                    event_data.value().value());
+    } else {
+      // Chain promises.
+      assertTrue(event_data.parentPromise().isPromise());
+      updatePromise(event_data.promise().value(),
+                    event_data.parentPromise().value());
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    }
+  } catch (e) {
+    print(e + e.stack)
+    exception = e;
+  }
+}
+
+Debug.setListener(listener);
+
+function resolver(resolve, reject) { resolve(); }
+
+var p1 = new Promise(resolver);  // event
+var p2 = p1.then().then();  // event
+var p3 = new Promise(function(resolve, reject) {  // event
+  reject("rejected");
+});
+var p4 = p3.then();  // event
+var p5 = p1.then();  // event
+
+function assertAsync(b, s) {
+  if (b) {
+    print(s, "succeeded");
+  } else {
+    %AbortJS(s + " FAILED!");
+  }
+}
+
+function testDone(iteration) {
+  function checkResult() {
+    if (eventsExpected === 0) {
+      assertAsync(result.length === 6, "result.length");
+
+      assertAsync(result[0].promise === p1, "result[0].promise");
+      assertAsync(result[0].parentPromise === undefined,
+                  "result[0].parentPromise");
+      assertAsync(result[0].status === 1, "result[0].status");
+      assertAsync(result[0].value === undefined, "result[0].value");
+
+      assertAsync(result[1].parentPromise === p1,
+                  "result[1].parentPromise");
+      assertAsync(result[1].status === 1, "result[1].status");
+
+      assertAsync(result[2].promise === p2, "result[2].promise");
+
+      assertAsync(result[3].promise === p3, "result[3].promise");
+      assertAsync(result[3].parentPromise === undefined,
+                  "result[3].parentPromise");
+      assertAsync(result[3].status === -1, "result[3].status");
+      assertAsync(result[3].value === "rejected", "result[3].value");
+
+      assertAsync(result[4].promise === p4, "result[4].promise");
+      assertAsync(result[4].parentPromise === p3,
+                  "result[4].parentPromise");
+      assertAsync(result[4].status === -1, "result[4].status");
+      assertAsync(result[4].value === "rejected", "result[4].value");
+
+      assertAsync(result[5].promise === p5, "result[5].promise");
+      assertAsync(result[5].parentPromise === p1,
+                  "result[5].parentPromise");
+      assertAsync(result[5].status === 1, "result[5].status");
+
+      assertAsync(exception === null, "exception === null");
+      Debug.setListener(null);
+    } else if (iteration > 10) {
+      %AbortJS("Not all events were received!");
+    } else {
+      testDone(iteration + 1);
+    }
+  }
+
+  var iteration = iteration || 0;
+  var dummy = {};
+  Object.observe(dummy, checkResult);
+  dummy.dummy = dummy;
+}
+
+testDone();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reentry.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,17 @@
+// 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
+
+// Test reentry of special try catch for Promises.
+
+Debug = debug.Debug;
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(function(event, exec_state, event_data, data) { });
+
+var p = new Promise(function(resolve, reject) { resolve(); });
+var q = p.chain(function() {
+  new Promise(function(resolve, reject) { resolve(); });
+});
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-after-resolve.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,37 @@
+// 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 listen to uncaught exceptions and
+// the Promise is rejected in a chained closure after it has been resolved.
+// We expect no Exception debug event to be triggered.
+
+Debug = debug.Debug;
+
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve(reject);
+});
+
+var q = p.chain(
+  function(value) {
+    assertEquals(["resolve", "end main"], log);
+    value(new Error("reject"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    assertTrue(event != Debug.DebugEvent.Exception);
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnException();
+Debug.setListener(listener);
+
+log.push("end main");
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-all.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,72 @@
+// 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 listen to all exceptions and
+// there is a catch handler for the to-be-rejected Promise.
+// We expect a normal Exception debug event to be triggered.
+
+Debug = debug.Debug;
+
+var log = [];
+var expected_events = 1;
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function(value) {
+    log.push("reject");
+    return Promise.reject(new Error("reject"));
+  });
+
+q.catch(
+  function(e) {
+    assertEquals("reject", e.message);
+  });
+
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("reject", event_data.exception().message);
+      assertEquals(q, event_data.promise());
+      assertFalse(event_data.uncaught());
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "end main", "reject"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-late.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,34 @@
+// 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, the Promise +// is rejected, and a catch handler is installed right before the rejection.
+// We expect no debug event to be triggered.
+
+Debug = debug.Debug;
+
+var p = new Promise(function(resolve, reject) {
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    q.catch(function(e) {
+      assertEquals("caught", e.message);
+    });
+    return Promise.reject(Error("caught"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    assertTrue(event != Debug.DebugEvent.Exception);
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-caught-uncaught.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,36 @@
+// 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 no debug event to be triggered.
+
+Debug = debug.Debug;
+
+var p = new Promise(function(resolve, reject) {
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    return Promise.reject(Error("caught reject"));
+  });
+
+q.catch(
+  function(e) {
+    assertEquals("caught reject", e.message);
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    assertTrue(event != Debug.DebugEvent.Exception);
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-in-constructor.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,39 @@
+// 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
+
+// Test debug events when we only listen to uncaught exceptions and
+// the Promise is rejected in the Promise constructor.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var steps = 0;
+var exception = null;
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      steps++;
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    }
+  } catch (e) {
+    exception = e;
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+var p = new Promise(function(resolve, reject) {
+  reject(new Error("uncaught"));  // event
+});
+
+assertEquals(1, steps);
+assertNull(exception);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-all.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,69 @@
+// 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 listen to all 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 = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    log.push("reject");
+    return Promise.reject(new Error("uncaught reject"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("uncaught reject", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertEquals(q, event_data.promise());
+      assertTrue(event_data.uncaught());
+      // All of the frames on the stack are from native Javascript.
+      assertEquals(0, exec_state.frameCount());
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "end main", "reject"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-late.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,76 @@
+// 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 = 1;
+var log = [];
+
+var reject_closure;
+
+var p = new Promise(function(resolve, reject) {
+  log.push("postpone p");
+  reject_closure = reject;
+});
+
+var q = new Promise(function(resolve, reject) {
+  log.push("resolve q");
+  resolve();
+});
+
+q.then(function() {
+  log.push("reject p");
+  reject_closure(new Error("uncaught reject p"));  // event
+})
+
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("uncaught reject p", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertEquals(p, event_data.promise());
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    }
+  } 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(["postpone p", "resolve q", "end main", "reject p"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-uncaught-uncaught.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,69 @@
+// 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 no 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 = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    log.push("reject");
+    return Promise.reject(Error("uncaught reject"));  // event
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("uncaught reject", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertEquals(q, event_data.promise());
+      assertTrue(event_data.uncaught());
+      // All of the frames on the stack are from native Javascript.
+      assertEquals(0, exec_state.frameCount());
+    }
+  } 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", "end main", "reject"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-invalid-reject.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,78 @@
+// 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 a Promise is rejected, which is caught by a custom +// promise, which has a number for reject closure. We expect an Exception debug
+// events trying to call the invalid reject closure.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+function MyPromise(resolver) {
+  var reject = 1;
+  var resolve = function() { };
+  log.push("construct");
+  resolver(resolve, reject);
+};
+
+MyPromise.prototype = new Promise(function() {});
+p.constructor = MyPromise;
+
+var q = p.chain(
+  function() {
+    log.push("reject caught");
+    return Promise.reject(new Error("caught"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+ assertEquals("number is not a function", event_data.exception().message);
+      // All of the frames on the stack are from native Javascript.
+      assertEquals(0, exec_state.frameCount());
+      assertEquals(q, event_data.promise());
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "construct", "end main", "reject caught"],
+                     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);
+
+log.push("end main");
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-throw-in-reject.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,87 @@
+// 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 a Promise is rejected, which is caught by a
+// custom promise, which throws a new exception in its reject handler.
+// We expect two Exception debug events:
+//  1) when promise q is rejected.
+//  2) when the custom reject closure in MyPromise throws an exception.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+function MyPromise(resolver) {
+  var reject = function() {
+    log.push("throw in reject");
+    throw new Error("reject");  // event
+  };
+  var resolve = function() { };
+  log.push("construct");
+  resolver(resolve, reject);
+};
+
+MyPromise.prototype = new Promise(function() {});
+p.constructor = MyPromise;
+
+var q = p.chain(
+  function() {
+    log.push("reject caught");
+    return Promise.reject(new Error("caught"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("reject", event_data.exception().message);
+      // Assert that the debug event is triggered at the throw site.
+      assertTrue(
+          exec_state.frame(0).sourceLineText().indexOf("// event") > 0);
+    }
+  } catch (e) {
+    // Signal a failure with exit code 1.  This is necessary since the
+    // debugger swallows exceptions and we expect the chained function
+    // and this listener to be executed after the main script is finished.
+    print("Unexpected exception: " + e + "\n" + e.stack);
+    quit(1);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "construct", "end main",
+                      "reject caught", "throw in reject"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/reject-with-undefined-reject.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,78 @@
+// 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 a Promise is rejected, which is caught by a custom
+// promise, which has undefined for reject closure.  We expect an Exception
+// debug even calling the (undefined) custom rejected closure.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+function MyPromise(resolver) {
+  var reject = undefined;
+  var resolve = function() { };
+  log.push("construct");
+  resolver(resolve, reject);
+};
+
+MyPromise.prototype = new Promise(function() {});
+p.constructor = MyPromise;
+
+var q = p.chain(
+  function() {
+    log.push("reject caught");
+    return Promise.reject(new Error("caught"));
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("caught", event_data.exception().message);
+      // All of the frames on the stack are from native Javascript.
+      assertEquals(0, exec_state.frameCount());
+      assertEquals(q, event_data.promise());
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "construct", "end main", "reject caught"],
+                     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);
+
+log.push("end main");
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-all.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,71 @@
+// 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 listen to all exceptions and
+// there is a catch handler for the exception thrown in a Promise.
+// We expect a normal Exception debug event to be triggered.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    log.push("throw");
+    throw new Error("caught");
+  });
+
+q.catch(
+  function(e) {
+    assertEquals("caught", e.message);
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("caught", event_data.exception().message);
+      assertEquals(q, event_data.promise());
+      assertFalse(event_data.uncaught());
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "end main", "throw"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-late.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,34 @@
+// 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, the Promise
+// throws, and a catch handler is installed right before throwing.
+// We expect no debug event to be triggered.
+
+Debug = debug.Debug;
+
+var p = new Promise(function(resolve, reject) {
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    q.catch(function(e) {
+      assertEquals("caught", e.message);
+    });
+    throw new Error("caught");
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    assertTrue(event != Debug.DebugEvent.Exception);
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-caught-uncaught.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,36 @@
+// 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 exception thrown in a Promise.
+// We expect no debug event to be triggered.
+
+Debug = debug.Debug;
+
+var p = new Promise(function(resolve, reject) {
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    throw new Error("caught throw");
+  });
+
+q.catch(
+  function(e) {
+    assertEquals("caught throw", e.message);
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    assertTrue(event != Debug.DebugEvent.Exception);
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-in-constructor.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,40 @@
+// 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
+
+// Test debug events when we only listen to uncaught exceptions and
+// an exception is thrown in the Promise constructor.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var step = 0;
+var exception = null;
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      assertEquals(0, step);
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+      step++;
+    }
+  } catch (e) {
+    exception = e;
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+var p = new Promise(function(resolve, reject) {
+  throw new Error("uncaught");  // event
+});
+
+assertEquals(1, step);
+assertNull(exception);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-uncaught-all.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,70 @@
+// 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 listen to all exceptions and
+// there is no catch handler for the exception thrown in a Promise.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    log.push("throw");
+    throw new Error("uncaught");  // event
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    // Ignore exceptions during startup in stress runs.
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertEquals(q, event_data.promise());
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    }
+  } catch (e) {
+    %AbortJS(e + "\n" + e.stack);
+  }
+}
+
+Debug.setBreakOnException();
+Debug.setListener(listener);
+
+log.push("end main");
+
+function testDone(iteration) {
+  function checkResult() {
+    try {
+      assertTrue(iteration < 10);
+      if (expected_events === 0) {
+        assertEquals(["resolve", "end main", "throw"], log);
+      } else {
+        testDone(iteration + 1);
+      }
+    } catch (e) {
+      %AbortJS(e + "\n" + e.stack);
+    }
+  }
+
+  // Rerun testDone through the Object.observe processing loop.
+  var dummy = {};
+  Object.observe(dummy, checkResult);
+  dummy.dummy = dummy;
+}
+
+testDone(0);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-uncaught-uncaught.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,70 @@
+// 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 exception thrown in a Promise.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var expected_events = 1;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+var q = p.chain(
+  function() {
+    log.push("throw");
+    throw new Error("uncaught");  // event
+  });
+
+function listener(event, exec_state, event_data, data) {
+  if (event == Debug.DebugEvent.AsyncTaskEvent) return;
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertEquals(q, event_data.promise());
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+    }
+  } 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", "end main", "throw"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-with-throw-in-reject.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,90 @@
+// 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 an exception is thrown inside a Promise, which is
+// caught by a custom promise, which throws a new exception in its reject
+// handler. We expect two Exception debug events:
+//  1) when the exception is thrown in the promise q.
+//  2) when the custom reject closure in MyPromise throws an exception.
+
+Debug = debug.Debug;
+
+var expected_events = 2;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+function MyPromise(resolver) {
+  var reject = function() {
+    log.push("throw in reject");
+    throw new Error("reject");  // event
+  };
+  var resolve = function() { };
+  log.push("construct");
+  resolver(resolve, reject);
+};
+
+MyPromise.prototype = new Promise(function() {});
+p.constructor = MyPromise;
+
+var q = p.chain(
+  function() {
+    log.push("throw caught");
+    throw new Error("caught");  // event
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      if (expected_events == 1) {
+        assertEquals(["resolve", "construct", "end main",
+                      "throw caught"], log);
+        assertEquals("caught", event_data.exception().message);
+      } else if (expected_events == 0) {
+        assertEquals("reject", event_data.exception().message);
+      } else {
+        assertUnreachable();
+      }
+      assertEquals(q, event_data.promise());
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf('// event')
0);
+    }
+  } 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", "construct", "end main",
+                      "throw caught", "throw in reject"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/throw-with-undefined-reject.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,88 @@
+// 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 an exception is thrown inside a Promise, which is
+// caught by a custom promise, which has no reject handler.
+// We expect two Exception debug events:
+//  1) when the exception is thrown in the promise q.
+// 2) when calling the undefined custom reject closure in MyPromise throws.
+
+Debug = debug.Debug;
+
+var expected_events = 2;
+var log = [];
+
+var p = new Promise(function(resolve, reject) {
+  log.push("resolve");
+  resolve();
+});
+
+function MyPromise(resolver) {
+  var reject = undefined;
+  var resolve = function() { };
+  log.push("construct");
+  resolver(resolve, reject);
+};
+
+MyPromise.prototype = new Promise(function() {});
+p.constructor = MyPromise;
+
+var q = p.chain(
+  function() {
+    log.push("throw caught");
+    throw new Error("caught");  // event
+  });
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      expected_events--;
+      assertTrue(expected_events >= 0);
+      if (expected_events == 1) {
+        assertTrue(
+            exec_state.frame(0).sourceLineText().indexOf('// event') > 0);
+        assertEquals("caught", event_data.exception().message);
+      } else if (expected_events == 0) {
+        // All of the frames on the stack are from native Javascript.
+        assertEquals(0, exec_state.frameCount());
+        assertEquals("undefined is not a function",
+                     event_data.exception().message);
+      } else {
+        assertUnreachable();
+      }
+      assertEquals(q, 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", "construct", "end main", "throw caught"], 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
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/try-reject-in-constructor.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,42 @@
+// 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
+
+// Test debug events when we only listen to uncaught exceptions and
+// the Promise is rejected within a try-catch in the Promise constructor.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var step = 0;
+var exception = null;
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      assertEquals(0, step);
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+      step++;
+    }
+  } catch (e) {
+    exception = e;
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+var p = new Promise(function(resolve, reject) {
+  try {  // This try-catch must not prevent this uncaught reject event.
+    reject(new Error("uncaught"));  // event
+  } catch (e) { }
+});
+
+assertEquals(1, step);
+assertNull(exception);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/debug-promises/try-throw-reject-in-constructor.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,44 @@
+// 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
+
+// Test debug events when we only listen to uncaught exceptions and
+// an exception is thrown in the Promise constructor, but caught in an
+// inner try-catch.  The Promise is rejected afterwards.
+// We expect an Exception debug event with a promise to be triggered.
+
+Debug = debug.Debug;
+
+var step = 0;
+var exception = null;
+
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Exception) {
+      assertEquals(0, step);
+      assertEquals("uncaught", event_data.exception().message);
+      assertTrue(event_data.promise() instanceof Promise);
+      assertTrue(event_data.uncaught());
+      // Assert that the debug event is triggered at the throw site.
+ assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
+      step++;
+    }
+  } catch (e) {
+    exception = e;
+  }
+}
+
+Debug.setBreakOnUncaughtException();
+Debug.setListener(listener);
+
+var p = new Promise(function(resolve, reject) {
+  try {  // This try-catch must not prevent this uncaught reject event.
+    throw new Error("caught");
+  } catch (e) { }
+  reject(new Error("uncaught"));  // event
+});
+
+assertEquals(1, step);
+assertNull(exception);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpoppromise.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,4 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
+// Flags: --allow-natives-syntax --harmony
+%DebugPopPromise();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromiserejectevent.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,6 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
+// Flags: --allow-natives-syntax --harmony
+var _promise = new Object();
+var _value = new Object();
+%DebugPromiseRejectEvent(_promise, _value);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpushpromise.js Wed Aug 6 09:41:52 2014 UTC
@@ -0,0 +1,5 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
+// Flags: --allow-natives-syntax --harmony
+var _promise = new Object();
+%DebugPushPromise(_promise);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promise-events.js Tue Aug 5 08:16:02 2014 UTC
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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: --allow-natives-syntax --expose-debug-as debug
-
-Debug = debug.Debug;
-
-var eventsExpected = 16;
-var exception = null;
-var result = [];
-
-function updatePromise(promise, parentPromise, status, value) {
-  var i;
-  for (i = 0; i < result.length; ++i) {
-    if (result[i].promise === promise) {
-      result[i].parentPromise = parentPromise || result[i].parentPromise;
-      result[i].status = status || result[i].status;
-      result[i].value = value || result[i].value;
-      break;
-    }
-  }
-  assertTrue(i < result.length);
-}
-
-function listener(event, exec_state, event_data, data) {
-  if (event != Debug.DebugEvent.PromiseEvent) return;
-  try {
-    eventsExpected--;
-    assertTrue(event_data.promise().isPromise());
-    if (event_data.status() === 0) {
-      // New promise.
-      assertEquals("pending", event_data.promise().status());
-      result.push({ promise: event_data.promise().value(), status: 0 });
- assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
-    } else if (event_data.status() !== undefined) {
-      // Resolve/reject promise.
-      updatePromise(event_data.promise().value(),
-                    undefined,
-                    event_data.status(),
-                    event_data.value().value());
-    } else {
-      // Chain promises.
-      assertTrue(event_data.parentPromise().isPromise());
-      updatePromise(event_data.promise().value(),
-                    event_data.parentPromise().value());
- assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
-    }
-  } catch (e) {
-    print(e + e.stack)
-    exception = e;
-  }
-}
-
-Debug.setListener(listener);
-
-function resolver(resolve, reject) { resolve(); }
-
-var p1 = new Promise(resolver);  // event
-var p2 = p1.then().then();  // event
-var p3 = new Promise(function(resolve, reject) {  // event
-  reject("rejected");
-});
-var p4 = p3.then();  // event
-var p5 = p1.then();  // event
-
-function assertAsync(b, s) {
-  if (b) {
-    print(s, "succeeded");
-  } else {
-    %AbortJS(s + " FAILED!");
-  }
-}
-
-function testDone(iteration) {
-  function checkResult() {
-    if (eventsExpected === 0) {
-      assertAsync(result.length === 6, "result.length");
-
-      assertAsync(result[0].promise === p1, "result[0].promise");
-      assertAsync(result[0].parentPromise === undefined,
-                  "result[0].parentPromise");
-      assertAsync(result[0].status === 1, "result[0].status");
-      assertAsync(result[0].value === undefined, "result[0].value");
-
-      assertAsync(result[1].parentPromise === p1,
-                  "result[1].parentPromise");
-      assertAsync(result[1].status === 1, "result[1].status");
-
-      assertAsync(result[2].promise === p2, "result[2].promise");
-
-      assertAsync(result[3].promise === p3, "result[3].promise");
-      assertAsync(result[3].parentPromise === undefined,
-                  "result[3].parentPromise");
-      assertAsync(result[3].status === -1, "result[3].status");
-      assertAsync(result[3].value === "rejected", "result[3].value");
-
-      assertAsync(result[4].promise === p4, "result[4].promise");
-      assertAsync(result[4].parentPromise === p3,
-                  "result[4].parentPromise");
-      assertAsync(result[4].status === -1, "result[4].status");
-      assertAsync(result[4].value === "rejected", "result[4].value");
-
-      assertAsync(result[5].promise === p5, "result[5].promise");
-      assertAsync(result[5].parentPromise === p1,
-                  "result[5].parentPromise");
-      assertAsync(result[5].status === 1, "result[5].status");
-
-      assertAsync(exception === null, "exception === null");
-      Debug.setListener(null);
-    } else if (iteration > 10) {
-      %AbortJS("Not all events were received!");
-    } else {
-      testDone(iteration + 1);
-    }
-  }
-
-  var iteration = iteration || 0;
-  var dummy = {};
-  Object.observe(dummy, checkResult);
-  dummy.dummy = dummy;
-}
-
-testDone();
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-async-task-event.js Tue Jul 8 14:45:57 2014 UTC
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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
-
-Debug = debug.Debug;
-
-var base_id = -1;
-var exception = null;
-var expected = [
-  "enqueue #1",
-  "willHandle #1",
-  "then #1",
-  "enqueue #2",
-  "didHandle #1",
-  "willHandle #2",
-  "then #2",
-  "enqueue #3",
-  "didHandle #2",
-  "willHandle #3",
-  "didHandle #3"
-];
-
-function assertLog(msg) {
-  print(msg);
-  assertTrue(expected.length > 0);
-  assertEquals(expected.shift(), msg);
-  if (!expected.length) {
-    Debug.setListener(null);
-  }
-}
-
-function listener(event, exec_state, event_data, data) {
-  if (event != Debug.DebugEvent.AsyncTaskEvent) return;
-  try {
-    if (base_id < 0)
-      base_id = event_data.id();
-    var id = event_data.id() - base_id + 1;
-    assertEquals("Promise.resolve", event_data.name());
-    assertLog(event_data.type() + " #" + id);
-  } catch (e) {
-    print(e + e.stack)
-    exception = e;
-  }
-}
-
-Debug.setListener(listener);
-
-var resolver;
-var p = new Promise(function(resolve, reject) {
-  resolver = resolve;
-});
-p.then(function() {
-  assertLog("then #1");
-}).then(function() {
-  assertLog("then #2");
-});
-resolver();
-
-assertNull(exception);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-all.js Thu Jul 3 15:56:01 2014 UTC
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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
-
-// Test debug events when we listen to all exceptions and
-// there is a catch handler for the exception thrown in a Promise.
-// We expect a normal Exception debug event to be triggered.
-
-Debug = debug.Debug;
-
-var log = [];
-var step = 0;
-
-var p = new Promise(function(resolve, reject) {
-  log.push("resolve");
-  resolve();
-});
-
-var q = p.chain(
-  function() {
-    log.push("throw");
-    throw new Error("caught");
-  });
-
-q.catch(
-  function(e) {
-    assertEquals("caught", e.message);
-  });
-
-function listener(event, exec_state, event_data, data) {
-  if (event == Debug.DebugEvent.AsyncTaskEvent) return;
-  try {
-    // Ignore exceptions during startup in stress runs.
-    if (step >= 1) return;
-    assertEquals(["resolve", "end main", "throw"], log);
-    if (event == Debug.DebugEvent.Exception) {
-      assertEquals("caught", event_data.exception().message);
-      assertEquals(undefined, event_data.promise());
-      assertFalse(event_data.uncaught());
-      step++;
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnException();
-Debug.setListener(listener);
-
-log.push("end main");
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-late.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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
-
-// Test debug events when we only listen to uncaught exceptions, the Promise
-// throws, and a catch handler is installed right before throwing.
-// We expect no debug event to be triggered.
-
-Debug = debug.Debug;
-
-var p = new Promise(function(resolve, reject) {
-  resolve();
-});
-
-var q = p.chain(
-  function() {
-    q.catch(function(e) {
-      assertEquals("caught", e.message);
-    });
-    throw new Error("caught");
-  });
-
-function listener(event, exec_state, event_data, data) {
-  try {
-    assertTrue(event != Debug.DebugEvent.Exception);
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-caught-uncaught.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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
-
-// Test debug events when we only listen to uncaught exceptions and
-// there is a catch handler for the exception thrown in a Promise.
-// We expect no debug event to be triggered.
-
-Debug = debug.Debug;
-
-var p = new Promise(function(resolve, reject) {
-  resolve();
-});
-
-var q = p.chain(
-  function() {
-    throw new Error("caught");
-  });
-
-q.catch(
-  function(e) {
-    assertEquals("caught", e.message);
-  });
-
-function listener(event, exec_state, event_data, data) {
-  try {
-    assertTrue(event != Debug.DebugEvent.Exception);
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-reentry.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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
-
-// Test reentry of special try catch for Promises.
-
-Debug = debug.Debug;
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(function(event, exec_state, event_data, data) { });
-
-var p = new Promise(function(resolve, reject) { resolve(); });
-var q = p.chain(function() {
-  new Promise(function(resolve, reject) { resolve(); });
-});
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-throw-in-constructor.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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
-
-// Test debug events when we only listen to uncaught exceptions and
-// an exception is thrown in the the Promise constructor.
-// We expect an Exception debug event with a promise to be triggered.
-
-Debug = debug.Debug;
-
-var step = 0;
-var exception = null;
-
-function listener(event, exec_state, event_data, data) {
-  try {
-    // Ignore exceptions during startup in stress runs.
-    if (step >= 1) return;
-    if (event == Debug.DebugEvent.Exception) {
-      assertEquals(0, step);
-      assertEquals("uncaught", event_data.exception().message);
-      assertTrue(event_data.promise() instanceof Promise);
-      assertTrue(event_data.uncaught());
-      // Assert that the debug event is triggered at the throw site.
- assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
-      step++;
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    exception = e;
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
-
-var p = new Promise(function(resolve, reject) {
-  throw new Error("uncaught");  // event
-});
-
-assertEquals(1, step);
-assertNull(exception);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-throw-in-reject.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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
-
-// Test debug events when an exception is thrown inside a Promise, which is
-// caught by a custom promise, which throws a new exception in its reject
-// handler. We expect an Exception debug event with a promise to be triggered.
-
-Debug = debug.Debug;
-
-var log = [];
-var step = 0;
-
-var p = new Promise(function(resolve, reject) {
-  log.push("resolve");
-  resolve();
-});
-
-function MyPromise(resolver) {
-  var reject = function() {
-    log.push("throw reject");
-    throw new Error("reject");  // event
-  };
-  var resolve = function() { };
-  log.push("construct");
-  resolver(resolve, reject);
-};
-
-MyPromise.prototype = p;
-p.constructor = MyPromise;
-
-var q = p.chain(
-  function() {
-    log.push("throw caught");
-    throw new Error("caught");
-  });
-
-function listener(event, exec_state, event_data, data) {
-  try {
-    if (event == Debug.DebugEvent.Exception) {
-      assertEquals(["resolve", "construct", "end main",
-                    "throw caught", "throw reject"], log);
-      assertEquals("reject", event_data.exception().message);
-      assertEquals(q, event_data.promise());
- assertTrue(exec_state.frame(0).sourceLineText().indexOf('// event')
0);
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
-
-log.push("end main");
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-uncaught-all.js Thu Jul 3 15:56:01 2014 UTC
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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
-
-// Test debug events when we listen to all exceptions and
-// there is a catch handler for the exception thrown in a Promise.
-// We expect an Exception debug event with a promise to be triggered.
-
-Debug = debug.Debug;
-
-var log = [];
-var step = 0;
-var exception = undefined;
-
-var p = new Promise(function(resolve, reject) {
-  log.push("resolve");
-  resolve();
-});
-
-var q = p.chain(
-  function() {
-    log.push("throw");
-    throw new Error("uncaught");  // event
-  });
-
-function listener(event, exec_state, event_data, data) {
-  if (event == Debug.DebugEvent.AsyncTaskEvent) return;
-  try {
-    // Ignore exceptions during startup in stress runs.
-    if (step >= 1) return;
-    assertEquals(["resolve", "end main", "throw"], log);
-    if (event == Debug.DebugEvent.Exception) {
-      assertEquals(0, step);
-      assertEquals("uncaught", event_data.exception().message);
-      assertTrue(event_data.promise() instanceof Promise);
-      assertEquals(q, event_data.promise());
-      assertTrue(event_data.uncaught());
-      // Assert that the debug event is triggered at the throw site.
- assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
-      step++;
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnException();
-Debug.setListener(listener);
-
-log.push("end main");
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-uncaught-uncaught.js Thu Jul 3 15:56:01 2014 UTC
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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
-
-// Test debug events when we only listen to uncaught exceptions and
-// there is a catch handler for the exception thrown in a Promise.
-// We expect an Exception debug event with a promise to be triggered.
-
-Debug = debug.Debug;
-
-var log = [];
-var step = 0;
-
-var p = new Promise(function(resolve, reject) {
-  log.push("resolve");
-  resolve();
-});
-
-var q = p.chain(
-  function() {
-    log.push("throw");
-    throw new Error("uncaught");  // event
-  });
-
-function listener(event, exec_state, event_data, data) {
-  if (event == Debug.DebugEvent.AsyncTaskEvent) return;
-  try {
-    // Ignore exceptions during startup in stress runs.
-    if (step >= 1) return;
-    assertEquals(["resolve", "end main", "throw"], log);
-    if (event == Debug.DebugEvent.Exception) {
-      assertEquals(0, step);
-      assertEquals("uncaught", event_data.exception().message);
-      assertTrue(event_data.promise() instanceof Promise);
-      assertEquals(q, event_data.promise());
-      assertTrue(event_data.uncaught());
-      // Assert that the debug event is triggered at the throw site.
- assertTrue(exec_state.frame(0).sourceLineText().indexOf("// event")
0);
-      step++;
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
-
-log.push("end main");
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/debug-promises-undefined-reject.js Fri Jun 20 10:47:50 2014 UTC
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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
-
-// Test debug events when an exception is thrown inside a Promise, which is
-// caught by a custom promise, which has no reject handler.
-// We expect an Exception event with a promise to be triggered.
-
-Debug = debug.Debug;
-
-var log = [];
-var step = 0;
-
-var p = new Promise(function(resolve, reject) {
-  log.push("resolve");
-  resolve();
-});
-
-function MyPromise(resolver) {
-  var reject = undefined;
-  var resolve = function() { };
-  log.push("construct");
-  resolver(resolve, reject);
-};
-
-MyPromise.prototype = p;
-p.constructor = MyPromise;
-
-var q = p.chain(
-  function() {
-    log.push("throw caught");
-    throw new Error("caught");  // event
-  });
-
-function listener(event, exec_state, event_data, data) {
-  try {
-    if (event == Debug.DebugEvent.Exception) {
- assertEquals(["resolve", "construct", "end main", "throw caught"], log);
-      assertEquals("undefined is not a function",
-                   event_data.exception().message);
-      assertEquals(q, event_data.promise());
-    }
-  } catch (e) {
-    // Signal a failure with exit code 1.  This is necessary since the
-    // debugger swallows exceptions and we expect the chained function
-    // and this listener to be executed after the main script is finished.
-    print("Unexpected exception: " + e + "\n" + e.stack);
-    quit(1);
-  }
-}
-
-Debug.setBreakOnUncaughtException();
-Debug.setListener(listener);
-
-log.push("end main");
=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromisehandleepilogue.js Thu May 8 13:11:59 2014 UTC
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
-// Flags: --allow-natives-syntax --harmony
-%DebugPromiseHandleEpilogue();
=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/debugpromisehandleprologue.js Thu May 8 13:11:59 2014 UTC
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
-// Flags: --allow-natives-syntax --harmony
-var _promise_getter = function() {};
-%DebugPromiseHandlePrologue(_promise_getter);
=======================================
--- /branches/bleeding_edge/src/debug.cc        Mon Aug  4 11:34:54 2014 UTC
+++ /branches/bleeding_edge/src/debug.cc        Wed Aug  6 09:41:52 2014 UTC
@@ -855,8 +855,8 @@
   ClearAllBreakPoints();
   ClearStepping();

-  // Match unmatched PromiseHandlePrologue calls.
-  while (thread_local_.promise_on_stack_) PromiseHandleEpilogue();
+  // Match unmatched PopPromise calls.
+  while (thread_local_.promise_on_stack_) PopPromise();

   // Return debugger is not loaded.
   if (!is_loaded()) return;
@@ -1272,30 +1272,29 @@
 }


-PromiseOnStack::PromiseOnStack(Isolate* isolate,
-                                      PromiseOnStack* prev,
-                                      Handle<JSFunction> getter)
+PromiseOnStack::PromiseOnStack(Isolate* isolate, PromiseOnStack* prev,
+                               Handle<JSObject> promise)
     : isolate_(isolate), prev_(prev) {
   handler_ = StackHandler::FromAddress(
       Isolate::handler(isolate->thread_local_top()));
-  getter_ = Handle<JSFunction>::cast(
-      isolate->global_handles()->Create(*getter));
+  promise_ =
+      Handle<JSObject>::cast(isolate->global_handles()->Create(*promise));
 }


 PromiseOnStack::~PromiseOnStack() {
- isolate_->global_handles()->Destroy(Handle<Object>::cast(getter_).location());
+  isolate_->global_handles()->Destroy(
+      Handle<Object>::cast(promise_).location());
 }


-void Debug::PromiseHandlePrologue(Handle<JSFunction> promise_getter) {
+void Debug::PushPromise(Handle<JSObject> promise) {
   PromiseOnStack* prev = thread_local_.promise_on_stack_;
-  thread_local_.promise_on_stack_ =
-      new PromiseOnStack(isolate_, prev, promise_getter);
+ thread_local_.promise_on_stack_ = new PromiseOnStack(isolate_, prev, promise);
 }


-void Debug::PromiseHandleEpilogue() {
+void Debug::PopPromise() {
   if (thread_local_.promise_on_stack_ == NULL) return;
   PromiseOnStack* prev = thread_local_.promise_on_stack_->prev();
   delete thread_local_.promise_on_stack_;
@@ -1303,33 +1302,38 @@
 }


-Handle<Object> Debug::GetPromiseForUncaughtException() {
+Handle<Object> Debug::GetPromiseOnStackOnThrow() {
   Handle<Object> undefined = isolate_->factory()->undefined_value();
   if (thread_local_.promise_on_stack_ == NULL) return undefined;
- Handle<JSFunction> promise_getter = thread_local_.promise_on_stack_->getter();
-  StackHandler* promise_catch = thread_local_.promise_on_stack_->handler();
+  StackHandler* promise_try = thread_local_.promise_on_stack_->handler();
   // Find the top-most try-catch handler.
   StackHandler* handler = StackHandler::FromAddress(
       Isolate::handler(isolate_->thread_local_top()));
-  while (handler != NULL && !handler->is_catch()) {
+  do {
+    if (handler == promise_try) {
+ // Mark the pushed try-catch handler to prevent a later duplicate event
+      // triggered with the following reject.
+      return thread_local_.promise_on_stack_->promise();
+    }
     handler = handler->next();
-  }
-#ifdef DEBUG
-  // Make sure that our promise catch handler is in the list of handlers,
-  // even if it's not the top-most try-catch handler.
-  StackHandler* temp = handler;
-  while (temp != promise_catch && !temp->is_catch()) {
-    temp = temp->next();
-    CHECK(temp != NULL);
-  }
-#endif  // DEBUG
-
-  if (handler == promise_catch) {
-    return Execution::Call(
-        isolate_, promise_getter, undefined, 0, NULL).ToHandleChecked();
-  }
+    // There must be a try-catch handler if a promise is on stack.
+    DCHECK_NE(NULL, handler);
+ // Throwing inside a Promise can be intercepted by an inner try-catch, so
+    // we stop at the first try-catch handler.
+  } while (!handler->is_catch());
   return undefined;
 }
+
+
+bool Debug::PromiseHasRejectHandler(Handle<JSObject> promise) {
+  Handle<JSFunction> fun = Handle<JSFunction>::cast(
+      JSObject::GetDataProperty(isolate_->js_builtins_object(),
+ isolate_->factory()->NewStringFromStaticAscii(
+                                    "PromiseHasRejectHandler")));
+  Handle<Object> result =
+      Execution::Call(isolate_, fun, promise, 0, NULL).ToHandleChecked();
+  return result->IsTrue();
+}


 void Debug::PrepareStep(StepAction step_action,
@@ -2562,13 +2566,25 @@
 }


-void Debug::OnException(Handle<Object> exception, bool uncaught) {
+void Debug::OnThrow(Handle<Object> exception, bool uncaught) {
   if (in_debug_scope() || ignore_events()) return;
+  HandleScope scope(isolate_);
+  OnException(exception, uncaught, GetPromiseOnStackOnThrow());
+}

+
+void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) {
+  if (in_debug_scope() || ignore_events()) return;
   HandleScope scope(isolate_);
-  Handle<Object> promise = GetPromiseForUncaughtException();
-  uncaught |= !promise->IsUndefined();
+  OnException(value, false, promise);
+}

+
+void Debug::OnException(Handle<Object> exception, bool uncaught,
+                        Handle<Object> promise) {
+  if (promise->IsJSObject()) {
+    uncaught |= !PromiseHasRejectHandler(Handle<JSObject>::cast(promise));
+  }
   // Bail out if exception breaks are not active
   if (uncaught) {
     // Uncaught exceptions are reported by either flags.
=======================================
--- /branches/bleeding_edge/src/debug.h Mon Aug  4 11:34:54 2014 UTC
+++ /branches/bleeding_edge/src/debug.h Wed Aug  6 09:41:52 2014 UTC
@@ -335,17 +335,16 @@

 class PromiseOnStack {
  public:
-  PromiseOnStack(Isolate* isolate,
-                 PromiseOnStack* prev,
-                 Handle<JSFunction> getter);
+  PromiseOnStack(Isolate* isolate, PromiseOnStack* prev,
+                 Handle<JSObject> getter);
   ~PromiseOnStack();
   StackHandler* handler() { return handler_; }
-  Handle<JSFunction> getter() { return getter_; }
+  Handle<JSObject> promise() { return promise_; }
   PromiseOnStack* prev() { return prev_; }
  private:
   Isolate* isolate_;
   StackHandler* handler_;
-  Handle<JSFunction> getter_;
+  Handle<JSObject> promise_;
   PromiseOnStack* prev_;
 };

@@ -361,7 +360,9 @@
  public:
   // Debug event triggers.
   void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
-  void OnException(Handle<Object> exception, bool uncaught);
+
+  void OnThrow(Handle<Object> exception, bool uncaught);
+  void OnPromiseReject(Handle<JSObject> promise, Handle<Object> value);
   void OnCompileError(Handle<Script> script);
   void OnBeforeCompile(Handle<Script> script);
   void OnAfterCompile(Handle<Script> script);
@@ -452,8 +453,9 @@
   bool IsBreakAtReturn(JavaScriptFrame* frame);

   // Promise handling.
-  void PromiseHandlePrologue(Handle<JSFunction> promise_getter);
-  void PromiseHandleEpilogue();
+  // Push and pop a promise and the current try-catch handler.
+  void PushPromise(Handle<JSObject> promise);
+  void PopPromise();

   // Support for LiveEdit
   void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
@@ -522,6 +524,9 @@
   // Check whether there are commands in the command queue.
   inline bool has_commands() const { return !command_queue_.IsEmpty(); }
inline bool ignore_events() const { return is_suppressed_ | | !is_active_; }
+
+  void OnException(Handle<Object> exception, bool uncaught,
+                   Handle<Object> promise);

   // Constructors for debug event objects.
   MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
@@ -545,8 +550,9 @@
   // Mirror cache handling.
   void ClearMirrorCache();

-  // Returns a promise if it does not have a reject handler.
-  Handle<Object> GetPromiseForUncaughtException();
+ // Returns a promise if the pushed try-catch handler matches the current one.
+  Handle<Object> GetPromiseOnStackOnThrow();
+  bool PromiseHasRejectHandler(Handle<JSObject> promise);

   void CallEventCallback(v8::DebugEvent event,
                          Handle<Object> exec_state,
=======================================
--- /branches/bleeding_edge/src/isolate.cc      Tue Aug  5 08:18:22 2014 UTC
+++ /branches/bleeding_edge/src/isolate.cc      Wed Aug  6 09:41:52 2014 UTC
@@ -1026,7 +1026,7 @@

   // Notify debugger of exception.
   if (catchable_by_javascript) {
-    debug()->OnException(exception_handle, report_exception);
+    debug()->OnThrow(exception_handle, report_exception);
   }

   // Generate the message if required.
=======================================
--- /branches/bleeding_edge/src/promise.js      Tue Aug  5 07:42:06 2014 UTC
+++ /branches/bleeding_edge/src/promise.js      Wed Aug  6 09:41:52 2014 UTC
@@ -18,6 +18,7 @@
 var PromiseChain;
 var PromiseCatch;
 var PromiseThen;
+var PromiseHasRejectHandler;

// mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
 // if we could move these property names into the closure below.
@@ -29,6 +30,7 @@
 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
+var promiseDebug = GLOBAL_PRIVATE("Promise#debug");
 var lastMicrotaskId = 0;

 (function() {
@@ -40,13 +42,13 @@
       throw MakeTypeError('resolver_not_a_function', [resolver]);
     var promise = PromiseInit(this);
     try {
-      %DebugPromiseHandlePrologue(function() { return promise });
+      %DebugPushPromise(promise);
       resolver(function(x) { PromiseResolve(promise, x) },
                function(r) { PromiseReject(promise, r) });
     } catch (e) {
       PromiseReject(promise, e);
     } finally {
-      %DebugPromiseHandleEpilogue();
+      %DebugPopPromise();
     }
   }

@@ -98,11 +100,7 @@

   function PromiseHandle(value, handler, deferred) {
     try {
-      %DebugPromiseHandlePrologue(
-          function() {
-            var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
- return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
-          });
+      %DebugPushPromise(deferred.promise);
       var result = handler(value);
       if (result === deferred.promise)
         throw MakeTypeError('promise_cyclic', [result]);
@@ -111,14 +109,9 @@
       else
         deferred.resolve(result);
     } catch (exception) {
-      try {
- %DebugPromiseHandlePrologue(function() { return deferred.promise });
-        deferred.reject(exception);
-      } catch (e) { } finally {
-        %DebugPromiseHandleEpilogue();
-      }
+      try { deferred.reject(exception); } catch (e) { }
     } finally {
-      %DebugPromiseHandleEpilogue();
+      %DebugPopPromise();
     }
   }

@@ -165,6 +158,13 @@
   }

   PromiseReject = function PromiseReject(promise, r) {
+    // Check promise status to confirm that this reject has an effect.
+    // Check promiseDebug property to avoid duplicate event.
+    if (DEBUG_IS_ACTIVE &&
+        GET_PRIVATE(promise, promiseStatus) == 0 &&
+        !HAS_PRIVATE(promise, promiseDebug)) {
+      %DebugPromiseRejectEvent(promise, r);
+    }
     PromiseDone(promise, -1, r, promiseOnReject)
   }

@@ -321,6 +321,16 @@
     }
     return deferred.promise;
   }
+
+
+  // Utility for debugger
+
+  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;
+  };

   // -------------------------------------------------------------------
   // Install exported functions.
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Aug  5 08:16:02 2014 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Wed Aug  6 09:41:52 2014 UTC
@@ -5465,22 +5465,19 @@
 }


-// The argument is a closure that is kept until the epilogue is called.
-// On exception, the closure is called, which returns the promise if the
-// exception is considered uncaught, or undefined otherwise.
-RUNTIME_FUNCTION(Runtime_DebugPromiseHandlePrologue) {
+RUNTIME_FUNCTION(Runtime_DebugPushPromise) {
   DCHECK(args.length() == 1);
   HandleScope scope(isolate);
-  CONVERT_ARG_HANDLE_CHECKED(JSFunction, promise_getter, 0);
-  isolate->debug()->PromiseHandlePrologue(promise_getter);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
+  isolate->debug()->PushPromise(promise);
   return isolate->heap()->undefined_value();
 }


-RUNTIME_FUNCTION(Runtime_DebugPromiseHandleEpilogue) {
+RUNTIME_FUNCTION(Runtime_DebugPopPromise) {
   DCHECK(args.length() == 0);
   SealHandleScope shs(isolate);
-  isolate->debug()->PromiseHandleEpilogue();
+  isolate->debug()->PopPromise();
   return isolate->heap()->undefined_value();
 }

@@ -5492,6 +5489,16 @@
   isolate->debug()->OnPromiseEvent(data);
   return isolate->heap()->undefined_value();
 }
+
+
+RUNTIME_FUNCTION(Runtime_DebugPromiseRejectEvent) {
+  DCHECK(args.length() == 2);
+  HandleScope scope(isolate);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
+  CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
+  isolate->debug()->OnPromiseReject(promise, value);
+  return isolate->heap()->undefined_value();
+}


 RUNTIME_FUNCTION(Runtime_DebugAsyncTaskEvent) {
=======================================
--- /branches/bleeding_edge/src/runtime.h       Fri Aug  1 09:23:07 2014 UTC
+++ /branches/bleeding_edge/src/runtime.h       Wed Aug  6 09:41:52 2014 UTC
@@ -76,9 +76,10 @@
   F(SetInlineBuiltinFlag, 1, 1)                             \
   F(StoreArrayLiteralElement, 5, 1)                         \
   F(DebugPrepareStepInIfStepping, 1, 1)                     \
-  F(DebugPromiseHandlePrologue, 1, 1)                       \
-  F(DebugPromiseHandleEpilogue, 0, 1)                       \
+  F(DebugPushPromise, 1, 1)                                 \
+  F(DebugPopPromise, 0, 1)                                  \
   F(DebugPromiseEvent, 1, 1)                                \
+  F(DebugPromiseRejectEvent, 2, 1)                          \
   F(DebugAsyncTaskEvent, 1, 1)                              \
   F(FlattenString, 1, 1)                                    \
   F(LoadMutableDouble, 2, 1)                                \
=======================================
--- /branches/bleeding_edge/test/mjsunit/mjsunit.status Tue Aug 5 09:24:27 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/mjsunit.status Wed Aug 6 09:41:52 2014 UTC
@@ -127,10 +127,14 @@
   'debug-stepout-scope-part3': [PASS, NO_VARIANTS],
   'debug-stepout-scope-part7': [PASS, NO_VARIANTS],
   'debug-stepout-to-builtin': [PASS, NO_VARIANTS],
-  'es6/debug-promises-throw-in-constructor': [PASS, NO_VARIANTS],
-  'es6/debug-promises-throw-in-reject': [PASS, NO_VARIANTS],
-  'es6/debug-promises-uncaught-all': [PASS, NO_VARIANTS],
-  'es6/debug-promises-uncaught-uncaught': [PASS, NO_VARIANTS],
+  'es6/debug-promises/throw-in-constructor': [PASS, NO_VARIANTS],
+  'es6/debug-promises/reject-in-constructor': [PASS, NO_VARIANTS],
+  'es6/debug-promises/throw-with-undefined-reject': [PASS, NO_VARIANTS],
+  'es6/debug-promises/throw-with-throw-in-reject': [PASS, NO_VARIANTS],
+  'es6/debug-promises/reject-with-throw-in-reject': [PASS, NO_VARIANTS],
+  'es6/debug-promises/throw-uncaught-all': [PASS, NO_VARIANTS],
+  'es6/debug-promises/throw-uncaught-uncaught': [PASS, NO_VARIANTS],
+  'es6/debug-promises/reject-uncaught-late': [PASS, NO_VARIANTS],
   'harmony/debug-blockscopes': [PASS, NO_VARIANTS],
   'harmony/generators-debug-scopes': [PASS, NO_VARIANTS],
   'regress/regress-1081309': [PASS, NO_VARIANTS],
=======================================
--- /branches/bleeding_edge/tools/generate-runtime-tests.py Tue Aug 5 07:42:06 2014 UTC +++ /branches/bleeding_edge/tools/generate-runtime-tests.py Wed Aug 6 09:41:52 2014 UTC
@@ -47,8 +47,8 @@
# that the parser doesn't bit-rot. Change the values as needed when you add, # remove or change runtime functions, but make sure we don't lose our ability
 # to parse them!
-EXPECTED_FUNCTION_COUNT = 425
-EXPECTED_FUZZABLE_COUNT = 328
+EXPECTED_FUNCTION_COUNT = 426
+EXPECTED_FUZZABLE_COUNT = 329
 EXPECTED_CCTEST_COUNT = 7
 EXPECTED_UNKNOWN_COUNT = 16
 EXPECTED_BUILTINS_COUNT = 813

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to