From 155bbd40ae0dd2259d7be938b4752419d3f8be9d Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 15 Oct 2015 21:10:43 +0300 Subject: [PATCH] Do not generate not-null assertion for argument of Collection.contains Of course not only for contains but for other methods having INSTANCEOF bar.rier Otherwise using platform null values becomes for such methods becomes dangerous in cases like `platformString in setOf("", "")` --- .../kotlin/codegen/ExpressionCodegen.java | 2 + .../kotlin/codegen/builtinSpecialBridges.kt | 30 ++++++++++++++ .../collections/platformValueContains/J.java | 7 ++++ .../platformValueContains.kt | 40 +++++++++++++++++++ .../BlackBoxWithJavaCodegenTestGenerated.java | 6 +++ 5 files changed, 85 insertions(+) create mode 100644 compiler/testData/codegen/boxWithJava/collections/platformValueContains/J.java create mode 100644 compiler/testData/codegen/boxWithJava/collections/platformValueContains/platformValueContains.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index cf7e21e1064..99ea259ad92 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -284,6 +284,8 @@ public class ExpressionCodegen extends JetVisitor implem runtimeAssertionInfo = bindingContext.get(BindingContextSlicesKt.getRUNTIME_ASSERTION_INFO(), (JetExpression) selector); } + if (BuiltinSpecialBridgesKt.isValueArgumentForCallToMethodWithTypeCheckBarrier(selector, bindingContext)) return stackValue; + return genNotNullAssertions(state, stackValue, runtimeAssertionInfo); } catch (ProcessCanceledException e) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt index ada93d726b6..2ad7bd71fa3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -18,9 +18,19 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.backend.common.bridges.* import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.getOverriddenBuiltinWithDifferentJvmDescriptor +import org.jetbrains.kotlin.psi.JetCallElement +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetPsiUtil +import org.jetbrains.kotlin.psi.JetValueArgument +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure import org.jetbrains.kotlin.utils.singletonOrEmptyList @@ -123,3 +133,23 @@ private fun needGenerateSpecialBridge( return true } + +public fun isValueArgumentForCallToMethodWithTypeCheckBarrier( + element: JetElement, + bindingContext: BindingContext +): Boolean { + + val parentCall = element.getParentCall(bindingContext, strict = true) ?: return false + val argumentExpression = parentCall.valueArguments.singleOrNull()?.getArgumentExpression() ?: return false + if (JetPsiUtil.deparenthesize(argumentExpression) !== element) return false + + val candidateDescriptor = parentCall.getResolvedCall(bindingContext)?.candidateDescriptor as CallableMemberDescriptor? + ?: return false + + + if (candidateDescriptor.getSpecialSignatureInfo() == BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo.GENERIC_PARAMETER) { + return true + } + + return false +} diff --git a/compiler/testData/codegen/boxWithJava/collections/platformValueContains/J.java b/compiler/testData/codegen/boxWithJava/collections/platformValueContains/J.java new file mode 100644 index 00000000000..2f9e2cb1da2 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/platformValueContains/J.java @@ -0,0 +1,7 @@ +import java.util.*; + +public class J { + public static String nullValue() { + return null; + } +} diff --git a/compiler/testData/codegen/boxWithJava/collections/platformValueContains/platformValueContains.kt b/compiler/testData/codegen/boxWithJava/collections/platformValueContains/platformValueContains.kt new file mode 100644 index 00000000000..8172f14bfb8 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/collections/platformValueContains/platformValueContains.kt @@ -0,0 +1,40 @@ +class MySet : Set { + override val size: Int + get() = throw UnsupportedOperationException() + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun contains(o: String): Boolean { + throw UnsupportedOperationException() + } + + override fun iterator(): Iterator { + throw UnsupportedOperationException() + } + + override fun containsAll(c: Collection): Boolean { + throw UnsupportedOperationException() + } +} + +fun box(): String { + val mySet = MySet() + + // no UnsupportedOperationException thrown + mySet.contains(J.nullValue()) + J.nullValue() in mySet + + val set: Set = mySet + set.contains(J.nullValue()) + J.nullValue() in set + + val anySet: Set = mySet as Set + anySet.contains(J.nullValue()) + anySet.contains(null) + J.nullValue() in anySet + null in anySet + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index 5674bb9177f..09cbd9dee28 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -197,6 +197,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("platformValueContains") + public void testPlatformValueContains() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/platformValueContains/"); + doTestWithJava(fileName); + } + @TestMetadata("removeAtInt") public void testRemoveAtInt() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/removeAtInt/");