Throw NPE instead of TypeCastException since 1.4

#KT-22275 In Progress
This commit is contained in:
Alexander Udalov
2019-08-06 11:21:48 +02:00
parent 2baddb029c
commit e207c96336
9 changed files with 48 additions and 13 deletions
@@ -4773,8 +4773,7 @@ The "returned" value of try expression with no finally is either the last expres
return Unit.INSTANCE; return Unit.INSTANCE;
} }
CodegenUtilKt.generateAsCast(v, rightKotlinType, boxedRightType, safeAs, CodegenUtilKt.generateAsCast(v, rightKotlinType, boxedRightType, safeAs, state.getLanguageVersionSettings());
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
@@ -21,6 +21,9 @@ import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper 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.*
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
@@ -98,16 +101,16 @@ fun generateAsCast(
kotlinType: KotlinType, kotlinType: KotlinType,
asmType: Type, asmType: Type,
isSafe: Boolean, isSafe: Boolean,
isReleaseCoroutines: Boolean languageVersionSettings: LanguageVersionSettings
) { ) {
if (!isSafe) { if (!isSafe) {
if (!TypeUtils.isNullableType(kotlinType)) { if (!TypeUtils.isNullableType(kotlinType)) {
generateNullCheckForNonSafeAs(v, kotlinType) generateNullCheckForNonSafeAs(v, kotlinType, languageVersionSettings)
} }
} else { } else {
with(v) { with(v) {
dup() dup()
TypeIntrinsics.instanceOf(v, kotlinType, asmType, isReleaseCoroutines) TypeIntrinsics.instanceOf(v, kotlinType, asmType, languageVersionSettings.isReleaseCoroutines())
val ok = Label() val ok = Label()
ifne(ok) ifne(ok)
pop() pop()
@@ -121,15 +124,19 @@ fun generateAsCast(
private fun generateNullCheckForNonSafeAs( private fun generateNullCheckForNonSafeAs(
v: InstructionAdapter, v: InstructionAdapter,
type: KotlinType type: KotlinType,
languageVersionSettings: LanguageVersionSettings
) { ) {
with(v) { with(v) {
dup() dup()
val nonnull = Label() val nonnull = Label()
ifnonnull(nonnull) ifnonnull(nonnull)
val exceptionClass =
if (languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4) "java/lang/NullPointerException"
else "kotlin/TypeCastException"
AsmUtil.genThrow( AsmUtil.genThrow(
v, v,
"kotlin/TypeCastException", exceptionClass,
"null cannot be cast to non-null type " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type) "null cannot be cast to non-null type " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
) )
mark(nonnull) mark(nonnull)
@@ -74,7 +74,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
private val initialFrameSize = codegen.frameMap.currentSize private val initialFrameSize = codegen.frameMap.currentSize
private val reifiedTypeInliner = private val reifiedTypeInliner =
ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings.isReleaseCoroutines()) ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings)
protected val functionDescriptor: FunctionDescriptor = protected val functionDescriptor: FunctionDescriptor =
if (InlineUtil.isArrayConstructorWithLambda(function)) if (InlineUtil.isArrayConstructorWithLambda(function))
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.common.intConstant import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper 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.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
@@ -65,7 +67,7 @@ class ReificationArgument(
class ReifiedTypeInliner( class ReifiedTypeInliner(
private val parametersMapping: TypeParameterMappings?, private val parametersMapping: TypeParameterMappings?,
private val typeMapper: KotlinTypeMapper, private val typeMapper: KotlinTypeMapper,
private val isReleaseCoroutines: Boolean private val languageVersionSettings: LanguageVersionSettings
) { ) {
enum class OperationKind { enum class OperationKind {
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF; NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF;
@@ -188,7 +190,7 @@ class ReifiedTypeInliner(
if (stubCheckcast !is TypeInsnNode) return false if (stubCheckcast !is TypeInsnNode) return false
val newMethodNode = MethodNode(Opcodes.API_VERSION) 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.insert(insn, newMethodNode.instructions)
instructions.remove(stubCheckcast) instructions.remove(stubCheckcast)
@@ -208,7 +210,7 @@ class ReifiedTypeInliner(
if (stubInstanceOf !is TypeInsnNode) return false if (stubInstanceOf !is TypeInsnNode) return false
val newMethodNode = MethodNode(Opcodes.API_VERSION) 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.insert(insn, newMethodNode.instructions)
instructions.remove(stubInstanceOf) instructions.remove(stubInstanceOf)
@@ -782,8 +782,7 @@ class ExpressionCodegen(
v.checkcast(boxedRightType) v.checkcast(boxedRightType)
} else { } else {
generateAsCast( generateAsCast(
mv, kotlinType, boxedRightType, expression.operator == IrTypeOperator.SAFE_CAST, mv, kotlinType, boxedRightType, expression.operator == IrTypeOperator.SAFE_CAST, state.languageVersionSettings
state.languageVersionSettings.isReleaseCoroutines()
) )
} }
MaterialValue(this, boxedRightType, expression.type).coerce(expression.type) MaterialValue(this, boxedRightType, expression.type).coerce(expression.type)
+13
View File
@@ -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"
}
}
@@ -2696,6 +2696,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); 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") @TestMetadata("asUnit.kt")
public void testAsUnit() throws Exception { public void testAsUnit() throws Exception {
runTest("compiler/testData/codegen/box/casts/asUnit.kt"); runTest("compiler/testData/codegen/box/casts/asUnit.kt");
@@ -2696,6 +2696,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); 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") @TestMetadata("asUnit.kt")
public void testAsUnit() throws Exception { public void testAsUnit() throws Exception {
runTest("compiler/testData/codegen/box/casts/asUnit.kt"); runTest("compiler/testData/codegen/box/casts/asUnit.kt");
@@ -2676,6 +2676,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/casts/asSafeForConstants.kt"); 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") @TestMetadata("asUnit.kt")
public void testAsUnit() throws Exception { public void testAsUnit() throws Exception {
runTest("compiler/testData/codegen/box/casts/asUnit.kt"); runTest("compiler/testData/codegen/box/casts/asUnit.kt");