IDEA-67596 (class stub builder: decode only relevant access modifiers)

This commit is contained in:
Roman Shevchenko
2011-04-06 20:54:42 +02:00
parent 39a750c684
commit e856733aab
9 changed files with 152 additions and 83 deletions
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.psi.impl.compiled;
import com.intellij.openapi.vfs.VirtualFile;
@@ -26,6 +22,9 @@ import com.intellij.psi.stubs.BinaryFileStubBuilder;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.cls.ClsFormatException;
/**
* @author max
*/
public class ClassFileStubBuilder implements BinaryFileStubBuilder {
public boolean acceptsFile(final VirtualFile file) {
return !isInner(file.getNameWithoutExtension(), new ParentDirectory(file));
@@ -37,8 +36,7 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
private static boolean isInner(final String name, final int from, final Directory directory) {
final int index = name.indexOf('$', from);
return index == -1 ? false
: containsPart(directory, name, index) ? true : isInner(name, index + 1, directory);
return index != -1 && (containsPart(directory, name, index) || isInner(name, index + 1, directory));
}
private static boolean containsPart(Directory directory, String name, int endIndex) {
@@ -55,7 +53,7 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
}
public int getStubVersion() {
return JavaFileElementType.STUB_VERSION;
return JavaFileElementType.STUB_VERSION + 1;
}
@@ -74,7 +72,7 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
public boolean contains(final String name) {
final String fullName = myExtension == null ? name : name + "." + myExtension;
return myDirectory == null ? false : myDirectory.findChild(fullName) != null;
return myDirectory != null && myDirectory.findChild(fullName) != null;
}
}
}
@@ -85,29 +85,32 @@ public class ClsModifierListImpl extends ClsRepositoryPsiElement<PsiModifierList
}
public void appendMirrorText(final int indentLevel, final StringBuffer buffer) {
PsiAnnotation[] annotations = getAnnotations();
final PsiAnnotation[] annotations = getAnnotations();
final boolean formattingAllowed = isAnnotationFormattingAllowed();
for (PsiAnnotation annotation : annotations) {
((ClsAnnotationImpl)annotation).appendMirrorText(indentLevel, buffer);
if (formattingAllowed) {
goNextLine(indentLevel, buffer);
} else {
}
else {
buffer.append(' ');
}
}
PsiElement parent = getParent();
final PsiElement parent = getParent();
//TODO : filtering & ordering modifiers can go to CodeStyleManager
boolean isInterface = parent instanceof PsiClass && ((PsiClass)parent).isInterface();
boolean isInterfaceMethod = parent instanceof PsiMethod && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
boolean isInterfaceField = parent instanceof PsiField && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
boolean isInterfaceClass = parent instanceof PsiClass && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
if (hasModifierProperty(PsiModifier.PUBLIC)) {
if (!isInterfaceMethod && !isInterfaceField && !isInterfaceClass) {
buffer.append(PsiModifier.PUBLIC);
buffer.append(' ');
}
final boolean isClass = parent instanceof PsiClass;
final boolean isInterface = isClass && ((PsiClass)parent).isInterface();
final boolean isInterfaceClass = isClass && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
final boolean isMethod = parent instanceof PsiMethod;
final boolean isInterfaceMethod = isMethod && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
final boolean isField = parent instanceof PsiField;
final boolean isInterfaceField = isField && parent.getParent() instanceof PsiClass && ((PsiClass)parent.getParent()).isInterface();
if (hasModifierProperty(PsiModifier.PUBLIC) && !isInterfaceMethod && !isInterfaceField && !isInterfaceClass) {
buffer.append(PsiModifier.PUBLIC);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.PROTECTED)) {
buffer.append(PsiModifier.PROTECTED);
@@ -117,23 +120,17 @@ public class ClsModifierListImpl extends ClsRepositoryPsiElement<PsiModifierList
buffer.append(PsiModifier.PRIVATE);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.STATIC)) {
if (!isInterfaceField) {
buffer.append(PsiModifier.STATIC);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.STATIC) && !isInterfaceField) {
buffer.append(PsiModifier.STATIC);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.ABSTRACT)) {
if (!isInterface && !isInterfaceMethod) {
buffer.append(PsiModifier.ABSTRACT);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.ABSTRACT) && !isInterface && !isInterfaceMethod) {
buffer.append(PsiModifier.ABSTRACT);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.FINAL)) {
if (!isInterfaceField) {
buffer.append(PsiModifier.FINAL);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.FINAL) && !isInterfaceField) {
buffer.append(PsiModifier.FINAL);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.NATIVE)) {
buffer.append(PsiModifier.NATIVE);
@@ -151,16 +148,20 @@ public class ClsModifierListImpl extends ClsRepositoryPsiElement<PsiModifierList
buffer.append(PsiModifier.VOLATILE);
buffer.append(' ');
}
if (hasModifierProperty(PsiModifier.STRICTFP)) {
buffer.append(PsiModifier.STRICTFP);
buffer.append(' ');
}
}
public void setMirror(@NotNull TreeElement element) {
setMirrorCheckingType(element, JavaElementType.MODIFIER_LIST);
PsiElement[] mirrorAnnotations = ((PsiModifierList)SourceTreeToPsiMap.treeElementToPsi(element)).getAnnotations();
PsiElement[] mirrorAnnotations = SourceTreeToPsiMap.<PsiModifierList>treeToPsiNotNull(element).getAnnotations();
PsiAnnotation[] annotations = getAnnotations();
LOG.assertTrue(annotations.length == mirrorAnnotations.length);
for (int i = 0; i < annotations.length; i++) {
((ClsElementImpl)annotations[i]).setMirror((TreeElement)SourceTreeToPsiMap.psiElementToTree(mirrorAnnotations[i]));
((ClsElementImpl)annotations[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorAnnotations[i]));
}
}
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.psi.impl.compiled;
import com.intellij.lexer.JavaLexer;
@@ -25,6 +21,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.PsiReferenceList;
@@ -51,12 +48,14 @@ import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
/**
* @author max
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class ClsStubBuilder {
private static final Pattern REGEX_PATTERN = Pattern.compile("(?<=[^\\$])\\${1}(?=[^\\$])");
private ClsStubBuilder() {
}
private ClsStubBuilder() { }
@Nullable
public static PsiFileStub build(final VirtualFile vFile, byte[] bytes) throws ClsFormatException {
@@ -102,7 +101,7 @@ public class ClsStubBuilder {
private final StubElement myParent;
private final int myAccess;
private final VirtualFile myVFile;
private PsiModifierListStub myModlist;
private PsiModifierListStub myModList;
private PsiClassStub myResult;
@NonNls private static final String SYNTHETIC_CLINIT_METHOD = "<clinit>";
@NonNls private static final String SYNTHETIC_INIT_METHOD = "<init>";
@@ -143,7 +142,7 @@ public class ClsStubBuilder {
myLexer = new JavaLexer(languageLevel);
((PsiClassStubImpl)myResult).setLanguageLevel(languageLevel);
myModlist = new PsiModifierListStubImpl(myResult, packModlistFlags(flags));
myModList = new PsiModifierListStubImpl(myResult, packClassFlags(flags));
CharacterIterator signatureIterator = signature != null ? new StringCharacterIterator(signature) : null;
if (signatureIterator != null) {
@@ -177,7 +176,7 @@ public class ClsStubBuilder {
new PsiClassReferenceListStubImpl(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, ArrayUtil.EMPTY_STRING_ARRAY,
PsiReferenceList.Role.IMPLEMENTS_LIST);
} else {
if (convertedSuper != null && !"java.lang.Object".equals(convertedSuper)) {
if (convertedSuper != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(convertedSuper)) {
new PsiClassReferenceListStubImpl(JavaStubElementTypes.EXTENDS_LIST, myResult, new String[]{convertedSuper},
PsiReferenceList.Role.EXTENDS_LIST);
} else {
@@ -226,36 +225,66 @@ public class ClsStubBuilder {
return LanguageLevel.HIGHEST;
}
private static int packModlistFlags(final int access) {
private static int packCommonFlags(final int access) {
int flags = 0;
if ((access & Opcodes.ACC_PRIVATE) != 0) {
flags |= ModifierFlags.PRIVATE_MASK;
} else if ((access & Opcodes.ACC_PROTECTED) != 0) {
}
else if ((access & Opcodes.ACC_PROTECTED) != 0) {
flags |= ModifierFlags.PROTECTED_MASK;
} else if ((access & Opcodes.ACC_PUBLIC) != 0) {
}
else if ((access & Opcodes.ACC_PUBLIC) != 0) {
flags |= ModifierFlags.PUBLIC_MASK;
} else {
}
else {
flags |= ModifierFlags.PACKAGE_LOCAL_MASK;
}
if ((access & Opcodes.ACC_ABSTRACT) != 0) {
flags |= ModifierFlags.ABSTRACT_MASK;
if ((access & Opcodes.ACC_STATIC) != 0) {
flags |= ModifierFlags.STATIC_MASK;
}
if ((access & Opcodes.ACC_FINAL) != 0) {
flags |= ModifierFlags.FINAL_MASK;
}
if ((access & Opcodes.ACC_NATIVE) != 0) {
flags |= ModifierFlags.NATIVE_MASK;
return flags;
}
private static int packClassFlags(final int access) {
int flags = packCommonFlags(access);
if ((access & Opcodes.ACC_ABSTRACT) != 0) {
flags |= ModifierFlags.ABSTRACT_MASK;
}
if ((access & Opcodes.ACC_STATIC) != 0) {
flags |= ModifierFlags.STATIC_MASK;
return flags;
}
private static int packFieldFlags(final int access) {
int flags = packCommonFlags(access);
if ((access & Opcodes.ACC_VOLATILE) != 0) {
flags |= ModifierFlags.VOLATILE_MASK;
}
if ((access & Opcodes.ACC_TRANSIENT) != 0) {
flags |= ModifierFlags.TRANSIENT_MASK;
}
if ((access & Opcodes.ACC_VOLATILE) != 0) {
flags |= ModifierFlags.VOLATILE_MASK;
return flags;
}
private static int packMethodFlags(final int access) {
int flags = packCommonFlags(access);
if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
flags |= ModifierFlags.SYNCHRONIZED_MASK;
}
if ((access & Opcodes.ACC_NATIVE) != 0) {
flags |= ModifierFlags.NATIVE_MASK;
}
if ((access & Opcodes.ACC_ABSTRACT) != 0) {
flags |= ModifierFlags.ABSTRACT_MASK;
}
if ((access & Opcodes.ACC_STRICT) != 0) {
flags |= ModifierFlags.STRICTFP_MASK;
@@ -274,7 +303,7 @@ public class ClsStubBuilder {
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
return new AnnotationTextCollector(desc, new AnnotationResultCallback() {
public void callback(final String text) {
new PsiAnnotationStubImpl(myModlist, text);
new PsiAnnotationStubImpl(myModList, text);
}
});
}
@@ -287,11 +316,11 @@ public class ClsStubBuilder {
if (!isCorrectName(innerName)) return;
if (innerName != null && outerName != null && getClassName(outerName).equals(myResult.getQualifiedName())) {
final String basename = myVFile.getNameWithoutExtension();
final String baseName = myVFile.getNameWithoutExtension();
final VirtualFile dir = myVFile.getParent();
assert dir != null;
final VirtualFile innerFile = dir.findChild(basename + "$" + innerName + ".class");
final VirtualFile innerFile = dir.findChild(baseName + "$" + innerName + ".class");
if (innerFile != null) {
try {
buildClass(innerFile, innerFile.contentsToByteArray(), myResult, access);
@@ -318,8 +347,8 @@ public class ClsStubBuilder {
final byte flags = PsiFieldStubImpl.packFlags((access & Opcodes.ACC_ENUM) != 0, (access & Opcodes.ACC_DEPRECATED) != 0, false);
PsiFieldStub stub = new PsiFieldStubImpl(myResult, name, fieldType(desc, signature), constToString(value), flags);
final PsiModifierListStub modlist = new PsiModifierListStubImpl(stub, packModlistFlags(access));
return new AnnotationCollectingVisitor(stub, modlist);
final PsiModifierListStub modList = new PsiModifierListStubImpl(stub, packFieldFlags(access));
return new AnnotationCollectingVisitor(stub, modList);
}
@NotNull
@@ -346,7 +375,6 @@ public class ClsStubBuilder {
return new TypeInfo(StringRef.fromString(getTypeText(type)), (byte)dim, false, Collections.<PsiAnnotationStub>emptyList()); //todo read annos from .class file
}
@Nullable
public MethodVisitor visitMethod(final int access,
final String name,
@@ -372,7 +400,7 @@ public class ClsStubBuilder {
PsiMethodStubImpl stub = new PsiMethodStubImpl(myResult, StringRef.fromString(canonicalMethodName), flags, null);
final PsiModifierListStub modlist = new PsiModifierListStubImpl(stub, packMethodFlags(access));
final PsiModifierListStub modList = new PsiModifierListStubImpl(stub, packMethodFlags(access));
boolean parsedViaGenericSignature = false;
String returnType;
if (signature == null) {
@@ -392,7 +420,7 @@ public class ClsStubBuilder {
final boolean isNonStaticInnerClassConstructor =
isConstructor && !(myParent instanceof PsiFileStub) && (myModlist.getModifiersMask() & Opcodes.ACC_STATIC) == 0;
isConstructor && !(myParent instanceof PsiFileStub) && (myModList.getModifiersMask() & Opcodes.ACC_STATIC) == 0;
final boolean shouldSkipFirstParamForNonStaticInnerClassConstructor = !parsedViaGenericSignature && isNonStaticInnerClassConstructor;
final PsiParameterListStubImpl parameterList = new PsiParameterListStubImpl(stub);
@@ -420,7 +448,7 @@ public class ClsStubBuilder {
localVarIgnoreCount += 2;
}
final int paramIgnoreCount = isEnumConstructor? 2 : isNonStaticInnerClassConstructor ? 1 : 0;
return new AnnotationParamCollectingVisitor(stub, modlist, localVarIgnoreCount, paramIgnoreCount, paramCount, paramStubs);
return new AnnotationParamCollectingVisitor(stub, modList, localVarIgnoreCount, paramIgnoreCount, paramCount, paramStubs);
}
private static String[] buildThrowsList(String[] exceptions, List<String> throwables, boolean parsedViaGenericSignature) {
@@ -445,15 +473,6 @@ public class ClsStubBuilder {
}
}
private static int packMethodFlags(final int access) {
int commonFlags = packModlistFlags(access);
if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
commonFlags |= ModifierFlags.SYNCHRONIZED_MASK;
}
return commonFlags;
}
private static String parseMethodViaDescription(final String desc, final PsiMethodStubImpl stub, final List<String> args) {
final String returnType = getTypeText(Type.getReturnType(desc));
final Type[] argTypes = Type.getArgumentTypes(desc);
@@ -691,10 +710,10 @@ public class ClsStubBuilder {
private static String getTypeText(final Type type) {
final String raw = type.getClassName();
// As the '$' char is a valid java identifier and is actively used by bytecode genarators, the problem is
// As the '$' char is a valid java identifier and is actively used by byte code generators, the problem is
// which occurrences of this char should be replaced and which should not.
// Heuristic: replace only those $ occurrences that are surrounded non-"$" chars
// (most likely generated by javac to separate inner or anonymoys class name)
// (most likely generated by javac to separate inner or anonymous class name)
// Leading and trailing $ chars should be left unchanged.
return raw.contains("$")? REGEX_PATTERN.matcher(raw).replaceAll("\\.") : raw;
}
@@ -23,7 +23,7 @@ PsiJavaFileStub []
PsiModifierListStub[mask=0]
PsiRefListStub[THROWS_LIST:]
PsiMethodStub[cons varargs AnnotatedEnumConstructor:void]
PsiModifierListStub[mask=130]
PsiModifierListStub[mask=2]
PsiTypeParameterListStub
PsiParameterListStub
PsiParameterStub[names:java.lang.String...]
@@ -0,0 +1,27 @@
PsiJavaFileStub [pack]
PsiClassStub[name=Modifiers fqn=pack.Modifiers]
PsiModifierListStub[mask=1]
PsiTypeParameterListStub
PsiRefListStub[EXTENDS_LIST:]
PsiRefListStub[IMPLEMENTS_LIST:]
PsiFieldStub[f1:int]
PsiModifierListStub[mask=130]
PsiFieldStub[f2:int]
PsiModifierListStub[mask=66]
PsiMethodStub[cons Modifiers:void]
PsiModifierListStub[mask=1]
PsiTypeParameterListStub
PsiParameterListStub
PsiRefListStub[THROWS_LIST:]
PsiMethodStub[varargs m1:void]
PsiModifierListStub[mask=2]
PsiTypeParameterListStub
PsiParameterListStub
PsiParameterStub[i:int...]
PsiModifierListStub[mask=0]
PsiRefListStub[THROWS_LIST:]
PsiMethodStub[m2:void]
PsiModifierListStub[mask=34]
PsiTypeParameterListStub
PsiParameterListStub
PsiRefListStub[THROWS_LIST:]
@@ -2289,7 +2289,7 @@ PsiJavaFileStub [java.util]
PsiModifierListStub[mask=0]
PsiRefListStub[THROWS_LIST:]
PsiMethodStub[varargs addAll:boolean]
PsiModifierListStub[mask=137]
PsiModifierListStub[mask=9]
PsiTypeParameterListStub
PsiTypeParameter[T]
PsiRefListStub[EXTENDS_BOUNDS_LIST:]
@@ -61,15 +61,23 @@ public class ClsBuilderTest extends LightIdeaTestCase {
doTestFromTestData();
}
public void testModifiers() throws Exception {
final String clsFilePath = JavaTestUtil.getJavaTestDataPath() + "/psi/repositoryUse/cls/pack/" + getTestName(false) + ".class";
final VirtualFile clsFile = LocalFileSystem.getInstance().findFileByPath(clsFilePath);
assert clsFile != null : clsFilePath;
doTest(clsFile, getTestName(false) + ".txt");
}
private void doTestFromTestData() throws ClsFormatException, IOException {
final String clsFilePath = JavaTestUtil.getJavaTestDataPath() + "/psi/cls/stubBuilder/" + getTestName(false) + ".class";
VirtualFile clsFile = LocalFileSystem.getInstance().findFileByPath(clsFilePath);
final VirtualFile clsFile = LocalFileSystem.getInstance().findFileByPath(clsFilePath);
assert clsFile != null : clsFilePath;
doTest(clsFile, getTestName(false) + ".txt");
}
private void doTest(final String className) throws IOException, ClsFormatException {
VirtualFile vFile = findFile(className);
doTest(vFile, getTestName(false)+".txt");
final VirtualFile clsFile = findFile(className);
doTest(clsFile, getTestName(false) + ".txt");
}
private static void doTest(VirtualFile vFile, String goldFile) throws ClsFormatException, IOException {
@@ -752,4 +752,20 @@ public class ClsRepositoryUseTest extends PsiTestCase{
assertTrue(substitution instanceof PsiWildcardType);
assertEquals(PsiWildcardType.createUnbounded(myPsiManager), substitution);
}
public void testModifiers() throws Exception {
final PsiClass psiClass = myJavaFacade.findClass("pack.Modifiers", RESOLVE_SCOPE);
assertNotNull(psiClass);
assertEquals("public class Modifiers {\n" +
" private transient int f1;\n" +
" private volatile int f2;\n" +
" \n" +
" public Modifiers() { /* compiled code */ }\n" +
" \n" +
" private void m1(int... i) { /* compiled code */ }\n" +
" \n" +
" private synchronized void m2() { /* compiled code */ }\n" +
"}",
psiClass.getText().trim());
}
}