From 81ef1c19d8c58192fd0ccb1721a44d80b1cf514f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 13 Jul 2015 18:59:10 +0300 Subject: [PATCH] Support KClass.constructors in reflection --- .../classesWithoutConstructors.kt | 19 ++++ .../constructors/simpleGetConstructors.kt | 29 +++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 12 +++ core/builtins/src/kotlin/reflect/KClass.kt | 9 +- .../impl/ConstructorDescriptorImpl.java | 2 +- .../deserialization/MemberDeserializer.kt | 12 +-- .../DeserializedConstructorDescriptor.kt | 45 ++++++++++ .../jvm/internal/KCallableContainerImpl.kt | 6 +- .../kotlin/reflect/jvm/internal/KClassImpl.kt | 87 ++++++++++++------- .../internal/KFunctionFromReferenceImpl.kt | 4 + .../reflect/jvm/internal/KPackageImpl.kt | 6 +- .../reflect/jvm/internal/RuntimeTypeMapper.kt | 10 +-- 12 files changed, 190 insertions(+), 51 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/constructors/classesWithoutConstructors.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/constructors/simpleGetConstructors.kt create mode 100644 core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedConstructorDescriptor.kt diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/constructors/classesWithoutConstructors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/constructors/classesWithoutConstructors.kt new file mode 100644 index 00000000000..04027d34f89 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/constructors/classesWithoutConstructors.kt @@ -0,0 +1,19 @@ +import kotlin.reflect.* +import kotlin.test.assertTrue + +interface Interface +annotation class Anno(val x: Int) +object Obj + +class C { + companion object +} + +fun box(): String { + assertTrue(Interface::class.constructors.isEmpty()) + assertTrue(Anno::class.constructors.isEmpty()) + assertTrue(Obj::class.constructors.isEmpty()) + assertTrue(C.Companion::class.constructors.isEmpty()) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/constructors/simpleGetConstructors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/constructors/simpleGetConstructors.kt new file mode 100644 index 00000000000..0803ece9190 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/constructors/simpleGetConstructors.kt @@ -0,0 +1,29 @@ +import java.util.Collections +import kotlin.reflect.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +open class A private constructor(x: Int) { + public constructor(s: String): this(s.length()) + constructor(): this("") +} + +class B : A("") + +class C { + class Nested + inner class Inner +} + +fun box(): String { + assertEquals(3, A::class.constructors.size()) + assertEquals(1, B::class.constructors.size()) + + assertTrue(Collections.disjoint(A::class.members, A::class.constructors)) + assertTrue(Collections.disjoint(B::class.members, B::class.constructors)) + + assertEquals(1, C.Nested::class.constructors.size()) + assertEquals(1, C.Inner::class.constructors.size()) + + 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 d1b59679596..28cd9799b52 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2847,11 +2847,23 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/constructors"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("classesWithoutConstructors.kt") + public void testClassesWithoutConstructors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/constructors/classesWithoutConstructors.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("constructorName.kt") public void testConstructorName() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/constructors/constructorName.kt"); doTestWithStdlib(fileName); } + + @TestMetadata("simpleGetConstructors.kt") + public void testSimpleGetConstructors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/constructors/simpleGetConstructors.kt"); + doTestWithStdlib(fileName); + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing") diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 0e6512c1499..39cd612a24a 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -39,8 +39,13 @@ public interface KClass : KDeclarationContainer { public val qualifiedName: String? /** - * All elements accessible in this class, including functions and properties - * declared in this class and all of its superclasses. + * All functions and properties accessible in this class, including those declared in this class + * and all of its superclasses. Does not include constructors. */ public val members: Collection> + + /** + * All constructors declared in this class. + */ + public val constructors: Collection> } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ConstructorDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ConstructorDescriptorImpl.java index 659d185798b..445d8a34b31 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ConstructorDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ConstructorDescriptorImpl.java @@ -112,7 +112,7 @@ public class ConstructorDescriptorImpl extends FunctionDescriptorImpl implements @NotNull @Override - protected FunctionDescriptorImpl createSubstitutedCopy( + protected ConstructorDescriptorImpl createSubstitutedCopy( @NotNull DeclarationDescriptor newOwner, @Nullable FunctionDescriptor original, @NotNull Kind kind diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 694f079c0b2..5fcb2d16034 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.serialization.deserialization import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl @@ -28,10 +27,7 @@ import org.jetbrains.kotlin.serialization.ProtoBuf.Callable import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.FUN import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAL import org.jetbrains.kotlin.serialization.ProtoBuf.Callable.CallableKind.VAR -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedAnnotations -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor +import org.jetbrains.kotlin.serialization.deserialization.descriptors.* public class MemberDeserializer(private val c: DeserializationContext) { public fun loadCallable(proto: Callable): CallableMemberDescriptor { @@ -152,9 +148,9 @@ public class MemberDeserializer(private val c: DeserializationContext) { public fun loadConstructor(proto: Callable, isPrimary: Boolean): ConstructorDescriptor { val classDescriptor = c.containingDeclaration as ClassDescriptor - val descriptor = ConstructorDescriptorImpl.create( - classDescriptor, getAnnotations(proto, proto.getFlags(), AnnotatedCallableKind.FUNCTION), - isPrimary, SourceElement.NO_SOURCE + val descriptor = DeserializedConstructorDescriptor( + classDescriptor, null, getAnnotations(proto, proto.getFlags(), AnnotatedCallableKind.FUNCTION), + isPrimary, CallableMemberDescriptor.Kind.DECLARATION, proto, c.nameResolver ) val local = c.childContext(descriptor, listOf()) descriptor.initialize( diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedConstructorDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedConstructorDescriptor.kt new file mode 100644 index 00000000000..aebcbd909c6 --- /dev/null +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedConstructorDescriptor.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.serialization.deserialization.descriptors + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.NameResolver + +public class DeserializedConstructorDescriptor( + containingDeclaration: ClassDescriptor, + original: ConstructorDescriptor?, + annotations: Annotations, + isPrimary: Boolean, + kind: CallableMemberDescriptor.Kind, + override val proto: ProtoBuf.Callable, + override val nameResolver: NameResolver +) : ConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, SourceElement.NO_SOURCE), + DeserializedCallableMemberDescriptor { + + override fun createSubstitutedCopy( + newOwner: DeclarationDescriptor, + original: FunctionDescriptor?, + kind: CallableMemberDescriptor.Kind + ): DeserializedConstructorDescriptor { + return DeserializedConstructorDescriptor( + newOwner as ClassDescriptor, original as ConstructorDescriptor?, getAnnotations(), isPrimary, kind, proto, nameResolver + ) + } +} diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt index 08141bae80b..06396056aca 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt @@ -16,6 +16,7 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.load.java.structure.reflect.classId @@ -43,6 +44,8 @@ abstract class KCallableContainerImpl : KDeclarationContainer { abstract val scope: JetScope + abstract val constructorDescriptors: Collection + fun findPropertyDescriptor(name: String, signature: String): PropertyDescriptor { val properties = scope .getProperties(Name.guess(name)) @@ -63,8 +66,7 @@ abstract class KCallableContainerImpl : KDeclarationContainer { } fun findFunctionDescriptor(name: String, signature: String): FunctionDescriptor { - val functions = scope - .getFunctions(Name.guess(name)) + val functions = (if (name == "") constructorDescriptors.toList() else scope.getFunctions(Name.guess(name))) .filter { descriptor -> RuntimeTypeMapper.mapSignature(descriptor) == signature } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index b7a58e346f5..4f0970724d1 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import kotlin.reflect.KCallable import kotlin.reflect.KClass +import kotlin.reflect.KFunction import kotlin.reflect.KotlinReflectionInternalError class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), KClass { @@ -43,6 +44,15 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K descriptor, "KClassImpl scope", descriptor.getDefaultType().getMemberScope(), descriptor.getStaticScope() ) + override val constructorDescriptors: Collection + get() { + val descriptor = descriptor + if (descriptor.getKind() == ClassKind.CLASS || descriptor.getKind() == ClassKind.ENUM_CLASS) { + return descriptor.getConstructors() + } + return emptyList() + } + override val simpleName: String? get() { if (jClass.isAnonymousClass()) return null @@ -77,44 +87,57 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K override val members: Collection> get() = getMembers(declaredOnly = false, nonExtensions = true, extensions = true).toList() - fun getMembers(declaredOnly: Boolean, nonExtensions: Boolean, extensions: Boolean): Sequence> = - scope.getAllDescriptors().asSequence() - .filter { descriptor -> - descriptor !is MemberDescriptor || descriptor.getVisibility() != Visibilities.INVISIBLE_FAKE - } - .map { descriptor -> - descriptor.accept(object : DeclarationDescriptorVisitorEmptyBodies?, Nothing>() { - private fun skipCallable(descriptor: CallableMemberDescriptor): Boolean { - if (declaredOnly && !descriptor.getKind().isReal()) return true + @suppress("UNCHECKED_CAST") + override val constructors: Collection> + get() = constructorDescriptors.map { + KFunctionImpl(this, it) as KFunction + } - val isExtension = descriptor.getExtensionReceiverParameter() != null - if (isExtension && !extensions) return true - if (!isExtension && !nonExtensions) return true + fun getMembers(declaredOnly: Boolean, nonExtensions: Boolean, extensions: Boolean): Sequence> { + val visitor = object : DeclarationDescriptorVisitorEmptyBodies?, Nothing>() { + private fun skipCallable(descriptor: CallableMemberDescriptor): Boolean { + if (declaredOnly && !descriptor.getKind().isReal()) return true - return false - } + val isExtension = descriptor.getExtensionReceiverParameter() != null + if (isExtension && !extensions) return true + if (!isExtension && !nonExtensions) return true - override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?): KCallable<*>? { - if (skipCallable(descriptor)) return null + return false + } - return if (descriptor.getExtensionReceiverParameter() == null) { - if (descriptor.isVar()) KMutableProperty1Impl(this@KClassImpl, descriptor) - else KProperty1Impl(this@KClassImpl, descriptor) - } - else { - if (descriptor.isVar()) KMutableProperty2Impl(this@KClassImpl, descriptor) - else KProperty2Impl(this@KClassImpl, descriptor) - } - } + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?): KCallable<*>? { + if (skipCallable(descriptor)) return null - override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?): KCallable<*>? { - if (skipCallable(descriptor)) return null + return if (descriptor.getExtensionReceiverParameter() == null) { + if (descriptor.isVar()) KMutableProperty1Impl(this@KClassImpl, descriptor) + else KProperty1Impl(this@KClassImpl, descriptor) + } + else { + if (descriptor.isVar()) KMutableProperty2Impl(this@KClassImpl, descriptor) + else KProperty2Impl(this@KClassImpl, descriptor) + } + } - return KFunctionImpl(this@KClassImpl, descriptor) - } - }, null) - } - .filterNotNull() + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?): KCallable<*>? { + if (skipCallable(descriptor)) return null + + return KFunctionImpl(this@KClassImpl, descriptor) + } + + override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Nothing?): KCallable<*>? { + throw IllegalStateException("No constructors should appear in this scope: $descriptor") + } + } + + return scope.getAllDescriptors().asSequence() + .filter { descriptor -> + descriptor !is MemberDescriptor || descriptor.getVisibility() != Visibilities.INVISIBLE_FAKE + } + .map { descriptor -> + descriptor.accept(visitor, null) + } + .filterNotNull() + } override fun equals(other: Any?): Boolean = other is KClassImpl<*> && jClass == other.jClass diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionFromReferenceImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionFromReferenceImpl.kt index ede7ae70e58..e36116551cd 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionFromReferenceImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionFromReferenceImpl.kt @@ -16,6 +16,7 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.resolve.scopes.JetScope import kotlin.jvm.internal.FunctionReference import kotlin.reflect.KotlinReflectionInternalError @@ -65,5 +66,8 @@ object EmptyContainerForLocal : KCallableContainerImpl() { override val scope: JetScope get() = fail() + override val constructorDescriptors: Collection + get() = fail() + private fun fail() = throw KotlinReflectionInternalError("Introspecting local functions is not yet supported in Kotlin reflection") } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt index 22d236f0b45..c4fe6a5c322 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt @@ -16,10 +16,11 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.load.java.structure.reflect.classId import org.jetbrains.kotlin.resolve.scopes.JetScope import kotlin.jvm.internal.KotlinPackage -import kotlin.reflect.* +import kotlin.reflect.KPackage class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KPackage { val descriptor by ReflectProperties.lazySoft { @@ -31,6 +32,9 @@ class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KP override val scope: JetScope get() = descriptor.memberScope + override val constructorDescriptors: Collection + get() = emptyList() + override fun equals(other: Any?): Boolean = other is KPackageImpl && jClass == other.jClass diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt index 74fef9465d1..61dcb7d472b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -31,22 +31,22 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf import kotlin.reflect.KotlinReflectionInternalError object RuntimeTypeMapper { fun mapSignature(function: FunctionDescriptor): String { - if (function is DeserializedSimpleFunctionDescriptor) { - val proto = function.getProto() + if (function is DeserializedCallableMemberDescriptor) { + val proto = function.proto if (!proto.hasExtension(JvmProtoBuf.methodSignature)) { // If it's a deserialized function but has no JVM signature, it must be from built-ins return mapIntrinsicFunctionSignature(function) ?: throw KotlinReflectionInternalError("No metadata found for $function") } val signature = proto.getExtension(JvmProtoBuf.methodSignature) - return SignatureDeserializer(function.getNameResolver()).methodSignatureString(signature) + return SignatureDeserializer(function.nameResolver).methodSignatureString(signature) } else if (function is JavaMethodDescriptor) { val method = (function.getSource() as? JavaSourceElement)?.javaElement as? JavaMethod ?: @@ -130,7 +130,7 @@ object RuntimeTypeMapper { else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})") } - private fun mapIntrinsicFunctionSignature(function: DeserializedSimpleFunctionDescriptor): String? { + private fun mapIntrinsicFunctionSignature(function: FunctionDescriptor): String? { val parameters = function.getValueParameters() when (function.getName().asString()) {