From 2c8ab81db7ded83a53619aebf7338ea79c543732 Mon Sep 17 00:00:00 2001 From: Erik Luo Date: Tue, 1 May 2018 19:26:52 +0000 Subject: [PATCH] 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 Reviewed-by: Dmitry Gozman Reviewed-by: Joel Einbinder Cr-Original-Commit-Position: refs/heads/master@{#555142} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: fcaafdd756b1aef4f8244474e37be9ee12c75284 --- front_end/console/ConsolePrompt.js | 5 ++- front_end/formatter/FormatterWorkerPool.js | 10 +++++- front_end/formatter_worker/FormatterWorker.js | 35 +++++++++++++++++-- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/front_end/console/ConsolePrompt.js b/front_end/console/ConsolePrompt.js index 7cfeff4a84..b8ea650cff 100644 --- a/front_end/console/ConsolePrompt.js +++ b/front_end/console/ConsolePrompt.js @@ -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 */); diff --git a/front_end/formatter/FormatterWorkerPool.js b/front_end/formatter/FormatterWorkerPool.js index 6890f5073d..94a28f2367 100644 --- a/front_end/formatter/FormatterWorkerPool.js +++ b/front_end/formatter/FormatterWorkerPool.js @@ -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} */ ( this._runTask('findLastExpression', {content})); } + + /** + * @param {string} content + * @return {!Promise} + */ + hasPossibleSideEffects(content) { + return /** @type {!Promise} */ (this._runTask('hasPossibleSideEffects', {content})); + } }; Formatter.FormatterWorkerPool.MaxWorkers = 2; diff --git a/front_end/formatter_worker/FormatterWorker.js b/front_end/formatter_worker/FormatterWorker.js index 791c5a8162..60d9e346e9 100644 --- a/front_end/formatter_worker/FormatterWorker.js +++ b/front_end/formatter_worker/FormatterWorker.js @@ -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); }; /**