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:
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -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)
|
||||
}
|
||||
|
||||
+23
@@ -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
|
||||
|
||||
+1
@@ -10,3 +10,4 @@ public fun <R : Any> foo(x: MutableCollection<in R>, block: java.util.AbstractLi
|
||||
}
|
||||
|
||||
// 1 checkExpressionValueIsNotNull
|
||||
// 0 checkNotNullExpressionValue
|
||||
|
||||
+1
@@ -35,4 +35,5 @@ internal fun bar(a: A<String, Int>, b: B<String>, c: C) {
|
||||
|
||||
// @JavaMultipleSubstitutionsKt.class
|
||||
// 3 checkExpressionValueIsNotNull
|
||||
// 0 checkNotNullExpressionValue
|
||||
// 3 checkParameterIsNotNull
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
Vendored
+2
-1
@@ -21,4 +21,5 @@ fun test() {
|
||||
|
||||
// @KKt.class:
|
||||
// 1 LDC "a"
|
||||
// 1 checkExpressionValueIsNotNull
|
||||
// 1 checkExpressionValueIsNotNull
|
||||
// 0 checkNotNullExpressionValue
|
||||
|
||||
Vendored
+24
@@ -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
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user