UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

357 lines (355 loc) 17.6 kB
import { tsUtils } from '@neo-one/ts-utils'; import ts from 'typescript'; import { DiagnosticCode } from '../../DiagnosticCode'; import { DiagnosticMessage } from '../../DiagnosticMessage'; import { isBuiltinInstanceMemberValue, isBuiltinMemberValue } from '../builtins'; import { Types } from '../constants'; import { NodeCompiler } from '../NodeCompiler'; export class ElementAccessExpressionCompiler extends NodeCompiler { constructor() { super(...arguments); this.kind = ts.SyntaxKind.ElementAccessExpression; } visitNode(sb, expr, optionsIn) { const isOptionalChain = ts.isOptionalChain(expr); const value = tsUtils.expression.getExpression(expr); const valueType = sb.context.analysis.getType(value); const prop = tsUtils.expression.getArgumentExpressionOrThrow(expr); const propType = sb.context.analysis.getType(prop); const handleBuiltin = (member, visited) => { if (isBuiltinInstanceMemberValue(member)) { member.emitValue(sb, expr, optionsIn, visited); return; } if (isBuiltinMemberValue(member)) { member.emitValue(sb, expr, optionsIn); return; } if (optionsIn.setValue) { sb.context.reportError(prop, DiagnosticCode.InvalidBuiltinModify, DiagnosticMessage.CannotModifyBuiltin); } else { sb.context.reportError(prop, DiagnosticCode.InvalidBuiltinReference, 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, 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: 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: 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: Types.ArrayStorage, keyType: undefined, knownKeyType: 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: 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: 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: 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'), })); } } //# sourceMappingURL=ElementAccessExpressionCompiler.js.map