UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

362 lines (360 loc) 18.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ElementAccessExpressionCompiler = void 0; const tslib_1 = require("tslib"); const ts_utils_1 = require("@neo-one/ts-utils"); 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 NodeCompiler_1 = require("../NodeCompiler"); class ElementAccessExpressionCompiler extends NodeCompiler_1.NodeCompiler { constructor() { super(...arguments); this.kind = typescript_1.default.SyntaxKind.ElementAccessExpression; } visitNode(sb, expr, optionsIn) { const isOptionalChain = typescript_1.default.isOptionalChain(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 handleBuiltin = (member, visited) => { if (builtins_1.isBuiltinInstanceMemberValue(member)) { member.emitValue(sb, expr, optionsIn, visited); return; } if (builtins_1.isBuiltinMemberValue(member)) { member.emitValue(sb, expr, optionsIn); return; } if (optionsIn.setValue) { sb.context.reportError(prop, DiagnosticCode_1.DiagnosticCode.InvalidBuiltinModify, DiagnosticMessage_1.DiagnosticMessage.CannotModifyBuiltin); } else { sb.context.reportError(prop, DiagnosticCode_1.DiagnosticCode.InvalidBuiltinReference, DiagnosticMessage_1.DiagnosticMessage.CannotReferenceBuiltinProperty); } }; const builtinProp = sb.context.builtins.getMember(value, prop); if (builtinProp !== undefined) { handleBuiltin(builtinProp, false); return; } const getValueCases = (name, useSymbol = false) => sb.context.builtins .getMembers(name, builtins_1.isBuiltinInstanceMemberValue, () => true, useSymbol) .map(([propName, builtin]) => ({ condition: () => { sb.emitOp(prop, 'DUP'); sb.emitPushString(prop, propName); sb.emitOp(prop, 'EQUAL'); }, whenTrue: () => { sb.emitOp(expr, 'DROP'); handleBuiltin(builtin, true); }, })); const throwTypeError = (innerOptions) => { sb.emitOp(expr, 'DROP'); sb.emitHelper(expr, innerOptions, sb.helpers.throwTypeError); }; const throwInnerTypeError = (innerOptions) => { sb.emitOp(expr, 'DROP'); throwTypeError(innerOptions); }; const processUndefined = (innerOptions) => { sb.emitOp(expr, 'DROP'); sb.emitHelper(expr, innerOptions, sb.helpers.wrapUndefined); }; const throwTypeErrorUnlessOptionalChain = (innerOptions) => { isOptionalChain ? processUndefined(innerOptions) : 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 = (name) => { const handleStringBase = (innerInnerOptions) => { sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases(name, 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(getValueCases(name, true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const createProcessArray = () => { const handleNumberBase = (innerInnerOptions) => { if (optionsIn.pushValue && optionsIn.setValue) { sb.emitPushInt(expr, 2); sb.emitOp(expr, 'PICK'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayIndex); } else if (optionsIn.pushValue) { sb.emitHelper(expr, innerInnerOptions, sb.helpers.getArrayIndex); } else if (optionsIn.setValue) { sb.emitOp(expr, 'ROT'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayIndex); } else { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); } }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('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(getValueCases('Array', true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const createProcessArrayStorage = () => { const handleNumberBase = (innerInnerOptions) => { if (optionsIn.pushValue && optionsIn.setValue) { sb.emitPushInt(expr, 2); sb.emitOp(expr, 'PICK'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayStorage); } else if (optionsIn.pushValue) { sb.emitHelper(expr, options, sb.helpers.wrapNumber); sb.emitHelper(expr, innerInnerOptions, sb.helpers.getStructuredStorage({ type: constants_1.Types.ArrayStorage, keyType: undefined, knownKeyType: constants_1.Types.Number, })); } else if (optionsIn.setValue) { sb.emitOp(expr, 'ROT'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setArrayStorage); } else { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); } }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('ArrayStorage', 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); sb.emitHelper(prop, innerInnerOptions, sb.helpers.coerceToInt); handleNumberBase(innerInnerOptions); }; const handleSymbol = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapSymbol); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('ArrayStorage', true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const createProcessBuffer = () => { const handleNumberBase = (innerInnerOptions) => { if (optionsIn.pushValue) { sb.emitHelper(expr, innerInnerOptions, sb.helpers.getBufferIndex); } else { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); } }; const handleString = (innerInnerOptions) => { sb.emitHelper(prop, innerInnerOptions, sb.helpers.unwrapString); sb.emitHelper(expr, innerInnerOptions, sb.helpers.case(getValueCases('Buffer', 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(getValueCases('Buffer', true), () => { throwInnerTypeError(innerInnerOptions); })); }; return createHandleProp(handleString, handleNumber, handleSymbol); }; const processObject = (innerOptions) => { const handleStringBase = (innerInnerOptions) => { if (optionsIn.pushValue && optionsIn.setValue) { sb.emitOp(expr, 'OVER'); sb.emitOp(expr, 'OVER'); sb.emitPushInt(expr, 4); sb.emitOp(expr, 'ROLL'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setPropertyObjectProperty); sb.emitHelper(expr, innerInnerOptions, sb.helpers.getPropertyObjectProperty); } else if (optionsIn.pushValue) { sb.emitHelper(expr, innerInnerOptions, sb.helpers.getPropertyObjectProperty); } else if (optionsIn.setValue) { sb.emitOp(expr, 'ROT'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setPropertyObjectProperty); } }; 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); if (optionsIn.pushValue && optionsIn.setValue) { sb.emitOp(expr, 'OVER'); sb.emitOp(expr, 'OVER'); sb.emitPushInt(expr, 4); sb.emitOp(expr, 'ROLL'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setSymbolObjectProperty); sb.emitHelper(expr, innerInnerOptions, sb.helpers.getSymbolObjectProperty); } else if (optionsIn.pushValue) { sb.emitHelper(expr, innerInnerOptions, sb.helpers.getSymbolObjectProperty); } else if (optionsIn.setValue) { sb.emitOp(expr, 'ROT'); sb.emitHelper(expr, innerInnerOptions, sb.helpers.setSymbolObjectProperty); } }; sb.visit(prop, innerOptions); if (!optionsIn.pushValue && !optionsIn.setValue) { sb.emitOp(expr, 'DROP'); sb.emitOp(expr, 'DROP'); return; } 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)); sb.visit(value, options); sb.emitHelper(value, options, sb.helpers.forBuiltinType({ type: valueType, array: createProcessArray(), arrayStorage: createProcessArrayStorage(), boolean: createProcessBuiltin('Boolean'), buffer: createProcessBuffer(), null: throwTypeErrorUnlessOptionalChain, number: createProcessBuiltin('Number'), object: processObject, string: createProcessBuiltin('String'), symbol: createProcessBuiltin('Symbol'), undefined: throwTypeErrorUnlessOptionalChain, 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'), })); } } exports.ElementAccessExpressionCompiler = ElementAccessExpressionCompiler; //# sourceMappingURL=ElementAccessExpressionCompiler.js.map