class State {
constructor(wat, clif, asm) {
this.wat = wat;
this.clif = clif;
this.asm = asm;
}
}
const state = (window.STATE = new State(window.WAT, window.CLIF, window.ASM));
const hues = [
80, 160, 240, 320, 40, 120, 200, 280, 20, 100, 180, 260, 340, 60, 140, 220,
300,
];
const nextHue = (function () {
let i = 0;
return () => {
return hues[++i % hues.length];
};
})();
const offsetToHue = new Map();
const hueForOffset = offset => {
if (offsetToHue.has(offset)) {
return offsetToHue.get(offset);
} else {
let hue = nextHue();
offsetToHue.set(offset, hue);
return hue;
}
};
const existingHueForOffset = offset => {
return offsetToHue.get(offset);
};
const watByOffset = new Map();
const asmByOffset = new Map();
const clifByOffset = new Map();
const anyByOffset = new Map();
const addWatElem = (offset, elem) => {
if (!watByOffset.has(offset)) {
watByOffset.set(offset, []);
}
watByOffset.get(offset).push(elem);
if (!anyByOffset.has(offset)) {
anyByOffset.set(offset, []);
}
anyByOffset.get(offset).push(elem);
};
const addAsmElem = (offset, elem) => {
if (!asmByOffset.has(offset)) {
asmByOffset.set(offset, []);
}
asmByOffset.get(offset).push(elem);
if (!anyByOffset.has(offset)) {
anyByOffset.set(offset, []);
}
anyByOffset.get(offset).push(elem);
};
const addClifElem = (offset, elem) => {
if (!clifByOffset.has(offset)) {
clifByOffset.set(offset, []);
}
clifByOffset.get(offset).push(elem);
if (!anyByOffset.has(offset)) {
anyByOffset.set(offset, []);
}
anyByOffset.get(offset).push(elem);
};
const watElem = document.getElementById("wat");
watElem.addEventListener(
"click",
event => {
if (event.target.dataset.wasmOffset == null) {
return;
}
const offset = parseInt(event.target.dataset.wasmOffset);
if (!asmByOffset.get(offset)) {
return;
}
const firstClifElem = clifByOffset.get(offset)[0];
firstClifElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
const firstAsmElem = asmByOffset.get(offset)[0];
firstAsmElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
},
{ passive: true },
);
const asmElem = document.getElementById("asm");
asmElem.addEventListener(
"click",
event => {
if (event.target.dataset.wasmOffset == null) {
return;
}
const offset = parseInt(event.target.dataset.wasmOffset);
if (!watByOffset.get(offset)) {
return;
}
const firstWatElem = watByOffset.get(offset)[0];
firstWatElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
const firstClifElem = clifByOffset.get(offset)[0];
firstClifElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
},
{ passive: true },
);
const clifElem = document.getElementById("clif");
if (clifElem) {
clifElem.addEventListener(
"click",
event => {
if (event.target.dataset.wasmOffset == null) {
return;
}
const offset = parseInt(event.target.dataset.wasmOffset);
if (!watByOffset.get(offset)) {
return;
}
const firstWatElem = watByOffset.get(offset)[0];
firstWatElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
const firstAsmElem = asmByOffset.get(offset)[0];
firstAsmElem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "nearest",
});
},
{ passive: true },
);
}
const onMouseEnter = event => {
if (event.target.dataset.wasmOffset == null) {
return;
}
const offset = parseInt(event.target.dataset.wasmOffset);
const hue = hueForOffset(offset);
for (const elem of anyByOffset.get(offset)) {
elem.style.backgroundColor = `hsl(${hue} 75% 80%)`;
}
};
const onMouseLeave = event => {
if (event.target.dataset.wasmOffset == null) {
return;
}
const offset = parseInt(event.target.dataset.wasmOffset);
const hue = hueForOffset(offset);
for (const elem of anyByOffset.get(offset)) {
elem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
}
};
const repeat = (s, n) => {
return s.repeat(n >= 0 ? n : 0);
};
const renderAddress = addr => {
let hex = addr.toString(16);
return repeat("0", 8 - hex.length) + hex;
};
const renderBytes = bytes => {
let s = "";
for (let i = 0; i < bytes.length; i++) {
if (i != 0) {
s += " ";
}
const hexByte = bytes[i].toString(16);
s += hexByte.length == 2 ? hexByte : "0" + hexByte;
}
return s + repeat(" ", 30 - s.length);
};
const renderInst = (mnemonic, operands) => {
if (operands.length == 0) {
return mnemonic;
} else {
return mnemonic + " " + operands;
}
};
if (clifElem) {
for (const func of state.clif.functions) {
const funcElem = document.createElement("div");
const funcHeader = document.createElement("h3");
let func_name =
func.name === null ? `function[${func.func_index}]` : func.name;
let demangled_name =
func.demangled_name !== null ? func.demangled_name : func_name;
funcHeader.textContent = `Intermediate Representation of function <${demangled_name}>:`;
funcHeader.title = `Function ${func.func_index}: ${func_name}`;
funcElem.appendChild(funcHeader);
const bodyElem = document.createElement("pre");
for (const inst of func.instructions) {
const instElem = document.createElement("span");
instElem.textContent = `${inst.clif}\n`;
if (inst.wasm_offset != null) {
instElem.setAttribute("data-wasm-offset", inst.wasm_offset);
const hue = hueForOffset(inst.wasm_offset);
instElem.style.backgroundColor = `hsl(${hue} 50% 90%)`;
instElem.addEventListener("mouseenter", onMouseEnter);
instElem.addEventListener("mouseleave", onMouseLeave);
addClifElem(inst.wasm_offset, instElem);
}
bodyElem.appendChild(instElem);
}
funcElem.appendChild(bodyElem);
clifElem.appendChild(funcElem);
}
}
for (const func of state.asm.functions) {
const funcElem = document.createElement("div");
const funcHeader = document.createElement("h3");
let func_name =
func.name === null ? `function[${func.func_index}]` : func.name;
let demangled_name =
func.demangled_name !== null ? func.demangled_name : func_name;
funcHeader.textContent = `Disassembly of function <${demangled_name}>:`;
funcHeader.title = `Function ${func.func_index}: ${func_name}`;
funcElem.appendChild(funcHeader);
const bodyElem = document.createElement("pre");
for (const inst of func.instructions) {
const instElem = document.createElement("span");
instElem.textContent = `${renderAddress(inst.address)} ${renderBytes(inst.bytes)} ${renderInst(inst.mnemonic, inst.operands)}\n`;
if (inst.wasm_offset != null) {
instElem.setAttribute("data-wasm-offset", inst.wasm_offset);
const hue = hueForOffset(inst.wasm_offset);
instElem.style.backgroundColor = `hsl(${hue} 50% 90%)`;
instElem.addEventListener("mouseenter", onMouseEnter);
instElem.addEventListener("mouseleave", onMouseLeave);
addAsmElem(inst.wasm_offset, instElem);
}
bodyElem.appendChild(instElem);
}
funcElem.appendChild(bodyElem);
asmElem.appendChild(funcElem);
}
for (const chunk of state.wat.chunks) {
const chunkElem = document.createElement("span");
if (chunk.wasm_offset != null) {
chunkElem.dataset.wasmOffset = chunk.wasm_offset;
const hue = existingHueForOffset(chunk.wasm_offset);
if (hue) {
chunkElem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
chunkElem.addEventListener("mouseenter", onMouseEnter);
chunkElem.addEventListener("mouseleave", onMouseLeave);
addWatElem(chunk.wasm_offset, chunkElem);
}
}
chunkElem.textContent = chunk.wat;
watElem.appendChild(chunkElem);
}