tmp
diff --git a/compiler/build-tools/kotlin-build-tools-api-tests/src/testCrossModuleIncrementalChanges/kotlin/InlinedLambdaChangeTest.kt b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCrossModuleIncrementalChanges/kotlin/InlinedLambdaChangeTest.kt index f480725..e489be7 100644 --- a/compiler/build-tools/kotlin-build-tools-api-tests/src/testCrossModuleIncrementalChanges/kotlin/InlinedLambdaChangeTest.kt +++ b/compiler/build-tools/kotlin-build-tools-api-tests/src/testCrossModuleIncrementalChanges/kotlin/InlinedLambdaChangeTest.kt
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.buildtools.api.tests.compilation.util.execute import org.jetbrains.kotlin.buildtools.api.tests.compilation.util.moduleWithInlineSnapshotting import org.jetbrains.kotlin.test.TestMetadata -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName
diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/BasicClassInfo.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/BasicClassInfo.kt index d3cdc61..103ca3a 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/BasicClassInfo.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/BasicClassInfo.kt
@@ -24,7 +24,8 @@ val supertypes: List<JvmClassName>, private val accessFlags: Int, - val isAnonymous: Boolean + val isAnonymous: Boolean, + val outerClassWithMethodName: String?, // per ASM documentation, null if this is not a proper local or anonymous class ) { val isKotlinClass = kotlinClassHeader != null val isPrivate = flagEnabled(accessFlags, Opcodes.ACC_PRIVATE) @@ -60,12 +61,14 @@ kotlinClassHeader = kotlinClassHeaderClassVisitor.getKotlinClassHeader(), supertypes = basicClassInfoVisitor.getSupertypes(), accessFlags = basicClassInfoVisitor.getAccessFlags(), - isAnonymous = innerClassesInfo[className]?.let { it.innerSimpleName == null } ?: false + isAnonymous = innerClassesInfo[className]?.let { it.innerSimpleName == null } == true, + outerClassWithMethodName = outerClassClassVisitor.getOuterClassWithMethod(), ) } } } +//TODO: I don't like how it looks, why not merge all classvisitors into a single one? thoughts? private class BasicClassInfoClassVisitor(cv: ClassVisitor) : ClassVisitor(Opcodes.API_VERSION, cv) { private var className: String? = null private var classAccess: Int? = null @@ -96,12 +99,26 @@ fun getInnerClassesInfo(): InnerClassesInfo = innerClassesInfo } +/** + * Example inputs with just two layers of local classes: + * + * com/example/ictest/InlinedLocalClassKt calculate ()I + * com/example/ictest/InlinedLocalClassKt$calculate$outer$1 invoke ()Ljava/lang/Integer; + * + * If the [calc] at the end of OneClass$InnerClass$AnotherInnerClass$calc is actually an inline fun, + * we'd want to include into its abi snapshot every local class whose "outer class with method" + * has a prefix of "com/example/ictest/OneClass$InnerClass$AnotherInnerClass$calc" + */ private class OuterClassClassVisitor(cv: ClassVisitor) : ClassVisitor(Opcodes.API_VERSION, cv) { + private var outerClassWithMethod: String? = null override fun visitOuterClass(owner: String?, name: String?, descriptor: String?) { - println("yo $owner $name $descriptor") + println("yo $owner $name") + outerClassWithMethod = owner?.let { owner -> name?.let { name -> "$owner\$$name" }} super.visitOuterClass(owner, name, descriptor) } + + fun getOuterClassWithMethod(): String? = outerClassWithMethod } private class KotlinClassHeaderClassVisitor : ClassVisitor(Opcodes.API_VERSION) {
diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/ClassListSnapshotter.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/ClassListSnapshotter.kt index d513708..7311a4b 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/ClassListSnapshotter.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/impl/ClassListSnapshotter.kt
@@ -144,14 +144,24 @@ */ override fun snapshot(): List<ClassSnapshot> { - // expected order: - // InlinedLocalClassKt$calculate$foo$1, InlinedLocalClassKt$calculate$bar$1, InlinedLocalClassKt - // this allows us to visit inner classes before the outer class - val sortedClasses = classes.sortedByDescending { it.classFile.getClassName().internalName } - println(sortedClasses.map {it.classFile.getClassName().internalName}.joinToString(", ")) + // Very fair assumption: it's not possible to define a top-level class inside an inline function + // Fair assumption #2: class names can be obfuscated, - val (inner, outer) = classes.partition { it.classFile.getClassName().internalName.contains("$") } - val innerSorted = inner.sortedBy { it.classFile.getClassName().internalName } + //TODO - critical - look at r8ed classfiles + + val nonTopLevelClasses = classes.filter { it.classFile.getClassName().internalName.contains("$") } + //val innerSorted = inner.sortedBy { it.classFile.getClassName().internalName } + + /** + * We assume that inner classes are relatively light-weight, so it is not a problem to load them twice. + * A possible optimization is adding a cache with soft references so we'd utilize the available RAM efficiently? + */ + + for (innerClass in inner) { + //TODO add test case - inline fun in a local class + //disgusting + makeOrReuseClassSnapshot(innerClass) + } for (outerClass in outer) { // outer class snapshotting might find accessible inline functons ->
diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/BasicClassInfoTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/BasicClassInfoTest.kt index fa02dd3..c9bb074 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/BasicClassInfoTest.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/BasicClassInfoTest.kt
@@ -24,7 +24,9 @@ @Test fun `compute BasicClassInfo`() { val compiledClasses = compileJava(className, sourceCode) - val classIds = compiledClasses.map { BasicClassInfo.compute(it).classId } + val basicInfos = compiledClasses.map { BasicClassInfo.compute(it) } + val classIds = basicInfos.map { it.classId } + val containingMethods = basicInfos.map { it.outerClassWithMethodName } assertEquals( listOf( @@ -43,6 +45,21 @@ ), classIds ) + + // [classId]'s definition of "local" is wider than JVM definition of local, so we don't visitOuterClass for every class + // with expected "local = true" state from above + assertEquals( + listOf( + null, + "com/example/TopLevelClass\$methodWithinTopLevelClass", + "com/example/TopLevelClass\$methodWithinTopLevelClass", + "com/example/TopLevelClass\$1LocalClass\$methodWithinLocalClass", + null, null, null, + "com/example/TopLevelClass\$InnerClass\$someMethod", + null, null, null, null, + ), + containingMethods + ) } @Suppress("SameParameterValue")