From b052cb8aa9caea567fb4174fa9fec93fa6bed2c1 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 19 Feb 2014 17:19:31 +0400 Subject: [PATCH] Switch to new decompiler API for IDEA Patch from Roman Shevchenko adapted --- idea/src/META-INF/plugin.xml | 4 +- .../libraries/DecompiledNavigationUtils.java | 2 +- .../jet/plugin/libraries/DecompiledUtils.java | 45 ++++++- .../libraries/JetClassFileDecompiler.java | 45 +++++++ .../libraries/JetClassFileViewProvider.java | 64 ++++++++++ .../plugin/libraries/JetClsStubBuilder.java | 46 +++++++ .../JetContentBasedFileSubstitutor.java | 115 ------------------ ...yPackageFragmentClsStubBuilderFactory.java | 58 --------- 8 files changed, 196 insertions(+), 183 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileDecompiler.java create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java create mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetClsStubBuilder.java delete mode 100644 idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java delete mode 100644 idea/src/org/jetbrains/jet/plugin/stubindex/builder/EmptyPackageFragmentClsStubBuilderFactory.java diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index ccdb4e7b10a..613085ada95 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -326,13 +326,11 @@ - + - - diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java index 02e4803615a..643df3b5f68 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledNavigationUtils.java @@ -55,7 +55,7 @@ public final class DecompiledNavigationUtils { DeclarationDescriptor effectiveReferencedDescriptor = getEffectiveReferencedDescriptor(referencedDescriptor); VirtualFile virtualFile = findVirtualFileContainingDescriptor(project, effectiveReferencedDescriptor); - if (virtualFile == null || !DecompiledUtils.isKotlinCompiledFile(project, virtualFile)) return null; + if (virtualFile == null || !DecompiledUtils.isKotlinCompiledFile(virtualFile)) return null; JetDecompiledData data = JetDecompiledData.getDecompiledData(virtualFile, project); JetDeclaration jetDeclaration = data.getDeclarationForDescriptor(effectiveReferencedDescriptor); diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java index fb04feb02cd..b290a64c1f1 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledUtils.java @@ -18,24 +18,57 @@ package org.jetbrains.jet.plugin.libraries; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass; -import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder; +import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; +import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass; import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader; +import org.jetbrains.jet.storage.LockBasedStorageManager; public final class DecompiledUtils { + private static final String PACKAGE_FRAGMENT_SIGNATURE = PackageClassUtils.PACKAGE_CLASS_NAME_SUFFIX + "-"; - public static boolean isKotlinCompiledFile(@NotNull Project project, @NotNull VirtualFile file) { + public static boolean isKotlinCompiledFile(@NotNull VirtualFile file) { if (!StdFileTypes.CLASS.getDefaultExtension().equals(file.getExtension())) { return false; } - //TODO: check index - KotlinJvmBinaryClass kotlinClass = VirtualFileFinder.SERVICE.getInstance(project).createKotlinClass(file); - KotlinClassHeader header = kotlinClass.getClassHeader(); + + return isKotlinInternalClass(file) || checkFile(file); + } + + public static boolean isKotlinInternalClass(@NotNull VirtualFile file) { + // FIXME: not sure if this is a good heuristic + String name = file.getName(); + int pos = name.indexOf('$'); + if (pos > 0) { + name = name.substring(0, pos) + ".class"; + VirtualFile supposedHost = file.getParent().findChild(name); + if (supposedHost != null) { + return checkFile(supposedHost); + } + } + + if (name.contains(PACKAGE_FRAGMENT_SIGNATURE)) { + KotlinClassHeader header = new VirtualFileKotlinClass(LockBasedStorageManager.NO_LOCKS, file).getClassHeader(); + if (header != null && header.getKind() == KotlinClassHeader.Kind.PACKAGE_FRAGMENT) { + return true; + } + } + + return false; + } + + private static boolean checkFile(@NotNull VirtualFile file) { + KotlinClassHeader header = new VirtualFileKotlinClass(LockBasedStorageManager.NO_LOCKS, file).getClassHeader(); return header != null && header.getAnnotationData() != null; } + 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/JetClassFileDecompiler.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileDecompiler.java new file mode 100644 index 00000000000..ae69f559de2 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileDecompiler.java @@ -0,0 +1,45 @@ +/* + * 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.vfs.VirtualFile; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiManager; +import com.intellij.psi.compiled.ClassFileDecompilers; +import com.intellij.psi.compiled.ClsStubBuilder; +import org.jetbrains.annotations.NotNull; + +public class JetClassFileDecompiler extends ClassFileDecompilers.Full { + private final ClsStubBuilder stubBuilder = new JetClsStubBuilder(); + + @Override + public boolean accepts(@NotNull VirtualFile file) { + return DecompiledUtils.isKotlinCompiledFile(file); + } + + @NotNull + @Override + public ClsStubBuilder getStubBuilder() { + return stubBuilder; + } + + @NotNull + @Override + public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, @NotNull PsiManager manager, boolean physical) { + return new JetClassFileViewProvider(manager, file, physical, DecompiledUtils.isKotlinInternalClass(file)); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java new file mode 100644 index 00000000000..508e93ce0ec --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetClassFileViewProvider.java @@ -0,0 +1,64 @@ +/* + * 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/JetClsStubBuilder.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetClsStubBuilder.java new file mode 100644 index 00000000000..848707ab28c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetClsStubBuilder.java @@ -0,0 +1,46 @@ +/* + * 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.vfs.VirtualFile; +import com.intellij.psi.compiled.ClsStubBuilder; +import com.intellij.psi.impl.compiled.ClassFileStubBuilder; +import com.intellij.psi.impl.compiled.ClsFileImpl; +import com.intellij.psi.stubs.PsiFileStub; +import com.intellij.util.cls.ClsFormatException; +import com.intellij.util.indexing.FileContent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class JetClsStubBuilder extends ClsStubBuilder { + @Override + public int getStubVersion() { + return ClassFileStubBuilder.STUB_VERSION + 1; + } + + @Nullable + @Override + public PsiFileStub buildFileStub(@NotNull FileContent content) throws ClsFormatException { + VirtualFile file = content.getFile(); + + if (DecompiledUtils.isKotlinInternalClass(file)) { + return null; + } + + return ClsFileImpl.buildFileStub(file, content.getContent()); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java deleted file mode 100644 index 5cf073e388b..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java +++ /dev/null @@ -1,115 +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.google.common.collect.Maps; -import com.intellij.AppTopics; -import com.intellij.lang.Language; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter; -import com.intellij.openapi.fileTypes.ContentBasedClassFileProcessor; -import com.intellij.openapi.fileTypes.SyntaxHighlighter; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.impl.PsiDocumentManagerBase; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.plugin.highlighter.JetHighlighter; - -import java.util.concurrent.ConcurrentMap; - -public final class JetContentBasedFileSubstitutor implements ContentBasedClassFileProcessor { - private static final JetContentBasedFileSubstitutor instance = new JetContentBasedFileSubstitutor(); - - private static final ConcurrentMap deferredDocumentBinding = Maps.newConcurrentMap(); - - public static JetContentBasedFileSubstitutor getInstance() { - return instance; - } - - private JetContentBasedFileSubstitutor() { - ApplicationManager.getApplication().getMessageBus().connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() { - @Override - public void fileContentLoaded(@NotNull VirtualFile loadedFile, @NotNull Document document) { - processDeferredBindings(loadedFile, document); - } - - @Override - public void fileContentReloaded(VirtualFile loadedFile, @NotNull Document document) { - processDeferredBindings(loadedFile, document); - } - - private void processDeferredBindings(VirtualFile loadedFile, @NotNull Document document) { - JetFile file = deferredDocumentBinding.remove(loadedFile); - if (file != null) { - PsiDocumentManagerBase.cachePsi(document, file); - } - } - }); - } - - @Override - public boolean isApplicable(@Nullable final Project project, @NotNull final VirtualFile file) { - if (project == null) { - return false; - } - - if (DumbService.isDumb(project)) { - DumbService.getInstance(project).runWhenSmart(new Runnable() { - @Override - public void run() { - if (DecompiledUtils.isKotlinCompiledFile(project, file)) { - FileDocumentManager docManager = FileDocumentManager.getInstance(); - docManager.getDocument(file); // force getting document because it can be collected - docManager.reloadFiles(file); - } - } - }); - return false; - } - - return DecompiledUtils.isKotlinCompiledFile(project, file); - } - - @NotNull - @Override - public String obtainFileText(Project project, VirtualFile file) { - if (file != null && DecompiledUtils.isKotlinCompiledFile(project, file)) { - JetDecompiledData data = JetDecompiledData.getDecompiledData(file, project); - deferredDocumentBinding.put(file, data.getFile()); - - return data.getFileText(); - } - return ""; - } - - @Override - public Language obtainLanguageForFile(VirtualFile file) { - return null; - } - - @NotNull - @Override - public SyntaxHighlighter createHighlighter(Project project, VirtualFile vFile) { - return new JetHighlighter(); - } -} - diff --git a/idea/src/org/jetbrains/jet/plugin/stubindex/builder/EmptyPackageFragmentClsStubBuilderFactory.java b/idea/src/org/jetbrains/jet/plugin/stubindex/builder/EmptyPackageFragmentClsStubBuilderFactory.java deleted file mode 100644 index 843e10d88ae..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/stubindex/builder/EmptyPackageFragmentClsStubBuilderFactory.java +++ /dev/null @@ -1,58 +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.stubindex.builder; - -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiJavaFile; -import com.intellij.psi.impl.compiled.ClsStubBuilderFactory; -import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; -import com.intellij.psi.stubs.PsiFileStub; -import com.intellij.util.cls.ClsFormatException; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; -import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass; -import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader; -import org.jetbrains.jet.storage.LockBasedStorageManager; - -/** - * This class is needed to build an empty PSI stub for compiled package fragment classes. This results in these classes not showing up - * in completion, go-to-class, etc. - */ -public class EmptyPackageFragmentClsStubBuilderFactory extends ClsStubBuilderFactory { - - @Nullable - @Override - public PsiFileStub buildFileStub(VirtualFile file, byte[] bytes) throws ClsFormatException { - return new PsiJavaFileStubImpl(null, true); - } - - @Override - public boolean canBeProcessed(VirtualFile file, byte[] bytes) { - if (file.getName().contains(PackageClassUtils.PACKAGE_CLASS_NAME_SUFFIX + "-") && - StdFileTypes.CLASS.getDefaultExtension().equals(file.getExtension())) { - KotlinClassHeader header = new VirtualFileKotlinClass(LockBasedStorageManager.NO_LOCKS, file).getClassHeader(); - return header != null && header.getKind() == KotlinClassHeader.Kind.PACKAGE_PART; - } - return false; - } - - @Override - public boolean isInnerClass(VirtualFile file) { - return false; - } -}