diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index badbff196fc..ce05e888687 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -785,4 +785,12 @@ public class AsmUtil { return PackagePartClassUtils.getPackagePartInternalName(containingFile); } + public static void putJavaLangClassInstance(@NotNull InstructionAdapter v, @NotNull Type type) { + if (isPrimitive(type)) { + v.getstatic(boxType(type).getInternalName(), "TYPE", "Ljava/lang/Class;"); + } + else { + v.aconst(type); + } + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 401b5687db2..04865fb6c1a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -2426,6 +2426,8 @@ public class ExpressionCodegen extends JetVisitor implem if (variableDescriptor != null) { VariableDescriptor descriptor = (VariableDescriptor) resolvedCall.getResultingDescriptor(); + ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter(); + String reflectionFieldOwner; Type propertyType; Type ownerType; @@ -2436,7 +2438,13 @@ public class ExpressionCodegen extends JetVisitor implem reflectionFieldOwner = PackageClassUtils.getPackageClassInternalName(((PackageFragmentDescriptor) containingDeclaration).getFqName()); - propertyType = descriptor.isVar() ? K_MUTABLE_TOP_LEVEL_PROPERTY_IMPL_TYPE : K_TOP_LEVEL_PROPERTY_IMPL_TYPE; + if (receiverParameter != null) { + propertyType = descriptor.isVar() ? K_MUTABLE_EXTENSION_PROPERTY_IMPL_TYPE : K_EXTENSION_PROPERTY_IMPL_TYPE; + } + else { + propertyType = descriptor.isVar() ? K_MUTABLE_TOP_LEVEL_PROPERTY_IMPL_TYPE : K_TOP_LEVEL_PROPERTY_IMPL_TYPE; + } + ownerType = K_PACKAGE_IMPL_TYPE; reflectionFieldName = JvmAbi.KOTLIN_PACKAGE_FIELD_NAME; } @@ -2455,8 +2463,16 @@ public class ExpressionCodegen extends JetVisitor implem v.visitLdcInsn(descriptor.getName().asString()); v.getstatic(reflectionFieldOwner, reflectionFieldName, ownerType.getDescriptor()); - v.invokespecial(propertyType.getInternalName(), "", - Type.getMethodDescriptor(Type.VOID_TYPE, JAVA_STRING_TYPE, ownerType), false); + String constructorDesc; + if (receiverParameter != null) { + putJavaLangClassInstance(v, typeMapper.mapType(receiverParameter)); + constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, JAVA_STRING_TYPE, ownerType, getType(Class.class)); + } + else { + constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, JAVA_STRING_TYPE, ownerType); + } + + v.invokespecial(propertyType.getInternalName(), "", constructorDesc, false); return StackValue.onStack(propertyType); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/JavaClassFunction.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/JavaClassFunction.java index d1b3b660cb6..cc2c8c52be2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/JavaClassFunction.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/JavaClassFunction.java @@ -19,8 +19,6 @@ package org.jetbrains.jet.codegen.intrinsics; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.org.objectweb.asm.Type; -import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.jet.codegen.ExpressionCodegen; import org.jetbrains.jet.codegen.StackValue; import org.jetbrains.jet.lang.psi.JetCallExpression; @@ -28,11 +26,12 @@ import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import java.util.List; -import static org.jetbrains.jet.codegen.AsmUtil.boxType; -import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; +import static org.jetbrains.jet.codegen.AsmUtil.putJavaLangClassInstance; import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.getType; public class JavaClassFunction extends IntrinsicMethod { @@ -51,13 +50,7 @@ public class JavaClassFunction extends IntrinsicMethod { assert resolvedCall != null; JetType returnType = resolvedCall.getResultingDescriptor().getReturnType(); assert returnType != null; - Type type = codegen.getState().getTypeMapper().mapType(returnType.getArguments().get(0).getType()); - if (isPrimitive(type)) { - v.getstatic(boxType(type).getInternalName(), "TYPE", "Ljava/lang/Class;"); - } - else { - v.aconst(type); - } + putJavaLangClassInstance(v, codegen.getState().getTypeMapper().mapType(returnType.getArguments().get(0).getType())); return getType(Class.class); } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java index 4fe57bdf616..5ec815ea374 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AsmTypeConstants.java @@ -44,6 +44,8 @@ public class AsmTypeConstants { public static final Type K_MUTABLE_TOP_LEVEL_PROPERTY_IMPL_TYPE = reflectInternal("KMutableTopLevelPropertyImpl"); public static final Type K_MEMBER_PROPERTY_IMPL_TYPE = reflectInternal("KMemberPropertyImpl"); public static final Type K_MUTABLE_MEMBER_PROPERTY_IMPL_TYPE = reflectInternal("KMutableMemberPropertyImpl"); + public static final Type K_EXTENSION_PROPERTY_IMPL_TYPE = reflectInternal("KExtensionPropertyImpl"); + public static final Type K_MUTABLE_EXTENSION_PROPERTY_IMPL_TYPE = reflectInternal("KMutableExtensionPropertyImpl"); public static final Type OBJECT_REF_TYPE = Type.getObjectType("kotlin/jvm/internal/Ref$ObjectRef"); diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleExtension.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleExtension.kt new file mode 100644 index 00000000000..7e6b5c92df1 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleExtension.kt @@ -0,0 +1,12 @@ +val String.id: String + get() = this + +fun box(): String { + val pr = String::id + + if (pr["123"] != "123") return "Fail value: ${pr["123"]}" + + if (pr.name != "id") return "Fail name: ${pr.name}" + + return pr.get("OK") +} diff --git a/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMutableExtension.kt b/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMutableExtension.kt new file mode 100644 index 00000000000..0d5c562bcff --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMutableExtension.kt @@ -0,0 +1,18 @@ +var storage = 0 + +var Int.foo: Int + get() { + return this + storage + } + set(value) { + storage = this + value + } + +fun box(): String { + val pr = Int::foo + if (pr.get(42) != 42) return "Fail 1: ${pr[42]}" + pr.set(200, 39) + if (pr.get(-239) != 0) return "Fail 2: ${pr[-239]}" + if (storage != 239) return "Fail 3: $storage" + return "OK" +} diff --git a/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.A.kt b/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.A.kt new file mode 100644 index 00000000000..ea68a0dea53 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.A.kt @@ -0,0 +1,6 @@ +package a + +public var topLevel: Int = 42 + +public val String.extension: Long + get() = length.toLong() diff --git a/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.B.kt b/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.B.kt new file mode 100644 index 00000000000..5a9e82e1b29 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/PropertyReference.B.kt @@ -0,0 +1,14 @@ +import a.* + +fun main(args: Array) { + val f = ::topLevel + val x1 = f.get() + if (x1 != 42) throw AssertionError("Fail x1: $x1") + f.set(239) + val x2 = f.get() + if (x2 != 239) throw AssertionError("Fail x2: $x2") + + val g = String::extension + val y1 = g.get("abcde") + if (y1 != 5L) throw AssertionError("Fail y1: $y1") +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 547428cc25d..257d4fb5d7f 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -464,6 +464,11 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMember.kt"); } + @TestMetadata("simpleMutableExtension.kt") + public void testSimpleMutableExtension() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMutableExtension.kt"); + } + @TestMetadata("simpleMutableMember.kt") public void testSimpleMutableMember() throws Exception { doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/callableReference/property/simpleMutableMember.kt"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java index 899551591e1..e5b95bc318d 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java @@ -28,6 +28,7 @@ import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.codegen.ClassFileFactory; import org.jetbrains.jet.codegen.GenerationUtils; import org.jetbrains.jet.codegen.InlineTestUtil; +import org.jetbrains.jet.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.jet.config.CompilerConfiguration; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; @@ -108,7 +109,7 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit private void invokeMain() throws Exception { URLClassLoader classLoader = new URLClassLoader( - new URL[]{ aDir.toURI().toURL(), bDir.toURI().toURL() }, + new URL[]{ aDir.toURI().toURL(), bDir.toURI().toURL(), ForTestCompileRuntime.runtimeJarForTests().toURI().toURL() }, AbstractCompileKotlinAgainstKotlinTest.class.getClassLoader() ); Class clazz = classLoader.loadClass(PackageClassUtils.getPackageClassName(FqName.ROOT)); @@ -118,7 +119,7 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit private void invokeBox() throws Exception { URLClassLoader classLoader = new URLClassLoader( - new URL[]{ bDir.toURI().toURL(), aDir.toURI().toURL() }, + new URL[]{ bDir.toURI().toURL(), aDir.toURI().toURL(), ForTestCompileRuntime.runtimeJarForTests().toURI().toURL() }, AbstractCompileKotlinAgainstKotlinTest.class.getClassLoader() ); Class clazz = classLoader.loadClass(PackageClassUtils.getPackageClassName(FqName.ROOT)); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java index fcece966531..aa7a31d4f0c 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstKotlinTestGenerated.java @@ -106,6 +106,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl doTest("compiler/testData/compileKotlinAgainstKotlin/PlatformNames.A.kt"); } + @TestMetadata("PropertyReference.A.kt") + public void testPropertyReference() throws Exception { + doTest("compiler/testData/compileKotlinAgainstKotlin/PropertyReference.A.kt"); + } + @TestMetadata("Simple.A.kt") public void testSimple() throws Exception { doTest("compiler/testData/compileKotlinAgainstKotlin/Simple.A.kt"); diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KExtensionPropertyImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KExtensionPropertyImpl.kt new file mode 100644 index 00000000000..0850703dad6 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KExtensionPropertyImpl.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2014 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 java.lang.reflect.Method + +open class KExtensionPropertyImpl( + name: String, + protected val owner: KPackageImpl, + protected val receiverClass: Class +) : KExtensionProperty, KPropertyImpl(name) { + // TODO: extract, make lazy (weak?), use our descriptors knowledge, support Java fields + protected val getter: Method = owner.jClass.getMethod(getterName(name), receiverClass) + + override fun get(receiver: T): R { + return getter(null, receiver) as R + } +} + +class KMutableExtensionPropertyImpl( + name: String, + owner: KPackageImpl, + receiverClass: Class +) : KMutableExtensionProperty, KExtensionPropertyImpl(name, owner, receiverClass) { + private val setter = owner.jClass.getMethod(setterName(name), receiverClass, getter.getReturnType()!!) + + override fun set(receiver: T, value: R) { + setter.invoke(null, receiver, value) + } +}