Use Intrinsics.checkNotNullExpressionValue to throw NPE in Java null checks

Similarly to previous commit, this method was unused since its
introduction before 1.0, so we're changing its semantics to throw NPE
and starting to use it with API version >= 1.4.

 #KT-22275 In Progress
This commit is contained in:
Alexander Udalov
2019-08-06 09:50:21 +02:00
parent a7c8fdcbe2
commit 480313210a
16 changed files with 93 additions and 13 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.intrinsics.HashCode;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
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.JvmTarget;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
@@ -1013,7 +1014,6 @@ public class AsmUtil {
if (runtimeAssertionInfo == null || !runtimeAssertionInfo.getNeedNotNullAssertion()) return stackValue;
return new StackValue(stackValue.type, stackValue.kotlinType) {
@Override
public void putSelector(@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v) {
Type innerType = stackValue.type;
@@ -1022,8 +1022,10 @@ public class AsmUtil {
if (innerType.getSort() == Type.OBJECT || innerType.getSort() == Type.ARRAY) {
v.dup();
v.visitLdcInsn(runtimeAssertionInfo.getMessage());
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkExpressionValueIsNotNull",
"(Ljava/lang/Object;Ljava/lang/String;)V", false);
String methodName = state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_4) >= 0
? "checkNotNullExpressionValue"
: "checkExpressionValueIsNotNull";
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, methodName, "(Ljava/lang/Object;Ljava/lang/String;)V", false);
}
StackValue.coerce(innerType, innerKotlinType, type, kotlinType, v);
}
@@ -311,7 +311,7 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
// ALOAD v
// DUP
// LDC *
// INVOKESTATIC checkExpressionValueIsNotNull
// INVOKESTATIC checkExpressionValueIsNotNull/checkNotNullExpressionValue
// <...> -- v is not null here (otherwise an exception was thrown)
methodNode.instructions.insert(insn, listOfSynthetics {
@@ -440,7 +440,7 @@ internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
internal fun AbstractInsnNode.isCheckExpressionValueIsNotNull() =
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
name == "checkExpressionValueIsNotNull" &&
(name == "checkExpressionValueIsNotNull" || name == "checkNotNullExpressionValue") &&
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -805,10 +806,10 @@ class ExpressionCodegen(
val value = expression.argument.accept(this, data).materialized
mv.dup()
mv.visitLdcInsn("TODO provide message for IMPLICIT_NOTNULL") /*TODO*/
mv.invokestatic(
"kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull",
"(Ljava/lang/Object;Ljava/lang/String;)V", false
)
val methodName =
if (state.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4) "checkNotNullExpressionValue"
else "checkExpressionValueIsNotNull"
mv.invokestatic("kotlin/jvm/internal/Intrinsics", methodName, "(Ljava/lang/Object;Ljava/lang/String;)V", false)
// Unbox primitives.
value.coerce(expression.type)
}
@@ -0,0 +1,23 @@
// !API_VERSION: LATEST
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FILE: A.java
import org.jetbrains.annotations.NotNull;
public class A {
@NotNull
public static String foo() { return null; }
}
// FILE: test.kt
fun box(): String {
try {
val s: String = A.foo()
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"
}
}
@@ -11,4 +11,5 @@ fun bar(a: ArrayList<String>) {
}
// 1 checkExpressionValueIsNotNull
// 1 checkParameterIsNotNull
// 0 checkNotNullExpressionValue
// 1 checkParameterIsNotNull
@@ -10,3 +10,4 @@ public fun <R : Any> foo(x: MutableCollection<in R>, block: java.util.AbstractLi
}
// 1 checkExpressionValueIsNotNull
// 0 checkNotNullExpressionValue
@@ -35,4 +35,5 @@ internal fun bar(a: A<String, Int>, b: B<String>, c: C) {
// @JavaMultipleSubstitutionsKt.class
// 3 checkExpressionValueIsNotNull
// 0 checkNotNullExpressionValue
// 3 checkParameterIsNotNull
@@ -18,3 +18,4 @@ fun <D : CallableDescriptor> D.overriddenTreeUniqueAsSequenceA(): Boolean {
// TODO: in fact, there should be an assertion, but it's missing because of https://youtrack.jetbrains.com/issue/KT-24210.
// (This test's aim is not to check whether or not the assertion is generated, but to ensure that the behavior is deterministic.)
// 0 checkExpressionValueIsNotNull
// 0 checkNotNullExpressionValue
@@ -21,4 +21,5 @@ fun test() {
// @KKt.class:
// 1 LDC "a"
// 1 checkExpressionValueIsNotNull
// 1 checkExpressionValueIsNotNull
// 0 checkNotNullExpressionValue
@@ -0,0 +1,24 @@
// !API_VERSION: LATEST
// IGNORE_BACKEND: JVM_IR
// FILE: j/J.java
package j;
public class J {
public static final String ok() { return "OK"; }
}
// FILE: foo.kt
fun foo(a: Any) {}
// FILE: k.kt
import j.J
fun test() {
val a = J.ok()
foo(a)
foo(a)
}
// @KKt.class:
// 1 checkNotNullExpressionValue
@@ -16414,6 +16414,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/nullCheckOptimization/isNullable.kt");
}
@TestMetadata("javaNullCheckThrowsNpe_1_4.kt")
public void testJavaNullCheckThrowsNpe_1_4() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/javaNullCheckThrowsNpe_1_4.kt");
}
@TestMetadata("kt22410.kt")
public void testKt22410() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/kt22410.kt");
@@ -3276,6 +3276,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt");
}
@TestMetadata("notNullExpressionValueTwice_1_4.kt")
public void testNotNullExpressionValueTwice_1_4() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullExpressionValueTwice_1_4.kt");
}
@TestMetadata("nullCheckAfterExclExcl_1_4.kt")
public void testNullCheckAfterExclExcl_1_4() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/nullCheckAfterExclExcl_1_4.kt");
@@ -16414,6 +16414,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/nullCheckOptimization/isNullable.kt");
}
@TestMetadata("javaNullCheckThrowsNpe_1_4.kt")
public void testJavaNullCheckThrowsNpe_1_4() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/javaNullCheckThrowsNpe_1_4.kt");
}
@TestMetadata("kt22410.kt")
public void testKt22410() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/kt22410.kt");
@@ -15299,6 +15299,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/nullCheckOptimization/isNullable.kt");
}
@TestMetadata("javaNullCheckThrowsNpe_1_4.kt")
public void testJavaNullCheckThrowsNpe_1_4() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/javaNullCheckThrowsNpe_1_4.kt");
}
@TestMetadata("kt22410.kt")
public void testKt22410() throws Exception {
runTest("compiler/testData/codegen/box/nullCheckOptimization/kt22410.kt");
@@ -3246,6 +3246,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullAsNotNullable.kt");
}
@TestMetadata("notNullExpressionValueTwice_1_4.kt")
public void testNotNullExpressionValueTwice_1_4() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/notNullExpressionValueTwice_1_4.kt");
}
@TestMetadata("nullCheckAfterExclExcl_1_4.kt")
public void testNullCheckAfterExclExcl_1_4() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/nullCheckAfterExclExcl_1_4.kt");
@@ -88,9 +88,9 @@ public class Intrinsics {
}
}
public static void checkNotNullExpressionValue(Object value, String message) {
public static void checkNotNullExpressionValue(Object value, String expression) {
if (value == null) {
throw sanitizeStackTrace(new IllegalStateException(message));
throw sanitizeStackTrace(new NullPointerException(expression + " must not be null"));
}
}