diff --git a/typescript/ts-converter/src/AstConverter.ts b/typescript/ts-converter/src/AstConverter.ts index 04d7eb699..2b01ee0b3 100644 --- a/typescript/ts-converter/src/AstConverter.ts +++ b/typescript/ts-converter/src/AstConverter.ts @@ -26,7 +26,7 @@ import {AstExpressionConverter} from "./ast/AstExpressionConverter"; import {ExportContext} from "./ExportContext"; import {AstVisitor} from "./AstVisitor"; import {tsInternals} from "./TsInternals"; -import {ReferenceClauseDeclarationProto, ReferenceDeclarationProto} from "declarations"; +import {ReferenceClauseDeclarationProto, ReferenceDeclarationProto, AccessorDeclarationProto, MemberDeclarationProto} from "declarations"; export class AstConverter { private log = createLogger("AstConverter"); @@ -178,6 +178,43 @@ export class AstConverter { return null; } + convertAccessorDeclaration(declaration: ts.AccessorDeclaration): MemberDeclaration | null { + let typeParameterDeclarations: Array = this.convertTypeParams(declaration.typeParameters); + + let parameterDeclarations = declaration.parameters.map( + (param, count) => this.convertParameterDeclaration(param, count) + ); + + if (ts.isIdentifier(declaration.name)) { + let method = this.astFactory.createFunctionDeclaration( + declaration.name ? declaration.name.getText() : "", + parameterDeclarations, + declaration.type ? this.convertType(declaration.type) : this.createTypeDeclaration("Unit"), + typeParameterDeclarations, + this.convertModifiers(declaration.modifiers), + this.convertBlock(declaration.body), + "__NO_UID__" + ); + + let accessor = new AccessorDeclarationProto(); + accessor.setMethod(method); + if (ts.isGetAccessorDeclaration(declaration)) { + accessor.setAccess(AccessorDeclarationProto.ACCESS.READ) + } else if (ts.isSetAccessorDeclaration(declaration)) { + accessor.setAccess(AccessorDeclarationProto.ACCESS.WRITE) + } else { + throw Error("Invalid accessor" + declaration) + } + + let memberProto = new MemberDeclarationProto(); + memberProto.setAccessor(accessor); + return memberProto; + } + + return null; + } + + convertTypeParams(nativeTypeDeclarations: ts.NodeArray | undefined): Array { let typeParameterDeclarations: Array = []; @@ -653,6 +690,11 @@ export class AstConverter { this.convertConstructorDeclaration(memberDeclaration as ts.ConstructorDeclaration).map(member => { this.registerDeclaration(member, members); }); + } else if (ts.isSetAccessorDeclaration(memberDeclaration) || ts.isGetAccessorDeclaration(memberDeclaration)) { + let accessorDeclaration = this.convertAccessorDeclaration(memberDeclaration as ts.AccessorDeclaration); + if (accessorDeclaration != null) { + this.registerDeclaration(accessorDeclaration, members) + } } } diff --git a/typescript/ts-converter/src/ast/ast.ts b/typescript/ts-converter/src/ast/ast.ts index 45a8f3646..6b4a01856 100644 --- a/typescript/ts-converter/src/ast/ast.ts +++ b/typescript/ts-converter/src/ast/ast.ts @@ -1,4 +1,5 @@ import { + AccessorDeclarationProto, BlockDeclarationProto, CallSignatureDeclarationProto, ClassDeclarationProto, @@ -59,6 +60,7 @@ export type NameEntity = NameDeclarationProto; export type ParameterDeclaration = ParameterDeclarationProto; export type TypeDeclaration = ParameterValueDeclarationProto; export type PropertyDeclaration = PropertyDeclarationProto; +export type AccessorDeclaration = AccessorDeclarationProto; export type ReferenceEntity = ReferenceDeclarationProto; export type SourceBundle = SourceBundleDeclarationProto; export type SourceFileDeclaration = SourceFileDeclarationProto; diff --git a/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationLowering.kt b/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationLowering.kt index e871b32e4..d2c8bcd8a 100644 --- a/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationLowering.kt +++ b/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationLowering.kt @@ -1,6 +1,7 @@ package org.jetbrains.dukat.tsLowerings import org.jetbrains.dukat.ownerContext.NodeOwner +import org.jetbrains.dukat.tsmodel.AccessorDeclaration import org.jetbrains.dukat.tsmodel.ClassDeclaration import org.jetbrains.dukat.tsmodel.ClassLikeDeclaration import org.jetbrains.dukat.tsmodel.Declaration @@ -29,6 +30,7 @@ import org.jetbrains.dukat.tsmodel.types.UnionTypeDeclaration interface DeclarationLowering { fun lowerVariableDeclaration(declaration: VariableDeclaration, owner: NodeOwner): VariableDeclaration + fun lowerAccessorDeclaration(declaration: AccessorDeclaration, onwer:NodeOwner): AccessorDeclaration fun lowerFunctionDeclaration(declaration: FunctionDeclaration, owner: NodeOwner): FunctionDeclaration fun lowerClassDeclaration(declaration: ClassDeclaration, owner: NodeOwner): ClassDeclaration fun lowerInterfaceDeclaration(declaration: InterfaceDeclaration, owner: NodeOwner): InterfaceDeclaration diff --git a/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationTypeLowering.kt b/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationTypeLowering.kt index bcb8d1d9c..d5ecd4741 100644 --- a/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationTypeLowering.kt +++ b/typescript/ts-lowerings/src/org/jetbrains/dukat/tsLowerings/DeclarationTypeLowering.kt @@ -2,6 +2,7 @@ package org.jetbrains.dukat.tsLowerings import org.jetbrains.dukat.logger.Logging import org.jetbrains.dukat.ownerContext.NodeOwner +import org.jetbrains.dukat.tsmodel.AccessorDeclaration import org.jetbrains.dukat.tsmodel.CallSignatureDeclaration import org.jetbrains.dukat.tsmodel.ClassDeclaration import org.jetbrains.dukat.tsmodel.ClassLikeDeclaration @@ -77,6 +78,7 @@ interface DeclarationTypeLowering : DeclarationLowering { is MethodSignatureDeclaration -> lowerMethodSignatureDeclaration(declaration, newOwner) is CallSignatureDeclaration -> lowerCallSignatureDeclaration(declaration, newOwner) is IndexSignatureDeclaration -> lowerIndexSignatureDeclaration(declaration, newOwner) + is AccessorDeclaration -> lowerAccessorDeclaration(declaration, newOwner) else -> { logger.debug("[${this}] skipping ${declaration}") declaration @@ -94,6 +96,10 @@ interface DeclarationTypeLowering : DeclarationLowering { ) } + override fun lowerAccessorDeclaration(declaration: AccessorDeclaration, onwer:NodeOwner):AccessorDeclaration { + return declaration.copy() + } + override fun lowerFunctionDeclaration(declaration: FunctionDeclaration, owner: NodeOwner): FunctionDeclaration { return declaration.copy( parameters = declaration.parameters.map { parameter -> lowerParameterDeclaration(parameter, owner.wrap(declaration)) }, diff --git a/typescript/ts-model-proto/src/Declarations.proto b/typescript/ts-model-proto/src/Declarations.proto index dfd46d556..f1cfd0fb9 100644 --- a/typescript/ts-model-proto/src/Declarations.proto +++ b/typescript/ts-model-proto/src/Declarations.proto @@ -138,6 +138,15 @@ message PropertyDeclarationProto { repeated ModifierDeclarationProto modifiers = 6; } +message AccessorDeclarationProto { + FunctionDeclarationProto method = 1; + enum ACCESS { + READ = 0; + WRITE = 1; + } + ACCESS access = 2; +} + message ModifierDeclarationProto { string token = 1; } @@ -156,6 +165,7 @@ message MemberDeclarationProto { MethodSignatureDeclarationProto methodSignature = 5; PropertyDeclarationProto property = 6; ObjectLiteralDeclarationProto objectLiteral = 7; + AccessorDeclarationProto accessor = 8; } } diff --git a/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/AccessorDeclaration.kt b/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/AccessorDeclaration.kt new file mode 100644 index 000000000..37d8729c1 --- /dev/null +++ b/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/AccessorDeclaration.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dukat.tsmodel + +data class AccessorDeclaration( + val method: FunctionDeclaration, + val access: ACCESS +) : MemberDeclaration { + enum class ACCESS { + WRITE, + READ + } +} \ No newline at end of file diff --git a/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/factory/convertProtobuf.kt b/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/factory/convertProtobuf.kt index f6ca09fda..403701dfb 100644 --- a/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/factory/convertProtobuf.kt +++ b/typescript/ts-model/src/org/jetbrains/dukat/tsmodel/factory/convertProtobuf.kt @@ -41,6 +41,7 @@ import org.jetbrains.dukat.tsmodel.types.TupleDeclaration import org.jetbrains.dukat.tsmodel.types.TypeDeclaration import org.jetbrains.dukat.tsmodel.types.TypeParamReferenceDeclaration import org.jetbrains.dukat.tsmodel.types.UnionTypeDeclaration +import org.jetbrains.dukat.tsmodelproto.AccessorDeclarationProto import org.jetbrains.dukat.tsmodelproto.ArrayLiteralExpressionDeclarationProto import org.jetbrains.dukat.tsmodelproto.BigIntLiteralExpressionDeclarationProto import org.jetbrains.dukat.tsmodelproto.BinaryExpressionDeclarationProto @@ -167,6 +168,11 @@ fun InterfaceDeclarationProto.convert(): InterfaceDeclaration { ) } +fun AccessorDeclarationProto.convert(): AccessorDeclaration { + val access:AccessorDeclaration.ACCESS = if (this.accessValue == AccessorDeclarationProto.ACCESS.WRITE_VALUE) AccessorDeclaration.ACCESS.WRITE else AccessorDeclaration.ACCESS.READ + return AccessorDeclaration(method.convert(),access) +} + fun BlockDeclarationProto.convert(): BlockDeclaration { return BlockDeclaration( statements = statementsList.map { it.convert() } @@ -350,6 +356,7 @@ fun MemberDeclarationProto.convert(): MemberDeclaration { hasProperty() -> property.convert() hasIndexSignature() -> indexSignature.convert() hasCallSignature() -> callSignature.convert() + hasAccessor() -> accessor.convert() else -> throw Exception("unknown MemberEntityProto: ${this}") } } diff --git a/typescript/ts-node-introduction/src/org/jetbrains/dukat/nodeIntroduction/introduceNodes.kt b/typescript/ts-node-introduction/src/org/jetbrains/dukat/nodeIntroduction/introduceNodes.kt index 45b630a73..d0ff5ed03 100644 --- a/typescript/ts-node-introduction/src/org/jetbrains/dukat/nodeIntroduction/introduceNodes.kt +++ b/typescript/ts-node-introduction/src/org/jetbrains/dukat/nodeIntroduction/introduceNodes.kt @@ -35,6 +35,30 @@ private class LowerDeclarationsToNodes(private val fileName: String, private val private fun PropertyDeclaration.isStatic() = modifiers.contains(ModifierDeclaration.STATIC_KEYWORD) + fun convertPropertyFromAccessorDeclaration(declaration: AccessorDeclaration):PropertyNode { + var type = declaration.method.type + if (declaration.access == AccessorDeclaration.ACCESS.WRITE) { + val customType = declaration.method.parameters.firstOrNull()?.type + ?: { + raiseConcern("Invalid parameters: " + declaration.method.parameters, {}) + type + }() + type = customType + } + + return PropertyNode( + declaration.method.name, + type, + convertTypeParameters(declaration.method.typeParameters), + + declaration.method.isStatic(), + declaration.access == AccessorDeclaration.ACCESS.READ, + declaration.access == AccessorDeclaration.ACCESS.WRITE, + + true + ) + } + fun convertPropertyDeclaration(declaration: PropertyDeclaration): PropertyNode { return PropertyNode( declaration.name, @@ -411,6 +435,7 @@ private class LowerDeclarationsToNodes(private val fileName: String, private val true, null )) + is AccessorDeclaration -> listOf(convertPropertyFromAccessorDeclaration(declaration)) is MethodSignatureDeclaration -> listOf(lowerMethodSignatureDeclaration(declaration)).mapNotNull { it } is CallSignatureDeclaration -> listOf(declaration.convert()) is PropertyDeclaration -> listOf(convertPropertyDeclaration(declaration)) diff --git a/typescript/ts-node-lowering/src/org/jetrbains/dukat/nodeLowering/lowerings/introduceModels.kt b/typescript/ts-node-lowering/src/org/jetrbains/dukat/nodeLowering/lowerings/introduceModels.kt index af39cd814..fe5f43d70 100644 --- a/typescript/ts-node-lowering/src/org/jetrbains/dukat/nodeLowering/lowerings/introduceModels.kt +++ b/typescript/ts-node-lowering/src/org/jetrbains/dukat/nodeLowering/lowerings/introduceModels.kt @@ -285,8 +285,8 @@ private class DocumentConverter(private val documentRootNode: DocumentRootNode, static = static, override = null, immutable = getter && !setter, - getter = false, - setter = false, + getter = getter, + setter = setter, open = open ) else -> raiseConcern("unprocessed MemberNode: ${this}") { null } @@ -457,8 +457,52 @@ private class DocumentConverter(private val documentRootNode: DocumentRootNode, } } + /** + * in kotlin writable accessor is readable too + * + * Convert records: + * "write accessor" + "read accessor" => "write accessor" + * "read accessor" => "read accessor" + * "write accessor" => remove (write and not read accessor is not possible in kotlin) + */ + private fun filterAccessors(className:String, source: List): List { + val writeAccessors = mutableMapOf() + val readAccessors = mutableMapOf() + + for (member in source) { + if (member is PropertyNode) { + if (member.getter) { + readAccessors.put(member.name, member) + } + if (member.setter) { + writeAccessors.put(member.name, member) + } + } + } + + val target = mutableListOf() + source.forEach { member-> + if (member is PropertyNode) { + val write = writeAccessors.get(member.name) + val read = readAccessors.get(member.name) + if (write != null && read != null) { + target.add(write) + } else if (read != null) { + target.add(read) + } else if (write != null) { + logger.warn("write only accessor: $className::${member.name} now not supported") + } else { + target.add(member) + } + } else { + target.add(member) + } + } + return target + } + private fun ClassNode.convertToClassModel(): TopLevelModel { - val membersSplitted = split(members) + val membersSplitted = split(filterAccessors(name.toString(), members)) val generatedMethods = mutableListOf() val parentModelEntities = convertParentEntities(parentEntities) {