summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/source-map-loader/wasm-dwarf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/client/shared/source-map-loader/wasm-dwarf
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/shared/source-map-loader/wasm-dwarf')
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js66
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasmbin0 -> 246995 bytes
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/moz.build12
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js17
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js260
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js215
6 files changed, 570 insertions, 0 deletions
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js b/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js
new file mode 100644
index 0000000000..9faae5c901
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js
@@ -0,0 +1,66 @@
+/* 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 {
+ getDwarfToWasmData,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js");
+
+let cachedWasmModule;
+let utf8Decoder;
+
+function convertDwarf(wasm, instance) {
+ const { memory, alloc_mem, free_mem, convert_dwarf } = instance.exports;
+ const wasmPtr = alloc_mem(wasm.byteLength);
+ new Uint8Array(memory.buffer, wasmPtr, wasm.byteLength).set(
+ new Uint8Array(wasm)
+ );
+ const resultPtr = alloc_mem(12);
+ const enableXScopes = true;
+ const success = convert_dwarf(
+ wasmPtr,
+ wasm.byteLength,
+ resultPtr,
+ resultPtr + 4,
+ enableXScopes
+ );
+ free_mem(wasmPtr);
+ const resultView = new DataView(memory.buffer, resultPtr, 12);
+ const outputPtr = resultView.getUint32(0, true),
+ outputLen = resultView.getUint32(4, true);
+ free_mem(resultPtr);
+ if (!success) {
+ throw new Error("Unable to convert from DWARF sections");
+ }
+ if (!utf8Decoder) {
+ utf8Decoder = new TextDecoder("utf-8");
+ }
+ const output = utf8Decoder.decode(
+ new Uint8Array(memory.buffer, outputPtr, outputLen)
+ );
+ free_mem(outputPtr);
+ return output;
+}
+
+async function convertToJSON(buffer) {
+ // Note: We don't 'await' here because it could mean that multiple
+ // calls to 'convertToJSON' could cause multiple fetches to be started.
+ cachedWasmModule = cachedWasmModule || loadConverterModule();
+
+ return convertDwarf(buffer, await cachedWasmModule);
+}
+
+async function loadConverterModule() {
+ const wasm = await getDwarfToWasmData();
+ const imports = {};
+ const { instance } = await WebAssembly.instantiate(wasm, imports);
+ return instance;
+}
+
+module.exports = {
+ convertToJSON,
+};
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm b/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm
new file mode 100644
index 0000000000..c58b189035
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm
Binary files differ
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build b/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build
new file mode 100644
index 0000000000..8bc40676fa
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "convertToJSON.js",
+ "wasmAsset.js",
+ "wasmDwarfExpressions.js",
+ "wasmXScopes.js",
+)
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js
new file mode 100644
index 0000000000..b4de822ffd
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js
@@ -0,0 +1,17 @@
+/* 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/>. */
+
+"use strict";
+
+async function getDwarfToWasmData(name) {
+ const response = await fetch(
+ "resource://devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm"
+ );
+
+ return response.arrayBuffer();
+}
+
+module.exports = {
+ getDwarfToWasmData,
+};
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js
new file mode 100644
index 0000000000..c2f124e99f
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js
@@ -0,0 +1,260 @@
+/* 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*/
+/* eslint-disable no-inline-comments */
+
+"use strict";
+
+class Value {
+ val;
+
+ constructor(val) {
+ this.val = val;
+ }
+ toString() {
+ return `${this.val}`;
+ }
+}
+
+const Int32Formatter = {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).getInt32(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `${value.val}`;
+ },
+};
+
+const Uint32Formatter = {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).getUint32(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `(${value.val} >>> 0)`;
+ },
+};
+
+function createPieceFormatter(bytes) {
+ let getter;
+ switch (bytes) {
+ case 0:
+ case 1:
+ getter = "getUint8";
+ break;
+ case 2:
+ getter = "getUint16";
+ break;
+ case 3:
+ case 4:
+ default:
+ // FIXME 64-bit
+ getter = "getUint32";
+ break;
+ }
+ const mask = (1 << (8 * bytes)) - 1;
+ return {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).${getter}(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `((${value.val} & ${mask}) >>> 0)`;
+ },
+ };
+}
+
+// eslint-disable-next-line complexity
+function toJS(buf, typeFormatter, frame_base = "fp()") {
+ const readU8 = function () {
+ return buf[i++];
+ };
+ const readS8 = function () {
+ return (readU8() << 24) >> 24;
+ };
+ const readU16 = function () {
+ const w = buf[i] | (buf[i + 1] << 8);
+ i += 2;
+ return w;
+ };
+ const readS16 = function () {
+ return (readU16() << 16) >> 16;
+ };
+ const readS32 = function () {
+ const w =
+ buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);
+ i += 4;
+ return w;
+ };
+ const readU32 = function () {
+ return readS32() >>> 0;
+ };
+ const readU = function () {
+ let n = 0,
+ shift = 0,
+ b;
+ while ((b = readU8()) & 0x80) {
+ n |= (b & 0x7f) << shift;
+ shift += 7;
+ }
+ return n | (b << shift);
+ };
+ const readS = function () {
+ let n = 0,
+ shift = 0,
+ b;
+ while ((b = readU8()) & 0x80) {
+ n |= (b & 0x7f) << shift;
+ shift += 7;
+ }
+ n |= b << shift;
+ shift += 7;
+ return shift > 32 ? (n << (32 - shift)) >> (32 - shift) : n;
+ };
+ const popValue = function (formatter) {
+ const loc = stack.pop();
+ if (loc instanceof Value) {
+ return formatter.fromValue(loc);
+ }
+ return formatter.fromAddr(loc);
+ };
+ let i = 0,
+ a,
+ b;
+ const stack = [frame_base];
+ while (i < buf.length) {
+ const code = buf[i++];
+ switch (code) {
+ case 0x03 /* DW_OP_addr */:
+ stack.push(Uint32Formatter.fromAddr(readU32()));
+ break;
+ case 0x08 /* DW_OP_const1u 0x08 1 1-byte constant */:
+ stack.push(readU8());
+ break;
+ case 0x09 /* DW_OP_const1s 0x09 1 1-byte constant */:
+ stack.push(readS8());
+ break;
+ case 0x0a /* DW_OP_const2u 0x0a 1 2-byte constant */:
+ stack.push(readU16());
+ break;
+ case 0x0b /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS16());
+ break;
+ case 0x0c /* DW_OP_const2u 0x0a 1 2-byte constant */:
+ stack.push(readU32());
+ break;
+ case 0x0d /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS32());
+ break;
+ case 0x10 /* DW_OP_constu 0x10 1 ULEB128 constant */:
+ stack.push(readU());
+ break;
+ case 0x11 /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS());
+ break;
+
+ case 0x1c /* DW_OP_minus */:
+ b = stack.pop();
+ a = stack.pop();
+ stack.push(`${a} - ${b}`);
+ break;
+
+ case 0x22 /* DW_OP_plus */:
+ b = stack.pop();
+ a = stack.pop();
+ stack.push(`${a} + ${b}`);
+ break;
+
+ case 0x23 /* DW_OP_plus_uconst */:
+ b = readU();
+ a = stack.pop();
+ stack.push(`${a} + ${b}`);
+ break;
+
+ case 0x30 /* DW_OP_lit0 */:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ stack.push(`${code - 0x30}`);
+ break;
+
+ case 0x93 /* DW_OP_piece */: {
+ a = readU();
+ const formatter = createPieceFormatter(a);
+ stack.push(popValue(formatter));
+ break;
+ }
+
+ case 0x9f /* DW_OP_stack_value */:
+ stack.push(new Value(stack.pop()));
+ break;
+
+ case 0xf6 /* WASM ext (old, FIXME phase out) */:
+ case 0xed /* WASM ext */:
+ b = readU();
+ a = readS();
+ switch (b) {
+ case 0:
+ stack.push(`var${a}`);
+ break;
+ case 1:
+ stack.push(`global${a}`);
+ break;
+ default:
+ stack.push(`ti${b}(${a})`);
+ break;
+ }
+ break;
+
+ default:
+ // Unknown encoding, baling out
+ return null;
+ }
+ }
+ // FIXME use real DWARF type information
+ return popValue(typeFormatter);
+}
+
+function decodeExpr(expr) {
+ if (expr.includes("//")) {
+ expr = expr.slice(0, expr.indexOf("//")).trim();
+ }
+ const code = new Uint8Array(expr.length >> 1);
+ for (let i = 0; i < code.length; i++) {
+ code[i] = parseInt(expr.substr(i << 1, 2), 16);
+ }
+ const typeFormatter = Int32Formatter;
+ return toJS(code, typeFormatter) || `dwarf("${expr}")`;
+}
+
+module.exports = {
+ decodeExpr,
+};
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,
+};