From 30b9caeae6ea399dd160d6d504315d5a21b8d22b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 12 Apr 2018 19:47:49 +0200 Subject: [PATCH] Fix NPE in data class 'equals' codegen Do not perform the optimization from 0a11385006e for data class properties of nullable types --- .../kotlin/codegen/ImplementationBodyCodegen.java | 9 ++++++++- .../codegen/box/dataClasses/equals/null.kt | 15 +++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++++ .../codegen/LightAnalysisModeTestGenerated.java | 6 ++++++ .../test/semantics/JsCodegenBoxTestGenerated.java | 6 ++++++ 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/dataClasses/equals/null.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 09aebc0bc82..14fec179f65 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.org.objectweb.asm.FieldVisitor; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -577,7 +578,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if (isPrimitive(asmType)) { StackValue value = StackValue.cmp(KtTokens.EQEQ, asmType, StackValue.onStack(asmType), StackValue.onStack(asmType)); value.put(Type.BOOLEAN_TYPE, iv); - } else { + } + else if (TypeUtils.isNullableType(propertyDescriptor.getType())) { + StackValue value = + genEqualsForExpressionsOnStack(KtTokens.EQEQ, StackValue.onStack(asmType), StackValue.onStack(asmType)); + value.put(Type.BOOLEAN_TYPE, iv); + } + else { Type owner = DescriptorUtils.isInterface(propertyDescriptor.getType().getConstructor().getDeclarationDescriptor()) ? AsmTypes.OBJECT_TYPE diff --git a/compiler/testData/codegen/box/dataClasses/equals/null.kt b/compiler/testData/codegen/box/dataClasses/equals/null.kt new file mode 100644 index 00000000000..a6008d0d670 --- /dev/null +++ b/compiler/testData/codegen/box/dataClasses/equals/null.kt @@ -0,0 +1,15 @@ +data class A(val v: Any?) + +data class B(val v: T) + +fun box(): String { + val a1 = A(null) + val a2 = A("") + if (a1 == a2 || a2 == a1) return "Fail 1" + + val b1 = B(null) + val b2 = B("") + if (b1 == b2 || b2 == b1) return "Fail 2" + + return "OK" +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 81612068f66..8865c075f23 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7230,6 +7230,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("null.kt") + public void testNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/null.kt"); + doTest(fileName); + } + @TestMetadata("nullother.kt") public void testNullother() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/nullother.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 4e9941a64b0..2e8d4c7b365 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -7230,6 +7230,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("null.kt") + public void testNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/null.kt"); + doTest(fileName); + } + @TestMetadata("nullother.kt") public void testNullother() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/nullother.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 7a30b131cab..452a81c907c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -7230,6 +7230,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("null.kt") + public void testNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/null.kt"); + doTest(fileName); + } + @TestMetadata("nullother.kt") public void testNullother() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/nullother.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 03243d4f717..e96fee8042b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -8563,6 +8563,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("null.kt") + public void testNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/null.kt"); + doTest(fileName); + } + @TestMetadata("nullother.kt") public void testNullother() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/dataClasses/equals/nullother.kt");