UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

266 lines (264 loc) 9.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveJumps = void 0; const pc_1 = require("../pc"); const KnownProgramCounter_1 = require("../pc/KnownProgramCounter"); const MAX_JUMP = 32000; class CodePoint { constructor(node, tags) { this.node = node; this.tags = tags; } get pc() { return this.resolvePC(); } get prev() { return this.mutablePrev; } set prev(prev) { this.mutablePC = undefined; this.mutablePrev = prev; } get next() { return this.mutableNext; } set next(next) { this.mutableNext = next; } resolveAllPCs() { let current = this; while (current !== undefined) { current.resolvePC(true); current = current.next; } } resolvePC(force = false) { if (force || this.mutablePC === undefined || (this.prev !== undefined && this.prev.mutablePC === undefined)) { this.mutablePC = this.prev === undefined ? 0 : this.prev.pc + this.prev.length; } return this.mutablePC; } } class JumpCodePoint extends CodePoint { constructor(node, tags, type) { super(node, tags); this.type = type; this.length = 3; } get target() { if (this.mutableTarget === undefined) { throw new Error('Target not set'); } return this.mutableTarget; } set target(target) { this.mutableTarget = target; } get isForwardJump() { return this.target.pc > this.pc; } get isReverseJump() { return this.target.pc < this.pc; } } class LineCodePoint extends CodePoint { constructor() { super(...arguments); this.length = 5; } } class BufferCodePoint extends CodePoint { constructor(node, tags, value) { super(node, tags); this.value = value; this.length = value.length; } } class JumpStationCodePoint extends CodePoint { constructor(node, hasForward, reverseTarget) { super(node, ['JumpStation']); this.reverseTarget = reverseTarget; this.length = hasForward ? 9 : 6; } get target() { return this.mutableTarget; } set target(target) { this.mutableTarget = target; } } const getCodePoint = (bytecode) => { const mutableSources = {}; const mutableCodePoints = {}; const [firstNode, firstTags, firstBytecode] = bytecode[0]; if (!(firstBytecode instanceof pc_1.Jump)) { throw new Error('Expected first bytecode to be a jump.'); } const first = new JumpCodePoint(firstNode, firstTags, firstBytecode.op); const jumpTargetPC = firstBytecode.pc.getPC(); mutableSources[jumpTargetPC] = [first]; mutableCodePoints[0] = first; let pc = first.length; let mutablePrev = first; bytecode.slice(1).forEach(([node, tags, value]) => { let mutableCodePoint; if (value instanceof pc_1.Jump) { const targetPC = value.pc.getPC(); let mutableJumpCodePoint; if (targetPC < pc) { mutableJumpCodePoint = new JumpCodePoint(node, tags, value.op); mutableJumpCodePoint.target = mutableCodePoints[targetPC]; } else { mutableJumpCodePoint = new JumpCodePoint(node, tags, value.op); if (mutableSources[targetPC] === undefined) { mutableSources[targetPC] = []; } mutableSources[targetPC].push(mutableJumpCodePoint); } mutableCodePoint = mutableJumpCodePoint; } else if (value instanceof pc_1.Line) { mutableCodePoint = new LineCodePoint(node, tags); } else { mutableCodePoint = new BufferCodePoint(node, tags, value); } const pcSources = mutableSources[pc]; if (pcSources !== undefined) { pcSources.forEach((mutableSource) => { mutableSource.target = mutableCodePoint; }); } mutableCodePoints[pc] = mutableCodePoint; pc += mutableCodePoint.length; mutableCodePoint.prev = mutablePrev; mutablePrev.next = mutableCodePoint; mutablePrev = mutableCodePoint; }); return first; }; const addJumpStations = (node, codePoint, maxOffset) => { codePoint.resolveAllPCs(); const mutableFirstCodePoint = codePoint; if (!(mutableFirstCodePoint instanceof JumpCodePoint)) { throw new Error('Expected first codepoint to be a jump'); } const secondCodePoint = codePoint.next; if (secondCodePoint === undefined) { throw new Error('Expected at least two codepoints'); } let mutableReverseTarget = secondCodePoint; let forwardDone = false; let mutableCurrent = mutableFirstCodePoint; let firstJumpStation; while (mutableCurrent !== undefined) { if (mutableCurrent instanceof JumpCodePoint && mutableCurrent.isReverseJump && mutableCurrent.pc - mutableCurrent.target.pc > maxOffset) { mutableCurrent.target = mutableReverseTarget; } const reversePC = mutableReverseTarget.pc + (firstJumpStation === undefined ? 0 : 3); const mutableNext = mutableCurrent.next; if (mutableNext !== undefined && mutableNext.pc - reversePC + 9 > maxOffset) { const hasForward = !forwardDone && mutableFirstCodePoint.target.pc - maxOffset >= mutableNext.pc; let mutableJumpStation; if (!hasForward && !forwardDone) { mutableJumpStation = new JumpStationCodePoint(node, true, mutableReverseTarget); mutableJumpStation.target = mutableFirstCodePoint.target; if (mutableReverseTarget instanceof JumpStationCodePoint) { mutableReverseTarget.target = mutableJumpStation; } forwardDone = true; } else { mutableJumpStation = new JumpStationCodePoint(node, hasForward, mutableReverseTarget); if (hasForward && mutableReverseTarget instanceof JumpStationCodePoint) { mutableReverseTarget.target = mutableJumpStation; } } if (firstJumpStation === undefined) { firstJumpStation = mutableJumpStation; } mutableReverseTarget = mutableJumpStation; const mutablePrev = mutableCurrent.prev; if (mutablePrev !== undefined) { mutablePrev.next = mutableJumpStation; } mutableCurrent.prev = mutableJumpStation; mutableJumpStation.next = mutableCurrent; mutableJumpStation.prev = mutablePrev; } mutableCurrent = mutableCurrent.next; } if (firstJumpStation !== undefined) { mutableFirstCodePoint.target = firstJumpStation; } }; const getTargetPC = (codePoint, target) => { if (target instanceof JumpStationCodePoint) { if (target.pc > codePoint.pc) { return new KnownProgramCounter_1.KnownProgramCounter(target.pc + 6); } return new KnownProgramCounter_1.KnownProgramCounter(target.pc + 3); } return new KnownProgramCounter_1.KnownProgramCounter(target.pc); }; const getBytecode = (first) => { first.resolveAllPCs(); let current = first; const mutableOut = []; while (current !== undefined) { if (current instanceof JumpCodePoint) { const pc = getTargetPC(current, current.target); if (current.type === 'CALL') { mutableOut.push([current.node, current.tags, new pc_1.Call(pc)]); } else { mutableOut.push([current.node, current.tags, new pc_1.Jmp(current.type, pc)]); } } else if (current instanceof BufferCodePoint) { mutableOut.push([current.node, current.tags, current.value]); } else if (current instanceof JumpStationCodePoint) { const target = current.target; const reverseTarget = new pc_1.Jmp('JMP', getTargetPC(current, current.reverseTarget)); if (target === undefined) { mutableOut.push([ current.node, current.tags, new pc_1.Jmp('JMP', new KnownProgramCounter_1.KnownProgramCounter(current.pc + current.length)), ]); mutableOut.push([current.node, current.tags, reverseTarget]); } else { mutableOut.push([ current.node, current.tags, new pc_1.Jmp('JMP', new KnownProgramCounter_1.KnownProgramCounter(current.pc + current.length)), ]); mutableOut.push([current.node, current.tags, reverseTarget]); mutableOut.push([current.node, current.tags, new pc_1.Jmp('JMP', getTargetPC(current, target))]); } } else if (current instanceof LineCodePoint) { mutableOut.push([current.node, current.tags, new pc_1.Line()]); } else { throw new Error('Something went wrong.'); } current = current.next; } return mutableOut; }; exports.resolveJumps = (bytecode, maxOffset = MAX_JUMP) => { const length = bytecode.reduce((acc, value) => (value instanceof pc_1.Jump ? acc + 3 : value instanceof pc_1.Line ? acc + 5 : acc + value.length), 0); if (length < MAX_JUMP) { return bytecode; } const codePoint = getCodePoint(bytecode); addJumpStations(bytecode[0][0], codePoint, maxOffset); return getBytecode(codePoint); }; //# sourceMappingURL=resolveJumps.js.map