| /** |
| * @license |
| * Copyright 2019 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| function WasmOffsetConverter(wasmBytes, wasmModule) { |
| // This class parses a WASM binary file, and constructs a mapping from |
| // function indices to the start of their code in the binary file, as well |
| // as parsing the name section to allow conversion of offsets to function names. |
| // |
| // The main purpose of this module is to enable the conversion of function |
| // index and offset from start of function to an offset into the WASM binary. |
| // This is needed to look up the WASM source map as well as generate |
| // consistent program counter representations given v8's non-standard |
| // WASM stack trace format. |
| // |
| // v8 bug: https://crbug.com/v8/9172 |
| // |
| // This code is also used to check if the candidate source map offset is |
| // actually part of the same function as the offset we are looking for, |
| // as well as providing the function names for a given offset. |
| |
| // current byte offset into the WASM binary, as we parse it |
| // the first section starts at offset 8. |
| var offset = 8; |
| |
| // the index of the next function we see in the binary |
| var funcidx = 0; |
| |
| // map from function index to byte offset in WASM binary |
| this.offset_map = {}; |
| this.func_starts = []; |
| |
| // map from function index to names in WASM binary |
| this.name_map = {}; |
| |
| // number of imported functions this module has |
| this.import_functions = 0; |
| |
| // the buffer unsignedLEB128 will read from. |
| var buffer = wasmBytes; |
| |
| function unsignedLEB128() { |
| // consumes an unsigned LEB128 integer starting at `offset`. |
| // changes `offset` to immediately after the integer |
| var result = 0; |
| var shift = 0; |
| do { |
| var byte = buffer[offset++]; |
| result += (byte & 0x7F) << shift; |
| shift += 7; |
| } while (byte & 0x80); |
| return result; |
| } |
| |
| function skipLimits() { |
| var flags = unsignedLEB128(); |
| unsignedLEB128(); // initial size |
| var hasMax = (flags & 1) != 0; |
| if (hasMax) { |
| unsignedLEB128(); |
| } |
| } |
| |
| binary_parse: |
| while (offset < buffer.length) { |
| var start = offset; |
| var type = buffer[offset++]; |
| var end = unsignedLEB128() + offset; |
| switch (type) { |
| case 2: // import section |
| // we need to find all function imports and increment funcidx for each one |
| // since functions defined in the module are numbered after all imports |
| var count = unsignedLEB128(); |
| |
| while (count-- > 0) { |
| // skip module |
| offset = unsignedLEB128() + offset; |
| // skip name |
| offset = unsignedLEB128() + offset; |
| |
| switch (buffer[offset++]) { |
| case 0: // function import |
| ++funcidx; |
| unsignedLEB128(); // skip function type |
| break; |
| case 1: // table import |
| ++offset; // FIXME: should be SLEB128 |
| skipLimits(); |
| break; |
| case 2: // memory import |
| skipLimits(); |
| break; |
| case 3: // global import |
| offset += 2; // skip type id byte and mutability byte |
| break; |
| #if ASSERTIONS |
| default: throw 'bad import kind'; |
| #endif |
| } |
| } |
| this.import_functions = funcidx; |
| break; |
| case 10: // code section |
| var count = unsignedLEB128(); |
| while (count-- > 0) { |
| var size = unsignedLEB128(); |
| this.offset_map[funcidx++] = offset; |
| this.func_starts.push(offset); |
| offset += size; |
| } |
| break binary_parse; |
| } |
| offset = end; |
| } |
| |
| var sections = WebAssembly.Module.customSections(wasmModule, "name"); |
| for (var i = 0; i < sections.length; ++i) { |
| buffer = new Uint8Array(sections[i]); |
| if (buffer[0] != 1) // not a function name section |
| continue; |
| offset = 1; |
| unsignedLEB128(); // skip byte count |
| var count = unsignedLEB128(); |
| while (count-- > 0) { |
| var index = unsignedLEB128(); |
| var length = unsignedLEB128(); |
| this.name_map[index] = UTF8ArrayToString(buffer, offset, length); |
| offset += length; |
| } |
| } |
| } |
| |
| WasmOffsetConverter.prototype.convert = function (funcidx, offset) { |
| return this.offset_map[funcidx] + offset; |
| } |
| |
| WasmOffsetConverter.prototype.getIndex = function (offset) { |
| var lo = 0; |
| var hi = this.func_starts.length; |
| var mid; |
| |
| while (lo < hi) { |
| mid = Math.floor((lo + hi) / 2); |
| if (this.func_starts[mid] > offset) { |
| hi = mid; |
| } else { |
| lo = mid + 1; |
| } |
| } |
| return lo + this.import_functions - 1; |
| } |
| |
| WasmOffsetConverter.prototype.isSameFunc = function (offset1, offset2) { |
| return this.getIndex(offset1) == this.getIndex(offset2); |
| } |
| |
| WasmOffsetConverter.prototype.getName = function (offset) { |
| var index = this.getIndex(offset); |
| return this.name_map[index] || ('wasm-function[' + index + ']'); |
| } |