From 1c889c4e678379a0fddf8622862436cfeefe02f9 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 5 Nov 2015 18:42:56 +0300 Subject: [PATCH] Support equals, hashCode, toString for ::-references without reflection --- .../methodsFromAny/callableReferences.kt | 31 +++++++++++++++++++ .../methodsFromAny/classReference.kt | 24 ++++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 21 +++++++++++++ .../src/kotlin/jvm/internal/ClassReference.kt | 9 ++++++ .../jvm/internal/FunctionReference.java | 24 ++++++++++++++ .../jvm/internal/PropertyReference.java | 20 ++++++++++++ .../src/kotlin/jvm/internal/Reflection.java | 2 ++ 7 files changed, 131 insertions(+) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/callableReferences.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/classReference.kt diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/callableReferences.kt b/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/callableReferences.kt new file mode 100644 index 00000000000..87a17ca3bd0 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/callableReferences.kt @@ -0,0 +1,31 @@ +// NO_KOTLIN_REFLECT + +import kotlin.reflect.KCallable +import kotlin.test.* + +class M { + fun foo() {} + val bar = 1 +} + +fun checkEquals(x: KCallable<*>, y: KCallable<*>) { + assertEquals(x, y) + assertEquals(y, x) + assertEquals(x.hashCode(), y.hashCode()) +} + +fun checkToString(x: KCallable<*>, expected: String) { + assertEquals(expected + " (Kotlin reflection is not available)", x.toString()) +} + +fun box(): String { + checkEquals(M::foo, M::foo) + checkEquals(M::bar, M::bar) + checkEquals(::M, ::M) + + checkToString(M::foo, "function foo") + checkToString(M::bar, "property bar") + checkToString(::M, "constructor") + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/classReference.kt b/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/classReference.kt new file mode 100644 index 00000000000..cb99eb8129d --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/classReference.kt @@ -0,0 +1,24 @@ +// NO_KOTLIN_REFLECT + +import kotlin.reflect.KClass +import kotlin.test.* + +class M + +fun check(x: KClass<*>) { + assertEquals(x, x.java.kotlin) + assertEquals(x.hashCode(), x.java.kotlin.hashCode()) + assertEquals(x.java.toString() + " (Kotlin reflection is not available)", x.toString()) +} + +fun box(): String { + check(M::class) + check(String::class) + check(Error::class) + check(Int::class) + check(java.lang.Integer::class) + check(MutableList::class) + check(Array::class) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 1ae3240bb9d..116e3d1e93d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -4022,6 +4022,27 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/simpleClassLiterals.kt"); doTestWithStdlib(fileName); } + + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MethodsFromAny extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInMethodsFromAny() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("callableReferences.kt") + public void testCallableReferences() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/callableReferences.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("classReference.kt") + public void testClassReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/methodsFromAny/classReference.kt"); + doTestWithStdlib(fileName); + } + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/parameters") diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt index 87409cce9e2..b1331c91c51 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt @@ -43,4 +43,13 @@ public class ClassReference(override val jClass: Class<*>) : KClass, ClassB get() = error() private fun error(): Nothing = throw KotlinReflectionNotSupportedError() + + override fun equals(other: Any?) = + other is ClassReference && jClass == other.jClass + + override fun hashCode() = + jClass.hashCode() + + override fun toString() = + jClass.toString() + Reflection.REFLECTION_NOT_AVAILABLE } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index 55c5ce5a1f9..8edfab7b366 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -79,4 +79,28 @@ public class FunctionReference extends FunctionImpl implements KFunction { protected static Error error() { throw new KotlinReflectionNotSupportedError(); } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof FunctionReference)) return false; + + FunctionReference other = (FunctionReference) obj; + return getOwner().equals(other.getOwner()) && + getName().equals(other.getName()) && + getSignature().equals(other.getSignature()); + } + + @Override + public int hashCode() { + return (getOwner().hashCode() * 31 + getName().hashCode()) * 31 + getSignature().hashCode(); + } + + @Override + public String toString() { + // TODO: consider adding the class name to toString() for constructors + return "".equals(getName()) + ? "constructor" + Reflection.REFLECTION_NOT_AVAILABLE + : "function " + getName() + Reflection.REFLECTION_NOT_AVAILABLE; + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java index 822d9fc9af2..4eaa42f5a9d 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java @@ -19,4 +19,24 @@ package kotlin.jvm.internal; import kotlin.reflect.KProperty; public abstract class PropertyReference extends CallableReference implements KProperty { + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof PropertyReference)) return false; + + PropertyReference other = (PropertyReference) obj; + return getOwner().equals(other.getOwner()) && + getName().equals(other.getName()) && + getSignature().equals(other.getSignature()); + } + + @Override + public int hashCode() { + return (getOwner().hashCode() * 31 + getName().hashCode()) * 31 + getSignature().hashCode(); + } + + @Override + public String toString() { + return "property " + getName() + Reflection.REFLECTION_NOT_AVAILABLE; + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java index 57c5150853a..df59ac90357 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java @@ -39,6 +39,8 @@ public class Reflection { factory = impl != null ? impl : new ReflectionFactory(); } + /* package */ static final String REFLECTION_NOT_AVAILABLE = " (Kotlin reflection is not available)"; + private static final KClass[] EMPTY_K_CLASS_ARRAY = new KClass[0]; public static KClass createKotlinClass(Class javaClass) {