Use descriptors for reflection on class properties
- use ConcurrentHashMap as a cache of class loaders to module descriptors - KClassImpl now has a lazy class descriptor and it manages property creation by looking (also lazily) for the property descriptor in the corresponding scope - since deserialized descriptors have full information about where a JVM symbol is located and what signature it has, new tests will begin to pass where Kotlin model and Java reflection model differ, see classObjectVar.kt
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
class A {
|
||||||
|
default object B {
|
||||||
|
var state: String = "12345"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val p = A.B::state
|
||||||
|
|
||||||
|
if (p.name != "state") return "Fail state: ${p.name}"
|
||||||
|
if (p.get(A.B) != "12345") return "Fail value: ${p.get(A.B)}"
|
||||||
|
p.set(A.B, "OK")
|
||||||
|
|
||||||
|
return p[A.B]
|
||||||
|
}
|
||||||
+6
@@ -741,6 +741,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/callableReference/property"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/callableReference/property"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classObjectVar.kt")
|
||||||
|
public void testClassObjectVar() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/classObjectVar.kt");
|
||||||
|
doTestWithStdlib(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("delegated.kt")
|
@TestMetadata("delegated.kt")
|
||||||
public void testDelegated() throws Exception {
|
public void testDelegated() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/delegated.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/delegated.kt");
|
||||||
|
|||||||
+17
-1
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.descriptors.Visibility
|
|||||||
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import java.lang.reflect.Array
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
private fun calculateVisibility(modifiers: Int): Visibility {
|
private fun calculateVisibility(modifiers: Int): Visibility {
|
||||||
@@ -34,6 +36,9 @@ private fun calculateVisibility(modifiers: Int): Visibility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public val Class<*>.classLoader: ClassLoader
|
||||||
|
get() = getClassLoader() ?: ClassLoader.getSystemClassLoader()
|
||||||
|
|
||||||
public fun Class<*>.isEnumClassOrSpecializedEnumEntryClass(): Boolean =
|
public fun Class<*>.isEnumClassOrSpecializedEnumEntryClass(): Boolean =
|
||||||
javaClass<Enum<*>>().isAssignableFrom(this)
|
javaClass<Enum<*>>().isAssignableFrom(this)
|
||||||
|
|
||||||
@@ -41,4 +46,15 @@ public val Class<*>.fqName: FqName
|
|||||||
get() = classId.asSingleFqName().toSafe()
|
get() = classId.asSingleFqName().toSafe()
|
||||||
|
|
||||||
public val Class<*>.classId: ClassId
|
public val Class<*>.classId: ClassId
|
||||||
get() = getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName())) ?: ClassId.topLevel(FqName(getName()))
|
get() = when {
|
||||||
|
isPrimitive() -> throw IllegalArgumentException("Can't compute ClassId for primitive type: $this")
|
||||||
|
isArray() -> throw IllegalArgumentException("Can't compute ClassId for array type: $this")
|
||||||
|
getEnclosingMethod() != null, getEnclosingConstructor() != null, getSimpleName().isEmpty() -> {
|
||||||
|
val fqName = FqName(getName())
|
||||||
|
ClassId(fqName.parent(), FqNameUnsafe.topLevel(fqName.shortName()), /* local = */ true)
|
||||||
|
}
|
||||||
|
else -> getDeclaringClass()?.classId?.createNestedClassId(Name.identifier(getSimpleName())) ?: ClassId.topLevel(FqName(getName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Class<*>.createArrayType(): Class<*> =
|
||||||
|
Array.newInstance(this, 0).javaClass
|
||||||
|
|||||||
@@ -16,16 +16,20 @@
|
|||||||
|
|
||||||
package kotlin.reflect
|
package kotlin.reflect
|
||||||
|
|
||||||
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
|
||||||
public class IllegalPropertyAccessException(cause: IllegalAccessException) : Exception(cause.getMessage()) {
|
public class IllegalPropertyAccessException(cause: IllegalAccessException) : Exception(cause.getMessage()) {
|
||||||
{
|
{
|
||||||
|
[suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")]
|
||||||
(this as java.lang.Throwable).initCause(cause)
|
(this as java.lang.Throwable).initCause(cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
public class NoSuchPropertyException(cause: Exception? = null) : Exception() {
|
||||||
public class NoSuchPropertyException(cause: Exception) : Exception() {
|
|
||||||
{
|
{
|
||||||
(this as java.lang.Throwable).initCause(cause)
|
[suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")]
|
||||||
|
if (cause != null) {
|
||||||
|
(this as java.lang.Throwable).initCause(cause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class KotlinReflectionInternalError(message: String) : Error(message)
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import kotlin.reflect.KotlinReflectionInternalError
|
||||||
|
|
||||||
|
abstract class DescriptorBasedProperty(computeDescriptor: () -> PropertyDescriptor) {
|
||||||
|
protected abstract val container: KCallableContainerImpl
|
||||||
|
|
||||||
|
data class PropertyProtoData(
|
||||||
|
val proto: ProtoBuf.Callable,
|
||||||
|
val nameResolver: NameResolver,
|
||||||
|
val signature: JvmProtoBuf.JvmPropertySignature
|
||||||
|
)
|
||||||
|
|
||||||
|
protected val descriptor: PropertyDescriptor by ReflectProperties.lazySoft { computeDescriptor() }
|
||||||
|
|
||||||
|
protected val protoData: PropertyProtoData by ReflectProperties.lazyWeak {
|
||||||
|
val property = DescriptorUtils.unwrapFakeOverride(descriptor) as? DeserializedPropertyDescriptor
|
||||||
|
?: throw KotlinReflectionInternalError("Member property resolved incorrectly: $descriptor")
|
||||||
|
val proto = property.proto
|
||||||
|
if (!proto.hasExtension(JvmProtoBuf.propertySignature)) {
|
||||||
|
throw KotlinReflectionInternalError("Member property lacks JVM signature: $descriptor")
|
||||||
|
}
|
||||||
|
PropertyProtoData(proto, property.nameResolver, proto.getExtension(JvmProtoBuf.propertySignature))
|
||||||
|
}
|
||||||
|
|
||||||
|
val field: Field? by ReflectProperties.lazySoft {
|
||||||
|
val proto = protoData
|
||||||
|
if (!proto.signature.hasField()) null
|
||||||
|
else container.findFieldBySignature(proto.signature.getField(), proto.nameResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
val getter: Method? by ReflectProperties.lazySoft {
|
||||||
|
val proto = protoData
|
||||||
|
if (!proto.signature.hasGetter()) null
|
||||||
|
else container.findMethodBySignature(proto.signature.getGetter(), proto.nameResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
val setter: Method? by ReflectProperties.lazySoft {
|
||||||
|
val proto = protoData
|
||||||
|
if (!proto.signature.hasSetter()) null
|
||||||
|
else container.findMethodBySignature(proto.signature.getSetter(), proto.nameResolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||||
|
import kotlin.reflect.KotlinReflectionInternalError
|
||||||
|
|
||||||
|
abstract class KCallableContainerImpl {
|
||||||
|
abstract val jClass: Class<*>
|
||||||
|
|
||||||
|
abstract val scope: JetScope
|
||||||
|
|
||||||
|
protected fun findPropertyDescriptor(name: String): () -> PropertyDescriptor = {
|
||||||
|
val properties = scope.getProperties(Name.identifier(name))
|
||||||
|
if (properties.size() != 1) {
|
||||||
|
throw KotlinReflectionInternalError(
|
||||||
|
if (properties.isEmpty()) "Property $name not resolved in $this"
|
||||||
|
else "${properties.size()} properties with name $name resolved in $this"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.single() as PropertyDescriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,15 @@
|
|||||||
|
|
||||||
package kotlin.reflect.jvm.internal
|
package kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
import kotlin.reflect.*
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||||
import kotlin.jvm.internal.KotlinClass
|
import kotlin.jvm.internal.KotlinClass
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KMemberProperty
|
||||||
|
import kotlin.reflect.KMutableMemberProperty
|
||||||
|
import kotlin.reflect.KotlinReflectionInternalError
|
||||||
|
|
||||||
enum class KClassOrigin {
|
enum class KClassOrigin {
|
||||||
BUILT_IN
|
BUILT_IN
|
||||||
@@ -25,10 +32,21 @@ enum class KClassOrigin {
|
|||||||
FOREIGN
|
FOREIGN
|
||||||
}
|
}
|
||||||
|
|
||||||
class KClassImpl<T>(val jClass: Class<T>) : KClass<T> {
|
class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), KClass<T> {
|
||||||
// Don't use kotlin.properties.Delegates here because it's a Kotlin class which will invoke KClassImpl() in <clinit>,
|
// Don't use kotlin.properties.Delegates here because it's a Kotlin class which will invoke KClassImpl() in <clinit>,
|
||||||
// resulting in infinite recursion
|
// resulting in infinite recursion
|
||||||
|
|
||||||
|
val descriptor by ReflectProperties.lazySoft {(): ClassDescriptor ->
|
||||||
|
val moduleData = jClass.getOrCreateModule()
|
||||||
|
|
||||||
|
val found = moduleData.module.findClassAcrossModuleDependencies(jClass.classId)
|
||||||
|
if (found != null) return@lazySoft found
|
||||||
|
|
||||||
|
throw KotlinReflectionInternalError("Class not resolved: $jClass")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val scope: JetScope get() = descriptor.getDefaultType().getMemberScope()
|
||||||
|
|
||||||
private val origin by ReflectProperties.lazy {(): KClassOrigin ->
|
private val origin by ReflectProperties.lazy {(): KClassOrigin ->
|
||||||
if (jClass.isAnnotationPresent(javaClass<KotlinClass>())) {
|
if (jClass.isAnnotationPresent(javaClass<KotlinClass>())) {
|
||||||
KClassOrigin.KOTLIN
|
KClassOrigin.KOTLIN
|
||||||
@@ -39,21 +57,27 @@ class KClassImpl<T>(val jClass: Class<T>) : KClass<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun memberProperty(name: String): KMemberProperty<T, *> =
|
fun memberProperty(name: String): KMemberProperty<T, *> {
|
||||||
if (origin === KClassOrigin.KOTLIN) {
|
val computeDescriptor = findPropertyDescriptor(name)
|
||||||
KMemberPropertyImpl<T, Any>(name, this)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
KForeignMemberProperty<T, Any>(name, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mutableMemberProperty(name: String): KMutableMemberProperty<T, *> =
|
if (origin === KClassOrigin.KOTLIN) {
|
||||||
if (origin === KClassOrigin.KOTLIN) {
|
return KMemberPropertyImpl<T, Any>(this, computeDescriptor)
|
||||||
KMutableMemberPropertyImpl<T, Any>(name, this)
|
}
|
||||||
}
|
else {
|
||||||
else {
|
return KForeignMemberProperty<T, Any>(name, this)
|
||||||
KMutableForeignMemberProperty<T, Any>(name, this)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mutableMemberProperty(name: String): KMutableMemberProperty<T, *> {
|
||||||
|
val computeDescriptor = findPropertyDescriptor(name)
|
||||||
|
|
||||||
|
if (origin === KClassOrigin.KOTLIN) {
|
||||||
|
return KMutableMemberPropertyImpl<T, Any>(this, computeDescriptor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return KMutableForeignMemberProperty<T, Any>(name, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other is KClassImpl<*> && jClass == other.jClass
|
other is KClassImpl<*> && jClass == other.jClass
|
||||||
|
|||||||
@@ -16,39 +16,22 @@
|
|||||||
|
|
||||||
package kotlin.reflect.jvm.internal
|
package kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
import java.lang.reflect.*
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.IllegalPropertyAccessException
|
||||||
|
import kotlin.reflect.KMemberProperty
|
||||||
// TODO: properties of built-in classes
|
import kotlin.reflect.KMutableMemberProperty
|
||||||
|
|
||||||
open class KMemberPropertyImpl<T : Any, out R>(
|
open class KMemberPropertyImpl<T : Any, out R>(
|
||||||
override val name: String,
|
override val container: KClassImpl<T>,
|
||||||
protected val owner: KClassImpl<T>
|
computeDescriptor: () -> PropertyDescriptor
|
||||||
) : KMemberProperty<T, R>, KPropertyImpl<R> {
|
) : DescriptorBasedProperty(computeDescriptor), KMemberProperty<T, R>, KPropertyImpl<R> {
|
||||||
// TODO: extract, make lazy (weak?), use our descriptors knowledge
|
override val name: String get() = descriptor.getName().asString()
|
||||||
override val field: Field?
|
|
||||||
override val getter: Method?
|
|
||||||
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
field = owner.jClass.getDeclaredField(name)
|
|
||||||
}
|
|
||||||
catch (e: NoSuchFieldException) {
|
|
||||||
field = null
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
getter = owner.jClass.getMaybeDeclaredMethod(getterName(name))
|
|
||||||
}
|
|
||||||
catch (e: NoSuchMethodException) {
|
|
||||||
if (field == null) throw NoSuchPropertyException(e)
|
|
||||||
getter = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(receiver: T): R {
|
override fun get(receiver: T): R {
|
||||||
try {
|
try {
|
||||||
return (if (getter != null) getter!!(receiver) else field!!.get(receiver)) as R
|
val getter = getter
|
||||||
|
[suppress("UNCHECKED_CAST")]
|
||||||
|
return if (getter != null) getter(receiver) as R else field!!.get(receiver) as R
|
||||||
}
|
}
|
||||||
catch (e: IllegalAccessException) {
|
catch (e: IllegalAccessException) {
|
||||||
throw IllegalPropertyAccessException(e)
|
throw IllegalPropertyAccessException(e)
|
||||||
@@ -56,42 +39,27 @@ open class KMemberPropertyImpl<T : Any, out R>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
other is KMemberPropertyImpl<*, *> && name == other.name && owner == other.owner
|
other is KMemberPropertyImpl<*, *> && descriptor == other.descriptor
|
||||||
|
|
||||||
override fun hashCode(): Int =
|
override fun hashCode(): Int =
|
||||||
name.hashCode() * 31 + owner.hashCode()
|
descriptor.hashCode()
|
||||||
|
|
||||||
// TODO: include visibility, return type
|
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
"val ${owner.jClass.getName()}.$name"
|
ReflectionObjectRenderer.renderProperty(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class KMutableMemberPropertyImpl<T : Any, R>(
|
class KMutableMemberPropertyImpl<T : Any, R>(
|
||||||
name: String,
|
container: KClassImpl<T>,
|
||||||
owner: KClassImpl<T>
|
computeDescriptor: () -> PropertyDescriptor
|
||||||
) : KMutableMemberProperty<T, R>, KMutablePropertyImpl<R>, KMemberPropertyImpl<T, R>(name, owner) {
|
) : KMemberPropertyImpl<T, R>(container, computeDescriptor), KMutableMemberProperty<T, R>, KMutablePropertyImpl<R> {
|
||||||
override val setter: Method?
|
|
||||||
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
val returnType = if (getter != null) getter.getReturnType() else field!!.getType()
|
|
||||||
setter = owner.jClass.getMaybeDeclaredMethod(setterName(name), returnType)
|
|
||||||
}
|
|
||||||
catch (e: NoSuchMethodException) {
|
|
||||||
if (field == null) throw NoSuchPropertyException(e)
|
|
||||||
setter = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun set(receiver: T, value: R) {
|
override fun set(receiver: T, value: R) {
|
||||||
try {
|
try {
|
||||||
if (setter != null) setter!!(receiver, value) else field!!.set(receiver, value)
|
val setter = setter
|
||||||
|
if (setter != null) setter(receiver, value) else field!!.set(receiver, value)
|
||||||
}
|
}
|
||||||
catch (e: IllegalAccessException) {
|
catch (e: IllegalAccessException) {
|
||||||
throw IllegalPropertyAccessException(e)
|
throw IllegalPropertyAccessException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String =
|
|
||||||
"var ${owner.jClass.getName()}.$name"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
|
|
||||||
|
object ReflectionObjectRenderer {
|
||||||
|
private val renderer = DescriptorRenderer.FQ_NAMES_IN_TYPES
|
||||||
|
|
||||||
|
// TODO: include visibility, return type
|
||||||
|
fun renderProperty(descriptor: PropertyDescriptor): String {
|
||||||
|
fun StringBuilder.appendReceiverType(receiver: ReceiverParameterDescriptor?) {
|
||||||
|
if (receiver != null) {
|
||||||
|
append(renderer.renderType(receiver.getType()))
|
||||||
|
append(".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringBuilder {
|
||||||
|
append(if (descriptor.isVar()) "var " else "val ")
|
||||||
|
|
||||||
|
val dispatchReceiver = descriptor.getDispatchReceiverParameter()
|
||||||
|
val extensionReceiver = descriptor.getExtensionReceiverParameter()
|
||||||
|
|
||||||
|
appendReceiverType(dispatchReceiver)
|
||||||
|
|
||||||
|
val addParentheses = dispatchReceiver != null && extensionReceiver != null
|
||||||
|
if (addParentheses) append("(")
|
||||||
|
appendReceiverType(extensionReceiver)
|
||||||
|
if (addParentheses) append(")")
|
||||||
|
|
||||||
|
append(renderer.renderName(descriptor.getName()))
|
||||||
|
}.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.reflect.classLoader
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
|
||||||
|
// TODO: collect nulls periodically
|
||||||
|
private val moduleByClassLoader: ConcurrentMap<WeakClassLoaderBox, WeakReference<RuntimeModuleData>> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
private class WeakClassLoaderBox(classLoader: ClassLoader) {
|
||||||
|
val ref: WeakReference<ClassLoader> = WeakReference(classLoader)
|
||||||
|
|
||||||
|
// Identity hash code is saved because otherwise once the weak reference is GC'd we cannot compute it anymore
|
||||||
|
val identityHashCode: Int = System.identityHashCode(classLoader)
|
||||||
|
|
||||||
|
// Temporary strong reference to the class loader to ensure it won't get GC'd when we're inserting this box into the map
|
||||||
|
var temporaryStrongRef: ClassLoader? = classLoader
|
||||||
|
|
||||||
|
override fun equals(other: Any?) =
|
||||||
|
other is WeakClassLoaderBox && ref.get() === other.ref.get()
|
||||||
|
|
||||||
|
override fun hashCode() =
|
||||||
|
identityHashCode
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Class<*>.getOrCreateModule(): RuntimeModuleData {
|
||||||
|
val classLoader = this.classLoader
|
||||||
|
val key = WeakClassLoaderBox(classLoader)
|
||||||
|
|
||||||
|
val cached = moduleByClassLoader[key]
|
||||||
|
if (cached != null) {
|
||||||
|
cached.get()?.let { return it }
|
||||||
|
moduleByClassLoader.remove(key, cached)
|
||||||
|
}
|
||||||
|
|
||||||
|
val module = RuntimeModuleData.create(classLoader)
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
val ref = moduleByClassLoader.putIfAbsent(key, WeakReference(module))
|
||||||
|
if (ref == null) return module
|
||||||
|
|
||||||
|
val result = ref.get()
|
||||||
|
if (result != null) return result
|
||||||
|
moduleByClassLoader.remove(key, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
key.temporaryStrongRef = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,14 @@
|
|||||||
|
|
||||||
package kotlin.reflect.jvm.internal
|
package kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.reflect.classLoader
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||||
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||||
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
|
||||||
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
import kotlin.reflect.KotlinReflectionInternalError
|
||||||
|
|
||||||
private fun String.capitalizeWithJavaBeanConvention(): String {
|
private fun String.capitalizeWithJavaBeanConvention(): String {
|
||||||
if (length() > 1 && Character.isUpperCase(this[1])) return this
|
if (length() > 1 && Character.isUpperCase(this[1])) return this
|
||||||
@@ -36,3 +43,73 @@ private fun Class<*>.getMaybeDeclaredMethod(name: String, vararg parameterTypes:
|
|||||||
return getDeclaredMethod(name, *parameterTypes)
|
return getDeclaredMethod(name, *parameterTypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: check resulting method's return type
|
||||||
|
private fun KCallableContainerImpl.findMethodBySignature(signature: JvmProtoBuf.JvmMethodSignature, nameResolver: NameResolver): Method? {
|
||||||
|
val name = nameResolver.getString(signature.getName())
|
||||||
|
val classLoader = jClass.classLoader
|
||||||
|
val parameterTypes = signature.getParameterTypeList().map { jvmType ->
|
||||||
|
loadJvmType(jvmType, nameResolver, classLoader)
|
||||||
|
}.copyToArray()
|
||||||
|
return try {
|
||||||
|
jClass.getMaybeDeclaredMethod(name, *parameterTypes)
|
||||||
|
}
|
||||||
|
catch (e: NoSuchMethodException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check resulting field's type
|
||||||
|
private fun KCallableContainerImpl.findFieldBySignature(signature: JvmProtoBuf.JvmFieldSignature, nameResolver: NameResolver): Field? {
|
||||||
|
val name = nameResolver.getString(signature.getName())
|
||||||
|
val owner =
|
||||||
|
if (signature.getIsStaticInOuter())
|
||||||
|
jClass.getEnclosingClass()
|
||||||
|
?: throw KotlinReflectionInternalError("Inconsistent metadata for field $name in $jClass")
|
||||||
|
else jClass
|
||||||
|
|
||||||
|
return try {
|
||||||
|
owner.getDeclaredField(name)
|
||||||
|
}
|
||||||
|
catch (e: NoSuchFieldException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val PRIMITIVE_TYPES = mapOf(
|
||||||
|
VOID to Void.TYPE,
|
||||||
|
BOOLEAN to java.lang.Boolean.TYPE,
|
||||||
|
CHAR to java.lang.Character.TYPE,
|
||||||
|
BYTE to java.lang.Byte.TYPE,
|
||||||
|
SHORT to java.lang.Short.TYPE,
|
||||||
|
INT to java.lang.Integer.TYPE,
|
||||||
|
FLOAT to java.lang.Float.TYPE,
|
||||||
|
LONG to java.lang.Long.TYPE,
|
||||||
|
DOUBLE to java.lang.Double.TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun loadJvmType(
|
||||||
|
type: JvmProtoBuf.JvmType,
|
||||||
|
nameResolver: NameResolver,
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
arrayDimension: Int = type.getArrayDimension()
|
||||||
|
): Class<*> {
|
||||||
|
if (arrayDimension > 0) {
|
||||||
|
// TODO: test multi-dimensional arrays
|
||||||
|
return loadJvmType(type, nameResolver, classLoader, arrayDimension - 1).createArrayType()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.hasPrimitiveType()) {
|
||||||
|
return PRIMITIVE_TYPES[type.getPrimitiveType()]
|
||||||
|
?: throw KotlinReflectionInternalError("Unknown primitive type: ${type.getPrimitiveType()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.hasClassFqName()) {
|
||||||
|
val fqName = nameResolver.getFqName(type.getClassFqName())
|
||||||
|
return classLoader.loadClass(fqName.asString())
|
||||||
|
}
|
||||||
|
|
||||||
|
throw KotlinReflectionInternalError("Inconsistent metadata for JVM type")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user