/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ import { getSelectedLocation } from "./selected-location"; import { getSource } from "../selectors/index"; /** * Note that arguments can be created via `createLocation`. * But they can also be created via `createPendingLocation` in reducer/pending-breakpoints.js. * Both will have similar line and column attributes. */ export function comparePosition(a, b) { return a && b && a.line == b.line && a.column == b.column; } export function createLocation({ source, sourceActor = null, // Line 0 represents no specific line chosen for action line = 0, column, }) { return { source, sourceActor, sourceActorId: sourceActor?.id, // # Quick overview of 1-based versus 0-based lines and columns # // // Everything assumes a 1-based line, but columns can be 0 or 1 based. // Note that while lines are 1-based some RDP packet may refer to line 0 which should be considered as "no precise location". // // Columns are 0-based in: // - overall all debugger frontend // - anything around source maps (SourceMapLoader, SourceMapURLService, SourceMap library) // - most RDP packets, especially around the thread actor: // - breakpoints // - breakpoint positions // - pause location // - paused frames // // Columns are 1-based in: // - the UI displayed to the user (console messages, frames, stacktraces,...) // - asserted locations in tests (to match displayed numbers) // - Spidermonkey: // This data is mostly coming from and driven by // JSScript::lineno and JSScript::column // https://searchfox.org/mozilla-central/rev/4c065f1df299065c305fb48b36cdae571a43d97c/js/src/vm/JSScript.h#1567-1570 // - some RDP packets outside of the thread actor: // - CONSOLE_MESSAGE, CSS_MESSAGE, PAGE_ERROR resources for lineNumber, columnNumber and stacktrace attributes // - Error objects's Object Actor's grip's "preview" attribute will expose its stacktraces with 1-based columns // - SmartTrace is dealing with these RDP packets and consumes 1-based columns, // but has to map to 0-based columns as it depends on debugger frontend Frames components. // // The RDP server, especially in the thread actor ecosystem has to map from spidermonkey 1-based to historical 0-based columns. line, column, }; } /** * Convert location objects created via `createLocation` into * the format used by the Source Map Loader/Worker. * It only needs sourceId, line and column attributes. */ export function debuggerToSourceMapLocation(location) { return { sourceId: location.source.id, // In case of errors loading the source, we might not have a precise location. // Defaults to first line and column. line: location.line || 1, column: location.column || 0, }; } /** * Pending location only need these three attributes, * and especially doesn't need the large source and sourceActor objects of the regular location objects. * * @param {object} location */ export function createPendingSelectedLocation(location) { return { url: location.source.url, line: location.line, column: location.column, }; } export function sortSelectedLocations(locations, selectedSource) { return Array.from(locations).sort((locationA, locationB) => { const aSelected = getSelectedLocation(locationA, selectedSource); const bSelected = getSelectedLocation(locationB, selectedSource); // Order the locations by line number… if (aSelected.line < bSelected.line) { return -1; } if (aSelected.line > bSelected.line) { return 1; } // … and if we have the same line, we want to return location with undefined columns // first, and then order them by column if (aSelected.column == bSelected.column) { return 0; } if (aSelected.column === undefined) { return -1; } if (bSelected.column === undefined) { return 1; } return aSelected.column < bSelected.column ? -1 : 1; }); } /** * Source map Loader/Worker and debugger frontend don't use the same objects for locations. * Worker uses 'sourceId' attributes whereas the frontend has 'source' attribute. */ export function sourceMapToDebuggerLocation(state, location) { // From MapScopes modules, we might re-process the exact same location objects // for which we would already have computed the source object, // and which would lack sourceId attribute. if (location.source) { return location; } // SourceMapLoader doesn't known about debugger's source objects // so that we have to fetch it from here const source = getSource(state, location.sourceId); if (!source) { throw new Error(`Could not find source-map source ${location.sourceId}`); } return createLocation({ ...location, source, }); }