UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

521 lines (519 loc) 27.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CallLikeHelper = void 0; const tslib_1 = require("tslib"); const ts_utils_1 = require("@neo-one/ts-utils"); const lodash_1 = tslib_1.__importDefault(require("lodash")); const typescript_1 = tslib_1.__importDefault(require("typescript")); const DiagnosticCode_1 = require("../../../DiagnosticCode"); const DiagnosticMessage_1 = require("../../../DiagnosticMessage"); const builtins_1 = require("../../builtins"); const constants_1 = require("../../constants"); const Helper_1 = require("../Helper"); class CallLikeHelper extends Helper_1.Helper { constructor() { super(...arguments); this.kind = typescript_1.default.SyntaxKind.CallExpression; } emit(sb, expression, optionsIn) { const isOptionalChain = typescript_1.default.isOptionalChain(expression); const expr = typescript_1.default.isCallExpression(expression) ? ts_utils_1.tsUtils.expression.getExpression(expression) : ts_utils_1.tsUtils.template.getTag(expression); const valueBuiltin = sb.context.builtins.getValue(expr); if (valueBuiltin !== undefined && !ts_utils_1.tsUtils.guards.isSuperExpression(expr)) { if (typescript_1.default.isCallExpression(expression) && builtins_1.isBuiltinCall(valueBuiltin)) { valueBuiltin.emitCall(sb, expression, optionsIn); return; } if (typescript_1.default.isTaggedTemplateExpression(expression) && builtins_1.isBuiltinTemplate(valueBuiltin)) { valueBuiltin.emitCall(sb, expression, optionsIn); return; } sb.context.reportError(expr, DiagnosticCode_1.DiagnosticCode.InvalidBuiltinCall, DiagnosticMessage_1.DiagnosticMessage.InvalidBuiltinCall); } const throwTypeError = (innerOptions) => { sb.emitOp(expr, 'DROP'); sb.emitHelper(expr, innerOptions, sb.helpers.throwTypeError); }; const processUndefined = (innerOptions) => { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); sb.emitHelper(expr, innerOptions, sb.helpers.wrapUndefined); }; const throwTypeErrorUnlessOptionalChain = (innerOptions) => { isOptionalChain ? processUndefined(innerOptions) : throwTypeError(innerOptions); }; const handleArguments = (innerOptions) => { if (typescript_1.default.isCallExpression(expression)) { sb.emitHelper(expression, innerOptions, sb.helpers.args); } else { const template = ts_utils_1.tsUtils.template.getTemplate(expression); if (typescript_1.default.isNoSubstitutionTemplateLiteral(template)) { sb.emitPushInt(template, 0); sb.emitOp(template, 'NEWARRAY'); sb.emitOp(template, 'DUP'); sb.visit(template, innerOptions); sb.emitOp(template, 'APPEND'); sb.emitHelper(template, innerOptions, sb.helpers.wrapArray); sb.emitPushInt(template, 1); sb.emitOp(template, 'PACK'); } else { const head = ts_utils_1.tsUtils.template.getTemplateHead(template); lodash_1.default.reverse([...ts_utils_1.tsUtils.template.getTemplateSpans(template)]).forEach((span) => { sb.visit(ts_utils_1.tsUtils.expression.getExpression(span), innerOptions); }); sb.emitPushInt(template, 0); sb.emitOp(template, 'NEWARRAY'); sb.emitOp(template, 'DUP'); sb.emitPushString(head, ts_utils_1.tsUtils.literal.getLiteralValue(head)); sb.emitHelper(head, innerOptions, sb.helpers.wrapString); sb.emitOp(head, 'APPEND'); ts_utils_1.tsUtils.template.getTemplateSpans(template).forEach((span) => { const spanLiteral = ts_utils_1.tsUtils.template.getLiteral(span); sb.emitOp(spanLiteral, 'DUP'); sb.emitPushString(spanLiteral, ts_utils_1.tsUtils.literal.getLiteralValue(spanLiteral)); sb.emitHelper(head, innerOptions, sb.helpers.wrapString); sb.emitOp(expr, 'APPEND'); }); sb.emitHelper(template, innerOptions, sb.helpers.wrapArray); sb.emitPushInt(template, ts_utils_1.tsUtils.template.getTemplateSpans(template).length + 1); sb.emitOp(template, 'PACK'); } } }; const handlePropertyVisit = (lhs, innerOptions) => { if (ts_utils_1.tsUtils.guards.isSuperExpression(lhs)) { sb.scope.getThis(sb, lhs, innerOptions); sb.visit(lhs, innerOptions); } else { sb.visit(lhs, innerOptions); sb.emitOp(lhs, 'DUP'); } }; const isValidBuiltinCall = (builtinProp) => { if (typescript_1.default.isCallExpression(expression)) { return builtins_1.isBuiltinMemberCall(builtinProp) || builtins_1.isBuiltinInstanceMemberCall(builtinProp); } if (typescript_1.default.isTaggedTemplateExpression(expression)) { return builtins_1.isBuiltinMemberTemplate(builtinProp) || builtins_1.isBuiltinInstanceMemberTemplate(builtinProp); } return false; }; const handleBuiltinMemberCall = (builtinProp, memberLike, visited) => { if (typescript_1.default.isCallExpression(expression)) { if (builtins_1.isBuiltinMemberCall(builtinProp)) { builtinProp.emitCall(sb, memberLike, expression, optionsIn); return; } if (builtins_1.isBuiltinInstanceMemberCall(builtinProp)) { builtinProp.emitCall(sb, memberLike, expression, optionsIn, visited); return; } } else if (typescript_1.default.isTaggedTemplateExpression(expression)) { if (builtins_1.isBuiltinMemberTemplate(builtinProp)) { builtinProp.emitCall(sb, memberLike, expression, optionsIn); return; } if (builtins_1.isBuiltinInstanceMemberTemplate(builtinProp)) { builtinProp.emitCall(sb, memberLike, expression, optionsIn, visited); return; } } }; const emitDropNTimes = (innerSb, node, n) => { for (let i = 0; i < n; i += 1) { innerSb.emitOp(node, 'DROP'); } }; const callIfNotNullOrUndefined = ({ innerOptions, argsNum, callback, }) => { sb.emitHelper(expr, optionsIn, sb.helpers.if({ condition: () => { sb.emitOp(expr, 'DUP'); sb.emitHelper(expr, optionsIn, sb.helpers.isNull); }, whenTrue: () => { emitDropNTimes(sb, expr, argsNum); sb.emitHelper(expr, innerOptions, sb.helpers.wrapUndefined); }, whenFalse: () => { sb.emitHelper(expr, optionsIn, sb.helpers.if({ condition: () => { sb.emitOp(expr, 'DUP'); sb.emitHelper(expr, optionsIn, sb.helpers.isUndefined); }, whenTrue: () => { emitDropNTimes(sb, expr, argsNum); sb.emitHelper(expr, innerOptions, sb.helpers.wrapUndefined); }, whenFalse: () => { callback(); }, })); }, })); }; const superExpression = typescript_1.default.isCallExpression(expression) ? ts_utils_1.tsUtils.expression.getExpression(expression) : undefined; if (typescript_1.default.isCallExpression(expression) && superExpression !== undefined && ts_utils_1.tsUtils.guards.isSuperExpression(superExpression)) { if (optionsIn.handleSuperConstruct !== undefined) { optionsIn.handleSuperConstruct(expression, superExpression, optionsIn); return; } const superClass = optionsIn.superClass; if (superClass === undefined) { throw new Error('Something went wrong, expected super class to be defined.'); } const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn)); handleArguments(options); sb.scope.getThis(sb, expression, options); sb.scope.get(sb, expression, options, superClass); sb.emitHelper(expression, sb.noPushValueOptions(options), sb.helpers.invokeConstruct()); } else if (typescript_1.default.isPropertyAccessExpression(expr)) { const value = ts_utils_1.tsUtils.expression.getExpression(expr); const valueType = sb.context.analysis.getType(value); const name = ts_utils_1.tsUtils.node.getNameNode(expr); const nameValue = ts_utils_1.tsUtils.node.getName(expr); const builtinProp = sb.context.builtins.getMember(value, name); if (builtinProp !== undefined && isValidBuiltinCall(builtinProp)) { handleBuiltinMemberCall(builtinProp, expr, false); return; } const createProcessBuiltin = (valueName) => { const member = sb.context.builtins.getOnlyMember(valueName, nameValue); if (member === undefined) { return throwTypeError; } return () => { sb.emitOp(expression, 'DROP'); handleBuiltinMemberCall(member, expr, true); }; }; const processObject = (innerOptions) => { handleArguments(innerOptions); sb.emitOp(expr, 'ROT'); sb.emitOp(expr, 'ROT'); sb.emitPushString(name, nameValue); sb.emitHelper(expr, innerOptions, sb.helpers.getPropertyObjectProperty); isOptionalChain ? callIfNotNullOrUndefined({ innerOptions, argsNum: 3, callback: () => sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })), }) : sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })); }; const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn)); handlePropertyVisit(value, options); sb.emitHelper(value, options, sb.helpers.forBuiltinType({ type: valueType, array: createProcessBuiltin('Array'), arrayStorage: createProcessBuiltin('ArrayStorage'), boolean: createProcessBuiltin('Boolean'), buffer: createProcessBuiltin('Buffer'), null: throwTypeError, number: createProcessBuiltin('Number'), object: processObject, string: createProcessBuiltin('String'), symbol: createProcessBuiltin('Symbol'), undefined: throwTypeError, map: createProcessBuiltin('Map'), mapStorage: createProcessBuiltin('MapStorage'), set: createProcessBuiltin('Set'), setStorage: createProcessBuiltin('SetStorage'), error: createProcessBuiltin('Error'), forwardValue: createProcessBuiltin('ForwardValue'), iteratorResult: createProcessBuiltin('IteratorResult'), iterable: createProcessBuiltin('Iterable'), iterableIterator: createProcessBuiltin('IterableIterator'), transaction: createProcessBuiltin('TransactionBase'), output: createProcessBuiltin('Output'), attribute: createProcessBuiltin('AttributeBase'), input: createProcessBuiltin('Input'), account: createProcessBuiltin('Account'), asset: createProcessBuiltin('Asset'), contract: createProcessBuiltin('Contract'), header: createProcessBuiltin('Header'), block: createProcessBuiltin('Block'), })); } else if (typescript_1.default.isElementAccessExpression(expr)) { const value = ts_utils_1.tsUtils.expression.getExpression(expr); const valueType = sb.context.analysis.getType(value); const prop = ts_utils_1.tsUtils.expression.getArgumentExpressionOrThrow(expr); const propType = sb.context.analysis.getType(prop); const builtinProp = sb.context.builtins.getMember(value, prop); if (builtinProp !== undefined && isValidBuiltinCall(builtinProp)) { handleBuiltinMemberCall(builtinProp, expr, false); return; } const getCallCases = (instanceName, useSymbol = false) => sb.context.builtins .getMembers(instanceName, (call) => builtins_1.isBuiltinInstanceMemberCall(call) || builtins_1.isBuiltinInstanceMemberTemplate(call), (call) => (typescript_1.default.isCallExpression(expression) && builtins_1.isBuiltinInstanceMemberCall(call) && call.canCall(sb, expr, expression, optionsIn)) || (typescript_1.default.isTaggedTemplateExpression(expression) && builtins_1.isBuiltinInstanceMemberTemplate(call) && call.canCall(sb, expr, expression, optionsIn)), useSymbol) .map(([propName, builtin]) => ({ condition: () => { sb.emitOp(prop, 'DUP'); sb.emitPushString(prop, propName); sb.emitOp(prop, 'EQUAL'); }, whenTrue: () => { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); handleBuiltinMemberCall(builtin, expr, true); }, })); const throwInnerTypeError = (innerOptions) => { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); throwTypeError(innerOptions); }; const createHandleProp = (handleString, handleNumber, handleSymbol) => (innerOptions) => { sb.visit(prop, innerOptions); sb.emitHelper(prop, innerOptions, sb.helpers.forBuiltinType({ type: propType, array: throwInnerTypeError, arrayStorage: throwInnerTypeError, boolean: throwInnerTypeError, buffer: throwInnerTypeError, null: throwInnerTypeError, number: handleNumber, object: throwInnerTypeError, string: handleString, symbol: handleSymbol, undefined: throwInnerTypeError, map: throwInnerTypeError, mapStorage: throwInnerTypeError, set: throwInnerTypeError, setStorage: throwInnerTypeError, error: throwInnerTypeError, forwardValue: throwInnerTypeError, iteratorResult: throwInnerTypeError, iterable: throwInnerTypeError, iterableIterator: throwInnerTypeError, transaction: throwInnerTypeError, output: throwInnerTypeError, attribute: throwInnerTypeError, input: throwInnerTypeError, account: throwInnerTypeError, asset: throwInnerTypeError, contract: throwInnerTypeError, header: throwInnerTypeError, block: throwInnerTypeError, })); }; const createProcessBuiltin = (instanceName) => { const handleStringBase = (innerInnerOptions) => { sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getCallCases(instanceName, false), () => { throwInnerTypeError(innerInnerOptions); })); }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); handleStringBase(innerInnerOptions); }; const handleNumber = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.toString({ type: propType, knownType: constants_1.Types.Number })); handleStringBase(innerInnerOptions); }; const handleSymbol = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getCallCases(instanceName, true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const createProcessArray = () => { const handleNumberBase = (innerInnerOptions) => { sb.emitOp(expr, 'NIP'); handleArguments(innerInnerOptions); sb.emitOp(expr, 'ROT'); sb.emitOp(expr, 'ROT'); sb.emitOp(expr, 'OVER'); sb.emitOp(expr, 'SWAP'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.getArrayIndex); sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })); }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getCallCases('Array', false), () => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.wrapString); sb.emitHelper(prop, innerInnerOptions, sb.helpers.toNumber({ type: propType, knownType: constants_1.Types.String })); handleNumberBase(innerInnerOptions); })); }; const handleNumber = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapNumber); handleNumberBase(innerInnerOptions); }; const handleSymbol = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getCallCases('Array', true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const processObject = (innerOptions) => { const handleStringBase = (innerInnerOptions) => { sb.emitHelper(expr, innerInnerOptions, sb.helpers.getPropertyObjectProperty); isOptionalChain ? callIfNotNullOrUndefined({ innerOptions, argsNum: 3, callback: () => sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })), }) : sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })); }; const handleNumber = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.toString({ type: propType, knownType: constants_1.Types.Number })); handleStringBase(innerInnerOptions); }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); handleStringBase(innerInnerOptions); }; const handleSymbol = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol); sb.emitHelper(expr, innerInnerOptions, sb.helpers.getSymbolObjectProperty); isOptionalChain ? callIfNotNullOrUndefined({ innerOptions, argsNum: 3, callback: () => sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })), }) : sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: true })); }; handleArguments(innerOptions); sb.emitOp(expression, 'ROT'); sb.emitOp(expression, 'ROT'); sb.visit(prop, innerOptions); sb.emitHelper(prop, innerOptions, sb.helpers.forBuiltinType({ type: propType, array: throwInnerTypeError, arrayStorage: throwInnerTypeError, boolean: throwInnerTypeError, buffer: throwInnerTypeError, null: throwInnerTypeError, number: handleNumber, object: throwInnerTypeError, string: handleString, symbol: handleSymbol, undefined: throwInnerTypeError, map: throwInnerTypeError, mapStorage: throwInnerTypeError, set: throwInnerTypeError, setStorage: throwInnerTypeError, error: throwInnerTypeError, forwardValue: throwInnerTypeError, iteratorResult: throwInnerTypeError, iterable: throwInnerTypeError, iterableIterator: throwInnerTypeError, transaction: throwInnerTypeError, output: throwInnerTypeError, attribute: throwInnerTypeError, input: throwInnerTypeError, account: throwInnerTypeError, asset: throwInnerTypeError, contract: throwInnerTypeError, header: throwInnerTypeError, block: throwInnerTypeError, })); }; const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn)); handlePropertyVisit(value, options); sb.emitHelper(value, options, sb.helpers.forBuiltinType({ type: valueType, array: createProcessArray(), arrayStorage: createProcessBuiltin('ArrayStorage'), boolean: createProcessBuiltin('Boolean'), buffer: createProcessBuiltin('Buffer'), null: throwTypeError, number: createProcessBuiltin('Number'), object: processObject, string: createProcessBuiltin('String'), symbol: createProcessBuiltin('Symbol'), undefined: throwTypeError, map: createProcessBuiltin('Map'), mapStorage: createProcessBuiltin('MapStorage'), set: createProcessBuiltin('Set'), setStorage: createProcessBuiltin('SetStorage'), error: createProcessBuiltin('Error'), forwardValue: createProcessBuiltin('ForwardValue'), iteratorResult: createProcessBuiltin('IteratorResult'), iterable: createProcessBuiltin('Iterable'), iterableIterator: createProcessBuiltin('IterableIterator'), transaction: createProcessBuiltin('TransactionBase'), output: createProcessBuiltin('Output'), attribute: createProcessBuiltin('AttributeBase'), input: createProcessBuiltin('Input'), account: createProcessBuiltin('Account'), asset: createProcessBuiltin('Asset'), contract: createProcessBuiltin('Contract'), header: createProcessBuiltin('Header'), block: createProcessBuiltin('Block'), })); } else if (typescript_1.default.isCallExpression(expression)) { const value = ts_utils_1.tsUtils.expression.getExpression(expression); const valueType = sb.context.analysis.getType(value); const processCall = () => { sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: false })); }; const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn)); handleArguments(options); sb.visit(expr, options); sb.emitHelper(value, options, sb.helpers.forBuiltinType({ type: valueType, array: throwTypeError, arrayStorage: throwTypeError, boolean: throwTypeError, buffer: throwTypeError, null: throwTypeErrorUnlessOptionalChain, number: throwTypeError, object: processCall, string: throwTypeError, symbol: throwTypeError, undefined: throwTypeErrorUnlessOptionalChain, map: throwTypeError, mapStorage: throwTypeError, set: throwTypeError, setStorage: throwTypeError, error: throwTypeError, forwardValue: throwTypeError, iteratorResult: throwTypeError, iterable: throwTypeError, iterableIterator: throwTypeError, transaction: throwTypeError, output: throwTypeError, attribute: throwTypeError, input: throwTypeError, account: throwTypeError, asset: throwTypeError, contract: throwTypeError, header: throwTypeError, block: throwTypeError, })); } else { const options = sb.pushValueOptions(sb.noSetValueOptions(optionsIn)); handleArguments(options); sb.visit(expr, options); sb.emitHelper(expr, optionsIn, sb.helpers.invokeCall({ bindThis: false })); } } } exports.CallLikeHelper = CallLikeHelper; //# sourceMappingURL=CallLikeHelper.js.map