2016-10-02 19:41:17 +00:00
|
|
|
// Copyright 2016 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.
|
|
|
|
|
|
|
|
InspectorTest = {};
|
|
|
|
InspectorTest._dumpInspectorProtocolMessages = false;
|
2017-03-22 18:07:12 +00:00
|
|
|
InspectorTest._commandsForLogging = new Set();
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest._sessions = new Set();
|
2016-10-03 23:32:52 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.log = utils.print.bind(utils);
|
|
|
|
InspectorTest.quitImmediately = utils.quit.bind(utils);
|
|
|
|
|
|
|
|
InspectorTest.logProtocolCommandCalls = function(command) {
|
|
|
|
InspectorTest._commandsForLogging.add(command);
|
2017-05-18 23:11:20 +00:00
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.completeTest = function() {
|
|
|
|
var promises = [];
|
|
|
|
for (var session of InspectorTest._sessions)
|
|
|
|
promises.push(session.Protocol.Debugger.disable());
|
|
|
|
Promise.all(promises).then(() => utils.quit());
|
2017-05-18 23:11:20 +00:00
|
|
|
}
|
2016-10-02 19:41:17 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.waitForPendingTasks = function() {
|
|
|
|
var promises = [];
|
|
|
|
for (var session of InspectorTest._sessions)
|
|
|
|
promises.push(session.Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-for-pending-tasks.js", awaitPromise: true }));
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
[debugger] tuned StepNext and StepOut at return position
Proposed behaviour:
- StepNext at return position go into next function call (no changes with current behavior, but implemented in v8::Debug instead of hack on inspector side);
- StepOut at return position go into next non-current function call.
We need this to have better stepping in cases with native functions, blackboxed functions and/or different embedder calls (e.g. event listeners).
New behavior could be illustrated with two examples (for more see stepping-with-natives-and-frameworks test):
- let's assume that we've blackboxed callAll function, this function just takes its arguments and call one after another:
var foo = () => 1;
callAll(foo, foo, () => 2);
If we break inside of first call of function foo. Then on..
..StepNext - we're able to reach second call of function foo,
..StepOut - we're able to reach () => 2 call.
- let's consider case with native function:
[1,2,3].map(x => x * 2)
If we break inside of first callback call, then with StepNext we can iterate through all calls of callback, with StepOut we go to next statement after .map call.
Implementation details:
- when we request break we schedule step-in function call for any step action at return position and for step-in at any position,
- when we request StepOut at return position - we mark current function as needed-to-be-ignored inside of PrepareStepIn(function) call,
- when we request StepOut at not return position - we set break at return position and ask debugger to just repeat last step action on next stepping-related break.
Design doc: https://docs.google.com/document/d/1ihXHOIhP_q-fJCA0e2EiXz_Zr3B08KMjaPifcaqZ60Q/edit
BUG=v8:6118,chromium:583193
R=dgozman@chromium.org,yangguo@chromium.org
Review-Url: https://codereview.chromium.org/2758483002
Cr-Commit-Position: refs/heads/master@{#44028}
2017-03-22 14:16:18 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.startDumpingProtocolMessages = function() {
|
|
|
|
InspectorTest._dumpInspectorProtocolMessages = true;
|
|
|
|
}
|
2016-10-02 19:41:17 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.logMessage = function(originalMessage) {
|
2017-11-22 19:46:33 +00:00
|
|
|
const nonStableFields = new Set([
|
|
|
|
'objectId', 'scriptId', 'exceptionId', 'timestamp', 'executionContextId',
|
|
|
|
'callFrameId', 'breakpointId', 'bindRemoteObjectFunctionId',
|
|
|
|
'formatterObjectId', 'debuggerId'
|
|
|
|
]);
|
2018-10-09 04:13:49 +00:00
|
|
|
const message = JSON.parse(JSON.stringify(originalMessage, replacer.bind(null, Symbol(), nonStableFields)));
|
|
|
|
if (message.id)
|
|
|
|
message.id = '<messageId>';
|
2016-10-02 19:41:17 +00:00
|
|
|
|
2016-10-03 23:32:52 +00:00
|
|
|
InspectorTest.logObject(message);
|
2016-11-04 19:59:11 +00:00
|
|
|
return originalMessage;
|
2018-10-09 04:13:49 +00:00
|
|
|
|
|
|
|
function replacer(stableIdSymbol, nonStableFields, name, val) {
|
|
|
|
if (nonStableFields.has(name))
|
|
|
|
return `<${name}>`;
|
|
|
|
if (name === 'internalProperties') {
|
|
|
|
const stableId = val.find(prop => prop.name === '[[StableObjectId]]');
|
|
|
|
if (stableId)
|
|
|
|
stableId.value[stableIdSymbol] = true;
|
|
|
|
}
|
|
|
|
if (val && val[stableIdSymbol])
|
|
|
|
return '<StablectObjectId>';
|
|
|
|
return val;
|
|
|
|
}
|
2016-10-03 23:32:52 +00:00
|
|
|
}
|
2016-10-02 19:41:17 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.logObject = function(object, title) {
|
2016-10-02 19:41:17 +00:00
|
|
|
var lines = [];
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
function dumpValue(value, prefix, prefixWithName) {
|
2016-10-02 19:41:17 +00:00
|
|
|
if (typeof value === "object" && value !== null) {
|
|
|
|
if (value instanceof Array)
|
|
|
|
dumpItems(value, prefix, prefixWithName);
|
|
|
|
else
|
|
|
|
dumpProperties(value, prefix, prefixWithName);
|
|
|
|
} else {
|
|
|
|
lines.push(prefixWithName + String(value).replace(/\n/g, " "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
function dumpProperties(object, prefix, firstLinePrefix) {
|
2016-10-02 19:41:17 +00:00
|
|
|
prefix = prefix || "";
|
|
|
|
firstLinePrefix = firstLinePrefix || prefix;
|
|
|
|
lines.push(firstLinePrefix + "{");
|
|
|
|
|
|
|
|
var propertyNames = Object.keys(object);
|
|
|
|
propertyNames.sort();
|
|
|
|
for (var i = 0; i < propertyNames.length; ++i) {
|
|
|
|
var name = propertyNames[i];
|
|
|
|
if (!object.hasOwnProperty(name))
|
|
|
|
continue;
|
|
|
|
var prefixWithName = " " + prefix + name + " : ";
|
|
|
|
dumpValue(object[name], " " + prefix, prefixWithName);
|
|
|
|
}
|
|
|
|
lines.push(prefix + "}");
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
function dumpItems(object, prefix, firstLinePrefix) {
|
2016-10-02 19:41:17 +00:00
|
|
|
prefix = prefix || "";
|
|
|
|
firstLinePrefix = firstLinePrefix || prefix;
|
|
|
|
lines.push(firstLinePrefix + "[");
|
|
|
|
for (var i = 0; i < object.length; ++i)
|
|
|
|
dumpValue(object[i], " " + prefix, " " + prefix + "[" + i + "] : ");
|
|
|
|
lines.push(prefix + "]");
|
|
|
|
}
|
|
|
|
|
2016-10-28 06:47:58 +00:00
|
|
|
dumpValue(object, "", title || "");
|
2016-10-02 19:41:17 +00:00
|
|
|
InspectorTest.log(lines.join("\n"));
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.ContextGroup = class {
|
|
|
|
constructor() {
|
|
|
|
this.id = utils.createContextGroup();
|
2016-12-13 19:40:14 +00:00
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
schedulePauseOnNextStatement(reason, details) {
|
|
|
|
utils.schedulePauseOnNextStatement(this.id, reason, details);
|
2017-02-27 18:58:55 +00:00
|
|
|
}
|
2017-05-19 00:35:45 +00:00
|
|
|
|
|
|
|
cancelPauseOnNextStatement() {
|
|
|
|
utils.cancelPauseOnNextStatement(this.id);
|
2017-02-27 18:58:55 +00:00
|
|
|
}
|
2017-05-19 00:35:45 +00:00
|
|
|
|
|
|
|
addScript(string, lineOffset, columnOffset, url) {
|
|
|
|
utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
|
2017-02-27 18:58:55 +00:00
|
|
|
}
|
|
|
|
|
2018-05-30 14:21:39 +00:00
|
|
|
addInlineScript(string, url) {
|
|
|
|
const match = (new Error().stack).split('\n')[2].match(/([0-9]+):([0-9]+)/);
|
|
|
|
this.addScript(
|
|
|
|
string, match[1] * 1, match[1] * 1 + '.addInlineScript('.length, url);
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
addModule(string, url, lineOffset, columnOffset) {
|
|
|
|
utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
|
|
|
|
}
|
2017-03-03 12:38:41 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
loadScript(fileName) {
|
|
|
|
this.addScript(utils.read(fileName));
|
|
|
|
}
|
|
|
|
|
|
|
|
connect() {
|
|
|
|
return new InspectorTest.Session(this);
|
|
|
|
}
|
|
|
|
|
2017-08-25 23:13:23 +00:00
|
|
|
setupInjectedScriptEnvironment(session) {
|
2017-05-19 00:35:45 +00:00
|
|
|
let scriptSource = '';
|
|
|
|
// First define all getters on Object.prototype.
|
|
|
|
let injectedScriptSource = utils.read('src/inspector/injected-script-source.js');
|
|
|
|
let getterRegex = /\.[a-zA-Z0-9]+/g;
|
|
|
|
let match;
|
|
|
|
let getters = new Set();
|
|
|
|
while (match = getterRegex.exec(injectedScriptSource)) {
|
|
|
|
getters.add(match[0].substr(1));
|
|
|
|
}
|
|
|
|
scriptSource += `(function installSettersAndGetters() {
|
|
|
|
let defineProperty = Object.defineProperty;
|
2017-08-25 23:13:23 +00:00
|
|
|
let ObjectPrototype = Object.prototype;
|
|
|
|
let ArrayPrototype = Array.prototype;
|
|
|
|
defineProperty(ArrayPrototype, 0, {
|
|
|
|
set() { debugger; throw 42; }, get() { debugger; throw 42; },
|
|
|
|
__proto__: null
|
|
|
|
});`,
|
|
|
|
scriptSource += Array.from(getters).map(getter => `
|
2017-05-19 00:35:45 +00:00
|
|
|
defineProperty(ObjectPrototype, '${getter}', {
|
|
|
|
set() { debugger; throw 42; }, get() { debugger; throw 42; },
|
|
|
|
__proto__: null
|
|
|
|
});
|
|
|
|
`).join('\n') + '})();';
|
|
|
|
this.addScript(scriptSource);
|
|
|
|
|
2017-08-25 23:13:23 +00:00
|
|
|
if (session) {
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
|
|
|
|
InspectorTest.log('WARNING: run test with --expose-inspector-scripts flag to get more details.');
|
|
|
|
InspectorTest.log('WARNING: you can additionally comment rjsmin in xxd.py to get unminified injected-script-source.js.');
|
|
|
|
session.setupScriptMap();
|
2017-08-25 23:13:23 +00:00
|
|
|
session.Protocol.Debugger.enable();
|
2017-05-19 00:35:45 +00:00
|
|
|
session.Protocol.Debugger.onPaused(message => {
|
|
|
|
let callFrames = message.params.callFrames;
|
|
|
|
session.logSourceLocations(callFrames.map(frame => frame.location));
|
|
|
|
})
|
2017-01-26 09:32:37 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-19 00:35:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
InspectorTest.Session = class {
|
|
|
|
constructor(contextGroup) {
|
|
|
|
this.contextGroup = contextGroup;
|
|
|
|
this._dispatchTable = new Map();
|
|
|
|
this._eventHandlers = new Map();
|
|
|
|
this._requestId = 0;
|
|
|
|
this.Protocol = this._setupProtocol();
|
|
|
|
InspectorTest._sessions.add(this);
|
|
|
|
this.id = utils.connectSession(contextGroup.id, '', this._dispatchMessage.bind(this));
|
|
|
|
}
|
2017-01-26 09:32:37 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
disconnect() {
|
|
|
|
InspectorTest._sessions.delete(this);
|
|
|
|
utils.disconnectSession(this.id);
|
|
|
|
}
|
2016-10-02 19:41:17 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
reconnect() {
|
|
|
|
var state = utils.disconnectSession(this.id);
|
|
|
|
this.id = utils.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
|
|
|
|
}
|
2017-06-05 17:37:25 +00:00
|
|
|
|
|
|
|
async addInspectedObject(serializable) {
|
|
|
|
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
|
|
|
|
}
|
2016-10-03 21:10:40 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
sendRawCommand(requestId, command, handler) {
|
|
|
|
if (InspectorTest._dumpInspectorProtocolMessages)
|
|
|
|
utils.print("frontend: " + command);
|
|
|
|
this._dispatchTable.set(requestId, handler);
|
|
|
|
utils.sendMessageToBackend(this.id, command);
|
|
|
|
}
|
2017-01-31 00:19:41 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
setupScriptMap() {
|
|
|
|
if (this._scriptMap)
|
|
|
|
return;
|
|
|
|
this._scriptMap = new Map();
|
|
|
|
}
|
2016-10-03 23:32:52 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
logCallFrames(callFrames) {
|
|
|
|
for (var frame of callFrames) {
|
|
|
|
var functionName = frame.functionName || '(anonymous)';
|
2018-05-30 14:21:39 +00:00
|
|
|
var scriptId = frame.location ? frame.location.scriptId : frame.scriptId;
|
|
|
|
var url = frame.url ? frame.url : this._scriptMap.get(scriptId).url;
|
2017-05-19 00:35:45 +00:00
|
|
|
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
|
|
|
|
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
|
|
|
|
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
|
|
|
|
}
|
2016-10-02 19:41:17 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 18:48:22 +00:00
|
|
|
logSourceLocation(location, forceSourceRequest) {
|
2017-05-19 00:35:45 +00:00
|
|
|
var scriptId = location.scriptId;
|
|
|
|
if (!this._scriptMap || !this._scriptMap.has(scriptId)) {
|
|
|
|
InspectorTest.log("setupScriptMap should be called before Protocol.Debugger.enable.");
|
|
|
|
InspectorTest.completeTest();
|
|
|
|
}
|
|
|
|
var script = this._scriptMap.get(scriptId);
|
2017-07-28 18:48:22 +00:00
|
|
|
if (!script.scriptSource || forceSourceRequest) {
|
2017-05-19 00:35:45 +00:00
|
|
|
return this.Protocol.Debugger.getScriptSource({ scriptId })
|
|
|
|
.then(message => script.scriptSource = message.result.scriptSource)
|
|
|
|
.then(dumpSourceWithLocation);
|
|
|
|
}
|
|
|
|
return Promise.resolve().then(dumpSourceWithLocation);
|
|
|
|
|
|
|
|
function dumpSourceWithLocation() {
|
|
|
|
var lines = script.scriptSource.split('\n');
|
|
|
|
var line = lines[location.lineNumber];
|
|
|
|
line = line.slice(0, location.columnNumber) + '#' + (line.slice(location.columnNumber) || '');
|
|
|
|
lines[location.lineNumber] = line;
|
|
|
|
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
|
|
|
|
InspectorTest.log(lines.slice(Math.max(location.lineNumber - 1, 0), location.lineNumber + 2).join('\n'));
|
|
|
|
InspectorTest.log('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logSourceLocations(locations) {
|
|
|
|
if (locations.length == 0) return Promise.resolve();
|
|
|
|
return this.logSourceLocation(locations[0]).then(() => this.logSourceLocations(locations.splice(1)));
|
|
|
|
}
|
|
|
|
|
2017-06-07 12:26:44 +00:00
|
|
|
async logBreakLocations(inputLocations) {
|
|
|
|
let locations = inputLocations.slice();
|
[inspector] moved var initialization break location before init expression (reland)
This CL improves break locations for expressions like 'var a = <expr>'. Without CL we use <expr> position as break location for initialization statement, with this CL we use position of first character after '=' as position.
Benefits (see test for details):
- only one break in expressions which includes mix of property lookup and calls, e.g. var p = Promise.resolve().then(x => x * 2),
- removed redundant break location for expressions like: let { x, y } = { x: 1, y: 2}.
TBR=dgozman@chromium.org,rmcilroy@chromium.org,machenbach@chromium.org,marja@chromium.org,kozyatinskiy@chromium.org,devtools-reviews@chromium.org,v8-reviews@googlegroups.com
# Not skipping CQ checks because original CL landed > 1 day ago.
Bug: v8:5909
Change-Id: Ie84fa79afeed09e28cf8478ba610a0cfbfdfc294
Reviewed-on: https://chromium-review.googlesource.com/518116
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45598}
2017-05-30 12:25:55 +00:00
|
|
|
let scriptId = locations[0].scriptId;
|
|
|
|
let script = this._scriptMap.get(scriptId);
|
|
|
|
if (!script.scriptSource) {
|
|
|
|
let message = await this.Protocol.Debugger.getScriptSource({scriptId});
|
|
|
|
script.scriptSource = message.result.scriptSource;
|
|
|
|
}
|
|
|
|
let lines = script.scriptSource.split('\n');
|
|
|
|
locations = locations.sort((loc1, loc2) => {
|
|
|
|
if (loc2.lineNumber !== loc1.lineNumber) return loc2.lineNumber - loc1.lineNumber;
|
|
|
|
return loc2.columnNumber - loc1.columnNumber;
|
|
|
|
});
|
|
|
|
for (let location of locations) {
|
|
|
|
let line = lines[location.lineNumber];
|
|
|
|
line = line.slice(0, location.columnNumber) + locationMark(location.type) + line.slice(location.columnNumber);
|
|
|
|
lines[location.lineNumber] = line;
|
|
|
|
}
|
|
|
|
lines = lines.filter(line => line.indexOf('//# sourceURL=') === -1);
|
|
|
|
InspectorTest.log(lines.join('\n') + '\n');
|
2017-06-07 12:26:44 +00:00
|
|
|
return inputLocations;
|
[inspector] moved var initialization break location before init expression (reland)
This CL improves break locations for expressions like 'var a = <expr>'. Without CL we use <expr> position as break location for initialization statement, with this CL we use position of first character after '=' as position.
Benefits (see test for details):
- only one break in expressions which includes mix of property lookup and calls, e.g. var p = Promise.resolve().then(x => x * 2),
- removed redundant break location for expressions like: let { x, y } = { x: 1, y: 2}.
TBR=dgozman@chromium.org,rmcilroy@chromium.org,machenbach@chromium.org,marja@chromium.org,kozyatinskiy@chromium.org,devtools-reviews@chromium.org,v8-reviews@googlegroups.com
# Not skipping CQ checks because original CL landed > 1 day ago.
Bug: v8:5909
Change-Id: Ie84fa79afeed09e28cf8478ba610a0cfbfdfc294
Reviewed-on: https://chromium-review.googlesource.com/518116
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45598}
2017-05-30 12:25:55 +00:00
|
|
|
|
|
|
|
function locationMark(type) {
|
|
|
|
if (type === 'return') return '|R|';
|
|
|
|
if (type === 'call') return '|C|';
|
|
|
|
if (type === 'debuggerStatement') return '|D|';
|
|
|
|
return '|_|';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[type-profile] Incorporate into inspector protocol.
JavaScript is a dynamically typed language. But most code is
written with fixed types in mind. When debugging JavaScript,
it is helpful to know the types of variables and parameters
at runtime. It is often hard to infer types for complex code.
Type profiling provides this information at runtime.
Node.js uses the inspector protocol. This CL allows Node.js users
to access and analyse type profile for via Node modules or the
in-procress api. Type Profile helps developers to analyze
their code for correctness and performance.
Design doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing
Add `takeTypeProfile` to the inspector protocol. It returns a list
of TypeProfileForScripts, which in turn contains the type profile for
each function. We can use TypeProfile data to annotate JavaScript code.
Sample script with data from TypeProfile:
function f(/*Object, number, undefined*/a,
/*Array, number, null*/b,
/*boolean, Object, symbol*/c) {
return 'bye';
/*string*/};
f({}, [], true);
f(3, 2.3, {a: 42});
f(undefined, null, Symbol('hello'));/*string*/
Bug: v8:5933
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I626bfb886b752f90b9c86cc6953601558b18b60d
Reviewed-on: https://chromium-review.googlesource.com/508588
Commit-Queue: Franziska Hinkelmann <franzih@chromium.org>
Reviewed-by: Pavel Feldman <pfeldman@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47920}
2017-09-08 08:28:29 +00:00
|
|
|
async logTypeProfile(typeProfile, source) {
|
|
|
|
let entries = typeProfile.entries;
|
|
|
|
|
|
|
|
// Sort in reverse order so we can replace entries without invalidating
|
|
|
|
// the other offsets.
|
|
|
|
entries = entries.sort((a, b) => b.offset - a.offset);
|
|
|
|
|
|
|
|
for (let entry of entries) {
|
|
|
|
source = source.slice(0, entry.offset) + typeAnnotation(entry.types) +
|
|
|
|
source.slice(entry.offset);
|
|
|
|
}
|
|
|
|
InspectorTest.log(source);
|
|
|
|
return typeProfile;
|
|
|
|
|
|
|
|
function typeAnnotation(types) {
|
|
|
|
return `/*${types.map(t => t.name).join(', ')}*/`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
logAsyncStackTrace(asyncStackTrace) {
|
|
|
|
while (asyncStackTrace) {
|
2017-12-14 19:49:25 +00:00
|
|
|
InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
|
2017-05-19 00:35:45 +00:00
|
|
|
this.logCallFrames(asyncStackTrace.callFrames);
|
2017-12-14 19:49:25 +00:00
|
|
|
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
|
2017-05-19 00:35:45 +00:00
|
|
|
asyncStackTrace = asyncStackTrace.parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_sendCommandPromise(method, params) {
|
2018-03-14 05:25:12 +00:00
|
|
|
if (typeof params !== 'object')
|
|
|
|
utils.print(`WARNING: non-object params passed to invocation of method ${method}`);
|
2017-05-19 00:35:45 +00:00
|
|
|
if (InspectorTest._commandsForLogging.has(method))
|
|
|
|
utils.print(method + ' called');
|
|
|
|
var requestId = ++this._requestId;
|
|
|
|
var messageObject = { "id": requestId, "method": method, "params": params };
|
|
|
|
return new Promise(fulfill => this.sendRawCommand(requestId, JSON.stringify(messageObject), fulfill));
|
|
|
|
}
|
|
|
|
|
|
|
|
_setupProtocol() {
|
|
|
|
return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
|
|
|
|
get: (target, methodName, receiver) => {
|
|
|
|
const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/;
|
|
|
|
var match = eventPattern.exec(methodName);
|
|
|
|
if (!match)
|
|
|
|
return args => this._sendCommandPromise(`${agentName}.${methodName}`, args || {});
|
|
|
|
var eventName = match[2];
|
|
|
|
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
|
|
|
|
if (match[1])
|
2018-09-13 19:19:30 +00:00
|
|
|
return numOfEvents => this._waitForEventPromise(
|
|
|
|
`${agentName}.${eventName}`, numOfEvents || 1);
|
2017-05-19 00:35:45 +00:00
|
|
|
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
|
|
|
|
}
|
|
|
|
})});
|
|
|
|
}
|
|
|
|
|
|
|
|
_dispatchMessage(messageString) {
|
|
|
|
var messageObject = JSON.parse(messageString);
|
|
|
|
if (InspectorTest._dumpInspectorProtocolMessages)
|
|
|
|
utils.print("backend: " + JSON.stringify(messageObject));
|
|
|
|
try {
|
|
|
|
var messageId = messageObject["id"];
|
|
|
|
if (typeof messageId === "number") {
|
|
|
|
var handler = this._dispatchTable.get(messageId);
|
|
|
|
if (handler) {
|
|
|
|
handler(messageObject);
|
|
|
|
this._dispatchTable.delete(messageId);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var eventName = messageObject["method"];
|
|
|
|
var eventHandler = this._eventHandlers.get(eventName);
|
|
|
|
if (this._scriptMap && eventName === "Debugger.scriptParsed")
|
|
|
|
this._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params)));
|
|
|
|
if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-for-pending-tasks.js")
|
|
|
|
return;
|
|
|
|
if (eventHandler)
|
|
|
|
eventHandler(messageObject);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2));
|
|
|
|
InspectorTest.completeTest();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-13 19:19:30 +00:00
|
|
|
_waitForEventPromise(eventName, numOfEvents) {
|
|
|
|
let events = [];
|
2017-05-19 00:35:45 +00:00
|
|
|
return new Promise(fulfill => {
|
|
|
|
this._eventHandlers.set(eventName, result => {
|
2018-09-13 19:19:30 +00:00
|
|
|
--numOfEvents;
|
|
|
|
events.push(result);
|
|
|
|
if (numOfEvents === 0) {
|
|
|
|
delete this._eventHandlers.delete(eventName);
|
|
|
|
fulfill(events.length > 1 ? events : events[0]);
|
|
|
|
}
|
2017-05-19 00:35:45 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2016-12-13 19:40:14 +00:00
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.runTestSuite = function(testSuite) {
|
|
|
|
function nextTest() {
|
2016-10-02 19:41:17 +00:00
|
|
|
if (!testSuite.length) {
|
|
|
|
InspectorTest.completeTest();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var fun = testSuite.shift();
|
|
|
|
InspectorTest.log("\nRunning test: " + fun.name);
|
|
|
|
fun(nextTest);
|
|
|
|
}
|
|
|
|
nextTest();
|
|
|
|
}
|
2016-10-03 23:32:52 +00:00
|
|
|
|
2017-03-06 16:28:21 +00:00
|
|
|
InspectorTest.runAsyncTestSuite = async function(testSuite) {
|
|
|
|
for (var test of testSuite) {
|
|
|
|
InspectorTest.log("\nRunning test: " + test.name);
|
2017-05-18 23:11:20 +00:00
|
|
|
try {
|
|
|
|
await test();
|
|
|
|
} catch (e) {
|
|
|
|
utils.print(e.stack);
|
|
|
|
}
|
2017-03-06 16:28:21 +00:00
|
|
|
}
|
|
|
|
InspectorTest.completeTest();
|
|
|
|
}
|
|
|
|
|
2017-05-19 00:35:45 +00:00
|
|
|
InspectorTest.start = function(description) {
|
|
|
|
try {
|
|
|
|
InspectorTest.log(description);
|
|
|
|
var contextGroup = new InspectorTest.ContextGroup();
|
|
|
|
var session = contextGroup.connect();
|
|
|
|
return { session: session, contextGroup: contextGroup, Protocol: session.Protocol };
|
|
|
|
} catch (e) {
|
|
|
|
utils.print(e.stack);
|
2016-10-03 23:32:52 +00:00
|
|
|
}
|
|
|
|
}
|