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