412 lines
15 KiB
Plaintext
412 lines
15 KiB
Plaintext
|
utils = new Proxy(utils, {
|
||
|
get: function(target, prop) {
|
||
|
if (prop in target) return target[prop];
|
||
|
return i=>i;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Loaded from 'test/inspector/protocol-test.js':
|
||
|
// 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;
|
||
|
InspectorTest._commandsForLogging = new Set();
|
||
|
InspectorTest._sessions = new Set();
|
||
|
|
||
|
InspectorTest.log = utils.print.bind(utils);
|
||
|
InspectorTest.quitImmediately = utils.quit.bind(utils);
|
||
|
|
||
|
InspectorTest.logProtocolCommandCalls = function(command) {
|
||
|
InspectorTest._commandsForLbytes;
|
||
|
}
|
||
|
|
||
|
InspectorTest.trimErrorMessage = function(message) {
|
||
|
if (!message.error || !message.error.data)
|
||
|
return message;
|
||
|
message.error.data = message.error.data.replace(/at position \d+/,
|
||
|
'at <some position>');
|
||
|
return message;
|
||
|
}
|
||
|
|
||
|
InspectorTest.ContextGroup = class {
|
||
|
constructor() {
|
||
|
this.id = utils.createContextGroup();
|
||
|
}
|
||
|
|
||
|
createContext(name) {
|
||
|
utils.createContext(this.id, name || '');
|
||
|
}
|
||
|
|
||
|
schedulePauseOnNextStatement(reason, details) {
|
||
|
utils.schedulePauseOnNextStatement(this.id, reason, details);
|
||
|
}
|
||
|
|
||
|
cancelPauseOnNextStatement() {
|
||
|
utils.cancelPauseOnNextStatement(this.id);
|
||
|
}
|
||
|
|
||
|
addScript(string, lineOffset, columnOffset, url) {
|
||
|
utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
addModule(string, url, lineOffset, columnOffset) {
|
||
|
utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
|
||
|
}
|
||
|
|
||
|
loadScript(fileName) {
|
||
|
this.addScript(utils.read(fileName));
|
||
|
}
|
||
|
|
||
|
connect() {
|
||
|
return new InspectorTest.Session(this);
|
||
|
}
|
||
|
|
||
|
reset() {
|
||
|
utils.resetContextGroup(this.id);
|
||
|
}
|
||
|
|
||
|
setupInjectedScriptEnvironment(session) {
|
||
|
let scriptSource = '';
|
||
|
let getters = ["length","internalConstructorName","subtype","getProperty",
|
||
|
"objectHasOwnProperty","nullifyPrototype","primitiveTypes",
|
||
|
"closureTypes","prototype","all","RemoteObject","bind",
|
||
|
"PropertyDescriptor","object","get","set","value","configurable",
|
||
|
"enumerable","symbol","getPrototypeOf","nativeAccessorDescriptor",
|
||
|
"isBuiltin","hasGetter","hasSetter","getOwnPropertyDescriptor",
|
||
|
"description","isOwn","name",
|
||
|
"typedArrayProperties","keys","getOwnPropertyNames",
|
||
|
"getOwnPropertySymbols","isPrimitiveValue","com","toLowerCase",
|
||
|
"ELEMENT","trim","replace","DOCUMENT","size","byteLength","toString",
|
||
|
"stack","substr","message","indexOf","key","type","unserializableValue",
|
||
|
"objectId","className","preview","proxyTargetValue","customPreview",
|
||
|
"CustomPreview","resolve","then","console","error","header","hasBody",
|
||
|
"stringify","ObjectPreview","ObjectPreviewType","properties",
|
||
|
"ObjectPreviewSubtype","getInternalProperties","wasThrown","indexes",
|
||
|
"overflow","valuePreview","entries"];
|
||
|
scriptSource += `(function installSettersAndGetters() {
|
||
|
let defineProperty = Object.defineProperty;
|
||
|
let ObjectPrototype = Object.prototype;
|
||
|
let ArrayPrototype = Array.prototype;
|
||
|
defineProperty(ArrayPrototype, 0, {
|
||
|
{ debugger; throw 42; }, get() { debugger; throw 42; },
|
||
|
__proto__: null
|
||
|
});`,
|
||
|
scriptSource += getters.map(getter => `
|
||
|
defineProperty(ObjectPrototype, '${getter}', {
|
||
|
set() { debugger; throw 42; }, get() { debugger; throw 42; },
|
||
|
__proto__: null
|
||
|
});
|
||
|
`).join('\n') + '})();';
|
||
|
this.addScript(scriptSource);
|
||
|
|
||
|
if (session) {
|
||
|
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
|
||
|
session.setupScriptMap();
|
||
|
session.Protocol.Debugger.enable();
|
||
|
session.Protocol.Debugger.onPaused(message => {
|
||
|
let callFrames = message.params.callFrames;
|
||
|
session.logSourceLocations(callFrames.map(frame => frame.location));
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
disconnect() {
|
||
|
InspectorTest._sessions.delete(this);
|
||
|
utils.disconnectSession(this.id);
|
||
|
}
|
||
|
|
||
|
reconnect() {
|
||
|
var state = utils.disconnectSession(this.id);
|
||
|
this.id = utils.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
|
||
|
}
|
||
|
|
||
|
async addInspectedObject(serializable) {
|
||
|
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
|
||
|
}
|
||
|
|
||
|
sendRawCommand(requestId, command, handler) {
|
||
|
if (InspectorTest._dumpInspectorProtocolMessages)
|
||
|
utils.print("frontend: " + command);
|
||
|
this._dispatchTable.set(requestId, handler);
|
||
|
utils.sendMessageToBackend(this.id, command);
|
||
|
}
|
||
|
|
||
|
setupScriptMap() {
|
||
|
if (this._scriptMap)
|
||
|
return;
|
||
|
this._scriptMap = new Map();
|
||
|
}
|
||
|
|
||
|
logCallFrames(callFrames) {
|
||
|
for (var frame of callFrames) {
|
||
|
var functionName = frame.functionName || '(anonymous)';
|
||
|
var scriptId = frame.location ? frame.location.scriptId : frame.scriptId;
|
||
|
var url = frame.url ? frame.url : this._scriptMap.get(scriptId).url;
|
||
|
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
|
||
|
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
|
||
|
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async getScriptWithSource(scriptId, forceSourceRequest) {
|
||
|
var script = this._scriptMap.get(scriptId);
|
||
|
if (forceSourceRequest || !(script.scriptSource || script.bytecode)) {
|
||
|
var message = await this.Protocol.Debugger.getScriptSource({ scriptId });
|
||
|
script.scriptSource = message.result.scriptSource;
|
||
|
if (message.result.bytecode) {
|
||
|
script.bytecode = InspectorTest.decodeBase64(message.result.bytecode);
|
||
|
}
|
||
|
}
|
||
|
return script;
|
||
|
}
|
||
|
|
||
|
async logSourceLocation(location, forceSourceRequest) {
|
||
|
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 = await this.getScriptWithSource(scriptId, forceSourceRequest);
|
||
|
|
||
|
if (script.bytecode) {
|
||
|
if (location.lineNumber != 0) {
|
||
|
InspectorTest.log('Unexpected wasm line number: ' + location.lineNumber);
|
||
|
}
|
||
|
let wasm_opcode = script.bytecode[location.columnNumber];
|
||
|
let opcode_str = wasm_opcode.toString(16);
|
||
|
if (opcode_str.length % 2) opcode_str = `0${opcode_str}`;
|
||
|
if (InspectorTest.getWasmOpcodeName) {
|
||
|
opcode_str += ` (${InspectorTest.getWasmOpcodeName(wasm_opcode)})`;
|
||
|
}
|
||
|
InspectorTest.log(`Script ${script.url} byte offset ${
|
||
|
location.columnNumber}: Wasm opcode 0x${opcode_str}`);
|
||
|
} else {
|
||
|
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)));
|
||
|
}
|
||
|
|
||
|
async logBreakLocations(inputLocations) {
|
||
|
let locations = inputLocations.slice();
|
||
|
let scriptId = locations[0].scriptId;
|
||
|
let script = await this.getScriptWithSource(scriptId);
|
||
|
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');
|
||
|
return inputLocations;
|
||
|
|
||
|
function locationMark(type) {
|
||
|
if (type === 'return') return '|R|';
|
||
|
if (type === 'call') return '|C|';
|
||
|
if (type === 'debuggerStatement') return '|D|';
|
||
|
return '|_|';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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(', ')}*/`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
logAsyncStackTrace(asyncStackTrace) {
|
||
|
while (asyncStackTrace) {
|
||
|
InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
|
||
|
this.logCallFrames(asyncStackTrace.callFrames);
|
||
|
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
|
||
|
asyncStackTrace = asyncStackTrace.parent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_sendCommandPromise(method, params) {
|
||
|
if (typeof params !== 'object')
|
||
|
utils.print(`WARNING: non-object params passed to invocation of method ${method}`);
|
||
|
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])
|
||
|
return numOfEvents => this._waitForEventPromise(
|
||
|
`${agentName}.${eventName}`, numOfEvents || 1);
|
||
|
return listener => this._eventHandlers.set(`${agentName}.${eventName}`, listener);
|
||
|
}
|
||
|
})});
|
||
|
}
|
||
|
|
||
|
_dispatchMessage(messageString) {
|
||
|
var messageObject = JSON.parse(messageString);
|
||
|
if (InspectorTest._dumpInspectorProtocolMessages)
|
||
|
utils.print("backend: " + JSON.stringify(messageObject));
|
||
|
const kMethodNotFound = -32601;
|
||
|
if (messageObject.error && messageObject.error.code === kMethodNotFound) {
|
||
|
InspectorTest.log(`Error: Called non-existent method. ${
|
||
|
messageObject.error.message} code: ${messageObject.error.code}`);
|
||
|
InspectorTest.completeTest();
|
||
|
}
|
||
|
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();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
_waitForEventPromise(eventName, numOfEvents) {
|
||
|
let events = [];
|
||
|
return new Promise(fulfill => {
|
||
|
this._eventHandlers.set(eventName, result => {
|
||
|
--numOfEvents;
|
||
|
events.push(result);
|
||
|
if (numOfEvents === 0) {
|
||
|
delete this._eventHandlers.delete(eventName);
|
||
|
fulfill(events.length > 1 ? events : events[0]);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
InspectorTest.runTestSuite = function(testSuite) {
|
||
|
function nextTest() {
|
||
|
if (!testSuite.length) {
|
||
|
InspectorTest.completeTest();
|
||
|
return;
|
||
|
}
|
||
|
var fun = testSuite.shift();
|
||
|
InspectorTest.log("\nRunning test: " + fun.name);
|
||
|
fun(nextTest);
|
||
|
}
|
||
|
nextTest();
|
||
|
}
|
||
|
|
||
|
InspectorTest.runAsyncTestSuite = async function(testSuite) {
|
||
|
const selected = testSuite.filter(test => test.name.startsWith('f_'));
|
||
|
if (selected.length)
|
||
|
testSuite = selected;
|
||
|
for (var test of testSuite) {
|
||
|
InspectorTest.log("\nRunning test: " + test.name);
|
||
|
try {
|
||
|
await test();
|
||
|
} catch (e) {
|
||
|
utils.print(e.stack);
|
||
|
}
|
||
|
}
|
||
|
InspectorTest.completeTest();
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Loaded from 'test/inspector/debugger/step-into-break-on-async-call.js':
|
||
|
// Copyright 2017 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.
|
||
|
|
||
|
let {session, contextGroup, Protocol} =
|
||
|
InspectorTest.start('Test for Debugger.stepInto with breakOnAsyncCall.');
|
||
|
|
||
|
InspectorTest.runAsyncTestSuite([
|
||
|
async function testSetTimeout() {
|
||
|
Protocol.Debugger.enable();
|
||
|
Protocol.Debugger.pause();
|
||
|
let pausedPromise = Protocol.Debugger.oncePaused();
|
||
|
Protocol.Runtime.evaluate({ expression: 'setTimeout(() => 42, 0)//# sourceURL=setTimeout.js' });
|
||
|
await pausedPromise;
|
||
|
Protocol.Debugger.stepInto({breakOnAsyncCall: true});
|
||
|
await Protocol.Debugger.oncePaused();
|
||
|
},
|
||
|
|
||
|
async function testPromiseThen() {
|
||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 1});
|
||
|
Protocol.Runtime.evaluate({expression: 'Promise.resolve().then(() => 21)//# sourceURL=promiseThen.js'});
|
||
|
}
|
||
|
]);
|