UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

208 lines (206 loc) 7.48 kB
import * as constants from '../../constants'; class IdentifierName { constructor(value) { this.value = value; this.nameBrand = 0; } } export class ResolvedScope { constructor(variableCount, parent) { this.variableCount = variableCount; this.parent = parent; this.mutablePosition = 0; this.mutableVariables = {}; this.uniqueVariables = new Map(); if (this.parent === undefined) { this.addScope = true; this.scopeCount = 1; this.scopeLength = 1; } else { this.addScope = variableCount > 0; this.scopeCount = this.addScope ? 1 : 0; this.scopeLength = this.parent.scopeLength + this.scopeCount; } } add(name) { const identifier = new IdentifierName(name); const existing = this.mutableVariables[name]; if (existing !== undefined) { return identifier; } this.mutableVariables[identifier.value] = this.mutablePosition; this.mutablePosition += 1; if (this.mutablePosition > this.variableCount) { throw new Error(`Something went wrong. Name: ${name} Position: ${this.mutablePosition} Count: ${this.variableCount}`); } return identifier; } addUnique() { const name = { nameBrand: 0 }; this.uniqueVariables.set(name, this.mutablePosition); this.mutablePosition += 1; if (this.mutablePosition > this.variableCount) { throw new Error(`Something went wrong. Position: ${this.mutablePosition} Count: ${this.variableCount}`); } return name; } set(sb, node, optionsIn, name, scopeLength = this.scopeLength, scopePosition = 0) { const options = sb.pushValueOptions(optionsIn); const position = this.getPosition(name); if (position === undefined) { if (this.parent !== undefined) { this.parent.set(sb, node, options, name, scopeLength, scopePosition + this.scopeCount); } } else { this.loadScope(sb, node, scopeLength, scopePosition); sb.emitPushInt(node, position); sb.emitOp(node, 'ROT'); sb.emitOp(node, 'SETITEM'); } } get(sb, node, options, name, scopeLength = this.scopeLength, scopePosition = 0) { const position = this.getPosition(name); if (position === undefined) { if (this.parent !== undefined) { this.parent.get(sb, node, options, name, scopeLength, scopePosition + this.scopeCount); } } else { this.loadScope(sb, node, scopeLength, scopePosition); sb.emitPushInt(node, position); sb.emitOp(node, 'PICKITEM'); } } getThis(sb, node, _options) { this.loadAll(sb, node); sb.emitPushInt(node, 1); sb.emitOp(node, 'PICKITEM'); } getGlobal(sb, node, options) { if (this.parent === undefined) { this.loadAll(sb, node); sb.emitPushInt(node, 2); sb.emitOp(node, 'PICKITEM'); } else { this.parent.getGlobal(sb, node, options); } } setGlobal(sb, node, options) { if (this.parent === undefined) { this.loadAll(sb, node); sb.emitOp(node, 'TUCK'); sb.emitOp(node, 'OVER'); sb.emitPushInt(node, 2); sb.emitOp(node, 'SWAP'); sb.emitOp(node, 'SETITEM'); sb.emitPushInt(node, 1); sb.emitOp(node, 'SWAP'); sb.emitOp(node, 'SETITEM'); } else { this.parent.setGlobal(sb, node, options); } } pushAll(sb, node, _options) { sb.emitOp(node, 'DUPFROMALTSTACK'); } emit(sb, node, options, func) { if (this.addScope) { this.surround(sb, node, options, func); } else { func(options); } } surround(sb, node, options, func) { if (this.parent === undefined) { sb.emitHelper(node, sb.pushValueOptions(options), sb.helpers.wrapUndefined); sb.emitHelper(node, sb.pushValueOptions(options), sb.helpers.wrapUndefined); sb.emitPushInt(node, 0); sb.emitOp(node, 'NEWARRAY'); sb.emitPushInt(node, 3); sb.emitOp(node, 'PACK'); sb.emitOp(node, 'DUP'); sb.emitOp(node, 'TOALTSTACK'); } else { sb.emitOp(node, 'DUPFROMALTSTACK'); } sb.emitPushInt(node, 0); sb.emitOp(node, 'PICKITEM'); sb.emitOp(node, 'NEWMAP'); sb.emitOp(node, 'APPEND'); const { breakPC, continuePC, catchPC, finallyPC } = options; const nonLocal = breakPC !== undefined || continuePC !== undefined || catchPC !== undefined || finallyPC !== undefined; sb.withProgramCounter((pc) => { let innerOptions = options; if (breakPC !== undefined) { innerOptions = sb.breakPCOptions(innerOptions, pc.getLast()); } if (continuePC !== undefined) { innerOptions = sb.continuePCOptions(innerOptions, pc.getLast()); } if (catchPC !== undefined) { innerOptions = sb.catchPCOptions(innerOptions, pc.getLast()); } if (finallyPC !== undefined) { innerOptions = sb.finallyPCOptions(innerOptions, pc.getLast()); } func(innerOptions); if (nonLocal) { sb.emitPushInt(node, constants.NORMAL_COMPLETION); } }); if (this.parent === undefined) { sb.emitOp(node, 'FROMALTSTACK'); sb.emitOp(node, 'DROP'); } else { sb.emitOp(node, 'DUPFROMALTSTACK'); sb.emitPushInt(node, 0); sb.emitOp(node, 'PICKITEM'); sb.emitOp(node, 'DUP'); sb.emitOp(node, 'ARRAYSIZE'); sb.emitOp(node, 'DEC'); sb.emitOp(node, 'REMOVE'); } if (nonLocal) { this.emitNonLocal(sb, node, constants.BREAK_COMPLETION, breakPC); this.emitNonLocal(sb, node, constants.CONTINUE_COMPLETION, continuePC); this.emitNonLocal(sb, node, constants.THROW_COMPLETION, catchPC); this.emitNonLocal(sb, node, constants.FINALLY_COMPLETION, finallyPC); sb.emitOp(node, 'DROP'); } } emitNonLocal(sb, node, completion, pc) { if (pc !== undefined) { sb.emitOp(node, 'DUP'); sb.emitPushInt(node, completion); sb.emitOp(node, 'NUMEQUAL'); sb.emitJmp(node, 'JMPIF', pc); } } getPosition(name) { if (typeof name === 'string') { return this.mutableVariables[name]; } if (name instanceof IdentifierName) { return this.mutableVariables[name.value]; } return this.uniqueVariables.get(name); } loadScope(sb, node, scopeLength, scopePosition) { this.loadAll(sb, node); sb.emitPushInt(node, 0); sb.emitOp(node, 'PICKITEM'); sb.emitPushInt(node, scopeLength - scopePosition - 1); sb.emitOp(node, 'PICKITEM'); } loadAll(sb, node) { sb.emitOp(node, 'DUPFROMALTSTACK'); } } //# sourceMappingURL=ResolvedScope.js.map