Rework reified markers format

Use integer operation identifier instead of method name to differentiate
reified operations
This commit is contained in:
Denis Zharkov
2015-12-28 08:31:47 +03:00
parent 890c961383
commit 39bbd7c795
7 changed files with 89 additions and 80 deletions
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="dzharkov">
<words>
<w>insn</w>
</words>
</dictionary>
</component>
@@ -2780,7 +2780,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
"Non-reified type parameter under ::class should be rejected by type checker: " + typeParameterDescriptor; "Non-reified type parameter under ::class should be rejected by type checker: " + typeParameterDescriptor;
assert codegen != null : assert codegen != null :
"Reference to member of reified type should be rejected by type checker " + typeParameterDescriptor; "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); putJavaLangClassInstance(v, classAsmType);
@@ -3394,9 +3394,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
public void newArrayInstruction(@NotNull KotlinType arrayType) { public void newArrayInstruction(@NotNull KotlinType arrayType) {
if (KotlinBuiltIns.isArray(arrayType)) { if (KotlinBuiltIns.isArray(arrayType)) {
KotlinType elementJetType = arrayType.getArguments().get(0).getType(); KotlinType elementJetType = arrayType.getArguments().get(0).getType();
putReifierMarkerIfTypeIsReifiedParameter( putReifiedOperationMarkerIfTypeIsReifiedParameter(
elementJetType, elementJetType,
ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME ReifiedTypeInliner.OperationKind.NEW_ARRAY
); );
v.newarray(boxType(asmType(elementJetType))); 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) { private void generateInstanceOfInstruction(@NotNull KotlinType jetType) {
Type type = boxType(asmType(jetType)); Type type = boxType(asmType(jetType));
putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.INSTANCEOF_MARKER_METHOD_NAME); putReifiedOperationMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.OperationKind.INSTANCEOF);
TypeIntrinsics.instanceOf(v, jetType, type); TypeIntrinsics.instanceOf(v, jetType, type);
} }
@NotNull @NotNull
private StackValue generateCheckCastInstruction(@NotNull KotlinType jetType, boolean safeAs) { private StackValue generateCheckCastInstruction(@NotNull KotlinType jetType, boolean safeAs) {
Type type = boxType(asmType(jetType)); Type type = boxType(asmType(jetType));
putReifierMarkerIfTypeIsReifiedParameter(jetType, putReifiedOperationMarkerIfTypeIsReifiedParameter(jetType,
safeAs ? ReifiedTypeInliner.SAFE_CHECKCAST_MARKER_METHOD_NAME safeAs ? ReifiedTypeInliner.OperationKind.SAFE_CHECKCAST
: ReifiedTypeInliner.CHECKCAST_MARKER_METHOD_NAME); : ReifiedTypeInliner.OperationKind.CHECKCAST);
TypeIntrinsics.checkcast(v, jetType, type, safeAs); TypeIntrinsics.checkcast(v, jetType, type, safeAs);
return StackValue.onStack(type); 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); TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type);
if (typeParameterDescriptor != null && typeParameterDescriptor.isReified()) { if (typeParameterDescriptor != null && typeParameterDescriptor.isReified()) {
if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) { if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) {
parentCodegen.getReifiedTypeParametersUsages(). parentCodegen.getReifiedTypeParametersUsages().
addUsedReifiedParameter(typeParameterDescriptor.getName().asString()); 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.visitLdcInsn(typeParameterDescriptor.getName().asString() + (putNullableFlag ? "?" : ""));
v.invokestatic( v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, markerMethodName, IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.REIFIED_OPERATION_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(String.class)), false
); );
} }
} }
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.generateIsCheck import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics 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.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.org.objectweb.asm.MethodVisitor 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?) { 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 { companion object {
public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray" @JvmField
public val CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast" public val REIFIED_OPERATION_MARKER_METHOD_NAME = "reifiedOperationMarker"
public val SAFE_CHECKCAST_MARKER_METHOD_NAME: String = "reifySafeCheckcast" @JvmField
public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof" public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME = "needClassReification"
public val JAVA_CLASS_MARKER_METHOD_NAME: String = "reifyJavaClass"
public val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME: String = "needClassReification"
private val PARAMETRISED_MARKERS = ImmutableSet.of( private fun isOperationReifiedMarker(insn: AbstractInsnNode) =
NEW_ARRAY_MARKER_METHOD_NAME, isReifiedMarker(insn) { it == REIFIED_OPERATION_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 isReifiedMarker(insn: AbstractInsnNode, namePredicate: (String) -> Boolean): Boolean { private fun isReifiedMarker(insn: AbstractInsnNode, namePredicate: (String) -> Boolean): Boolean {
if (insn.getOpcode() != Opcodes.INVOKESTATIC || insn !is MethodInsnNode) return false 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 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 private var maxStackSize = 0
@@ -89,7 +84,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
maxStackSize = 0 maxStackSize = 0
var result = ReifiedTypeParametersUsages() var result = ReifiedTypeParametersUsages()
for (insn in instructions.toArray()) { for (insn in instructions.toArray()) {
if (isParametrisedReifiedMarker(insn)) { if (isOperationReifiedMarker(insn)) {
val newName: String? = processReifyMarker(insn as MethodInsnNode, instructions) val newName: String? = processReifyMarker(insn as MethodInsnNode, instructions)
if (newName != null) { if (newName != null) {
result.addUsedReifiedParameter(newName) result.addUsedReifiedParameter(newName)
@@ -142,10 +137,11 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
* or null if it shouldn't * or null if it shouldn't
*/ */
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList): String? { 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 mapping = parametersMapping?.get(parameter.name) ?: return null
val kotlinType = val kotlinType =
if (isNullableMarkerInstruction(insn.name) && parameter.nullable) if (operationKind.isTypeNullabilityAware && parameter.nullable)
TypeUtils.makeNullable(mapping.type) TypeUtils.makeNullable(mapping.type)
else else
mapping.type mapping.type
@@ -156,21 +152,21 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
// process* methods return false if marker should be reified further // process* methods return false if marker should be reified further
// or it's invalid (may be emitted explicitly in code) // or it's invalid (may be emitted explicitly in code)
// they return true if instruction is reified and marker can be deleted // they return true if instruction is reified and marker can be deleted
if (when (insn.name) { if (when (operationKind) {
NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, asmType) OperationKind.NEW_ARRAY -> processNewArray(insn, asmType)
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = false) OperationKind.CHECKCAST -> processCheckcast(insn, instructions, kotlinType, asmType, safe = false)
SAFE_CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = true) OperationKind.SAFE_CHECKCAST -> processCheckcast(insn, instructions, kotlinType, asmType, safe = true)
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, instructions, kotlinType, asmType) OperationKind.INSTANCEOF -> processInstanceof(insn, instructions, kotlinType, asmType)
JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType) OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
else -> false
}) { }) {
instructions.remove(insn.getPrevious()!!) instructions.remove(insn.previous.previous!!) // PUSH operation ID
instructions.remove(insn) instructions.remove(insn.previous!!) // PUSH type parameter
instructions.remove(insn) // INVOKESTATIC marker method
} }
return null return null
} else { } 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)) instructions.set(insn.previous!!, LdcInsnNode(mapping.newName + nullableSuffix))
return mapping.newName return mapping.newName
} }
@@ -187,7 +183,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode -> rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false 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) TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe)
return true return true
} }
@@ -214,7 +210,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) { private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) {
if (TypeUtils.isNullableType(type)) { if (TypeUtils.isNullableType(type)) {
val instanceOf = insn.next val instanceOf = insn.next
insertNullCheckAround(instructions, insn.previous!!, instanceOf) insertNullCheckAround(instructions, insn.previous.previous, instanceOf)
maxStackSize = Math.max(maxStackSize, 2) maxStackSize = Math.max(maxStackSize, 2)
} }
} }
@@ -262,10 +258,13 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
return true 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 Opcodes.LDC -> (prev as LdcInsnNode).cst as String
else -> return null else -> return null
} }
@@ -273,7 +272,11 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
val parameterName = if (parameterNameWithFlag.endsWith("?")) parameterNameWithFlag.dropLast(1) else parameterNameWithFlag val parameterName = if (parameterNameWithFlag.endsWith("?")) parameterNameWithFlag.dropLast(1) else parameterNameWithFlag
return ParameterNameAndNullability(parameterName, parameterName !== 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() { public class ReifiedTypeParameterMappings() {
private val mappingsByName = hashMapOf<String, ReifiedTypeParameterMapping>() private val mappingsByName = hashMapOf<String, ReifiedTypeParameterMapping>()
@@ -30,7 +30,7 @@ public class JavaClassFunction : IntrinsicMethod() {
val javaClass = resolvedCall.getResultingDescriptor().getReturnType()!!.getArguments().first().getType() val javaClass = resolvedCall.getResultingDescriptor().getReturnType()!!.getArguments().first().getType()
return object : IntrinsicCallable(getType(javaClass<Class<Any>>()), listOf(), null, null) { return object : IntrinsicCallable(getType(javaClass<Class<Any>>()), listOf(), null, null) {
override fun invokeIntrinsic(v: InstructionAdapter) { 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)) putJavaLangClassInstance(v, codegen.getState().typeMapper.mapType(javaClass))
} }
} }
@@ -39,7 +39,7 @@ public class KClassJavaProperty : IntrinsicPropertyGetter() {
return when { return when {
isReifiedTypeParameter(type) -> { isReifiedTypeParameter(type) -> {
StackValue.operation(returnType) { iv -> StackValue.operation(returnType) { iv ->
codegen.putReifierMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.JAVA_CLASS_MARKER_METHOD_NAME) codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS)
AsmUtil.putJavaLangClassInstance(iv, asmType) AsmUtil.putJavaLangClassInstance(iv, asmType)
coerceToJavaLangClass(iv, returnType) coerceToJavaLangClass(iv, returnType)
} }
@@ -16,9 +16,8 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.tree.InsnList import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
val AbstractInsnNode.isMeaningful: Boolean get() = val AbstractInsnNode.isMeaningful: Boolean get() =
@@ -97,3 +96,24 @@ inline fun AbstractInsnNode.findPreviousOrNull(predicate: (AbstractInsnNode) ->
fun AbstractInsnNode.hasOpcode(): Boolean = fun AbstractInsnNode.hasOpcode(): Boolean =
getOpcode() >= 0 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
}
@@ -173,35 +173,11 @@ public class Intrinsics {
throw new UnsupportedOperationException(message); throw new UnsupportedOperationException(message);
} }
public static void reifyNewArray(String typeParameterIdentifier) { public static void reifiedOperationMarker(int id, String typeParameterIdentifier) {
throwUndefinedForReified(); throwUndefinedForReified();
} }
public static void reifyNewArray(String typeParameterIdentifier, String message) { public static void reifiedOperationMarker(int id, 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) {
throwUndefinedForReified(message); throwUndefinedForReified(message);
} }