DevTools: use autocomplete's possibleSideEffect to bypass throwOnSideEffect

Allow Console to evaluate without throwOnSideEffect when the entire
expression meets the same strict conditions used by JSAutocomplete.

Bug: 810176
Change-Id: If5c80250cd75d81eec1e495d0414903213fac930
Reviewed-on: https://chromium-review.googlesource.com/1028934
Commit-Queue: Erik Luo <luoe@chromium.org>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: Joel Einbinder <einbinder@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#555142}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: fcaafdd756b1aef4f8244474e37be9ee12c75284
This commit is contained in:
Erik Luo
2018-05-01 19:26:52 +00:00
committed by Commit Bot
parent f4b89c8c75
commit 2c8ab81db7
3 changed files with 45 additions and 5 deletions
+4 -1
View File
@@ -81,11 +81,14 @@ Console.ConsolePrompt = class extends UI.Widget {
return;
}
// Checking for possible side effects uses the same logic as JavaScriptAutocomplete.
// This grows the set of previewable expressions without whitelisting.
const hasPossibleSideEffects = await Formatter.formatterWorkerPool().hasPossibleSideEffects(text);
const options = {
expression: SDK.RuntimeModel.wrapObjectLiteralExpressionIfNeeded(text),
includeCommandLineAPI: true,
generatePreview: true,
throwOnSideEffect: true,
throwOnSideEffect: hasPossibleSideEffects,
timeout: 500
};
const result = await executionContext.evaluate(options, true /* userGesture */, false /* awaitPromise */);
+9 -1
View File
@@ -49,7 +49,7 @@ Formatter.FormatterWorkerPool = class {
this._workerTasks.set(worker, null);
this._processNextTask();
task.callback(event.data ? event.data : null);
task.callback(event.data);
}
/**
@@ -239,6 +239,14 @@ Formatter.FormatterWorkerPool = class {
return /** @type {!Promise<?{baseExpression: string, possibleSideEffects:boolean}>} */ (
this._runTask('findLastExpression', {content}));
}
/**
* @param {string} content
* @return {!Promise<boolean>}
*/
hasPossibleSideEffects(content) {
return /** @type {!Promise<boolean>} */ (this._runTask('hasPossibleSideEffects', {content}));
}
};
Formatter.FormatterWorkerPool.MaxWorkers = 2;
+32 -3
View File
@@ -87,6 +87,9 @@ self.onmessage = function(event) {
case 'findLastExpression':
postMessage(FormatterWorker.findLastExpression(params.content));
break;
case 'hasPossibleSideEffects':
postMessage(FormatterWorker.hasPossibleSideEffects(params.content));
break;
default:
console.error('Unsupport method name: ' + method);
}
@@ -371,9 +374,18 @@ FormatterWorker.findLastExpression = function(content) {
let baseExpression = parsedContent.substring(baseNode.start, parsedContent.length - suffix.length);
if (baseExpression.startsWith('{'))
baseExpression = `(${baseExpression})`;
const possibleSideEffects = FormatterWorker._possibleSideEffects(/** @type {!ESTree.Node} */ (baseNode));
return {baseExpression, possibleSideEffects};
};
/**
* @param {!ESTree.Node} node
* @return {boolean}
*/
FormatterWorker._possibleSideEffects = function(node) {
const sideEffectFreeTypes = new Set([
'MemberExpression', 'Identifier', 'BinaryExpression', 'Literal', 'TemplateLiteral', 'TemplateElement',
'ObjectExpression', 'ArrayExpression', 'Property', 'ThisExpression'
'ObjectExpression', 'ArrayExpression', 'Property', 'ThisExpression', 'Program', 'ExpressionStatement'
]);
let possibleSideEffects = false;
const sideEffectwalker = new FormatterWorker.ESTreeWalker(node => {
@@ -382,8 +394,25 @@ FormatterWorker.findLastExpression = function(content) {
if (possibleSideEffects)
return FormatterWorker.ESTreeWalker.SkipSubtree;
});
sideEffectwalker.walk(/** @type {!ESTree.Node} */ (baseNode));
return {baseExpression, possibleSideEffects};
sideEffectwalker.walk(node);
return possibleSideEffects;
};
/**
* @param {string} text
* @return {boolean}
*/
FormatterWorker.hasPossibleSideEffects = function(text) {
if (text.length > 10000)
return true;
let ast = null;
try {
ast = acorn.parse(text, {ecmaVersion: 9});
} catch (e) {
}
if (!ast)
return true;
return FormatterWorker._possibleSideEffects(ast);
};
/**