@neo-one/smart-contract-compiler
Version:
NEO•ONE TypeScript smart contract compiler.
394 lines (392 loc) • 21.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BinaryExpressionCompiler = void 0;
const tslib_1 = require("tslib");
const ts_utils_1 = require("@neo-one/ts-utils");
const utils_1 = require("@neo-one/utils");
const typescript_1 = tslib_1.__importDefault(require("typescript"));
const builtins_1 = require("../builtins");
const types_1 = require("../helper/types");
const NodeCompiler_1 = require("../NodeCompiler");
class BinaryExpressionCompiler extends NodeCompiler_1.NodeCompiler {
constructor() {
super(...arguments);
this.kind = typescript_1.default.SyntaxKind.BinaryExpression;
}
visitNode(sb, expr, options) {
const kind = ts_utils_1.tsUtils.expression.getOperatorToken(expr).kind;
switch (kind) {
case typescript_1.default.SyntaxKind.EqualsToken:
case typescript_1.default.SyntaxKind.PlusEqualsToken:
case typescript_1.default.SyntaxKind.MinusEqualsToken:
case typescript_1.default.SyntaxKind.AsteriskAsteriskEqualsToken:
case typescript_1.default.SyntaxKind.AsteriskEqualsToken:
case typescript_1.default.SyntaxKind.SlashEqualsToken:
case typescript_1.default.SyntaxKind.PercentEqualsToken:
case typescript_1.default.SyntaxKind.AmpersandEqualsToken:
case typescript_1.default.SyntaxKind.BarEqualsToken:
case typescript_1.default.SyntaxKind.CaretEqualsToken:
case typescript_1.default.SyntaxKind.LessThanLessThanEqualsToken:
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanEqualsToken:
this.visitAssignmentOperator(sb, kind, expr, options);
break;
case typescript_1.default.SyntaxKind.AsteriskToken:
case typescript_1.default.SyntaxKind.SlashToken:
case typescript_1.default.SyntaxKind.PercentToken:
case typescript_1.default.SyntaxKind.PlusToken:
case typescript_1.default.SyntaxKind.MinusToken:
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanToken:
case typescript_1.default.SyntaxKind.LessThanLessThanToken:
case typescript_1.default.SyntaxKind.LessThanToken:
case typescript_1.default.SyntaxKind.LessThanEqualsToken:
case typescript_1.default.SyntaxKind.GreaterThanToken:
case typescript_1.default.SyntaxKind.GreaterThanEqualsToken:
case typescript_1.default.SyntaxKind.ExclamationEqualsToken:
case typescript_1.default.SyntaxKind.EqualsEqualsToken:
case typescript_1.default.SyntaxKind.AmpersandToken:
case typescript_1.default.SyntaxKind.BarToken:
case typescript_1.default.SyntaxKind.CaretToken:
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case typescript_1.default.SyntaxKind.InKeyword:
case typescript_1.default.SyntaxKind.InstanceOfKeyword:
case typescript_1.default.SyntaxKind.CommaToken:
case typescript_1.default.SyntaxKind.AsteriskAsteriskToken:
case typescript_1.default.SyntaxKind.EqualsEqualsEqualsToken:
case typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken:
this.visitExpressionOperator(sb, kind, expr, options);
break;
case typescript_1.default.SyntaxKind.AmpersandAmpersandToken:
case typescript_1.default.SyntaxKind.BarBarToken:
case typescript_1.default.SyntaxKind.QuestionQuestionToken:
this.visitLogicalExpressionOperator(sb, kind, expr, options);
break;
default:
utils_1.utils.assertNever(kind);
}
}
visitAssignmentOperator(sb, kind, expr, options) {
const left = ts_utils_1.tsUtils.expression.getLeft(expr);
const right = ts_utils_1.tsUtils.expression.getRight(expr);
const token = ts_utils_1.tsUtils.expression.getOperatorToken(expr);
const pushValueOptions = sb.pushValueOptions(options);
switch (kind) {
case typescript_1.default.SyntaxKind.EqualsToken:
sb.visit(right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.PlusEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.PlusToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.MinusEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.MinusToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.AsteriskAsteriskEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.AsteriskAsteriskToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.AsteriskEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.AsteriskToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.SlashEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.SlashToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.PercentEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.PercentToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.AmpersandEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.AmpersandToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.BarEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.BarToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.CaretEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.CaretToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.LessThanLessThanEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.LessThanLessThanToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, left, right, pushValueOptions);
break;
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanEqualsToken:
this.visitExpressionOperatorBase(sb, token, typescript_1.default.SyntaxKind.GreaterThanGreaterThanToken, left, right, pushValueOptions);
break;
default:
utils_1.utils.assertNever(kind);
}
types_1.handleTypeAssignment(sb.context, right, left);
sb.visit(ts_utils_1.tsUtils.expression.getLeft(expr), sb.setValueOptions(options));
}
visitExpressionOperator(sb, kind, expr, options) {
this.visitExpressionOperatorBase(sb, ts_utils_1.tsUtils.expression.getOperatorToken(expr), kind, ts_utils_1.tsUtils.expression.getLeft(expr), ts_utils_1.tsUtils.expression.getRight(expr), options);
}
visitLogicalExpressionOperator(sb, kind, expr, options) {
this.visitLogicalExpressionOperatorBase(sb, ts_utils_1.tsUtils.expression.getOperatorToken(expr), kind, ts_utils_1.tsUtils.expression.getLeft(expr), ts_utils_1.tsUtils.expression.getRight(expr), options);
}
visitExpressionOperatorBase(sb, node, kind, left, right, optionsIn) {
const options = sb.pushValueOptions(optionsIn);
const leftType = sb.context.analysis.getType(left);
const rightType = sb.context.analysis.getType(right);
const visit = (leftHelper, rightHelper) => {
sb.visit(left, options);
sb.emitHelper(left, options, leftHelper({ type: leftType }));
sb.visit(right, options);
sb.emitHelper(right, options, (rightHelper === undefined ? leftHelper : rightHelper)({ type: rightType }));
};
const visitNumeric = () => visit(sb.helpers.toNumber);
const isBinaryNumeric = leftType !== undefined &&
ts_utils_1.tsUtils.type_.isOnlyNumberish(leftType) &&
rightType !== undefined &&
ts_utils_1.tsUtils.type_.isOnlyNumberish(rightType);
switch (kind) {
case typescript_1.default.SyntaxKind.AsteriskToken:
visitNumeric();
sb.emitOp(node, 'MUL');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.SlashToken:
visitNumeric();
sb.emitOp(node, 'DIV');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.PercentToken:
visitNumeric();
sb.emitOp(node, 'MOD');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.PlusToken:
if (isBinaryNumeric) {
visitNumeric();
sb.emitOp(node, 'ADD');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
}
else if (leftType !== undefined &&
ts_utils_1.tsUtils.type_.isOnlyStringish(leftType) &&
rightType !== undefined &&
ts_utils_1.tsUtils.type_.isOnlyStringish(rightType)) {
visit(() => sb.helpers.unwrapString);
sb.emitOp(node, 'CAT');
sb.emitHelper(node, options, sb.helpers.wrapString);
}
else if (leftType !== undefined && ts_utils_1.tsUtils.type_.isOnlyStringish(leftType)) {
visit(() => sb.helpers.unwrapString, sb.helpers.toString);
sb.emitOp(node, 'CAT');
sb.emitHelper(node, options, sb.helpers.wrapString);
}
else if (rightType !== undefined && ts_utils_1.tsUtils.type_.isOnlyStringish(rightType)) {
visit(sb.helpers.toString, () => sb.helpers.unwrapString);
sb.emitOp(node, 'CAT');
sb.emitHelper(node, options, sb.helpers.wrapString);
}
else {
visit(sb.helpers.toPrimitive);
sb.emitOp(node, 'SWAP');
sb.emitOp(node, 'TUCK');
sb.emitOp(node, 'OVER');
sb.emitHelper(node, options, sb.helpers.isString);
sb.emitOp(node, 'SWAP');
sb.emitHelper(node, options, sb.helpers.isString);
sb.emitHelper(node, options, sb.helpers.if({
condition: () => {
sb.emitOp(node, 'BOOLOR');
},
whenTrue: () => {
sb.emitHelper(node, options, sb.helpers.toString({ type: sb.context.analysis.getType(right) }));
sb.emitOp(node, 'SWAP');
sb.emitHelper(node, options, sb.helpers.toString({ type: sb.context.analysis.getType(left) }));
sb.emitOp(node, 'SWAP');
sb.emitOp(node, 'CAT');
sb.emitHelper(node, options, sb.helpers.wrapString);
},
whenFalse: () => {
sb.emitHelper(node, options, sb.helpers.toNumber({ type: sb.context.analysis.getType(right) }));
sb.emitOp(node, 'SWAP');
sb.emitHelper(node, options, sb.helpers.toNumber({ type: sb.context.analysis.getType(left) }));
sb.emitOp(node, 'SWAP');
sb.emitOp(node, 'ADD');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
},
}));
}
break;
case typescript_1.default.SyntaxKind.MinusToken:
visitNumeric();
sb.emitOp(node, 'SUB');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanToken:
visitNumeric();
sb.emitOp(node, 'SHR');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
visitNumeric();
sb.emitOp(node, 'SHR');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.LessThanLessThanToken:
visitNumeric();
sb.emitOp(node, 'SHL');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.LessThanToken:
sb.emitHelper(node, options, sb.helpers.lessThan({ leftFirst: true, left, right }));
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.LessThanEqualsToken:
sb.emitHelper(node, options, sb.helpers.lessThan({ leftFirst: false, left: right, right: left }));
sb.emitOp(node, 'NOT');
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.GreaterThanToken:
sb.emitHelper(node, options, sb.helpers.lessThan({ leftFirst: false, left: right, right: left }));
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.GreaterThanEqualsToken:
sb.emitHelper(node, options, sb.helpers.lessThan({ leftFirst: true, left, right }));
sb.emitOp(node, 'NOT');
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.ExclamationEqualsToken:
sb.emitHelper(node, options, sb.helpers.equalsEquals({ left, right }));
sb.emitOp(node, 'NOT');
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.EqualsEqualsToken:
sb.emitHelper(node, options, sb.helpers.equalsEquals({ left, right }));
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.AmpersandToken:
visitNumeric();
sb.emitOp(node, 'AND');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.BarToken:
visitNumeric();
sb.emitOp(node, 'OR');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.CaretToken:
visitNumeric();
sb.emitOp(node, 'XOR');
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.InKeyword:
sb.visit(right, options);
sb.visit(left, options);
sb.emitHelper(node, options, sb.helpers.inObjectProperty({ propType: leftType }));
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.InstanceOfKeyword:
this.handleInstanceOf(sb, node, left, right, options);
break;
case typescript_1.default.SyntaxKind.CommaToken:
sb.visit(left, options);
sb.emitOp(node, 'DROP');
sb.visit(right, options);
break;
case typescript_1.default.SyntaxKind.AsteriskAsteriskToken:
visitNumeric();
sb.emitHelper(node, options, sb.helpers.exp);
sb.emitHelper(node, options, sb.helpers.wrapNumber);
break;
case typescript_1.default.SyntaxKind.EqualsEqualsEqualsToken:
sb.visit(left, options);
sb.visit(right, options);
sb.emitHelper(node, options, sb.helpers.equalsEqualsEquals({ leftType, rightType }));
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
case typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken:
sb.visit(left, options);
sb.visit(right, options);
sb.emitHelper(node, options, sb.helpers.equalsEqualsEquals({ leftType, rightType }));
sb.emitOp(node, 'NOT');
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
break;
default:
utils_1.utils.assertNever(kind);
}
if (!optionsIn.pushValue) {
sb.emitOp(node, 'DROP');
return;
}
}
visitLogicalExpressionOperatorBase(sb, node, kind, left, right, options) {
switch (kind) {
case typescript_1.default.SyntaxKind.AmpersandAmpersandToken: {
sb.emitHelper(node, options, sb.helpers.if({
condition: () => {
sb.visit(left, sb.pushValueOptions(options));
if (options.pushValue) {
sb.emitOp(left, 'DUP');
}
sb.emitHelper(left, sb.pushValueOptions(options), sb.helpers.toBoolean({ type: sb.context.analysis.getType(left) }));
},
whenTrue: () => {
if (options.pushValue) {
sb.emitOp(node, 'DROP');
}
sb.visit(right, options);
},
}));
break;
}
case typescript_1.default.SyntaxKind.BarBarToken: {
sb.emitHelper(node, options, sb.helpers.if({
condition: () => {
sb.visit(left, sb.pushValueOptions(options));
if (options.pushValue) {
sb.emitOp(left, 'DUP');
}
sb.emitHelper(left, sb.pushValueOptions(options), sb.helpers.toBoolean({ type: sb.context.analysis.getType(left) }));
},
whenFalse: () => {
if (options.pushValue) {
sb.emitOp(node, 'DROP');
}
sb.visit(right, options);
},
}));
break;
}
case typescript_1.default.SyntaxKind.QuestionQuestionToken: {
sb.emitHelper(node, options, sb.helpers.if({
condition: () => {
sb.visit(left, sb.pushValueOptions(options));
if (options.pushValue) {
sb.emitOp(left, 'DUP');
}
sb.emitHelper(left, sb.pushValueOptions(options), sb.helpers.toNullishBoolean({ type: sb.context.analysis.getType(left) }));
},
whenFalse: () => {
if (options.pushValue) {
sb.emitOp(node, 'DROP');
}
sb.visit(right, options);
},
whenTrue: () => {
if (options.pushValue) {
sb.emitOp(node, 'DROP');
}
sb.visit(left, options);
},
}));
break;
}
default:
utils_1.utils.assertNever(kind);
}
}
handleInstanceOf(sb, node, left, right, options) {
const builtin = sb.context.builtins.getValue(right);
if (builtin !== undefined && builtins_1.isBuiltinInstanceOf(builtin)) {
builtin.emitInstanceOf(sb, left, options);
return;
}
sb.visit(left, options);
sb.visit(right, options);
sb.emitHelper(node, options, sb.helpers.instanceof);
sb.emitHelper(node, options, sb.helpers.wrapBoolean);
}
}
exports.BinaryExpressionCompiler = BinaryExpressionCompiler;
//# sourceMappingURL=BinaryExpressionCompiler.js.map