Support references to extension properties in JVM codegen
#KT-1183 In Progress
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2426,6 +2426,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> implem
|
||||
v.visitLdcInsn(descriptor.getName().asString());
|
||||
v.getstatic(reflectionFieldOwner, reflectionFieldName, ownerType.getDescriptor());
|
||||
|
||||
v.invokespecial(propertyType.getInternalName(), "<init>",
|
||||
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(), "<init>", constructorDesc, false);
|
||||
return StackValue.onStack(propertyType);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
+18
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package a
|
||||
|
||||
public var topLevel: Int = 42
|
||||
|
||||
public val String.extension: Long
|
||||
get() = length.toLong()
|
||||
@@ -0,0 +1,14 @@
|
||||
import a.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
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")
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
+3
-2
@@ -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));
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
@@ -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<T, out R>(
|
||||
name: String,
|
||||
protected val owner: KPackageImpl,
|
||||
protected val receiverClass: Class<T>
|
||||
) : KExtensionProperty<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), receiverClass)
|
||||
|
||||
override fun get(receiver: T): R {
|
||||
return getter(null, receiver) as R
|
||||
}
|
||||
}
|
||||
|
||||
class KMutableExtensionPropertyImpl<T, R>(
|
||||
name: String,
|
||||
owner: KPackageImpl,
|
||||
receiverClass: Class<T>
|
||||
) : KMutableExtensionProperty<T, R>, KExtensionPropertyImpl<T, R>(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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user