mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 04:26:32 +08:00
Bug: chromium:1011811 Change-Id: Ie8eb0d43c87b904035889edd1b5862b979359ac9 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2479124 Commit-Queue: Philip Pfaffe <pfaffe@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org>
662 lines
21 KiB
JavaScript
662 lines
21 KiB
JavaScript
// Copyright 2014 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.
|
|
|
|
import * as Common from '../common/common.js'; // eslint-disable-line no-unused-vars
|
|
import * as Platform from '../platform/platform.js';
|
|
import * as Root from '../root/root.js';
|
|
import * as SDK from '../sdk/sdk.js';
|
|
import * as Workspace from '../workspace/workspace.js'; // eslint-disable-line no-unused-vars
|
|
|
|
import {BlackboxManager} from './BlackboxManager.js';
|
|
import {CompilerScriptMapping} from './CompilerScriptMapping.js';
|
|
import {DebuggerLanguagePluginManager} from './DebuggerLanguagePlugins.js';
|
|
import {DefaultScriptMapping} from './DefaultScriptMapping.js';
|
|
import {LiveLocation, LiveLocationPool, LiveLocationWithPool} from './LiveLocation.js'; // eslint-disable-line no-unused-vars
|
|
import {ResourceMapping} from './ResourceMapping.js';
|
|
import {ResourceScriptFile, ResourceScriptMapping} from './ResourceScriptMapping.js'; // eslint-disable-line no-unused-vars
|
|
|
|
/**
|
|
* @type {!DebuggerWorkspaceBinding}
|
|
*/
|
|
let debuggerWorkspaceBindingInstance;
|
|
|
|
/**
|
|
* @unrestricted
|
|
* @implements {SDK.SDKModel.SDKModelObserver<!SDK.DebuggerModel.DebuggerModel>}
|
|
*/
|
|
export class DebuggerWorkspaceBinding {
|
|
/**
|
|
* @private
|
|
* @param {!SDK.SDKModel.TargetManager} targetManager
|
|
* @param {!Workspace.Workspace.WorkspaceImpl} workspace
|
|
*/
|
|
constructor(targetManager, workspace) {
|
|
this._workspace = workspace;
|
|
|
|
/** @type {!Array<!DebuggerSourceMapping>} */
|
|
this._sourceMappings = [];
|
|
|
|
/** @type {!Map.<!SDK.DebuggerModel.DebuggerModel, !ModelData>} */
|
|
this._debuggerModelToData = new Map();
|
|
targetManager.addModelListener(
|
|
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, this._globalObjectCleared, this);
|
|
targetManager.addModelListener(
|
|
SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
|
|
targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
|
|
|
|
/** @type {!Set.<!Promise<?>>} */
|
|
this._liveLocationPromises = new Set();
|
|
}
|
|
|
|
/**
|
|
* @param {{forceNew: ?boolean, targetManager: ?SDK.SDKModel.TargetManager, workspace: ?Workspace.Workspace.WorkspaceImpl}} opts
|
|
*/
|
|
static instance(opts = {forceNew: null, targetManager: null, workspace: null}) {
|
|
const {forceNew, targetManager, workspace} = opts;
|
|
if (!debuggerWorkspaceBindingInstance || forceNew) {
|
|
if (!targetManager || !workspace) {
|
|
throw new Error(
|
|
`Unable to create settings: targetManager and workspace must be provided: ${new Error().stack}`);
|
|
}
|
|
|
|
debuggerWorkspaceBindingInstance = new DebuggerWorkspaceBinding(targetManager, workspace);
|
|
}
|
|
|
|
return debuggerWorkspaceBindingInstance;
|
|
}
|
|
|
|
/**
|
|
* @param {!DebuggerSourceMapping} sourceMapping
|
|
*/
|
|
addSourceMapping(sourceMapping) {
|
|
this._sourceMappings.push(sourceMapping);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
*/
|
|
modelAdded(debuggerModel) {
|
|
this._debuggerModelToData.set(debuggerModel, new ModelData(debuggerModel, this));
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
*/
|
|
modelRemoved(debuggerModel) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (modelData) {
|
|
modelData._dispose();
|
|
this._debuggerModelToData.delete(debuggerModel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
* @return {?DebuggerLanguagePluginManager}
|
|
*/
|
|
getLanguagePluginManager(debuggerModel) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (!modelData) {
|
|
return null;
|
|
}
|
|
return modelData.pluginManager;
|
|
}
|
|
|
|
/**
|
|
* The promise returned by this function is resolved once all *currently*
|
|
* pending LiveLocations are processed.
|
|
*
|
|
* @return {!Promise<?>}
|
|
*/
|
|
async pendingLiveLocationChangesPromise() {
|
|
await Promise.all(this._liveLocationPromises);
|
|
}
|
|
|
|
/**
|
|
* @param {!Promise<?>} promise
|
|
*/
|
|
_recordLiveLocationChange(promise) {
|
|
promise.then(() => {
|
|
this._liveLocationPromises.delete(promise);
|
|
});
|
|
this._liveLocationPromises.add(promise);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script.Script} script
|
|
*/
|
|
async updateLocations(script) {
|
|
const modelData = this._debuggerModelToData.get(script.debuggerModel);
|
|
if (modelData) {
|
|
const updatePromise = modelData._updateLocations(script);
|
|
this._recordLiveLocationChange(updatePromise);
|
|
await updatePromise;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
* @return {!Promise<?Location>}
|
|
*/
|
|
createLiveLocation(rawLocation, updateDelegate, locationPool) {
|
|
const script = rawLocation.script();
|
|
if (!script) {
|
|
return Promise.resolve(null);
|
|
}
|
|
const modelData = this._debuggerModelToData.get(script.debuggerModel);
|
|
if (!modelData) {
|
|
return Promise.resolve(null);
|
|
}
|
|
const liveLocationPromise = modelData._createLiveLocation(rawLocation, updateDelegate, locationPool);
|
|
this._recordLiveLocationChange(liveLocationPromise);
|
|
return liveLocationPromise;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<!SDK.DebuggerModel.Location>} rawLocations
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
* @return {!Promise<!LiveLocation>}
|
|
*/
|
|
async createStackTraceTopFrameLiveLocation(rawLocations, updateDelegate, locationPool) {
|
|
console.assert(rawLocations.length > 0);
|
|
const locationPromise =
|
|
StackTraceTopFrameLocation.createStackTraceTopFrameLocation(rawLocations, this, updateDelegate, locationPool);
|
|
this._recordLiveLocationChange(locationPromise);
|
|
return locationPromise;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} location
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
* @return {!Promise<?Location>}
|
|
*/
|
|
async createCallFrameLiveLocation(location, updateDelegate, locationPool) {
|
|
const script = location.script();
|
|
if (!script) {
|
|
return null;
|
|
}
|
|
const debuggerModel = location.debuggerModel;
|
|
const liveLocationPromise = this.createLiveLocation(location, updateDelegate, locationPool);
|
|
this._recordLiveLocationChange(liveLocationPromise);
|
|
const liveLocation = await liveLocationPromise;
|
|
if (!liveLocation) {
|
|
return null;
|
|
}
|
|
this._registerCallFrameLiveLocation(debuggerModel, liveLocation);
|
|
return liveLocation;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @return {!Promise<?Workspace.UISourceCode.UILocation>}
|
|
*/
|
|
async rawLocationToUILocation(rawLocation) {
|
|
for (const sourceMapping of this._sourceMappings) {
|
|
const uiLocation = sourceMapping.rawLocationToUILocation(rawLocation);
|
|
if (uiLocation) {
|
|
return uiLocation;
|
|
}
|
|
}
|
|
const modelData = this._debuggerModelToData.get(rawLocation.debuggerModel);
|
|
return modelData ? modelData._rawLocationToUILocation(rawLocation) : null;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
* @param {string} url
|
|
* @param {boolean} isContentScript
|
|
*/
|
|
uiSourceCodeForSourceMapSourceURL(debuggerModel, url, isContentScript) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (!modelData) {
|
|
return null;
|
|
}
|
|
return modelData._compilerMapping.uiSourceCodeForURL(url, isContentScript);
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Promise<!Array<!SDK.DebuggerModel.Location>>}
|
|
*/
|
|
async uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
|
|
for (const sourceMapping of this._sourceMappings) {
|
|
const locations = sourceMapping.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
|
|
if (locations.length) {
|
|
return locations;
|
|
}
|
|
}
|
|
|
|
const locationsPromises = [];
|
|
for (const modelData of this._debuggerModelToData.values()) {
|
|
locationsPromises.push(modelData._uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber));
|
|
}
|
|
return (await Promise.all(locationsPromises)).flat();
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Array<!SDK.DebuggerModel.Location>}
|
|
*/
|
|
uiLocationToRawLocationsForUnformattedJavaScript(uiSourceCode, lineNumber, columnNumber) {
|
|
console.assert(uiSourceCode.contentType().isScript());
|
|
const locations = [];
|
|
for (const modelData of this._debuggerModelToData.values()) {
|
|
locations.push(...modelData._uiLocationToRawLocationsExcludeAsync(uiSourceCode, lineNumber, columnNumber));
|
|
}
|
|
return locations;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UILocation} uiLocation
|
|
* @return {!Promise<!Workspace.UISourceCode.UILocation>}
|
|
*/
|
|
async normalizeUILocation(uiLocation) {
|
|
const rawLocations =
|
|
await this.uiLocationToRawLocations(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
|
|
for (const location of rawLocations) {
|
|
const uiLocationCandidate = await this.rawLocationToUILocation(location);
|
|
if (uiLocationCandidate) {
|
|
return uiLocationCandidate;
|
|
}
|
|
}
|
|
return uiLocation;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
* @return {?ResourceScriptFile}
|
|
*/
|
|
scriptFile(uiSourceCode, debuggerModel) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
return modelData ? modelData._resourceMapping.scriptFile(uiSourceCode) : null;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script.Script} script
|
|
* @return {?SDK.SourceMap.SourceMap}
|
|
*/
|
|
sourceMapForScript(script) {
|
|
const modelData = this._debuggerModelToData.get(script.debuggerModel);
|
|
if (!modelData) {
|
|
return null;
|
|
}
|
|
return modelData._compilerMapping.sourceMapForScript(script);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_globalObjectCleared(event) {
|
|
const debuggerModel = /** @type {!SDK.DebuggerModel.DebuggerModel} */ (event.data);
|
|
this._reset(debuggerModel);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
*/
|
|
_reset(debuggerModel) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (!modelData) {
|
|
return;
|
|
}
|
|
for (const location of modelData.callFrameLocations.values()) {
|
|
this._removeLiveLocation(location);
|
|
}
|
|
modelData.callFrameLocations.clear();
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.SDKModel.Target} target
|
|
*/
|
|
_resetForTest(target) {
|
|
const debuggerModel =
|
|
/** @type {!SDK.DebuggerModel.DebuggerModel} */ (target.model(SDK.DebuggerModel.DebuggerModel));
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (modelData) {
|
|
modelData._resourceMapping.resetForTest();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
* @param {!Location} location
|
|
*/
|
|
_registerCallFrameLiveLocation(debuggerModel, location) {
|
|
const modelData = this._debuggerModelToData.get(debuggerModel);
|
|
if (modelData) {
|
|
const locations = modelData.callFrameLocations;
|
|
locations.add(location);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Location} location
|
|
*/
|
|
_removeLiveLocation(location) {
|
|
const modelData = this._debuggerModelToData.get(location._script.debuggerModel);
|
|
if (modelData) {
|
|
modelData._disposeLocation(location);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.EventTarget.EventTargetEvent} event
|
|
*/
|
|
_debuggerResumed(event) {
|
|
const debuggerModel = /** @type {!SDK.DebuggerModel.DebuggerModel} */ (event.data);
|
|
this._reset(debuggerModel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
class ModelData {
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
|
|
* @param {!DebuggerWorkspaceBinding} debuggerWorkspaceBinding
|
|
*/
|
|
constructor(debuggerModel, debuggerWorkspaceBinding) {
|
|
this._debuggerModel = debuggerModel;
|
|
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
|
|
/** @type {!Set.<!Location>} */
|
|
this.callFrameLocations = new Set();
|
|
|
|
const workspace = debuggerWorkspaceBinding._workspace;
|
|
|
|
if (Root.Runtime.experiments.isEnabled('wasmDWARFDebugging')) {
|
|
this._pluginManager = new DebuggerLanguagePluginManager(debuggerModel, workspace, debuggerWorkspaceBinding);
|
|
}
|
|
|
|
|
|
this._defaultMapping = new DefaultScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
|
|
this._resourceMapping = new ResourceScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
|
|
this._compilerMapping = new CompilerScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
|
|
|
|
/** @type {!Platform.Multimap<!SDK.Script.Script, !Location>} */
|
|
this._locations = new Platform.Multimap();
|
|
|
|
debuggerModel.setBeforePausedCallback(this._beforePaused.bind(this));
|
|
}
|
|
|
|
/**
|
|
* return {?DebuggerLanguagePluginManager}
|
|
*/
|
|
get pluginManager() {
|
|
return this._pluginManager || null;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
* @return {!Promise<!Location>}
|
|
*/
|
|
async _createLiveLocation(rawLocation, updateDelegate, locationPool) {
|
|
console.assert(rawLocation.script() !== null);
|
|
const script = /** @type {!SDK.Script.Script} */ (rawLocation.script());
|
|
const location = new Location(script, rawLocation, this._debuggerWorkspaceBinding, updateDelegate, locationPool);
|
|
this._locations.set(script, location);
|
|
await location.update();
|
|
return location;
|
|
}
|
|
|
|
/**
|
|
* @param {!Location} location
|
|
*/
|
|
_disposeLocation(location) {
|
|
this._locations.delete(location._script, location);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.Script.Script} script
|
|
*/
|
|
async _updateLocations(script) {
|
|
const promises = [];
|
|
for (const location of this._locations.get(script)) {
|
|
promises.push(location.update());
|
|
}
|
|
await Promise.all(promises);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @return {!Promise<?Workspace.UISourceCode.UILocation>}
|
|
*/
|
|
async _rawLocationToUILocation(rawLocation) {
|
|
let uiLocation = null;
|
|
if (this._pluginManager) {
|
|
uiLocation = await this._pluginManager.rawLocationToUILocation(rawLocation);
|
|
}
|
|
uiLocation = uiLocation || this._compilerMapping.rawLocationToUILocation(rawLocation);
|
|
uiLocation = uiLocation || this._resourceMapping.rawLocationToUILocation(rawLocation);
|
|
uiLocation = uiLocation || ResourceMapping.instance().jsLocationToUILocation(rawLocation);
|
|
uiLocation = uiLocation || this._defaultMapping.rawLocationToUILocation(rawLocation);
|
|
return /** @type {!Workspace.UISourceCode.UILocation} */ (uiLocation);
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Promise<!Array<!SDK.DebuggerModel.Location>>}
|
|
*/
|
|
async _uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
|
|
let rawLocations = null;
|
|
if (this._pluginManager) {
|
|
rawLocations = await this._pluginManager.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
|
|
}
|
|
rawLocations = rawLocations || this._uiLocationToRawLocationsExcludeAsync(uiSourceCode, lineNumber, columnNumber);
|
|
return rawLocations;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Array<!SDK.DebuggerModel.Location>}
|
|
*/
|
|
_uiLocationToRawLocationsExcludeAsync(uiSourceCode, lineNumber, columnNumber) {
|
|
let locations = this._compilerMapping.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
|
|
locations = locations.length ?
|
|
locations :
|
|
this._resourceMapping.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
|
|
locations = locations.length ?
|
|
locations :
|
|
ResourceMapping.instance().uiLocationToJSLocations(uiSourceCode, lineNumber, columnNumber);
|
|
locations = locations.length ?
|
|
locations :
|
|
this._defaultMapping.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
|
|
return locations;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.DebuggerPausedDetails} debuggerPausedDetails
|
|
* @return {boolean}
|
|
*/
|
|
_beforePaused(debuggerPausedDetails) {
|
|
const callFrame = debuggerPausedDetails.callFrames[0];
|
|
if (!callFrame) {
|
|
return false;
|
|
}
|
|
if (callFrame.script.sourceMapURL !== SDK.SourceMap.WasmSourceMap.FAKE_URL &&
|
|
!Root.Runtime.experiments.isEnabled('emptySourceMapAutoStepping')) {
|
|
return true;
|
|
}
|
|
return !!this._compilerMapping.mapsToSourceCode(callFrame.location());
|
|
}
|
|
|
|
_dispose() {
|
|
this._debuggerModel.setBeforePausedCallback(null);
|
|
this._compilerMapping.dispose();
|
|
this._resourceMapping.dispose();
|
|
this._defaultMapping.dispose();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
export class Location extends LiveLocationWithPool {
|
|
/**
|
|
* @param {!SDK.Script.Script} script
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @param {!DebuggerWorkspaceBinding} binding
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
*/
|
|
constructor(script, rawLocation, binding, updateDelegate, locationPool) {
|
|
super(updateDelegate, locationPool);
|
|
this._script = script;
|
|
this._rawLocation = rawLocation;
|
|
this._binding = binding;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Promise<?Workspace.UISourceCode.UILocation>}
|
|
*/
|
|
async uiLocation() {
|
|
const debuggerModelLocation = this._rawLocation;
|
|
return this._binding.rawLocationToUILocation(debuggerModelLocation);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
dispose() {
|
|
super.dispose();
|
|
this._binding._removeLiveLocation(this);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Promise<boolean>}
|
|
*/
|
|
async isBlackboxed() {
|
|
const uiLocation = await this.uiLocation();
|
|
return uiLocation ? BlackboxManager.instance().isBlackboxedUISourceCode(uiLocation.uiSourceCode) : false;
|
|
}
|
|
}
|
|
|
|
class StackTraceTopFrameLocation extends LiveLocationWithPool {
|
|
/**
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
*/
|
|
constructor(updateDelegate, locationPool) {
|
|
super(updateDelegate, locationPool);
|
|
this._updateScheduled = true;
|
|
/** @type {?LiveLocation} */
|
|
this._current = null;
|
|
/** @type {?Array<!LiveLocation>} */
|
|
this._locations = null;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<!SDK.DebuggerModel.Location>} rawLocations
|
|
* @param {!DebuggerWorkspaceBinding} binding
|
|
* @param {function(!LiveLocation): !Promise<?>} updateDelegate
|
|
* @param {!LiveLocationPool} locationPool
|
|
* @return {!Promise<!StackTraceTopFrameLocation>}
|
|
*/
|
|
static async createStackTraceTopFrameLocation(rawLocations, binding, updateDelegate, locationPool) {
|
|
const location = new StackTraceTopFrameLocation(updateDelegate, locationPool);
|
|
const locationsPromises = rawLocations.map(
|
|
rawLocation => binding.createLiveLocation(rawLocation, location._scheduleUpdate.bind(location), locationPool));
|
|
location._locations = /** @type {!Array<!Location>} */ ((await Promise.all(locationsPromises)).filter(l => !!l));
|
|
await location._updateLocation();
|
|
return location;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Promise<?Workspace.UISourceCode.UILocation>}
|
|
*/
|
|
async uiLocation() {
|
|
return this._current ? this._current.uiLocation() : null;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {!Promise<boolean>}
|
|
*/
|
|
async isBlackboxed() {
|
|
return this._current ? this._current.isBlackboxed() : false;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
dispose() {
|
|
super.dispose();
|
|
if (this._locations) {
|
|
for (const location of this._locations) {
|
|
location.dispose();
|
|
}
|
|
}
|
|
this._locations = null;
|
|
this._current = null;
|
|
}
|
|
|
|
async _scheduleUpdate() {
|
|
if (this._updateScheduled) {
|
|
return;
|
|
}
|
|
this._updateScheduled = true;
|
|
setImmediate(this._updateLocation.bind(this));
|
|
}
|
|
|
|
async _updateLocation() {
|
|
this._updateScheduled = false;
|
|
if (!this._locations || this._locations.length === 0) {
|
|
return;
|
|
}
|
|
|
|
this._current = this._locations[0];
|
|
for (const location of this._locations) {
|
|
if (!(await location.isBlackboxed())) {
|
|
this._current = location;
|
|
break;
|
|
}
|
|
}
|
|
this.update();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @interface
|
|
*/
|
|
export class DebuggerSourceMapping {
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} rawLocation
|
|
* @return {?Workspace.UISourceCode.UILocation}
|
|
*/
|
|
rawLocationToUILocation(rawLocation) {
|
|
throw new Error('Not yet implemented');
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Array<!SDK.DebuggerModel.Location>}
|
|
*/
|
|
uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
|
|
throw new Error('Not yet implemented');
|
|
}
|
|
}
|