Implement typeOf intrinsic on JVM

#KT-29915 Fixed
This commit is contained in:
Alexander Udalov
2019-02-21 19:47:45 +01:00
parent 5d297c40fd
commit d1e33534db
28 changed files with 950 additions and 4 deletions
@@ -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
}