Use descriptors for reflection on top level properties

This also allows to obtain a Field object from the package part (see
mapping.kt)
This commit is contained in:
Alexander Udalov
2015-02-19 20:28:58 +03:00
parent d1debf586c
commit 1a618deeab
9 changed files with 116 additions and 51 deletions
@@ -0,0 +1,24 @@
import kotlin.platform.platformName
import kotlin.reflect.jvm.*
var state: String = "value"
[platformName("getter")]
get
[platformName("setter")]
set
fun box(): String {
val p = ::state
if (p.name != "state") return "Fail name: ${p.name}"
if (p.get() != "value") return "Fail get: ${p.get()}"
p.set("OK")
val getterName = p.javaGetter!!.getName()
if (getterName != "getter") return "Fail getter name: $getterName"
val setterName = p.javaSetter!!.getName()
if (setterName != "setter") return "Fail setter name: $setterName"
return p.get()
}
@@ -6,8 +6,9 @@ var topLevel = "123"
fun box(): String {
val p = ::topLevel
// TODO: uncomment when fields are loaded from package parts
// assert(p.javaField != null, "Fail p field")
assert(p.javaField != null, "Fail p field")
val field = p.javaField!!
assert(field.getDeclaringClass().getName() contains "_DefaultPackage\$", "Fail field not in package part: ${field.getDeclaringClass()}")
val getter = p.javaGetter!!
val setter = p.javaSetter!!
@@ -789,6 +789,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
doTestWithStdlib(fileName);
}
@TestMetadata("platformNamedAccessors.kt")
public void testPlatformNamedAccessors() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/platformNamedAccessors.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("privateClassVal.kt")
public void testPrivateClassVal() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/callableReference/property/privateClassVal.kt");
@@ -50,16 +50,16 @@ abstract class DescriptorBasedProperty(computeDescriptor: () -> PropertyDescript
val field: Field? by ReflectProperties.lazySoft {
val proto = protoData
if (!proto.signature.hasField()) null
else container.findFieldBySignature(proto.signature.getField(), proto.nameResolver)
else container.findFieldBySignature(proto.proto, proto.signature.getField(), proto.nameResolver)
}
val getter: Method? by ReflectProperties.lazySoft {
open 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 {
open val setter: Method? by ReflectProperties.lazySoft {
val proto = protoData
if (!proto.signature.hasSetter()) null
else container.findMethodBySignature(proto.signature.getSetter(), proto.nameResolver)
@@ -16,12 +16,39 @@
package kotlin.reflect.jvm.internal
import kotlin.reflect.KPackage
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.resolve.scopes.JetScope
import kotlin.jvm.internal.KotlinPackage
import kotlin.reflect.*
private val KOTLIN_PACKAGE_ANNOTATION_CLASS = javaClass<KotlinPackage>()
class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KPackage {
val descriptor by ReflectProperties.lazySoft {
val moduleData = jClass.getOrCreateModule()
val fqName = jClass.classId.getPackageFqName()
moduleData.module.getPackage(fqName) ?: throw KotlinReflectionInternalError("Package not resolved: $fqName")
}
override val scope: JetScope get() = descriptor.getMemberScope()
fun topLevelVariable(name: String): KTopLevelVariable<*> {
val descriptor = findPropertyDescriptor(name)
return KTopLevelVariableImpl(this, descriptor)
}
fun mutableTopLevelVariable(name: String): KMutableTopLevelVariable<*> {
val descriptor = findPropertyDescriptor(name)
return KMutableTopLevelVariableImpl(this, descriptor)
}
fun <T> topLevelExtensionProperty(name: String, receiver: Class<T>): KTopLevelExtensionProperty<T, *> {
return KTopLevelExtensionPropertyImpl(name, this, receiver)
}
fun <T> mutableTopLevelExtensionProperty(name: String, receiver: Class<T>): KMutableTopLevelExtensionProperty<T, *> {
return KMutableTopLevelExtensionPropertyImpl(name, this, receiver)
}
class KPackageImpl(val jClass: Class<*>) : KPackage {
override fun equals(other: Any?): Boolean =
other is KPackageImpl && jClass == other.jClass
@@ -30,7 +57,7 @@ class KPackageImpl(val jClass: Class<*>) : KPackage {
override fun toString(): String {
val name = jClass.getName()
return "package " + if (jClass.isAnnotationPresent(KOTLIN_PACKAGE_ANNOTATION_CLASS)) {
return "package " + if (jClass.isAnnotationPresent(javaClass<KotlinPackage>())) {
if (name == "_DefaultPackage") "<default>" else name.substringBeforeLast(".")
}
else name
@@ -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.KMutableTopLevelVariable
import kotlin.reflect.KTopLevelVariable
open class KTopLevelVariableImpl<out R>(
override val name: String,
protected val owner: KPackageImpl
) : KTopLevelVariable<R>, KVariableImpl<R> {
// TODO: load the field from the corresponding package part
override val field: Field? get() = null
override val container: KPackageImpl,
computeDescriptor: () -> PropertyDescriptor
) : DescriptorBasedProperty(computeDescriptor), KTopLevelVariable<R>, KVariableImpl<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))
}
catch (e: NoSuchMethodException) {
throw NoSuchPropertyException(e)
}
override val getter: Method get() = super<DescriptorBasedProperty>.getter!!
override fun get(): R {
try {
return getter(null) as R
[suppress("UNCHECKED_CAST")]
return getter.invoke(null) as R
}
catch (e: IllegalAccessException) {
throw IllegalPropertyAccessException(e)
@@ -44,26 +41,20 @@ open class KTopLevelVariableImpl<out R>(
}
override fun equals(other: Any?): Boolean =
other is KTopLevelVariableImpl<*> && name == other.name && owner == other.owner
other is KTopLevelVariableImpl<*> && descriptor == other.descriptor
override fun hashCode(): Int =
name.hashCode() * 31 + owner.hashCode()
descriptor.hashCode()
// TODO: include visibility, return type, maybe package
override fun toString(): String =
"val $name"
ReflectionObjectRenderer.renderProperty(descriptor)
}
class KMutableTopLevelVariableImpl<R>(
name: String,
owner: KPackageImpl
) : KMutableTopLevelVariable<R>, KMutableVariableImpl<R>, KTopLevelVariableImpl<R>(name, owner) {
override val setter: Method = try {
owner.jClass.getMethod(setterName(name), getter.getReturnType())
}
catch (e: NoSuchMethodException) {
throw NoSuchPropertyException(e)
}
container: KPackageImpl,
computeDescriptor: () -> PropertyDescriptor
) : KTopLevelVariableImpl<R>(container, computeDescriptor), KMutableTopLevelVariable<R>, KMutableVariableImpl<R> {
override val setter: Method get() = super<KTopLevelVariableImpl>.setter!!
override fun set(value: R) {
try {
@@ -73,7 +64,4 @@ class KMutableTopLevelVariableImpl<R>(
throw IllegalPropertyAccessException(e)
}
}
override fun toString(): String =
"var $name"
}
@@ -48,21 +48,21 @@ public class ReflectionFactoryImpl extends ReflectionFactory {
@Override
public KTopLevelVariable topLevelVariable(String name, KPackage owner) {
return new KTopLevelVariableImpl(name, ((KPackageImpl) owner));
return ((KPackageImpl) owner).topLevelVariable(name);
}
@Override
public KMutableTopLevelVariable mutableTopLevelVariable(String name, KPackage owner) {
return new KMutableTopLevelVariableImpl(name, (KPackageImpl) owner);
return ((KPackageImpl) owner).mutableTopLevelVariable(name);
}
@Override
public KTopLevelExtensionProperty topLevelExtensionProperty(String name, KPackage owner, Class receiver) {
return new KTopLevelExtensionPropertyImpl(name, (KPackageImpl) owner, receiver);
return ((KPackageImpl) owner).topLevelExtensionProperty(name, receiver);
}
@Override
public KMutableTopLevelExtensionProperty mutableTopLevelExtensionProperty(String name, KPackage owner, Class receiver) {
return new KMutableTopLevelExtensionPropertyImpl(name, (KPackageImpl) owner, receiver);
return ((KPackageImpl) owner).mutableTopLevelExtensionProperty(name, receiver);
}
}
@@ -16,8 +16,11 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.java.structure.reflect.classLoader
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
@@ -61,13 +64,26 @@ private fun KCallableContainerImpl.findMethodBySignature(signature: JvmProtoBuf.
}
// TODO: check resulting field's type
private fun KCallableContainerImpl.findFieldBySignature(signature: JvmProtoBuf.JvmFieldSignature, nameResolver: NameResolver): Field? {
private fun KCallableContainerImpl.findFieldBySignature(
proto: ProtoBuf.Callable,
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
when {
proto.hasExtension(JvmProtoBuf.implClassName) -> {
val implClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName))
// TODO: store fq name of impl class name in jvm_descriptors.proto
val classId = ClassId(jClass.classId.getPackageFqName(), implClassName)
jClass.classLoader.loadClass(classId.asSingleFqName().asString())
}
signature.getIsStaticInOuter() -> {
jClass.getEnclosingClass() ?: throw KotlinReflectionInternalError("Inconsistent metadata for field $name in $jClass")
}
else -> jClass
}
return try {
owner.getDeclaredField(name)
@@ -16,7 +16,9 @@
package kotlin.reflect.jvm
import java.lang.reflect.*
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import kotlin.jvm.internal.Reflection
import kotlin.reflect.*
import kotlin.reflect.jvm.internal.*
@@ -37,7 +39,8 @@ public val KMutableProperty<*>.javaSetter: Method?
get() = (this as? KMutablePropertyImpl<*>)?.setter
// TODO: val KTopLevelVariable<*>.javaField: Field
public val KTopLevelVariable<*>.javaField: Field?
get() = (this as KPropertyImpl<*>).field
public val KTopLevelVariable<*>.javaGetter: Method
get() = (this as KTopLevelVariableImpl<*>).getter