diff --git a/j2k/src/org/jetbrains/jet/j2k/ConstructorUtils.kt b/j2k/src/org/jetbrains/jet/j2k/ConstructorUtils.kt index b73edbd1d80..a10604634dd 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ConstructorUtils.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ConstructorUtils.kt @@ -16,20 +16,8 @@ package org.jetbrains.jet.j2k -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiReferenceExpression -import com.intellij.psi.JavaRecursiveElementVisitor +import com.intellij.psi.* import java.util.LinkedHashSet -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethodCallExpression -import com.intellij.psi.PsiParameter -import com.intellij.psi.PsiField -import com.intellij.psi.PsiAssignmentExpression -import com.intellij.psi.PsiThisExpression -import com.intellij.psi.PsiStatement -import com.intellij.psi.PsiExpressionStatement -import com.intellij.psi.PsiBlockStatement import com.intellij.psi.util.PsiUtil fun PsiMethod.isPrimaryConstructor(): Boolean { @@ -48,6 +36,7 @@ fun PsiClass.getPrimaryConstructor(): PsiMethod? { else -> { // if there is more than one constructor then choose one invoked by all others + //TODO: logic is incorrect - there can be a constructor which does not call any other class Visitor() : JavaRecursiveElementVisitor() { //TODO: skip all non-constructor members (optimization) private val invokedConstructors = LinkedHashSet() @@ -89,7 +78,7 @@ fun PsiElement.getContainingConstructor(): PsiMethod? { fun PsiMethodCallExpression.isSuperConstructorCall(): Boolean { val ref = getMethodExpression() - if (ref.getCanonicalText().equals("super")) { + if (ref.getCanonicalText() == "super") { val target = ref.resolve() return target is PsiMethod && target.isConstructor() } diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index 35af58897aa..2c8bbaccaaa 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -122,53 +122,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) { else -> { if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) { - val finalOrWithEmptyInitializerFields = classBodyElements.filterIsInstance(javaClass()).filter { it.isVal() || it.initializer.toKotlin().isEmpty() } - val initializers = HashMap() - for (element in classBodyElements) { - if (element is SecondaryConstructor) { - for (field in finalOrWithEmptyInitializerFields) { - initializers.put(field.identifier.toKotlin(), getDefaultInitializer(field)) - } - - val newStatements = ArrayList() - for (statement in element.block!!.statements) { - var keepStatement = true - if (statement is AssignmentExpression) { - val assignee = statement.left - if (assignee is CallChainExpression) { - for (field in finalOrWithEmptyInitializerFields) { - val id = field.identifier.toKotlin() - if (assignee.identifier.toKotlin().endsWith("." + id)) { - initializers.put(id, statement.right.toKotlin()) - keepStatement = false - } - - } - } - - } - - if (keepStatement) { - newStatements.add(statement) - } - - } - newStatements.add(0, DummyStringExpression("val __ = " + createPrimaryConstructorInvocation(name.toKotlin(), finalOrWithEmptyInitializerFields, initializers))) - element.block = Block(newStatements) - } - } - - //TODO: comments? - val parameters = finalOrWithEmptyInitializerFields.map { field -> - val varValModifier = if (field.modifiers.contains(Modifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var - Parameter(field.identifier, field.`type`, varValModifier, field.modifiers.filter { ACCESS_MODIFIERS.contains(it) }) - } - classBodyElements.add(PrimaryConstructor(this, - MemberComments.Empty, - setOf(Modifier.PRIVATE), - ParameterList(parameters), - Block.Empty)) - classBodyElements.removeAll(finalOrWithEmptyInitializerFields) + generateArtificialPrimaryConstructor(name, classBodyElements) } val baseClassParams: List = run { @@ -188,34 +142,54 @@ public class Converter(val project: Project, val settings: ConverterSettings) { } } - private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair? { - val body = constructor.getBody() ?: return null + private fun generateArtificialPrimaryConstructor(className: Identifier, classBodyElements: MutableList) { + val finalOrWithEmptyInitializerFields = classBodyElements.filterIsInstance(javaClass()).filter { it.isVal() || it.initializer.toKotlin().isEmpty() } + val initializers = HashMap() + for (element in classBodyElements) { + if (element is SecondaryConstructor) { + for (field in finalOrWithEmptyInitializerFields) { + initializers.put(field.identifier.toKotlin(), getDefaultInitializer(field)) + } - val refs = findExpressionReferences(parameter, body) + val newStatements = ArrayList() + for (statement in element.block!!.statements) { + var keepStatement = true + if (statement is AssignmentExpression) { + val assignee = statement.left + if (assignee is CallChainExpression) { + for (field in finalOrWithEmptyInitializerFields) { + val id = field.identifier.toKotlin() + if (assignee.identifier.toKotlin().endsWith("." + id)) { + initializers.put(id, statement.right.toKotlin()) + keepStatement = false + } - if (refs.any { PsiUtil.isAccessedForWriting(it) }) return null + } + } - for(ref in refs) { - val assignment = ref.getParent() as? PsiAssignmentExpression ?: continue - if (assignment.getOperationSign().getTokenType() != JavaTokenType.EQ) continue - val assignee = assignment.getLExpression() as? PsiReferenceExpression ?: continue - if (!isQualifierEmptyOrThis(assignee)) continue - val field = assignee.resolve() as? PsiField ?: continue - if (field.getContainingClass() != constructor.getContainingClass()) continue - if (field.getInitializer() != null) continue + } - // assignment should be a top-level statement - val statement = assignment.getParent() as? PsiExpressionStatement ?: continue - if (statement.getParent() != body) continue + if (keepStatement) { + newStatements.add(statement) + } - // and no other assignments to field should exist in the constructor - if (findExpressionReferences(field, body).any { it != assignee && PsiUtil.isAccessedForWriting(it) && isQualifierEmptyOrThis(it) }) continue - //TODO: check access to field before assignment - - return field to statement + } + newStatements.add(0, DummyStringExpression("val __ = " + createPrimaryConstructorInvocation(className.toKotlin(), finalOrWithEmptyInitializerFields, initializers))) + element.block = Block(newStatements) + } } - return null + //TODO: comments? + val parameters = finalOrWithEmptyInitializerFields.map { field -> + val varValModifier = if (field.modifiers.contains(Modifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var + Parameter(field.identifier, field.`type`, varValModifier, field.modifiers.filter { ACCESS_MODIFIERS.contains(it) }) + } + classBodyElements.add(PrimaryConstructor(this, + MemberComments.Empty, + setOf(Modifier.PRIVATE), + ParameterList(parameters), + Block.Empty)) + classBodyElements.removeAll(finalOrWithEmptyInitializerFields) } private fun convertInitializer(initializer: PsiClassInitializer): Initializer { @@ -283,45 +257,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) { if (method.isConstructor()) { if (method.isPrimaryConstructor()) { - val params = method.getParameterList().getParameters() - val parameterToField = HashMap() - val body = method.getBody() - val block = if (body != null) { - val statementsToRemove = HashSet() - val usageReplacementMap = HashMap() - for (parameter in params) { - val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, method) ?: continue - if (membersToRemove.contains(field)) continue // already used as backing field - if (convertVariableType(field) != convertVariableType(parameter)) continue - - parameterToField.put(parameter, field) - statementsToRemove.add(initializationStatement) - - if (field.getName() != parameter.getName()) { - usageReplacementMap.put(parameter, field.getName()!!) - } - } - dispatcher.expressionVisitor = ExpressionVisitor(this, usageReplacementMap) - Block(convertStatements(body.getStatements().filter{ !statementsToRemove.contains(it) }), false) - } - else { - Block.Empty - } - - val parameterList = ParameterList(params.map { - val field = parameterToField[it] - if (field == null) { - convertParameter(it) - } - else { - membersToRemove.add(field) - Parameter(Identifier(field.getName()!!), - convertVariableType(it), - if (field.hasModifierProperty(PsiModifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, - convertModifierList(field.getModifierList()).filter { ACCESS_MODIFIERS.contains(it) }) - } - }) - return PrimaryConstructor(this, comments, modifiers, parameterList, block) + return convertPrimaryConstructor(method, modifiers, comments, membersToRemove) } else { val params = convertParameterList(method.getParameterList()) @@ -340,6 +276,81 @@ public class Converter(val project: Project, val settings: ConverterSettings) { } } + private fun convertPrimaryConstructor(constructor: PsiMethod, + modifiers: Set, + comments: MemberComments, + membersToRemove: MutableSet): PrimaryConstructor { + val params = constructor.getParameterList().getParameters() + val parameterToField = HashMap() + val body = constructor.getBody() + val block = if (body != null) { + val statementsToRemove = HashSet() + val usageReplacementMap = HashMap() + for (parameter in params) { + val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, constructor) ?: continue + if (convertVariableType(field) != convertVariableType(parameter)) continue + + parameterToField.put(parameter, field) + statementsToRemove.add(initializationStatement) + membersToRemove.add(field) + + if (field.getName() != parameter.getName()) { + usageReplacementMap.put(parameter, field.getName()!!) + } + } + dispatcher.expressionVisitor = ExpressionVisitor(this, usageReplacementMap) + Block(convertStatements(body.getStatements().filter{ !statementsToRemove.contains(it) }), false) + } + else { + Block.Empty + } + + val parameterList = ParameterList(params.map { + val field = parameterToField[it] + if (field == null) { + convertParameter(it) + } + else { + Parameter(Identifier(field.getName()!!), + convertVariableType(it), + if (field.hasModifierProperty(PsiModifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, + convertModifierList(field.getModifierList()).filter { ACCESS_MODIFIERS.contains(it) }) + } + }) + return PrimaryConstructor(this, comments, modifiers, parameterList, block) + } + + private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair? { + val body = constructor.getBody() ?: return null + + val refs = findExpressionReferences(parameter, body) + + if (refs.any { PsiUtil.isAccessedForWriting(it) }) return null + + for(ref in refs) { + val assignment = ref.getParent() as? PsiAssignmentExpression ?: continue + if (assignment.getOperationSign().getTokenType() != JavaTokenType.EQ) continue + val assignee = assignment.getLExpression() as? PsiReferenceExpression ?: continue + if (!isQualifierEmptyOrThis(assignee)) continue + val field = assignee.resolve() as? PsiField ?: continue + if (field.getContainingClass() != constructor.getContainingClass()) continue + if (field.hasModifierProperty(PsiModifier.STATIC)) continue + if (field.getInitializer() != null) continue + + // assignment should be a top-level statement + val statement = assignment.getParent() as? PsiExpressionStatement ?: continue + if (statement.getParent() != body) continue + + // and no other assignments to field should exist in the constructor + if (findExpressionReferences(field, body).any { it != assignee && PsiUtil.isAccessedForWriting(it) && isQualifierEmptyOrThis(it) }) continue + //TODO: check access to field before assignment + + return field to statement + } + + return null + } + public fun convertBlock(block: PsiCodeBlock?): Block { if (block == null) return Block.Empty diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt index 600a8bddca8..e0d5f557235 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt @@ -48,10 +48,7 @@ open class Field( val declaration: String = commentsToKotlin() + modifiersToKotlin() + identifier.toKotlin() + " : " + `type`.toKotlin() if (initializer.isEmpty) { - return declaration + ((if (isVal() && !isStatic() && writingAccesses != 0) - "" - else - " = " + getDefaultInitializer(this))) + return declaration + (if (isVal() && !isStatic() && writingAccesses != 0) "" else " = " + getDefaultInitializer(this)) } return declaration + " = " + initializer.toKotlin() diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/StatementVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/StatementVisitor.kt index f81fa378724..70f23a88146 100644 --- a/j2k/src/org/jetbrains/jet/j2k/visitors/StatementVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/visitors/StatementVisitor.kt @@ -80,11 +80,11 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() { val condition = statement.getCondition() val body = statement.getBody() - val initializationVar = initialization?.getFirstChild() as? PsiLocalVariable - val onceWritableIterator = initializationVar != null - && initializationVar.countWriteAccesses(body) == 0 - && initializationVar.countWriteAccesses(condition) == 0 - && initializationVar.countWriteAccesses(update) == 1 + val loopVar = initialization?.getFirstChild() as? PsiLocalVariable + val onceWritableIterator = loopVar != null + && loopVar.countWriteAccesses(body) == 0 + && loopVar.countWriteAccesses(condition) == 0 + && loopVar.countWriteAccesses(update) == 1 val operationTokenType = (condition as? PsiBinaryExpression)?.getOperationTokenType() if (initialization is PsiDeclarationStatement @@ -94,16 +94,16 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() { && update.getChildren().size == 1 && isPlusPlusExpression(update.getChildren().single()) && (operationTokenType == JavaTokenType.LT || operationTokenType == JavaTokenType.LE) - && initializationVar != null - && initializationVar.getNameIdentifier() != null + && loopVar != null + && loopVar.getNameIdentifier() != null && onceWritableIterator) { val end = converter.convertExpression((condition as PsiBinaryExpression).getROperand()) val endExpression = if (operationTokenType == JavaTokenType.LT) BinaryExpression(end, Identifier("1"), "-") else end - result = ForeachWithRangeStatement(Identifier(initializationVar.getName()!!), - converter.convertExpression(initializationVar.getInitializer()), + result = ForeachWithRangeStatement(Identifier(loopVar.getName()!!), + converter.convertExpression(loopVar.getInitializer()), endExpression, converter.convertStatement(body)) }