Rewrite DecompiledDataFactory to Kotlin
This commit is contained in:
@@ -48,85 +48,102 @@ public class DescriptorRendererBuilder {
|
||||
public DescriptorRendererBuilder() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setShortNames(boolean shortNames) {
|
||||
this.shortNames = shortNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setWithDefinedIn(boolean withDefinedIn) {
|
||||
this.withDefinedIn = withDefinedIn;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setModifiers(Set<DescriptorRenderer.Modifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setModifiers(DescriptorRenderer.Modifier... modifiers) {
|
||||
return setModifiers(ImmutableSet.copyOf(modifiers));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setStartFromName(boolean startFromName) {
|
||||
this.startFromName = startFromName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setDebugMode(boolean debugMode) {
|
||||
this.debugMode = debugMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setClassWithPrimaryConstructor(boolean classWithPrimaryConstructor) {
|
||||
this.classWithPrimaryConstructor = classWithPrimaryConstructor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setUnitReturnType(boolean unitReturnType) {
|
||||
this.unitReturnType = unitReturnType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setNormalizedVisibilities(boolean normalizedVisibilities) {
|
||||
this.normalizedVisibilities = normalizedVisibilities;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setShowInternalKeyword(boolean showInternalKeyword) {
|
||||
this.showInternalKeyword = showInternalKeyword;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setOverrideRenderingPolicy(@NotNull DescriptorRenderer.OverrideRenderingPolicy overrideRenderingPolicy) {
|
||||
this.overrideRenderingPolicy = overrideRenderingPolicy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setValueParametersHandler(@NotNull DescriptorRenderer.ValueParametersHandler valueParametersHandler) {
|
||||
this.valueParametersHandler = valueParametersHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setTextFormat(@NotNull DescriptorRenderer.TextFormat textFormat) {
|
||||
this.textFormat = textFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setExcludedAnnotationClasses(@NotNull Collection<FqName> excludedAnnotationClasses) {
|
||||
this.excludedAnnotationClasses = excludedAnnotationClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRendererBuilder setPrettyFunctionTypes(boolean prettyFunctionTypes) {
|
||||
this.prettyFunctionTypes = prettyFunctionTypes;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DescriptorRenderer build() {
|
||||
return new DescriptorRendererImpl(shortNames, withDefinedIn, modifiers, startFromName, debugMode, classWithPrimaryConstructor,
|
||||
verbose, unitReturnType, normalizedVisibilities, showInternalKeyword, prettyFunctionTypes,
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.libraries;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.resolve.MemberComparator;
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache;
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.jet.renderer.DescriptorRendererBuilder;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumEntry;
|
||||
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isSyntheticClassObject;
|
||||
import static org.jetbrains.jet.plugin.libraries.JetDecompiledData.descriptorToKey;
|
||||
|
||||
public final class DecompiledDataFactory {
|
||||
private static final String DECOMPILED_COMMENT = "/* compiled code */";
|
||||
public static final DescriptorRenderer DESCRIPTOR_RENDERER =
|
||||
new DescriptorRendererBuilder().setWithDefinedIn(false).setClassWithPrimaryConstructor(true).build();
|
||||
@NotNull
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
@NotNull
|
||||
private final Map<String, TextRange> renderedDescriptorsToRange = new HashMap<String, TextRange>();
|
||||
@NotNull
|
||||
private final ResolverForDecompiler resolver;
|
||||
@NotNull
|
||||
private final KotlinClassHeader classFileHeader;
|
||||
@NotNull
|
||||
private final FqName classFqName;
|
||||
@NotNull
|
||||
private final VirtualFile classFile;
|
||||
@NotNull
|
||||
private final Project project;
|
||||
|
||||
private DecompiledDataFactory(@NotNull VirtualFile classFile, @NotNull Project project, @NotNull ResolverForDecompiler resolver) {
|
||||
this.classFile = classFile;
|
||||
this.project = project;
|
||||
|
||||
KotlinJvmBinaryClass kotlinClass = KotlinBinaryClassCache.getKotlinBinaryClass(classFile);
|
||||
this.classFqName = kotlinClass.getClassName().getFqNameForClassNameWithoutDollars();
|
||||
|
||||
KotlinClassHeader header = kotlinClass.getClassHeader();
|
||||
assert header != null : "Decompiled data factory shouldn't be called on an unsupported file: " + classFile;
|
||||
this.classFileHeader = header;
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static JetDecompiledData createDecompiledData(@NotNull VirtualFile virtualFile, @NotNull Project project) {
|
||||
return new DecompiledDataFactory(virtualFile, project, new ProjectBasedResolverForDecompiler(project)).build();
|
||||
}
|
||||
|
||||
private JetDecompiledData build() {
|
||||
FqName packageFqName = classFqName.parent();
|
||||
appendDecompiledTextAndPackageName(packageFqName);
|
||||
KotlinClassHeader.Kind kind = classFileHeader.getKind();
|
||||
if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
|
||||
for (DeclarationDescriptor member : sortDeclarations(resolver.resolveDeclarationsInPackage(packageFqName))) {
|
||||
if (!(member instanceof ClassDescriptor)) {
|
||||
appendDescriptor(member, "");
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (kind == KotlinClassHeader.Kind.CLASS) {
|
||||
ClassDescriptor 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 new UnsupportedOperationException("Unknown header kind: " + kind);
|
||||
}
|
||||
|
||||
JetFile jetFile = JetDummyClassFileViewProvider.createJetFile(PsiManager.getInstance(project), classFile, builder.toString());
|
||||
return new JetDecompiledData(jetFile, renderedDescriptorsToRange);
|
||||
}
|
||||
|
||||
private void appendDecompiledTextAndPackageName(@NotNull FqName packageName) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DeclarationDescriptor> sortDeclarations(@NotNull Collection<DeclarationDescriptor> input) {
|
||||
ArrayList<DeclarationDescriptor> r = new ArrayList<DeclarationDescriptor>(input);
|
||||
Collections.sort(r, MemberComparator.INSTANCE);
|
||||
return r;
|
||||
}
|
||||
|
||||
private void appendDescriptor(@NotNull DeclarationDescriptor descriptor, String indent) {
|
||||
int startOffset = builder.length();
|
||||
String header = isEnumEntry(descriptor)
|
||||
? descriptor.getName().asString()
|
||||
: DESCRIPTOR_RENDERER.render(descriptor).replace("= ...", "= " + DECOMPILED_COMMENT);
|
||||
builder.append(header);
|
||||
int endOffset = builder.length();
|
||||
|
||||
if (descriptor instanceof FunctionDescriptor || descriptor instanceof PropertyDescriptor) {
|
||||
if (((CallableMemberDescriptor) descriptor).getModality() != Modality.ABSTRACT) {
|
||||
if (descriptor instanceof FunctionDescriptor) {
|
||||
builder.append(" { ").append(DECOMPILED_COMMENT).append(" }");
|
||||
endOffset = builder.length();
|
||||
}
|
||||
else { // descriptor instanceof PropertyDescriptor
|
||||
builder.append(" ").append(DECOMPILED_COMMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof ClassDescriptor && !isEnumEntry(descriptor)) {
|
||||
builder.append(" {\n");
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
|
||||
boolean firstPassed = false;
|
||||
String subindent = indent + " ";
|
||||
ClassDescriptor classObject = classDescriptor.getClassObjectDescriptor();
|
||||
if (classObject != null && !isSyntheticClassObject(classObject)) {
|
||||
firstPassed = true;
|
||||
builder.append(subindent);
|
||||
appendDescriptor(classObject, subindent);
|
||||
}
|
||||
for (DeclarationDescriptor member : sortDeclarations(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
|
||||
if (member.getContainingDeclaration() != descriptor) {
|
||||
continue;
|
||||
}
|
||||
if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstPassed) {
|
||||
builder.append("\n");
|
||||
}
|
||||
else {
|
||||
firstPassed = true;
|
||||
}
|
||||
builder.append(subindent);
|
||||
appendDescriptor(member, subindent);
|
||||
}
|
||||
builder.append(indent).append("}");
|
||||
endOffset = builder.length();
|
||||
}
|
||||
|
||||
builder.append("\n");
|
||||
saveDescriptorToRange(descriptor, startOffset, endOffset);
|
||||
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
ConstructorDescriptor primaryConstructor = ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor();
|
||||
if (primaryConstructor != null) {
|
||||
saveDescriptorToRange(primaryConstructor, startOffset, endOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDescriptorToRange(DeclarationDescriptor descriptor, int startOffset, int endOffset) {
|
||||
renderedDescriptorsToRange.put(descriptorToKey(descriptor), new TextRange(startOffset, endOffset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.libraries
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.jet.lang.descriptors.*
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import org.jetbrains.jet.lang.resolve.MemberComparator
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.renderer.DescriptorRendererBuilder
|
||||
import java.util.*
|
||||
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) {
|
||||
|
||||
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!!
|
||||
}
|
||||
|
||||
private val renderedDescriptorsToRange = HashMap<String, TextRange>()
|
||||
private val builder = StringBuilder()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
val jetFile = JetDummyClassFileViewProvider.createJetFile(PsiManager.getInstance(project), classFile, builder.toString())
|
||||
return JetDecompiledData(jetFile, renderedDescriptorsToRange)
|
||||
}
|
||||
|
||||
private fun appendDecompiledTextAndPackageName(packageName: FqName) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendDescriptor(descriptor: DeclarationDescriptor, indent: String) {
|
||||
val startOffset = builder.length()
|
||||
val header = if (isEnumEntry(descriptor))
|
||||
descriptor.getName().asString()
|
||||
else
|
||||
descriptorRendererForDecompiler.render(descriptor).replace("= ...", "= " + DECOMPILED_COMMENT)
|
||||
builder.append(header)
|
||||
var endOffset = builder.length()
|
||||
|
||||
if (descriptor is FunctionDescriptor || descriptor is PropertyDescriptor) {
|
||||
if ((descriptor as MemberDescriptor).getModality() != Modality.ABSTRACT) {
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
builder.append(" { ").append(DECOMPILED_COMMENT).append(" }")
|
||||
endOffset = builder.length()
|
||||
}
|
||||
else {
|
||||
// descriptor instanceof PropertyDescriptor
|
||||
builder.append(" ").append(DECOMPILED_COMMENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (descriptor is ClassDescriptor && !isEnumEntry(descriptor)) {
|
||||
builder.append(" {\n")
|
||||
var firstPassed = false
|
||||
val subindent = indent + " "
|
||||
val classObject = descriptor.getClassObjectDescriptor()
|
||||
if (classObject != null && !isSyntheticClassObject(classObject)) {
|
||||
firstPassed = true
|
||||
builder.append(subindent)
|
||||
appendDescriptor(classObject, subindent)
|
||||
}
|
||||
for (member in sortDeclarations(descriptor.getDefaultType().getMemberScope().getAllDescriptors())) {
|
||||
if (member.getContainingDeclaration() != descriptor) {
|
||||
continue
|
||||
}
|
||||
if (member is CallableMemberDescriptor && member.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (firstPassed) {
|
||||
builder.append("\n")
|
||||
}
|
||||
else {
|
||||
firstPassed = true
|
||||
}
|
||||
builder.append(subindent)
|
||||
appendDescriptor(member, subindent)
|
||||
}
|
||||
builder.append(indent).append("}")
|
||||
endOffset = builder.length()
|
||||
}
|
||||
|
||||
builder.append("\n")
|
||||
saveDescriptorToRange(descriptor, startOffset, endOffset)
|
||||
|
||||
if (descriptor is ClassDescriptor) {
|
||||
val primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor()
|
||||
if (primaryConstructor != null) {
|
||||
saveDescriptorToRange(primaryConstructor, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveDescriptorToRange(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
|
||||
renderedDescriptorsToRange[descriptorToKey(descriptor)] = TextRange(startOffset, endOffset)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -186,6 +186,7 @@ class JetDummyClassFileViewProvider extends UserDataHolderBase implements FileVi
|
||||
return psi;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetFile createJetFile(PsiManager psiManager, VirtualFile file, String text) {
|
||||
return new JetDummyClassFileViewProvider(psiManager, file, text).getPsi(JetLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user