From 35783f129bee443eed31aca7b99cc86cdbf735bf Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 8 Oct 2015 11:08:25 +0300 Subject: [PATCH] Generate INSTANCEOF barrier in bridges for `Collection.contains` --- .../kotlin/codegen/FunctionCodegen.java | 55 ++++++++++++++++++- .../codegen/box/bridges/strListContains.kt | 52 ++++++++++++++++++ .../BlackBoxCodegenTestGenerated.java | 6 ++ .../test/semantics/BridgeTestGenerated.java | 6 ++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/bridges/strListContains.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 92bf9d6199b..8d6cbb47a76 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.backend.common.bridges.Bridge; import org.jetbrains.kotlin.backend.common.bridges.BridgesPackage; import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithOnlyTargetedAnnotations; import org.jetbrains.kotlin.codegen.context.*; +import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics; import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; @@ -54,6 +55,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; +import org.jetbrains.kotlin.types.JetType; import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -815,6 +817,8 @@ public class FunctionCodegen { InstructionAdapter iv = new InstructionAdapter(mv); ImplementationBodyCodegen.markLineNumberForSyntheticFunction(owner.getThisDescriptor(), iv); + generateInstanceOfBarrierIfNeeded(iv, descriptor, bridge, delegateTo); + iv.load(0, OBJECT_TYPE); for (int i = 0, reg = 1; i < argTypes.length; i++) { StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv); @@ -822,7 +826,6 @@ public class FunctionCodegen { reg += argTypes[i].getSize(); } - if (superCallNeeded) { ClassDescriptor parentClass = getSuperClassDescriptor((ClassDescriptor) descriptor.getContainingDeclaration()); assert parentClass != null; @@ -839,6 +842,56 @@ public class FunctionCodegen { endVisit(mv, "bridge method", origin); } + private static void generateInstanceOfBarrierIfNeeded( + @NotNull InstructionAdapter iv, + @NotNull FunctionDescriptor descriptor, + @NotNull Method bridge, + @NotNull Method delegateTo + ) { + if (BuiltinsPropertiesUtilKt.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor) == null) return; + + assert descriptor.getValueParameters().size() == 1 : "Should be descriptor with one value parameter, but found: " + descriptor; + + iv.load(1, OBJECT_TYPE); + + JetType jetType = descriptor.getValueParameters().get(0).getType(); + + // TODO: reuse logic from ExpressionCodegen + if (jetType.isMarkedNullable()) { + Label nope = new Label(); + Label end = new Label(); + + iv.dup(); + iv.ifnull(nope); + TypeIntrinsics.instanceOf( + iv, + jetType, + boxType(delegateTo.getArgumentTypes()[0]) + ); + iv.goTo(end); + iv.mark(nope); + iv.pop(); + iv.iconst(1); + iv.mark(end); + } + else { + TypeIntrinsics.instanceOf( + iv, + jetType, + boxType(delegateTo.getArgumentTypes()[0]) + ); + } + + Label afterBarrier = new Label(); + + iv.ifne(afterBarrier); + + StackValue.none().put(bridge.getReturnType(), iv); + iv.areturn(bridge.getReturnType()); + + iv.visitLabel(afterBarrier); + } + public void genDelegate(@NotNull FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) { genDelegate(functionDescriptor, overriddenDescriptor.getOriginal(), (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field); diff --git a/compiler/testData/codegen/box/bridges/strListContains.kt b/compiler/testData/codegen/box/bridges/strListContains.kt new file mode 100644 index 00000000000..98887937e96 --- /dev/null +++ b/compiler/testData/codegen/box/bridges/strListContains.kt @@ -0,0 +1,52 @@ +class StrList : List { + override val size: Int + get() = throw UnsupportedOperationException() + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException() + } + + override fun contains(o: String?) = o == null || o == "abc" + + override fun iterator(): Iterator { + throw UnsupportedOperationException() + } + + override fun containsAll(c: Collection) = false + override fun get(index: Int): String { + throw UnsupportedOperationException() + } + + override fun indexOf(o: Any?): Int { + throw UnsupportedOperationException() + } + + override fun lastIndexOf(o: Any?): Int { + throw UnsupportedOperationException() + } + + override fun listIterator(): ListIterator { + throw UnsupportedOperationException() + } + + override fun listIterator(index: Int): ListIterator { + throw UnsupportedOperationException() + } + + override fun subList(fromIndex: Int, toIndex: Int): List { + throw UnsupportedOperationException() + } +} + +fun Collection.forceContains(x: Any?): Boolean = contains(x as E) + +fun box(): String { + val strList = StrList() + + if (strList.forceContains(1)) return "fail 1" + if (!strList.forceContains(null)) return "fail 2" + if (strList.forceContains("cde")) return "fail 3" + if (!strList.forceContains("abc")) return "fail 4" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 720e134cac1..21c44339400 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -664,6 +664,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("strListContains.kt") + public void testStrListContains() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/strListContains.kt"); + doTest(fileName); + } + @TestMetadata("traitImplInheritsTraitImpl.kt") public void testTraitImplInheritsTraitImpl() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java index 28874e46337..37adce31a2f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BridgeTestGenerated.java @@ -281,6 +281,12 @@ public class BridgeTestGenerated extends AbstractBridgeTest { doTest(fileName); } + @TestMetadata("strListContains.kt") + public void testStrListContains() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/strListContains.kt"); + doTest(fileName); + } + @TestMetadata("traitImplInheritsTraitImpl.kt") public void testTraitImplInheritsTraitImpl() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt");