wasm.js (4459B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const wasmparser = require("resource://devtools/client/shared/vendor/WasmParser.js"); 8 const wasmdis = require("resource://devtools/client/shared/vendor/WasmDis.js"); 9 10 const wasmStates = new WeakMap(); 11 12 function getWasmText(subject, data) { 13 if (wasmStates.has(subject)) { 14 const wasmState = wasmStates.get(subject); 15 return { lines: wasmState.result.lines, done: wasmState.result.done }; 16 } 17 const parser = new wasmparser.BinaryReader(); 18 parser.setData(data.buffer, 0, data.length); 19 const dis = new wasmdis.WasmDisassembler(); 20 dis.addOffsets = true; 21 const done = dis.disassembleChunk(parser); 22 let result = dis.getResult(); 23 if (result.lines.length === 0) { 24 result = { lines: ["No luck with wast conversion"], offsets: [0], done }; 25 } 26 // Cache mappings of WASM offsets to lines 27 const offsets = result.offsets, 28 lines = []; 29 for (let line = 0; line < offsets.length; line++) { 30 const offset = offsets[line]; 31 lines[offset] = line; 32 } 33 wasmStates.set(subject, { offsets, lines, result }); 34 35 return { lines: result.lines, done: result.done }; 36 } 37 38 /** 39 * Creates wasm formatter function used to generate the hexadecimal number 40 * displayed in the line gutter 41 * 42 * @param {object} subject - An object which decribes the source, it is comprised of the sourceId 43 * @returns {Function} 44 */ 45 function getWasmLineNumberFormatter(subject) { 46 const codeOf0 = 48, 47 codeOfA = 65; 48 const buffer = [ 49 codeOf0, 50 codeOf0, 51 codeOf0, 52 codeOf0, 53 codeOf0, 54 codeOf0, 55 codeOf0, 56 codeOf0, 57 ]; 58 let last0 = 7; 59 return function (line) { 60 const offset = lineToWasmOffset(subject, line - 1); 61 if (offset === undefined) { 62 return ""; 63 } 64 let i = 7; 65 for (let n = offset | 0; n !== 0 && i >= 0; n >>= 4, i--) { 66 const nibble = n & 15; 67 buffer[i] = nibble < 10 ? codeOf0 + nibble : codeOfA - 10 + nibble; 68 } 69 for (let j = i; j > last0; j--) { 70 buffer[j] = codeOf0; 71 } 72 last0 = i; 73 return String.fromCharCode.apply(null, buffer); 74 }; 75 } 76 77 /** 78 * Checks if the specified source exists in the cache. 79 * This is used to determine if the source is a WASM source 80 * 81 * @param {object} subject 82 * @returns {boolean} 83 */ 84 function isWasm(subject) { 85 return wasmStates.has(subject); 86 } 87 88 /** 89 * Converts the source (decimal) line to its WASM offset 90 * 91 * @param {object} subject 92 * @param {number} line 93 * @param {boolean} findNextOffset 94 * There are scenarios (e.g are empty lines) where we might want to find the next best offset match. 95 * Every line will usually have offsets assigned except empty lines (which could be between functions 96 * or some declarations). 97 * @returns {number} 98 */ 99 function lineToWasmOffset(subject, line, findNextOffset = false) { 100 const wasmState = wasmStates.get(subject); 101 if (!wasmState) { 102 return undefined; 103 } 104 105 let offset = wasmState.offsets[line]; 106 if (findNextOffset) { 107 while (offset === undefined && line > 0) { 108 offset = wasmState.offsets[--line]; 109 } 110 } 111 return offset; 112 } 113 114 /** 115 * Converts the WASM offset to the source line 116 * 117 * @param {object} subject 118 * @param {number} offset 119 * @returns {number} 120 */ 121 function wasmOffsetToLine(subject, offset) { 122 const wasmState = wasmStates.get(subject); 123 return wasmState.lines[offset]; 124 } 125 126 // A cache of the wasm source text as an array of lines. 127 // The lines are cached with the value object of the source content 128 // as the key. 129 const wasmLines = new WeakMap(); 130 131 function renderWasmText(subject, content) { 132 if (wasmLines.has(content)) { 133 return wasmLines.get(content) || []; 134 } 135 136 // binary does not survive as Uint8Array, converting from string 137 const { binary } = content.value; 138 const data = new Uint8Array(binary.length); 139 for (let i = 0; i < data.length; i++) { 140 data[i] = binary.charCodeAt(i); 141 } 142 const { lines } = getWasmText(subject, data); 143 const MAX_LINES = 1000000; 144 if (lines.length > MAX_LINES) { 145 lines.splice(MAX_LINES, lines.length - MAX_LINES); 146 lines.push(";; .... text is truncated due to the size"); 147 } 148 149 wasmLines.set(content, lines); 150 return lines; 151 } 152 153 module.exports = { 154 getWasmText, 155 getWasmLineNumberFormatter, 156 isWasm, 157 lineToWasmOffset, 158 wasmOffsetToLine, 159 renderWasmText, 160 };