/* 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 . */
// @flow
/* eslint camelcase: 0*/
import type { OriginalFrame, SourceLocation, SourceId } from "debugger-html";
const { getSourceMap } = require("./sourceMapRequests");
const { generatedToOriginalId } = require("./index");
const xScopes = new Map();
type XScopeItem = any;
type XScopeItemsIndex = Map;
function indexLinkingNames(items: XScopeItem[]): XScopeItemsIndex {
const result = new Map();
let queue = [...items];
while (queue.length > 0) {
const item = queue.shift();
if ("uid" in item) {
result.set(item.uid, item);
} else if ("linkage_name" in item) {
// TODO the linkage_name string value is used for compatibility
// with old format. Remove in favour of the uid referencing.
result.set(item.linkage_name, item);
}
if ("children" in item) {
queue = [...queue, ...item.children];
}
}
return result;
}
function getIndexedItem(
index: XScopeItemsIndex,
key: string | { uid: number }
): XScopeItem {
if (typeof key === "object" && key != null) {
return index.get(key.uid);
}
if (typeof key === "string") {
return index.get(key);
}
return null;
}
type XScopeData = {
code_section_offset: number,
debug_info: Array,
idIndex: XScopeItemsIndex,
sources: Array
};
async function getXScopes(sourceId: SourceId): Promise {
if (xScopes.has(sourceId)) {
return xScopes.get(sourceId);
}
const map = await getSourceMap(sourceId);
if (!map || !map.xScopes) {
xScopes.set(sourceId, null);
return null;
}
const { code_section_offset, debug_info } = map.xScopes;
const xScope = {
code_section_offset,
debug_info,
idIndex: indexLinkingNames(debug_info),
sources: map.sources
};
xScopes.set(sourceId, xScope);
return xScope;
}
function isInRange(item: XScopeItem, pc: number): boolean {
if ("ranges" in item) {
return item.ranges.some(r => r[0] <= pc && pc < r[1]);
}
if ("high_pc" in item) {
return item.low_pc <= pc && pc < item.high_pc;
}
return false;
}
type FoundScope = {
id: string,
name?: string,
file?: number,
line?: number
};
function filterScopes(
items: XScopeItem[],
pc: number,
lastItem: ?FoundScope,
index: XScopeItemsIndex
): FoundScope[] {
if (!items) {
return [];
}
return items.reduce((result, item) => {
switch (item.tag) {
case "compile_unit":
if (isInRange(item, pc)) {
result = [
...result,
...filterScopes(item.children, pc, lastItem, index)
];
}
break;
case "namespace":
case "structure_type":
case "union_type":
result = [
...result,
...filterScopes(item.children, pc, lastItem, index)
];
break;
case "subprogram":
if (isInRange(item, pc)) {
const s: FoundScope = {
id: item.linkage_name,
name: item.name
};
result = [...result, s, ...filterScopes(item.children, pc, s, index)];
}
break;
case "inlined_subroutine":
if (isInRange(item, pc)) {
const linkedItem = getIndexedItem(index, item.abstract_origin);
const s: FoundScope = {
id: item.abstract_origin,
name: linkedItem ? linkedItem.name : void 0
};
if (lastItem) {
lastItem.file = item.call_file;
lastItem.line = item.call_line;
}
result = [...result, s, ...filterScopes(item.children, pc, s, index)];
}
break;
}
return result;
}, []);
}
class XScope {
xScope: XScopeData;
constructor(xScopeData: XScopeData) {
this.xScope = xScopeData;
}
search(generatedLocation: SourceLocation): Array {
const { code_section_offset, debug_info, sources, idIndex } = this.xScope;
const pc = generatedLocation.line - (code_section_offset || 0);
const scopes = filterScopes(debug_info, pc, null, idIndex);
scopes.reverse();
return scopes.map(i => {
if (!("file" in i)) {
return {
displayName: i.name || ""
};
}
const sourceId = generatedToOriginalId(
generatedLocation.sourceId,
sources[i.file || 0]
);
return {
displayName: i.name || "",
location: {
line: i.line || 0,
sourceId
}
};
});
}
}
async function getWasmXScopes(sourceId: SourceId): Promise {
const xScopeData = await getXScopes(sourceId);
if (!xScopeData) {
return null;
}
return new XScope(xScopeData);
}
function clearWasmXScopes() {
xScopes.clear();
}
module.exports = {
getWasmXScopes,
clearWasmXScopes
};