From b5c119200fcf4245d0b717562719c8d7231d6c31 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 26 Feb 2014 19:36:59 +0400 Subject: [PATCH] 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 --- .../libraries/DecompiledNavigationUtils.java | 9 +- ...ataFactory.kt => DecompiledTextFactory.kt} | 28 +++-- .../jet/plugin/libraries/DecompiledUtils.java | 5 - .../libraries/JetClassFileViewProvider.java | 64 ----------- .../libraries/JetClassFileViewProvider.kt | 50 +++++++++ .../jet/plugin/libraries/JetClsFile.kt | 51 +++++++++ .../plugin/libraries/JetDecompiledData.java | 101 ------------------ .../DecompiledTextConsistencyTest.kt | 4 +- .../NavigateToDecompiledLibraryTest.java | 15 ++- 9 files changed, 134 insertions(+), 193 deletions(-) rename idea/src/org/jetbrains/jet/plugin/libraries/{DecompiledDataFactory.kt => DecompiledTextFactory.kt} (87%) delete mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.kt create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt delete mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java index 643df3b5f68..721f48d8d43 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java @@ -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; } diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledTextFactory.kt similarity index 87% rename from idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt rename to idea/src/org/jetbrains/jet/plugin/libraries/DecompiledTextFactory.kt index b2851f86bdf..0be7234e195 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledTextFactory.kt @@ -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) private fun buildDecompiledText(packageFqName: FqName, descriptors: List): DecompiledText { diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java index 572cbba470f..210baff072c 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java @@ -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() { } } diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java deleted file mode 100644 index 508e93ce0ec..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java +++ /dev/null @@ -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(); - } - } -} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.kt b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.kt new file mode 100644 index 00000000000..4dc40cb76ae --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.kt @@ -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) + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt b/idea/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt new file mode 100644 index 00000000000..317bb78c71c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetClsFile.kt @@ -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()) + } + + TestOnly + fun getRenderedDescriptorsToRange(): Map { + return provider.decompiledText.renderedDescriptorsToRange + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java deleted file mode 100644 index e1f11c0d92e..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java +++ /dev/null @@ -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 USER_DATA_KEY = new Key("USER_DATA_KEY"); - private static final Object LOCK = new String("decompiled data lock"); - - @NotNull - private final JetFile file; - @NotNull - private final Map renderedDescriptorsToRanges; - - JetDecompiledData(@NotNull JetFile file, @NotNull Map 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 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(); - } -} diff --git a/idea/tests/org/jetbrains/jet/plugin/libraries/DecompiledTextConsistencyTest.kt b/idea/tests/org/jetbrains/jet/plugin/libraries/DecompiledTextConsistencyTest.kt index 5fb94bf2b25..ceacdecf4c1 100644 --- a/idea/tests/org/jetbrains/jet/plugin/libraries/DecompiledTextConsistencyTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/libraries/DecompiledTextConsistencyTest.kt @@ -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")) diff --git a/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java b/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java index 76f6442e68e..e16ce12943f 100644 --- a/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java @@ -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 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 map = getRenderedDescriptorToKotlinPsiMap( + (JetFile) decompiledPsiFile, ((JetClsFile) clsFileForClassFile).getRenderedDescriptorsToRange() + ); String decompiledTextWithMarks = getDecompiledTextWithMarks(map); assertSameLinesWithFile(TEST_DATA_PATH + "/decompiled/" + getTestName(false) + ".kt", decompiledTextWithMarks);