From 61d3b6ee1fc944cb3e37732afe486deb818186c6 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 31 Oct 2018 16:53:27 +0100 Subject: [PATCH] Restore heuristic to compute inner class names in ClassifierResolutionContext This commit restores the heuristic removed in 9df02b2366, but only enables it in case the class wasn't found with the name mapped initially according to the InnerClasses attribute values. This helps to support class files which do not have the InnerClasses attribute for all nested referenced classes, such as those generated by Groovy. Note that in theory it's still possible for this code to behave incorrectly, for example a reference to a class `C` nested in a _top-level_ class named `A$B` will not be loaded by this code correctly. This is a lower-priority issue and it will need to be fixed later. #KT-27874 Fixed --- .../impl/classFiles/BinaryJavaClass.kt | 2 +- .../classFiles/ClassifierResolutionContext.kt | 47 ++++++++++++++++++- .../kotlin/gradle/SimpleKotlinGradleIT.kt | 4 +- .../build.gradle | 0 .../lib/build.gradle | 0 .../groovy/ClassWithReferenceToInner.groovy | 9 ++++ .../lib/src/main/groovy/MyTrait.groovy | 0 .../src/main/groovy/MyTraitAccessor.groovy | 0 .../settings.gradle | 0 .../src/main/kotlin/MyKotlinClass.kt | 6 +++ .../src/main/kotlin/MyKotlinClass.kt | 3 -- 11 files changed, 64 insertions(+), 7 deletions(-) rename libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/{groovyTraitsWithFields => groovyInterop}/build.gradle (100%) rename libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/{groovyTraitsWithFields => groovyInterop}/lib/build.gradle (100%) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/ClassWithReferenceToInner.groovy rename libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/{groovyTraitsWithFields => groovyInterop}/lib/src/main/groovy/MyTrait.groovy (100%) rename libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/{groovyTraitsWithFields => groovyInterop}/lib/src/main/groovy/MyTraitAccessor.groovy (100%) rename libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/{groovyTraitsWithFields => groovyInterop}/settings.gradle (100%) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/src/main/kotlin/MyKotlinClass.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt index 8889cf142d0..a747aab8afc 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -33,7 +33,7 @@ import java.text.StringCharacterIterator class BinaryJavaClass( override val virtualFile: VirtualFile, override val fqName: FqName, - private val context: ClassifierResolutionContext, + internal val context: ClassifierResolutionContext, private val signatureParser: BinaryClassSignatureParser, override var access: Int = 0, override val outerClass: JavaClass?, diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt index 789b272f38f..67f3593021c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.javaslang.ImmutableHashMap import org.jetbrains.kotlin.util.javaslang.ImmutableMap import org.jetbrains.kotlin.util.javaslang.getOrNull +import org.jetbrains.kotlin.utils.SmartList typealias ClassIdToJavaClass = (ClassId) -> JavaClass? @@ -73,5 +74,49 @@ class ClassifierResolutionContext private constructor( return ClassId.topLevel(FqName(internalName.replace('/', '.'))) } - internal fun resolveByInternalName(c: String): Result = resolveClass(mapInternalNameToClassId(c)) + // See com.intellij.psi.impl.compiled.StubBuildingVisitor.GUESSING_MAPPER + private fun convertNestedClassInternalNameWithSimpleHeuristic(internalName: String): ClassId? { + val splitPoints = SmartList() + for (p in 0 until internalName.length) { + val c = internalName[p] + if (c == '$' && p > 0 && internalName[p - 1] != '/' && p < internalName.length - 1 && internalName[p + 1] != '$') { + splitPoints.add(p) + } + } + + if (splitPoints.isEmpty()) return null + + val substrings = (listOf(-1) + splitPoints).zip(splitPoints + internalName.length).map { (from, to) -> + internalName.substring(from + 1, to) + } + + val outerFqName = FqName(substrings[0].replace('/', '.')) + val packageFqName = outerFqName.parent() + val relativeName = FqName(outerFqName.shortName().asString() + "." + substrings.subList(1, substrings.size).joinToString(".")) + + return ClassId(packageFqName, relativeName, false) + } + + internal fun resolveByInternalName(internalName: String): Result { + val result = resolveClass(mapInternalNameToClassId(internalName)) + if (result.classifier == null && '$' in internalName) { + // Class files generated by some (non-javac) compilers lack the InnerClasses attribute which would make it possible to + // unambiguously determine how to parse a particular internal name found in the class file. For example, see + // https://issues.apache.org/jira/browse/GROOVY-8863. In this case, we're trying to treat all dollar characters in the class + // name as nested class separators, lookup the class with that ClassId, and see if its InnerClasses attribute confirms our + // suspicion that this class was in fact nested. + val heuristicName = convertNestedClassInternalNameWithSimpleHeuristic(internalName) + if (heuristicName != null) { + val heuristicResult = resolveClass(heuristicName) + if (heuristicResult.classifier is BinaryJavaClass) { + val realName = heuristicResult.classifier.context.mapInternalNameToClassId(internalName) + if (heuristicName == realName) { + return heuristicResult + } + } + } + } + + return result + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt index d8d7e16c863..7394597e30c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt @@ -109,8 +109,8 @@ class SimpleKotlinGradleIT : BaseGradleIT() { } @Test - fun testGroovyTraitsWithFields() { - Project("groovyTraitsWithFields").build("build") { + fun testGroovyInterop() { + Project("groovyInterop").build("build") { assertSuccessful() } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/build.gradle similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/build.gradle rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/build.gradle diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/build.gradle similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/build.gradle rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/build.gradle diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/ClassWithReferenceToInner.groovy b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/ClassWithReferenceToInner.groovy new file mode 100644 index 00000000000..08b7fe9bf5d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/ClassWithReferenceToInner.groovy @@ -0,0 +1,9 @@ +public class ClassWithReferenceToInner { + public void f1(Thread.State state) {} + + public void f2(Outer.Nested nested) {} +} + +class Outer { + class Nested {} +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/src/main/groovy/MyTrait.groovy b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/MyTrait.groovy similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/src/main/groovy/MyTrait.groovy rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/MyTrait.groovy diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/src/main/groovy/MyTraitAccessor.groovy b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/MyTraitAccessor.groovy similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/lib/src/main/groovy/MyTraitAccessor.groovy rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/lib/src/main/groovy/MyTraitAccessor.groovy diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/settings.gradle similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/settings.gradle rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/settings.gradle diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/src/main/kotlin/MyKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/src/main/kotlin/MyKotlinClass.kt new file mode 100644 index 00000000000..1ddd25dfba0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyInterop/src/main/kotlin/MyKotlinClass.kt @@ -0,0 +1,6 @@ +fun main(args: Array) { + System.out.println(MyTraitAccessor().myField) + + System.out.println(ClassWithReferenceToInner().f1(null)) + System.out.println(ClassWithReferenceToInner().f2(null)) +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt deleted file mode 100644 index 0f51db3c229..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt +++ /dev/null @@ -1,3 +0,0 @@ -fun main(args: Array) { - System.out.println(MyTraitAccessor().myField) -}