From 92ac865c72ac7f1cf573f74ddbe7cdd5a806ce20 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 26 Sep 2012 12:32:16 +0400 Subject: [PATCH] KT-2819 Duplicate toString() method generated in data class #KT-2819 Fixed --- .../jetbrains/jet/codegen/CodegenUtil.java | 42 +++++++++++++++++-- .../codegen/ImplementationBodyCodegen.java | 27 +++++++++++- .../dataClasses/equals/alreadyDeclared.kt | 8 ++++ .../equals/alreadyDeclaredWrongSignature.kt | 38 +++++++++++++++++ .../dataClasses/hashcode/alreadyDeclared.kt | 7 ++++ .../hashcode/alreadyDeclaredWrongSignature.kt | 17 ++++++++ .../dataClasses/tostring/alreadyDeclared.kt | 7 ++++ .../tostring/alreadyDeclaredWrongSignature.kt | 17 ++++++++ .../DataClassCodegenTestGenerated.java | 31 +++++++++++++- 9 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt create mode 100644 compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt create mode 100644 compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt create mode 100644 compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt create mode 100644 compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt create mode 100644 compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index ddf47372e72..d553322b328 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -37,10 +37,7 @@ import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Random; +import java.util.*; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; @@ -200,4 +197,41 @@ public class CodegenUtil { throw new IllegalStateException("code generation for synthesized members should be handled separately"); } } + + @Nullable + public static FunctionDescriptor getDeclaredFunctionByRawSignature( + @NotNull ClassDescriptor owner, + @NotNull Name name, + @NotNull ClassDescriptor returnedClass, + @NotNull ClassDescriptor... valueParameterClasses + ) { + Collection functions = owner.getDefaultType().getMemberScope().getFunctions(name); + for (FunctionDescriptor function : functions) { + if (function.getKind() == CallableMemberDescriptor.Kind.DECLARATION + && function.getTypeParameters().isEmpty() + && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClasses)) + && rawTypeMatches(function.getReturnType(), returnedClass)) { + return function; + } + } + return null; + } + + private static boolean valueParameterClassesMatch( + @NotNull List parameters, + @NotNull List classes) { + if (parameters.size() != classes.size()) return false; + for (int i = 0; i < parameters.size(); i++) { + ValueParameterDescriptor parameterDescriptor = parameters.get(i); + ClassDescriptor classDescriptor = classes.get(i); + if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) { + return false; + } + } + return true; + } + + private static boolean rawTypeMatches(JetType type, ClassDescriptor classDescriptor) { + return type.getConstructor().getDeclarationDescriptor().getOriginal() == classDescriptor.getOriginal(); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 2abc342250d..f742f9b3dda 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -50,7 +50,9 @@ import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils; +import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.lexer.JetTokens; @@ -411,10 +413,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateComponentFunctionsForDataClasses(); - final List properties = getDataProperties(); + List properties = getDataProperties(); if (!properties.isEmpty()) { + generateDataClassToStringIfNeeded(properties); + generateDataClassHashCodeIfNeeded(properties); + generateDataClassEqualsIfNeeded(properties); + } + } + + private void generateDataClassToStringIfNeeded(List properties) { + ClassDescriptor stringClass = JetStandardLibrary.getInstance().getString(); + if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("toString"), stringClass) == null) { generateDataClassToStringMethod(properties); + } + } + + private void generateDataClassHashCodeIfNeeded(List properties) { + ClassDescriptor intClass = JetStandardLibrary.getInstance().getInt(); + if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("hashCode"), intClass) == null) { generateDataClassHashCodeMethod(properties); + } + } + + private void generateDataClassEqualsIfNeeded(List properties) { + ClassDescriptor booleanClass = JetStandardLibrary.getInstance().getBoolean(); + ClassDescriptor anyClass = JetStandardClasses.getAny(); + FunctionDescriptor equalsFunction = getDeclaredFunctionByRawSignature(descriptor, Name.identifier("equals"), booleanClass, anyClass); + if (equalsFunction == null) { generateDataClassEqualsMethod(properties); } } diff --git a/compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt b/compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt new file mode 100644 index 00000000000..9f631add9aa --- /dev/null +++ b/compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt @@ -0,0 +1,8 @@ +data class A(val x: Int) { + fun equals(other: Any?): Boolean = false +} + +fun box(): String { + val a = A(0) + return if (a equals a) "fail" else "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt b/compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt new file mode 100644 index 00000000000..a2562a546ec --- /dev/null +++ b/compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt @@ -0,0 +1,38 @@ +data class A(val x: Int) { + fun equals(other: Any): Boolean = false +} + +data class B(val x: Int) { + fun equals(other: B): Boolean = false +} + +data class C(val x: Int) { + fun equals(): Boolean = false +} + +data class D(val x: Int) { + fun equals(other: Any?, another: String): Boolean = false +} + +data class E(val x: Int) { + fun equals(x: E): Boolean = false + fun equals(x: Any?): Boolean = false +} + +fun box(): String { + javaClass().getDeclaredMethod("equals", javaClass()) + + javaClass().getDeclaredMethod("equals", javaClass()) + javaClass().getDeclaredMethod("equals", javaClass()) + + javaClass().getDeclaredMethod("equals", javaClass()) + javaClass().getDeclaredMethod("equals") + + javaClass().getDeclaredMethod("equals", javaClass()) + javaClass().getDeclaredMethod("equals", javaClass(), javaClass()) + + javaClass().getDeclaredMethod("equals", javaClass()) + javaClass().getDeclaredMethod("equals", javaClass()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt b/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt new file mode 100644 index 00000000000..73586f7f6d5 --- /dev/null +++ b/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt @@ -0,0 +1,7 @@ +data class A(val x: Int) { + fun hashCode(): Int = -3 +} + +fun box(): String { + return if (A(0).hashCode() == -3) "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt b/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt new file mode 100644 index 00000000000..84f6757a6be --- /dev/null +++ b/compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt @@ -0,0 +1,17 @@ +data class A(val x: Int) { + fun hashCode(other: Any): Int = 0 +} + +data class B(val x: Int) { + fun hashCode(other: B, another: Any): Int = 0 +} + +fun box(): String { + javaClass().getDeclaredMethod("hashCode") + javaClass().getDeclaredMethod("hashCode", javaClass()) + + javaClass().getDeclaredMethod("hashCode") + javaClass().getDeclaredMethod("hashCode", javaClass(), javaClass()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt b/compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt new file mode 100644 index 00000000000..f699fbaf727 --- /dev/null +++ b/compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt @@ -0,0 +1,7 @@ +data class A(val x: Int) { + fun toString(): String = "!" +} + +fun box(): String { + return if (A(0).toString() == "!") "OK" else "fail" +} \ No newline at end of file diff --git a/compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt b/compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt new file mode 100644 index 00000000000..248a004a10a --- /dev/null +++ b/compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt @@ -0,0 +1,17 @@ +data class A(val x: Int) { + fun toString(other: Any): String = "" +} + +data class B(val x: Int) { + fun toString(other: B, another: Any): String = "" +} + +fun box(): String { + javaClass().getDeclaredMethod("toString") + javaClass().getDeclaredMethod("toString", javaClass()) + + javaClass().getDeclaredMethod("toString") + javaClass().getDeclaredMethod("toString", javaClass(), javaClass()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java index 052e53c90d9..d52fc48795a 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.jetbrains.jet.codegen; import junit.framework.Test; @@ -88,6 +87,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/equals"), "kt", true); } + @TestMetadata("alreadyDeclared.kt") + public void testAlreadyDeclared() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt"); + } + + @TestMetadata("alreadyDeclaredWrongSignature.kt") + public void testAlreadyDeclaredWrongSignature() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt"); + } + @TestMetadata("genericarray.kt") public void testGenericarray() throws Exception { blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/genericarray.kt"); @@ -121,6 +130,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/hashcode"), "kt", true); } + @TestMetadata("alreadyDeclared.kt") + public void testAlreadyDeclared() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt"); + } + + @TestMetadata("alreadyDeclaredWrongSignature.kt") + public void testAlreadyDeclaredWrongSignature() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt"); + } + @TestMetadata("array.kt") public void testArray() throws Exception { blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/array.kt"); @@ -179,6 +198,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/tostring"), "kt", true); } + @TestMetadata("alreadyDeclared.kt") + public void testAlreadyDeclared() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt"); + } + + @TestMetadata("alreadyDeclaredWrongSignature.kt") + public void testAlreadyDeclaredWrongSignature() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt"); + } + @TestMetadata("arrayParams.kt") public void testArrayParams() throws Exception { blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/arrayParams.kt");