New decompiler API

This commit is contained in:
Roman Shevchenko
2014-02-10 20:22:20 +01:00
parent a281951184
commit f6c6bec736
17 changed files with 383 additions and 278 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -22,30 +22,34 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.BinaryFileDecompiler;
import com.intellij.openapi.fileTypes.ContentBasedFileSubstitutor;
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 com.intellij.psi.PsiManager;
import com.intellij.psi.compiled.ClassFileDecompilers;
import org.jetbrains.annotations.NotNull;
public class ClassFileDecompiler implements BinaryFileDecompiler {
@Override
@NotNull
public CharSequence decompile(final VirtualFile file) {
assert file.getFileType() == StdFileTypes.CLASS;
public CharSequence decompile(@NotNull VirtualFile file) {
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
if (decompiler instanceof ClassFileDecompilers.Full) {
PsiManager manager = PsiManager.getInstance(ProjectManager.getInstance().getDefaultProject());
return ((ClassFileDecompilers.Full)decompiler).createFileViewProvider(file, manager, true).getContents();
}
final Project[] projects = ProjectManager.getInstance().getOpenProjects();
if (projects.length == 0) return "";
final Project project = projects[0];
final ContentBasedFileSubstitutor[] processors = Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME);
for (ContentBasedFileSubstitutor processor : processors) {
if (processor.isApplicable(project, file)) {
return processor.obtainFileText(project, file);
Project[] projects = ProjectManager.getInstance().getOpenProjects();
if (projects.length > 0) {
Project project = projects[0];
//noinspection deprecation
for (ContentBasedFileSubstitutor processor : Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME)) {
if (processor.isApplicable(project, file)) {
return processor.obtainFileText(project, file);
}
}
}
return ClsFileImpl.decompile(PsiManager.getInstance(project), file);
return ClsFileImpl.decompile(file);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -17,21 +17,11 @@ package com.intellij.psi;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Evgeny Gerashchenko
* @since 3/20/12
*/
/** @deprecated API no longer supported, use {@link com.intellij.psi.compiled.ClassFileDecompilers} instead (to remove in IDEA 14) */
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
public interface ClsFileDecompiledPsiFileProvider {
ExtensionPointName<ClsFileDecompiledPsiFileProvider> EP_NAME = ExtensionPointName.create("com.intellij.psi.clsDecompiledFileProvider");
/**
* Returns decompiled PSI associated with this classfile
*
* @param clsFile instance of ClsFile
* @return decompiled PSI file
*/
@Nullable
PsiFile getDecompiledPsiFile(@NotNull PsiJavaFile clsFile);
}
@@ -0,0 +1,96 @@
/*
* Copyright 2000-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 com.intellij.psi.compiled;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An API to extend default IDEA .class file decompiler and handle files compiled from sources other than Java.
*
* @since 134.1050
*/
public class ClassFileDecompilers {
/**
* Actual implementations should extend either {@link Light} or {@link Full} classes -
* those that don't are silently ignored.
*/
public interface Decompiler {
boolean accepts(@NotNull VirtualFile file);
}
/**
* <p>"Light" decompilers are intended for augmenting file text constructed by standard IDEA decompiler
* without changing it's structure - i.e. providing additional information in comments,
* or replacing standard "compiled code" method body comment with something more meaningful.</p>
*
* <p>Plugins registering extension of this type normally should accept all files and use {@code order="last"}
* attribute to avoid interfering with other decompilers.</p>
*/
public abstract static class Light implements Decompiler {
@NotNull
public abstract CharSequence getText(@NotNull VirtualFile file);
}
/**
* <p>"Full" decompilers are designed to provide extended support for languages significantly different from Java.
* Extensions of this type should take care of building file stubs and properly indexing them -
* in return they have an ability to represent decompiled file in a way natural for original language.</p>
*/
public abstract static class Full implements Decompiler {
@NotNull
public abstract ClsStubBuilder getStubBuilder();
/**
* <h5>Notes for implementers</h5>
*
* <p>1. Return a correct language from {@link FileViewProvider#getBaseLanguage()}.</p>
*
* <p>2. This method is called for both PSI file construction and obtaining document text.
* In the latter case the PsiManager is based on default project, and the only method called
* on a resulting view provider is {@link FileViewProvider#getContents()}.</p>
*
* <p>3. A language compiler may produce auxiliary .class files which should be handled as part of their parent classes.
* A standard practice is to hide such files by returning {@code null} from
* {@link FileViewProvider#getPsi(com.intellij.lang.Language)}.</p>
*/
@NotNull
public abstract FileViewProvider createFileViewProvider(@NotNull VirtualFile file, @NotNull PsiManager manager, boolean physical);
}
public static ExtensionPointName<Decompiler> EP_NAME = ExtensionPointName.create("com.intellij.psi.classFileDecompiler");
private ClassFileDecompilers() { }
@Nullable
public static Decompiler find(@NotNull VirtualFile file) {
for (Decompiler decompiler : EP_NAME.getExtensions()) {
if ((decompiler instanceof Light || decompiler instanceof Full) && decompiler.accepts(file)) {
return decompiler;
}
}
return null;
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-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 com.intellij.psi.compiled;
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 abstract class ClsStubBuilder {
/**
* Non-zero positive number expected.
*/
public abstract int getStubVersion();
/**
* May return {@code null} for inner or synthetic classes - i.e. those indexed as a part of their parent .class file.
*/
@Nullable
public abstract PsiFileStub<?> buildFileStub(@NotNull FileContent fileContent) throws ClsFormatException;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -39,7 +39,6 @@ import com.intellij.psi.impl.LanguageConstantExpressionEvaluator;
import com.intellij.psi.impl.PsiExpressionEvaluator;
import com.intellij.psi.impl.compiled.ClassFileStubBuilder;
import com.intellij.psi.impl.compiled.ClsStubBuilderFactory;
import com.intellij.psi.impl.compiled.DefaultClsStubBuilderFactory;
import com.intellij.psi.impl.file.PsiPackageImplementationHelper;
import com.intellij.psi.impl.source.tree.CoreJavaASTFactory;
import com.intellij.psi.impl.source.tree.PlainTextASTFactory;
@@ -63,7 +62,7 @@ public class JavaCoreApplicationEnvironment extends CoreApplicationEnvironment {
addExplicitExtension(FileTypeFileViewProviders.INSTANCE, JavaClassFileType.INSTANCE, new ClassFileViewProviderFactory());
addExplicitExtension(BinaryFileStubBuilders.INSTANCE, JavaClassFileType.INSTANCE, new ClassFileStubBuilder());
addExplicitExtension(LanguageASTFactory.INSTANCE, JavaLanguage.INSTANCE, new CoreJavaASTFactory());
addExplicitExtension(LanguageParserDefinitions.INSTANCE, JavaLanguage.INSTANCE, new JavaParserDefinition());
addExplicitExtension(LanguageConstantExpressionEvaluator.INSTANCE, JavaLanguage.INSTANCE, new PsiExpressionEvaluator());
@@ -71,7 +70,6 @@ public class JavaCoreApplicationEnvironment extends CoreApplicationEnvironment {
registerExtensionPoint(Extensions.getRootArea(), ClsStubBuilderFactory.EP_NAME, ClsStubBuilderFactory.class);
registerExtensionPoint(Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class);
registerExtensionPoint(Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class);
addExtension(ClsStubBuilderFactory.EP_NAME, new DefaultClsStubBuilderFactory());
myApplication.registerService(PsiPackageImplementationHelper.class, new CorePsiPackageImplementationHelper());
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-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.
@@ -45,7 +45,7 @@ public class ClassFileViewProvider extends SingleRootFileViewProvider {
// skip inners & anonymous
if (isInnerClass(vFile)) return null;
return new ClsFileImpl(PsiManager.getInstance(project), this);
return new ClsFileImpl(this);
}
public static boolean isInnerClass(VirtualFile vFile) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -13,24 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.psi;
import com.intellij.lang.Language;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.ContentBasedFileSubstitutor;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.compiled.ClassFileDecompilers;
import org.jetbrains.annotations.NotNull;
public class ClassFileViewProviderFactory implements FileViewProviderFactory{
import static com.intellij.psi.compiled.ClassFileDecompilers.Full;
/**
* @author max
*/
public class ClassFileViewProviderFactory implements FileViewProviderFactory {
@Override
public FileViewProvider createFileViewProvider(@NotNull final VirtualFile file, final Language language, @NotNull final PsiManager manager, final boolean physical) {
// Define language for compiled file
final ContentBasedFileSubstitutor[] processors = Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME);
for (ContentBasedFileSubstitutor processor : processors) {
public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean physical) {
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
if (decompiler instanceof Full) {
return ((Full)decompiler).createFileViewProvider(file, manager, physical);
}
for (ContentBasedFileSubstitutor processor : Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME)) {
Language lang = processor.obtainLanguageForFile(file);
if (lang != null) {
FileViewProviderFactory factory = LanguageFileViewProviders.INSTANCE.forLanguage(language);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-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.
@@ -19,13 +19,20 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.stubs.BinaryFileStubBuilder;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.cls.ClsFormatException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.intellij.psi.compiled.ClassFileDecompilers.Full;
/**
* @author max
@@ -33,56 +40,71 @@ import java.util.*;
public class ClassFileStubBuilder implements BinaryFileStubBuilder {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClassFileStubBuilder");
public static final int STUB_VERSION = 8;
public static final int STUB_VERSION = 9;
@Override
public boolean acceptsFile(final VirtualFile file) {
final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME);
for (ClsStubBuilderFactory factory : factories) {
if (!factory.isInnerClass(file)) {
return true;
}
}
return false;
public boolean acceptsFile(@NotNull VirtualFile file) {
return true;
}
@SuppressWarnings("deprecation")
@Override
public StubElement buildStubTree(FileContent fileContent) {
public StubElement buildStubTree(@NotNull FileContent fileContent) {
try {
VirtualFile file = fileContent.getFile();
Project project = fileContent.getProject();
byte[] content = fileContent.getContent();
final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME);
for (ClsStubBuilderFactory factory : factories) {
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
if (decompiler instanceof Full) {
return ((Full)decompiler).getStubBuilder().buildFileStub(fileContent);
}
Project project = fileContent.getProject();
for (ClsStubBuilderFactory factory : Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME)) {
if (!factory.isInnerClass(file) && factory.canBeProcessed(file, content)) {
PsiFileStub stub = factory.buildFileStub(file, content, project);
if (stub != null) return stub;
}
}
if (!fileContent.getFileName().contains("$")) {
PsiFileStub<?> stub = ClsFileImpl.buildFileStub(file, content);
if (stub == null && !fileContent.getFileName().contains("$")) {
LOG.info("No stub built for file " + fileContent);
}
return null;
return stub;
}
catch (ClsFormatException e) {
LOG.info(e);
return null;
}
}
private static final Comparator<Object> CLASS_NAME_COMPARATOR = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return o1.getClass().getName().compareTo(o2.getClass().getName());
}
};
@SuppressWarnings("deprecation")
@Override
public int getStubVersion() {
int version = STUB_VERSION;
List<ClsStubBuilderFactory> factories = new ArrayList<ClsStubBuilderFactory>(Arrays.asList(
Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME)));
Collections.sort(factories, new Comparator<ClsStubBuilderFactory>() { // stable order in copy
@Override
public int compare(ClsStubBuilderFactory o1, ClsStubBuilderFactory o2) {
return o1.getClass().getName().compareTo(o2.getClass().getName());
List<ClassFileDecompilers.Decompiler> decompilers = ContainerUtil.newArrayList(ClassFileDecompilers.EP_NAME.getExtensions());
Collections.sort(decompilers, CLASS_NAME_COMPARATOR);
for (ClassFileDecompilers.Decompiler decompiler : decompilers) {
if (decompiler instanceof Full) {
version = version * 31 + ((Full)decompiler).getStubBuilder().getStubVersion() + decompiler.getClass().getName().hashCode();
}
});
for(ClsStubBuilderFactory factory:factories) {
}
List<ClsStubBuilderFactory> factories = ContainerUtil.newArrayList(Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME));
Collections.sort(factories, CLASS_NAME_COMPARATOR);
for (ClsStubBuilderFactory factory : factories) {
version = version * 31 + factory.getStubVersion() + factory.getClass().getName().hashCode();
}
return version;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -20,10 +20,8 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Evgeny Gerashchenko
* @since 3/2/12
*/
/** @deprecated API no longer supported, use {@link com.intellij.psi.compiled.ClassFileDecompilers} instead (to remove in IDEA 14) */
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
public interface ClsCustomNavigationPolicy {
ExtensionPointName<ClsCustomNavigationPolicy> EP_NAME = ExtensionPointName.create("com.intellij.psi.clsCustomNavigationPolicy");
@@ -27,16 +27,19 @@ import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.progress.NonCancelableSection;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.project.DefaultProjectFactory;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.compiled.ClassFileDecompilers;
import com.intellij.psi.impl.JavaPsiImplementationHelper;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
@@ -52,10 +55,13 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.cls.ClsFormatException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -71,30 +77,42 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
private final Object myMirrorLock = new Object();
private final Object myStubLock = new Object();
private final PsiManager myManager;
private final boolean myIsForDecompiling;
private final FileViewProvider myViewProvider;
private final boolean myIsForDecompiling;
private volatile SoftReference<StubTree> myStub;
private volatile TreeElement myMirrorFileElement;
private volatile ClsPackageStatementImpl myPackageStatement = null;
private boolean myIsPhysical = true;
private ClsFileImpl(@NotNull PsiManager manager, @NotNull FileViewProvider viewProvider, boolean forDecompiling) {
//noinspection ConstantConditions
super(null);
myManager = manager;
myIsForDecompiling = forDecompiling;
myViewProvider = viewProvider;
JavaElementType.CLASS.getIndex(); // initialize Java stubs
public ClsFileImpl(@NotNull FileViewProvider viewProvider) {
this(viewProvider, null);
}
public ClsFileImpl(PsiManager manager, FileViewProvider viewProvider) {
this(manager, viewProvider, false);
/** @deprecated use {@link #ClsFileImpl(FileViewProvider)} (to remove in IDEA 14) */
@SuppressWarnings("unused")
public ClsFileImpl(@NotNull PsiManager manager, @NotNull FileViewProvider viewProvider) {
this(viewProvider, null);
}
private ClsFileImpl(@NotNull FileViewProvider viewProvider, @Nullable PsiClassHolderFileStub<?> stub) {
//noinspection ConstantConditions
super(null);
myViewProvider = viewProvider;
if (stub != null) {
myStub = new SoftReference<StubTree>(new StubTree(stub));
myIsForDecompiling = true;
}
else {
myIsForDecompiling = false;
}
JavaElementType.CLASS.getIndex(); // initialize Java stubs
}
@Override
public PsiManager getManager() {
return myManager;
return myViewProvider.getManager();
}
@Override
@@ -267,6 +285,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
setMirrors(getClasses(), mirrorFile.getClasses());
}
@SuppressWarnings("deprecation")
@Override
@NotNull
public PsiElement getNavigationElement() {
@@ -298,7 +317,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
mirrorTreeElement = myMirrorFileElement;
if (mirrorTreeElement == null) {
VirtualFile file = getVirtualFile();
String mirrorText = decompile(getManager(), file);
CharSequence mirrorText = decompile(file, getStub());
String ext = JavaFileType.INSTANCE.getDefaultExtension();
PsiClass[] classes = getClasses();
@@ -329,13 +348,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
@Override
public PsiFile getDecompiledPsiFile() {
for (ClsFileDecompiledPsiFileProvider provider : Extensions.getExtensions(ClsFileDecompiledPsiFileProvider.EP_NAME)) {
PsiFile decompiledPsiFile = provider.getDecompiledPsiFile(this);
if (decompiledPsiFile != null) {
return decompiledPsiFile;
}
}
return (PsiFile) getMirror();
return (PsiFile)getMirror();
}
@Override
@@ -385,26 +398,6 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
public void subtreeChanged() {
}
public static String decompile(PsiManager manager, VirtualFile file) {
ClsFileImpl psiFile = null;
final FileViewProvider provider = ((PsiManagerEx)manager).getFileManager().findViewProvider(file);
if (provider != null) {
final PsiFile psi = provider.getPsi(provider.getBaseLanguage());
if (psi instanceof ClsFileImpl) {
psiFile = (ClsFileImpl)psi;
}
}
if (psiFile == null) {
psiFile = new ClsFileImpl(manager, new ClassFileViewProvider(manager, file), true);
}
final StringBuilder buffer = new StringBuilder();
psiFile.appendMirrorText(0, buffer);
return buffer.toString();
}
@Override
public PsiElement getContext() {
return FileContextUtil.getFileContext(this);
@@ -412,7 +405,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
@Override
@NotNull
public PsiClassHolderFileStub getStub() {
public PsiClassHolderFileStub<?> getStub() {
return (PsiClassHolderFileStub)getStubTree().getRoot();
}
@@ -437,8 +430,8 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
stubTree = newStubTree;
//noinspection unchecked
((PsiFileStubImpl)stubTree.getRoot()).setPsi(this);
@SuppressWarnings("unchecked") PsiFileStubImpl<PsiFile> fileStub = (PsiFileStubImpl)stubTree.getRoot();
fileStub.setPsi(this);
myStub = new SoftReference<StubTree>(stubTree);
}
@@ -500,4 +493,92 @@ public class ClsFileImpl extends ClsRepositoryPsiElement<PsiClassHolderFileStub>
public void setPhysical(boolean isPhysical) {
myIsPhysical = isPhysical;
}
// default decompiler implementation
@NotNull
public static CharSequence decompile(@NotNull VirtualFile file) {
ClassFileDecompilers.Decompiler decompiler = ClassFileDecompilers.find(file);
if (decompiler instanceof ClassFileDecompilers.Light) {
return ((ClassFileDecompilers.Light)decompiler).getText(file);
}
PsiJavaFileStub stub = null;
try {
stub = buildFileStub(file, file.contentsToByteArray());
}
catch (Exception e) {
LOG.warn(e);
}
return stub == null ? "" : decompile(file, stub);
}
@NotNull
private static CharSequence decompile(@NotNull VirtualFile file, @NotNull PsiClassHolderFileStub<?> stub) {
PsiManager manager = PsiManager.getInstance(DefaultProjectFactory.getInstance().getDefaultProject());
ClsFileImpl psi = new ClsFileImpl(new ClassFileViewProvider(manager, file), stub);
if (stub.getPsi() == null) {
((PsiJavaFileStubImpl)stub).setPsi(psi);
}
StringBuilder buffer = new StringBuilder();
psi.appendMirrorText(0, buffer);
return buffer;
}
@Nullable
public static PsiJavaFileStub buildFileStub(@NotNull VirtualFile vFile, @NotNull byte[] bytes) throws ClsFormatException {
if (ClassFileViewProvider.isInnerClass(vFile)) {
return null;
}
try {
PsiJavaFileStubImpl file = new PsiJavaFileStubImpl("do.not.know.yet", true);
StubBuildingVisitor<VirtualFile> visitor = new StubBuildingVisitor<VirtualFile>(vFile, STRATEGY, file, 0, vFile.getNameWithoutExtension());
try {
new ClassReader(bytes).accept(visitor, ClassReader.SKIP_FRAMES);
}
catch (OutOfOrderInnerClassException e) {
return null;
}
PsiClassStub<?> result = visitor.getResult();
if (result == null) return null;
file.setPackageName(getPackageName(result));
return file;
}
catch (Exception e) {
throw new ClsFormatException(vFile.getPath() + ": " + e.getMessage());
}
}
private static final InnerClassSourceStrategy<VirtualFile> STRATEGY = new InnerClassSourceStrategy<VirtualFile>() {
@Nullable
@Override
public VirtualFile findInnerClass(String innerName, VirtualFile outerClass) {
String baseName = outerClass.getNameWithoutExtension();
VirtualFile dir = outerClass.getParent();
assert dir != null : outerClass;
return dir.findChild(baseName + "$" + innerName + ".class");
}
@Nullable
@Override
public ClassReader readerForInnerClass(VirtualFile innerClass) {
try {
return new ClassReader(innerClass.contentsToByteArray());
}
catch (IOException e) {
return null;
}
}
};
private static String getPackageName(@NotNull PsiClassStub<?> result) {
String fqn = result.getQualifiedName();
String shortName = result.getName();
return fqn == null || Comparing.equal(shortName, fqn) ? "" : fqn.substring(0, fqn.lastIndexOf('.'));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -21,32 +21,23 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.util.cls.ClsFormatException;
import org.jetbrains.annotations.Nullable;
/**
* @author ilyas
*/
/** @deprecated API no longer supported, use {@link com.intellij.psi.compiled.ClassFileDecompilers} instead (to remove in IDEA 14) */
@SuppressWarnings({"deprecation", "unused"})
public abstract class ClsStubBuilderFactory<T extends PsiFile> {
public static final ExtensionPointName<ClsStubBuilderFactory> EP_NAME = ExtensionPointName.create("com.intellij.clsStubBuilderFactory");
@Nullable
public abstract PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes) throws ClsFormatException;
public abstract PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes) throws ClsFormatException;
@Nullable
public PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes, Project project) throws ClsFormatException {
public PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes, Project project) throws ClsFormatException {
return buildFileStub(file, bytes);
}
public abstract boolean canBeProcessed(final VirtualFile file, byte[] bytes);
/**
* Should be fast, because of processing file only according to the name.
* It can be inconsistent with 'canBeProcessed' method.
* @param file classFile
* @return false in case if it's not inner class
*/
public abstract boolean isInnerClass(final VirtualFile file);
public int getStubVersion() { return 1; }
public int getStubVersion() {
return 1;
}
}
@@ -1,109 +0,0 @@
/*
* Copyright 2000-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 com.intellij.psi.impl.compiled;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.ClassFileViewProvider;
import com.intellij.psi.PsiClass;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
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.asm4.ClassReader;
import java.io.IOException;
/**
* @author max
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class DefaultClsStubBuilderFactory extends ClsStubBuilderFactory {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.DefaultClsStubBuilderFactory");
@Override
public PsiFileStub buildFileStub(VirtualFile vFile, byte[] bytes) throws ClsFormatException {
final PsiJavaFileStubImpl file = new PsiJavaFileStubImpl("do.not.know.yet", true);
try {
final ClassReader reader = new ClassReader(bytes);
final StubBuildingVisitor<VirtualFile> classVisitor =
new StubBuildingVisitor<VirtualFile>(vFile, VirtualFileInnerClassStrategy.INSTANCE, file, 0, vFile.getNameWithoutExtension());
try {
reader.accept(classVisitor, ClassReader.SKIP_FRAMES);
}
catch (OutOfOrderInnerClassException e) {
return null;
}
@SuppressWarnings("unchecked") final PsiClassStub<PsiClass> result = (PsiClassStub<PsiClass>)classVisitor.getResult();
if (result == null) return null;
file.setPackageName(getPackageName(result));
return file;
}
catch (Exception e) {
LOG.debug(vFile.getPath(), e);
throw new ClsFormatException();
}
}
@Override
public boolean canBeProcessed(VirtualFile file, byte[] bytes) {
return true;
}
@Override
public boolean isInnerClass(VirtualFile file) {
return ClassFileViewProvider.isInnerClass(file);
}
private static class VirtualFileInnerClassStrategy implements InnerClassSourceStrategy<VirtualFile> {
public static VirtualFileInnerClassStrategy INSTANCE = new VirtualFileInnerClassStrategy();
@Nullable
@Override
public VirtualFile findInnerClass(String innerName, VirtualFile outerClass) {
final String baseName = outerClass.getNameWithoutExtension();
final VirtualFile dir = outerClass.getParent();
assert dir != null;
return dir.findChild(baseName + "$" + innerName + ".class");
}
@Nullable
@Override
public ClassReader readerForInnerClass(VirtualFile innerClass) {
try {
return new ClassReader(innerClass.contentsToByteArray());
}
catch (IOException e) {
return null;
}
}
}
private static String getPackageName(final PsiClassStub<PsiClass> result) {
final String fqn = result.getQualifiedName();
final String shortName = result.getName();
if (fqn == null || Comparing.equal(shortName, fqn)) {
return "";
}
return fqn.substring(0, fqn.lastIndexOf('.'));
}
}
@@ -1,3 +1,18 @@
/*
* Copyright 2000-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 com.intellij.psi;
import com.intellij.JavaTestUtil;
@@ -6,7 +21,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.compiled.DefaultClsStubBuilderFactory;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubBase;
import com.intellij.testFramework.LightIdeaTestCase;
@@ -81,7 +96,7 @@ public class ClsBuilderTest extends LightIdeaTestCase {
}
private static void doTest(VirtualFile vFile, String goldFile) throws ClsFormatException, IOException {
final PsiFileStub stub = (new DefaultClsStubBuilderFactory()).buildFileStub(vFile, vFile.contentsToByteArray());
final PsiFileStub stub = ClsFileImpl.buildFileStub(vFile, vFile.contentsToByteArray());
assert stub != null : vFile;
final String butWas = ((StubBase)stub).printTree();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-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.
@@ -22,23 +22,16 @@ import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** @deprecated use com.intellij.psi.compiled.ClassFileDecompilers or com.intellij.psi.LanguageSubstitutors API (to remove in IDEA 14) */
@SuppressWarnings("deprecation")
public interface ContentBasedFileSubstitutor {
ExtensionPointName<ContentBasedFileSubstitutor> EP_NAME = ExtensionPointName.create("com.intellij.contentBasedClassFileProcessor");
/**
* Checks whether appropriate specific activity is available on given file
*/
boolean isApplicable(Project project, VirtualFile vFile);
/**
* @return specific text representation of compiled classfile
*/
@NotNull
String obtainFileText(Project project, VirtualFile file);
/**
* @return language for compiled classfile
*/
@Nullable
Language obtainLanguageForFile(VirtualFile file);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -34,8 +34,7 @@ import org.jetbrains.annotations.Nullable;
public class StubTreeBuilder {
private static final Key<Stub> stubElementKey = Key.create("stub.tree.for.file.content");
private StubTreeBuilder() {
}
private StubTreeBuilder() { }
@Nullable
public static Stub buildStubTree(final FileContent inputData) {
@@ -53,7 +52,7 @@ public class StubTreeBuilder {
if (builder != null) {
data = builder.buildStubTree(inputData);
}
if (data == null && !fileType.isBinary()) {
else {
final LanguageFileType languageFileType = (LanguageFileType)fileType;
Language l = languageFileType.getLanguage();
final IFileElementType type = LanguageParserDefinitions.INSTANCE.forLanguage(l).getFileNodeType();
@@ -74,7 +73,7 @@ public class StubTreeBuilder {
}
psi = psi.getViewProvider().getStubBindingRoot();
psi.putUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY, contentAsText);
// if we load AST, it should be easily gc-able. See PsiFileImpl.createTreeElementPointer()
psi.getManager().startBatchFilesProcessingMode();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-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.
@@ -19,17 +19,8 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* Perfroms additional analyses on file with {@link com.intellij.openapi.fileTypes.StdFileTypes#CLASS} filetype (e. g. classfile,
* compiled from other than Java source language).
*
* @author ilyas
*/
/** @deprecated use com.intellij.psi.compiled.ClassFileDecompilers or com.intellij.psi.LanguageSubstitutors API (to remove in IDEA 14) */
public interface ContentBasedClassFileProcessor extends ContentBasedFileSubstitutor {
/**
* @return syntax highlighter for recognized classfile
*/
@NotNull
SyntaxHighlighter createHighlighter(Project project, VirtualFile vFile);
}
+4 -8
View File
@@ -229,10 +229,10 @@
<with attribute="implementationClass" implements="com.intellij.refactoring.encapsulateFields.EncapsulateFieldHelper"/>
</extensionPoint>
<extensionPoint name="psi.clsCustomNavigationPolicy"
interface="com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy"/>
<extensionPoint name="psi.clsDecompiledFileProvider"
interface="com.intellij.psi.ClsFileDecompiledPsiFileProvider"/>
<extensionPoint name="psi.classFileDecompiler" interface="com.intellij.psi.compiled.ClassFileDecompilers$Decompiler"/>
<!-- deprecated API -->
<extensionPoint name="psi.clsCustomNavigationPolicy" interface="com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy"/>
<extensionPoint name="psi.clsDecompiledFileProvider" interface="com.intellij.psi.ClsFileDecompiledPsiFileProvider"/>
<extensionPoint name="vetoSPICondition" interface="com.intellij.openapi.util.Condition"/>
@@ -1309,7 +1309,6 @@
<codeInsight.surroundWithRangeAdjuster implementation="com.intellij.codeInsight.generation.surroundWith.XmlSurroundWithRangeAdjuster"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaAnonymousClassBaseRefOccurenceIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaFieldNameIndex"/>
@@ -1322,9 +1321,6 @@
<stubElementTypeHolder class="com.intellij.psi.impl.java.stubs.JavaStubElementTypes"/>
<clsStubBuilderFactory implementation="com.intellij.psi.impl.compiled.DefaultClsStubBuilderFactory" order="last"/>
<dom.customAnnotationChecker implementation="com.intellij.util.xml.impl.ExtendsClassChecker"/>
<methodNavigationOffsetProvider implementation="com.intellij.codeInsight.navigation.JavaMethodNavigationOffsetProvider"/>