import { scanSlotCounts, scanStaticSlotCount, slotIndex } from "./util.js";
export function inferTypes(instructions, methodGroups, manifest = null) {
const staticCount = scanStaticSlotCount(instructions);
const statics = Array.from({ length: staticCount }, () => "unknown");
const methods = methodGroups.map((group) => {
const [localCount, argCount] = scanSlotCounts(group.instructions);
const locals = Array.from({ length: localCount }, () => "unknown");
const argumentsTypes = Array.from({ length: argCount }, () => "unknown");
if (group.source?.parameters) {
while (argumentsTypes.length < group.source.parameters.length) {
argumentsTypes.push("unknown");
}
for (let index = 0; index < group.source.parameters.length; index += 1) {
argumentsTypes[index] = manifestType(group.source.parameters[index].kind);
}
}
for (let index = 0; index < group.instructions.length; index += 1) {
const instruction = group.instructions[index];
const next = group.instructions[index + 1];
const store = next?.opcode?.mnemonic;
if (instruction.opcode.mnemonic === "NEWMAP") {
assignStoredType(locals, statics, store, next, "map");
}
if (
instruction.opcode.mnemonic === "NEWSTRUCT0" ||
instruction.opcode.mnemonic === "NEWSTRUCT" ||
instruction.opcode.mnemonic === "PACKSTRUCT"
) {
assignStoredType(locals, statics, store, next, "struct");
}
if (
instruction.opcode.mnemonic === "NEWARRAY0" ||
instruction.opcode.mnemonic === "NEWARRAY" ||
instruction.opcode.mnemonic === "NEWARRAY_T"
) {
assignStoredType(locals, statics, store, next, "array");
}
if (instruction.opcode.mnemonic === "NEWBUFFER") {
assignStoredType(locals, statics, store, next, "buffer");
}
if (instruction.opcode.mnemonic === "PACKMAP") {
assignStoredType(locals, statics, store, next, "map");
}
if (instruction.opcode.mnemonic === "CONVERT") {
const target = convertTargetType(instruction.operand);
if (target) {
assignStoredType(locals, statics, store, next, target);
}
}
}
return {
method: { offset: group.start, name: group.name },
arguments: argumentsTypes,
locals,
};
});
return { methods, statics };
}
function assignStoredType(locals, statics, store, instruction, type) {
if (store?.startsWith("STLOC")) {
locals[slotIndex(store, instruction)] = type;
} else if (store?.startsWith("STSFLD")) {
statics[slotIndex(store, instruction)] = type;
}
}
function manifestType(kind) {
const normalized = String(kind).toLowerCase();
if (normalized === "boolean") return "bool";
if (normalized === "integer") return "integer";
if (
normalized === "string" ||
normalized === "bytearray" ||
normalized === "signature" ||
normalized === "hash160" ||
normalized === "hash256"
) {
return "bytestring";
}
if (normalized === "array") return "array";
if (normalized === "map") return "map";
if (normalized === "interopinterface") return "interopinterface";
return "unknown";
}
function convertTargetType(operand) {
if (!operand || (operand.kind !== "U8" && operand.kind !== "I8")) {
return null;
}
const byte = operand.kind === "U8" ? operand.value : operand.value & 0xff;
const map = {
0x00: "any",
0x10: "pointer",
0x20: "bool",
0x21: "integer",
0x28: "bytestring",
0x30: "buffer",
0x40: "array",
0x41: "struct",
0x48: "map",
0x60: "interopinterface",
};
return map[byte] ?? null;
}