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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testDelegated() throws Exception {
|
||||
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.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import java.lang.reflect.Array
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
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 =
|
||||
javaClass<Enum<*>>().isAssignableFrom(this)
|
||||
|
||||
@@ -41,4 +46,15 @@ public val Class<*>.fqName: FqName
|
||||
get() = classId.asSingleFqName().toSafe()
|
||||
|
||||
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
|
||||
|
||||
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
public class IllegalPropertyAccessException(cause: IllegalAccessException) : Exception(cause.getMessage()) {
|
||||
{
|
||||
[suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")]
|
||||
(this as java.lang.Throwable).initCause(cause)
|
||||
}
|
||||
}
|
||||
|
||||
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
public class NoSuchPropertyException(cause: Exception) : Exception() {
|
||||
public class NoSuchPropertyException(cause: Exception? = null) : 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
|
||||
|
||||
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.reflect.KClass
|
||||
import kotlin.reflect.KMemberProperty
|
||||
import kotlin.reflect.KMutableMemberProperty
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
|
||||
enum class KClassOrigin {
|
||||
BUILT_IN
|
||||
@@ -25,10 +32,21 @@ enum class KClassOrigin {
|
||||
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>,
|
||||
// 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 ->
|
||||
if (jClass.isAnnotationPresent(javaClass<KotlinClass>())) {
|
||||
KClassOrigin.KOTLIN
|
||||
@@ -39,21 +57,27 @@ class KClassImpl<T>(val jClass: Class<T>) : KClass<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fun memberProperty(name: String): KMemberProperty<T, *> =
|
||||
if (origin === KClassOrigin.KOTLIN) {
|
||||
KMemberPropertyImpl<T, Any>(name, this)
|
||||
}
|
||||
else {
|
||||
KForeignMemberProperty<T, Any>(name, this)
|
||||
}
|
||||
fun memberProperty(name: String): KMemberProperty<T, *> {
|
||||
val computeDescriptor = findPropertyDescriptor(name)
|
||||
|
||||
fun mutableMemberProperty(name: String): KMutableMemberProperty<T, *> =
|
||||
if (origin === KClassOrigin.KOTLIN) {
|
||||
KMutableMemberPropertyImpl<T, Any>(name, this)
|
||||
}
|
||||
else {
|
||||
KMutableForeignMemberProperty<T, Any>(name, this)
|
||||
}
|
||||
if (origin === KClassOrigin.KOTLIN) {
|
||||
return KMemberPropertyImpl<T, Any>(this, computeDescriptor)
|
||||
}
|
||||
else {
|
||||
return KForeignMemberProperty<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 =
|
||||
other is KClassImpl<*> && jClass == other.jClass
|
||||
|
||||
@@ -16,39 +16,22 @@
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import java.lang.reflect.*
|
||||
import kotlin.reflect.*
|
||||
|
||||
// TODO: properties of built-in classes
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import kotlin.reflect.IllegalPropertyAccessException
|
||||
import kotlin.reflect.KMemberProperty
|
||||
import kotlin.reflect.KMutableMemberProperty
|
||||
|
||||
open class KMemberPropertyImpl<T : Any, out R>(
|
||||
override val name: String,
|
||||
protected val owner: KClassImpl<T>
|
||||
) : KMemberProperty<T, R>, KPropertyImpl<R> {
|
||||
// TODO: extract, make lazy (weak?), use our descriptors knowledge
|
||||
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 val container: KClassImpl<T>,
|
||||
computeDescriptor: () -> PropertyDescriptor
|
||||
) : DescriptorBasedProperty(computeDescriptor), KMemberProperty<T, R>, KPropertyImpl<R> {
|
||||
override val name: String get() = descriptor.getName().asString()
|
||||
|
||||
override fun get(receiver: T): R {
|
||||
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) {
|
||||
throw IllegalPropertyAccessException(e)
|
||||
@@ -56,42 +39,27 @@ open class KMemberPropertyImpl<T : Any, out R>(
|
||||
}
|
||||
|
||||
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 =
|
||||
name.hashCode() * 31 + owner.hashCode()
|
||||
descriptor.hashCode()
|
||||
|
||||
// TODO: include visibility, return type
|
||||
override fun toString(): String =
|
||||
"val ${owner.jClass.getName()}.$name"
|
||||
ReflectionObjectRenderer.renderProperty(descriptor)
|
||||
}
|
||||
|
||||
|
||||
class KMutableMemberPropertyImpl<T : Any, R>(
|
||||
name: String,
|
||||
owner: KClassImpl<T>
|
||||
) : KMutableMemberProperty<T, R>, KMutablePropertyImpl<R>, KMemberPropertyImpl<T, R>(name, owner) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
container: KClassImpl<T>,
|
||||
computeDescriptor: () -> PropertyDescriptor
|
||||
) : KMemberPropertyImpl<T, R>(container, computeDescriptor), KMutableMemberProperty<T, R>, KMutablePropertyImpl<R> {
|
||||
override fun set(receiver: T, value: R) {
|
||||
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) {
|
||||
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
|
||||
|
||||
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 kotlin.reflect.KotlinReflectionInternalError
|
||||
|
||||
private fun String.capitalizeWithJavaBeanConvention(): String {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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