Support class reference expression for reified type parameters

Also make KClass::java an intrinsic property.

Based on the work by @dnpetrov

 #KT-6976 Fixed
This commit is contained in:
Alexander Udalov
2015-07-29 23:43:11 +03:00
parent 628bb774fd
commit a0cc313156
13 changed files with 283 additions and 41 deletions
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsPackage;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
@@ -53,7 +53,6 @@ import org.jetbrains.kotlin.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.jvm.bindingContextSlices.BindingContextSlicesPackage;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
@@ -1981,10 +1980,11 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
CallableMemberDescriptor memberDescriptor = DescriptorUtils.unwrapFakeOverride((CallableMemberDescriptor) descriptor);
IntrinsicMethod intrinsic = state.getIntrinsics().getIntrinsic(memberDescriptor);
if (intrinsic instanceof JavaClassProperty) {
if (intrinsic instanceof IntrinsicPropertyGetter) {
//TODO: intrinsic properties (see intermediateValueForProperty)
Type returnType = typeMapper.mapType(memberDescriptor);
return ((JavaClassProperty) intrinsic).generate(returnType, receiver);
StackValue intrinsicResult = ((IntrinsicPropertyGetter) intrinsic).generate(resolvedCall, this, returnType, receiver);
if (intrinsicResult != null) return intrinsicResult;
}
}
@@ -2498,15 +2498,17 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
TypeParameterDescriptor key = entry.getKey();
if (!key.isReified()) continue;
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(entry.getValue());
JetType type = entry.getValue();
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type);
if (parameterDescriptor == null) {
// type is not generic
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.TYPE);
Type type = typeMapper.mapTypeParameter(entry.getValue(), signatureWriter);
Type asmType = typeMapper.mapTypeParameter(type, signatureWriter);
mappings.addParameterMappingToType(
key.getName().getIdentifier(),
type,
asmType,
signatureWriter.toString()
);
}
@@ -2813,12 +2815,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
public Unit invoke(InstructionAdapter v) {
Type classAsmType = typeMapper.mapType(type);
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
//noinspection ConstantConditions
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
// Instantiate annotation classes as foreign due to bug in JDK 6 and 7:
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6857918
if (descriptor instanceof JavaClassDescriptor || module == module.getBuiltIns().getBuiltInsModule() ||
DescriptorUtils.isAnnotationClass(descriptor)) {
if (descriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) descriptor;
if (typeParameterDescriptor.isReified()) {
// Emit reified type parameters as Kotlin classes.
// ReifiedTypeInliner will rewrite the following GETSTATIC to proper bytecode instructions
// if the class literal for actual type should be emitted with wrapped Java class.
v.visitLdcInsn(typeParameterDescriptor.getName().asString());
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.CLASS_LITERAL_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false
);
v.getstatic(classAsmType.getInternalName(), JvmAbi.KOTLIN_CLASS_FIELD_NAME, K_CLASS_TYPE.getDescriptor());
}
else {
throw new AssertionError("Non-reified type parameter under ::class should be rejected by type checker: " +
typeParameterDescriptor.getName().asString());
}
}
else if (shouldUseJavaClassForClassLiteral(descriptor)) {
putJavaLangClassInstance(v, classAsmType);
wrapJavaClassIntoKClass(v);
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.PackageContext;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
@@ -237,4 +238,11 @@ public class JvmCodegenUtil {
return InlineUtil.canBeInlineArgument(declaration) &&
InlineUtil.isInlinedArgument((JetFunction) declaration, bindingContext, false);
}
public static boolean shouldUseJavaClassForClassLiteral(@NotNull ClassifierDescriptor descriptor) {
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
return descriptor instanceof JavaClassDescriptor ||
module == module.getBuiltIns().getBuiltInsModule() ||
DescriptorUtils.isAnnotationClass(descriptor);
}
}
@@ -16,21 +16,21 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import kotlin.platform.platformStatic
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import org.jetbrains.org.objectweb.asm.signature.SignatureWriter
import org.jetbrains.org.objectweb.asm.MethodVisitor
import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.org.objectweb.asm.tree.*
import kotlin.platform.platformStatic
public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParameterMappings?) {
@@ -39,11 +39,13 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
public val CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast"
public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof"
public val JAVA_CLASS_MARKER_METHOD_NAME: String = "reifyJavaClass"
public val CLASS_LITERAL_MARKER_METHOD_NAME: String = "reifyClassLiteral"
public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME: String = "needClassReification"
private val PARAMETRISED_MARKERS = ImmutableSet.of(
NEW_ARRAY_MARKER_METHOD_NAME, CHECKCAST_MARKER_METHOD_NAME,
INSTANCEOF_MARKER_METHOD_NAME, JAVA_CLASS_MARKER_METHOD_NAME
INSTANCEOF_MARKER_METHOD_NAME, JAVA_CLASS_MARKER_METHOD_NAME,
CLASS_LITERAL_MARKER_METHOD_NAME
)
private fun isParametrisedReifiedMarker(insn: AbstractInsnNode) =
@@ -139,6 +141,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, asmType)
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, asmType)
JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType)
CLASS_LITERAL_MARKER_METHOD_NAME -> processClassLiteral(insn, asmType, instructions, mapping.type!!)
else -> false
}) {
instructions.remove(insn.getPrevious()!!)
@@ -153,13 +156,13 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
}
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
private fun processCheckcast(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST)
processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST)
private fun processInstanceof(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.INSTANCEOF)
processNextTypeInsn(insn, parameter, Opcodes.INSTANCEOF)
private fun processNextTypeInsn(insn: MethodInsnNode, parameter: Type, expectedNextOpcode: Int): Boolean {
if (insn.getNext()?.getOpcode() != expectedNextOpcode) return false
@@ -174,6 +177,30 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
return true
}
private fun processClassLiteral(insn: MethodInsnNode, parameter: Type, instructions: InsnList, type: JetType): Boolean {
val next = insn.next
if (next !is FieldInsnNode || next.opcode != Opcodes.GETSTATIC) return false
val descriptor = type.constructor.declarationDescriptor!!
if (JvmCodegenUtil.shouldUseJavaClassForClassLiteral(descriptor)) {
instructions.insertBefore(
next,
if (AsmUtil.isPrimitive(parameter))
FieldInsnNode(Opcodes.GETSTATIC, AsmUtil.boxType(parameter).internalName, "TYPE", "Ljava/lang/Class;")
else
LdcInsnNode(parameter)
)
val foreignKotlinClassDesc = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
instructions.insertBefore(
next, MethodInsnNode(Opcodes.INVOKESTATIC, AsmTypes.REFLECTION, "foreignKotlinClass", foreignKotlinClassDesc, false)
)
instructions.remove(next)
}
else {
next.owner = AsmUtil.boxType(parameter).internalName
}
return true
}
private fun getParameterName(insn: MethodInsnNode): String? {
val prev = insn.getPrevious()!!
@@ -191,12 +218,12 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
public class ReifiedTypeParameterMappings() {
private val mappingsByName = hashMapOf<String, ReifiedTypeParameterMapping>()
public fun addParameterMappingToType(name: String, asmType: Type, signature: String) {
mappingsByName[name] = ReifiedTypeParameterMapping(name, asmType, newName = null, signature = signature)
public fun addParameterMappingToType(name: String, type: JetType, asmType: Type, signature: String) {
mappingsByName[name] = ReifiedTypeParameterMapping(name, type, asmType, newName = null, signature = signature)
}
public fun addParameterMappingToNewParameter(name: String, newName: String) {
mappingsByName[name] = ReifiedTypeParameterMapping(name, asmType = null, newName = newName, signature = null)
mappingsByName[name] = ReifiedTypeParameterMapping(name, type = null, asmType = null, newName = newName, signature = null)
}
fun get(name: String): ReifiedTypeParameterMapping? {
@@ -204,7 +231,9 @@ public class ReifiedTypeParameterMappings() {
}
}
public class ReifiedTypeParameterMapping(val name: String, val asmType: Type?, val newName: String?, val signature: String?)
public class ReifiedTypeParameterMapping(
val name: String, val type: JetType?, val asmType: Type?, val newName: String?, val signature: String?
)
public class ReifiedTypeParametersUsages {
val usedTypeParameters: MutableSet<String> = hashSetOf()
@@ -62,6 +62,7 @@ public class IntrinsicMethods {
public IntrinsicMethods() {
namedMethods.put("kotlin.javaClass.function", new JavaClassFunction());
namedMethods.put("kotlin.javaClass.property", new JavaClassProperty());
namedMethods.put("kotlin.KClass.java.property", new KClassJavaProperty());
namedMethods.put("kotlin.arrays.array", new JavaClassArray());
namedMethods.put("kotlin.jvm.internal.unsafe.monitorEnter", MonitorInstruction.MONITOR_ENTER);
namedMethods.put("kotlin.jvm.internal.unsafe.monitorExit", MonitorInstruction.MONITOR_EXIT);
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2015 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 org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.org.objectweb.asm.Type
public abstract class IntrinsicPropertyGetter : IntrinsicMethod() {
public abstract fun generate(
resolvedCall: ResolvedCall<*>?,
codegen: ExpressionCodegen,
returnType: Type,
receiver: StackValue
): StackValue?
}
@@ -28,8 +28,13 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.getType
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
public class JavaClassProperty : IntrinsicMethod() {
public fun generate(returnType: Type, receiver: StackValue): StackValue =
public class JavaClassProperty : IntrinsicPropertyGetter() {
public override fun generate(
resolvedCall: ResolvedCall<*>?,
codegen: ExpressionCodegen,
returnType: Type,
receiver: StackValue
): StackValue? =
StackValue.operation(returnType) {
val actualType = generateImpl(it, receiver)
StackValue.coerce(actualType, returnType, it)
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2015 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 org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.psi.JetClassLiteralExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
public class KClassJavaProperty : IntrinsicPropertyGetter() {
override fun generate(resolvedCall: ResolvedCall<*>?, codegen: ExpressionCodegen, returnType: Type, receiver: StackValue): StackValue? {
val type = resolvedCall!!.extensionReceiver.type.arguments.single().type
val asmType = codegen.state.typeMapper.mapType(type)
return when {
isReifiedTypeParameter(type) -> {
StackValue.operation(returnType) { iv ->
codegen.putReifierMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.JAVA_CLASS_MARKER_METHOD_NAME)
AsmUtil.putJavaLangClassInstance(iv, asmType)
coerceToJavaLangClass(iv, returnType)
}
}
isWithClassLiteralArgument(resolvedCall) -> {
StackValue.operation(returnType) { iv ->
if (AsmUtil.isPrimitive(asmType)) {
iv.getstatic(AsmUtil.boxType(asmType).internalName, "TYPE", "Ljava/lang/Class;")
}
else {
iv.tconst(asmType)
}
coerceToJavaLangClass(iv, returnType)
}
}
else -> null
}
}
private fun isReifiedTypeParameter(type: JetType): Boolean {
val typeDescriptor = type.constructor.declarationDescriptor
return typeDescriptor is TypeParameterDescriptor && typeDescriptor.isReified
}
private fun isWithClassLiteralArgument(resolvedCall: ResolvedCall<*>): Boolean {
val extensionReceiver = resolvedCall.extensionReceiver
return extensionReceiver is ExpressionReceiver && extensionReceiver.expression is JetClassLiteralExpression
}
private fun coerceToJavaLangClass(iv: InstructionAdapter, returnType: Type) {
StackValue.coerce(AsmTypes.JAVA_CLASS_TYPE, returnType, iv)
}
}