java: ensure PSI/document compatibility for decompiled .class files

This commit is contained in:
Roman Shevchenko
2014-09-26 18:00:30 +02:00
parent b51df833ae
commit 1b1d58ce33
5 changed files with 45 additions and 19 deletions
@@ -17,8 +17,6 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.BinaryFileDecompiler;
import com.intellij.openapi.project.DefaultProjectFactory;
import com.intellij.openapi.vfs.VirtualFile;
@@ -54,17 +52,6 @@ public class ClassFileDecompiler implements BinaryFileDecompiler {
if (result != null) return result;
}
return decompileText(file);
}
@NotNull
public static CharSequence decompileText(@NotNull VirtualFile file) {
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
if (document != null) {
return document.getImmutableCharSequence();
}
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
if (decompiler instanceof ClassFileDecompilers.Light) {
try {
return ((ClassFileDecompilers.Light)decompiler).getText(file);
@@ -24,8 +24,10 @@ import com.intellij.lang.FileASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DefaultProjectFactory;
@@ -33,6 +35,7 @@ import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
@@ -77,6 +80,8 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
implements PsiJavaFile, PsiFileWithStubSupport, PsiFileEx, Queryable, PsiClassOwnerEx, PsiCompiledFile {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsFileImpl");
private static final Key<Document> CLS_DOCUMENT_LINK_KEY = Key.create("cls.document.link");
/** NOTE: you absolutely MUST NOT hold PsiLock under the mirror lock */
private final Object myMirrorLock = new Object();
private final Object myStubLock = new Object();
@@ -342,7 +347,10 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
}
private TreeElement trySetMirror(VirtualFile file, String fileName, boolean usePlugin) {
CharSequence mirrorText = ClassFileDecompiler.decompileText(file);
final Document document = FileDocumentManager.getInstance().getDocument(file);
assert document != null : file.getUrl();
CharSequence mirrorText = document.getImmutableCharSequence();
PsiFileFactory factory = PsiFileFactory.getInstance(getManager().getProject());
PsiFile mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, mirrorText, false, false);
mirror.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, getLanguageLevel());
@@ -353,6 +361,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
@Override
public void run() {
setMirror(mirrorTreeElement);
putUserData(CLS_DOCUMENT_LINK_KEY, document);
}
});
}
@@ -515,6 +524,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
ClsPackageStatementImpl packageStatement = new ClsPackageStatementImpl(this);
synchronized (myMirrorLock) {
putUserData(CLS_DOCUMENT_LINK_KEY, null);
myMirrorFileElement = null;
myPackageStatement = packageStatement;
}
@@ -17,10 +17,12 @@ package com.intellij.psi;
import com.intellij.JavaTestUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.compiled.ClsFileImpl;
@@ -31,6 +33,7 @@ import com.intellij.testFramework.LightIdeaTestCase;
import com.intellij.testFramework.PlatformTestUtil;
import org.jetbrains.org.objectweb.asm.ClassReader;
import java.io.File;
import java.io.IOException;
public class ClsMirrorBuildingTest extends LightIdeaTestCase {
@@ -107,6 +110,34 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
new ClassReader(file.contentsToByteArray()).accept(visitor, ClassReader.SKIP_FRAMES);
}
public void testDocumentReuse() throws IOException {
File classFile = new File(FileUtil.getTempDirectory(), "ReuseTest.class");
assertTrue(classFile.getPath(), classFile.createNewFile());
VirtualFile vFile = StandardFileSystems.local().findFileByPath(classFile.getPath());
assertNotNull(classFile.getPath(), vFile);
PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(vFile);
assertNotNull(psiFile);
String testDir = JavaTestUtil.getJavaTestDataPath() + "/psi/cls/mirror/";
FileUtil.copy(new File(testDir, "pkg/ReuseTestV1.class"), classFile);
vFile.refresh(false, false);
String text1 = psiFile.getText();
assertTrue(text1, text1.contains("private int f1"));
assertFalse(text1, text1.contains("private int f2"));
Document doc1 = FileDocumentManager.getInstance().getCachedDocument(vFile);
assertNotNull(doc1);
assertSame(doc1, PsiDocumentManager.getInstance(getProject()).getDocument(psiFile));
FileUtil.copy(new File(testDir, "pkg/ReuseTestV2.class"), classFile);
vFile.refresh(false, false);
String text2 = psiFile.getText();
assertTrue(text2, text2.contains("private int f1"));
assertTrue(text2, text2.contains("private int f2"));
Document doc2 = FileDocumentManager.getInstance().getCachedDocument(vFile);
assertNotNull(doc2);
assertSame(doc2, PsiDocumentManager.getInstance(getProject()).getDocument(psiFile));
}
private void doTest() {
doTest(getTestName(false));
}
@@ -117,8 +148,7 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
}
private static void doTest(String clsPath, String txtPath) {
VirtualFileSystem fs = clsPath.contains("!/") ? StandardFileSystems.jar() : StandardFileSystems.local();
VirtualFile file = fs.findFileByPath(clsPath);
VirtualFile file = (clsPath.contains("!/") ? StandardFileSystems.jar() : StandardFileSystems.local()).findFileByPath(clsPath);
assertNotNull(clsPath, file);
String expected;
@@ -126,8 +156,7 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
expected = StringUtil.trimTrailing(PlatformTestUtil.loadFileText(txtPath));
}
catch (IOException e) {
fail(e.getMessage());
return;
throw new RuntimeException(e);
}
assertEquals(expected, ClsFileImpl.decompile(file).toString());