| // 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; |