diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index d1caad8c3b6..52cad2d6c0d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -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() { - @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 ) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 0c974c96109..369bc2715ab 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -886,13 +886,7 @@ public class FunctionCodegen { iv.ifnonnull(afterBarrier); } else { - CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1() { - @Override - public Unit invoke(InstructionAdapter adapter) { - TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType)); - return Unit.INSTANCE; - } - }); + CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType)); iv.ifne(afterBarrier); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 2c37c290c18..2a0d78d2ce6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -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 ) { 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 4518346d5c9..cb2b531d5ea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt @@ -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, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/TypeIntrinsics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/TypeIntrinsics.kt index 97c78e211ce..e162d9fc295 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/TypeIntrinsics.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/TypeIntrinsics.kt @@ -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"