export function joinType(a, b) {
if (a === b) return a;
if (a === "unknown") return b;
if (b === "unknown") return a;
return "any";
}
export function directCallTarget(instruction) {
const operand = instruction.operand;
if (operand?.kind === "Jump" || operand?.kind === "Jump32") {
return instruction.offset + operand.value;
}
return null;
}
export function pushaTarget(instruction) {
const operand = instruction.operand;
if (operand?.kind === "I32") {
const target = instruction.offset + operand.value;
return target >= 0 ? target : null;
}
return null;
}
export function constrainOrigins(origins, expected, locals, args, statics) {
for (const origin of origins ?? []) {
const slots =
origin.kind === "local"
? locals
: origin.kind === "argument"
? args
: origin.kind === "static"
? statics
: null;
if (!slots || origin.index < 0 || origin.index >= slots.length) continue;
slots[origin.index] = joinType(slots[origin.index], expected);
}
}
export function reverseTop(stack, count) {
if (count === 0 || stack.length < count) return;
const start = stack.length - count;
const slice = stack.slice(start).reverse();
for (let i = 0; i < count; i += 1) stack[start + i] = slice[i];
}
export function replaceWithUnknowns(stack) {
for (let index = 0; index < stack.length; index += 1) {
stack[index] = { ty: "unknown", lit: null, ptr: null, origins: [] };
}
}
export function intLiteral(operand) {
if (!operand) return null;
switch (operand.kind) {
case "I8":
case "I16":
case "I32":
case "I64":
case "U8":
case "U16":
case "U32": {
const v = operand.value;
return typeof v === "bigint" ? v : Number(v);
}
default:
return null;
}
}
export function asCount(lit) {
if (lit === null || lit === undefined) return null;
if (typeof lit === "bigint") {
if (lit < 0n) return null;
return lit > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(lit);
}
if (!Number.isInteger(lit) || lit < 0) return null;
return lit;
}
export function manifestType(kind) {
const normalized = String(kind).toLowerCase();
if (normalized === "any") return "any";
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";
}
const CONVERT_TARGET_MAP = new Map([
[0x00, "any"],
[0x10, "pointer"],
[0x20, "bool"],
[0x21, "integer"],
[0x28, "bytestring"],
[0x30, "buffer"],
[0x40, "array"],
[0x41, "struct"],
[0x48, "map"],
[0x60, "interopinterface"],
]);
export function convertTargetType(operand) {
if (!operand || (operand.kind !== "U8" && operand.kind !== "I8")) {
return null;
}
const byte = operand.kind === "U8" ? operand.value : operand.value & 0xff;
return CONVERT_TARGET_MAP.get(byte) ?? null;
}