blob: 1bfe872bfbfbb0466bc97c5cccd562e8a9ba0136 [file] [log] [blame]
/**
* @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 + ']');
}