diff options
Diffstat (limited to 'devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js')
-rw-r--r-- | devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js new file mode 100644 index 0000000000..82faa51dbe --- /dev/null +++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js @@ -0,0 +1,215 @@ +/* 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 <http://mozilla.org/MPL/2.0/>. */ + +/* eslint camelcase: 0*/ + +"use strict"; + +const { + decodeExpr, +} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions"); + +const xScopes = new Map(); + +function indexLinkingNames(items) { + const result = new Map(); + let queue = [...items]; + while (queue.length) { + 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, key) { + if (typeof key === "object" && key != null) { + return index.get(key.uid); + } + if (typeof key === "string") { + return index.get(key); + } + return null; +} + +async function getXScopes(sourceId, getSourceMap) { + 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, pc) { + 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; +} + +function decodeExprAt(expr, pc) { + if (typeof expr === "string") { + return decodeExpr(expr); + } + const foundAt = expr.find(i => i.range[0] <= pc && pc < i.range[1]); + return foundAt ? decodeExpr(foundAt.expr) : null; +} + +function getVariables(scope, pc) { + const vars = scope.children + ? scope.children.reduce((result, item) => { + switch (item.tag) { + case "variable": + case "formal_parameter": + result.push({ + name: item.name || "", + expr: item.location ? decodeExprAt(item.location, pc) : null, + }); + break; + case "lexical_block": + // FIXME build scope blocks (instead of combining) + const tmp = getVariables(item, pc); + result = [...tmp.vars, ...result]; + break; + } + return result; + }, []) + : []; + const frameBase = scope.frame_base ? decodeExpr(scope.frame_base) : null; + return { + vars, + frameBase, + }; +} + +function filterScopes(items, pc, lastItem, index) { + 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 = { + id: item.linkage_name, + name: item.name, + variables: getVariables(item, pc), + }; + 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 = { + id: item.abstract_origin, + name: linkedItem ? linkedItem.name : void 0, + variables: getVariables(item, pc), + }; + 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; + sourceMapContext; + + constructor(xScopeData, sourceMapContext) { + this.xScope = xScopeData; + this.sourceMapContext = sourceMapContext; + } + + search(generatedLocation) { + 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 || "", + variables: i.variables, + }; + } + const sourceId = this.sourceMapContext.generatedToOriginalId( + generatedLocation.sourceId, + sources[i.file || 0] + ); + return { + displayName: i.name || "", + variables: i.variables, + location: { + line: i.line || 0, + sourceId, + }, + }; + }); + } +} + +async function getWasmXScopes(sourceId, sourceMapContext) { + const { getSourceMap } = sourceMapContext; + const xScopeData = await getXScopes(sourceId, getSourceMap); + if (!xScopeData) { + return null; + } + return new XScope(xScopeData, sourceMapContext); +} + +function clearWasmXScopes() { + xScopes.clear(); +} + +module.exports = { + getWasmXScopes, + clearWasmXScopes, +}; |