Decompiler: remove project from decompilation API
Remove JetDecompiledData, DecompiledText is enough Move JetDecompiled to upper level, rename to JetClsFile Move caches into JetClsFile and JetClassFileViewProvider Rewrite JetClassFileViewProvider and JetClsFile to Kotlin DecompiledDataFactory -> DecompiledTextFactory
This commit is contained in:
@@ -19,6 +19,8 @@ package org.jetbrains.jet.plugin.libraries;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
@@ -57,8 +59,11 @@ public final class DecompiledNavigationUtils {
|
||||
|
||||
if (virtualFile == null || !DecompiledUtils.isKotlinCompiledFile(virtualFile)) return null;
|
||||
|
||||
JetDecompiledData data = JetDecompiledData.getDecompiledData(virtualFile, project);
|
||||
JetDeclaration jetDeclaration = data.getDeclarationForDescriptor(effectiveReferencedDescriptor);
|
||||
PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
|
||||
if (!(psiFile instanceof JetClsFile)) {
|
||||
return null;
|
||||
}
|
||||
JetDeclaration jetDeclaration = ((JetClsFile) psiFile).getDeclarationForDescriptor(effectiveReferencedDescriptor);
|
||||
if (jetDeclaration != null) {
|
||||
return jetDeclaration;
|
||||
}
|
||||
|
||||
+12
-16
@@ -16,15 +16,11 @@
|
||||
|
||||
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
|
||||
@@ -32,21 +28,19 @@ 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 fun buildDecompiledData(classFile: VirtualFile, project: Project): JetDecompiledData {
|
||||
return buildDecompiledData(classFile, project, DeserializerForDecompiler(classFile))
|
||||
}
|
||||
|
||||
public fun buildDecompiledData(classFile: VirtualFile, project: Project, resolver: ResolverForDecompiler): JetDecompiledData {
|
||||
public fun buildDecompiledText(
|
||||
classFile: VirtualFile,
|
||||
resolver: ResolverForDecompiler = DeserializerForDecompiler(classFile)
|
||||
): DecompiledText {
|
||||
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) {
|
||||
|
||||
return if (kind == KotlinClassHeader.Kind.PACKAGE_FACADE) {
|
||||
buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInPackage(packageFqName)))
|
||||
}
|
||||
else if (kind == KotlinClassHeader.Kind.CLASS) {
|
||||
@@ -56,17 +50,19 @@ public fun buildDecompiledData(classFile: VirtualFile, project: Project, resolve
|
||||
// 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, text)
|
||||
return JetDecompiledData(jetFile, renderedDescriptorsToRange)
|
||||
}
|
||||
|
||||
private val DECOMPILED_COMMENT: String = "/* compiled code */"
|
||||
private val DECOMPILED_COMMENT = "/* compiled code */"
|
||||
public val descriptorRendererForDecompiler: DescriptorRenderer = DescriptorRendererBuilder()
|
||||
.setWithDefinedIn(false)
|
||||
.setClassWithPrimaryConstructor(true)
|
||||
.build()
|
||||
|
||||
//TODO: should use more accurate way to identify descriptors
|
||||
public fun descriptorToKey(descriptor: DeclarationDescriptor): String {
|
||||
return descriptorRendererForDecompiler.render(descriptor)
|
||||
}
|
||||
|
||||
private data class DecompiledText(val text: String, val renderedDescriptorsToRange: Map<String, TextRange>)
|
||||
|
||||
private fun buildDecompiledText(packageFqName: FqName, descriptors: List<DeclarationDescriptor>): DecompiledText {
|
||||
@@ -40,11 +40,6 @@ public final class DecompiledUtils {
|
||||
return header != null && header.getKind() == KotlinClassHeader.Kind.PACKAGE_FRAGMENT;
|
||||
}
|
||||
|
||||
public static CharSequence decompile(@NotNull VirtualFile file) {
|
||||
Project project = ProjectManager.getInstance().getOpenProjects()[0]; // FIXME: get rid of project usage here
|
||||
return JetDecompiledData.getDecompiledData(file, project).getFileText();
|
||||
}
|
||||
|
||||
private DecompiledUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.plugin.JetLanguage;
|
||||
|
||||
public class JetClassFileViewProvider extends SingleRootFileViewProvider {
|
||||
private final boolean isInternal;
|
||||
|
||||
public JetClassFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file, boolean physical, boolean internal) {
|
||||
super(manager, file, physical, JetLanguage.INSTANCE);
|
||||
isInternal = internal;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharSequence getContents() {
|
||||
return isInternal ? "" : DecompiledUtils.decompile(getVirtualFile());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiFile createFile(@NotNull Project project, @NotNull VirtualFile file, @NotNull FileType fileType) {
|
||||
return isInternal ? null : new JetDecompiledFile(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SingleRootFileViewProvider createCopy(@NotNull VirtualFile copy) {
|
||||
return new JetClassFileViewProvider(getManager(), copy, false, isInternal);
|
||||
}
|
||||
|
||||
private static class JetDecompiledFile extends ClsFileImpl {
|
||||
public JetDecompiledFile(FileViewProvider viewProvider) {
|
||||
super(viewProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiFile getDecompiledPsiFile() {
|
||||
return JetDecompiledData.getDecompiledData(getVirtualFile(), getProject()).getFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.fileTypes.FileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.SingleRootFileViewProvider
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.jet.plugin.JetLanguage
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class JetClassFileViewProvider(
|
||||
manager: PsiManager,
|
||||
file: VirtualFile,
|
||||
physical: Boolean,
|
||||
val isInternal: Boolean
|
||||
) : SingleRootFileViewProvider(manager, file, physical, JetLanguage.INSTANCE) {
|
||||
val decompiledText by Delegates.blockingLazy(this) {
|
||||
buildDecompiledText(getVirtualFile())
|
||||
}
|
||||
|
||||
override fun getContents(): CharSequence {
|
||||
return if (isInternal) "" else decompiledText.text
|
||||
}
|
||||
|
||||
override fun createFile(project: Project, file: VirtualFile, fileType: FileType): PsiFile? {
|
||||
return if (isInternal) null else JetClsFile(this)
|
||||
}
|
||||
|
||||
override fun createCopy(copy: VirtualFile): SingleRootFileViewProvider {
|
||||
return JetClassFileViewProvider(getManager(), copy, false, isInternal)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.util.TextRange
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import kotlin.properties.Delegates
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
public class JetClsFile(val provider: JetClassFileViewProvider) : ClsFileImpl(provider) {
|
||||
|
||||
private val decompiledFile by Delegates.blockingLazy(this) {
|
||||
JetDummyClassFileViewProvider.createJetFile(provider.getManager(), getVirtualFile(), provider.decompiledText.text)
|
||||
}
|
||||
|
||||
override fun getDecompiledPsiFile() = decompiledFile
|
||||
|
||||
public fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor): JetDeclaration? {
|
||||
val key = descriptorToKey(descriptor)
|
||||
val range = provider.decompiledText.renderedDescriptorsToRange[key]
|
||||
if (range == null) {
|
||||
return null
|
||||
}
|
||||
return PsiTreeUtil.findElementOfClassAtRange(decompiledFile, range.getStartOffset(), range.getEndOffset(), javaClass<JetDeclaration>())
|
||||
}
|
||||
|
||||
TestOnly
|
||||
fun getRenderedDescriptorsToRange(): Map<String, TextRange> {
|
||||
return provider.decompiledText.renderedDescriptorsToRange
|
||||
}
|
||||
}
|
||||
@@ -1,101 +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.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration;
|
||||
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 {
|
||||
|
||||
private static final Key<JetDecompiledData> USER_DATA_KEY = new Key<JetDecompiledData>("USER_DATA_KEY");
|
||||
private static final Object LOCK = new String("decompiled data lock");
|
||||
|
||||
@NotNull
|
||||
private final JetFile file;
|
||||
@NotNull
|
||||
private final Map<String, TextRange> renderedDescriptorsToRanges;
|
||||
|
||||
JetDecompiledData(@NotNull JetFile file, @NotNull Map<String,TextRange> renderedDescriptorsToRanges) {
|
||||
this.file = file;
|
||||
this.renderedDescriptorsToRanges = renderedDescriptorsToRanges;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getFileText() {
|
||||
return file.getText();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetDecompiledData getDecompiledData(@NotNull VirtualFile virtualFile, @NotNull Project project) {
|
||||
synchronized (LOCK) {
|
||||
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, buildDecompiledData(virtualFile, project));
|
||||
}
|
||||
JetDecompiledData decompiledData = virtualFile.getUserData(USER_DATA_KEY);
|
||||
assert decompiledData != null;
|
||||
return decompiledData;
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@NotNull
|
||||
public Map<String, TextRange> getRenderedDescriptorsToRanges() {
|
||||
return renderedDescriptorsToRanges;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JetDeclaration getDeclarationForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||
String key = descriptorToKey(descriptor);
|
||||
TextRange range = renderedDescriptorsToRanges.get(key);
|
||||
if (range == null) {
|
||||
return null;
|
||||
}
|
||||
return PsiTreeUtil.findElementOfClassAtRange(file, range.getStartOffset(), range.getEndOffset(), JetDeclaration.class);
|
||||
}
|
||||
|
||||
//TODO: should use more accurate way to identify descriptors
|
||||
@NotNull
|
||||
static String descriptorToKey(@NotNull DeclarationDescriptor descriptor) {
|
||||
return LibrariesPackage.getDescriptorRendererForDecompiler().render(descriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JetFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return file.getProject();
|
||||
}
|
||||
}
|
||||
@@ -43,9 +43,9 @@ public class DecompiledTextConsistencyTest : JetLightCodeInsightFixtureTestCase(
|
||||
val kotlinPackageClass = myFixture!!.getJavaFacade()!!.findClass(packageClassFqName.asString())!!
|
||||
val kotlinPackageFile = kotlinPackageClass.getContainingFile()!!.getVirtualFile()!!
|
||||
val project = getProject()!!
|
||||
val projectBasedText = buildDecompiledData(kotlinPackageFile, project, ProjectBasedResolverForDecompiler(project)).getFileText()
|
||||
val projectBasedText = buildDecompiledText(kotlinPackageFile, ProjectBasedResolverForDecompiler(project)).text
|
||||
val deserializerForDecompiler = DeserializerForDecompiler(kotlinPackageFile.getParent()!!, STANDARD_LIBRARY_FQNAME)
|
||||
val deserializedText = buildDecompiledData(kotlinPackageFile, project, deserializerForDecompiler).getFileText()
|
||||
val deserializedText = buildDecompiledText(kotlinPackageFile, deserializerForDecompiler).text
|
||||
Assert.assertEquals(projectBasedText, deserializedText)
|
||||
// sanity checks
|
||||
Assert.assertTrue(projectBasedText.contains("linkedListOf"))
|
||||
|
||||
@@ -27,6 +27,9 @@ import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.compiled.ClsFileImpl;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -86,9 +89,15 @@ public class NavigateToDecompiledLibraryTest extends AbstractNavigateToLibraryTe
|
||||
|
||||
private void doTest() {
|
||||
classFile = getClassFile(PACKAGE, getTestName(false), myModule);
|
||||
JetDecompiledData decompiledData = JetDecompiledData.getDecompiledData(classFile, getProject());
|
||||
Map<String, JetDeclaration> map = getRenderedDescriptorToKotlinPsiMap(decompiledData.getFile(),
|
||||
decompiledData.getRenderedDescriptorsToRanges());
|
||||
PsiFile clsFileForClassFile = PsiManager.getInstance(getProject()).findFile(classFile);
|
||||
assertNotNull(clsFileForClassFile);
|
||||
assertTrue("Expecting kotlin class file, was: " + clsFileForClassFile.getClass(), clsFileForClassFile instanceof JetClsFile);
|
||||
PsiFile decompiledPsiFile = ((ClsFileImpl) clsFileForClassFile).getDecompiledPsiFile();
|
||||
assertNotNull(decompiledPsiFile);
|
||||
assertTrue("Expecting decompiled Kotlin file, was: " + decompiledPsiFile.getClass(), decompiledPsiFile instanceof JetFile);
|
||||
Map<String, JetDeclaration> map = getRenderedDescriptorToKotlinPsiMap(
|
||||
(JetFile) decompiledPsiFile, ((JetClsFile) clsFileForClassFile).getRenderedDescriptorsToRange()
|
||||
);
|
||||
String decompiledTextWithMarks = getDecompiledTextWithMarks(map);
|
||||
|
||||
assertSameLinesWithFile(TEST_DATA_PATH + "/decompiled/" + getTestName(false) + ".kt", decompiledTextWithMarks);
|
||||
|
||||
Reference in New Issue
Block a user