Use descriptors for reflection on extension properties
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.desc
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
@@ -26,12 +27,25 @@ abstract class KCallableContainerImpl {
|
||||
|
||||
abstract val scope: JetScope
|
||||
|
||||
protected fun findPropertyDescriptor(name: String): () -> PropertyDescriptor = {
|
||||
val properties = scope.getProperties(Name.identifier(name))
|
||||
protected fun findPropertyDescriptor(name: String, receiverParameterClass: Class<*>? = null): () -> PropertyDescriptor = {
|
||||
val receiverDesc = receiverParameterClass?.desc
|
||||
|
||||
val properties = scope
|
||||
.getProperties(Name.identifier(name))
|
||||
.filter { descriptor ->
|
||||
descriptor is PropertyDescriptor &&
|
||||
descriptor.getName().asString() == name &&
|
||||
with(descriptor.getExtensionReceiverParameter()) {
|
||||
(this == null && receiverDesc == null) ||
|
||||
(this != null && RuntimeTypeMapper.mapTypeToJvmDesc(getType()) == receiverDesc)
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.size() != 1) {
|
||||
val debugText = if (receiverParameterClass == null) name else "${receiverParameterClass.getSimpleName()}.$name"
|
||||
throw KotlinReflectionInternalError(
|
||||
if (properties.isEmpty()) "Property $name not resolved in $this"
|
||||
else "${properties.size()} properties with name $name resolved in $this"
|
||||
if (properties.isEmpty()) "Property '$debugText' not resolved in $this"
|
||||
else "${properties.size()} properties '$debugText' resolved in $this"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
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
|
||||
@@ -38,7 +37,7 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
|
||||
|
||||
val descriptor by ReflectProperties.lazySoft {(): ClassDescriptor ->
|
||||
val moduleData = jClass.getOrCreateModule()
|
||||
val classId = jClass.classId
|
||||
val classId = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass)
|
||||
|
||||
val descriptor =
|
||||
if (classId.isLocal()) moduleData.localClassResolver.resolveLocalClass(classId)
|
||||
|
||||
@@ -32,21 +32,19 @@ class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KP
|
||||
override val scope: JetScope get() = descriptor.getMemberScope()
|
||||
|
||||
fun topLevelVariable(name: String): KTopLevelVariable<*> {
|
||||
val descriptor = findPropertyDescriptor(name)
|
||||
return KTopLevelVariableImpl(this, descriptor)
|
||||
return KTopLevelVariableImpl(this, findPropertyDescriptor(name))
|
||||
}
|
||||
|
||||
fun mutableTopLevelVariable(name: String): KMutableTopLevelVariable<*> {
|
||||
val descriptor = findPropertyDescriptor(name)
|
||||
return KMutableTopLevelVariableImpl(this, descriptor)
|
||||
return KMutableTopLevelVariableImpl(this, findPropertyDescriptor(name))
|
||||
}
|
||||
|
||||
fun <T> topLevelExtensionProperty(name: String, receiver: Class<T>): KTopLevelExtensionProperty<T, *> {
|
||||
return KTopLevelExtensionPropertyImpl(name, this, receiver)
|
||||
return KTopLevelExtensionPropertyImpl(this, findPropertyDescriptor(name, receiver))
|
||||
}
|
||||
|
||||
fun <T> mutableTopLevelExtensionProperty(name: String, receiver: Class<T>): KMutableTopLevelExtensionProperty<T, *> {
|
||||
return KMutableTopLevelExtensionPropertyImpl(name, this, receiver)
|
||||
return KMutableTopLevelExtensionPropertyImpl(this, findPropertyDescriptor(name, receiver))
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
|
||||
+19
-64
@@ -16,27 +16,24 @@
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import java.lang.reflect.*
|
||||
import kotlin.reflect.*
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.IllegalPropertyAccessException
|
||||
import kotlin.reflect.KMutableTopLevelExtensionProperty
|
||||
import kotlin.reflect.KTopLevelExtensionProperty
|
||||
|
||||
open class KTopLevelExtensionPropertyImpl<T, out R>(
|
||||
override val name: String,
|
||||
protected val owner: KPackageImpl,
|
||||
protected val receiverClass: Class<T>
|
||||
) : KTopLevelExtensionProperty<T, R>, KPropertyImpl<R> {
|
||||
override val field: Field? get() = null
|
||||
override val container: KPackageImpl,
|
||||
computeDescriptor: () -> PropertyDescriptor
|
||||
) : DescriptorBasedProperty(computeDescriptor), KTopLevelExtensionProperty<T, R>, KPropertyImpl<R> {
|
||||
override val name: String get() = descriptor.getName().asString()
|
||||
|
||||
// TODO: extract, make lazy (weak?), use our descriptors knowledge, support Java fields
|
||||
override val getter: Method = try {
|
||||
owner.jClass.getMethod(getterName(name), receiverClass)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
throw NoSuchPropertyException(e)
|
||||
}
|
||||
override val getter: Method get() = super<DescriptorBasedProperty>.getter!!
|
||||
|
||||
override fun get(receiver: T): R {
|
||||
try {
|
||||
return getter(null, receiver) as R
|
||||
[suppress("UNCHECKED_CAST")]
|
||||
return getter.invoke(null, receiver) as R
|
||||
}
|
||||
catch (e: IllegalAccessException) {
|
||||
throw IllegalPropertyAccessException(e)
|
||||
@@ -44,27 +41,20 @@ open class KTopLevelExtensionPropertyImpl<T, out R>(
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is KTopLevelExtensionPropertyImpl<*, *> && name == other.name && owner == other.owner && receiverClass == other.receiverClass
|
||||
other is KTopLevelExtensionPropertyImpl<*, *> && descriptor == other.descriptor
|
||||
|
||||
override fun hashCode(): Int =
|
||||
(name.hashCode() * 31 + owner.hashCode()) * 31 + receiverClass.hashCode()
|
||||
descriptor.hashCode()
|
||||
|
||||
// TODO: include visibility, return type, maybe package
|
||||
override fun toString(): String =
|
||||
"val ${mapJavaClassToKotlin(receiverClass.getName())}.$name"
|
||||
ReflectionObjectRenderer.renderProperty(descriptor)
|
||||
}
|
||||
|
||||
class KMutableTopLevelExtensionPropertyImpl<T, R>(
|
||||
name: String,
|
||||
owner: KPackageImpl,
|
||||
receiverClass: Class<T>
|
||||
) : KMutableTopLevelExtensionProperty<T, R>, KMutablePropertyImpl<R>, KTopLevelExtensionPropertyImpl<T, R>(name, owner, receiverClass) {
|
||||
override val setter: Method = try {
|
||||
owner.jClass.getMethod(setterName(name), receiverClass, getter.getReturnType())
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
throw NoSuchPropertyException(e)
|
||||
}
|
||||
container: KPackageImpl,
|
||||
computeDescriptor: () -> PropertyDescriptor
|
||||
) : KTopLevelExtensionPropertyImpl<T, R>(container, computeDescriptor), KMutableTopLevelExtensionProperty<T, R>, KMutablePropertyImpl<R> {
|
||||
override val setter: Method get() = super<KTopLevelExtensionPropertyImpl>.setter!!
|
||||
|
||||
override fun set(receiver: T, value: R) {
|
||||
try {
|
||||
@@ -74,39 +64,4 @@ class KMutableTopLevelExtensionPropertyImpl<T, R>(
|
||||
throw IllegalPropertyAccessException(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
"var ${mapJavaClassToKotlin(receiverClass.getName())}.$name"
|
||||
}
|
||||
|
||||
private fun mapJavaClassToKotlin(name: String): String {
|
||||
if (name[0].isLowerCase()) {
|
||||
return when (name) {
|
||||
"boolean" -> "kotlin.Boolean"
|
||||
"char" -> "kotlin.Char"
|
||||
"byte" -> "kotlin.Byte"
|
||||
"short" -> "kotlin.Short"
|
||||
"int" -> "kotlin.Int"
|
||||
"float" -> "kotlin.Float"
|
||||
"long" -> "kotlin.Long"
|
||||
"double" -> "kotlin.Double"
|
||||
else -> name
|
||||
}
|
||||
}
|
||||
if (name[0] == '[') {
|
||||
val element = name.substring(1)
|
||||
return when (element[0]) {
|
||||
'Z' -> "kotlin.BooleanArray"
|
||||
'C' -> "kotlin.CharArray"
|
||||
'B' -> "kotlin.ByteArray"
|
||||
'S' -> "kotlin.ShortArray"
|
||||
'I' -> "kotlin.IntArray"
|
||||
'F' -> "kotlin.FloatArray"
|
||||
'J' -> "kotlin.LongArray"
|
||||
'D' -> "kotlin.DoubleArray"
|
||||
'L' -> "kotlin.Array<${mapJavaClassToKotlin(element.substring(1, element.length() - 1))}>"
|
||||
else -> "kotlin.Array<${mapJavaClassToKotlin(element)}>"
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.desc
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMapBuilder
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
object RuntimeTypeMapper : JavaToKotlinClassMapBuilder() {
|
||||
private val kotlinFqNameToJvmDesc = linkedMapOf<FqName, String>()
|
||||
private val kotlinFqNameToJvmDescNullable = linkedMapOf<FqName, String>()
|
||||
private val jvmDescToKotlinClassId = linkedMapOf<String, ClassId>();
|
||||
|
||||
{
|
||||
init()
|
||||
initPrimitives()
|
||||
}
|
||||
|
||||
private fun initPrimitives() {
|
||||
val builtIns = KotlinBuiltIns.getInstance()
|
||||
|
||||
for (type in JvmPrimitiveType.values()) {
|
||||
val primitiveType = type.getPrimitiveType()
|
||||
val primitiveClassDescriptor = builtIns.getPrimitiveClassDescriptor(primitiveType)
|
||||
|
||||
recordMapping(primitiveClassDescriptor, type.getDesc())
|
||||
recordMapping(builtIns.getPrimitiveArrayClassDescriptor(primitiveType), "[" + type.getDesc())
|
||||
|
||||
recordNullableMapping(primitiveClassDescriptor, ClassId.topLevel(type.getWrapperFqName()).desc)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordMapping(kotlinDescriptor: ClassDescriptor, jvmDesc: String) {
|
||||
kotlinFqNameToJvmDesc[DescriptorUtils.getFqNameSafe(kotlinDescriptor)] = jvmDesc
|
||||
jvmDescToKotlinClassId[jvmDesc] = kotlinDescriptor.classId
|
||||
|
||||
val defaultObject = kotlinDescriptor.getDefaultObjectDescriptor()
|
||||
if (defaultObject != null) {
|
||||
// TODO: see org.jetbrains.kotlin.codegen.intrinsics.IntrinsicObjects, extract that logic to core/
|
||||
recordMapping(defaultObject, "Lkotlin/jvm/internal/${kotlinDescriptor.getName().asString()}DefaultObject;")
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordNullableMapping(kotlinDescriptor: ClassDescriptor, jvmDesc: String) {
|
||||
kotlinFqNameToJvmDescNullable[DescriptorUtils.getFqNameSafe(kotlinDescriptor)] = jvmDesc
|
||||
jvmDescToKotlinClassId[jvmDesc] = kotlinDescriptor.classId
|
||||
}
|
||||
|
||||
override fun register(javaClass: Class<*>, kotlinDescriptor: ClassDescriptor, direction: JavaToKotlinClassMapBuilder.Direction) {
|
||||
recordMapping(kotlinDescriptor, javaClass.classId.desc)
|
||||
}
|
||||
|
||||
override fun register(javaClass: Class<*>, kotlinDescriptor: ClassDescriptor, kotlinMutableDescriptor: ClassDescriptor) {
|
||||
// TODO: readonly collection mapping just rewrites the mutable one, improve readability here
|
||||
register(javaClass, kotlinMutableDescriptor, JavaToKotlinClassMapBuilder.Direction.BOTH)
|
||||
register(javaClass, kotlinDescriptor, JavaToKotlinClassMapBuilder.Direction.BOTH)
|
||||
}
|
||||
|
||||
fun mapTypeToJvmDesc(type: JetType): String {
|
||||
val classifier = type.getConstructor().getDeclarationDescriptor()
|
||||
if (classifier is TypeParameterDescriptor) return mapTypeToJvmDesc(classifier.getUpperBounds().first())
|
||||
|
||||
val classDescriptor = classifier as ClassDescriptor
|
||||
val fqNameUnsafe = DescriptorUtils.getFqName(classDescriptor)
|
||||
if (fqNameUnsafe.isSafe()) {
|
||||
val fqName = fqNameUnsafe.toSafe()
|
||||
if (TypeUtils.isNullableType(type)) {
|
||||
kotlinFqNameToJvmDescNullable[fqName]?.let { return it }
|
||||
}
|
||||
kotlinFqNameToJvmDesc[fqName]?.let { return it }
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isArray(type)) {
|
||||
var dimension = 0
|
||||
var elementType = type
|
||||
|
||||
while (KotlinBuiltIns.isArray(elementType)) {
|
||||
elementType = KotlinBuiltIns.getInstance().getArrayElementType(elementType)
|
||||
dimension++
|
||||
}
|
||||
|
||||
return "[".repeat(dimension) + mapTypeToJvmDesc(TypeUtils.makeNullable(elementType))
|
||||
}
|
||||
|
||||
return classDescriptor.classId.desc
|
||||
}
|
||||
|
||||
fun mapJvmClassToKotlinClassId(klass: Class<*>): ClassId {
|
||||
return jvmDescToKotlinClassId[klass.desc] ?: klass.classId
|
||||
}
|
||||
|
||||
private val ClassId.desc: String
|
||||
get() = "L${JvmClassName.byClassId(this).getInternalName()};"
|
||||
}
|
||||
@@ -28,14 +28,6 @@ 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
|
||||
return Character.toUpperCase(this[0]) + substring(1, length())
|
||||
}
|
||||
|
||||
private fun getterName(propertyName: String): String = "get" + propertyName.capitalizeWithJavaBeanConvention()
|
||||
private fun setterName(propertyName: String): String = "set" + propertyName.capitalizeWithJavaBeanConvention()
|
||||
|
||||
|
||||
private fun Class<*>.getMaybeDeclaredMethod(name: String, vararg parameterTypes: Class<*>): Method {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user