UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

394 lines (392 loc) 21.5 kB
"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