Commit 73c9be9b authored by yangguo's avatar yangguo Committed by Commit bot

Debugger: allow stepping into resolver from Promise constructor.

R=rossberg@chromium.org
BUG=chromium:451967
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31296}
parent dd3f1ecf
...@@ -1185,7 +1185,7 @@ function InnerArrayFilter(f, receiver, array, length) { ...@@ -1185,7 +1185,7 @@ function InnerArrayFilter(f, receiver, array, length) {
var accumulator = new InternalArray(); var accumulator = new InternalArray();
var accumulator_length = 0; var accumulator_length = 0;
var is_array = IS_ARRAY(array); var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1216,7 +1216,7 @@ function InnerArrayForEach(f, receiver, array, length) { ...@@ -1216,7 +1216,7 @@ function InnerArrayForEach(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array); var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1242,7 +1242,7 @@ function InnerArraySome(f, receiver, array, length) { ...@@ -1242,7 +1242,7 @@ function InnerArraySome(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array); var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1272,7 +1272,7 @@ function InnerArrayEvery(f, receiver, array, length) { ...@@ -1272,7 +1272,7 @@ function InnerArrayEvery(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array); var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1300,7 +1300,7 @@ function InnerArrayMap(f, receiver, array, length) { ...@@ -1300,7 +1300,7 @@ function InnerArrayMap(f, receiver, array, length) {
var accumulator = new InternalArray(length); var accumulator = new InternalArray(length);
var is_array = IS_ARRAY(array); var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1469,7 +1469,7 @@ function InnerArrayReduce(callback, current, array, length, argumentsLength) { ...@@ -1469,7 +1469,7 @@ function InnerArrayReduce(callback, current, array, length, argumentsLength) {
throw MakeTypeError(kReduceNoInitial); throw MakeTypeError(kReduceNoInitial);
} }
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback); var stepping = DEBUG_IS_STEPPING(callback);
for (; i < length; i++) { for (; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
...@@ -1512,7 +1512,7 @@ function InnerArrayReduceRight(callback, current, array, length, ...@@ -1512,7 +1512,7 @@ function InnerArrayReduceRight(callback, current, array, length,
throw MakeTypeError(kReduceNoInitial); throw MakeTypeError(kReduceNoInitial);
} }
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(callback); var stepping = DEBUG_IS_STEPPING(callback);
for (; i >= 0; i--) { for (; i >= 0; i--) {
if (HAS_INDEX(array, i, is_array)) { if (HAS_INDEX(array, i, is_array)) {
var element = array[i]; var element = array[i];
......
...@@ -246,7 +246,7 @@ function SetForEach(f, receiver) { ...@@ -246,7 +246,7 @@ function SetForEach(f, receiver) {
var iterator = new SetIterator(this, ITERATOR_KIND_VALUES); var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
var key; var key;
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
var value_array = [UNDEFINED]; var value_array = [UNDEFINED];
while (%SetIteratorNext(iterator, value_array)) { while (%SetIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f); if (stepping) %DebugPrepareStepInIfStepping(f);
...@@ -429,7 +429,7 @@ function MapForEach(f, receiver) { ...@@ -429,7 +429,7 @@ function MapForEach(f, receiver) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f); if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES); var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); var stepping = DEBUG_IS_STEPPING(f);
var value_array = [UNDEFINED, UNDEFINED]; var value_array = [UNDEFINED, UNDEFINED];
while (%MapIteratorNext(iterator, value_array)) { while (%MapIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f); if (stepping) %DebugPrepareStepInIfStepping(f);
......
...@@ -33,7 +33,7 @@ function GeneratorObjectNext(value) { ...@@ -33,7 +33,7 @@ function GeneratorObjectNext(value) {
var continuation = %GeneratorGetContinuation(this); var continuation = %GeneratorGetContinuation(this);
if (continuation > 0) { if (continuation > 0) {
// Generator is suspended. // Generator is suspended.
if (DEBUG_IS_ACTIVE) %DebugPrepareStepInIfStepping(this); DEBUG_PREPARE_STEP_IN_IF_STEPPING(this);
try { try {
return %_GeneratorNext(this, value); return %_GeneratorNext(this, value);
} catch (e) { } catch (e) {
......
...@@ -305,7 +305,7 @@ define NOT_FOUND = -1; ...@@ -305,7 +305,7 @@ define NOT_FOUND = -1;
# Check whether debug is active. # Check whether debug is active.
define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0); define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function)); macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function));
macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (DEBUG_IS_STEPPING(function)) %DebugPrepareStepInIfStepping(function); macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (%_DebugIsActive() != 0) %DebugPrepareStepInIfStepping(function);
# SharedFlag equivalents # SharedFlag equivalents
define kNotShared = false; define kNotShared = false;
......
...@@ -34,7 +34,7 @@ var GlobalPromise = function Promise(resolver) { ...@@ -34,7 +34,7 @@ var GlobalPromise = function Promise(resolver) {
throw MakeTypeError(kResolverNotAFunction, resolver); throw MakeTypeError(kResolverNotAFunction, resolver);
var promise = PromiseInit(this); var promise = PromiseInit(this);
try { try {
%DebugPushPromise(promise, Promise); %DebugPushPromise(promise, Promise, resolver);
resolver(function(x) { PromiseResolve(promise, x) }, resolver(function(x) { PromiseResolve(promise, x) },
function(r) { PromiseReject(promise, r) }); function(r) { PromiseReject(promise, r) });
} catch (e) { } catch (e) {
...@@ -100,8 +100,7 @@ function PromiseCoerce(constructor, x) { ...@@ -100,8 +100,7 @@ function PromiseCoerce(constructor, x) {
function PromiseHandle(value, handler, deferred) { function PromiseHandle(value, handler, deferred) {
try { try {
%DebugPushPromise(deferred.promise, PromiseHandle); %DebugPushPromise(deferred.promise, PromiseHandle, handler);
DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler);
var result = handler(value); var result = handler(value);
if (result === deferred.promise) if (result === deferred.promise)
throw MakeTypeError(kPromiseCyclic, result); throw MakeTypeError(kPromiseCyclic, result);
......
...@@ -1612,33 +1612,55 @@ RUNTIME_FUNCTION(Runtime_GetScript) { ...@@ -1612,33 +1612,55 @@ RUNTIME_FUNCTION(Runtime_GetScript) {
} }
bool DebugStepInIsActive(Debug* debug) {
return debug->is_active() && debug->IsStepping() &&
debug->last_step_action() == StepIn;
}
// Check whether debugger is about to step into the callback that is passed // Check whether debugger is about to step into the callback that is passed
// to a built-in function such as Array.forEach. // to a built-in function such as Array.forEach. This check is done before
// %DebugPrepareStepInIfStepping and is not strictly necessary. However, if it
// returns false, we can skip %DebugPrepareStepInIfStepping, useful in loops.
RUNTIME_FUNCTION(Runtime_DebugCallbackSupportsStepping) { RUNTIME_FUNCTION(Runtime_DebugCallbackSupportsStepping) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
Debug* debug = isolate->debug(); if (!DebugStepInIsActive(isolate->debug())) {
if (!debug->is_active() || !debug->IsStepping() ||
debug->last_step_action() != StepIn) {
return isolate->heap()->false_value(); return isolate->heap()->false_value();
} }
CONVERT_ARG_CHECKED(Object, callback, 0); CONVERT_ARG_CHECKED(Object, object, 0);
RUNTIME_ASSERT(object->IsJSFunction() || object->IsJSGeneratorObject());
// We do not step into the callback if it's a builtin other than a bound, // We do not step into the callback if it's a builtin other than a bound,
// or not even a function. // or not even a function.
return isolate->heap()->ToBoolean( JSFunction* fun;
callback->IsJSFunction() && if (object->IsJSFunction()) {
(JSFunction::cast(callback)->IsSubjectToDebugging() || fun = JSFunction::cast(object);
JSFunction::cast(callback)->shared()->bound())); } else {
fun = JSGeneratorObject::cast(object)->function();
}
return isolate->heap()->ToBoolean(fun->IsSubjectToDebugging() ||
fun->shared()->bound());
}
void FloodDebugSubjectWithOneShot(Debug* debug, Handle<JSFunction> function) {
if (function->IsSubjectToDebugging() || function->shared()->bound()) {
// When leaving the function, step out has been activated, but not performed
// if we do not leave the builtin. To be able to step into the function
// again, we need to clear the step out at this point.
debug->ClearStepOut();
debug->FloodWithOneShotGeneric(function);
}
} }
// Set one shot breakpoints for the callback function that is passed to a // Set one shot breakpoints for the callback function that is passed to a
// built-in function such as Array.forEach to enable stepping into the callback. // built-in function such as Array.forEach to enable stepping into the callback,
// if we are indeed stepping and the callback is subject to debugging.
RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) { RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
RUNTIME_ASSERT(isolate->debug()->is_active());
Debug* debug = isolate->debug(); Debug* debug = isolate->debug();
if (!debug->IsStepping()) return isolate->heap()->undefined_value(); if (!DebugStepInIsActive(debug)) return isolate->heap()->undefined_value();
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
...@@ -1650,21 +1672,23 @@ RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) { ...@@ -1650,21 +1672,23 @@ RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
fun = Handle<JSFunction>( fun = Handle<JSFunction>(
Handle<JSGeneratorObject>::cast(object)->function(), isolate); Handle<JSGeneratorObject>::cast(object)->function(), isolate);
} }
// When leaving the function, step out has been activated, but not performed
// if we do not leave the builtin. To be able to step into the function FloodDebugSubjectWithOneShot(debug, fun);
// again, we need to clear the step out at this point.
debug->ClearStepOut();
debug->FloodWithOneShotGeneric(fun);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugPushPromise) { RUNTIME_FUNCTION(Runtime_DebugPushPromise) {
DCHECK(args.length() == 2); DCHECK(args.length() == 3);
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1); CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, handler, 2);
isolate->PushPromise(promise, function); isolate->PushPromise(promise, function);
Debug* debug = isolate->debug();
if (handler->IsJSFunction() && DebugStepInIsActive(debug)) {
FloodDebugSubjectWithOneShot(debug, Handle<JSFunction>::cast(handler));
}
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -201,7 +201,7 @@ namespace internal { ...@@ -201,7 +201,7 @@ namespace internal {
F(GetScript, 1, 1) \ F(GetScript, 1, 1) \
F(DebugCallbackSupportsStepping, 1, 1) \ F(DebugCallbackSupportsStepping, 1, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \ F(DebugPrepareStepInIfStepping, 1, 1) \
F(DebugPushPromise, 2, 1) \ F(DebugPushPromise, 3, 1) \
F(DebugPopPromise, 0, 1) \ F(DebugPopPromise, 0, 1) \
F(DebugPromiseEvent, 1, 1) \ F(DebugPromiseEvent, 1, 1) \
F(DebugAsyncTaskEvent, 1, 1) \ F(DebugAsyncTaskEvent, 1, 1) \
......
// Copyright 2015 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
var Debug = debug.Debug;
var exception = null;
var breaks = [];
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
breaks.push(exec_state.frame(0).sourceLineText().trimLeft());
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} catch (e) {
exception = e;
}
}
Debug.setListener(listener);
function resolver(resolve, reject) {
1;
2;
3;
resolve();
}
debugger;
var p = new Promise(resolver);
Debug.setListener(null);
var expected_breaks = [
"debugger;",
"var p = new Promise(resolver);",
"1;",
"2;",
"3;",
"resolve();",
"}",
"Debug.setListener(null);"
];
assertEquals(expected_breaks, breaks);
assertNull(exception);
...@@ -10,7 +10,7 @@ Debug.setBreakOnException(); ...@@ -10,7 +10,7 @@ Debug.setBreakOnException();
try { try {
try { try {
%DebugPushPromise(new Promise(function() {}), function() {}); %DebugPushPromise(new Promise(function() {}), function() {}, function() {});
} catch (e) { } catch (e) {
} }
throw new Error(); throw new Error();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment