From 93d5f6e635e9a564c8eca9f8613fbecee56ade83 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 29 Mar 2017 13:04:32 +0300 Subject: [PATCH] Fix NPE on equals/hashCode of local function references Anonymous classes for local function references implement their getOwner() as "return null" currently (KT-14291). To avoid NPE in this case, we now consider two local functions the same if their name and signature are equal. This is incorrect in general, but unlikely to cause major problems and is going to be fixed by KT-14291 eventually anyway #KT-17055 --- .../function/local/equalsHashCode.kt | 13 +++++++++++++ .../codegen/ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++++ .../codegen/LightAnalysisModeTestGenerated.java | 6 ++++++ .../src/kotlin/jvm/internal/FunctionReference.java | 4 ++-- .../test/semantics/JsCodegenBoxTestGenerated.java | 12 ++++++++++++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt diff --git a/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt b/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt new file mode 100644 index 00000000000..b547cb1ee3f --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt @@ -0,0 +1,13 @@ +// IGNORE_BACKEND: JS, NATIVE + +fun box(): String { + fun bar() {} + fun baz() {} + + if (!::bar.equals(::bar)) return "Fail 1" + if (::bar.hashCode() != ::bar.hashCode()) return "Fail 2" + + if (::bar == ::baz) return "Fail 3" + + 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 d3ca4e10688..2a330a33db6 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 @@ -2040,6 +2040,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("equalsHashCode.kt") + public void testEqualsHashCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt"); + doTest(fileName); + } + @TestMetadata("extension.kt") public void testExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/extension.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d93f804ac11..fd16f61b991 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2040,6 +2040,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("equalsHashCode.kt") + public void testEqualsHashCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt"); + doTest(fileName); + } + @TestMetadata("extension.kt") public void testExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/extension.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 141c9522ebd..d8f89276e3d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2040,6 +2040,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("equalsHashCode.kt") + public void testEqualsHashCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt"); + doTest(fileName); + } + @TestMetadata("extension.kt") public void testExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/extension.kt"); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index ae69a055b16..3f3efbe7386 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -86,7 +86,7 @@ public class FunctionReference extends CallableReference implements FunctionBase if (obj instanceof FunctionReference) { FunctionReference other = (FunctionReference) obj; - return getOwner().equals(other.getOwner()) && + return (getOwner() == null ? other.getOwner() == null : getOwner().equals(other.getOwner())) && getName().equals(other.getName()) && getSignature().equals(other.getSignature()) && Intrinsics.areEqual(getBoundReceiver(), other.getBoundReceiver()); @@ -99,7 +99,7 @@ public class FunctionReference extends CallableReference implements FunctionBase @Override public int hashCode() { - return (getOwner().hashCode() * 31 + getName().hashCode()) * 31 + getSignature().hashCode(); + return ((getOwner() == null ? 0 : getOwner().hashCode() * 31) + getName().hashCode()) * 31 + getSignature().hashCode(); } @Override 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 0b54daf9135..df354e50432 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 @@ -2395,6 +2395,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("equalsHashCode.kt") + public void testEqualsHashCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/equalsHashCode.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("extension.kt") public void testExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/extension.kt");