Unify code generation of as/is operation
They should be the same for common and reified cases
This commit is contained in:
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.codegen.inline.*;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics;
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
@@ -3674,30 +3673,17 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
}
|
||||
|
||||
boolean safeAs = opToken == KtTokens.AS_SAFE;
|
||||
Type type = boxType(asmType(rightType));
|
||||
if (TypeUtils.isReifiedTypeParameter(rightType)) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
|
||||
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
|
||||
: ReifiedTypeInliner.OperationKind.AS);
|
||||
v.checkcast(boxType(asmType(rightType)));
|
||||
v.checkcast(type);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
if (opToken != KtTokens.AS_SAFE) {
|
||||
if (!TypeUtils.isNullableType(rightType)) {
|
||||
CodegenUtilKt.generateNullCheckForNonSafeAs(v, rightType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
v.dup();
|
||||
generateInstanceOfInstruction(rightType);
|
||||
Label ok = new Label();
|
||||
v.ifne(ok);
|
||||
v.pop();
|
||||
v.aconst(null);
|
||||
v.mark(ok);
|
||||
}
|
||||
CodegenUtilKt.generateAsCast(v, rightType, type, safeAs);
|
||||
|
||||
generateCheckCastInstruction(rightType, safeAs);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
@@ -3749,38 +3735,19 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
v.dup();
|
||||
}
|
||||
|
||||
Type type = boxType(asmType(kotlinType));
|
||||
if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
|
||||
v.instanceOf(boxType(asmType(kotlinType)));
|
||||
v.instanceOf(type);
|
||||
return null;
|
||||
}
|
||||
|
||||
CodegenUtilKt.generateIsCheck(v, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter adapter) {
|
||||
generateInstanceOfInstruction(kotlinType);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
CodegenUtilKt.generateIsCheck(v, kotlinType, type);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void generateInstanceOfInstruction(@NotNull KotlinType kotlinType) {
|
||||
Type type = boxType(asmType(kotlinType));
|
||||
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, type);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StackValue generateCheckCastInstruction(@NotNull KotlinType kotlinType, boolean safeAs) {
|
||||
Type type = boxType(asmType(kotlinType));
|
||||
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
|
||||
TypeIntrinsics.checkcast(v, kotlinType, type, safeAs);
|
||||
return StackValue.onStack(type);
|
||||
}
|
||||
|
||||
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
|
||||
) {
|
||||
|
||||
@@ -886,13 +886,7 @@ public class FunctionCodegen {
|
||||
iv.ifnonnull(afterBarrier);
|
||||
}
|
||||
else {
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(InstructionAdapter adapter) {
|
||||
TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType));
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType));
|
||||
iv.ifne(afterBarrier);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
|
||||
@@ -28,15 +29,17 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
fun generateIsCheck(
|
||||
v: InstructionAdapter,
|
||||
isNullable: Boolean,
|
||||
generateInstanceOfInstruction: (InstructionAdapter) -> Unit
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type
|
||||
) {
|
||||
if (isNullable) {
|
||||
if (TypeUtils.isNullableType(kotlinType)) {
|
||||
val nope = Label()
|
||||
val end = Label()
|
||||
|
||||
@@ -45,7 +48,8 @@ fun generateIsCheck(
|
||||
|
||||
ifnull(nope)
|
||||
|
||||
generateInstanceOfInstruction(this)
|
||||
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
|
||||
|
||||
goTo(end)
|
||||
|
||||
mark(nope)
|
||||
@@ -56,11 +60,37 @@ fun generateIsCheck(
|
||||
}
|
||||
}
|
||||
else {
|
||||
generateInstanceOfInstruction(v)
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateNullCheckForNonSafeAs(
|
||||
fun generateAsCast(
|
||||
v: InstructionAdapter,
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type,
|
||||
isSafe: Boolean
|
||||
) {
|
||||
if (!isSafe) {
|
||||
if (!TypeUtils.isNullableType(kotlinType)) {
|
||||
generateNullCheckForNonSafeAs(v, kotlinType)
|
||||
}
|
||||
}
|
||||
else {
|
||||
with(v) {
|
||||
dup()
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
val ok = Label()
|
||||
ifne(ok)
|
||||
pop()
|
||||
aconst(null)
|
||||
mark(ok)
|
||||
}
|
||||
}
|
||||
|
||||
TypeIntrinsics.checkcast(v, kotlinType, asmType, isSafe)
|
||||
}
|
||||
|
||||
private fun generateNullCheckForNonSafeAs(
|
||||
v: InstructionAdapter,
|
||||
type: KotlinType
|
||||
) {
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||
import org.jetbrains.kotlin.codegen.generateAsCast
|
||||
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
|
||||
@@ -177,64 +176,42 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
||||
|
||||
private fun processAs(insn: MethodInsnNode,
|
||||
instructions: InsnList,
|
||||
jetType: KotlinType,
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type,
|
||||
safe: Boolean) =
|
||||
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode ->
|
||||
if (instanceofInsn !is TypeInsnNode) return false
|
||||
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
|
||||
if (stubCheckcast !is TypeInsnNode) return false
|
||||
|
||||
val newMethodNode = MethodNode(InlineCodegenUtil.API)
|
||||
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
instructions.remove(stubCheckcast)
|
||||
|
||||
// TODO: refine max stack calculation (it's not always as big as +4)
|
||||
maxStackSize = Math.max(maxStackSize, 4)
|
||||
|
||||
addNullCheckForAsIfNeeded(insn.previous.previous, instructions, jetType, safe)
|
||||
TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun addNullCheckForAsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, jetType: KotlinType, safe: Boolean) {
|
||||
if (!safe && !TypeUtils.isNullableType(jetType)) {
|
||||
val methodNode = MethodNode(InlineCodegenUtil.API)
|
||||
generateNullCheckForNonSafeAs(InstructionAdapter(methodNode), jetType)
|
||||
private fun processIs(insn: MethodInsnNode,
|
||||
instructions: InsnList,
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type) =
|
||||
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
|
||||
if (stubInstanceOf !is TypeInsnNode) return false
|
||||
|
||||
InlineCodegenUtil.insertNodeBefore(methodNode, instructions, insn)
|
||||
maxStackSize = Math.max(maxStackSize, 4)
|
||||
}
|
||||
}
|
||||
val newMethodNode = MethodNode(InlineCodegenUtil.API)
|
||||
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
|
||||
|
||||
private fun processIs(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type) =
|
||||
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { instanceofInsn: AbstractInsnNode ->
|
||||
if (instanceofInsn !is TypeInsnNode) return false
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
instructions.remove(stubInstanceOf)
|
||||
|
||||
addNullCheckForIsIfNeeded(insn, instructions, jetType)
|
||||
TypeIntrinsics.instanceOf(instanceofInsn, instructions, jetType, asmType)
|
||||
// TODO: refine max stack calculation (it's not always as big as +2)
|
||||
maxStackSize = Math.max(maxStackSize, 2)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) {
|
||||
if (TypeUtils.isNullableType(type)) {
|
||||
val instanceOf = insn.next
|
||||
insertNullCheckAround(instructions, insn.previous.previous, instanceOf)
|
||||
maxStackSize = Math.max(maxStackSize, 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertNullCheckAround(instructions: InsnList, start: AbstractInsnNode, end: AbstractInsnNode) {
|
||||
val methodNode = MethodNode(InlineCodegenUtil.API)
|
||||
var splitIndex: Int = -1
|
||||
generateIsCheck(InstructionAdapter(methodNode), true) {
|
||||
splitIndex = methodNode.instructions.size()
|
||||
}
|
||||
assert(splitIndex >= 0) {
|
||||
"Split index should be non-negative, but $splitIndex"
|
||||
}
|
||||
|
||||
val nullCheckInsns = methodNode.instructions.toArray()
|
||||
nullCheckInsns.take(splitIndex).forEach {
|
||||
instructions.insertBefore(start, it)
|
||||
}
|
||||
|
||||
nullCheckInsns.drop(splitIndex).reversed().forEach {
|
||||
instructions.insert(end, it)
|
||||
}
|
||||
}
|
||||
|
||||
inline private fun rewriteNextTypeInsn(
|
||||
marker: MethodInsnNode,
|
||||
expectedNextOpcode: Int,
|
||||
|
||||
@@ -108,40 +108,6 @@ public object TypeIntrinsics {
|
||||
v.checkcast(asmType)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun checkcast(
|
||||
checkcastInsn: TypeInsnNode,
|
||||
instructions: InsnList,
|
||||
kotlinType: KotlinType, asmType: Type,
|
||||
safe: Boolean) {
|
||||
if (safe) {
|
||||
checkcastInsn.desc = asmType.internalName
|
||||
return
|
||||
}
|
||||
|
||||
val functionTypeArity = getFunctionTypeArity(kotlinType)
|
||||
if (functionTypeArity >= 0) {
|
||||
instructions.insertBefore(checkcastInsn, iconstNode(functionTypeArity))
|
||||
|
||||
val beforeCheckcast = typeIntrinsicNode(BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY, BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY_DESCRIPTOR)
|
||||
instructions.insertBefore(checkcastInsn, beforeCheckcast)
|
||||
|
||||
instructions.insertBefore(checkcastInsn, TypeInsnNode(Opcodes.CHECKCAST, asmType.internalName))
|
||||
instructions.remove(checkcastInsn)
|
||||
return
|
||||
}
|
||||
|
||||
val asMutableCollectionMethodName = getAsMutableCollectionMethodName(kotlinType)
|
||||
if (asMutableCollectionMethodName != null) {
|
||||
instructions.insertBefore(checkcastInsn,
|
||||
typeIntrinsicNode(asMutableCollectionMethodName, getAsMutableCollectionDescriptor(asmType)))
|
||||
instructions.remove(checkcastInsn)
|
||||
return
|
||||
}
|
||||
|
||||
checkcastInsn.desc = asmType.internalName
|
||||
}
|
||||
|
||||
private val INTRINSICS_CLASS = "kotlin/jvm/internal/TypeIntrinsics"
|
||||
|
||||
private val IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity"
|
||||
|
||||
Reference in New Issue
Block a user