Migrate decompiled light classes factories from IJ
This commit is contained in:
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.analysis.decompiled.light.classes
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.JavaClassFileType
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||||
|
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||||
|
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
|
||||||
|
import com.intellij.util.cls.ClsFormatException
|
||||||
|
import com.intellij.util.containers.ContainerUtil
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
internal class ClsJavaStubByVirtualFileCache {
|
||||||
|
private class CachedJavaStub(val modificationStamp: Long, val javaFileStub: PsiJavaFileStubImpl)
|
||||||
|
|
||||||
|
private val cache = ContainerUtil.createConcurrentWeakKeySoftValueMap<VirtualFile, CachedJavaStub>()
|
||||||
|
|
||||||
|
fun get(classFile: VirtualFile): PsiJavaFileStubImpl? {
|
||||||
|
val cached = cache[classFile]
|
||||||
|
val fileModificationStamp = classFile.modificationStamp
|
||||||
|
if (cached != null && cached.modificationStamp == fileModificationStamp) {
|
||||||
|
return cached.javaFileStub
|
||||||
|
}
|
||||||
|
val stub = createStub(classFile) as PsiJavaFileStubImpl? ?: return null
|
||||||
|
cache[classFile] = CachedJavaStub(fileModificationStamp, stub)
|
||||||
|
return stub
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStub(file: VirtualFile): PsiJavaFileStub? {
|
||||||
|
if (file.fileType !== JavaClassFileType.INSTANCE) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray(false))
|
||||||
|
} catch (e: ClsFormatException) {
|
||||||
|
LOG.warn("Failed to build java cls class for " + file.canonicalPath!!, e)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
LOG.warn("Failed to build java cls class for " + file.canonicalPath!!, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOG = Logger.getInstance(ClsJavaStubByVirtualFileCache::class.java)
|
||||||
|
|
||||||
|
fun getInstance(project: Project): ClsJavaStubByVirtualFileCache =
|
||||||
|
project.getService(ClsJavaStubByVirtualFileCache::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.analysis.decompiled.light.classes
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.ClassFileViewProvider
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.impl.compiled.ClsClassImpl
|
||||||
|
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
|
||||||
|
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtEnumEntry
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.utils.checkWithAttachment
|
||||||
|
|
||||||
|
object DecompiledLightClassesFactory {
|
||||||
|
fun getLightClassForDecompiledClassOrObject(
|
||||||
|
decompiledClassOrObject: KtClassOrObject,
|
||||||
|
project: Project
|
||||||
|
): KtLightClassForDecompiledDeclaration? {
|
||||||
|
if (decompiledClassOrObject is KtEnumEntry) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val containingKtFile = decompiledClassOrObject.containingFile as? KtClsFile ?: return null
|
||||||
|
val rootLightClassForDecompiledFile = createLightClassForDecompiledKotlinFile(containingKtFile, project) ?: return null
|
||||||
|
|
||||||
|
return findCorrespondingLightClass(decompiledClassOrObject, rootLightClassForDecompiledFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findCorrespondingLightClass(
|
||||||
|
decompiledClassOrObject: KtClassOrObject,
|
||||||
|
rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration
|
||||||
|
): KtLightClassForDecompiledDeclaration? {
|
||||||
|
val relativeFqName = getClassRelativeName(decompiledClassOrObject) ?: return null
|
||||||
|
val iterator = relativeFqName.pathSegments().iterator()
|
||||||
|
val base = iterator.next()
|
||||||
|
|
||||||
|
// In case class files have been obfuscated (i.e., SomeClass belongs to a.class file), just ignore them
|
||||||
|
if (rootLightClassForDecompiledFile.name != base.asString()) return null
|
||||||
|
|
||||||
|
var current: KtLightClassForDecompiledDeclaration = rootLightClassForDecompiledFile
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val name = iterator.next()
|
||||||
|
val innerClass = current.findInnerClassByName(name.asString(), false)
|
||||||
|
checkWithAttachment(
|
||||||
|
innerClass != null,
|
||||||
|
{ "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\nFile: " + decompiledClassOrObject.containingKtFile.virtualFile.name },
|
||||||
|
{
|
||||||
|
it.withAttachment("decompiledClassOrObject", decompiledClassOrObject.text)
|
||||||
|
it.withAttachment("fileClass", decompiledClassOrObject.containingFile::class)
|
||||||
|
it.withAttachment("file", decompiledClassOrObject.containingFile.text)
|
||||||
|
it.withAttachment("root", rootLightClassForDecompiledFile.text)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
current = innerClass as KtLightClassForDecompiledDeclaration
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getClassRelativeName(decompiledClassOrObject: KtClassOrObject): FqName? {
|
||||||
|
val name = decompiledClassOrObject.nameAsName ?: return null
|
||||||
|
val parent = PsiTreeUtil.getParentOfType(
|
||||||
|
decompiledClassOrObject,
|
||||||
|
KtClassOrObject::class.java,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
if (parent == null) {
|
||||||
|
assert(decompiledClassOrObject.isTopLevel())
|
||||||
|
return FqName.topLevel(name)
|
||||||
|
}
|
||||||
|
return getClassRelativeName(parent)?.child(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createLightClassForDecompiledKotlinFile(file: KtClsFile, project: Project): KtLightClassForDecompiledDeclaration? {
|
||||||
|
val virtualFile = file.virtualFile ?: return null
|
||||||
|
|
||||||
|
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
|
||||||
|
|
||||||
|
val javaClsClass = createClsJavaClassFromVirtualFile(
|
||||||
|
file, virtualFile,
|
||||||
|
correspondingClassOrObject = classOrObject,
|
||||||
|
project,
|
||||||
|
) ?: return null
|
||||||
|
|
||||||
|
return KtLightClassForDecompiledDeclaration(javaClsClass, javaClsClass.parent, file, classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createClsJavaClassFromVirtualFile(
|
||||||
|
mirrorFile: KtFile,
|
||||||
|
classFile: VirtualFile,
|
||||||
|
correspondingClassOrObject: KtClassOrObject?,
|
||||||
|
project: Project,
|
||||||
|
): ClsClassImpl? {
|
||||||
|
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
|
||||||
|
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
|
||||||
|
val manager = PsiManager.getInstance(mirrorFile.project)
|
||||||
|
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, classFile)) {
|
||||||
|
override fun getNavigationElement(): PsiElement {
|
||||||
|
if (correspondingClassOrObject != null) {
|
||||||
|
return correspondingClassOrObject.navigationElement.containingFile
|
||||||
|
}
|
||||||
|
return super.getNavigationElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStub() = javaFileStub
|
||||||
|
|
||||||
|
override fun getMirror() = mirrorFile
|
||||||
|
|
||||||
|
override fun isPhysical() = false
|
||||||
|
}
|
||||||
|
javaFileStub.psi = fakeFile
|
||||||
|
return fakeFile.classes.single() as ClsClassImpl
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user