@Override public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) { if ((access & Opcodes.ACC_SYNTHETIC) != 0) return; if (!isCorrectName(innerName) || outerName == null) return; if ((getClassName(outerName) + "." + innerName).equals(myResult.getQualifiedName())) { // our result is inner class if (myParent instanceof PsiFileStub) { throw new OutOfOrderInnerClassException(); } } if (!namesEqual(outerName, myResult.getQualifiedName())) { return; } T innerClass = myInnersStrategy.findInnerClass(innerName, mySource); if (innerClass != null) { StubBuildingVisitor<T> visitor = new StubBuildingVisitor<T>(innerClass, myInnersStrategy, myResult, access, innerName); myInnersStrategy.accept(innerClass, visitor); } }
private static boolean isAnonymousOrLocal(PsiClass aClass) { if (aClass instanceof PsiAnonymousClass) return true; final PsiClassStub stub = ((PsiClassImpl)aClass).getStub(); if (stub != null) { final StubElement parentStub = stub.getParentStub(); return !(parentStub instanceof PsiClassStub || parentStub instanceof PsiFileStub); } PsiElement parent = aClass.getParent(); while (parent != null) { if (parent instanceof PsiMethod || parent instanceof PsiField || parent instanceof PsiClassInitializer) return true; if (parent instanceof PsiClass || parent instanceof PsiFile) return false; parent = parent.getParent(); } return false; }
@Override public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) { if ((access & Opcodes.ACC_SYNTHETIC) != 0) return; if (!isCorrectName(innerName)) return; if (innerName == null || outerName == null) return; if ((getClassName(outerName) + "." + innerName).equals(myResult.getQualifiedName())) { // Our result is inner class if (myParent instanceof PsiFileStub) { throw new OutOfOrderInnerClassException(); } } if (!getClassName(outerName).equals(myResult.getQualifiedName())) return; final T innerSource = myInnersStrategy.findInnerClass(innerName, mySource); if (innerSource == null) return; final ClassReader reader = myInnersStrategy.readerForInnerClass(innerSource); if (reader == null) return; final StubBuildingVisitor<T> classVisitor = new StubBuildingVisitor<T>(innerSource, myInnersStrategy, myResult, access); reader.accept(classVisitor, ClassReader.SKIP_FRAMES); }
@Override public StubElement buildStubTree(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) { if (!factory.isInnerClass(file) && factory.canBeProcessed(file, content)) { PsiFileStub stub = factory.buildFileStub(file, content, project); if (stub != null) return stub; } } if (!fileContent.getFileName().contains("$")) { LOG.info("No stub built for file " + fileContent); } return null; } catch (ClsFormatException e) { return null; } }
@Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if(isSet(access, Opcodes.ACC_SYNTHETIC)) { return; } if(innerName == null || outerName == null) { return; } if(myParent instanceof PsiFileStub && myInternalName.equals(name)) { throw new OutOfOrderInnerClassException(); // our result is inner class } if(myInternalName.equals(outerName)) { T innerClass = myInnersStrategy.findInnerClass(innerName, mySource); if(innerClass != null) { myInnersStrategy.accept(innerClass, new StubBuildingVisitor<>(innerClass, myInnersStrategy, myResult, access, innerName)); } } }
private PsiJavaFileStub getFileStub(StubT stub) { StubElement parent = stub; while (!(parent instanceof PsiFileStub)) { parent = parent.getParentStub(); } return (PsiJavaFileStub)parent; }
@Override public PsiFile getContainingFile() { StubElement p = myStub; while (!(p instanceof PsiFileStub)) { p = p.getParentStub(); } return (PsiFile)p.getPsi(); }
@Override public PsiElement getParent() { final PsiClassStub stub = getStub(); if (stub != null) { final StubElement parentStub = stub.getParentStub(); if (parentStub instanceof PsiFileStub || parentStub instanceof PsiClassStub ) { return parentStub.getPsi(); } } return SharedImplUtil.getParent(getNode()); }
@Nonnull @Override public PsiFile getContainingFile() { StubElement stub = myStub; while (!(stub instanceof PsiFileStub)) { stub = stub.getParentStub(); } PsiFile psi = (PsiFile)stub.getPsi(); if (psi != null) { return psi; } return reportError(stub); }
@Override public void visitOuterClass(String owner, String name, String desc) { if(myParent instanceof PsiFileStub) { throw new OutOfOrderInnerClassException(); } }
private static boolean isAnonymousOrLocal(PsiClass aClass) { if(aClass instanceof PsiAnonymousClass) { return true; } final PsiClassStub stub = ((PsiClassImpl) aClass).getGreenStub(); if(stub != null) { final StubElement parentStub = stub.getParentStub(); return !(parentStub instanceof PsiClassStub || parentStub instanceof PsiFileStub); } PsiElement parent = aClass.getParent(); while(parent != null) { if(parent instanceof PsiMethod || parent instanceof PsiField || parent instanceof PsiClassInitializer) { return true; } if(parent instanceof PsiClass || parent instanceof PsiFile) { return false; } parent = parent.getParent(); } return false; }
public PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes, Project project) throws ClsFormatException { return buildFileStub(file, bytes); }
@Override public void visitOuterClass(final String owner, final String name, final String desc) { if (myParent instanceof PsiFileStub) { throw new OutOfOrderInnerClassException(); } }
public static int packModifierList(@NotNull LighterAST tree, @NotNull LighterASTNode modList, @NotNull StubElement parent) { int packed = 0; final LighterASTNode modListOwner = tree.getParent(modList); if (modListOwner != null && modListOwner.getTokenType() == parent.getStubType()) { final StubElement grandParent = parent.getParentStub(); if (parent instanceof PsiClassStub) { if (grandParent instanceof PsiClassStub && ((PsiClassStub)grandParent).isInterface()) { packed |= ModifierFlags.PUBLIC_MASK; packed |= ModifierFlags.STATIC_MASK; } if (((PsiClassStub)parent).isInterface()) { packed |= ModifierFlags.ABSTRACT_MASK; if (grandParent instanceof PsiClassStub) { packed |= ModifierFlags.STATIC_MASK; } } else if (((PsiClassStub)parent).isEnum()) { if (!(grandParent instanceof PsiFileStub)) { packed |= ModifierFlags.STATIC_MASK; } boolean isFinal = true; final List<LighterASTNode> enumConstants = LightTreeUtil.getChildrenOfType(tree, modListOwner, JavaElementType.ENUM_CONSTANT); for (final LighterASTNode constant : enumConstants) { if (LightTreeUtil.firstChildOfType(tree, constant, JavaElementType.ENUM_CONSTANT_INITIALIZER) != null) { isFinal = false; break; } } if (isFinal) { packed |= ModifierFlags.FINAL_MASK; } final List<LighterASTNode> methods = LightTreeUtil.getChildrenOfType(tree, modListOwner, JavaElementType.METHOD); for (final LighterASTNode method : methods) { final LighterASTNode mods = LightTreeUtil.requiredChildOfType(tree, method, JavaElementType.MODIFIER_LIST); if (LightTreeUtil.firstChildOfType(tree, mods, JavaTokenType.ABSTRACT_KEYWORD) != null) { packed |= ModifierFlags.ABSTRACT_MASK; break; } } } } else if (parent instanceof PsiMethodStub) { if (grandParent instanceof PsiClassStub && ((PsiClassStub)grandParent).isInterface()) { packed |= ModifierFlags.PUBLIC_MASK; packed |= ModifierFlags.ABSTRACT_MASK; } } else if (parent instanceof PsiFieldStub) { if (parent.getStubType() == JavaElementType.ENUM_CONSTANT || grandParent instanceof PsiClassStub && ((PsiClassStub)grandParent).isInterface()) { packed |= ModifierFlags.PUBLIC_MASK; packed |= ModifierFlags.STATIC_MASK; packed |= ModifierFlags.FINAL_MASK; } } } for (final LighterASTNode child : tree.getChildren(modList)) { final int flag = ModifierFlags.KEYWORD_TO_MODIFIER_FLAG_MAP.get(child.getTokenType()); if (flag != 0) { packed |= flag; } } if ((packed & ModifierFlags.DEFENDER_MASK) != 0) { packed &= ~ModifierFlags.ABSTRACT_MASK; } if ((packed & (ModifierFlags.PRIVATE_MASK | ModifierFlags.PROTECTED_MASK | ModifierFlags.PUBLIC_MASK)) == 0) { packed |= ModifierFlags.PACKAGE_LOCAL_MASK; } return packed; }
@NonNls @NotNull private static String reason(@NotNull PsiElement root) { if (root == PsiUtilCore.NULL_PSI_ELEMENT) return "NULL_PSI_ELEMENT"; PsiElement element = root instanceof PsiFile ? root : root.getParent(); if (element == null) { String m = "parent is null"; if (root instanceof StubBasedPsiElement) { StubElement stub = ((StubBasedPsiElement)root).getStub(); while (stub != null) { m += "\n each stub=" + stub; if (stub instanceof PsiFileStub) { m += "; fileStub.psi=" + stub.getPsi() + "; reason=" + ((PsiFileStub)stub).getInvalidationReason(); } stub = stub.getParentStub(); } } return m; } while (element != null && !(element instanceof PsiFile)) element = element.getParent(); PsiFile file = (PsiFile)element; if (file == null) return "containing file is null"; FileViewProvider provider = file.getViewProvider(); VirtualFile vFile = provider.getVirtualFile(); if (!vFile.isValid()) return vFile + " is invalid"; if (!provider.isPhysical()) { PsiElement context = file.getContext(); if (context != null && !context.isValid()) { return "invalid context: " + reason(context); } } PsiManager manager = file.getManager(); if (manager.getProject().isDisposed()) return "project is disposed"; Language language = file.getLanguage(); if (language != provider.getBaseLanguage()) return "File language:" + language + " != Provider base language:" + provider.getBaseLanguage(); FileViewProvider p = manager.findViewProvider(vFile); if (provider != p) return "different providers: " + provider + "(" + id(provider) + "); " + p + "(" + id(p) + ")"; if (!provider.isPhysical()) return "non-physical provider: " + provider; // "dummy" file? return "psi is outdated"; }
@Nullable 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 { return buildFileStub(file, bytes); }
@Override @Nullable public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { // JLS 13.1 says: Any constructs introduced by the compiler that do not have a corresponding construct in the source code // must be marked as synthetic, except for default constructors and the class initialization method. // However Scala compiler erroneously generates ACC_BRIDGE instead of ACC_SYNTHETIC flag for in-trait implementation delegation. // See IDEA-78649 if ((access & Opcodes.ACC_SYNTHETIC) != 0) return null; if (SYNTHETIC_CLASS_INIT_METHOD.equals(name)) return null; boolean isDeprecated = (access & Opcodes.ACC_DEPRECATED) != 0; boolean isConstructor = SYNTHETIC_INIT_METHOD.equals(name); boolean isVarargs = (access & Opcodes.ACC_VARARGS) != 0; boolean isAnnotationMethod = myResult.isAnnotationType(); if (!isConstructor && !isCorrectName(name)) return null; final byte flags = PsiMethodStubImpl.packFlags(isConstructor, isAnnotationMethod, isVarargs, isDeprecated, false); final String canonicalMethodName = isConstructor ? myResult.getName() : name; final List<String> args = new ArrayList<String>(); final List<String> throwables = exceptions != null ? new ArrayList<String>() : null; final PsiMethodStubImpl stub = new PsiMethodStubImpl(myResult, StringRef.fromString(canonicalMethodName), flags, null); final PsiModifierListStub modList = new PsiModifierListStubImpl(stub, packMethodFlags(access, myResult.isInterface())); boolean parsedViaGenericSignature = false; String returnType; if (signature == null) { returnType = parseMethodViaDescription(desc, stub, args); } else { try { returnType = parseMethodViaGenericSignature(signature, stub, args, throwables); parsedViaGenericSignature = true; } catch (ClsFormatException e) { returnType = parseMethodViaDescription(desc, stub, args); } } stub.setReturnType(TypeInfo.fromString(returnType)); final boolean isNonStaticInnerClassConstructor = isConstructor && !(myParent instanceof PsiFileStub) && (myModList.getModifiersMask() & Opcodes.ACC_STATIC) == 0; final boolean shouldSkipFirstParamForNonStaticInnerClassConstructor = !parsedViaGenericSignature && isNonStaticInnerClassConstructor; final PsiParameterListStubImpl parameterList = new PsiParameterListStubImpl(stub); final int paramCount = args.size(); final PsiParameterStubImpl[] paramStubs = new PsiParameterStubImpl[paramCount]; for (int i = 0; i < paramCount; i++) { if (shouldSkipFirstParamForNonStaticInnerClassConstructor && i == 0) continue; String arg = args.get(i); boolean isEllipsisParam = isVarargs && i == paramCount - 1; final TypeInfo typeInfo = TypeInfo.fromString(arg, isEllipsisParam); String paramName = i < parameterNames.length ? parameterNames[i] : "p" + (i + 1); PsiParameterStubImpl parameterStub = new PsiParameterStubImpl(parameterList, paramName, typeInfo, isEllipsisParam); paramStubs [i] = parameterStub; new PsiModifierListStubImpl(parameterStub, 0); } String[] thrownTypes = buildThrowsList(exceptions, throwables, parsedViaGenericSignature); newReferenceList(JavaStubElementTypes.THROWS_LIST, stub, thrownTypes); final int localVarIgnoreCount = (access & Opcodes.ACC_STATIC) != 0 ? 0 : isConstructor && myResult.isEnum() ? 3 : 1; final int paramIgnoreCount = isConstructor && myResult.isEnum() ? 2 : isNonStaticInnerClassConstructor ? 1 : 0; return new AnnotationParamCollectingVisitor(stub, modList, localVarIgnoreCount, paramIgnoreCount, paramCount, paramStubs); }
@Override public PsiClassStub createStub(final LighterAST tree, final LighterASTNode node, final StubElement parentStub) { String qualifiedName = null; String name = null; if(parentStub instanceof PsiFileStub) { PsiElement psi = parentStub.getPsi(); if(psi instanceof PsiFile) { name = JspClassImpl.buildName((PsiFile) psi); } } if(name != null) { if(parentStub instanceof PsiJavaFileStub) { final String pkg = ((PsiJavaFileStub) parentStub).getPackageName(); if(!pkg.isEmpty()) { qualifiedName = pkg + '.' + name; } else { qualifiedName = name; } } else if(parentStub instanceof PsiClassStub) { final String parentFqn = ((PsiClassStub) parentStub).getQualifiedName(); qualifiedName = parentFqn != null ? parentFqn + '.' + name : null; } else if(parentStub instanceof PsiClassLevelDeclarationStatementStub) { StubElement parent = parentStub; while(parent != null) { if(parent instanceof PsiClassStub) { qualifiedName = ((PsiClassStub) parent).getQualifiedName(); break; } parent = parent.getParentStub(); } } } return new PsiClassStubImpl(this, parentStub, qualifiedName, name, null, (byte) 0); }
@NonNls @Nonnull private static String reason(@Nonnull PsiElement root) { if (root == PsiUtilCore.NULL_PSI_ELEMENT) return "NULL_PSI_ELEMENT"; PsiElement element = root instanceof PsiFile ? root : root.getParent(); if (element == null) { String m = "parent is null"; if (root instanceof StubBasedPsiElement) { StubElement stub = ((StubBasedPsiElement)root).getStub(); while (stub != null) { m += "\n each stub=" + stub; if (stub instanceof PsiFileStub) { m += "; fileStub.psi=" + stub.getPsi() + "; reason=" + ((PsiFileStub)stub).getInvalidationReason(); } stub = stub.getParentStub(); } } return m; } while (element != null && !(element instanceof PsiFile)) element = element.getParent(); PsiFile file = (PsiFile)element; if (file == null) return "containing file is null"; FileViewProvider provider = file.getViewProvider(); VirtualFile vFile = provider.getVirtualFile(); if (!vFile.isValid()) return vFile + " is invalid"; if (!provider.isPhysical()) { PsiElement context = file.getContext(); if (context != null && !context.isValid()) { return "invalid context: " + reason(context); } } PsiManager manager = file.getManager(); if (manager.getProject().isDisposed()) return "project is disposed"; Language language = file.getLanguage(); if (language != provider.getBaseLanguage()) return "File language:" + language + " != Provider base language:" + provider.getBaseLanguage(); FileViewProvider p = manager.findViewProvider(vFile); if (provider != p) return "different providers: " + provider + "(" + id(provider) + "); " + p + "(" + id(p) + ")"; if (!provider.isPhysical()) return "non-physical provider: " + provider; // "dummy" file? return "psi is outdated"; }
/** * 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;
public abstract PsiFileStub<T> buildFileStub(final VirtualFile file, byte[] bytes) throws ClsFormatException;