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:
@@ -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);
|
||||
|
||||
+31
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user