| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| export default class JavaScriptREPL { |
| /** |
| * @param {string} code |
| * @return {string} |
| */ |
| static wrapObjectLiteral(code) { |
| // Only parenthesize what appears to be an object literal. |
| if (!(/^\s*\{/.test(code) && /\}\s*$/.test(code))) { |
| return code; |
| } |
| |
| const parse = (async () => 0).constructor; |
| try { |
| // Check if the code can be interpreted as an expression. |
| parse('return ' + code + ';'); |
| |
| // No syntax error! Does it work parenthesized? |
| const wrappedCode = '(' + code + ')'; |
| parse(wrappedCode); |
| |
| return wrappedCode; |
| } catch (e) { |
| return code; |
| } |
| } |
| |
| /** |
| * @param {string} text |
| * @return {string} |
| */ |
| static preprocessExpression(text) { |
| return JavaScriptREPL.wrapObjectLiteral(text); |
| } |
| |
| /** |
| * @param {string} text |
| * @param {boolean} throwOnSideEffect |
| * @param {number=} timeout |
| * @param {boolean=} allowErrors |
| * @param {string=} objectGroup |
| * @return {!Promise<!{preview: !DocumentFragment, result: ?SDK.RuntimeModel.EvaluationResult}>} |
| */ |
| static async evaluateAndBuildPreview(text, throwOnSideEffect, timeout, allowErrors, objectGroup) { |
| const executionContext = UI.context.flavor(SDK.ExecutionContext); |
| const isTextLong = text.length > ObjectUI.JavaScriptREPL._MaxLengthForEvaluation; |
| if (!text || !executionContext || (throwOnSideEffect && isTextLong)) { |
| return {preview: createDocumentFragment(), result: null}; |
| } |
| |
| const expression = JavaScriptREPL.preprocessExpression(text); |
| const options = { |
| expression: expression, |
| generatePreview: true, |
| includeCommandLineAPI: true, |
| throwOnSideEffect: throwOnSideEffect, |
| timeout: timeout, |
| objectGroup: objectGroup, |
| disableBreaks: true |
| }; |
| const result = await executionContext.evaluate(options, false /* userGesture */, false /* awaitPromise */); |
| const preview = JavaScriptREPL._buildEvaluationPreview(result, allowErrors); |
| return {preview, result}; |
| } |
| |
| /** |
| * @param {!SDK.RuntimeModel.EvaluationResult} result |
| * @param {boolean=} allowErrors |
| * @return {!DocumentFragment} |
| */ |
| static _buildEvaluationPreview(result, allowErrors) { |
| const fragment = createDocumentFragment(); |
| if (result.error) { |
| return fragment; |
| } |
| |
| if (result.exceptionDetails && result.exceptionDetails.exception && result.exceptionDetails.exception.description) { |
| const exception = result.exceptionDetails.exception.description; |
| if (exception.startsWith('TypeError: ') || allowErrors) { |
| fragment.createChild('span').textContent = result.exceptionDetails.text + ' ' + exception; |
| } |
| return fragment; |
| } |
| |
| const formatter = new ObjectUI.RemoteObjectPreviewFormatter(); |
| const {preview, type, subtype, description} = result.object; |
| if (preview && type === 'object' && subtype !== 'node') { |
| formatter.appendObjectPreview(fragment, preview, false /* isEntry */); |
| } else { |
| const nonObjectPreview = formatter.renderPropertyPreview(type, subtype, description.trimEndWithMaxLength(400)); |
| fragment.appendChild(nonObjectPreview); |
| } |
| return fragment; |
| } |
| } |
| |
| /** |
| * @const |
| * @type {number} |
| */ |
| export const _MaxLengthForEvaluation = 2000; |
| |
| /* Legacy exported object */ |
| self.ObjectUI = self.ObjectUI || {}; |
| |
| /* Legacy exported object */ |
| ObjectUI = ObjectUI || {}; |
| |
| /** @constructor */ |
| ObjectUI.JavaScriptREPL = JavaScriptREPL; |
| |
| ObjectUI.JavaScriptREPL._MaxLengthForEvaluation = _MaxLengthForEvaluation; |