All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.dirigible.dev-tools.bindings.DebuggerWorkspaceBinding.js Maven / Gradle / Ivy

There is a newer version: 10.6.27
Show newest version
// 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 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 {CXXDWARFLanguagePlugin} from './language_plugins/CXXDWARFLanguagePlugin.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}
 */
export class DebuggerWorkspaceBinding {
  /**
   * @private
   * @param {!SDK.SDKModel.TargetManager} targetManager
   * @param {!Workspace.Workspace.WorkspaceImpl} workspace
   */
  constructor(targetManager, workspace) {
    this._workspace = workspace;

    /** @type {!Array} */
    this._sourceMappings = [];

    /** @type {!Map.} */
    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.} */
    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);
    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}
   */
  pendingLiveLocationChangesPromise() {
    return 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)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   * @return {!Promise}
   */
  createLiveLocation(rawLocation, updateDelegate, locationPool) {
    const modelData = this._debuggerModelToData.get(rawLocation.script().debuggerModel);
    const liveLocationPromise = modelData._createLiveLocation(rawLocation, updateDelegate, locationPool);
    this._recordLiveLocationChange(liveLocationPromise);
    return liveLocationPromise;
  }

  /**
   * @param {!Array} rawLocations
   * @param {function(!LiveLocation)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   * @return {!Promise}
   */
  async createStackTraceTopFrameLiveLocation(rawLocations, updateDelegate, locationPool) {
    console.assert(rawLocations.length);
    const locationPromise =
        StackTraceTopFrameLocation.createStackTraceTopFrameLocation(rawLocations, this, updateDelegate, locationPool);
    this._recordLiveLocationChange(locationPromise);
    return locationPromise;
  }

  /**
   * @param {!SDK.DebuggerModel.Location} location
   * @param {function(!LiveLocation)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   * @return {!Promise}
   */
  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;
    this._registerCallFrameLiveLocation(debuggerModel, liveLocation);
    return liveLocation;
  }

  /**
   * @param {!SDK.DebuggerModel.Location} rawLocation
   * @return {!Promise}
   */
  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>}
   */
  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}
   */
  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}
   */
  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);
    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);
    modelData._resourceMapping.resetForTest();
  }

  /**
   * @param {!SDK.DebuggerModel.DebuggerModel} debuggerModel
   * @param {!Location} location
   */
  _registerCallFrameLiveLocation(debuggerModel, location) {
    const locations = this._debuggerModelToData.get(debuggerModel).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.} */
    this.callFrameLocations = new Set();

    const workspace = debuggerWorkspaceBinding._workspace;

    if (Root.Runtime.experiments.isEnabled('wasmDWARFDebugging')) {
      this._pluginManager = new DebuggerLanguagePluginManager(debuggerModel, workspace, debuggerWorkspaceBinding);
      this._pluginManager.addPlugin(new CXXDWARFLanguagePlugin());
    }


    this._defaultMapping = new DefaultScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
    this._resourceMapping = new ResourceScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);
    this._compilerMapping = new CompilerScriptMapping(debuggerModel, workspace, debuggerWorkspaceBinding);

    /** @type {!Platform.Multimap} */
    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)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   * @return {!Promise}
   */
  async _createLiveLocation(rawLocation, updateDelegate, locationPool) {
    const script = /** @type {!SDK.Script.Script} */ (rawLocation.script());
    console.assert(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());
    }
    return Promise.all(promises);
  }

  /**
   * @param {!SDK.DebuggerModel.Location} rawLocation
   * @return {!Promise}
   */
  async _rawLocationToUILocation(rawLocation) {
    let uiLocation = null;
    if (Root.Runtime.experiments.isEnabled('wasmDWARFDebugging')) {
      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>}
   */
  async _uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
    let rawLocations = null;
    if (Root.Runtime.experiments.isEnabled('wasmDWARFDebugging')) {
      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}
   */
  _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.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
 */
class Location extends LiveLocationWithPool {
  /**
   * @param {!SDK.Script.Script} script
   * @param {!SDK.DebuggerModel.Location} rawLocation
   * @param {!DebuggerWorkspaceBinding} binding
   * @param {function(!LiveLocation)} 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}
   */
  async uiLocation() {
    const debuggerModelLocation = this._rawLocation;
    return this._binding.rawLocationToUILocation(debuggerModelLocation);
  }

  /**
   * @override
   */
  dispose() {
    super.dispose();
    this._binding._removeLiveLocation(this);
  }

  /**
   * @override
   * @return {!Promise}
   */
  async isBlackboxed() {
    const uiLocation = await this.uiLocation();
    return uiLocation ? BlackboxManager.instance().isBlackboxedUISourceCode(uiLocation.uiSourceCode) : false;
  }
}

class StackTraceTopFrameLocation extends LiveLocationWithPool {
  /**
   * @param {function(!LiveLocation)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   */
  constructor(updateDelegate, locationPool) {
    super(updateDelegate, locationPool);
    this._updateScheduled = true;
    /** @type {?LiveLocation} */
    this._current = null;
    /** @type {?Array} */
    this._locations = null;
  }

  /**
   * @param {!Array} rawLocations
   * @param {!DebuggerWorkspaceBinding} binding
   * @param {function(!LiveLocation)} updateDelegate
   * @param {!LiveLocationPool} locationPool
   * @return {!Promise}
   */
  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 = await Promise.all(locationsPromises);
    await location._updateLocation();
    return location;
  }

  /**
   * @override
   * @return {!Promise}
   */
  async uiLocation() {
    return this._current ? this._current.uiLocation() : null;
  }

  /**
   * @override
   * @return {!Promise}
   */
  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;
  }

  _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) {
  }

  /**
   * @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
   * @param {number} lineNumber
   * @param {number} columnNumber
   * @return {!Array}
   */
  uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber) {
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy