@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
266 lines (264 loc) • 9.84 kB
JavaScript
;
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