diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/FieldToPropertyConversion.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/FieldToPropertyConversion.kt index 2678b482c11..7ede2f25533 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/FieldToPropertyConversion.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/FieldToPropertyConversion.kt @@ -5,10 +5,14 @@ package org.jetbrains.kotlin.j2k.conversions +import org.jetbrains.kotlin.j2k.ast.Mutability import org.jetbrains.kotlin.j2k.tree.* import org.jetbrains.kotlin.j2k.tree.impl.JKBlockImpl import org.jetbrains.kotlin.j2k.tree.impl.JKKtPropertyImpl +import org.jetbrains.kotlin.j2k.tree.impl.mutability +import org.jetbrains.kotlin.j2k.tree.impl.visibility import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class FieldToPropertyConversion : RecursiveApplicableConversionBase() { @@ -35,14 +39,14 @@ class FieldToPropertyConversion : RecursiveApplicableConversionBase() { when (it) { is JKJavaField -> propertyInfoFor(it.name.value).field = it is JKJavaMethod -> { - it.getterFor()?.let { field -> propertyInfoFor(field.name.value).getter = it } - ?: it.setterFor()?.let { field -> propertyInfoFor(field.name.value).setter = it } + propertyNameFromGet(it)?.let { fieldName -> propertyInfoFor(fieldName).getter = it } + ?: propertyNameFromSet(it)?.let { fieldName -> propertyInfoFor(fieldName).setter = it } } } } - propertyInfos.forEach { (_, info) -> + for ((_, info) in propertyInfos) { val field = info.field if (field != null) { @@ -52,8 +56,19 @@ class FieldToPropertyConversion : RecursiveApplicableConversionBase() { JKKtPropertyImpl(field.modifierList, field.type, field.name, field.initializer, JKBlockImpl(), JKBlockImpl()) declarations.remove(field) - declarations.remove(info.getter) - declarations.remove(info.setter) + + val getter = info.getter + if (getter?.fieldFromGetter(field.name.value) == field) { + declarations.remove(getter) + property.modifierList.mutability = Mutability.NonMutable + property.modifierList.visibility = minOf(getter.modifierList.visibility, property.modifierList.visibility) + } + val setter = info.setter + if (setter?.fieldFromSetter(field.name.value) == field) { + declarations.remove(setter) + property.modifierList.mutability = Mutability.Mutable + property.modifierList.visibility = minOf(setter.modifierList.visibility, property.modifierList.visibility) + } declarations.add(property) } } @@ -64,19 +79,55 @@ class FieldToPropertyConversion : RecursiveApplicableConversionBase() { } - private fun JKMethod.getterFor(): JKJavaField? { - if (JvmAbi.isGetterName(name.value)) return null - if (this.parameters.isNotEmpty()) return null + private fun propertyNameFromGet(method: JKMethod): String? { + if (!JvmAbi.isGetterName(method.name.value)) return null + if (method.parameters.isNotEmpty()) return null + if (method !is JKJavaMethod) return null + return method.name.value + .removePrefix("get") + .removePrefix("is") + .decapitalize() + } + + private fun propertyNameFromSet(method: JKMethod): String? { + if (!JvmAbi.isSetterName(method.name.value)) return null + if (method.parameters.size != 1) return null + if (method !is JKJavaMethod) return null + return method.name.value + .removePrefix("set") + .decapitalize() + } + + private fun JKExpression.unboxFieldReference(): JKFieldAccessExpression? = when { + this is JKFieldAccessExpression -> this + this is JKQualifiedExpression && receiver is JKThisExpression -> selector as? JKFieldAccessExpression + else -> null + } + + private fun JKMethod.fieldFromGetter(propertyName: String): JKJavaField? { if (this !is JKJavaMethod) return null val returnStatement = block.statements.singleOrNull() as? JKReturnStatement ?: return null - val fieldAccess = returnStatement.expression as? JKFieldAccessExpression ?: return null + val fieldAccess = returnStatement.expression.unboxFieldReference() ?: return null val field = fieldAccess.identifier.target as? JKJavaField ?: return null - if (name.value.endsWith(field.name.value)) return field - return null + if (propertyName != field.name.value) return null + return field } - fun JKMethod.setterFor(): JKJavaField? { - return null + private fun JKMethod.fieldFromSetter(propertyName: String): JKJavaField? { + if (this !is JKJavaMethod) return null + val expressionStatement = this.block.statements.singleOrNull() as? JKExpressionStatement ?: return null + val assignment = expressionStatement.expression as? JKJavaAssignmentExpression ?: return null + val lhs = assignment.field + val fieldAccess = lhs.unboxFieldReference() + val target = fieldAccess?.identifier?.target as? JKJavaField ?: return null + if (propertyName != target.name.value) return null + return target } + + private data class AccessorInfo( + val method: JKMethod, + val field: JKField?, + val propertyName: String + ) }