diff --git a/.idea/dictionaries/dzharkov.xml b/.idea/dictionaries/dzharkov.xml new file mode 100644 index 00000000000..fb88a11d30d --- /dev/null +++ b/.idea/dictionaries/dzharkov.xml @@ -0,0 +1,7 @@ + + + + insn + + + \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 3dfea9116ff..64c3e569a4d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2780,7 +2780,7 @@ public class ExpressionCodegen extends KtVisitor impleme "Non-reified type parameter under ::class should be rejected by type checker: " + typeParameterDescriptor; assert codegen != null : "Reference to member of reified type should be rejected by type checker " + typeParameterDescriptor; - codegen.putReifierMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.JAVA_CLASS_MARKER_METHOD_NAME); + codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS); } putJavaLangClassInstance(v, classAsmType); @@ -3394,9 +3394,9 @@ public class ExpressionCodegen extends KtVisitor impleme public void newArrayInstruction(@NotNull KotlinType arrayType) { if (KotlinBuiltIns.isArray(arrayType)) { KotlinType elementJetType = arrayType.getArguments().get(0).getType(); - putReifierMarkerIfTypeIsReifiedParameter( + putReifiedOperationMarkerIfTypeIsReifiedParameter( elementJetType, - ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME + ReifiedTypeInliner.OperationKind.NEW_ARRAY ); v.newarray(boxType(asmType(elementJetType))); } @@ -3753,32 +3753,35 @@ The "returned" value of try expression with no finally is either the last expres private void generateInstanceOfInstruction(@NotNull KotlinType jetType) { Type type = boxType(asmType(jetType)); - putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.INSTANCEOF_MARKER_METHOD_NAME); + putReifiedOperationMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.OperationKind.INSTANCEOF); TypeIntrinsics.instanceOf(v, jetType, type); } @NotNull private StackValue generateCheckCastInstruction(@NotNull KotlinType jetType, boolean safeAs) { Type type = boxType(asmType(jetType)); - putReifierMarkerIfTypeIsReifiedParameter(jetType, - safeAs ? ReifiedTypeInliner.SAFE_CHECKCAST_MARKER_METHOD_NAME - : ReifiedTypeInliner.CHECKCAST_MARKER_METHOD_NAME); + putReifiedOperationMarkerIfTypeIsReifiedParameter(jetType, + safeAs ? ReifiedTypeInliner.OperationKind.SAFE_CHECKCAST + : ReifiedTypeInliner.OperationKind.CHECKCAST); TypeIntrinsics.checkcast(v, jetType, type, safeAs); return StackValue.onStack(type); } - public void putReifierMarkerIfTypeIsReifiedParameter(@NotNull KotlinType type, @NotNull String markerMethodName) { + public void putReifiedOperationMarkerIfTypeIsReifiedParameter( + @NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind + ) { TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type); if (typeParameterDescriptor != null && typeParameterDescriptor.isReified()) { if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) { parentCodegen.getReifiedTypeParametersUsages(). addUsedReifiedParameter(typeParameterDescriptor.getName().asString()); } - boolean putNullableFlag = ReifiedTypeInliner.isNullableMarkerInstruction(markerMethodName) && type.isMarkedNullable(); + v.iconst(operationKind.getId()); + boolean putNullableFlag = operationKind.isTypeNullabilityAware() && type.isMarkedNullable(); v.visitLdcInsn(typeParameterDescriptor.getName().asString() + (putNullableFlag ? "?" : "")); v.invokestatic( - IntrinsicMethods.INTRINSICS_CLASS_NAME, markerMethodName, - Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false + IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.REIFIED_OPERATION_MARKER_METHOD_NAME, + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(String.class)), false ); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt index 859eabe13ca..806fa291e4e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt @@ -16,12 +16,12 @@ package org.jetbrains.kotlin.codegen.inline -import com.google.common.collect.ImmutableSet import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.generateIsCheck import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics +import org.jetbrains.kotlin.codegen.optimization.common.intConstant import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.org.objectweb.asm.MethodVisitor @@ -36,22 +36,21 @@ private class ParameterNameAndNullability(val name: String, val nullable: Boolea public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParameterMappings?) { + enum class OperationKind { + NEW_ARRAY, CHECKCAST, SAFE_CHECKCAST, INSTANCEOF, JAVA_CLASS; + + val id: Int get() = ordinal + val isTypeNullabilityAware: Boolean get() = this == CHECKCAST || this == INSTANCEOF + } + companion object { - public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray" - public val CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast" - public val SAFE_CHECKCAST_MARKER_METHOD_NAME: String = "reifySafeCheckcast" - public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof" - public val JAVA_CLASS_MARKER_METHOD_NAME: String = "reifyJavaClass" - public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME: String = "needClassReification" + @JvmField + public val REIFIED_OPERATION_MARKER_METHOD_NAME = "reifiedOperationMarker" + @JvmField + public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME = "needClassReification" - private val PARAMETRISED_MARKERS = ImmutableSet.of( - NEW_ARRAY_MARKER_METHOD_NAME, - CHECKCAST_MARKER_METHOD_NAME, SAFE_CHECKCAST_MARKER_METHOD_NAME, - INSTANCEOF_MARKER_METHOD_NAME, JAVA_CLASS_MARKER_METHOD_NAME - ) - - private fun isParametrisedReifiedMarker(insn: AbstractInsnNode) = - isReifiedMarker(insn) { PARAMETRISED_MARKERS.contains(it) } + private fun isOperationReifiedMarker(insn: AbstractInsnNode) = + isReifiedMarker(insn) { it == REIFIED_OPERATION_MARKER_METHOD_NAME } private fun isReifiedMarker(insn: AbstractInsnNode, namePredicate: (String) -> Boolean): Boolean { if (insn.getOpcode() != Opcodes.INVOKESTATIC || insn !is MethodInsnNode) return false @@ -70,10 +69,6 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame Type.getMethodDescriptor(Type.VOID_TYPE), false ); } - - @JvmStatic - public fun isNullableMarkerInstruction(marker: String) = INSTANCEOF_MARKER_METHOD_NAME == marker || - CHECKCAST_MARKER_METHOD_NAME == marker } private var maxStackSize = 0 @@ -89,7 +84,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame maxStackSize = 0 var result = ReifiedTypeParametersUsages() for (insn in instructions.toArray()) { - if (isParametrisedReifiedMarker(insn)) { + if (isOperationReifiedMarker(insn)) { val newName: String? = processReifyMarker(insn as MethodInsnNode, instructions) if (newName != null) { result.addUsedReifiedParameter(newName) @@ -142,10 +137,11 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame * or null if it shouldn't */ private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList): String? { - val parameter = getParameter(insn) ?: return null + val operationKind = insn.operationKind ?: return null + val parameter = insn.parameterNameAndNullability ?: return null val mapping = parametersMapping?.get(parameter.name) ?: return null val kotlinType = - if (isNullableMarkerInstruction(insn.name) && parameter.nullable) + if (operationKind.isTypeNullabilityAware && parameter.nullable) TypeUtils.makeNullable(mapping.type) else mapping.type @@ -156,21 +152,21 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame // process* methods return false if marker should be reified further // or it's invalid (may be emitted explicitly in code) // they return true if instruction is reified and marker can be deleted - if (when (insn.name) { - NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, asmType) - CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = false) - SAFE_CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = true) - INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, instructions, kotlinType, asmType) - JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType) - else -> false + if (when (operationKind) { + OperationKind.NEW_ARRAY -> processNewArray(insn, asmType) + OperationKind.CHECKCAST -> processCheckcast(insn, instructions, kotlinType, asmType, safe = false) + OperationKind.SAFE_CHECKCAST -> processCheckcast(insn, instructions, kotlinType, asmType, safe = true) + OperationKind.INSTANCEOF -> processInstanceof(insn, instructions, kotlinType, asmType) + OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType) }) { - instructions.remove(insn.getPrevious()!!) - instructions.remove(insn) + instructions.remove(insn.previous.previous!!) // PUSH operation ID + instructions.remove(insn.previous!!) // PUSH type parameter + instructions.remove(insn) // INVOKESTATIC marker method } return null } else { - val nullableSuffix = if (isNullableMarkerInstruction(insn.name) && kotlinType.isMarkedNullable) "?" else "" + val nullableSuffix = if (operationKind.isTypeNullabilityAware && kotlinType.isMarkedNullable) "?" else "" instructions.set(insn.previous!!, LdcInsnNode(mapping.newName + nullableSuffix)) return mapping.newName } @@ -187,7 +183,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode -> if (instanceofInsn !is TypeInsnNode) return false - addNullCheckForAsIfNeeded(insn.previous!!, instructions, jetType, safe) + addNullCheckForAsIfNeeded(insn.previous.previous, instructions, jetType, safe) TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe) return true } @@ -214,7 +210,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) { if (TypeUtils.isNullableType(type)) { val instanceOf = insn.next - insertNullCheckAround(instructions, insn.previous!!, instanceOf) + insertNullCheckAround(instructions, insn.previous.previous, instanceOf) maxStackSize = Math.max(maxStackSize, 2) } } @@ -262,10 +258,13 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame return true } - private fun getParameter(insn: MethodInsnNode): ParameterNameAndNullability? { - val prev = insn.getPrevious()!! +} - val parameterNameWithFlag = when (prev.getOpcode()) { +private val MethodInsnNode.parameterNameAndNullability: ParameterNameAndNullability? + get() { + val prev = previous!! + + val parameterNameWithFlag = when (prev.opcode) { Opcodes.LDC -> (prev as LdcInsnNode).cst as String else -> return null } @@ -273,7 +272,11 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame val parameterName = if (parameterNameWithFlag.endsWith("?")) parameterNameWithFlag.dropLast(1) else parameterNameWithFlag return ParameterNameAndNullability(parameterName, parameterName !== parameterNameWithFlag) } -} + +private val MethodInsnNode.operationKind: ReifiedTypeInliner.OperationKind? get() = + previous?.previous?.intConstant?.let { + ReifiedTypeInliner.OperationKind.values().getOrNull(it) + } public class ReifiedTypeParameterMappings() { private val mappingsByName = hashMapOf() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/JavaClassFunction.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/JavaClassFunction.kt index 10c21651fcc..6bb067cf8d8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/JavaClassFunction.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/JavaClassFunction.kt @@ -30,7 +30,7 @@ public class JavaClassFunction : IntrinsicMethod() { val javaClass = resolvedCall.getResultingDescriptor().getReturnType()!!.getArguments().first().getType() return object : IntrinsicCallable(getType(javaClass>()), listOf(), null, null) { override fun invokeIntrinsic(v: InstructionAdapter) { - codegen.putReifierMarkerIfTypeIsReifiedParameter(javaClass, ReifiedTypeInliner.JAVA_CLASS_MARKER_METHOD_NAME) + codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(javaClass, ReifiedTypeInliner.OperationKind.JAVA_CLASS) putJavaLangClassInstance(v, codegen.getState().typeMapper.mapType(javaClass)) } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt index aeac767d3a0..3a00838e5ea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt @@ -39,7 +39,7 @@ public class KClassJavaProperty : IntrinsicPropertyGetter() { return when { isReifiedTypeParameter(type) -> { StackValue.operation(returnType) { iv -> - codegen.putReifierMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.JAVA_CLASS_MARKER_METHOD_NAME) + codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS) AsmUtil.putJavaLangClassInstance(iv, asmType) coerceToJavaLangClass(iv, returnType) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt index 62679d28c1b..29ab5499a5e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt @@ -16,9 +16,8 @@ package org.jetbrains.kotlin.codegen.optimization.common -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.InsnList -import org.jetbrains.org.objectweb.asm.tree.MethodNode +import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue val AbstractInsnNode.isMeaningful: Boolean get() = @@ -97,3 +96,24 @@ inline fun AbstractInsnNode.findPreviousOrNull(predicate: (AbstractInsnNode) -> fun AbstractInsnNode.hasOpcode(): Boolean = getOpcode() >= 0 + +// See InstructionAdapter +// +// public void iconst(final int cst) { +// if (cst >= -1 && cst <= 5) { +// mv.visitInsn(Opcodes.ICONST_0 + cst); +// } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { +// mv.visitIntInsn(Opcodes.BIPUSH, cst); +// } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { +// mv.visitIntInsn(Opcodes.SIPUSH, cst); +// } else { +// mv.visitLdcInsn(new Integer(cst)); +// } +// } +val AbstractInsnNode.intConstant: Int? get() = + when (opcode) { + in ICONST_M1..ICONST_5 -> opcode - ICONST_0 + BIPUSH, SIPUSH -> (this as IntInsnNode).operand + LDC -> (this as LdcInsnNode).cst as? Int + else -> null + } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java b/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java index 0a2efcb5fa4..b0af8e612a3 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java @@ -173,35 +173,11 @@ public class Intrinsics { throw new UnsupportedOperationException(message); } - public static void reifyNewArray(String typeParameterIdentifier) { + public static void reifiedOperationMarker(int id, String typeParameterIdentifier) { throwUndefinedForReified(); } - public static void reifyNewArray(String typeParameterIdentifier, String message) { - throwUndefinedForReified(message); - } - - public static void reifyCheckcast(String typeParameterIdentifier) { - throwUndefinedForReified(); - } - - public static void reifyCheckcast(String typeParameterIdentifier, String message) { - throwUndefinedForReified(message); - } - - public static void reifyInstanceof(String typeParameterIdentifier) { - throwUndefinedForReified(); - } - - public static void reifyInstanceof(String typeParameterIdentifier, String message) { - throwUndefinedForReified(message); - } - - public static void reifyJavaClass(String typeParameterIdentifier) { - throwUndefinedForReified(); - } - - public static void reifyJavaClass(String typeParameterIdentifier, String message) { + public static void reifiedOperationMarker(int id, String typeParameterIdentifier, String message) { throwUndefinedForReified(message); }