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
This commit is contained in:
+1
-1
@@ -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?,
|
||||
|
||||
+46
-1
@@ -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<Int>()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user