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);