import { FieldDeclaration , NamedTypeNode , NodeKind , ParameterNode , TypeNode , Element , FieldPrototype , FunctionPrototype , Range , DecoratorNode , FunctionDeclaration } from "assemblyscript" ; import { AstUtil , DecoratorUtil , ElementUtil , RangeUtil } from "../utils/utils" ; import { Strings } from "../utils/primitiveutil" ; import { ArgumentSpec , ConstructorSpec , MessageSpec , TypeSpec } from "contract-metadata/src" ; import { KeySelector } from "../preprocess/selector" ; import { MetadataUtil } from "../utils/metadatautil" ; import { ContractDecoratorKind } from "../enums/decorator" ; import { FieldDefHelper , TypeHelper } from "../utils/typeutil" ; import { TypeKindEnum } from "../enums/customtype" ; import { NamedTypeNodeDef } from "./typedef" ; export class DecoratorsInfo { decorators : DecoratorNode [ ] | null ; isIgnore = false ; isTopic = false ; isPacked = false ; capacity = 0 ; constructor ( decorators ) { this . decorators = decorators ; if ( this . decorators ) { for ( let decorator of this . decorators ) { if ( DecoratorUtil . isDecoratorKind ( decorator , ContractDecoratorKind . IGNORE ) ) { this . isIgnore = true ; } if ( DecoratorUtil . isDecoratorKind ( decorator , ContractDecoratorKind . TOPIC ) ) { this . isTopic = true ; } if ( DecoratorUtil . isDecoratorKind ( decorator , ContractDecoratorKind . PACKED ) ) { this . isPacked = true ; let decratorDef = new DecoratorNodeDef ( decorator ) ; if ( decratorDef . pairs . has ( "capacity" ) ) { this . capacity = Number ( decratorDef . pairs . get ( "capacity" ) ) ; } } } } } } export class FieldDef { protected fieldPrototype : FieldPrototype ; name : string ; type ! : NamedTypeNodeDef ; selector : KeySelector ; varName : string ; doc : string [ ] ; declaration : FieldDeclaration ; decorators : DecoratorsInfo ; rangeString = "" ; constructor ( field ) { this . fieldPrototype = field ; this . name = field . name ; this . declaration = < FieldDeclaration > field . declaration ; this . rangeString = this . declaration . range . toString ( ) ; this . doc = DecoratorUtil . getDoc ( field . declaration ) ; this . varName = "_" + this . name ; this . decorators = new DecoratorsInfo ( this . fieldPrototype . declaration . decorators ) ; let storeKey = this . fieldPrototype . internalName + this . name ; this . selector = new KeySelector ( storeKey ) ; this . resolveField ( ) ; } /**
     *
     */ private resolveField ( ) { let fieldDeclaration = < FieldDeclaration > this . fieldPrototype . declaration ; let commonType = fieldDeclaration . type ; if ( commonType && commonType . kind == NodeKind . NAMEDTYPE ) { let typeNode = < NamedTypeNode > commonType ; this . type = new NamedTypeNodeDef ( this . fieldPrototype , typeNode ) ; } if ( this . type . typeKind == TypeKindEnum . ARRAY ) { let str = FieldDefHelper . getConcreteStorable ( this ) ; this . type . codecTypeAlias = FieldDefHelper . getStorableExport ( this ) ; this . type . instanceType = str ; this . type . capacity = this . decorators . capacity ; } if ( this . type . typeKind == TypeKindEnum . MAP ) { let str = FieldDefHelper . getConcreteStorable ( this ) ; this . type . codecTypeAlias = FieldDefHelper . getStorableExport ( this ) ; this . type . instanceType = str ; } } } export class TopicFieldDef extends FieldDef { isTopic = false ; constructor ( field ) { super ( field ) ; this . isTopic = ElementUtil . isTopicField ( field ) ; } } export class ParameterNodeDef { private parameterNode : ParameterNode ; name : string ; type : NamedTypeNodeDef ; constructor ( parent , parameterNode ) { this . parameterNode = parameterNode ; this . name = this . parameterNode . name . range . toString ( ) ; this . type = new NamedTypeNodeDef ( parent , < NamedTypeNode > this . parameterNode . type ) ; } generateTypeSeq ( typeNodeMap ) { this . type . genTypeSequence ( typeNodeMap ) ; } } export class DecoratorNodeDef { private decorator : DecoratorNode ; pairs : Map < string , string > ; constructor ( decorator ) { this . decorator = decorator ; this . pairs = new Map < string , string > ( ) ; if ( decorator . args ) { decorator . args . forEach ( expression => { if ( expression . kind == NodeKind . BINARY ) { let identifier = AstUtil . getIdentifier ( expression ) ; let val = AstUtil . getBinaryExprRight ( expression ) ; this . pairs . set ( identifier , val ) ; } } ) ; } } } /**
 * Doc decorator info
 */ export class DocDecoratorNodeDef extends DecoratorNodeDef { doc = "" ; constructor ( decorator ) { super ( decorator ) ; if ( this . pairs . has ( "desc" ) ) { this . doc = Strings . removeQuotation ( this . pairs . get ( "desc" ) || "" ) ; } else { DecoratorUtil . throwNoArguException ( decorator , "desc" ) ; } } } export class MessageDecoratorNodeDef extends DecoratorNodeDef { payable = false ; mutates = "true" ; selector = "" ; constructor ( decorator ) { super ( decorator ) ; if ( decorator . args ) { decorator . args . forEach ( expression => { let identifier = AstUtil . getIdentifier ( expression ) ; if ( identifier == 'payable' ) { this . payable = true ; } else if ( identifier == 'mutates' ) { this . mutates = AstUtil . getBinaryExprRight ( expression ) ; DecoratorUtil . checkMutates ( decorator , this . mutates ) ; } else if ( identifier == 'selector' ) { this . selector = Strings . removeQuotation ( AstUtil . getBinaryExprRight ( expression ) ) ; DecoratorUtil . checkSelecrot ( decorator , this . selector ) ; } else { DecoratorUtil . throwNoArguException ( decorator , identifier ) ; } } ) ; } if ( this . payable && this . mutates == 'false' ) { throw new Error ( `Decorator: ${ decorator . name . range . toString ( ) } arguments mutates and payable can only exist one. Trace: ${ RangeUtil . location ( decorator . range ) } ` ) ; } } } export class FunctionDef { protected funcProto : FunctionPrototype ; declaration : FunctionDeclaration ; parameters : ParameterNodeDef [ ] = [ ] ; methodName : string ; isReturnable = false ; isConstructor = false ; returnType : NamedTypeNodeDef | null = null ; doc : string [ ] ; rangeString = "" ; defaultVals : string [ ] = [ ] ; constructor ( funcPrototype ) { this . declaration = < FunctionDeclaration > funcPrototype . declaration ; this . doc = DecoratorUtil . getDoc ( funcPrototype . declaration ) ; this . funcProto = funcPrototype ; this . methodName = this . funcProto . name ; this . rangeString = this . declaration . range . toString ( ) ; this . resolve ( ) ; } resolve ( ) { let params = this . funcProto . functionTypeNode . parameters ; params . forEach ( param => { this . parameters . push ( new ParameterNodeDef ( this . funcProto , param ) ) ; } ) ; this . resolveReturnType ( ) ; } resolveReturnType ( ) { if ( this . funcProto . name == "constructor" ) { this . isConstructor = true ; return ; } let returnType = this . funcProto . functionTypeNode . returnType ; let returnTypeDesc = new NamedTypeNodeDef ( this . funcProto , < NamedTypeNode > returnType ) ; if ( returnTypeDesc . typeKind != TypeKindEnum . VOID ) { returnTypeDesc . codecType = TypeHelper . getCodecType ( returnTypeDesc . plainType ) ; this . isReturnable = true ; this . returnType = returnTypeDesc ; } else { this . returnType = null ; } } public genTypeSequence ( typeNodeMap ) { this . parameters . forEach ( item => { item . generateTypeSeq ( typeNodeMap ) ; } ) ; if ( this . isReturnable ) { this . returnType ! . genTypeSequence ( typeNodeMap ) ; } } } export class ConstructorDef extends FunctionDef { constructor ( funcPrototype ) { super ( funcPrototype ) ; AstUtil . checkPublic ( this . declaration ) ; if ( this . isReturnable ) { throw new Error ( `The method that marked by @constructor should return void type. Please check ${ RangeUtil . location ( this . declaration . range ) }` ) ; } } public createMetadata ( ) { let args = this . parameters . map ( item => { let type = new TypeSpec ( item . type . index , item . type . plainType ) ; return new ArgumentSpec ( type , item . name ) ; } ) ; return new ConstructorSpec ( [ this . methodName ] , new KeySelector ( this . methodName ) . short , args , this . doc ) ; } } export class MessageFunctionDef extends FunctionDef { messageDecorator : MessageDecoratorNodeDef ; bodyRange : Range ; mutatable = true ; selector : KeySelector ; metadata : MessageSpec ; constructor ( funcPrototype ) { super ( funcPrototype ) ; AstUtil . checkPublic ( this . declaration ) ; let msgDecorator = AstUtil . getSpecifyDecorator ( funcPrototype . declaration , ContractDecoratorKind . MESSAGE ) ; this . messageDecorator = new MessageDecoratorNodeDef ( msgDecorator ! ) ; this . selector = new KeySelector ( this . methodName ) ; this . bodyRange = this . funcProto . bodyNode ! . range ; if ( this . messageDecorator . mutates == "false" ) { this . mutatable = false ; } if ( this . messageDecorator . selector ) { this . selector . setShortHex ( this . messageDecorator . selector ) ; } this . metadata = this . createMetadata ( ) ; } public createMetadata ( ) { let args = this . parameters . map ( item => { let type = MetadataUtil . createTypeSpec ( item . type ) ; return new ArgumentSpec ( type ! , item . name ) ; } ) ; let msgSpec = new MessageSpec ( [ this . methodName ] , this . selector . short , args , MetadataUtil . createTypeSpec ( this . returnType ) , this . doc ) ; msgSpec . setMutates ( this . mutatable ) ; msgSpec . setPayable ( this . messageDecorator . payable ) ; return msgSpec ; } }