import { tryCollectionExpression, tryCollectionStatement } from "./high-level-collections.js";
import { createControlFlowHelpers } from "./high-level-control-flow.js";
import { emitLabelIfNeeded, tryControlTransferFallback } from "./high-level-labels.js";
import { tryInternalCall, tryIndirectCall, trySyscall, tryTokenCall } from "./high-level-calls.js";
import { captureStoreInfo, cloneState, createState, emptyContext, finishInstruction } from "./high-level-state.js";
import {
tryBinaryExpression,
tryControlStatement,
tryStackShapeOperation,
tryUnaryExpression,
} from "./high-level-stack.js";
import {
pushImmediate,
tryLoadLocalOrArg,
tryLoadStatic,
trySlotDeclarations,
tryStoreArgument,
tryStoreLocal,
tryStoreStatic,
} from "./high-level-slots.js";
import { renderHighLevelMethodGroups as renderHighLevelMethodGroupsWithLift } from "./high-level-render.js";
import { renderUntranslatedInstruction, stripOuterParens } from "./high-level-utils.js";
import { postprocess } from "./postprocess.js";
let CONTROL_FLOW;
function liftStructuredSlice(
instructions,
manifestMethod = null,
context = emptyContext(),
methodOffset = instructions[0]?.offset ?? 0,
) {
if (instructions.length === 0) {
return { statements: [], warnings: [] };
}
const result =
CONTROL_FLOW.tryLiftSimpleSwitch(instructions, manifestMethod, context, methodOffset) ??
CONTROL_FLOW.tryLiftSimpleLoop(instructions, manifestMethod, context, methodOffset) ??
CONTROL_FLOW.tryLiftSimpleTryBlock(instructions, manifestMethod, context, methodOffset) ??
CONTROL_FLOW.tryLiftSimpleBranch(instructions, manifestMethod, context, methodOffset) ??
liftStraightLineMethodBody(instructions, manifestMethod, context, undefined, methodOffset);
return result;
}
CONTROL_FLOW = createControlFlowHelpers({
createState,
cloneState,
executeStraightLine,
liftStructuredSlice,
});
export function renderHighLevelMethodGroups(groups, manifest, context = null) {
return renderHighLevelMethodGroupsWithLift(groups, manifest, context, liftMethodBody);
}
export const MAX_HIGH_LEVEL_METHOD_INSTRUCTIONS = 16384;
export function liftMethodBody(
instructions,
manifestMethod = null,
context = emptyContext(),
methodOffset = instructions[0]?.offset ?? 0,
) {
if (instructions.length > MAX_HIGH_LEVEL_METHOD_INSTRUCTIONS) {
const offsetHex = (instructions[0]?.offset ?? 0)
.toString(16)
.padStart(4, "0")
.toUpperCase();
return {
statements: [
`// method body too large for high-level lifting: ${instructions.length} instructions ` +
`exceeds the ${MAX_HIGH_LEVEL_METHOD_INSTRUCTIONS}-instruction limit; use the disassembler for the full listing`,
],
warnings: [
`high-level: method at 0x${offsetHex} skipped — ${instructions.length} instructions ` +
`exceeds the high-level lifting limit (${MAX_HIGH_LEVEL_METHOD_INSTRUCTIONS})`,
],
};
}
let result;
const switchLift = CONTROL_FLOW.tryLiftSimpleSwitch(instructions, manifestMethod, context, methodOffset);
if (switchLift !== null) {
result = switchLift;
} else {
const loopLift = CONTROL_FLOW.tryLiftSimpleLoop(instructions, manifestMethod, context, methodOffset);
if (loopLift !== null) {
result = loopLift;
} else {
const tryLift = CONTROL_FLOW.tryLiftSimpleTryBlock(instructions, manifestMethod, context, methodOffset);
if (tryLift !== null) {
result = tryLift;
} else {
const branchLift = CONTROL_FLOW.tryLiftSimpleBranch(instructions, manifestMethod, context, methodOffset);
if (branchLift !== null) {
result = branchLift;
} else {
result = liftStraightLineMethodBody(instructions, manifestMethod, context, undefined, methodOffset);
}
}
}
}
postprocess(result.statements, context.postprocessOptions);
return result;
}
function liftStraightLineMethodBody(
instructions,
manifestMethod = null,
context,
initialState,
methodOffset = instructions[0]?.offset ?? 0,
) {
const state = initialState ?? createState(manifestMethod, context, methodOffset, instructions);
executeStraightLine(state, instructions);
if (instructions.at(-1)?.opcode?.mnemonic !== "RET" && state.stack.length > 0) {
for (const expression of state.stack) {
state.statements.push(`${stripOuterParens(expression)};`);
}
state.stack.length = 0;
}
return { statements: state.statements, warnings: state.warnings };
}
function executeStraightLine(state, instructions) {
const {
statements,
initializedLocals,
initializedStatics,
parameterNames,
returnsVoid,
stack,
pointerTargetsByExpression,
pointerTargetsBySlot,
packedValuesByExpression,
packedValuesBySlot,
} = state;
for (const instruction of instructions) {
const mnemonic = instruction.opcode.mnemonic;
emitLabelIfNeeded(state, instruction.offset);
if (trySlotDeclarations(statements, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (pushImmediate(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (mnemonic === "NOP") {
finishInstruction(state, instruction);
continue;
}
if (mnemonic === "ENDFINALLY") {
finishInstruction(state, instruction);
continue;
}
if (tryLoadLocalOrArg(stack, mnemonic, parameterNames, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryLoadStatic(stack, mnemonic, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (
tryStoreLocal(
statements,
state,
initializedLocals,
pointerTargetsByExpression,
pointerTargetsBySlot,
packedValuesByExpression,
packedValuesBySlot,
mnemonic,
instruction,
)
) {
state.previousStoreInfo = captureStoreInfo(instruction, state);
finishInstruction(state, instruction);
continue;
}
if (tryStoreArgument(statements, state, parameterNames, mnemonic, instruction)) {
state.previousStoreInfo = captureStoreInfo(instruction, state);
finishInstruction(state, instruction);
continue;
}
if (
tryStoreStatic(
statements,
state,
initializedStatics,
pointerTargetsByExpression,
pointerTargetsBySlot,
packedValuesByExpression,
packedValuesBySlot,
mnemonic,
instruction,
)
) {
state.previousStoreInfo = captureStoreInfo(instruction, state);
finishInstruction(state, instruction);
continue;
}
if (tryBinaryExpression(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryInternalCall(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryIndirectCall(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryTokenCall(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (trySyscall(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryCollectionExpression(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryCollectionStatement(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryStackShapeOperation(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryUnaryExpression(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryControlStatement(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (tryControlTransferFallback(state, instruction)) {
finishInstruction(state, instruction);
continue;
}
if (mnemonic === "RET") {
if (returnsVoid || stack.length === 0) {
statements.push("return;");
} else {
statements.push(`return ${stripOuterParens(stack.pop())};`);
}
finishInstruction(state, instruction);
continue;
}
statements.push(renderUntranslatedInstruction(instruction));
const opcodeName =
instruction.opcode.mnemonic === "UNKNOWN"
? `UNKNOWN_0x${instruction.opcode.byte.toString(16).padStart(2, "0").toUpperCase()}`
: instruction.opcode.mnemonic;
state.warnings.push(
`high-level: 0x${instruction.offset.toString(16).padStart(4, "0").toUpperCase()}: ${opcodeName} (not yet translated)`,
);
finishInstruction(state, instruction);
}
}