Introduce debug events for promises.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 30 Jun 2014 11:12:42 +0000 (11:12 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 30 Jun 2014 11:12:42 +0000 (11:12 +0000)
R=aandrey@chromium.org, rossberg@chromium.org
BUG=v8:3093
LOG=Y

Review URL: https://codereview.chromium.org/357603005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22086 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8-debug.h
src/debug-debugger.js
src/debug.cc
src/debug.h
src/promise.js
src/runtime.cc
src/runtime.h
test/mjsunit/es6/debug-promises-new-event.js [new file with mode: 0644]
test/mjsunit/runtime-gen/debugpromiseevent.js [new file with mode: 0644]
tools/generate-runtime-tests.py

index 90d8544..dceb997 100644 (file)
@@ -20,7 +20,8 @@ enum DebugEvent {
   BeforeCompile = 4,
   AfterCompile  = 5,
   CompileError = 6,
-  BreakForCommand = 7
+  PromiseEvent = 7,
+  BreakForCommand = 8
 };
 
 
index 5de8292..8afac2e 100644 (file)
@@ -19,7 +19,8 @@ Debug.DebugEvent = { Break: 1,
                      NewFunction: 3,
                      BeforeCompile: 4,
                      AfterCompile: 5,
-                     CompileError: 6 };
+                     CompileError: 6,
+                     PromiseEvent: 7 };
 
 // Types of exceptions that can be broken upon.
 Debug.ExceptionBreak = { Caught : 0,
@@ -1199,6 +1200,32 @@ function MakeScriptObject_(script, include_source) {
 }
 
 
+function MakePromiseEvent(event_data) {
+  if (event_data.type = "new Promise") {
+    return new NewPromiseEvent(event_data);
+  }
+}
+
+
+function PromiseGetter() {
+  return MakeMirror(this.promise_);
+}
+
+
+function NewPromiseEvent(event_data) {
+  this.resolver_ = event_data.resolver;
+  this.promise_ = event_data.promise;
+}
+
+
+NewPromiseEvent.prototype.promise = PromiseGetter;
+
+
+NewPromiseEvent.prototype.resolver = function() {
+  return MakeMirror(this.resolver_);
+}
+
+
 function DebugCommandProcessor(exec_state, opt_is_running) {
   this.exec_state_ = exec_state;
   this.running_ = opt_is_running || false;
index d99e6f5..a081127 100644 (file)
@@ -2544,6 +2544,13 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
 }
 
 
+MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) {
+  // Create the promise event object.
+  Handle<Object> argv[] = { event_data };
+  return MakeJSObject("MakePromiseEvent", ARRAY_SIZE(argv), argv);
+}
+
+
 void Debug::OnException(Handle<Object> exception, bool uncaught) {
   if (in_debug_scope() || ignore_events()) return;
 
@@ -2689,6 +2696,25 @@ void Debug::OnAfterCompile(Handle<Script> script) {
 }
 
 
+void Debug::OnPromiseEvent(Handle<JSObject> data) {
+  if (in_debug_scope() || ignore_events()) return;
+
+  HandleScope scope(isolate_);
+  DebugScope debug_scope(this);
+  if (debug_scope.failed()) return;
+
+  // Create the script collected state object.
+  Handle<Object> event_data;
+  // Bail out and don't call debugger if exception.
+  if (!MakePromiseEvent(data).ToHandle(&event_data)) return;
+
+  // Process debug event.
+  ProcessDebugEvent(v8::PromiseEvent,
+                    Handle<JSObject>::cast(event_data),
+                    true);
+}
+
+
 void Debug::ProcessDebugEvent(v8::DebugEvent event,
                               Handle<JSObject> event_data,
                               bool auto_continue) {
index c295df2..cfd7059 100644 (file)
@@ -365,6 +365,7 @@ class Debug {
   void OnCompileError(Handle<Script> script);
   void OnBeforeCompile(Handle<Script> script);
   void OnAfterCompile(Handle<Script> script);
+  void OnPromiseEvent(Handle<JSObject> data);
 
   // API facing.
   void SetEventListener(Handle<Object> callback, Handle<Object> data);
@@ -535,6 +536,8 @@ class Debug {
       Handle<Object> promise);
   MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
       Handle<Script> script, v8::DebugEvent type);
+  MUST_USE_RESULT MaybeHandle<Object> MakePromiseEvent(
+      Handle<JSObject> promise_event);
 
   // Mirror cache handling.
   void ClearMirrorCache();
index 819074e..8214445 100644 (file)
@@ -38,6 +38,11 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
     if (!IS_SPEC_FUNCTION(resolver))
       throw MakeTypeError('resolver_not_a_function', [resolver]);
     var promise = PromiseInit(this);
+    if (DEBUG_IS_ACTIVE) {
+      %DebugPromiseEvent({ type : "new Promise",
+                           promise: this,
+                           resolver: resolver });
+    }
     try {
       %DebugPromiseHandlePrologue(function() { return promise });
       resolver(function(x) { PromiseResolve(promise, x) },
index eba17a1..f341d14 100644 (file)
@@ -5530,6 +5530,15 @@ RUNTIME_FUNCTION(Runtime_DebugPromiseHandleEpilogue) {
 }
 
 
+RUNTIME_FUNCTION(Runtime_DebugPromiseEvent) {
+  ASSERT(args.length() == 1);
+  HandleScope scope(isolate);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, data, 0);
+  isolate->debug()->OnPromiseEvent(data);
+  return isolate->heap()->undefined_value();
+}
+
+
 RUNTIME_FUNCTION(Runtime_DeleteProperty) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 3);
index f01f173..ebc8598 100644 (file)
@@ -77,6 +77,7 @@ namespace internal {
   F(DebugPrepareStepInIfStepping, 1, 1) \
   F(DebugPromiseHandlePrologue, 1, 1) \
   F(DebugPromiseHandleEpilogue, 0, 1) \
+  F(DebugPromiseEvent, 1, 1) \
   F(FlattenString, 1, 1) \
   F(LoadMutableDouble, 2, 1) \
   F(TryMigrateInstance, 1, 1) \
diff --git a/test/mjsunit/es6/debug-promises-new-event.js b/test/mjsunit/es6/debug-promises-new-event.js
new file mode 100644 (file)
index 0000000..06a9774
--- /dev/null
@@ -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
+
+Debug = debug.Debug;
+
+var exception = null;
+var new_promise;
+
+function listener(event, exec_state, event_data, data) {
+  if (event != Debug.DebugEvent.PromiseEvent) return;
+  try {
+    assertTrue(event_data.resolver().isFunction());
+    assertEquals(resolver, event_data.resolver().value());
+    assertTrue(event_data.resolver().resolved());
+    assertEquals("resolver", event_data.resolver().name());
+    assertTrue(event_data.resolver().source().indexOf("Token") > 0);
+
+    assertTrue(event_data.promise().isPromise());
+    new_promise = event_data.promise().value();
+    assertEquals("pending", event_data.promise().status());
+  } catch (e) {
+    print(e + e.stack)
+    exception = e;
+  }
+}
+
+Debug.setListener(listener);
+
+function resolver(resolve, reject) {
+  resolve();  // Token
+}
+
+var p = new Promise(resolver);
+assertEquals(new_promise, p);
+
+assertNull(exception);
+Debug.setListener(null);
diff --git a/test/mjsunit/runtime-gen/debugpromiseevent.js b/test/mjsunit/runtime-gen/debugpromiseevent.js
new file mode 100644 (file)
index 0000000..8f5020b
--- /dev/null
@@ -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 _data = new Object();
+%DebugPromiseEvent(_data);
index 52b7dcb..f018cf0 100755 (executable)
@@ -47,11 +47,11 @@ EXPAND_MACROS = [
 # 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 = 415
-EXPECTED_FUZZABLE_COUNT = 330
+EXPECTED_FUNCTION_COUNT = 416
+EXPECTED_FUZZABLE_COUNT = 331
 EXPECTED_CCTEST_COUNT = 6
 EXPECTED_UNKNOWN_COUNT = 4
-EXPECTED_BUILTINS_COUNT = 806
+EXPECTED_BUILTINS_COUNT = 809
 
 
 # Don't call these at all.