260 lines
5.8 KiB
JavaScript
260 lines
5.8 KiB
JavaScript
/* 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,
|
|
};
|