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.inline.*;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
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.pseudoInsns.PseudoInsnsPackage;
|
||||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
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.jvm.bindingContextSlices.BindingContextSlicesPackage;
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
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.load.java.descriptors.SamConstructorDescriptor;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
@@ -1981,10 +1980,11 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
CallableMemberDescriptor memberDescriptor = DescriptorUtils.unwrapFakeOverride((CallableMemberDescriptor) descriptor);
|
CallableMemberDescriptor memberDescriptor = DescriptorUtils.unwrapFakeOverride((CallableMemberDescriptor) descriptor);
|
||||||
|
|
||||||
IntrinsicMethod intrinsic = state.getIntrinsics().getIntrinsic(memberDescriptor);
|
IntrinsicMethod intrinsic = state.getIntrinsics().getIntrinsic(memberDescriptor);
|
||||||
if (intrinsic instanceof JavaClassProperty) {
|
if (intrinsic instanceof IntrinsicPropertyGetter) {
|
||||||
//TODO: intrinsic properties (see intermediateValueForProperty)
|
//TODO: intrinsic properties (see intermediateValueForProperty)
|
||||||
Type returnType = typeMapper.mapType(memberDescriptor);
|
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();
|
TypeParameterDescriptor key = entry.getKey();
|
||||||
if (!key.isReified()) continue;
|
if (!key.isReified()) continue;
|
||||||
|
|
||||||
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(entry.getValue());
|
JetType type = entry.getValue();
|
||||||
|
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type);
|
||||||
if (parameterDescriptor == null) {
|
if (parameterDescriptor == null) {
|
||||||
// type is not generic
|
// type is not generic
|
||||||
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.TYPE);
|
BothSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.TYPE);
|
||||||
Type type = typeMapper.mapTypeParameter(entry.getValue(), signatureWriter);
|
Type asmType = typeMapper.mapTypeParameter(type, signatureWriter);
|
||||||
|
|
||||||
mappings.addParameterMappingToType(
|
mappings.addParameterMappingToType(
|
||||||
key.getName().getIdentifier(),
|
key.getName().getIdentifier(),
|
||||||
type,
|
type,
|
||||||
|
asmType,
|
||||||
signatureWriter.toString()
|
signatureWriter.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2813,12 +2815,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
public Unit invoke(InstructionAdapter v) {
|
public Unit invoke(InstructionAdapter v) {
|
||||||
Type classAsmType = typeMapper.mapType(type);
|
Type classAsmType = typeMapper.mapType(type);
|
||||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||||
//noinspection ConstantConditions
|
if (descriptor instanceof TypeParameterDescriptor) {
|
||||||
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
|
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) descriptor;
|
||||||
// Instantiate annotation classes as foreign due to bug in JDK 6 and 7:
|
if (typeParameterDescriptor.isReified()) {
|
||||||
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6857918
|
// Emit reified type parameters as Kotlin classes.
|
||||||
if (descriptor instanceof JavaClassDescriptor || module == module.getBuiltIns().getBuiltInsModule() ||
|
// ReifiedTypeInliner will rewrite the following GETSTATIC to proper bytecode instructions
|
||||||
DescriptorUtils.isAnnotationClass(descriptor)) {
|
// 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);
|
putJavaLangClassInstance(v, classAsmType);
|
||||||
wrapJavaClassIntoKClass(v);
|
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.context.PackageContext;
|
||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
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.java.lazy.descriptors.LazyJavaPackageFragment;
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
|
||||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
|
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
|
||||||
@@ -237,4 +238,11 @@ public class JvmCodegenUtil {
|
|||||||
return InlineUtil.canBeInlineArgument(declaration) &&
|
return InlineUtil.canBeInlineArgument(declaration) &&
|
||||||
InlineUtil.isInlinedArgument((JetFunction) declaration, bindingContext, false);
|
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
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import com.google.common.collect.ImmutableSet
|
||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import kotlin.platform.platformStatic
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
|
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.SignatureReader
|
||||||
import org.jetbrains.org.objectweb.asm.signature.SignatureWriter
|
import org.jetbrains.org.objectweb.asm.signature.SignatureWriter
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
import com.google.common.collect.ImmutableSet
|
import kotlin.platform.platformStatic
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
|
|
||||||
public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParameterMappings?) {
|
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 CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast"
|
||||||
public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof"
|
public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof"
|
||||||
public val JAVA_CLASS_MARKER_METHOD_NAME: String = "reifyJavaClass"
|
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"
|
public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME: String = "needClassReification"
|
||||||
|
|
||||||
private val PARAMETRISED_MARKERS = ImmutableSet.of(
|
private val PARAMETRISED_MARKERS = ImmutableSet.of(
|
||||||
NEW_ARRAY_MARKER_METHOD_NAME, CHECKCAST_MARKER_METHOD_NAME,
|
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) =
|
private fun isParametrisedReifiedMarker(insn: AbstractInsnNode) =
|
||||||
@@ -139,6 +141,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, asmType)
|
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, asmType)
|
||||||
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, asmType)
|
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, asmType)
|
||||||
JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType)
|
JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType)
|
||||||
|
CLASS_LITERAL_MARKER_METHOD_NAME -> processClassLiteral(insn, asmType, instructions, mapping.type!!)
|
||||||
else -> false
|
else -> false
|
||||||
}) {
|
}) {
|
||||||
instructions.remove(insn.getPrevious()!!)
|
instructions.remove(insn.getPrevious()!!)
|
||||||
@@ -153,13 +156,13 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
|
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
|
||||||
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
|
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
|
||||||
|
|
||||||
private fun processCheckcast(insn: MethodInsnNode, parameter: Type) =
|
private fun processCheckcast(insn: MethodInsnNode, parameter: Type) =
|
||||||
processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST)
|
processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST)
|
||||||
|
|
||||||
private fun processInstanceof(insn: MethodInsnNode, parameter: Type) =
|
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 {
|
private fun processNextTypeInsn(insn: MethodInsnNode, parameter: Type, expectedNextOpcode: Int): Boolean {
|
||||||
if (insn.getNext()?.getOpcode() != expectedNextOpcode) return false
|
if (insn.getNext()?.getOpcode() != expectedNextOpcode) return false
|
||||||
@@ -174,6 +177,30 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
return true
|
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? {
|
private fun getParameterName(insn: MethodInsnNode): String? {
|
||||||
val prev = insn.getPrevious()!!
|
val prev = insn.getPrevious()!!
|
||||||
|
|
||||||
@@ -191,12 +218,12 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
public class ReifiedTypeParameterMappings() {
|
public class ReifiedTypeParameterMappings() {
|
||||||
private val mappingsByName = hashMapOf<String, ReifiedTypeParameterMapping>()
|
private val mappingsByName = hashMapOf<String, ReifiedTypeParameterMapping>()
|
||||||
|
|
||||||
public fun addParameterMappingToType(name: String, asmType: Type, signature: String) {
|
public fun addParameterMappingToType(name: String, type: JetType, asmType: Type, signature: String) {
|
||||||
mappingsByName[name] = ReifiedTypeParameterMapping(name, asmType, newName = null, signature = signature)
|
mappingsByName[name] = ReifiedTypeParameterMapping(name, type, asmType, newName = null, signature = signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun addParameterMappingToNewParameter(name: String, newName: String) {
|
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? {
|
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 {
|
public class ReifiedTypeParametersUsages {
|
||||||
val usedTypeParameters: MutableSet<String> = hashSetOf()
|
val usedTypeParameters: MutableSet<String> = hashSetOf()
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public class IntrinsicMethods {
|
|||||||
public IntrinsicMethods() {
|
public IntrinsicMethods() {
|
||||||
namedMethods.put("kotlin.javaClass.function", new JavaClassFunction());
|
namedMethods.put("kotlin.javaClass.function", new JavaClassFunction());
|
||||||
namedMethods.put("kotlin.javaClass.property", new JavaClassProperty());
|
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.arrays.array", new JavaClassArray());
|
||||||
namedMethods.put("kotlin.jvm.internal.unsafe.monitorEnter", MonitorInstruction.MONITOR_ENTER);
|
namedMethods.put("kotlin.jvm.internal.unsafe.monitorEnter", MonitorInstruction.MONITOR_ENTER);
|
||||||
namedMethods.put("kotlin.jvm.internal.unsafe.monitorExit", MonitorInstruction.MONITOR_EXIT);
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
public class JavaClassProperty : IntrinsicMethod() {
|
public class JavaClassProperty : IntrinsicPropertyGetter() {
|
||||||
public fun generate(returnType: Type, receiver: StackValue): StackValue =
|
public override fun generate(
|
||||||
|
resolvedCall: ResolvedCall<*>?,
|
||||||
|
codegen: ExpressionCodegen,
|
||||||
|
returnType: Type,
|
||||||
|
receiver: StackValue
|
||||||
|
): StackValue? =
|
||||||
StackValue.operation(returnType) {
|
StackValue.operation(returnType) {
|
||||||
val actualType = generateImpl(it, receiver)
|
val actualType = generateImpl(it, receiver)
|
||||||
StackValue.coerce(actualType, returnType, it)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-7
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
|||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
|
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
@@ -585,16 +584,23 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (type.isMarkedNullable()) return false;
|
if (type.isMarkedNullable()) return false;
|
||||||
|
|
||||||
TypeConstructor typeConstructor = type.getConstructor();
|
TypeConstructor typeConstructor = type.getConstructor();
|
||||||
if (!(typeConstructor.getDeclarationDescriptor() instanceof ClassDescriptor)) return false;
|
ClassifierDescriptor typeDeclarationDescriptor = typeConstructor.getDeclarationDescriptor();
|
||||||
|
|
||||||
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
if (typeDeclarationDescriptor instanceof ClassDescriptor) {
|
||||||
if (parameters.size() != type.getArguments().size()) return false;
|
List<TypeParameterDescriptor> parameters = typeConstructor.getParameters();
|
||||||
|
if (parameters.size() != type.getArguments().size()) return false;
|
||||||
|
|
||||||
for (TypeParameterDescriptor parameter : parameters) {
|
for (TypeParameterDescriptor parameter : parameters) {
|
||||||
if (!parameter.isReified()) return false;
|
if (!parameter.isReified()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (typeDeclarationDescriptor instanceof TypeParameterDescriptor) {
|
||||||
|
return ((TypeParameterDescriptor) typeDeclarationDescriptor).isReified();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
import kotlin.test.*
|
||||||
|
import kotlin.reflect.jvm.java
|
||||||
|
|
||||||
|
class Klass
|
||||||
|
class Other
|
||||||
|
|
||||||
|
inline fun <reified T> simpleName(): String =
|
||||||
|
T::class.simpleName!!
|
||||||
|
|
||||||
|
inline fun <reified T1, reified T2> twoReifiedParams(): String =
|
||||||
|
"${T1::class.simpleName!!}, ${T2::class.simpleName!!}"
|
||||||
|
|
||||||
|
inline fun <reified T> myJavaClass(): Class<T> =
|
||||||
|
T::class.java
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Klass", simpleName<Klass>())
|
||||||
|
assertEquals("Int", simpleName<Int>())
|
||||||
|
assertEquals("Array", simpleName<Array<Int>>())
|
||||||
|
assertEquals("Error", simpleName<Error>())
|
||||||
|
assertEquals("Klass, Other", twoReifiedParams<Klass, Other>())
|
||||||
|
|
||||||
|
assertEquals(javaClass<String>(), myJavaClass<String>())
|
||||||
|
assertEquals(javaClass<IntArray>(), myJavaClass<IntArray>())
|
||||||
|
assertEquals(javaClass<Klass>(), myJavaClass<Klass>())
|
||||||
|
assertEquals(javaClass<Error>(), myJavaClass<Error>())
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import kotlin.reflect.jvm.java
|
||||||
|
|
||||||
|
class Klass
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val c0 = (Klass::class).java // prevent intrinsic .java for class literal
|
||||||
|
val c1 = Klass::class.java
|
||||||
|
val c2 = Int::class.java
|
||||||
|
val c3 = Integer::class.java
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 LDC LKlass;.class
|
||||||
|
// 1 GETSTATIC java/lang/Integer.TYPE : Ljava/lang/Class;
|
||||||
|
// 1 INVOKESTATIC kotlin/reflect/jvm.*\.getJava
|
||||||
|
// 1 LDC Ljava/lang/Integer;.class
|
||||||
@@ -131,6 +131,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaExtensionPropertyIntrinsic.kt")
|
||||||
|
public void testJavaExtensionPropertyIntrinsic() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/javaExtensionPropertyIntrinsic.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt2202.kt")
|
@TestMetadata("kt2202.kt")
|
||||||
public void testKt2202() throws Exception {
|
public void testKt2202() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/kt2202.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/kt2202.kt");
|
||||||
|
|||||||
+21
@@ -2886,6 +2886,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
doTestWithStdlib(fileName);
|
doTestWithStdlib(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("reifiedTypeClassLiteral.kt")
|
||||||
|
public void testReifiedTypeClassLiteral() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/reifiedTypeClassLiteral.kt");
|
||||||
|
doTestWithStdlib(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("simpleClassLiteral.kt")
|
@TestMetadata("simpleClassLiteral.kt")
|
||||||
public void testSimpleClassLiteral() throws Exception {
|
public void testSimpleClassLiteral() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");
|
||||||
@@ -3331,6 +3337,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class NoKotlinReflect extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInNoKotlinReflect() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleClassLiterals.kt")
|
||||||
|
public void testSimpleClassLiterals() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/noKotlinReflect/simpleClassLiterals.kt");
|
||||||
|
doTestWithStdlib(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/packages")
|
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/packages")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.lang.reflect.Constructor
|
|||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
import kotlin.jvm.internal.Intrinsic
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
import kotlin.reflect.jvm.internal.*
|
import kotlin.reflect.jvm.internal.*
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ import kotlin.reflect.jvm.internal.*
|
|||||||
/**
|
/**
|
||||||
* Returns a Java [Class] instance corresponding to the given [KClass] instance.
|
* Returns a Java [Class] instance corresponding to the given [KClass] instance.
|
||||||
*/
|
*/
|
||||||
|
@Intrinsic("kotlin.KClass.java.property")
|
||||||
public val <T> KClass<T>.java: Class<T>
|
public val <T> KClass<T>.java: Class<T>
|
||||||
get() = (this as KClassImpl<T>).jClass
|
get() = (this as KClassImpl<T>).jClass
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user