Skip to content

Commit 650823e

Browse files
ziadziad
authored andcommitted
* Fix stack frame detection, remove all __stack_pointer references from decompiled output
* on bytes tab, highlight bytes on wat hover instead of click
1 parent 55cc5b8 commit 650823e

5 files changed

Lines changed: 325 additions & 43 deletions

File tree

playground/explorer.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4794,6 +4794,18 @@ export default class Explorer {
47944794
let selectedInstrIdx = -1;
47954795
const instrRows: HTMLElement[] = [];
47964796

4797+
const clearHighlight = (): void => {
4798+
if (selectedInstrIdx >= 0 && selectedInstrIdx < instrRows.length) {
4799+
instrRows[selectedInstrIdx].classList.remove('hex-instr-active');
4800+
}
4801+
for (const spans of byteSpans.values()) {
4802+
for (const span of spans) {
4803+
span.classList.remove('hex-byte-active');
4804+
}
4805+
}
4806+
selectedInstrIdx = -1;
4807+
};
4808+
47974809
const highlightInstruction = (targetIdx: number): void => {
47984810
if (selectedInstrIdx >= 0 && selectedInstrIdx < instrRows.length) {
47994811
instrRows[selectedInstrIdx].classList.remove('hex-instr-active');
@@ -4839,27 +4851,30 @@ export default class Explorer {
48394851
row.appendChild(immediates);
48404852
}
48414853

4842-
row.addEventListener('click', () => highlightInstruction(instrIdx));
4854+
row.addEventListener('mouseenter', () => highlightInstruction(instrIdx));
48434855
instrList.appendChild(row);
48444856
instrRows.push(row);
48454857
}
48464858

4847-
// Click hex bytes to highlight instruction
4848-
hexView.addEventListener('click', (event) => {
4859+
instrList.addEventListener('mouseleave', () => clearHighlight());
4860+
4861+
// Hover hex bytes to highlight instruction
4862+
hexView.addEventListener('mouseover', (event) => {
48494863
const target = event.target as HTMLElement;
48504864
if (target.dataset.offset !== undefined) {
4851-
const clickedOffset = parseInt(target.dataset.offset, 10);
4865+
const hoveredOffset = parseInt(target.dataset.offset, 10);
48524866
for (let instrIdx = 0; instrIdx < instructions.length; instrIdx++) {
48534867
const instruction = instructions[instrIdx];
4854-
if (clickedOffset >= instruction.offset && clickedOffset < instruction.offset + instruction.length) {
4868+
if (hoveredOffset >= instruction.offset && hoveredOffset < instruction.offset + instruction.length) {
48554869
highlightInstruction(instrIdx);
4856-
instrRows[instrIdx].scrollIntoView({ block: 'nearest' });
48574870
break;
48584871
}
48594872
}
48604873
}
48614874
});
48624875

4876+
hexView.addEventListener('mouseleave', () => clearHighlight());
4877+
48634878
splitContainer.appendChild(instrList);
48644879
splitContainer.appendChild(hexView);
48654880
parent.appendChild(splitContainer);

playground/playground.bundle.js

Lines changed: 199 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25406,6 +25406,11 @@ log(mod.toString());`
2540625406
indent++;
2540725407
const declaredVars = /* @__PURE__ */ new Set();
2540825408
collectAssignTargets(node, declaredVars);
25409+
const referencedVars = /* @__PURE__ */ new Set();
25410+
collectReferencedVars(node, referencedVars);
25411+
for (const refVar of referencedVars) {
25412+
declaredVars.add(refVar);
25413+
}
2540925414
const varTypes = /* @__PURE__ */ new Map();
2541025415
collectAssignTypes(node, varTypes);
2541125416
for (const varName of declaredVars) {
@@ -25825,6 +25830,127 @@ log(mod.toString());`
2582525830
break;
2582625831
}
2582725832
}
25833+
function collectReferencedVars(node, refs) {
25834+
function scanExpr(expr) {
25835+
if (expr.kind === "var") {
25836+
refs.add(expr.name);
25837+
}
25838+
if (expr.kind === "binary") {
25839+
scanExpr(expr.left);
25840+
scanExpr(expr.right);
25841+
}
25842+
if (expr.kind === "unary") {
25843+
scanExpr(expr.operand);
25844+
}
25845+
if (expr.kind === "compare") {
25846+
scanExpr(expr.left);
25847+
scanExpr(expr.right);
25848+
}
25849+
if (expr.kind === "load") {
25850+
scanExpr(expr.address);
25851+
}
25852+
if (expr.kind === "call") {
25853+
for (const arg of expr.args) {
25854+
scanExpr(arg);
25855+
}
25856+
}
25857+
if (expr.kind === "call_indirect") {
25858+
scanExpr(expr.tableIndex);
25859+
for (const arg of expr.args) {
25860+
scanExpr(arg);
25861+
}
25862+
}
25863+
if (expr.kind === "select") {
25864+
scanExpr(expr.condition);
25865+
scanExpr(expr.trueVal);
25866+
scanExpr(expr.falseVal);
25867+
}
25868+
if (expr.kind === "convert") {
25869+
scanExpr(expr.operand);
25870+
}
25871+
if (expr.kind === "field_access") {
25872+
scanExpr(expr.base);
25873+
}
25874+
}
25875+
function scanStmt(stmt) {
25876+
if (stmt.kind === "assign") {
25877+
scanExpr(stmt.value);
25878+
}
25879+
if (stmt.kind === "store") {
25880+
scanExpr(stmt.address);
25881+
scanExpr(stmt.value);
25882+
}
25883+
if (stmt.kind === "call") {
25884+
for (const arg of stmt.args) {
25885+
scanExpr(arg);
25886+
}
25887+
}
25888+
if (stmt.kind === "call_indirect") {
25889+
scanExpr(stmt.tableIndex);
25890+
for (const arg of stmt.args) {
25891+
scanExpr(arg);
25892+
}
25893+
}
25894+
if (stmt.kind === "global_set") {
25895+
scanExpr(stmt.value);
25896+
}
25897+
if (stmt.kind === "return" && stmt.value) {
25898+
scanExpr(stmt.value);
25899+
}
25900+
}
25901+
switch (node.kind) {
25902+
case "sequence":
25903+
for (const child of node.children) {
25904+
collectReferencedVars(child, refs);
25905+
}
25906+
break;
25907+
case "block":
25908+
for (const stmt of node.body) {
25909+
scanStmt(stmt);
25910+
}
25911+
break;
25912+
case "if":
25913+
if (node.condition) {
25914+
scanExpr(node.condition);
25915+
}
25916+
collectReferencedVars(node.thenBody, refs);
25917+
if (node.elseBody) {
25918+
collectReferencedVars(node.elseBody, refs);
25919+
}
25920+
break;
25921+
case "while":
25922+
if (node.condition) {
25923+
scanExpr(node.condition);
25924+
}
25925+
collectReferencedVars(node.body, refs);
25926+
break;
25927+
case "do_while":
25928+
collectReferencedVars(node.body, refs);
25929+
scanExpr(node.condition);
25930+
break;
25931+
case "for":
25932+
scanStmt(node.init);
25933+
scanExpr(node.condition);
25934+
scanStmt(node.increment);
25935+
collectReferencedVars(node.body, refs);
25936+
break;
25937+
case "labeled_block":
25938+
collectReferencedVars(node.body, refs);
25939+
break;
25940+
case "switch":
25941+
scanExpr(node.selector);
25942+
for (const c of node.cases) {
25943+
collectReferencedVars(c.body, refs);
25944+
}
25945+
collectReferencedVars(node.defaultBody, refs);
25946+
break;
25947+
case "return":
25948+
if (node.value) {
25949+
scanExpr(node.value);
25950+
}
25951+
break;
25952+
}
25953+
}
2582825954
function alwaysTerminates(node) {
2582925955
switch (node.kind) {
2583025956
case "return":
@@ -26136,13 +26262,22 @@ log(mod.toString());`
2613626262
}
2613726263
}
2613826264
if (statement.value.kind === "binary" && statement.value.op === "-") {
26139-
if (statement.value.left.kind === "global" && isStackPointerGlobal(statement.value.left.name) && statement.value.right.kind === "const") {
26140-
const frameSize = Number(statement.value.right.value);
26141-
return {
26142-
stackPointerName: statement.value.left.name,
26143-
frameVarName: statement.target,
26144-
frameSize
26145-
};
26265+
if (statement.value.left.kind === "global" && isStackPointerGlobal(statement.value.left.name)) {
26266+
const rightSide = statement.value.right;
26267+
if (rightSide.kind === "const") {
26268+
return {
26269+
stackPointerName: statement.value.left.name,
26270+
frameVarName: statement.target,
26271+
frameSize: Number(rightSide.value)
26272+
};
26273+
}
26274+
if (rightSide.kind === "string_literal") {
26275+
return {
26276+
stackPointerName: statement.value.left.name,
26277+
frameVarName: statement.target,
26278+
frameSize: rightSide.address
26279+
};
26280+
}
2614626281
}
2614726282
}
2614826283
}
@@ -26154,8 +26289,11 @@ log(mod.toString());`
2615426289
return false;
2615526290
}
2615626291
if (globalSet.value.kind === "binary" && globalSet.value.op === "+") {
26157-
if (globalSet.value.left.kind === "var" && globalSet.value.left.name === frameInfo.frameVarName && globalSet.value.right.kind === "const" && Number(globalSet.value.right.value) === frameInfo.frameSize) {
26158-
return true;
26292+
if (globalSet.value.left.kind === "var" && globalSet.value.left.name === frameInfo.frameVarName) {
26293+
const rightSide = globalSet.value.right;
26294+
if (rightSide.kind === "const" && Number(rightSide.value) === frameInfo.frameSize || rightSide.kind === "string_literal" && rightSide.address === frameInfo.frameSize) {
26295+
return true;
26296+
}
2615926297
}
2616026298
}
2616126299
if (globalSet.value.kind === "var") {
@@ -26164,10 +26302,10 @@ log(mod.toString());`
2616426302
return false;
2616526303
}
2616626304
function isStackFramePrologue(statement, frameInfo) {
26167-
if (statement.kind === "assign" && statement.value.kind === "global" && statement.value.name === frameInfo.stackPointerName) {
26305+
if (statement.kind === "assign" && statement.value.kind === "global" && isStackPointerGlobal(statement.value.name)) {
2616826306
return true;
2616926307
}
26170-
if (statement.kind === "assign" && statement.target === frameInfo.frameVarName && statement.value.kind === "binary" && statement.value.op === "-") {
26308+
if (statement.kind === "assign" && statement.target === frameInfo.frameVarName) {
2617126309
return true;
2617226310
}
2617326311
if (statement.kind === "global_set" && isStackPointerGlobal(statement.name)) {
@@ -26220,6 +26358,38 @@ log(mod.toString());`
2622026358
return node;
2622126359
}
2622226360
}
26361+
function removeBareSPReferences(node) {
26362+
switch (node.kind) {
26363+
case "block": {
26364+
const filtered = node.body.filter((statement) => {
26365+
if (statement.kind === "global_set" && isStackPointerGlobal(statement.name)) {
26366+
return false;
26367+
}
26368+
if (statement.kind === "assign" && statement.value.kind === "global" && isStackPointerGlobal(statement.value.name)) {
26369+
return false;
26370+
}
26371+
return true;
26372+
});
26373+
return { kind: "block", body: filtered };
26374+
}
26375+
case "sequence":
26376+
return { kind: "sequence", children: node.children.map((child) => removeBareSPReferences(child)) };
26377+
case "if":
26378+
return { kind: "if", condition: node.condition, thenBody: removeBareSPReferences(node.thenBody), elseBody: node.elseBody ? removeBareSPReferences(node.elseBody) : null };
26379+
case "while":
26380+
return { kind: "while", condition: node.condition, body: removeBareSPReferences(node.body) };
26381+
case "do_while":
26382+
return { kind: "do_while", body: removeBareSPReferences(node.body), condition: node.condition };
26383+
case "for":
26384+
return { kind: "for", init: node.init, condition: node.condition, increment: node.increment, body: removeBareSPReferences(node.body) };
26385+
case "labeled_block":
26386+
return { kind: "labeled_block", label: node.label, body: removeBareSPReferences(node.body) };
26387+
case "switch":
26388+
return { kind: "switch", selector: node.selector, cases: node.cases.map((c) => ({ values: c.values, body: removeBareSPReferences(c.body) })), defaultBody: removeBareSPReferences(node.defaultBody) };
26389+
default:
26390+
return node;
26391+
}
26392+
}
2622326393
function getFirstBlock(node) {
2622426394
if (node.kind === "block") {
2622526395
return node.body;
@@ -26236,7 +26406,7 @@ log(mod.toString());`
2623626406
}
2623726407
const frameInfo = extractFrameAlloc(firstBlock);
2623826408
if (!frameInfo) {
26239-
return { node, frameVarName: null, frameSize: 0 };
26409+
return { node: removeBareSPReferences(node), frameVarName: null, frameSize: 0 };
2624026410
}
2624126411
return {
2624226412
node: processNode(node, frameInfo),
@@ -30844,6 +31014,17 @@ ${funcEntry.body.length} bytes, ${totalLocals} locals`,
3084431014
}
3084531015
let selectedInstrIdx = -1;
3084631016
const instrRows = [];
31017+
const clearHighlight = () => {
31018+
if (selectedInstrIdx >= 0 && selectedInstrIdx < instrRows.length) {
31019+
instrRows[selectedInstrIdx].classList.remove("hex-instr-active");
31020+
}
31021+
for (const spans of byteSpans.values()) {
31022+
for (const span of spans) {
31023+
span.classList.remove("hex-byte-active");
31024+
}
31025+
}
31026+
selectedInstrIdx = -1;
31027+
};
3084731028
const highlightInstruction = (targetIdx) => {
3084831029
if (selectedInstrIdx >= 0 && selectedInstrIdx < instrRows.length) {
3084931030
instrRows[selectedInstrIdx].classList.remove("hex-instr-active");
@@ -30883,24 +31064,25 @@ ${funcEntry.body.length} bytes, ${totalLocals} locals`,
3088331064
immediates.textContent = instruction.immediates.values.join(", ");
3088431065
row.appendChild(immediates);
3088531066
}
30886-
row.addEventListener("click", () => highlightInstruction(instrIdx));
31067+
row.addEventListener("mouseenter", () => highlightInstruction(instrIdx));
3088731068
instrList.appendChild(row);
3088831069
instrRows.push(row);
3088931070
}
30890-
hexView.addEventListener("click", (event) => {
31071+
instrList.addEventListener("mouseleave", () => clearHighlight());
31072+
hexView.addEventListener("mouseover", (event) => {
3089131073
const target = event.target;
3089231074
if (target.dataset.offset !== void 0) {
30893-
const clickedOffset = parseInt(target.dataset.offset, 10);
31075+
const hoveredOffset = parseInt(target.dataset.offset, 10);
3089431076
for (let instrIdx = 0; instrIdx < instructions.length; instrIdx++) {
3089531077
const instruction = instructions[instrIdx];
30896-
if (clickedOffset >= instruction.offset && clickedOffset < instruction.offset + instruction.length) {
31078+
if (hoveredOffset >= instruction.offset && hoveredOffset < instruction.offset + instruction.length) {
3089731079
highlightInstruction(instrIdx);
30898-
instrRows[instrIdx].scrollIntoView({ block: "nearest" });
3089931080
break;
3090031081
}
3090131082
}
3090231083
}
3090331084
});
31085+
hexView.addEventListener("mouseleave", () => clearHighlight());
3090431086
splitContainer.appendChild(instrList);
3090531087
splitContainer.appendChild(hexView);
3090631088
parent.appendChild(splitContainer);

playground/playground.bundle.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)