UNPKG

@neo-one/smart-contract-compiler

Version:

NEO•ONE TypeScript smart contract compiler.

284 lines (282 loc) 13.5 kB
import { tsUtils } from '@neo-one/ts-utils'; import ts from 'typescript'; import { DiagnosticCode } from '../../DiagnosticCode'; import { DiagnosticMessage } from '../../DiagnosticMessage'; import { InternalObjectProperty } from '../constants'; import { NodeCompiler } from '../NodeCompiler'; export class ClassDeclarationCompiler extends NodeCompiler { constructor() { super(...arguments); this.kind = ts.SyntaxKind.ClassDeclaration; } visitNode(sb, decl, optionsIn) { if (sb.context.analysis.isSmartContract(decl)) { return; } let options = sb.pushValueOptions(sb.noSuperClassOptions(optionsIn)); const name = sb.scope.add(tsUtils.node.getNameOrThrow(decl)); const extendsExpr = tsUtils.class_.getExtends(decl); let superClassIn; if (extendsExpr !== undefined) { superClassIn = sb.scope.addUnique(); options = sb.superClassOptions(options, superClassIn); sb.visit(tsUtils.expression.getExpression(extendsExpr), options); sb.scope.set(sb, extendsExpr, options, superClassIn); } const superClass = superClassIn; const switchProperty = (node, innerOptions, handleString, handleSymbol) => { const nameNode = tsUtils.node.getNameNode(node); if (ts.isComputedPropertyName(nameNode)) { const expr = tsUtils.expression.getExpression(nameNode); const throwTypeError = (innerInnerOptions) => { sb.emitOp(node, 'DROP'); sb.emitHelper(node, innerInnerOptions, sb.helpers.throwTypeError); }; const processString = (innerInnerOptions) => { sb.emitHelper(node, innerInnerOptions, sb.helpers.unwrapString); handleString(innerInnerOptions); }; const processSymbol = (innerInnerOptions) => { sb.emitHelper(node, innerInnerOptions, sb.helpers.unwrapSymbol); handleSymbol(innerInnerOptions); }; sb.visit(expr, sb.pushValueOptions(innerOptions)); sb.emitHelper(expr, innerOptions, sb.helpers.forBuiltinType({ type: sb.context.analysis.getType(expr), array: throwTypeError, arrayStorage: throwTypeError, boolean: throwTypeError, buffer: throwTypeError, null: throwTypeError, number: throwTypeError, object: throwTypeError, string: processString, symbol: processSymbol, undefined: throwTypeError, 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 { sb.emitPushString(node, tsUtils.node.getName(node)); handleString(innerOptions); } }; const addProperty = (property, innerOptions) => { const initializer = tsUtils.initializer.getInitializer(property); const propNode = initializer === undefined ? property : initializer; sb.emitOp(propNode, 'DUP'); switchProperty(property, innerOptions, (innerInnerOptions) => { if (initializer === undefined) { sb.emitHelper(propNode, sb.pushValueOptions(innerInnerOptions), sb.helpers.wrapUndefined); } else { sb.visit(initializer, sb.pushValueOptions(innerInnerOptions)); } sb.emitHelper(propNode, innerOptions, sb.helpers.setDataPropertyObjectProperty); }, (innerInnerOptions) => { if (initializer === undefined) { sb.emitHelper(propNode, sb.pushValueOptions(innerInnerOptions), sb.helpers.wrapUndefined); } else { sb.visit(initializer, sb.pushValueOptions(innerInnerOptions)); } sb.emitHelper(propNode, innerOptions, sb.helpers.setDataSymbolObjectProperty); }); }; sb.emitHelper(decl, options, sb.helpers.createConstructArray({ withScope: true, body: (innerOptionsIn) => { const innerOptions = sb.pushValueOptions(innerOptionsIn); const ctorImpl = tsUtils.class_.getConcreteConstructor(decl); const ctorNode = ctorImpl === undefined ? decl : ctorImpl; if (ctorImpl !== undefined) { sb.emitHelper(ctorImpl, innerOptions, sb.helpers.parameters({ params: tsUtils.parametered.getParameters(ctorImpl) })); } else if (superClass !== undefined && extendsExpr !== undefined) { sb.scope.getThis(sb, extendsExpr, innerOptions); sb.scope.get(sb, extendsExpr, innerOptions, superClass); sb.emitHelper(extendsExpr, sb.noPushValueOptions(innerOptions), sb.helpers.invokeConstruct()); } else { sb.emitOp(decl, 'DROP'); } sb.scope.getThis(sb, ctorNode, innerOptions); tsUtils.class_ .getConcreteInstanceProperties(decl) .filter(ts.isPropertyDeclaration) .forEach((property) => { addProperty(property, innerOptions); }); sb.emitOp(ctorNode, 'DROP'); if (ctorImpl !== undefined) { sb.visit(tsUtils.body.getBodyOrThrow(ctorImpl), sb.noPushValueOptions(innerOptions)); } }, })); sb.emitHelper(decl, options, sb.helpers.createFunctionObject({ property: InternalObjectProperty.Construct, })); sb.emitOp(decl, 'DUP'); sb.emitPushString(decl, 'prototype'); sb.emitOp(decl, 'OVER'); sb.emitHelper(decl, options, sb.helpers.createObject); sb.emitOp(decl, 'TUCK'); sb.emitPushString(decl, 'constructor'); sb.emitOp(decl, 'ROT'); sb.emitHelper(decl, options, sb.helpers.setDataPropertyObjectProperty); const addMethod = (method) => { const visit = (innerOptions) => { sb.emitHelper(method, innerOptions, sb.helpers.createCallArray); sb.emitHelper(method, innerOptions, sb.helpers.createFunctionObject({ property: InternalObjectProperty.Call, })); }; sb.emitOp(method, 'DUP'); switchProperty(method, options, (innerOptions) => { visit(innerOptions); sb.emitHelper(method, innerOptions, sb.helpers.setDataPropertyObjectProperty); }, (innerOptions) => { visit(innerOptions); sb.emitHelper(method, innerOptions, sb.helpers.setDataSymbolObjectProperty); }); }; tsUtils.class_.getConcreteInstanceMethods(decl).forEach((method) => { addMethod(method); }); tsUtils.class_.getConcreteMembers(decl).forEach((member) => { const decorators = tsUtils.decoratable.getDecoratorsArray(member); if (decorators.length > 0) { sb.context.reportError(decorators[0], DiagnosticCode.UnsupportedSyntax, DiagnosticMessage.UnsupportedDecorator); } }); const addSetAccessor = (accessor) => { const visit = (innerOptions) => { sb.emitHelper(accessor, innerOptions, sb.helpers.createCallArray); sb.emitHelper(accessor, innerOptions, sb.helpers.createFunctionObject({ property: InternalObjectProperty.Call, })); const getAccessor = tsUtils.accessor.getGetAccessor(accessor); const hasGet = getAccessor !== undefined; if (getAccessor !== undefined) { sb.emitHelper(getAccessor, innerOptions, sb.helpers.createCallArray); sb.emitHelper(getAccessor, innerOptions, sb.helpers.createFunctionObject({ property: InternalObjectProperty.Call, })); } return hasGet; }; sb.emitOp(accessor, 'DUP'); switchProperty(accessor, options, (innerOptions) => { const hasGet = visit(innerOptions); sb.emitHelper(accessor, options, sb.helpers.setAccessorPropertyObjectProperty({ hasSet: true, hasGet, })); }, (innerOptions) => { const hasGet = visit(innerOptions); sb.emitHelper(accessor, options, sb.helpers.setAccessorSymbolObjectProperty({ hasSet: true, hasGet, })); }); }; tsUtils.class_ .getConcreteInstanceMembers(decl) .filter(ts.isSetAccessor) .forEach((accessor) => { addSetAccessor(accessor); }); const addGetAccessor = (accessor) => { const visit = (innerOptions) => { sb.emitHelper(accessor, innerOptions, sb.helpers.createCallArray); sb.emitHelper(accessor, innerOptions, sb.helpers.createFunctionObject({ property: InternalObjectProperty.Call, })); }; sb.emitOp(accessor, 'DUP'); switchProperty(accessor, options, (innerOptions) => { visit(innerOptions); sb.emitHelper(accessor, options, sb.helpers.setAccessorPropertyObjectProperty({ hasSet: false, hasGet: true, })); }, (innerOptions) => { visit(innerOptions); sb.emitHelper(accessor, options, sb.helpers.setAccessorSymbolObjectProperty({ hasSet: false, hasGet: true, })); }); }; tsUtils.class_ .getConcreteInstanceMembers(decl) .filter(ts.isGetAccessor) .filter((accessor) => tsUtils.accessor.getSetAccessor(accessor) === undefined) .forEach((accessor) => { addGetAccessor(accessor); }); if (superClass !== undefined && extendsExpr !== undefined) { sb.emitOp(extendsExpr, 'DUP'); sb.emitPushString(extendsExpr, '__proto__'); sb.scope.get(sb, extendsExpr, options, superClass); sb.emitPushString(extendsExpr, 'prototype'); sb.emitHelper(extendsExpr, options, sb.helpers.getPropertyObjectProperty); sb.emitHelper(extendsExpr, options, sb.helpers.setDataPropertyObjectProperty); } sb.emitHelper(decl, options, sb.helpers.setDataPropertyObjectProperty); tsUtils.class_ .getConcreteStaticProperties(decl) .filter(ts.isPropertyDeclaration) .forEach((property) => { addProperty(property, options); }); tsUtils.class_.getConcreteStaticMethods(decl).forEach((method) => { addMethod(method); }); tsUtils.class_ .getConcreteStaticMembers(decl) .filter(ts.isSetAccessor) .forEach((accessor) => { addSetAccessor(accessor); }); tsUtils.class_ .getConcreteStaticMembers(decl) .filter(ts.isGetAccessor) .filter((accessor) => tsUtils.accessor.getSetAccessor(accessor) === undefined) .forEach((accessor) => { addGetAccessor(accessor); }); if (superClass !== undefined && extendsExpr !== undefined) { sb.emitOp(extendsExpr, 'DUP'); sb.emitPushString(extendsExpr, '__proto__'); sb.scope.get(sb, extendsExpr, options, superClass); sb.emitHelper(extendsExpr, options, sb.helpers.setDataPropertyObjectProperty); } if (tsUtils.modifier.isNamedExport(decl) || tsUtils.modifier.isDefaultExport(decl)) { sb.emitOp(decl, 'DUP'); sb.emitHelper(decl, options, sb.helpers.exportSingle({ name: tsUtils.modifier.isNamedExport(decl) ? tsUtils.node.getNameOrThrow(decl) : undefined, defaultExport: tsUtils.modifier.isDefaultExport(decl), })); } sb.scope.set(sb, decl, options, name); } } //# sourceMappingURL=ClassDeclarationCompiler.js.map