diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 04865fb6c1a..4dedb68bf2b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -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 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) { diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.java b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.java new file mode 100644 index 00000000000..48056198e02 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.java @@ -0,0 +1,3 @@ +public class publicFinalField { + public final String field = "OK"; +} diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt new file mode 100644 index 00000000000..7cc41f66006 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt @@ -0,0 +1 @@ +fun box() = (publicFinalField::field).get(publicFinalField()) diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.java b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.java new file mode 100644 index 00000000000..b675de28fdb --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.java @@ -0,0 +1,3 @@ +public class publicMutableField { + public int field = 239; +} diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt new file mode 100644 index 00000000000..563afaaf69b --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt @@ -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" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index ba89e324887..22a8556dee2 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -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") diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 084a36d0ed1..e191d9878b6 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -16,6 +16,21 @@ package kotlin.reflect.jvm.internal +enum class KClassOrigin { + BUILT_IN + KOTLIN + FOREIGN +} + class KClassImpl( val jClass: Class -) : KClass +) : KClass { + val origin: KClassOrigin = + if (K_OBJECT_CLASS.isAssignableFrom(jClass)) { + KClassOrigin.KOTLIN + } + else { + KClassOrigin.FOREIGN + // TODO: built-in classes + } +} diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt index 38f3348982b..2cee6e4e2c1 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt @@ -16,17 +16,32 @@ package kotlin.reflect.jvm.internal +import java.lang.reflect.Field import java.lang.reflect.Method open class KMemberPropertyImpl( name: String, protected val owner: KClassImpl ) : KMemberProperty, KPropertyImpl(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( name: String, owner: KClassImpl ) : KMutableMemberProperty, KMemberPropertyImpl(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) } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt index ffaf0281354..b8a5c6368f5 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -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, KClassImpl<*>> = ConcurrentHashMap() + +fun foreignKotlinClass(jClass: Class): KClassImpl { + val cached = foreignKClasses[jClass] as? KClassImpl + if (cached != null) return cached + val result = KClassImpl(jClass) + foreignKClasses.put(jClass, result) + return result +} + private val K_OBJECT_CLASS = Class.forName("kotlin.jvm.internal.KObject") -// TODO fun kotlinClass(jClass: Class): KClassImpl { if (K_OBJECT_CLASS.isAssignableFrom(jClass)) { val field = jClass.getDeclaredField("\$kotlinClass") return field.get(null) as KClassImpl } - throw UnsupportedOperationException("Unsupported class: $jClass") + // TODO: built-in classes + return foreignKotlinClass(jClass) }