From e207c963365af6d25e8ea77564eb2438f2d4e633 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 6 Aug 2019 11:21:48 +0200 Subject: [PATCH] Throw NPE instead of TypeCastException since 1.4 #KT-22275 In Progress --- .../kotlin/codegen/ExpressionCodegen.java | 3 +-- .../org/jetbrains/kotlin/codegen/codegenUtil.kt | 17 ++++++++++++----- .../kotlin/codegen/inline/InlineCodegen.kt | 2 +- .../kotlin/codegen/inline/ReifiedTypeInliner.kt | 8 +++++--- .../backend/jvm/codegen/ExpressionCodegen.kt | 3 +-- .../codegen/box/casts/asThrowsNpe_1_4.kt | 13 +++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++++ .../codegen/LightAnalysisModeTestGenerated.java | 5 +++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++++ 9 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 3c11651d2bd..f2d7eb78df6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -4773,8 +4773,7 @@ The "returned" value of try expression with no finally is either the last expres return Unit.INSTANCE; } - CodegenUtilKt.generateAsCast(v, rightKotlinType, boxedRightType, safeAs, - state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines)); + CodegenUtilKt.generateAsCast(v, rightKotlinType, boxedRightType, safeAs, state.getLanguageVersionSettings()); return Unit.INSTANCE; }); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 72ac8aec3aa..b7d06fd947d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -21,6 +21,9 @@ import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl @@ -98,16 +101,16 @@ fun generateAsCast( kotlinType: KotlinType, asmType: Type, isSafe: Boolean, - isReleaseCoroutines: Boolean + languageVersionSettings: LanguageVersionSettings ) { if (!isSafe) { if (!TypeUtils.isNullableType(kotlinType)) { - generateNullCheckForNonSafeAs(v, kotlinType) + generateNullCheckForNonSafeAs(v, kotlinType, languageVersionSettings) } } else { with(v) { dup() - TypeIntrinsics.instanceOf(v, kotlinType, asmType, isReleaseCoroutines) + TypeIntrinsics.instanceOf(v, kotlinType, asmType, languageVersionSettings.isReleaseCoroutines()) val ok = Label() ifne(ok) pop() @@ -121,15 +124,19 @@ fun generateAsCast( private fun generateNullCheckForNonSafeAs( v: InstructionAdapter, - type: KotlinType + type: KotlinType, + languageVersionSettings: LanguageVersionSettings ) { with(v) { dup() val nonnull = Label() ifnonnull(nonnull) + val exceptionClass = + if (languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4) "java/lang/NullPointerException" + else "kotlin/TypeCastException" AsmUtil.genThrow( v, - "kotlin/TypeCastException", + exceptionClass, "null cannot be cast to non-null type " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type) ) mark(nonnull) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 79fd845157c..da4629c759e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -74,7 +74,7 @@ abstract class InlineCodegen( private val initialFrameSize = codegen.frameMap.currentSize private val reifiedTypeInliner = - ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings.isReleaseCoroutines()) + ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings) protected val functionDescriptor: FunctionDescriptor = if (InlineUtil.isArrayConstructorWithLambda(function)) 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 10ad7c3487b..02ef95b3e64 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt @@ -21,6 +21,8 @@ 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.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -65,7 +67,7 @@ class ReificationArgument( class ReifiedTypeInliner( private val parametersMapping: TypeParameterMappings?, private val typeMapper: KotlinTypeMapper, - private val isReleaseCoroutines: Boolean + private val languageVersionSettings: LanguageVersionSettings ) { enum class OperationKind { NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF; @@ -188,7 +190,7 @@ class ReifiedTypeInliner( if (stubCheckcast !is TypeInsnNode) return false val newMethodNode = MethodNode(Opcodes.API_VERSION) - generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, isReleaseCoroutines) + generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, languageVersionSettings) instructions.insert(insn, newMethodNode.instructions) instructions.remove(stubCheckcast) @@ -208,7 +210,7 @@ class ReifiedTypeInliner( if (stubInstanceOf !is TypeInsnNode) return false val newMethodNode = MethodNode(Opcodes.API_VERSION) - generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType, isReleaseCoroutines) + generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType, languageVersionSettings.isReleaseCoroutines()) instructions.insert(insn, newMethodNode.instructions) instructions.remove(stubInstanceOf) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 200a11d3c62..f2d6cfa9776 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -782,8 +782,7 @@ class ExpressionCodegen( v.checkcast(boxedRightType) } else { generateAsCast( - mv, kotlinType, boxedRightType, expression.operator == IrTypeOperator.SAFE_CAST, - state.languageVersionSettings.isReleaseCoroutines() + mv, kotlinType, boxedRightType, expression.operator == IrTypeOperator.SAFE_CAST, state.languageVersionSettings ) } MaterialValue(this, boxedRightType, expression.type).coerce(expression.type) diff --git a/compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt b/compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt new file mode 100644 index 00000000000..e83aaa646e0 --- /dev/null +++ b/compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt @@ -0,0 +1,13 @@ +// !API_VERSION: LATEST +// TARGET_BACKEND: JVM + +fun box(): String { + val s: String? = null + try { + s as String + return "Fail: NPE should have been thrown" + } catch (e: Throwable) { + if (e::class != NullPointerException::class) return "Fail: exception class should be NPE: ${e::class}" + return "OK" + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 2c9319bf15d..5cfcd559876 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2696,6 +2696,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); } + @TestMetadata("asThrowsNpe_1_4.kt") + public void testAsThrowsNpe_1_4() throws Exception { + runTest("compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt"); + } + @TestMetadata("asUnit.kt") public void testAsUnit() throws Exception { runTest("compiler/testData/codegen/box/casts/asUnit.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index bc551b50877..764d9783d88 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2696,6 +2696,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); } + @TestMetadata("asThrowsNpe_1_4.kt") + public void testAsThrowsNpe_1_4() throws Exception { + runTest("compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt"); + } + @TestMetadata("asUnit.kt") public void testAsUnit() throws Exception { runTest("compiler/testData/codegen/box/casts/asUnit.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index afcb1e6a9b2..30c6542f52c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -2676,6 +2676,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); } + @TestMetadata("asThrowsNpe_1_4.kt") + public void testAsThrowsNpe_1_4() throws Exception { + runTest("compiler/testData/codegen/box/casts/asThrowsNpe_1_4.kt"); + } + @TestMetadata("asUnit.kt") public void testAsUnit() throws Exception { runTest("compiler/testData/codegen/box/casts/asUnit.kt");