From 84eb009c299e4e4d271bd8a52e81a8fa1940bb9a Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 29 Jun 2016 19:50:26 +0300 Subject: [PATCH] Refine stubs generation for special built-ins Do not generate stub if declaration has the same signature #KT-12909 Fixed --- .../kotlin/codegen/builtinSpecialBridges.kt | 28 ++++++++++++++----- .../specialBuiltins/redundantStubForSize.kt | 28 +++++++++++++++++++ .../specialBridges/redundantStubForSize.kt | 23 +++++++++++++++ .../specialBridges/redundantStubForSize.txt | 27 ++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../codegen/BytecodeListingTestGenerated.java | 6 ++++ 6 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/codegen/box/specialBuiltins/redundantStubForSize.kt create mode 100644 compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.kt create mode 100644 compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt index ec1909a2b00..2501835bc86 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -78,7 +78,6 @@ object BuiltinSpecialBridgesUtil { val commonBridges = reachableDeclarations.mapTo(LinkedHashSet(), signatureByDescriptor) commonBridges.removeAll(specialBridgesSignaturesInSuperClass + specialBridge?.from.singletonOrEmptyList()) - if (fake) { for (overridden in function.overriddenDescriptors.map { it.original }) { if (!DescriptorBasedFunctionHandle(overridden, isBodyOwner).isAbstract) { @@ -89,11 +88,9 @@ object BuiltinSpecialBridgesUtil { val bridges: MutableSet> = mutableSetOf() - // Can be null if special builtin is final (e.g. 'name' in Enum) - // because there should be no stubs for override in subclasses val superImplementationDescriptor = if (specialBridge != null) - findSuperImplementationForStubDelegation(function, fake, isBodyOwner) + findSuperImplementationForStubDelegation(function, fake, isBodyOwner, signatureByDescriptor) else null @@ -127,16 +124,33 @@ object BuiltinSpecialBridgesUtil { } } - -private fun findSuperImplementationForStubDelegation( +/** + * Stub is a method having signature from Kotlin built-ins that we generate for non-abstract declarations, + * it's bytecode consists of INVOKESPECIAL-call to real declaration in super class. + * + * Note that stub is needed only for first Kotlin class in the hierarchy. + * + * For example: + * class A : HashMap + * + * Here we generate `entrySet()` special bridge with INVOKEVIRTUAL getEntries(), + * But the latter does not exists yet, so we create a stub for it with delegation to super-class + * + * Also note that there is no special bridges for final declarations, thus no stubs either + */ +private fun findSuperImplementationForStubDelegation( function: FunctionDescriptor, fake: Boolean, - isBodyOwner: (DeclarationDescriptor) -> Boolean + isBodyOwner: (DeclarationDescriptor) -> Boolean, + signatureByDescriptor: (FunctionDescriptor) -> Signature ): FunctionDescriptor? { if (function.modality != Modality.OPEN || !fake) return null val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, isBodyOwner)).descriptor if (DescriptorUtils.isInterface(implementation.containingDeclaration)) return null + // Implementation in super-class already has proper signature + if (signatureByDescriptor(function) == signatureByDescriptor(implementation)) return null + return implementation } diff --git a/compiler/testData/codegen/box/specialBuiltins/redundantStubForSize.kt b/compiler/testData/codegen/box/specialBuiltins/redundantStubForSize.kt new file mode 100644 index 00000000000..df0376cc9cb --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/redundantStubForSize.kt @@ -0,0 +1,28 @@ +open class A1 { + open val size: Int = 56 +} + +class A2 : A1(), Collection { + // No 'getSize()' method should be generated in A2 + + override fun contains(element: String): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun containsAll(elements: Collection): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun iterator(): Iterator { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun box(): String { + if (A2().size != 56) return "fail 1" + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.kt b/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.kt new file mode 100644 index 00000000000..290b0d93a62 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.kt @@ -0,0 +1,23 @@ +open class A1 { + open val size: Int = 56 +} + +class A2 : A1(), Collection { + // No 'getSize()' method should be generated in A2 + + override fun contains(element: String): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun containsAll(elements: Collection): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun isEmpty(): Boolean { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun iterator(): Iterator { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} diff --git a/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.txt b/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.txt new file mode 100644 index 00000000000..5bd65fc0c89 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.txt @@ -0,0 +1,27 @@ +@kotlin.Metadata +public class A1 { + private final field size: int + public method (): void + public method getSize(): int +} + +@kotlin.Metadata +public final class A2 { + public method (): void + public synthetic method add(p0: java.lang.Object): boolean + public method add(p0: java.lang.String): boolean + public method addAll(p0: java.util.Collection): boolean + public method clear(): void + public method contains(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean + public final method contains(p0: java.lang.Object): boolean + public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean + public method isEmpty(): boolean + public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator + public method remove(p0: java.lang.Object): boolean + public method remove(p0: java.lang.String): boolean + public method removeAll(p0: java.util.Collection): boolean + public method retainAll(p0: java.util.Collection): boolean + public final method size(): int + public method toArray(): java.lang.Object[] + public method toArray(p0: java.lang.Object[]): java.lang.Object[] +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index ab7f0ca3b66..d5cffc81050 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13687,6 +13687,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("redundantStubForSize.kt") + public void testRedundantStubForSize() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/redundantStubForSize.kt"); + doTest(fileName); + } + @TestMetadata("removeAtTwoSpecialBridges.kt") public void testRemoveAtTwoSpecialBridges() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/removeAtTwoSpecialBridges.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index ebbf04a4d5e..449cd3891bf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -142,6 +142,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { doTest(fileName); } + @TestMetadata("redundantStubForSize.kt") + public void testRedundantStubForSize() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/specialBridges/redundantStubForSize.kt"); + doTest(fileName); + } + @TestMetadata("removeAtTwoSpecialBridges.kt") public void testRemoveAtTwoSpecialBridges() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/specialBridges/removeAtTwoSpecialBridges.kt");