Support :: references to Java instance fields in codegen

#KT-1183 In Progress
This commit is contained in:
Alexander Udalov
2014-05-30 21:21:03 +04:00
parent 5ab83aad8a
commit a8e1de48b8
9 changed files with 98 additions and 9 deletions
@@ -50,6 +50,7 @@ import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor;
import org.jetbrains.jet.lang.resolve.java.descriptor.SamConstructorDescriptor;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -2461,7 +2462,16 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
v.anew(propertyType);
v.dup();
v.visitLdcInsn(descriptor.getName().asString());
v.getstatic(reflectionFieldOwner, reflectionFieldName, ownerType.getDescriptor());
if (containingDeclaration instanceof JavaClassDescriptor) {
v.aconst(Type.getObjectType(reflectionFieldOwner));
v.invokestatic("kotlin/reflect/jvm/internal/InternalPackage", "foreignKotlinClass",
Type.getMethodDescriptor(K_CLASS_IMPL_TYPE, getType(Class.class)), false);
}
else {
// TODO: built-in classes
v.getstatic(reflectionFieldOwner, reflectionFieldName, ownerType.getDescriptor());
}
String constructorDesc;
if (receiverParameter != null) {
@@ -0,0 +1,3 @@
public class publicFinalField {
public final String field = "OK";
}
@@ -0,0 +1 @@
fun box() = (publicFinalField::field).get(publicFinalField())
@@ -0,0 +1,3 @@
public class publicMutableField {
public int field = 239;
}
@@ -0,0 +1,11 @@
import publicMutableField as A
fun box(): String {
val a = A()
val f = A::field
if (f.get(a) != 239) return "Fail 1: ${f.get(a)}"
f[a] = 42
if (f.get(a) != 42) return "Fail 2: ${f.get(a)}"
if (f.get(a) != 42) return "Fail 2: ${f.get(a)}"
return "OK"
}
@@ -86,6 +86,16 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod
doTestAgainstJava("compiler/testData/codegen/boxAgainstJava/callableReference/constructor.kt");
}
@TestMetadata("publicFinalField.kt")
public void testPublicFinalField() throws Exception {
doTestAgainstJava("compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt");
}
@TestMetadata("publicMutableField.kt")
public void testPublicMutableField() throws Exception {
doTestAgainstJava("compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt");
}
}
@TestMetadata("compiler/testData/codegen/boxAgainstJava/constructor")
@@ -16,6 +16,21 @@
package kotlin.reflect.jvm.internal
enum class KClassOrigin {
BUILT_IN
KOTLIN
FOREIGN
}
class KClassImpl<out T>(
val jClass: Class<T>
) : KClass<T>
) : KClass<T> {
val origin: KClassOrigin =
if (K_OBJECT_CLASS.isAssignableFrom(jClass)) {
KClassOrigin.KOTLIN
}
else {
KClassOrigin.FOREIGN
// TODO: built-in classes
}
}
@@ -16,17 +16,32 @@
package kotlin.reflect.jvm.internal
import java.lang.reflect.Field
import java.lang.reflect.Method
open class KMemberPropertyImpl<T, out R>(
name: String,
protected val owner: KClassImpl<T>
) : KMemberProperty<T, R>, KPropertyImpl<R>(name) {
// TODO: extract, make lazy (weak?), use our descriptors knowledge, support Java fields
protected val getter: Method = owner.jClass.getMethod(getterName(name))
protected val field: Field? =
if (owner.origin == KClassOrigin.FOREIGN) {
owner.jClass.getField(name)
}
else null
// TODO: extract, make lazy (weak?), use our descriptors knowledge
protected val getter: Method? =
if (owner.origin == KClassOrigin.KOTLIN) {
owner.jClass.getMethod(getterName(name))
}
else null
// TODO: built-in classes
override fun get(receiver: T): R {
return getter(receiver) as R
if (getter != null) {
return getter!!(receiver) as R
}
return field!!.get(receiver) as R
}
}
@@ -34,9 +49,17 @@ class KMutableMemberPropertyImpl<T, R>(
name: String,
owner: KClassImpl<T>
) : KMutableMemberProperty<T, R>, KMemberPropertyImpl<T, R>(name, owner) {
private val setter = owner.jClass.getMethod(setterName(name), getter.getReturnType()!!)
private val setter: Method? =
if (owner.origin == KClassOrigin.KOTLIN) {
owner.jClass.getMethod(setterName(name), getter!!.getReturnType()!!)
}
else null
override fun set(receiver: T, value: R) {
setter.invoke(receiver, value)
if (setter != null) {
setter!!(receiver, value)
return
}
field!!.set(receiver, value)
}
}
@@ -16,6 +16,8 @@
package kotlin.reflect.jvm.internal
import java.util.concurrent.ConcurrentHashMap
// TODO: use stdlib?
suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
private fun String.capitalizeWithJavaBeanConvention(): String {
@@ -32,13 +34,24 @@ private fun getterName(propertyName: String): String = "get" + propertyName.capi
private fun setterName(propertyName: String): String = "set" + propertyName.capitalizeWithJavaBeanConvention()
// TODO: should use weak references
private val foreignKClasses: MutableMap<Class<*>, KClassImpl<*>> = ConcurrentHashMap()
fun <T> foreignKotlinClass(jClass: Class<T>): KClassImpl<T> {
val cached = foreignKClasses[jClass] as? KClassImpl<T>
if (cached != null) return cached
val result = KClassImpl<T>(jClass)
foreignKClasses.put(jClass, result)
return result
}
private val K_OBJECT_CLASS = Class.forName("kotlin.jvm.internal.KObject")
// TODO
fun <T> kotlinClass(jClass: Class<T>): KClassImpl<T> {
if (K_OBJECT_CLASS.isAssignableFrom(jClass)) {
val field = jClass.getDeclaredField("\$kotlinClass")
return field.get(null) as KClassImpl<T>
}
throw UnsupportedOperationException("Unsupported class: $jClass")
// TODO: built-in classes
return foreignKotlinClass(jClass)
}