From 65a479e3efdcd550b5f13691448c8c21510d5249 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 25 Feb 2014 21:39:15 +0400 Subject: [PATCH] Rewrite DecompiledDataFactory to Kotlin --- .../renderer/DescriptorRendererBuilder.java | 17 ++ .../libraries/DecompiledDataFactory.java | 184 ------------------ .../plugin/libraries/DecompiledDataFactory.kt | 173 ++++++++++++++++ .../JetDummyClassFileViewProvider.java | 1 + 4 files changed, 191 insertions(+), 184 deletions(-) delete mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt diff --git a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java index 464fe5c0fab..aa97bea98dc 100644 --- a/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java +++ b/core/descriptors/src/org/jetbrains/jet/renderer/DescriptorRendererBuilder.java @@ -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 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 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, diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java deleted file mode 100644 index b55cafd1b8f..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java +++ /dev/null @@ -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 renderedDescriptorsToRange = new HashMap(); - @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 sortDeclarations(@NotNull Collection input) { - ArrayList r = new ArrayList(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)); - } -} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt new file mode 100644 index 00000000000..1731b5606ea --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt @@ -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() + 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): List { + val r = ArrayList(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() +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java index a94d7832e3d..4d79215f1c2 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetDummyClassFileViewProvider.java @@ -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); }