| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @unrestricted |
| */ |
| export default class DebuggerModel extends SDK.SDKModel { |
| /** |
| * @param {!SDK.Target} target |
| */ |
| constructor(target) { |
| super(target); |
| |
| target.registerDebuggerDispatcher(new DebuggerDispatcher(this)); |
| this._agent = target.debuggerAgent(); |
| this._runtimeModel = /** @type {!SDK.RuntimeModel} */ (target.model(SDK.RuntimeModel)); |
| |
| /** @type {!SDK.SourceMapManager<!SDK.Script>} */ |
| this._sourceMapManager = new SDK.SourceMapManager(target); |
| /** @type {!Map<string, !SDK.Script>} */ |
| this._sourceMapIdToScript = new Map(); |
| |
| /** @type {?DebuggerPausedDetails} */ |
| this._debuggerPausedDetails = null; |
| /** @type {!Map<string, !SDK.Script>} */ |
| this._scripts = new Map(); |
| /** @type {!Map.<string, !Array.<!SDK.Script>>} */ |
| this._scriptsBySourceURL = new Map(); |
| /** @type {!Array.<!SDK.Script>} */ |
| this._discardableScripts = []; |
| |
| /** @type {!Common.Object} */ |
| this._breakpointResolvedEventTarget = new Common.Object(); |
| |
| /** @type {boolean} */ |
| this._autoStepOver = false; |
| |
| this._isPausing = false; |
| Common.moduleSetting('pauseOnExceptionEnabled').addChangeListener(this._pauseOnExceptionStateChanged, this); |
| Common.moduleSetting('pauseOnCaughtException').addChangeListener(this._pauseOnExceptionStateChanged, this); |
| Common.moduleSetting('disableAsyncStackTraces').addChangeListener(this._asyncStackTracesStateChanged, this); |
| Common.moduleSetting('breakpointsActive').addChangeListener(this._breakpointsActiveChanged, this); |
| |
| if (!target.suspended()) { |
| this._enableDebugger(); |
| } |
| |
| /** @type {!Map<string, string>} */ |
| this._stringMap = new Map(); |
| this._sourceMapManager.setEnabled(Common.moduleSetting('jsSourceMapsEnabled').get()); |
| Common.moduleSetting('jsSourceMapsEnabled') |
| .addChangeListener(event => this._sourceMapManager.setEnabled(/** @type {boolean} */ (event.data))); |
| } |
| |
| /** |
| * @param {string} executionContextId |
| * @param {string} sourceURL |
| * @param {?string} sourceMapURL |
| * @return {?string} |
| */ |
| static _sourceMapId(executionContextId, sourceURL, sourceMapURL) { |
| if (!sourceMapURL) { |
| return null; |
| } |
| return executionContextId + ':' + sourceURL + ':' + sourceMapURL; |
| } |
| |
| /** |
| * @return {!SDK.SourceMapManager<!SDK.Script>} |
| */ |
| sourceMapManager() { |
| return this._sourceMapManager; |
| } |
| |
| /** |
| * @return {!SDK.RuntimeModel} |
| */ |
| runtimeModel() { |
| return this._runtimeModel; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| debuggerEnabled() { |
| return !!this._debuggerEnabled; |
| } |
| |
| /** |
| * @return {!Promise} |
| */ |
| _enableDebugger() { |
| if (this._debuggerEnabled) { |
| return Promise.resolve(); |
| } |
| this._debuggerEnabled = true; |
| |
| // Set a limit for the total size of collected script sources retained by debugger. |
| // 10MB for remote frontends, 100MB for others. |
| const isRemoteFrontend = Root.Runtime.queryParam('remoteFrontend') || Root.Runtime.queryParam('ws'); |
| const maxScriptsCacheSize = isRemoteFrontend ? 10e6 : 100e6; |
| const enablePromise = this._agent.enable(maxScriptsCacheSize); |
| enablePromise.then(this._registerDebugger.bind(this)); |
| this._pauseOnExceptionStateChanged(); |
| this._asyncStackTracesStateChanged(); |
| if (!Common.moduleSetting('breakpointsActive').get()) { |
| this._breakpointsActiveChanged(); |
| } |
| if (SDK.DebuggerModel._scheduledPauseOnAsyncCall) { |
| this._pauseOnAsyncCall(SDK.DebuggerModel._scheduledPauseOnAsyncCall); |
| } |
| this.dispatchEventToListeners(Events.DebuggerWasEnabled, this); |
| return enablePromise; |
| } |
| |
| /** |
| * @param {string|null} debuggerId |
| */ |
| _registerDebugger(debuggerId) { |
| if (!debuggerId) { |
| return; |
| } |
| SDK.DebuggerModel._debuggerIdToModel.set(debuggerId, this); |
| this._debuggerId = debuggerId; |
| this.dispatchEventToListeners(Events.DebuggerIsReadyToPause, this); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isReadyToPause() { |
| return !!this._debuggerId; |
| } |
| |
| /** |
| * @param {string} debuggerId |
| * @return {?DebuggerModel} |
| */ |
| static modelForDebuggerId(debuggerId) { |
| return SDK.DebuggerModel._debuggerIdToModel.get(debuggerId) || null; |
| } |
| |
| /** |
| * @return {!Promise} |
| */ |
| _disableDebugger() { |
| if (!this._debuggerEnabled) { |
| return Promise.resolve(); |
| } |
| this._debuggerEnabled = false; |
| |
| const disablePromise = this._agent.disable(); |
| this._isPausing = false; |
| this._asyncStackTracesStateChanged(); |
| this.globalObjectCleared(); |
| this.dispatchEventToListeners(Events.DebuggerWasDisabled); |
| SDK.DebuggerModel._debuggerIdToModel.delete(this._debuggerId); |
| return disablePromise; |
| } |
| |
| /** |
| * @param {boolean} skip |
| */ |
| _skipAllPauses(skip) { |
| if (this._skipAllPausesTimeout) { |
| clearTimeout(this._skipAllPausesTimeout); |
| delete this._skipAllPausesTimeout; |
| } |
| this._agent.setSkipAllPauses(skip); |
| } |
| |
| /** |
| * @param {number} timeout |
| */ |
| skipAllPausesUntilReloadOrTimeout(timeout) { |
| if (this._skipAllPausesTimeout) { |
| clearTimeout(this._skipAllPausesTimeout); |
| } |
| this._agent.setSkipAllPauses(true); |
| // If reload happens before the timeout, the flag will be already unset and the timeout callback won't change anything. |
| this._skipAllPausesTimeout = setTimeout(this._skipAllPauses.bind(this, false), timeout); |
| } |
| |
| _pauseOnExceptionStateChanged() { |
| let state; |
| if (!Common.moduleSetting('pauseOnExceptionEnabled').get()) { |
| state = PauseOnExceptionsState.DontPauseOnExceptions; |
| } else if (Common.moduleSetting('pauseOnCaughtException').get()) { |
| state = PauseOnExceptionsState.PauseOnAllExceptions; |
| } else { |
| state = PauseOnExceptionsState.PauseOnUncaughtExceptions; |
| } |
| |
| this._agent.setPauseOnExceptions(state); |
| } |
| |
| _asyncStackTracesStateChanged() { |
| const maxAsyncStackChainDepth = 32; |
| const enabled = !Common.moduleSetting('disableAsyncStackTraces').get() && this._debuggerEnabled; |
| this._agent.setAsyncCallStackDepth(enabled ? maxAsyncStackChainDepth : 0); |
| } |
| |
| _breakpointsActiveChanged() { |
| this._agent.setBreakpointsActive(Common.moduleSetting('breakpointsActive').get()); |
| } |
| |
| stepInto() { |
| this._agent.stepInto(); |
| } |
| |
| stepOver() { |
| // Mark that in case of auto-stepping, we should be doing |
| // step-over instead of step-in. |
| this._autoStepOver = true; |
| this._agent.stepOver(); |
| } |
| |
| stepOut() { |
| this._agent.stepOut(); |
| } |
| |
| scheduleStepIntoAsync() { |
| this._agent.invoke_stepInto({breakOnAsyncCall: true}); |
| } |
| |
| resume() { |
| this._agent.resume(); |
| this._isPausing = false; |
| } |
| |
| pause() { |
| this._isPausing = true; |
| this._skipAllPauses(false); |
| this._agent.pause(); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.StackTraceId} parentStackTraceId |
| * @return {!Promise} |
| */ |
| _pauseOnAsyncCall(parentStackTraceId) { |
| return this._agent.invoke_pauseOnAsyncCall({parentStackTraceId: parentStackTraceId}); |
| } |
| |
| /** |
| * @param {string} url |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| * @param {string=} condition |
| * @return {!Promise<!SDK.DebuggerModel.SetBreakpointResult>} |
| */ |
| async setBreakpointByURL(url, lineNumber, columnNumber, condition) { |
| // Convert file url to node-js path. |
| let urlRegex; |
| if (this.target().type() === SDK.Target.Type.Node) { |
| const platformPath = Common.ParsedURL.urlToPlatformPath(url, Host.isWin()); |
| urlRegex = `${platformPath.escapeForRegExp()}|${url.escapeForRegExp()}`; |
| } |
| // Adjust column if needed. |
| let minColumnNumber = 0; |
| const scripts = this._scriptsBySourceURL.get(url) || []; |
| for (let i = 0, l = scripts.length; i < l; ++i) { |
| const script = scripts[i]; |
| if (lineNumber === script.lineOffset) { |
| minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset; |
| } |
| } |
| columnNumber = Math.max(columnNumber, minColumnNumber); |
| const response = await this._agent.invoke_setBreakpointByUrl({ |
| lineNumber: lineNumber, |
| url: urlRegex ? undefined : url, |
| urlRegex: urlRegex, |
| columnNumber: columnNumber, |
| condition: condition |
| }); |
| if (response[Protocol.Error]) { |
| return {locations: [], breakpointId: null}; |
| } |
| let locations = []; |
| if (response.locations) { |
| locations = response.locations.map(payload => Location.fromPayload(this, payload)); |
| } |
| return {locations: locations, breakpointId: response.breakpointId}; |
| } |
| |
| /** |
| * @param {string} scriptId |
| * @param {string} scriptHash |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| * @param {string=} condition |
| * @return {!Promise<!SDK.DebuggerModel.SetBreakpointResult>} |
| */ |
| async setBreakpointInAnonymousScript(scriptId, scriptHash, lineNumber, columnNumber, condition) { |
| const response = await this._agent.invoke_setBreakpointByUrl( |
| {lineNumber: lineNumber, scriptHash: scriptHash, columnNumber: columnNumber, condition: condition}); |
| const error = response[Protocol.Error]; |
| if (error) { |
| // Old V8 backend doesn't support scriptHash argument. |
| if (error !== 'Either url or urlRegex must be specified.') { |
| return {locations: [], breakpointId: null}; |
| } |
| return this._setBreakpointBySourceId(scriptId, lineNumber, columnNumber, condition); |
| } |
| let locations = []; |
| if (response.locations) { |
| locations = response.locations.map(payload => Location.fromPayload(this, payload)); |
| } |
| return {locations: locations, breakpointId: response.breakpointId}; |
| } |
| |
| /** |
| * @param {string} scriptId |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| * @param {string=} condition |
| * @return {!Promise<!SDK.DebuggerModel.SetBreakpointResult>} |
| */ |
| async _setBreakpointBySourceId(scriptId, lineNumber, columnNumber, condition) { |
| // This method is required for backward compatibility with V8 before 6.3.275. |
| const response = await this._agent.invoke_setBreakpoint( |
| {location: {scriptId: scriptId, lineNumber: lineNumber, columnNumber: columnNumber}, condition: condition}); |
| if (response[Protocol.Error]) { |
| return {breakpointId: null, locations: []}; |
| } |
| let actualLocation = []; |
| if (response.actualLocation) { |
| actualLocation = [Location.fromPayload(this, response.actualLocation)]; |
| } |
| return {locations: actualLocation, breakpointId: response.breakpointId}; |
| } |
| |
| /** |
| * @param {!Protocol.Debugger.BreakpointId} breakpointId |
| * @return {!Promise} |
| */ |
| async removeBreakpoint(breakpointId) { |
| const response = await this._agent.invoke_removeBreakpoint({breakpointId}); |
| if (response[Protocol.Error]) { |
| console.error('Failed to remove breakpoint: ' + response[Protocol.Error]); |
| } |
| } |
| |
| /** |
| * @param {!Location} startLocation |
| * @param {?Location} endLocation |
| * @param {boolean} restrictToFunction |
| * @return {!Promise<!Array<!BreakLocation>>} |
| */ |
| async getPossibleBreakpoints(startLocation, endLocation, restrictToFunction) { |
| const response = await this._agent.invoke_getPossibleBreakpoints({ |
| start: startLocation.payload(), |
| end: endLocation ? endLocation.payload() : undefined, |
| restrictToFunction: restrictToFunction |
| }); |
| if (response[Protocol.Error] || !response.locations) { |
| return []; |
| } |
| return response.locations.map(location => BreakLocation.fromPayload(this, location)); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.StackTraceId} stackId |
| * @return {!Promise<?Protocol.Runtime.StackTrace>} |
| */ |
| async fetchAsyncStackTrace(stackId) { |
| const response = await this._agent.invoke_getStackTrace({stackTraceId: stackId}); |
| return response[Protocol.Error] ? null : response.stackTrace; |
| } |
| |
| /** |
| * @param {!Protocol.Debugger.BreakpointId} breakpointId |
| * @param {!Protocol.Debugger.Location} location |
| */ |
| _breakpointResolved(breakpointId, location) { |
| this._breakpointResolvedEventTarget.dispatchEventToListeners(breakpointId, Location.fromPayload(this, location)); |
| } |
| |
| globalObjectCleared() { |
| this._setDebuggerPausedDetails(null); |
| this._reset(); |
| // TODO(dgozman): move clients to ExecutionContextDestroyed/ScriptCollected events. |
| this.dispatchEventToListeners(Events.GlobalObjectCleared, this); |
| } |
| |
| _reset() { |
| for (const scriptWithSourceMap of this._sourceMapIdToScript.values()) { |
| this._sourceMapManager.detachSourceMap(scriptWithSourceMap); |
| } |
| this._sourceMapIdToScript.clear(); |
| |
| this._scripts.clear(); |
| this._scriptsBySourceURL.clear(); |
| this._stringMap.clear(); |
| this._discardableScripts = []; |
| this._autoStepOver = false; |
| } |
| |
| /** |
| * @return {!Array<!SDK.Script>} |
| */ |
| scripts() { |
| return Array.from(this._scripts.values()); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @return {?SDK.Script} |
| */ |
| scriptForId(scriptId) { |
| return this._scripts.get(scriptId) || null; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.Script>} |
| */ |
| scriptsForSourceURL(sourceURL) { |
| if (!sourceURL) { |
| return []; |
| } |
| return this._scriptsBySourceURL.get(sourceURL) || []; |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| * @return {!Array<!SDK.Script>} |
| */ |
| scriptsForExecutionContext(executionContext) { |
| const result = []; |
| for (const script of this._scripts.values()) { |
| if (script.executionContextId === executionContext.id) { |
| result.push(script); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {string} newSource |
| * @param {function(?Protocol.Error, !Protocol.Runtime.ExceptionDetails=)} callback |
| */ |
| setScriptSource(scriptId, newSource, callback) { |
| this._scripts.get(scriptId).editSource( |
| newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback)); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {string} newSource |
| * @param {function(?Protocol.Error, !Protocol.Runtime.ExceptionDetails=)} callback |
| * @param {?Protocol.Error} error |
| * @param {!Protocol.Runtime.ExceptionDetails=} exceptionDetails |
| * @param {!Array.<!Protocol.Debugger.CallFrame>=} callFrames |
| * @param {!Protocol.Runtime.StackTrace=} asyncStackTrace |
| * @param {!Protocol.Runtime.StackTraceId=} asyncStackTraceId |
| * @param {boolean=} needsStepIn |
| */ |
| _didEditScriptSource( |
| scriptId, newSource, callback, error, exceptionDetails, callFrames, asyncStackTrace, asyncStackTraceId, |
| needsStepIn) { |
| callback(error, exceptionDetails); |
| if (needsStepIn) { |
| this.stepInto(); |
| return; |
| } |
| |
| if (!error && callFrames && callFrames.length) { |
| this._pausedScript( |
| callFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData, |
| this._debuggerPausedDetails.breakpointIds, asyncStackTrace, asyncStackTraceId); |
| } |
| } |
| |
| /** |
| * @return {?Array.<!CallFrame>} |
| */ |
| get callFrames() { |
| return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null; |
| } |
| |
| /** |
| * @return {?DebuggerPausedDetails} |
| */ |
| debuggerPausedDetails() { |
| return this._debuggerPausedDetails; |
| } |
| |
| /** |
| * @param {?DebuggerPausedDetails} debuggerPausedDetails |
| * @return {boolean} |
| */ |
| _setDebuggerPausedDetails(debuggerPausedDetails) { |
| this._isPausing = false; |
| this._debuggerPausedDetails = debuggerPausedDetails; |
| if (this._debuggerPausedDetails) { |
| if (this._beforePausedCallback) { |
| if (!this._beforePausedCallback.call(null, this._debuggerPausedDetails)) { |
| return false; |
| } |
| } |
| // If we resolved a location in auto-stepping callback, reset the |
| // step-over marker. |
| this._autoStepOver = false; |
| this.dispatchEventToListeners(Events.DebuggerPaused, this); |
| } |
| if (debuggerPausedDetails) { |
| this.setSelectedCallFrame(debuggerPausedDetails.callFrames[0]); |
| } else { |
| this.setSelectedCallFrame(null); |
| } |
| return true; |
| } |
| |
| /** |
| * @param {?function(!DebuggerPausedDetails):boolean} callback |
| */ |
| setBeforePausedCallback(callback) { |
| this._beforePausedCallback = callback; |
| } |
| |
| /** |
| * @param {!Array.<!Protocol.Debugger.CallFrame>} callFrames |
| * @param {string} reason |
| * @param {!Object|undefined} auxData |
| * @param {!Array.<string>} breakpointIds |
| * @param {!Protocol.Runtime.StackTrace=} asyncStackTrace |
| * @param {!Protocol.Runtime.StackTraceId=} asyncStackTraceId |
| * @param {!Protocol.Runtime.StackTraceId=} asyncCallStackTraceId |
| */ |
| async _pausedScript( |
| callFrames, reason, auxData, breakpointIds, asyncStackTrace, asyncStackTraceId, asyncCallStackTraceId) { |
| if (asyncCallStackTraceId) { |
| // Note: this is only to support old backends. Newer ones do not send asyncCallStackTraceId. |
| SDK.DebuggerModel._scheduledPauseOnAsyncCall = asyncCallStackTraceId; |
| const promises = []; |
| for (const model of SDK.DebuggerModel._debuggerIdToModel.values()) { |
| promises.push(model._pauseOnAsyncCall(asyncCallStackTraceId)); |
| } |
| await Promise.all(promises); |
| this.resume(); |
| return; |
| } |
| |
| const pausedDetails = |
| new DebuggerPausedDetails(this, callFrames, reason, auxData, breakpointIds, asyncStackTrace, asyncStackTraceId); |
| |
| if (pausedDetails && this._continueToLocationCallback) { |
| const callback = this._continueToLocationCallback; |
| delete this._continueToLocationCallback; |
| if (callback(pausedDetails)) { |
| return; |
| } |
| } |
| |
| if (!this._setDebuggerPausedDetails(pausedDetails)) { |
| if (this._autoStepOver) { |
| this._agent.stepOver(); |
| } else { |
| this._agent.stepInto(); |
| } |
| } |
| |
| SDK.DebuggerModel._scheduledPauseOnAsyncCall = null; |
| } |
| |
| _resumedScript() { |
| this._setDebuggerPausedDetails(null); |
| this.dispatchEventToListeners(Events.DebuggerResumed, this); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {string} sourceURL |
| * @param {number} startLine |
| * @param {number} startColumn |
| * @param {number} endLine |
| * @param {number} endColumn |
| * @param {!Protocol.Runtime.ExecutionContextId} executionContextId |
| * @param {string} hash |
| * @param {*|undefined} executionContextAuxData |
| * @param {boolean} isLiveEdit |
| * @param {string|undefined} sourceMapURL |
| * @param {boolean} hasSourceURLComment |
| * @param {boolean} hasSyntaxError |
| * @param {number} length |
| * @param {?Protocol.Runtime.StackTrace} originStackTrace |
| * @return {!SDK.Script} |
| */ |
| _parsedScriptSource( |
| scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash, |
| executionContextAuxData, isLiveEdit, sourceMapURL, hasSourceURLComment, hasSyntaxError, length, |
| originStackTrace) { |
| if (this._scripts.has(scriptId)) { |
| return this._scripts.get(scriptId); |
| } |
| let isContentScript = false; |
| if (executionContextAuxData && ('isDefault' in executionContextAuxData)) { |
| isContentScript = !executionContextAuxData['isDefault']; |
| } |
| sourceURL = this._internString(sourceURL); |
| const script = new SDK.Script( |
| this, scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, |
| this._internString(hash), isContentScript, isLiveEdit, sourceMapURL, hasSourceURLComment, length, |
| originStackTrace); |
| this._registerScript(script); |
| this.dispatchEventToListeners(Events.ParsedScriptSource, script); |
| |
| const sourceMapId = |
| SDK.DebuggerModel._sourceMapId(script.executionContextId, script.sourceURL, script.sourceMapURL); |
| if (sourceMapId && !hasSyntaxError) { |
| // Consecutive script evaluations in the same execution context with the same sourceURL |
| // and sourceMappingURL should result in source map reloading. |
| const previousScript = this._sourceMapIdToScript.get(sourceMapId); |
| if (previousScript) { |
| this._sourceMapManager.detachSourceMap(previousScript); |
| } |
| this._sourceMapIdToScript.set(sourceMapId, script); |
| this._sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL); |
| } |
| |
| const isDiscardable = hasSyntaxError && script.isAnonymousScript(); |
| if (isDiscardable) { |
| this._discardableScripts.push(script); |
| this._collectDiscardedScripts(); |
| } |
| return script; |
| } |
| |
| /** |
| * @param {!SDK.Script} script |
| * @param {string} newSourceMapURL |
| */ |
| setSourceMapURL(script, newSourceMapURL) { |
| let sourceMapId = SDK.DebuggerModel._sourceMapId(script.executionContextId, script.sourceURL, script.sourceMapURL); |
| if (sourceMapId && this._sourceMapIdToScript.get(sourceMapId) === script) { |
| this._sourceMapIdToScript.delete(sourceMapId); |
| } |
| this._sourceMapManager.detachSourceMap(script); |
| |
| script.sourceMapURL = newSourceMapURL; |
| sourceMapId = SDK.DebuggerModel._sourceMapId(script.executionContextId, script.sourceURL, script.sourceMapURL); |
| if (!sourceMapId) { |
| return; |
| } |
| this._sourceMapIdToScript.set(sourceMapId, script); |
| this._sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL); |
| } |
| |
| /** |
| * @param {!SDK.ExecutionContext} executionContext |
| */ |
| executionContextDestroyed(executionContext) { |
| const sourceMapIds = Array.from(this._sourceMapIdToScript.keys()); |
| for (const sourceMapId of sourceMapIds) { |
| const script = this._sourceMapIdToScript.get(sourceMapId); |
| if (script.executionContextId === executionContext.id) { |
| this._sourceMapIdToScript.delete(sourceMapId); |
| this._sourceMapManager.detachSourceMap(script); |
| } |
| } |
| } |
| |
| /** |
| * @param {!SDK.Script} script |
| */ |
| _registerScript(script) { |
| this._scripts.set(script.scriptId, script); |
| if (script.isAnonymousScript()) { |
| return; |
| } |
| |
| let scripts = this._scriptsBySourceURL.get(script.sourceURL); |
| if (!scripts) { |
| scripts = []; |
| this._scriptsBySourceURL.set(script.sourceURL, scripts); |
| } |
| scripts.push(script); |
| } |
| |
| /** |
| * @param {!SDK.Script} script |
| */ |
| _unregisterScript(script) { |
| console.assert(script.isAnonymousScript()); |
| this._scripts.delete(script.scriptId); |
| } |
| |
| _collectDiscardedScripts() { |
| if (this._discardableScripts.length < 1000) { |
| return; |
| } |
| const scriptsToDiscard = this._discardableScripts.splice(0, 100); |
| for (const script of scriptsToDiscard) { |
| this._unregisterScript(script); |
| this.dispatchEventToListeners(Events.DiscardedAnonymousScriptSource, script); |
| } |
| } |
| |
| /** |
| * @param {!SDK.Script} script |
| * @param {number} lineNumber |
| * @param {number} columnNumber |
| * @return {?Location} |
| */ |
| createRawLocation(script, lineNumber, columnNumber) { |
| return new Location(this, script.scriptId, lineNumber, columnNumber); |
| } |
| |
| /** |
| * @param {string} sourceURL |
| * @param {number} lineNumber |
| * @param {number} columnNumber |
| * @return {?Location} |
| */ |
| createRawLocationByURL(sourceURL, lineNumber, columnNumber) { |
| let closestScript = null; |
| const scripts = this._scriptsBySourceURL.get(sourceURL) || []; |
| for (let i = 0, l = scripts.length; i < l; ++i) { |
| const script = scripts[i]; |
| if (!closestScript) { |
| closestScript = script; |
| } |
| if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber)) { |
| continue; |
| } |
| if (script.endLine < lineNumber || (script.endLine === lineNumber && script.endColumn <= columnNumber)) { |
| continue; |
| } |
| closestScript = script; |
| break; |
| } |
| return closestScript ? new Location(this, closestScript.scriptId, lineNumber, columnNumber) : null; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {number} lineNumber |
| * @param {number} columnNumber |
| * @return {?Location} |
| */ |
| createRawLocationByScriptId(scriptId, lineNumber, columnNumber) { |
| const script = this.scriptForId(scriptId); |
| return script ? this.createRawLocation(script, lineNumber, columnNumber) : null; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.StackTrace} stackTrace |
| * @return {!Array<!Location>} |
| */ |
| createRawLocationsByStackTrace(stackTrace) { |
| const frames = []; |
| while (stackTrace) { |
| for (const frame of stackTrace.callFrames) { |
| frames.push(frame); |
| } |
| stackTrace = stackTrace.parent; |
| } |
| |
| const rawLocations = []; |
| for (const frame of frames) { |
| const rawLocation = this.createRawLocationByScriptId(frame.scriptId, frame.lineNumber, frame.columnNumber); |
| if (rawLocation) { |
| rawLocations.push(rawLocation); |
| } |
| } |
| return rawLocations; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isPaused() { |
| return !!this.debuggerPausedDetails(); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isPausing() { |
| return this._isPausing; |
| } |
| |
| /** |
| * @param {?CallFrame} callFrame |
| */ |
| setSelectedCallFrame(callFrame) { |
| if (this._selectedCallFrame === callFrame) { |
| return; |
| } |
| this._selectedCallFrame = callFrame; |
| this.dispatchEventToListeners(Events.CallFrameSelected, this); |
| } |
| |
| /** |
| * @return {?CallFrame} |
| */ |
| selectedCallFrame() { |
| return this._selectedCallFrame; |
| } |
| |
| /** |
| * @param {!SDK.RuntimeModel.EvaluationOptions} options |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} |
| */ |
| evaluateOnSelectedCallFrame(options) { |
| return this.selectedCallFrame().evaluate(options); |
| } |
| |
| /** |
| * @param {!SDK.RemoteObject} remoteObject |
| * @return {!Promise<?SDK.DebuggerModel.FunctionDetails>} |
| */ |
| functionDetailsPromise(remoteObject) { |
| return remoteObject.getAllProperties(false /* accessorPropertiesOnly */, false /* generatePreview */) |
| .then(buildDetails.bind(this)); |
| |
| /** |
| * @param {!SDK.GetPropertiesResult} response |
| * @return {?SDK.DebuggerModel.FunctionDetails} |
| * @this {!DebuggerModel} |
| */ |
| function buildDetails(response) { |
| if (!response) { |
| return null; |
| } |
| let location = null; |
| if (response.internalProperties) { |
| for (const prop of response.internalProperties) { |
| if (prop.name === '[[FunctionLocation]]') { |
| location = prop.value; |
| } |
| } |
| } |
| let functionName = null; |
| if (response.properties) { |
| for (const prop of response.properties) { |
| if (prop.name === 'name' && prop.value && prop.value.type === 'string') { |
| functionName = prop.value; |
| } |
| if (prop.name === 'displayName' && prop.value && prop.value.type === 'string') { |
| functionName = prop.value; |
| break; |
| } |
| } |
| } |
| let debuggerLocation = null; |
| if (location) { |
| debuggerLocation = this.createRawLocationByScriptId( |
| location.value.scriptId, location.value.lineNumber, location.value.columnNumber); |
| } |
| return {location: debuggerLocation, functionName: functionName ? functionName.value : ''}; |
| } |
| } |
| |
| /** |
| * @param {number} scopeNumber |
| * @param {string} variableName |
| * @param {!Protocol.Runtime.CallArgument} newValue |
| * @param {string} callFrameId |
| * @return {!Promise<string|undefined>} |
| */ |
| async setVariableValue(scopeNumber, variableName, newValue, callFrameId) { |
| const response = await this._agent.invoke_setVariableValue({scopeNumber, variableName, newValue, callFrameId}); |
| const error = response[Protocol.Error]; |
| if (error) { |
| console.error(error); |
| } |
| return error; |
| } |
| |
| /** |
| * @param {!Protocol.Debugger.BreakpointId} breakpointId |
| * @param {function(!Common.Event)} listener |
| * @param {!Object=} thisObject |
| */ |
| addBreakpointListener(breakpointId, listener, thisObject) { |
| this._breakpointResolvedEventTarget.addEventListener(breakpointId, listener, thisObject); |
| } |
| |
| /** |
| * @param {!Protocol.Debugger.BreakpointId} breakpointId |
| * @param {function(!Common.Event)} listener |
| * @param {!Object=} thisObject |
| */ |
| removeBreakpointListener(breakpointId, listener, thisObject) { |
| this._breakpointResolvedEventTarget.removeEventListener(breakpointId, listener, thisObject); |
| } |
| |
| /** |
| * @param {!Array<string>} patterns |
| * @return {!Promise<boolean>} |
| */ |
| async setBlackboxPatterns(patterns) { |
| const response = await this._agent.invoke_setBlackboxPatterns({patterns}); |
| const error = response[Protocol.Error]; |
| if (error) { |
| console.error(error); |
| } |
| return !error; |
| } |
| |
| /** |
| * @override |
| */ |
| dispose() { |
| this._sourceMapManager.dispose(); |
| SDK.DebuggerModel._debuggerIdToModel.delete(this._debuggerId); |
| Common.moduleSetting('pauseOnExceptionEnabled').removeChangeListener(this._pauseOnExceptionStateChanged, this); |
| Common.moduleSetting('pauseOnCaughtException').removeChangeListener(this._pauseOnExceptionStateChanged, this); |
| Common.moduleSetting('disableAsyncStackTraces').removeChangeListener(this._asyncStackTracesStateChanged, this); |
| } |
| |
| /** |
| * @override |
| * @return {!Promise} |
| */ |
| async suspendModel() { |
| await this._disableDebugger(); |
| } |
| |
| /** |
| * @override |
| * @return {!Promise} |
| */ |
| async resumeModel() { |
| await this._enableDebugger(); |
| } |
| |
| /** |
| * @param {string} string |
| * @return {string} string |
| */ |
| _internString(string) { |
| if (!this._stringMap.has(string)) { |
| this._stringMap.set(string, string); |
| } |
| return this._stringMap.get(string); |
| } |
| } |
| |
| /** @type {!Map<string, !SDK.DebuggerModel>} */ |
| export const _debuggerIdToModel = new Map(); |
| |
| /** @type {?Protocol.Runtime.StackTraceId} */ |
| export const _scheduledPauseOnAsyncCall = null; |
| |
| /** |
| * Keep these in sync with WebCore::V8Debugger |
| * |
| * @enum {string} |
| */ |
| export const PauseOnExceptionsState = { |
| DontPauseOnExceptions: 'none', |
| PauseOnAllExceptions: 'all', |
| PauseOnUncaughtExceptions: 'uncaught' |
| }; |
| |
| /** @enum {symbol} */ |
| export const Events = { |
| DebuggerWasEnabled: Symbol('DebuggerWasEnabled'), |
| DebuggerWasDisabled: Symbol('DebuggerWasDisabled'), |
| DebuggerPaused: Symbol('DebuggerPaused'), |
| DebuggerResumed: Symbol('DebuggerResumed'), |
| ParsedScriptSource: Symbol('ParsedScriptSource'), |
| FailedToParseScriptSource: Symbol('FailedToParseScriptSource'), |
| DiscardedAnonymousScriptSource: Symbol('DiscardedAnonymousScriptSource'), |
| GlobalObjectCleared: Symbol('GlobalObjectCleared'), |
| CallFrameSelected: Symbol('CallFrameSelected'), |
| ConsoleCommandEvaluatedInSelectedCallFrame: Symbol('ConsoleCommandEvaluatedInSelectedCallFrame'), |
| DebuggerIsReadyToPause: Symbol('DebuggerIsReadyToPause'), |
| }; |
| |
| /** @enum {string} */ |
| export const BreakReason = { |
| DOM: 'DOM', |
| EventListener: 'EventListener', |
| XHR: 'XHR', |
| Exception: 'exception', |
| PromiseRejection: 'promiseRejection', |
| Assert: 'assert', |
| DebugCommand: 'debugCommand', |
| OOM: 'OOM', |
| Other: 'other' |
| }; |
| |
| const ContinueToLocationTargetCallFrames = { |
| Any: 'any', |
| Current: 'current' |
| }; |
| |
| /** |
| * @extends {Protocol.DebuggerDispatcher} |
| * @unrestricted |
| */ |
| class DebuggerDispatcher { |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| */ |
| constructor(debuggerModel) { |
| this._debuggerModel = debuggerModel; |
| } |
| |
| /** |
| * @override |
| * @param {!Array.<!Protocol.Debugger.CallFrame>} callFrames |
| * @param {string} reason |
| * @param {!Object=} auxData |
| * @param {!Array.<string>=} breakpointIds |
| * @param {!Protocol.Runtime.StackTrace=} asyncStackTrace |
| * @param {!Protocol.Runtime.StackTraceId=} asyncStackTraceId |
| * @param {!Protocol.Runtime.StackTraceId=} asyncCallStackTraceId |
| */ |
| paused(callFrames, reason, auxData, breakpointIds, asyncStackTrace, asyncStackTraceId, asyncCallStackTraceId) { |
| this._debuggerModel._pausedScript( |
| callFrames, reason, auxData, breakpointIds || [], asyncStackTrace, asyncStackTraceId, asyncCallStackTraceId); |
| } |
| |
| /** |
| * @override |
| */ |
| resumed() { |
| this._debuggerModel._resumedScript(); |
| } |
| |
| /** |
| * @override |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {string} sourceURL |
| * @param {number} startLine |
| * @param {number} startColumn |
| * @param {number} endLine |
| * @param {number} endColumn |
| * @param {!Protocol.Runtime.ExecutionContextId} executionContextId |
| * @param {string} hash |
| * @param {*=} executionContextAuxData |
| * @param {boolean=} isLiveEdit |
| * @param {string=} sourceMapURL |
| * @param {boolean=} hasSourceURL |
| * @param {boolean=} isModule |
| * @param {number=} length |
| * @param {!Protocol.Runtime.StackTrace=} stackTrace |
| */ |
| scriptParsed( |
| scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash, |
| executionContextAuxData, isLiveEdit, sourceMapURL, hasSourceURL, isModule, length, stackTrace) { |
| this._debuggerModel._parsedScriptSource( |
| scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash, |
| executionContextAuxData, !!isLiveEdit, sourceMapURL, !!hasSourceURL, false, length || 0, stackTrace || null); |
| } |
| |
| /** |
| * @override |
| * @param {!Protocol.Runtime.ScriptId} scriptId |
| * @param {string} sourceURL |
| * @param {number} startLine |
| * @param {number} startColumn |
| * @param {number} endLine |
| * @param {number} endColumn |
| * @param {!Protocol.Runtime.ExecutionContextId} executionContextId |
| * @param {string} hash |
| * @param {*=} executionContextAuxData |
| * @param {string=} sourceMapURL |
| * @param {boolean=} hasSourceURL |
| * @param {boolean=} isModule |
| * @param {number=} length |
| * @param {!Protocol.Runtime.StackTrace=} stackTrace |
| */ |
| scriptFailedToParse( |
| scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash, |
| executionContextAuxData, sourceMapURL, hasSourceURL, isModule, length, stackTrace) { |
| this._debuggerModel._parsedScriptSource( |
| scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash, |
| executionContextAuxData, false, sourceMapURL, !!hasSourceURL, true, length || 0, stackTrace || null); |
| } |
| |
| /** |
| * @override |
| * @param {!Protocol.Debugger.BreakpointId} breakpointId |
| * @param {!Protocol.Debugger.Location} location |
| */ |
| breakpointResolved(breakpointId, location) { |
| this._debuggerModel._breakpointResolved(breakpointId, location); |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| export class Location { |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {string} scriptId |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| */ |
| constructor(debuggerModel, scriptId, lineNumber, columnNumber) { |
| this.debuggerModel = debuggerModel; |
| this.scriptId = scriptId; |
| this.lineNumber = lineNumber; |
| this.columnNumber = columnNumber || 0; |
| } |
| |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {!Protocol.Debugger.Location} payload |
| * @return {!Location} |
| */ |
| static fromPayload(debuggerModel, payload) { |
| return new Location(debuggerModel, payload.scriptId, payload.lineNumber, payload.columnNumber); |
| } |
| |
| /** |
| * @return {!Protocol.Debugger.Location} |
| */ |
| payload() { |
| return {scriptId: this.scriptId, lineNumber: this.lineNumber, columnNumber: this.columnNumber}; |
| } |
| |
| /** |
| * @return {?SDK.Script} |
| */ |
| script() { |
| return this.debuggerModel.scriptForId(this.scriptId); |
| } |
| |
| /** |
| * @param {function()=} pausedCallback |
| */ |
| continueToLocation(pausedCallback) { |
| if (pausedCallback) { |
| this.debuggerModel._continueToLocationCallback = this._paused.bind(this, pausedCallback); |
| } |
| this.debuggerModel._agent.continueToLocation(this.payload(), ContinueToLocationTargetCallFrames.Current); |
| } |
| |
| /** |
| * @param {function()|undefined} pausedCallback |
| * @param {!DebuggerPausedDetails} debuggerPausedDetails |
| * @return {boolean} |
| */ |
| _paused(pausedCallback, debuggerPausedDetails) { |
| const location = debuggerPausedDetails.callFrames[0].location(); |
| if (location.scriptId === this.scriptId && location.lineNumber === this.lineNumber && |
| location.columnNumber === this.columnNumber) { |
| pausedCallback(); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| id() { |
| return this.debuggerModel.target().id() + ':' + this.scriptId + ':' + this.lineNumber + ':' + this.columnNumber; |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| export class BreakLocation extends Location { |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {string} scriptId |
| * @param {number} lineNumber |
| * @param {number=} columnNumber |
| * @param {!Protocol.Debugger.BreakLocationType=} type |
| */ |
| constructor(debuggerModel, scriptId, lineNumber, columnNumber, type) { |
| super(debuggerModel, scriptId, lineNumber, columnNumber); |
| if (type) { |
| this.type = type; |
| } |
| } |
| |
| /** |
| * @override |
| * @param {!DebuggerModel} debuggerModel |
| * @param {!Protocol.Debugger.BreakLocation} payload |
| * @return {!BreakLocation} |
| */ |
| static fromPayload(debuggerModel, payload) { |
| return new BreakLocation(debuggerModel, payload.scriptId, payload.lineNumber, payload.columnNumber, payload.type); |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| export class CallFrame { |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {!SDK.Script} script |
| * @param {!Protocol.Debugger.CallFrame} payload |
| */ |
| constructor(debuggerModel, script, payload) { |
| this.debuggerModel = debuggerModel; |
| this._script = script; |
| this._payload = payload; |
| this._location = Location.fromPayload(debuggerModel, payload.location); |
| this._scopeChain = []; |
| this._localScope = null; |
| for (let i = 0; i < payload.scopeChain.length; ++i) { |
| const scope = new Scope(this, i); |
| this._scopeChain.push(scope); |
| if (scope.type() === Protocol.Debugger.ScopeType.Local) { |
| this._localScope = scope; |
| } |
| } |
| if (payload.functionLocation) { |
| this._functionLocation = Location.fromPayload(debuggerModel, payload.functionLocation); |
| } |
| this._returnValue = |
| payload.returnValue ? this.debuggerModel._runtimeModel.createRemoteObject(payload.returnValue) : null; |
| } |
| |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {!Array.<!Protocol.Debugger.CallFrame>} callFrames |
| * @return {!Array.<!CallFrame>} |
| */ |
| static fromPayloadArray(debuggerModel, callFrames) { |
| const result = []; |
| for (let i = 0; i < callFrames.length; ++i) { |
| const callFrame = callFrames[i]; |
| const script = debuggerModel.scriptForId(callFrame.location.scriptId); |
| if (script) { |
| result.push(new CallFrame(debuggerModel, script, callFrame)); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @return {!SDK.Script} |
| */ |
| get script() { |
| return this._script; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get id() { |
| return this._payload.callFrameId; |
| } |
| |
| /** |
| * @return {!Array.<!Scope>} |
| */ |
| scopeChain() { |
| return this._scopeChain; |
| } |
| |
| /** |
| * @return {?Scope} |
| */ |
| localScope() { |
| return this._localScope; |
| } |
| |
| /** |
| * @return {?SDK.RemoteObject} |
| */ |
| thisObject() { |
| return this._payload.this ? this.debuggerModel._runtimeModel.createRemoteObject(this._payload.this) : null; |
| } |
| |
| /** |
| * @return {?SDK.RemoteObject} |
| */ |
| returnValue() { |
| return this._returnValue; |
| } |
| |
| /** |
| * @param {string} expression |
| * @return {!Promise<?SDK.RemoteObject>} |
| */ |
| async setReturnValue(expression) { |
| if (!this._returnValue) { |
| return null; |
| } |
| |
| const evaluateResponse = await this.debuggerModel._agent.invoke_evaluateOnCallFrame( |
| {callFrameId: this.id, expression: expression, silent: true, objectGroup: 'backtrace'}); |
| if (evaluateResponse[Protocol.Error] || evaluateResponse.exceptionDetails) { |
| return null; |
| } |
| const response = await this.debuggerModel._agent.invoke_setReturnValue({newValue: evaluateResponse.result}); |
| if (response[Protocol.Error]) { |
| return null; |
| } |
| this._returnValue = this.debuggerModel._runtimeModel.createRemoteObject(evaluateResponse.result); |
| return this._returnValue; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get functionName() { |
| return this._payload.functionName; |
| } |
| |
| /** |
| * @return {!Location} |
| */ |
| location() { |
| return this._location; |
| } |
| |
| /** |
| * @return {?Location} |
| */ |
| functionLocation() { |
| return this._functionLocation || null; |
| } |
| |
| /** |
| * @param {!SDK.RuntimeModel.EvaluationOptions} options |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} |
| */ |
| async evaluate(options) { |
| const runtimeModel = this.debuggerModel.runtimeModel(); |
| // Assume backends either support both throwOnSideEffect and timeout options or neither. |
| const needsTerminationOptions = !!options.throwOnSideEffect || options.timeout !== undefined; |
| if (needsTerminationOptions && |
| (runtimeModel.hasSideEffectSupport() === false || |
| (runtimeModel.hasSideEffectSupport() === null && !await runtimeModel.checkSideEffectSupport()))) { |
| return {error: 'Side-effect checks not supported by backend.'}; |
| } |
| |
| const response = await this.debuggerModel._agent.invoke_evaluateOnCallFrame({ |
| callFrameId: this.id, |
| expression: options.expression, |
| objectGroup: options.objectGroup, |
| includeCommandLineAPI: options.includeCommandLineAPI, |
| silent: options.silent, |
| returnByValue: options.returnByValue, |
| generatePreview: options.generatePreview, |
| throwOnSideEffect: options.throwOnSideEffect, |
| timeout: options.timeout |
| }); |
| const error = response[Protocol.Error]; |
| if (error) { |
| console.error(error); |
| return {error: error}; |
| } |
| return {object: runtimeModel.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; |
| } |
| |
| async restart() { |
| const response = await this.debuggerModel._agent.invoke_restartFrame({callFrameId: this._payload.callFrameId}); |
| if (!response[Protocol.Error]) { |
| this.debuggerModel.stepInto(); |
| } |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| export class Scope { |
| /** |
| * @param {!CallFrame} callFrame |
| * @param {number} ordinal |
| */ |
| constructor(callFrame, ordinal) { |
| this._callFrame = callFrame; |
| this._payload = callFrame._payload.scopeChain[ordinal]; |
| this._type = this._payload.type; |
| this._name = this._payload.name; |
| this._ordinal = ordinal; |
| this._startLocation = |
| this._payload.startLocation ? Location.fromPayload(callFrame.debuggerModel, this._payload.startLocation) : null; |
| this._endLocation = |
| this._payload.endLocation ? Location.fromPayload(callFrame.debuggerModel, this._payload.endLocation) : null; |
| } |
| |
| /** |
| * @return {!CallFrame} |
| */ |
| callFrame() { |
| return this._callFrame; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| type() { |
| return this._type; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| typeName() { |
| switch (this._type) { |
| case Protocol.Debugger.ScopeType.Local: |
| return Common.UIString('Local'); |
| case Protocol.Debugger.ScopeType.Closure: |
| return Common.UIString('Closure'); |
| case Protocol.Debugger.ScopeType.Catch: |
| return Common.UIString('Catch'); |
| case Protocol.Debugger.ScopeType.Block: |
| return Common.UIString('Block'); |
| case Protocol.Debugger.ScopeType.Script: |
| return Common.UIString('Script'); |
| case Protocol.Debugger.ScopeType.With: |
| return Common.UIString('With Block'); |
| case Protocol.Debugger.ScopeType.Global: |
| return Common.UIString('Global'); |
| case Protocol.Debugger.ScopeType.Module: |
| return Common.UIString('Module'); |
| } |
| return ''; |
| } |
| |
| |
| /** |
| * @return {string|undefined} |
| */ |
| name() { |
| return this._name; |
| } |
| |
| /** |
| * @return {?Location} |
| */ |
| startLocation() { |
| return this._startLocation; |
| } |
| |
| /** |
| * @return {?Location} |
| */ |
| endLocation() { |
| return this._endLocation; |
| } |
| |
| /** |
| * @return {!SDK.RemoteObject} |
| */ |
| object() { |
| if (this._object) { |
| return this._object; |
| } |
| const runtimeModel = this._callFrame.debuggerModel._runtimeModel; |
| |
| const declarativeScope = |
| this._type !== Protocol.Debugger.ScopeType.With && this._type !== Protocol.Debugger.ScopeType.Global; |
| if (declarativeScope) { |
| this._object = runtimeModel.createScopeRemoteObject( |
| this._payload.object, new SDK.ScopeRef(this._ordinal, this._callFrame.id)); |
| } else { |
| this._object = runtimeModel.createRemoteObject(this._payload.object); |
| } |
| |
| return this._object; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| description() { |
| const declarativeScope = |
| this._type !== Protocol.Debugger.ScopeType.With && this._type !== Protocol.Debugger.ScopeType.Global; |
| return declarativeScope ? '' : (this._payload.object.description || ''); |
| } |
| } |
| |
| /** |
| * @unrestricted |
| */ |
| export class DebuggerPausedDetails { |
| /** |
| * @param {!DebuggerModel} debuggerModel |
| * @param {!Array.<!Protocol.Debugger.CallFrame>} callFrames |
| * @param {string} reason |
| * @param {!Object|undefined} auxData |
| * @param {!Array.<string>} breakpointIds |
| * @param {!Protocol.Runtime.StackTrace=} asyncStackTrace |
| * @param {!Protocol.Runtime.StackTraceId=} asyncStackTraceId |
| */ |
| constructor(debuggerModel, callFrames, reason, auxData, breakpointIds, asyncStackTrace, asyncStackTraceId) { |
| this.debuggerModel = debuggerModel; |
| this.callFrames = CallFrame.fromPayloadArray(debuggerModel, callFrames); |
| this.reason = reason; |
| this.auxData = auxData; |
| this.breakpointIds = breakpointIds; |
| if (asyncStackTrace) { |
| this.asyncStackTrace = this._cleanRedundantFrames(asyncStackTrace); |
| } |
| this.asyncStackTraceId = asyncStackTraceId; |
| } |
| |
| /** |
| * @return {?SDK.RemoteObject} |
| */ |
| exception() { |
| if (this.reason !== BreakReason.Exception && this.reason !== BreakReason.PromiseRejection) { |
| return null; |
| } |
| return this.debuggerModel._runtimeModel.createRemoteObject( |
| /** @type {!Protocol.Runtime.RemoteObject} */ (this.auxData)); |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.StackTrace} asyncStackTrace |
| * @return {!Protocol.Runtime.StackTrace} |
| */ |
| _cleanRedundantFrames(asyncStackTrace) { |
| let stack = asyncStackTrace; |
| let previous = null; |
| while (stack) { |
| if (stack.description === 'async function' && stack.callFrames.length) { |
| stack.callFrames.shift(); |
| } |
| if (previous && !stack.callFrames.length) { |
| previous.parent = stack.parent; |
| } else { |
| previous = stack; |
| } |
| stack = stack.parent; |
| } |
| return asyncStackTrace; |
| } |
| } |
| |
| /* Legacy exported object */ |
| self.SDK = self.SDK || {}; |
| |
| /* Legacy exported object */ |
| SDK = SDK || {}; |
| |
| /** @constructor */ |
| SDK.DebuggerModel = DebuggerModel; |
| |
| /** @enum {string} */ |
| SDK.DebuggerModel.PauseOnExceptionsState = PauseOnExceptionsState; |
| |
| /** @enum {symbol} */ |
| SDK.DebuggerModel.Events = Events; |
| |
| /** @enum {string} */ |
| SDK.DebuggerModel.BreakReason = BreakReason; |
| |
| /** @constructor */ |
| SDK.DebuggerModel.Location = Location; |
| |
| /** @constructor */ |
| SDK.DebuggerModel.BreakLocation = BreakLocation; |
| |
| /** @constructor */ |
| SDK.DebuggerModel.CallFrame = CallFrame; |
| |
| /** @constructor */ |
| SDK.DebuggerModel.Scope = Scope; |
| |
| /** @constructor */ |
| SDK.DebuggerPausedDetails = DebuggerPausedDetails; |
| |
| SDK.SDKModel.register(SDK.DebuggerModel, SDK.Target.Capability.JS, true); |
| |
| /** @typedef {{location: ?Location, functionName: string}} */ |
| SDK.DebuggerModel.FunctionDetails; |
| |
| /** @typedef {{ |
| * breakpointId: ?Protocol.Debugger.BreakpointId, |
| * locations: !Array<!Location> |
| * }} |
| */ |
| SDK.DebuggerModel.SetBreakpointResult; |
| |
| /** @type {!Map<string, !SDK.DebuggerModel>} */ |
| SDK.DebuggerModel._debuggerIdToModel = _debuggerIdToModel; |
| |
| /** @type {?Protocol.Runtime.StackTraceId} */ |
| SDK.DebuggerModel._scheduledPauseOnAsyncCall = _scheduledPauseOnAsyncCall; |