Refactor DecompiledDataFactory

This commit is contained in:
Pavel V. Talanov
2014-02-26 15:09:23 +04:00
parent 65a479e3ef
commit 2499e95d38
3 changed files with 58 additions and 64 deletions
@@ -34,59 +34,64 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumEntry
import org.jetbrains.jet.lang.resolve.DescriptorUtils.isSyntheticClassObject
import org.jetbrains.jet.plugin.libraries.JetDecompiledData.descriptorToKey
public class DecompiledDataFactory(val classFile: VirtualFile, val project: Project, val resolver: ResolverForDecompiler) {
public fun buildDecompiledData(classFile: VirtualFile, project: Project): JetDecompiledData {
return buildDecompiledData(classFile, project, ProjectBasedResolverForDecompiler(project))
}
private val classFqName: FqName
private val classFileHeader: KotlinClassHeader
{
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile)
this.classFqName = kotlinClass.getClassName().getFqNameForClassNameWithoutDollars()
val header = kotlinClass.getClassHeader()
assert(header != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
this.classFileHeader = header!!
public fun buildDecompiledData(classFile: VirtualFile, project: Project, resolver: ResolverForDecompiler): JetDecompiledData {
val kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile)
val classFqName = kotlinClass.getClassName().getFqNameForClassNameWithoutDollars()
val classFileHeader = kotlinClass.getClassHeader()
assert(classFileHeader != null) { "Decompiled data factory shouldn't be called on an unsupported file: " + classFile }
val kind = classFileHeader!!.getKind()
val packageFqName = classFqName.parent()
val (text, renderedDescriptorsToRange) =
if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInPackage(packageFqName)))
}
else if (kind == KotlinClassHeader.Kind.CLASS) {
buildDecompiledText(packageFqName, listOf(resolver.resolveClass(classFqName)).filterNotNull())
}
else {
// TODO: support other header kinds: for trait-impl show the trait, for package fragment - the whole package
throw UnsupportedOperationException("Unknown header kind: " + kind)
}
private val renderedDescriptorsToRange = HashMap<String, TextRange>()
private val builder = StringBuilder()
val jetFile = JetDummyClassFileViewProvider.createJetFile(PsiManager.getInstance(project), classFile, text)
return JetDecompiledData(jetFile, renderedDescriptorsToRange)
}
fun build(): JetDecompiledData {
val packageFqName = classFqName.parent()
appendDecompiledTextAndPackageName(packageFqName)
val kind = classFileHeader.getKind()
if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
for (member in sortDeclarations(resolver.resolveDeclarationsInPackage(packageFqName))) {
if (member !is ClassDescriptor) {
appendDescriptor(member, "")
builder.append("\n")
}
}
}
else
if (kind == KotlinClassHeader.Kind.CLASS) {
val cd = resolver.resolveClass(classFqName)
if (cd != null) {
appendDescriptor(cd, "")
}
}
else {
// TODO: support other header kinds: for trait-impl show the trait, for package fragment - the whole package
throw UnsupportedOperationException("Unknown header kind: " + kind)
}
private val DECOMPILED_COMMENT: String = "/* compiled code */"
public val descriptorRendererForDecompiler: DescriptorRenderer = DescriptorRendererBuilder()
.setWithDefinedIn(false)
.setClassWithPrimaryConstructor(true)
.build()
val jetFile = JetDummyClassFileViewProvider.createJetFile(PsiManager.getInstance(project), classFile, builder.toString())
return JetDecompiledData(jetFile, renderedDescriptorsToRange)
}
private data class DecompiledText(val text: String, val renderedDescriptorsToRange: Map<String, TextRange>)
private fun appendDecompiledTextAndPackageName(packageName: FqName) {
private fun buildDecompiledText(packageFqName: FqName, descriptors: List<DeclarationDescriptor>): DecompiledText {
val builder = StringBuilder()
val renderedDescriptorsToRange = HashMap<String, TextRange>()
fun appendDecompiledTextAndPackageName() {
builder.append("// IntelliJ API Decompiler stub source generated from a class file\n" + "// Implementation of methods is not available")
builder.append("\n\n")
if (!packageName.isRoot()) {
builder.append("package ").append(packageName).append("\n\n")
if (!packageFqName.isRoot()) {
builder.append("package ").append(packageFqName).append("\n\n")
}
}
private fun appendDescriptor(descriptor: DeclarationDescriptor, indent: String) {
fun sortDeclarations(input: Collection<DeclarationDescriptor>): List<DeclarationDescriptor> {
val r = ArrayList<DeclarationDescriptor>(input)
Collections.sort(r, MemberComparator.INSTANCE)
return r
}
fun saveDescriptorToRange(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
renderedDescriptorsToRange[descriptorToKey(descriptor)] = TextRange(startOffset, endOffset)
}
fun appendDescriptor(descriptor: DeclarationDescriptor, indent: String) {
val startOffset = builder.length()
val header = if (isEnumEntry(descriptor))
descriptor.getName().asString()
@@ -150,24 +155,11 @@ public class DecompiledDataFactory(val classFile: VirtualFile, val project: Proj
}
}
private fun saveDescriptorToRange(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
renderedDescriptorsToRange[descriptorToKey(descriptor)] = TextRange(startOffset, endOffset)
appendDecompiledTextAndPackageName()
for (member in sortDeclarations(descriptors)) {
appendDescriptor(member, "")
builder.append("\n")
}
private fun sortDeclarations(input: Collection<DeclarationDescriptor>): List<DeclarationDescriptor> {
val r = ArrayList<DeclarationDescriptor>(input)
Collections.sort(r, MemberComparator.INSTANCE)
return r
}
}
private val DECOMPILED_COMMENT: String = "/* compiled code */"
public val descriptorRendererForDecompiler: DescriptorRenderer = DescriptorRendererBuilder()
.setWithDefinedIn(false)
.setClassWithPrimaryConstructor(true)
.build()
fun createDecompiledData(virtualFile: VirtualFile, project: Project): JetDecompiledData {
return DecompiledDataFactory(virtualFile, project, ProjectBasedResolverForDecompiler(project)).build()
}
return DecompiledText(builder.toString(), renderedDescriptorsToRange)
}
@@ -30,6 +30,8 @@ import org.jetbrains.jet.lang.psi.JetFile;
import java.util.Map;
import static org.jetbrains.jet.plugin.libraries.LibrariesPackage.buildDecompiledData;
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
public class JetDecompiledData {
@@ -57,7 +59,7 @@ public class JetDecompiledData {
JetDecompiledData cachedData = virtualFile.getUserData(USER_DATA_KEY);
//TODO: use cached value that keeps modification tracking for this virtual file
if (cachedData == null || cachedData.getProject() != project) {
virtualFile.putUserData(USER_DATA_KEY, DecompiledDataFactory.createDecompiledData(virtualFile, project));
virtualFile.putUserData(USER_DATA_KEY, buildDecompiledData(virtualFile, project));
}
JetDecompiledData decompiledData = virtualFile.getUserData(USER_DATA_KEY);
assert decompiledData != null;
@@ -84,7 +86,7 @@ public class JetDecompiledData {
//TODO: should use more accurate way to identify descriptors
@NotNull
static String descriptorToKey(@NotNull DeclarationDescriptor descriptor) {
return DecompiledDataFactory.DESCRIPTOR_RENDERER.render(descriptor);
return LibrariesPackage.getDescriptorRendererForDecompiler().render(descriptor);
}
@NotNull
@@ -36,6 +36,6 @@ public class ProjectBasedResolverForDecompiler(project: Project) : ResolverForDe
if (packageFragment == null) {
return Collections.emptyList()
}
return packageFragment.getMemberScope().getAllDescriptors()
return packageFragment.getMemberScope().getAllDescriptors() filter { it !is ClassDescriptor }
}
}