Implement typeOf intrinsic on JVM
#KT-29915 Fixed
This commit is contained in:
@@ -69,7 +69,8 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
private val initialFrameSize = codegen.frameMap.currentSize
|
||||
|
||||
private val reifiedTypeInliner = ReifiedTypeInliner(typeParameterMappings, state.languageVersionSettings.isReleaseCoroutines())
|
||||
private val reifiedTypeInliner =
|
||||
ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings.isReleaseCoroutines())
|
||||
|
||||
protected val functionDescriptor: FunctionDescriptor =
|
||||
if (InlineUtil.isArrayConstructorWithLambda(function))
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.generateAsCast
|
||||
import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -60,9 +61,13 @@ class ReificationArgument(
|
||||
}
|
||||
}
|
||||
|
||||
class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?, private val isReleaseCoroutines: Boolean) {
|
||||
class ReifiedTypeInliner(
|
||||
private val parametersMapping: TypeParameterMappings?,
|
||||
private val typeMapper: KotlinTypeMapper,
|
||||
private val isReleaseCoroutines: Boolean
|
||||
) {
|
||||
enum class OperationKind {
|
||||
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED;
|
||||
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF;
|
||||
|
||||
val id: Int get() = ordinal
|
||||
}
|
||||
@@ -143,6 +148,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?,
|
||||
OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType)
|
||||
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
|
||||
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, instructions, asmType)
|
||||
OperationKind.TYPE_OF -> processTypeOf(insn, instructions, kotlinType)
|
||||
}
|
||||
) {
|
||||
instructions.remove(insn.previous.previous!!) // PUSH operation ID
|
||||
@@ -201,6 +207,21 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?,
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processTypeOf(
|
||||
insn: MethodInsnNode,
|
||||
instructions: InsnList,
|
||||
kotlinType: KotlinType
|
||||
) = rewriteNextTypeInsn(insn, Opcodes.ACONST_NULL) { stubConstNull: AbstractInsnNode ->
|
||||
val newMethodNode = MethodNode(Opcodes.API_VERSION)
|
||||
val stackSize = generateTypeOf(InstructionAdapter(newMethodNode), kotlinType, typeMapper)
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
instructions.remove(stubConstNull)
|
||||
|
||||
maxStackSize = Math.max(maxStackSize, stackSize)
|
||||
return true
|
||||
}
|
||||
|
||||
inline private fun rewriteNextTypeInsn(
|
||||
marker: MethodInsnNode,
|
||||
expectedNextOpcode: Int,
|
||||
|
||||
@@ -20,9 +20,13 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.TypeOfChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.isBuiltInCoroutineContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeProjection
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
@@ -38,6 +42,8 @@ internal fun generateInlineIntrinsic(
|
||||
return when {
|
||||
isSpecialEnumMethod(descriptor) ->
|
||||
createSpecialEnumMethodBody(descriptor.name.asString(), typeArguments!!.keys.single().defaultType, typeMapper)
|
||||
TypeOfChecker.isTypeOf(descriptor) ->
|
||||
createTypeOfMethodBody(typeArguments!!.keys.single().defaultType)
|
||||
descriptor.isBuiltInIntercepted(languageVersionSettings) ->
|
||||
createMethodNodeForIntercepted(descriptor, typeMapper, languageVersionSettings)
|
||||
descriptor.isBuiltInCoroutineContext(languageVersionSettings) ->
|
||||
@@ -94,3 +100,101 @@ private fun createSpecialEnumMethodBody(name: String, type: KotlinType, typeMapp
|
||||
internal fun getSpecialEnumFunDescriptor(type: Type, isValueOf: Boolean): String =
|
||||
if (isValueOf) Type.getMethodDescriptor(type, JAVA_STRING_TYPE)
|
||||
else Type.getMethodDescriptor(AsmUtil.getArrayType(type))
|
||||
|
||||
private fun createTypeOfMethodBody(type: KotlinType): MethodNode {
|
||||
val node = MethodNode(Opcodes.API_VERSION, Opcodes.ACC_STATIC, "fake", Type.getMethodDescriptor(K_TYPE), null, null)
|
||||
val v = InstructionAdapter(node)
|
||||
|
||||
putTypeOfReifiedTypeParameter(v, type)
|
||||
v.areturn(K_TYPE)
|
||||
|
||||
v.visitMaxs(2, 0)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
private fun putTypeOfReifiedTypeParameter(v: InstructionAdapter, type: KotlinType) {
|
||||
ExpressionCodegen.putReifiedOperationMarkerIfTypeIsReifiedParameterWithoutPropagation(type, ReifiedTypeInliner.OperationKind.TYPE_OF, v)
|
||||
v.aconst(null)
|
||||
}
|
||||
|
||||
// Returns some upper bound on maximum stack size
|
||||
internal fun generateTypeOf(v: InstructionAdapter, kotlinType: KotlinType, typeMapper: KotlinTypeMapper): Int {
|
||||
val asmType = typeMapper.mapType(kotlinType)
|
||||
AsmUtil.putJavaLangClassInstance(v, asmType, kotlinType, typeMapper)
|
||||
|
||||
val arguments = kotlinType.arguments
|
||||
val useArray = arguments.size >= 3
|
||||
|
||||
if (useArray) {
|
||||
v.iconst(arguments.size)
|
||||
v.newarray(K_TYPE_PROJECTION)
|
||||
}
|
||||
|
||||
var maxStackSize = 3
|
||||
|
||||
for (i in 0 until arguments.size) {
|
||||
if (useArray) {
|
||||
v.dup()
|
||||
v.iconst(i)
|
||||
}
|
||||
|
||||
val stackSize = doGenerateTypeProjection(v, arguments[i], typeMapper)
|
||||
maxStackSize = maxOf(maxStackSize, stackSize + i + 5)
|
||||
|
||||
if (useArray) {
|
||||
v.astore(K_TYPE_PROJECTION)
|
||||
}
|
||||
}
|
||||
|
||||
val methodName = if (kotlinType.isMarkedNullable) "nullableTypeOf" else "typeOf"
|
||||
|
||||
val projections = when (arguments.size) {
|
||||
0 -> emptyArray()
|
||||
1 -> arrayOf(K_TYPE_PROJECTION)
|
||||
2 -> arrayOf(K_TYPE_PROJECTION, K_TYPE_PROJECTION)
|
||||
else -> arrayOf(AsmUtil.getArrayType(K_TYPE_PROJECTION))
|
||||
}
|
||||
val signature = Type.getMethodDescriptor(K_TYPE, JAVA_CLASS_TYPE, *projections)
|
||||
|
||||
v.invokestatic(REFLECTION, methodName, signature, false)
|
||||
|
||||
return maxStackSize
|
||||
}
|
||||
|
||||
private fun doGenerateTypeProjection(
|
||||
v: InstructionAdapter,
|
||||
projection: TypeProjection,
|
||||
typeMapper: KotlinTypeMapper
|
||||
): Int {
|
||||
// KTypeProjection members could be static, see KT-30083 and KT-30084
|
||||
v.getstatic(K_TYPE_PROJECTION.internalName, "Companion", K_TYPE_PROJECTION_COMPANION.descriptor)
|
||||
|
||||
if (projection.isStarProjection) {
|
||||
v.invokevirtual(K_TYPE_PROJECTION_COMPANION.internalName, "getSTAR", Type.getMethodDescriptor(K_TYPE_PROJECTION), false)
|
||||
return 1
|
||||
}
|
||||
|
||||
val type = projection.type
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
val stackSize = if (descriptor is TypeParameterDescriptor) {
|
||||
if (descriptor.isReified) {
|
||||
putTypeOfReifiedTypeParameter(v, type)
|
||||
2
|
||||
} else {
|
||||
// TODO: support non-reified type parameters in typeOf
|
||||
generateTypeOf(v, type.builtIns.nullableAnyType, typeMapper)
|
||||
}
|
||||
} else {
|
||||
generateTypeOf(v, type, typeMapper)
|
||||
}
|
||||
|
||||
val methodName = when (projection.projectionKind) {
|
||||
Variance.INVARIANT -> "invariant"
|
||||
Variance.IN_VARIANCE -> "contravariant"
|
||||
Variance.OUT_VARIANCE -> "covariant"
|
||||
}
|
||||
v.invokevirtual(K_TYPE_PROJECTION_COMPANION.internalName, methodName, Type.getMethodDescriptor(K_TYPE_PROJECTION, K_TYPE), false)
|
||||
|
||||
return stackSize + 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user