Optimize method count for collection stubs

Do not generate stubs if they're already present in superclasses

 #KT-13698 In Progress
This commit is contained in:
Denis Zharkov
2016-10-05 15:52:48 +03:00
parent 900c442efd
commit e05e0ec921
13 changed files with 414 additions and 25 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERR
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.isBuiltinWithSpecialDescriptorInJvm
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.isFromBuiltins
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
@@ -32,10 +33,10 @@ import org.jetbrains.kotlin.resolve.OverridingStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl
@@ -63,10 +64,15 @@ class CollectionStubMethodGenerator(
}
private fun computeTasksToGenerate(): TasksToGenerate {
if (descriptor.kind == ClassKind.INTERFACE) return NO_TASKS
if (descriptor.kind == ClassKind.INTERFACE || descriptor is JavaClassDescriptor) return NO_TASKS
val superCollectionClasses = findRelevantSuperCollectionClasses()
if (superCollectionClasses.isEmpty()) return NO_TASKS
val existingMethodsInSuperclasses = descriptor.getAllSuperclassesWithoutAny().flatMap {
val tasksFromSuperClass = CollectionStubMethodGenerator(typeMapper, it).computeTasksToGenerate()
(tasksFromSuperClass.methodStubsToGenerate + tasksFromSuperClass.syntheticStubsToGenerate).map { it.asmMethod }
}
val methodStubsToGenerate = LinkedHashSet<JvmMethodGenericSignature>()
val syntheticStubsToGenerate = LinkedHashSet<JvmMethodGenericSignature>()
val bridgesToGenerate = LinkedHashSet<FunctionDescriptor>()
@@ -139,10 +145,19 @@ class CollectionStubMethodGenerator(
method.signature()
}
methodStubsToGenerate.add(commonSignature)
if (commonSignature.asmMethod !in existingMethodsInSuperclasses &&
// If original method already defined in a superclass we mustn't care about specialized version
// The same way we do not generate specialized version in a common case like:
// open class A<T> : MutableList<T> {
// fun add(x: T) = true
// }
// class B : A<String>() // No 'B.add(String)Z'
originalSignature.asmMethod !in existingMethodsInSuperclasses) {
methodStubsToGenerate.add(commonSignature)
if (originalSignature.asmMethod != commonSignature.asmMethod) {
syntheticStubsToGenerate.add(originalSignature)
if (originalSignature.asmMethod != commonSignature.asmMethod) {
syntheticStubsToGenerate.add(originalSignature)
}
}
}
else {
@@ -16,12 +16,11 @@
package org.jetbrains.kotlin.resolve.lazy.descriptors
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.utils.ThrowingLexicalScope
import org.jetbrains.kotlin.storage.StorageManager
@@ -75,18 +74,6 @@ class ClassResolutionScopesSupport(
}
}
fun ClassDescriptor.getAllSuperclassesWithoutAny(): List<ClassDescriptor> {
val superClasses = SmartList<ClassDescriptor>()
var parent: ClassDescriptor? = getSuperClassNotAny()
while(parent != null && parent != this) {
superClasses.add(parent)
parent = parent.getSuperClassNotAny()
}
return superClasses
}
private fun createInheritanceScope(
parent: LexicalScope,
ownerDescriptor: DeclarationDescriptor,
@@ -0,0 +1,66 @@
// FILE: B.java
public abstract class B<E> extends A<E> implements L<E> {
public String callIndexAdd(int x) {
add(0, null);
return null;
}
}
// FILE: main.kt
open class A<T> : Collection<T> {
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun contains(element: T): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsAll(elements: Collection<T>): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun iterator(): Iterator<T> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
interface L<Q> : List<Q>
// 'add(Int; Object)' method must be present in C though it has supeclass that is subclass of List
class C<F> : B<F>() {
override fun get(index: Int): F {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun indexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun lastIndexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(index: Int): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun subList(fromIndex: Int, toIndex: Int): List<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
fun box() = try {
C<String>().callIndexAdd(1)
throw RuntimeException("fail 1")
} catch (e: UnsupportedOperationException) {
if (e.message != "Operation is not supported for read-only collection") throw RuntimeException("fail 2")
"OK"
}
@@ -0,0 +1,58 @@
// FILE: test/B.java
package test;
public abstract class B<E> extends A<E> implements L<E> {
}
// FILE: main.kt
package test
open class A<T> : Collection<T> {
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun contains(element: T): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsAll(elements: Collection<T>): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun iterator(): Iterator<T> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
interface L<Q> : List<Q>
// 'remove(Int)' method must be present in C though it has supeclass that is subclass of List
class C<F> : B<F>() {
override fun get(index: Int): F {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun indexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun lastIndexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(index: Int): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun subList(fromIndex: Int, toIndex: Int): List<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,38 @@
@kotlin.Metadata
public class test/A {
public method <init>(): void
public method add(p0: java.lang.Object): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public method contains(p0: java.lang.Object): boolean
public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean
public method getSize(): int
public method isEmpty(): boolean
public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): 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[]
}
@kotlin.Metadata
public final class test/C {
public method <init>(): void
public method add(p0: int, p1: java.lang.Object): void
public method addAll(p0: int, p1: java.util.Collection): boolean
public method get(p0: int): java.lang.Object
public method indexOf(p0: java.lang.Object): int
public method lastIndexOf(p0: java.lang.Object): int
public @org.jetbrains.annotations.NotNull method listIterator(): java.util.ListIterator
public @org.jetbrains.annotations.NotNull method listIterator(p0: int): java.util.ListIterator
public method remove(p0: int): java.lang.Object
public method set(p0: int, p1: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.NotNull method subList(p0: int, p1: int): java.util.List
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@kotlin.Metadata
public interface test/L
@@ -0,0 +1,47 @@
open class A<T> : Collection<T> {
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun contains(element: T): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsAll(elements: Collection<T>): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun iterator(): Iterator<T> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
open class B<E> : A<E>()
class C<F> : B<F>(), List<F> {
override fun get(index: Int): F {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun indexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun lastIndexOf(element: F): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(index: Int): ListIterator<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun subList(fromIndex: Int, toIndex: Int): List<F> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,42 @@
@kotlin.Metadata
public class A {
public method <init>(): void
public method add(p0: java.lang.Object): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public method contains(p0: java.lang.Object): boolean
public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean
public method getSize(): int
public method isEmpty(): boolean
public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): 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[]
}
@kotlin.Metadata
public class B {
public method <init>(): void
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@kotlin.Metadata
public final class C {
public method <init>(): void
public method add(p0: int, p1: java.lang.Object): void
public method addAll(p0: int, p1: java.util.Collection): boolean
public method get(p0: int): java.lang.Object
public method indexOf(p0: java.lang.Object): int
public method lastIndexOf(p0: java.lang.Object): int
public @org.jetbrains.annotations.NotNull method listIterator(): java.util.ListIterator
public @org.jetbrains.annotations.NotNull method listIterator(p0: int): java.util.ListIterator
public method remove(p0: int): java.lang.Object
public method set(p0: int, p1: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.NotNull method subList(p0: int, p1: int): java.util.List
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,47 @@
open class A<T> : Collection<T> {
override val size: Int
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override fun contains(element: T): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun containsAll(elements: Collection<T>): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isEmpty(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun iterator(): Iterator<T> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
open class B<E : CharSequence> : A<E>()
class C : B<CharSequence>(), List<CharSequence> {
override fun get(index: Int): CharSequence {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun indexOf(element: CharSequence): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun lastIndexOf(element: CharSequence): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(): ListIterator<CharSequence> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun listIterator(index: Int): ListIterator<CharSequence> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun subList(fromIndex: Int, toIndex: Int): List<CharSequence> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,50 @@
@kotlin.Metadata
public class A {
public method <init>(): void
public method add(p0: java.lang.Object): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public method contains(p0: java.lang.Object): boolean
public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean
public method getSize(): int
public method isEmpty(): boolean
public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): 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[]
}
@kotlin.Metadata
public class B {
public method <init>(): void
public method contains(p0: java.lang.CharSequence): boolean
public final method contains(p0: java.lang.Object): boolean
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@kotlin.Metadata
public final class C {
public method <init>(): void
public method add(p0: int, p1: java.lang.CharSequence): void
public synthetic method add(p0: int, p1: java.lang.Object): void
public method addAll(p0: int, p1: java.util.Collection): boolean
public @org.jetbrains.annotations.NotNull method get(p0: int): java.lang.CharSequence
public synthetic method get(p0: int): java.lang.Object
public method indexOf(@org.jetbrains.annotations.NotNull p0: java.lang.CharSequence): int
public final method indexOf(p0: java.lang.Object): int
public method lastIndexOf(@org.jetbrains.annotations.NotNull p0: java.lang.CharSequence): int
public final method lastIndexOf(p0: java.lang.Object): int
public @org.jetbrains.annotations.NotNull method listIterator(): java.util.ListIterator
public @org.jetbrains.annotations.NotNull method listIterator(p0: int): java.util.ListIterator
public method remove(p0: int): java.lang.CharSequence
public synthetic method remove(p0: int): java.lang.Object
public method set(p0: int, p1: java.lang.CharSequence): java.lang.CharSequence
public synthetic method set(p0: int, p1: java.lang.Object): java.lang.Object
public @org.jetbrains.annotations.NotNull method subList(p0: int, p1: int): java.util.List
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -25,12 +25,14 @@ import java.io.File
abstract class AbstractBytecodeListingTest : CodegenTestCase() {
protected open val classBuilderFactory: ClassBuilderFactory
get() = ClassBuilderFactories.TEST
override fun doTest(filename: String) {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL)
loadFileByFullPath(filename)
val ktFile = File(filename)
val txtFile = File(ktFile.parentFile, ktFile.nameWithoutExtension + ".txt")
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
loadMultiFiles(files)
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".txt")
val generatedFiles = CodegenTestUtil.generateFiles(myEnvironment, myFiles, classBuilderFactory)
.getClassFiles()
.sortedBy { it.relativePath }
@@ -3586,6 +3586,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("noStubsInJavaSuperClass.kt")
public void testNoStubsInJavaSuperClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/collections/noStubsInJavaSuperClass.kt");
doTest(fileName);
}
@TestMetadata("platformValueContains.kt")
public void testPlatformValueContains() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/collections/platformValueContains.kt");
@@ -140,6 +140,33 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeListing/collectionStubs")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CollectionStubs extends AbstractBytecodeListingTest {
public void testAllFilesPresentInCollectionStubs() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/collectionStubs"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("noStubsInJavaSuperClass.kt")
public void testNoStubsInJavaSuperClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/collectionStubs/noStubsInJavaSuperClass.kt");
doTest(fileName);
}
@TestMetadata("stubsFromSuperclass.kt")
public void testStubsFromSuperclass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/collectionStubs/stubsFromSuperclass.kt");
doTest(fileName);
}
@TestMetadata("stubsFromSuperclassNoBridges.kt")
public void testStubsFromSuperclassNoBridges() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/collectionStubs/stubsFromSuperclassNoBridges.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeListing/specialBridges")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.check
fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor = companionObjectDescriptor ?: this
@@ -322,3 +323,6 @@ private fun ClassDescriptor.getAllSuperClassesTypesIncludeItself(): List<KotlinT
return result
}
fun ClassDescriptor.getAllSuperclassesWithoutAny() =
generateSequence(getSuperClassNotAny(), ClassDescriptor::getSuperClassNotAny).toCollection(SmartList<ClassDescriptor>())