From 0edd9d24be9e73beb5e1305ddfc4a639d2e28eb7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 25 Jun 2014 19:39:32 +0400 Subject: [PATCH] Java to Kotlin converter: refactoring working with constructors (in progress) --- j2k/src/org/jetbrains/jet/j2k/Converter.kt | 77 ++++++++++++++----- .../jet/j2k/ast/AnonymousClassBody.kt | 2 +- j2k/src/org/jetbrains/jet/j2k/ast/Class.kt | 2 +- .../org/jetbrains/jet/j2k/ast/ClassBody.kt | 15 ++-- .../org/jetbrains/jet/j2k/ast/Constructors.kt | 42 ++++------ j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt | 2 +- j2k/src/org/jetbrains/jet/j2k/ast/Function.kt | 7 +- .../jet/j2k/visitors/ExpressionVisitor.kt | 5 +- 8 files changed, 87 insertions(+), 65 deletions(-) diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index 9c037e9e780..68e97c44eaa 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -122,22 +122,22 @@ public class Converter private(val project: Project, val settings: ConverterSett convertedMembers.remove(member) } - val primaryConstructor = convertedMembers.values().filterIsInstance(javaClass()).firstOrNull() - val secondaryConstructors = convertedMembers.values().filterIsInstance(javaClass()) - val useClassObject = shouldGenerateClassObject(psiClass, convertedMembers) val members = ArrayList() val classObjectMembers = ArrayList() + var primaryConstructorSignature: PrimaryConstructorSignature? = null for ((psiMember, member) in convertedMembers) { - if (member is SecondaryConstructor) continue if (member is PrimaryConstructor) { + assert(primaryConstructorSignature == null) + primaryConstructorSignature = member.signature() val initializer = member.initializer() if (initializer != null) { members.add(initializer) } } - else if (useClassObject && psiMember !is PsiClass && psiMember.hasModifierProperty(PsiModifier.STATIC)) { + else if (useClassObject && psiMember !is PsiClass && psiMember.hasModifierProperty(PsiModifier.STATIC) || + member is FactoryFunction) { classObjectMembers.add(member) } else { @@ -147,7 +147,7 @@ public class Converter private(val project: Project, val settings: ConverterSett val lBrace = LBrace().assignPrototype(psiClass.getLBrace()) val rBrace = RBrace().assignPrototype(psiClass.getRBrace()) - return ClassBody(primaryConstructor?.signature(), members, secondaryConstructors, classObjectMembers, lBrace, rBrace) + return ClassBody(primaryConstructorSignature, members, classObjectMembers, lBrace, rBrace) } // do not convert private static methods into class object if possible @@ -190,6 +190,9 @@ public class Converter private(val project: Project, val settings: ConverterSett if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) { classBody = generateArtificialPrimaryConstructor(name, classBody) } + else { + correctFactoryFunctions(classBody, psiClass.getName()!!) + } val baseClassParams: List = run { val superVisitor = SuperVisitor() @@ -216,22 +219,24 @@ public class Converter private(val project: Project, val settings: ConverterSett }.assignPrototype(psiClass) } + private fun ClassBody.factoryFunctions() = classObjectMembers.filterIsInstance(javaClass()) + private fun generateArtificialPrimaryConstructor(className: Identifier, classBody: ClassBody): ClassBody { assert(classBody.primaryConstructorSignature == null) val finalOrWithEmptyInitializerFields = classBody.members.filterIsInstance(javaClass()).filter { it.isVal || it.initializer.isEmpty } val initializers = HashMap() - for (constructor in classBody.secondaryConstructors) { + for (factoryFunction in classBody.factoryFunctions()) { for (field in finalOrWithEmptyInitializerFields) { initializers.put(field, getDefaultInitializer(field)) } - val newStatements = ArrayList() - for (statement in constructor.block!!.statements) { + val statements = ArrayList() + for (statement in factoryFunction.body!!.statements) { var keepStatement = true if (statement is AssignmentExpression) { val assignee = statement.left - if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == SecondaryConstructor.tempValName) { + if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == FactoryFunction.tempValName) { val name = (assignee.identifier as Identifier).name for (field in finalOrWithEmptyInitializerFields) { if (name == field.identifier.name) { @@ -245,21 +250,21 @@ public class Converter private(val project: Project, val settings: ConverterSett } if (keepStatement) { - newStatements.add(statement) + statements.add(statement) } - } val initializer = MethodCallExpression.buildNotNull(null, className.name, finalOrWithEmptyInitializerFields.map { initializers[it]!! }) initializer.assignNoPrototype() - val localVar = LocalVariable(SecondaryConstructor.tempValIdentifier(), + val localVar = LocalVariable(FactoryFunction.tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, initializer, true).assignNoPrototype() - newStatements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype()) - constructor.block = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() + statements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype()) + statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype()) + factoryFunction.body = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() } val parameters = finalOrWithEmptyInitializerFields.map { field -> @@ -271,7 +276,31 @@ public class Converter private(val project: Project, val settings: ConverterSett val parameterList = ParameterList(parameters).assignNoPrototype() val constructorSignature = PrimaryConstructorSignature(modifiers, parameterList).assignNoPrototype() val updatedMembers = classBody.members.filter { !finalOrWithEmptyInitializerFields.contains(it) } - return ClassBody(constructorSignature, updatedMembers, classBody.secondaryConstructors, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace) + return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace) + } + + private fun correctFactoryFunctions(classBody: ClassBody, className: String) { + for (factoryFunction in classBody.factoryFunctions()) { + val body = factoryFunction.body!! + val statements = ArrayList(body.statements) + + // searching for other constructor call in form "this(...)" + // it's not necessary the first statement because of statements inserted for writable parameters + for (i in statements.indices) { + val statement = statements[i] + if (statement is MethodCallExpression) { + if ((statement.methodExpression as? Identifier)?.name == "this") { + val constructorCall = MethodCallExpression.buildNotNull(null, className, statement.arguments).assignPrototypesFrom(statement) + val localVar = LocalVariable(FactoryFunction.tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, constructorCall, true).assignNoPrototype() + statements[i] = DeclarationStatement(listOf(localVar)).assignNoPrototype() + break + } + } + } + + statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype()) + factoryFunction.body = Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body) + } } private fun convertInitializer(initializer: PsiClassInitializer): Initializer { @@ -344,11 +373,11 @@ public class Converter private(val project: Project, val settings: ConverterSett return false } - private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet): Function { + private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet): Member { return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method) } - private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet): Function { + private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet): Member { val returnType = typeConverter.convertMethodReturnType(method) val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype() @@ -382,7 +411,13 @@ public class Converter private(val project: Project, val settings: ConverterSett else { val params = convertParameterList(method.getParameterList()) var body = postProcessBody(convertBlock(method.getBody())) - return SecondaryConstructor(this, annotations, modifiers, params, body) + val containingClass = method.getContainingClass() + val typeParameterList = convertTypeParameterList(containingClass?.getTypeParameterList()) + val factoryFunctionType = ClassType(containingClass?.declarationIdentifier() ?: Identifier.Empty, + typeParameterList.parameters, + Nullability.NotNull, + settings).assignNoPrototype() + return FactoryFunction(annotations, modifiers, factoryFunctionType, params, typeParameterList, body) } } else { @@ -404,7 +439,7 @@ public class Converter private(val project: Project, val settings: ConverterSett var params = convertParameterList(method.getParameterList()) val typeParameterList = convertTypeParameterList(method.getTypeParameterList()) var body = postProcessBody(convertBlock(method.getBody())) - return Function(this, method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false) + return Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false) } } @@ -499,7 +534,7 @@ public class Converter private(val project: Project, val settings: ConverterSett convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false)) } }).assignPrototype(constructor.getParameterList()) - return PrimaryConstructor(this, annotations, modifiers, parameterList, block).assignPrototype(constructor) + return PrimaryConstructor(annotations, modifiers, parameterList, block).assignPrototype(constructor) } private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair? { diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt index 0c9d2c3b953..2691210e640 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt @@ -21,6 +21,6 @@ import org.jetbrains.jet.j2k.CodeBuilder class AnonymousClassBody(body: ClassBody, val extendsTrait: Boolean) : Class(Identifier.Empty, Annotations.Empty, Modifiers.Empty, TypeParameterList.Empty, listOf(), listOf(), listOf(), body) { override fun generateCode(builder: CodeBuilder) { - body.append(builder, null) + body.append(builder) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt index ffd5749cb18..a0e809f9b13 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt @@ -41,7 +41,7 @@ open class Class( } appendBaseTypes(builder) typeParameterList.appendWhere(builder) - body.append(builder, this) + body.append(builder) } protected open val keyword: String diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt index 38207f1af23..eb6a4c16062 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt @@ -20,30 +20,29 @@ import org.jetbrains.jet.j2k.* abstract class Member(val annotations: Annotations, val modifiers: Modifiers) : Element() +//TODO: should be Element? class ClassBody ( val primaryConstructorSignature: PrimaryConstructorSignature?, val members: List, - val secondaryConstructors: List, val classObjectMembers: List, val lBrace: LBrace, val rBrace: RBrace) { - fun append(builder: CodeBuilder, containingClass: Class?) { - if (members.isEmpty() && classObjectMembers.isEmpty() && secondaryConstructors.isEmpty()) return + fun append(builder: CodeBuilder) { + if (members.isEmpty() && classObjectMembers.isEmpty()) return builder append " " append lBrace append "\n" builder.append(members, "\n") - appendClassObject(builder, containingClass, members.isNotEmpty()) + appendClassObject(builder, members.isNotEmpty()) builder append "\n" append rBrace } - private fun appendClassObject(builder: CodeBuilder, containingClass: Class?, blankLineBefore: Boolean) { - if (secondaryConstructors.isEmpty() && classObjectMembers.isEmpty()) return + private fun appendClassObject(builder: CodeBuilder, blankLineBefore: Boolean) { + if (classObjectMembers.isEmpty()) return if (blankLineBefore) builder.append("\n\n") - val factoryFunctions = secondaryConstructors.map { it.toFactoryFunction(containingClass) } - builder.append(factoryFunctions + classObjectMembers, "\n", "class object {\n", "\n}") + builder.append(classObjectMembers, "\n", "class object {\n", "\n}") } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt index 0e73d75b679..37a6dd55b7e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt @@ -16,37 +16,26 @@ package org.jetbrains.jet.j2k.ast -import java.util.ArrayList import org.jetbrains.jet.j2k.* import com.intellij.util.IncorrectOperationException -abstract class Constructor( - converter: Converter, - annotations: Annotations, - modifiers: Modifiers, - parameterList: ParameterList, - block: Block -) : Function(converter, Identifier.Empty, annotations, modifiers, ErrorType(), TypeParameterList.Empty, parameterList, block, false) { +class PrimaryConstructor(annotations: Annotations, + modifiers: Modifiers, + val parameterList: ParameterList, + val block: Block) + : Member(annotations, modifiers) { override fun generateCode(builder: CodeBuilder) { throw IncorrectOperationException() } -} - -class PrimaryConstructor(converter: Converter, - annotations: Annotations, - modifiers: Modifiers, - parameterList: ParameterList, - block: Block) - : Constructor(converter, annotations, modifiers, parameterList, block) { public fun initializer(): Initializer? { - return if (!block!!.isEmpty) - Initializer(block!!, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false)) + return if (!block.isEmpty) + Initializer(block, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false)) else null } public fun signature(): PrimaryConstructorSignature { - val noBody = block!!.isEmpty + val noBody = block.isEmpty val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = noBody, commentsInside = noBody) return PrimaryConstructorSignature(modifiers, parameterList).assignPrototypesFrom(this, inheritance) } @@ -63,13 +52,15 @@ class PrimaryConstructorSignature(val modifiers: Modifiers, val parameterList: P } } -class SecondaryConstructor(converter: Converter, - annotations: Annotations, - modifiers: Modifiers, - parameterList: ParameterList, - block: Block) - : Constructor(converter, annotations, modifiers, parameterList, block) { +class FactoryFunction(annotations: Annotations, + modifiers: Modifiers, + returnType: Type, + parameterList: ParameterList, + typeParameterList: TypeParameterList, + block: Block) +: Function(Identifier("create").assignNoPrototype(), annotations, modifiers, returnType, typeParameterList, parameterList, block, false) { + /* public fun toFactoryFunction(containingClass: Class?): Function { val statements = ArrayList(block?.statements ?: listOf()) statements.add(ReturnStatement(tempValIdentifier()).assignNoPrototype()) @@ -85,6 +76,7 @@ class SecondaryConstructor(converter: Converter, return Function(converter, Identifier("create").assignNoPrototype(), annotations, modifiers, returnType, typeParameterList, parameterList, newBlock, false).assignPrototypesFrom(this) } + */ class object { public val tempValName: String = "__" diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt index cb03da59a26..63804d3f685 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt @@ -37,6 +37,6 @@ class Enum( } builder append typeParameterList appendBaseTypes(builder) - body.append(builder, this) + body.append(builder) } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt index 8e2bc8e0f68..7e262aa10f7 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt @@ -19,14 +19,13 @@ package org.jetbrains.jet.j2k.ast import org.jetbrains.jet.j2k.* open class Function( - val converter: Converter, val name: Identifier, annotations: Annotations, modifiers: Modifiers, val `type`: Type, val typeParameterList: TypeParameterList, val parameterList: ParameterList, - var block: Block?, + var body: Block?, val isInTrait: Boolean ) : Member(annotations, modifiers) { @@ -59,8 +58,8 @@ open class Function( typeParameterList.appendWhere(builder) - if (block != null) { - builder append " " append block!! + if (body != null) { + builder append " " append body!! } } } diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt index ce69ce4eb05..581eaa0f9b8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.kt @@ -318,10 +318,7 @@ class ExpressionVisitor(private val converter: Converter, val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor() if (insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()) { - identifier = QualifiedExpression(SecondaryConstructor.tempValIdentifier(), Identifier(referencedName, isNullable).assignNoPrototype()) - } - else if (insideSecondaryConstructor && expression.isThisConstructorCall()) { - identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: "")).assignNoPrototype() + identifier = QualifiedExpression(FactoryFunction.tempValIdentifier(), Identifier(referencedName, isNullable).assignNoPrototype()) } else if (qualifier != null && qualifier.getType() is PsiArrayType && referencedName == "length") { identifier = Identifier("size", isNullable).assignNoPrototype()