From e997557ba19535541d6852f7229095f751689261 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 26 Jun 2014 22:55:49 +0400 Subject: [PATCH] Java to Kotlin converter: refactored code to perform primary constructor detection only once --- .../jetbrains/jet/j2k/ConstructorConverter.kt | 116 +++++++++--------- j2k/src/org/jetbrains/jet/j2k/Converter.kt | 30 ++--- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt b/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt index 658b4651a0e..3fe3236f359 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt @@ -24,18 +24,62 @@ import java.util.HashMap import java.util.ArrayList import java.util.HashSet -class ConstructorConverter(private val converter: Converter) { +class ConstructorConverter(private val psiClass: PsiClass, private val converter: Converter) { private val typeConverter = converter.typeConverter private val tempValName: String = "__" private fun tempValIdentifier(): Identifier = Identifier(tempValName, false).assignNoPrototype() + private val className = psiClass.getName()!! + private val constructors = psiClass.getConstructors() + private val primaryConstructor: PsiMethod? = run { + when (constructors.size) { + 0 -> null + + 1 -> constructors.single() + + else -> { + val toTargetConstructorMap = HashMap() + for (constructor in constructors) { + val firstStatement = constructor.getBody()?.getStatements()?.firstOrNull() + val refExpr = ((firstStatement as? PsiExpressionStatement) + ?.getExpression() as? PsiMethodCallExpression) + ?.getMethodExpression() + if (refExpr != null && refExpr.getCanonicalText() == "this") { + val target = refExpr.resolve() as? PsiMethod + if (target != null && target.isConstructor()) { + val finalTarget = toTargetConstructorMap[target] ?: target!!/*TODO: see KT-5335*/ + toTargetConstructorMap[constructor] = finalTarget + for (entry in toTargetConstructorMap.entrySet()) { + if (entry.getValue() == constructor) { + entry.setValue(finalTarget) + } + } + } + } + } + + val candidates = constructors.filter { it !in toTargetConstructorMap } + if (candidates.size == 1) { // there should be only one constructor which does not call other constructor + val candidate = candidates.single() + if (toTargetConstructorMap.values().all { it == candidate } /* all other constructors call our candidate (directly or indirectly)*/) + candidate + else + null + } + else { + null + } + } + } + } + public fun convertConstructor(constructor: PsiMethod, annotations: Annotations, modifiers: Modifiers, membersToRemove: MutableSet, postProcessBody: (Block) -> Block): Member { - if (constructor.isPrimaryConstructor()) { + if (constructor == primaryConstructor) { return convertPrimaryConstructor(constructor, annotations, modifiers, membersToRemove, postProcessBody) } else { @@ -165,17 +209,17 @@ class ConstructorConverter(private val converter: Converter) { return null } - public fun postProcessConstructors(classBody: ClassBody, psiClass: PsiClass): ClassBody { - if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) { - return generateArtificialPrimaryConstructor(psiClass.getName()!!, classBody) + public fun postProcessConstructors(classBody: ClassBody): ClassBody { + if (primaryConstructor == null && constructors.size > 1) { + return generateArtificialPrimaryConstructor(classBody) } else { - val updatedFunctions = replaceConstructorCallsInFactoryFunctions(classBody.factoryFunctions, psiClass.getName()!!) + val updatedFunctions = replaceConstructorCallsInFactoryFunctions(classBody.factoryFunctions) return ClassBody(classBody.primaryConstructorSignature, classBody.members, classBody.classObjectMembers, updatedFunctions, classBody.lBrace, classBody.rBrace) } } - private fun generateArtificialPrimaryConstructor(className: String, classBody: ClassBody): ClassBody { + private fun generateArtificialPrimaryConstructor(classBody: ClassBody): ClassBody { assert(classBody.primaryConstructorSignature == null) val fieldsToInitialize = classBody.members.filterIsInstance(javaClass()).filter { it.isVal } @@ -183,8 +227,8 @@ class ConstructorConverter(private val converter: Converter) { for (function in classBody.factoryFunctions) { val body = function.body!! // 2 cases: secondary constructor either calls another constructor or does not call any - val newStatements = replaceConstructorCallInFactoryFunction(body, className) ?: - insertCallToArtificialPrimary(body, className, fieldsToInitialize) + val newStatements = replaceConstructorCallInFactoryFunction(body) ?: + insertCallToArtificialPrimary(body, fieldsToInitialize) val newBody = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() updatedFactoryFunctions.add(function.withBody(newBody)) } @@ -203,10 +247,10 @@ class ConstructorConverter(private val converter: Converter) { return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, updatedFactoryFunctions, classBody.lBrace, classBody.rBrace) } - private fun replaceConstructorCallsInFactoryFunctions(functions: List, className: String): List { + private fun replaceConstructorCallsInFactoryFunctions(functions: List): List { return functions.map { function -> val body = function.body!! - val statements = replaceConstructorCallInFactoryFunction(body, className) + val statements = replaceConstructorCallInFactoryFunction(body) if (statements != null) { function.withBody(Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body)) } @@ -216,7 +260,7 @@ class ConstructorConverter(private val converter: Converter) { } } - private fun replaceConstructorCallInFactoryFunction(body: Block, className: String): List? { + private fun replaceConstructorCallInFactoryFunction(body: Block): List? { val statements = ArrayList(body.statements) // searching for other constructor call in form "this(...)" @@ -241,7 +285,7 @@ class ConstructorConverter(private val converter: Converter) { return null } - private fun insertCallToArtificialPrimary(body: Block, className: String, fieldsToInitialize: Collection): List { + private fun insertCallToArtificialPrimary(body: Block, fieldsToInitialize: Collection): List { val initializers = HashMap() for (field in fieldsToInitialize) { initializers.put(field, getDefaultInitializer(field)) @@ -287,50 +331,4 @@ class ConstructorConverter(private val converter: Converter) { } return statements } - - private fun PsiMethod.isPrimaryConstructor(): Boolean { - if (!isConstructor()) return false - val parent = getParent() - if (parent !is PsiClass) return false - return parent.getPrimaryConstructor() == this - } - - private fun PsiClass.getPrimaryConstructor(): PsiMethod? { - val constructors = getConstructors() - when (constructors.size) { - 0 -> return null - - 1 -> return constructors.single() - - else -> { - val toTargetConstructorMap = HashMap() - for (constructor in constructors) { - val firstStatement = constructor.getBody()?.getStatements()?.firstOrNull() - val refExpr = ((firstStatement as? PsiExpressionStatement) - ?.getExpression() as? PsiMethodCallExpression) - ?.getMethodExpression() - if (refExpr != null && refExpr.getCanonicalText() == "this") { - val target = refExpr.resolve() as? PsiMethod - if (target != null && target.isConstructor()) { - val finalTarget = toTargetConstructorMap[target] ?: target!!/*TODO: see KT-5335*/ - toTargetConstructorMap[constructor] = finalTarget - for (entry in toTargetConstructorMap.entrySet()) { - if (entry.getValue() == constructor) { - entry.setValue(finalTarget) - } - } - } - } - } - - val candidates = constructors.filter { it !in toTargetConstructorMap } - if (candidates.size != 1) return null // there should be only one constructor which does not call other constructor - val candidate = candidates.single() - return if (toTargetConstructorMap.values().all { it == candidate } /* all other constructors call our candidate (directly or indirectly)*/) - candidate - else - null - } - } - } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index f04f06dc9bc..53412772fac 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -40,7 +40,6 @@ public class Converter private(val project: Project, val settings: ConverterSett val typeConverter: TypeConverter = state.typeConverter val methodReturnType: PsiType? = state.methodReturnType - private val constructorConverter = ConstructorConverter(this) private val expressionVisitor = state.expressionVisitorFactory(this) private val statementVisitor = state.statementVisitorFactory(this) @@ -68,7 +67,7 @@ public class Converter private(val project: Project, val settings: ConverterSett private fun convertTopElement(element: PsiElement?): Element? = when (element) { is PsiJavaFile -> convertFile(element) is PsiClass -> convertClass(element) - is PsiMethod -> convertMethod(element, HashSet()) + is PsiMethod -> convertMethod(element, null, null) is PsiField -> convertField(element) is PsiStatement -> convertStatement(element) is PsiExpression -> convertExpression(element) @@ -102,15 +101,15 @@ public class Converter private(val project: Project, val settings: ConverterSett } fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody { - return AnonymousClassBody(convertBody(anonymousClass), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass) + return AnonymousClassBody(convertBody(anonymousClass, null), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass) } - private fun convertBody(psiClass: PsiClass): ClassBody { + private fun convertBody(psiClass: PsiClass, constructorConverter: ConstructorConverter?): ClassBody { val membersToRemove = HashSet() val convertedMembers = LinkedHashMap() for (element in psiClass.getChildren()) { if (element is PsiMember) { - val converted = convertMember(element, membersToRemove) + val converted = convertMember(element, membersToRemove, constructorConverter) if (!converted.isEmpty) { convertedMembers.put(element, converted) } @@ -166,8 +165,8 @@ public class Converter private(val project: Project, val settings: ConverterSett } } - private fun convertMember(member: PsiMember, membersToRemove: MutableSet): Member = when (member) { - is PsiMethod -> convertMethod(member, membersToRemove) + private fun convertMember(member: PsiMember, membersToRemove: MutableSet, constructorConverter: ConstructorConverter?): Member = when (member) { + is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter) is PsiField -> convertField(member) is PsiClass -> convertClass(member) is PsiClassInitializer -> convertInitializer(member) @@ -181,7 +180,10 @@ public class Converter private(val project: Project, val settings: ConverterSett val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes()) val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes()) val name = psiClass.declarationIdentifier() - var classBody = convertBody(psiClass) + + val constructorConverter = ConstructorConverter(psiClass, this) + var classBody = convertBody(psiClass, constructorConverter) + classBody = constructorConverter.postProcessConstructors(classBody) return when { psiClass.isInterface() -> Trait(name, annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody) @@ -189,8 +191,6 @@ public class Converter private(val project: Project, val settings: ConverterSett psiClass.isEnum() -> Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody) else -> { - classBody = constructorConverter.postProcessConstructors(classBody, psiClass) - val baseClassParams: List = run { val superVisitor = SuperVisitor() psiClass.accept(superVisitor) @@ -266,11 +266,11 @@ public class Converter private(val project: Project, val settings: ConverterSett return if (convertedType == initializerType) null else convertedType } - private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet): Member { - return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method) + private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet?, constructorConverter: ConstructorConverter?): Member { + return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove, constructorConverter).assignPrototype(method) } - private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet): Member { + private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet?, constructorConverter: ConstructorConverter?): Member { val returnType = typeConverter.convertMethodReturnType(method) val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype() @@ -297,8 +297,8 @@ public class Converter private(val project: Project, val settings: ConverterSett } } - if (method.isConstructor()) { - return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove, postProcessBody) + if (method.isConstructor() && constructorConverter != null) { + return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody) } else { val isOverride = isOverride(method)