ton-assembly
Version:
TON assembler and disassembler
129 lines • 5.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findInstructionInfo = exports.createTraceInfoPerTransaction = exports.createTraceInfo = void 0;
const logs_1 = require("./logs");
/**
* Creates a trace from the logs and mapping.
*/
const createTraceInfo = (logs, mapping, sourceMap) => {
const stepLogInfo = (0, logs_1.parseLogs)(logs).flat();
const steps = stepLogInfo.flatMap((stepInfo) => {
// TODO: handle JMPREF and first ref instruction with offset 0
const res = (0, exports.findInstructionInfo)(mapping, stepInfo);
if (!res) {
// skip for simplicity for now
return [];
}
const [instructions, index] = res;
const instr = instructions[index];
if (!instr)
return [];
const sourceMapEntries = sourceMap
? instr.debugSections.map(it => sourceMap.locations[it]).filter(it => it !== undefined)
: [];
return [
{
loc: instr.loc,
instructionName: instr.name,
stack: stepInfo.stack,
gas: stepInfo.gas,
gasCost: stepInfo.gasCost,
sourceMapEntries,
},
];
});
return { steps };
};
exports.createTraceInfo = createTraceInfo;
const createTraceInfoPerTransaction = (logs, mapping, sourceMap) => {
const transactionLogs = (0, logs_1.parseLogs)(logs);
return transactionLogs.map((transactionLog) => {
const steps = [];
// handle cases where we have two instructions with offset 0
// like JMPREF and the first instruction in the ref
let offsetZeroCount = 0;
for (const stepInfo of transactionLog) {
const res = (0, exports.findInstructionInfo)(mapping, stepInfo);
if (!res) {
// skip for simplicity for now
continue;
}
if (stepInfo.offset !== 0 && offsetZeroCount !== 0) {
// if we found an instruction with a non-zero offset,
// we reset the offsetZeroCount to correctly handle it
offsetZeroCount = 0;
}
const [instructions, index] = res;
const instr = instructions[index + offsetZeroCount];
if (!instr)
continue;
if (stepInfo.offset === 0 && offsetZeroCount === 0) {
// if we handled the first instruction with offset 0,
// on the next step we will handle the second one
offsetZeroCount = 1;
}
const sourceMapEntries = sourceMap
? instr.debugSections
.map(it => sourceMap.locations[it])
.filter(it => it !== undefined)
: [];
if (stepInfo.implicit) {
const actualInstr = index === -1 && instructions.at(-1)?.name === "RET"
? (instructions.at(-1) ?? instr)
: instructions.length > index + 1 && instructions[index + 1]?.name === "RET"
? (instructions[index + 1] ?? instr)
: instr;
const actualSourceMapEntries = sourceMap
? actualInstr.debugSections
.map(it => sourceMap.locations[it])
.filter(it => it !== undefined)
: [];
steps.push({
loc: actualInstr === instr ? undefined : actualInstr.loc,
instructionName: "implicit RET",
stack: stepInfo.stack,
gas: stepInfo.gas,
gasCost: stepInfo.gasCost,
sourceMapEntries: actualSourceMapEntries,
});
continue;
}
steps.push({
loc: instr.loc,
instructionName: instr.name,
stack: stepInfo.stack,
gas: stepInfo.gas,
gasCost: stepInfo.gasCost,
sourceMapEntries,
});
}
return { steps };
});
};
exports.createTraceInfoPerTransaction = createTraceInfoPerTransaction;
/**
* Finds the instruction in the mapping.
*/
const findInstructionInfo = (info, stepInfo) => {
// Cell hash can be both Dictionary Cell hash or just a regular code Cell hash
const hash = stepInfo.hash;
// We try to find the Dictionary Cell in the mapping
const dictCell = info.dictionaryCells.find(it => it.cell === hash);
// And if we found it, we use not Dictionary Cell hash,
// but the data Cell hash that we stored in the mapping
const [targetCellHash, offset] = dictCell ? [dictCell.dataCell, dictCell.offset] : [hash, 0];
const cell = info.cells[targetCellHash];
if (!cell) {
// no luck, we don't have this cell in the mapping
return undefined;
}
// Important note that we need to shift the step offset that we have in the log
// since in the code Cell we start from the 0, while in logs it starts from the offset
// of the code Cell in the Dictionary Cell
return [
cell.instructions,
cell.instructions.findIndex(it => it.offset === stepInfo.offset - offset),
];
};
exports.findInstructionInfo = findInstructionInfo;
//# sourceMappingURL=trace.js.map