Introduce debug events for Microtask queue.
R=yangguo@chromium.org, adamk@chromium.org, rafaelw@chromium.org, rossberg@chromium.org BUG=chromium:272416 LOG=Y Review URL: https://codereview.chromium.org/362783002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22204 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
7acb28a120
commit
952a986dd1
@ -21,7 +21,8 @@ enum DebugEvent {
|
||||
AfterCompile = 5,
|
||||
CompileError = 6,
|
||||
PromiseEvent = 7,
|
||||
BreakForCommand = 8
|
||||
AsyncTaskEvent = 8,
|
||||
BreakForCommand = 9
|
||||
};
|
||||
|
||||
|
||||
|
@ -20,7 +20,8 @@ Debug.DebugEvent = { Break: 1,
|
||||
BeforeCompile: 4,
|
||||
AfterCompile: 5,
|
||||
CompileError: 6,
|
||||
PromiseEvent: 7 };
|
||||
PromiseEvent: 7,
|
||||
AsyncTaskEvent: 8 };
|
||||
|
||||
// Types of exceptions that can be broken upon.
|
||||
Debug.ExceptionBreak = { Caught : 0,
|
||||
@ -1226,6 +1227,33 @@ NewPromiseEvent.prototype.resolver = function() {
|
||||
}
|
||||
|
||||
|
||||
function MakeAsyncTaskEvent(event_data) {
|
||||
return new AsyncTaskEvent(event_data);
|
||||
}
|
||||
|
||||
|
||||
function AsyncTaskEvent(event_data) {
|
||||
this.type_ = event_data.type;
|
||||
this.name_ = event_data.name;
|
||||
this.id_ = event_data.id;
|
||||
}
|
||||
|
||||
|
||||
AsyncTaskEvent.prototype.type = function() {
|
||||
return this.type_;
|
||||
}
|
||||
|
||||
|
||||
AsyncTaskEvent.prototype.name = function() {
|
||||
return this.name_;
|
||||
}
|
||||
|
||||
|
||||
AsyncTaskEvent.prototype.id = function() {
|
||||
return this.id_;
|
||||
}
|
||||
|
||||
|
||||
function DebugCommandProcessor(exec_state, opt_is_running) {
|
||||
this.exec_state_ = exec_state;
|
||||
this.running_ = opt_is_running || false;
|
||||
|
26
src/debug.cc
26
src/debug.cc
@ -2554,6 +2554,13 @@ MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) {
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) {
|
||||
// Create the async task event object.
|
||||
Handle<Object> argv[] = { task_event };
|
||||
return MakeJSObject("MakeAsyncTaskEvent", ARRAY_SIZE(argv), argv);
|
||||
}
|
||||
|
||||
|
||||
void Debug::OnException(Handle<Object> exception, bool uncaught) {
|
||||
if (in_debug_scope() || ignore_events()) return;
|
||||
|
||||
@ -2718,6 +2725,25 @@ void Debug::OnPromiseEvent(Handle<JSObject> data) {
|
||||
}
|
||||
|
||||
|
||||
void Debug::OnAsyncTaskEvent(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 (!MakeAsyncTaskEvent(data).ToHandle(&event_data)) return;
|
||||
|
||||
// Process debug event.
|
||||
ProcessDebugEvent(v8::AsyncTaskEvent,
|
||||
Handle<JSObject>::cast(event_data),
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
void Debug::ProcessDebugEvent(v8::DebugEvent event,
|
||||
Handle<JSObject> event_data,
|
||||
bool auto_continue) {
|
||||
|
@ -366,6 +366,7 @@ class Debug {
|
||||
void OnBeforeCompile(Handle<Script> script);
|
||||
void OnAfterCompile(Handle<Script> script);
|
||||
void OnPromiseEvent(Handle<JSObject> data);
|
||||
void OnAsyncTaskEvent(Handle<JSObject> data);
|
||||
|
||||
// API facing.
|
||||
void SetEventListener(Handle<Object> callback, Handle<Object> data);
|
||||
@ -538,6 +539,8 @@ class Debug {
|
||||
Handle<Script> script, v8::DebugEvent type);
|
||||
MUST_USE_RESULT MaybeHandle<Object> MakePromiseEvent(
|
||||
Handle<JSObject> promise_event);
|
||||
MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
|
||||
Handle<JSObject> task_event);
|
||||
|
||||
// Mirror cache handling.
|
||||
void ClearMirrorCache();
|
||||
|
@ -45,6 +45,7 @@ function GetObservationStateJS() {
|
||||
observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
|
||||
observationState.pendingObservers = null;
|
||||
observationState.nextCallbackPriority = 0;
|
||||
observationState.lastMicrotaskId = 0;
|
||||
}
|
||||
|
||||
return observationState;
|
||||
@ -421,7 +422,18 @@ function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
|
||||
var callbackInfo = CallbackInfoNormalize(callback);
|
||||
if (IS_NULL(GetPendingObservers())) {
|
||||
SetPendingObservers(nullProtoObject());
|
||||
%EnqueueMicrotask(ObserveMicrotaskRunner);
|
||||
if (DEBUG_IS_ACTIVE) {
|
||||
var id = ++GetObservationStateJS().lastMicrotaskId;
|
||||
var name = "Object.observe";
|
||||
%EnqueueMicrotask(function() {
|
||||
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
|
||||
ObserveMicrotaskRunner();
|
||||
%DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
|
||||
});
|
||||
%DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
|
||||
} else {
|
||||
%EnqueueMicrotask(ObserveMicrotaskRunner);
|
||||
}
|
||||
}
|
||||
GetPendingObservers()[callbackInfo.priority] = callback;
|
||||
callbackInfo.push(changeRecord);
|
||||
|
@ -29,6 +29,7 @@ var promiseValue = GLOBAL_PRIVATE("Promise#value");
|
||||
var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
|
||||
var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
|
||||
var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
||||
var lastMicrotaskId = 0;
|
||||
|
||||
(function() {
|
||||
|
||||
@ -71,7 +72,7 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
||||
|
||||
function PromiseDone(promise, status, value, promiseQueue) {
|
||||
if (GET_PRIVATE(promise, promiseStatus) === 0) {
|
||||
PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
|
||||
PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue), status);
|
||||
PromiseSet(promise, status, value);
|
||||
}
|
||||
}
|
||||
@ -123,12 +124,24 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
||||
}
|
||||
}
|
||||
|
||||
function PromiseEnqueue(value, tasks) {
|
||||
function PromiseEnqueue(value, tasks, status) {
|
||||
var id, name, instrumenting = DEBUG_IS_ACTIVE;
|
||||
%EnqueueMicrotask(function() {
|
||||
if (instrumenting) {
|
||||
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
|
||||
}
|
||||
for (var i = 0; i < tasks.length; i += 2) {
|
||||
PromiseHandle(value, tasks[i], tasks[i + 1])
|
||||
}
|
||||
if (instrumenting) {
|
||||
%DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
|
||||
}
|
||||
});
|
||||
if (instrumenting) {
|
||||
id = ++lastMicrotaskId;
|
||||
name = status > 0 ? "Promise.Resolved" : "Promise.Rejected";
|
||||
%DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
|
||||
}
|
||||
}
|
||||
|
||||
function PromiseIdResolveHandler(x) { return x }
|
||||
@ -199,7 +212,7 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
||||
// Simple chaining.
|
||||
|
||||
PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a.
|
||||
// flatMap
|
||||
// flatMap
|
||||
onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
|
||||
onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
|
||||
var deferred = %_CallFunction(this.constructor, PromiseDeferred);
|
||||
@ -211,10 +224,14 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
||||
GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
|
||||
break;
|
||||
case +1: // Resolved
|
||||
PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
|
||||
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
|
||||
[onResolve, deferred],
|
||||
+1);
|
||||
break;
|
||||
case -1: // Rejected
|
||||
PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
|
||||
PromiseEnqueue(GET_PRIVATE(this, promiseValue),
|
||||
[onReject, deferred],
|
||||
-1);
|
||||
break;
|
||||
}
|
||||
return deferred.promise;
|
||||
|
@ -5576,6 +5576,15 @@ RUNTIME_FUNCTION(Runtime_DebugPromiseEvent) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DebugAsyncTaskEvent) {
|
||||
ASSERT(args.length() == 1);
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, data, 0);
|
||||
isolate->debug()->OnAsyncTaskEvent(data);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DeleteProperty) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 3);
|
||||
|
@ -78,6 +78,7 @@ namespace internal {
|
||||
F(DebugPromiseHandlePrologue, 1, 1) \
|
||||
F(DebugPromiseHandleEpilogue, 0, 1) \
|
||||
F(DebugPromiseEvent, 1, 1) \
|
||||
F(DebugAsyncTaskEvent, 1, 1) \
|
||||
F(FlattenString, 1, 1) \
|
||||
F(LoadMutableDouble, 2, 1) \
|
||||
F(TryMigrateInstance, 1, 1) \
|
||||
|
61
test/mjsunit/es6/debug-promises-async-task-event.js
Normal file
61
test/mjsunit/es6/debug-promises-async-task-event.js
Normal file
@ -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.Resolved", 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);
|
@ -30,6 +30,7 @@ q.catch(
|
||||
});
|
||||
|
||||
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;
|
||||
|
@ -26,6 +26,7 @@ var q = p.chain(
|
||||
});
|
||||
|
||||
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;
|
||||
|
@ -25,6 +25,7 @@ var q = p.chain(
|
||||
});
|
||||
|
||||
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;
|
||||
|
51
test/mjsunit/es7/object-observe-debug-event.js
Normal file
51
test/mjsunit/es7/object-observe-debug-event.js
Normal file
@ -0,0 +1,51 @@
|
||||
// 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",
|
||||
"didHandle #1",
|
||||
];
|
||||
|
||||
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("Object.observe", event_data.name());
|
||||
assertLog(event_data.type() + " #" + id);
|
||||
} catch (e) {
|
||||
print(e + e.stack)
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.setListener(listener);
|
||||
|
||||
var obj = {};
|
||||
Object.observe(obj, function(changes) {
|
||||
print(change.type + " " + change.name + " " + change.oldValue);
|
||||
});
|
||||
|
||||
obj.foo = 1;
|
||||
obj.zoo = 2;
|
||||
obj.foo = 3;
|
||||
|
||||
assertNull(exception);
|
5
test/mjsunit/runtime-gen/debugasynctaskevent.js
Normal file
5
test/mjsunit/runtime-gen/debugasynctaskevent.js
Normal file
@ -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();
|
||||
%DebugAsyncTaskEvent(_data);
|
@ -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 = 416
|
||||
EXPECTED_FUZZABLE_COUNT = 331
|
||||
EXPECTED_FUNCTION_COUNT = 417
|
||||
EXPECTED_FUZZABLE_COUNT = 332
|
||||
EXPECTED_CCTEST_COUNT = 6
|
||||
EXPECTED_UNKNOWN_COUNT = 4
|
||||
EXPECTED_BUILTINS_COUNT = 808
|
||||
EXPECTED_BUILTINS_COUNT = 810
|
||||
|
||||
|
||||
# Don't call these at all.
|
||||
|
Loading…
Reference in New Issue
Block a user