check if annotations are type-use only when they're really needed (rarely)

This commit is contained in:
peter
2015-11-19 21:15:31 +01:00
parent 967f0e089a
commit d3b7d35d20
14 changed files with 180 additions and 46 deletions
@@ -39,6 +39,10 @@ public class PsiArrayType extends PsiType.Stub {
super(annotations);
myComponentType = componentType;
}
public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) {
super(annotations);
myComponentType = componentType;
}
@NotNull
@Override
@@ -114,7 +118,9 @@ public class PsiArrayType extends PsiType.Stub {
}
public boolean equals(Object obj) {
return obj != null && getClass().equals(obj.getClass()) && myComponentType.equals(((PsiArrayType)obj).getComponentType());
return obj instanceof PsiArrayType &&
(this instanceof PsiEllipsisType == obj instanceof PsiEllipsisType) &&
myComponentType.equals(((PsiArrayType)obj).getComponentType());
}
public int hashCode() {
@@ -53,6 +53,11 @@ public abstract class PsiClassType extends PsiType {
myLanguageLevel = languageLevel;
}
public PsiClassType(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider annotations) {
super(annotations);
myLanguageLevel = languageLevel;
}
/**
* Resolves the class reference and returns the resulting class.
*
@@ -295,6 +300,10 @@ public abstract class PsiClassType extends PsiType {
super(languageLevel, annotations);
}
public Stub(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider annotations) {
super(languageLevel, annotations);
}
@NotNull
@Override
public final String getCanonicalText() {
@@ -35,6 +35,9 @@ public class PsiEllipsisType extends PsiArrayType {
public PsiEllipsisType(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) {
super(componentType, annotations);
}
public PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) {
super(componentType, annotations);
}
@NotNull
public static PsiType createEllipsis(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) {
@@ -71,7 +74,7 @@ public class PsiEllipsisType extends PsiArrayType {
* @return the array type instance.
*/
public PsiType toArrayType() {
return getComponentType().createArrayType(getAnnotations());
return new PsiArrayType(getComponentType(), getAnnotationProvider());
}
@Override
@@ -79,10 +82,6 @@ public class PsiEllipsisType extends PsiArrayType {
return visitor.visitEllipsisType(this);
}
public boolean equals(Object obj) {
return obj instanceof PsiEllipsisType && super.equals(obj);
}
public int hashCode() {
return super.hashCode() * 5;
}
@@ -48,6 +48,10 @@ public class PsiPrimitiveType extends PsiType.Stub {
super(annotations);
myName = name;
}
public PsiPrimitiveType(@NonNls @NotNull String name, @NotNull TypeAnnotationProvider annotations) {
super(annotations);
myName = name;
}
@NotNull
@Override
@@ -51,10 +51,26 @@ public abstract class PsiType implements PsiAnnotationOwner {
return ARRAY_FACTORY.create(count);
}
private final PsiAnnotation[] myAnnotations;
private final TypeAnnotationProvider myAnnotationProvider;
protected PsiType(@NotNull PsiAnnotation[] annotations) {
myAnnotations = annotations;
/**
* Constructs a PsiType with given annotations
*/
protected PsiType(@NotNull final PsiAnnotation[] annotations) {
this(annotations.length == 0 ? TypeAnnotationProvider.EMPTY : new TypeAnnotationProvider() {
@NotNull
@Override
public PsiAnnotation[] getAnnotations() {
return annotations;
}
});
}
/**
* Constructs a PsiType that will take its annotations from the given annotation provider.
*/
protected PsiType(@NotNull TypeAnnotationProvider annotations) {
myAnnotationProvider = annotations;
}
/**
@@ -273,15 +289,27 @@ public abstract class PsiType implements PsiAnnotationOwner {
@NotNull
public abstract PsiType[] getSuperTypes();
/**
* @return provider for this type's annotations. Can be used to construct other PsiType instances
* without actually evaluating the annotation array, which can be computationally expensive sometimes.
*/
@NotNull
public final TypeAnnotationProvider getAnnotationProvider() {
return myAnnotationProvider;
}
/**
* @return annotations for this type. Uses {@link #getAnnotationProvider()} to retrieve the annotations.
*/
@Override
@NotNull
public PsiAnnotation[] getAnnotations() {
return myAnnotations;
return myAnnotationProvider.getAnnotations();
}
@Override
public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
for (PsiAnnotation annotation : myAnnotations) {
for (PsiAnnotation annotation : getAnnotations()) {
if (qualifiedName.equals(annotation.getQualifiedName())) {
return annotation;
}
@@ -315,6 +343,10 @@ public abstract class PsiType implements PsiAnnotationOwner {
super(annotations);
}
public Stub(@NotNull TypeAnnotationProvider annotations) {
super(annotations);
}
@NotNull
@Override
public final String getCanonicalText() {
@@ -45,7 +45,7 @@ public class PsiWildcardType extends PsiType.Stub {
myBound = bound;
}
private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull PsiAnnotation[] annotations) {
private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull TypeAnnotationProvider annotations) {
super(annotations);
myManager = type.myManager;
myIsExtending = type.myIsExtending;
@@ -76,8 +76,19 @@ public class PsiWildcardType extends PsiType.Stub {
}
@NotNull
public PsiWildcardType annotate(@NotNull PsiAnnotation[] annotations) {
return annotations.length == 0 ? this : new PsiWildcardType(this, annotations);
public PsiWildcardType annotate(@NotNull final PsiAnnotation[] annotations) {
return annotations.length == 0 ? this : new PsiWildcardType(this, new TypeAnnotationProvider() {
@NotNull
@Override
public PsiAnnotation[] getAnnotations() {
return annotations;
}
});
}
@NotNull
public PsiWildcardType annotate(@NotNull final TypeAnnotationProvider annotations) {
return new PsiWildcardType(this, annotations);
}
@NotNull
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2015 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 org.jetbrains.annotations.NotNull;
/**
* An object that returns annotations for {@link PsiType}. Since computing type annotations might be computationally expensive sometimes,
* this object is used to delay the calculation until annotations are really needed,
* and to pass the annotations without calculating them when creating new types based on existing types.
*
* @see PsiType#getAnnotationProvider()
* @see PsiType#PsiType(TypeAnnotationProvider)
*/
public interface TypeAnnotationProvider {
TypeAnnotationProvider EMPTY = new TypeAnnotationProvider() {
@NotNull
@Override
public PsiAnnotation[] getAnnotations() {
return PsiAnnotation.EMPTY_ARRAY;
}
};
@NotNull
PsiAnnotation[] getAnnotations();
}
@@ -1195,14 +1195,6 @@ public final class PsiUtil extends PsiUtilCore {
}
throw new AssertionError("Invalid type: " + type + " of class " + type.getClass() + " " + customMessage);
}
for (PsiAnnotation annotation : type.getAnnotations()) {
try {
PsiUtilCore.ensureValid(annotation);
}
catch (PsiInvalidElementAccessException e) {
throw customMessage == null? e : new RuntimeException(customMessage, e);
}
}
}
@Nullable
@@ -42,7 +42,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx<PsiType> {
PsiType mappedComponent = mapType(componentType);
if (mappedComponent == null) return null;
if (mappedComponent == componentType) return type;
return new PsiArrayType(mappedComponent, type.getAnnotations());
return new PsiArrayType(mappedComponent, type.getAnnotationProvider());
}
@Override
@@ -51,7 +51,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx<PsiType> {
PsiType mappedComponent = mapType(componentType);
if (mappedComponent == null) return null;
if (mappedComponent == componentType) return type;
return new PsiEllipsisType(mappedComponent, type.getAnnotations());
return new PsiEllipsisType(mappedComponent, type.getAnnotationProvider());
}
@Override
@@ -167,7 +167,7 @@ class TypeCorrector extends PsiTypeMapper {
public PsiCorrectedClassType(LanguageLevel languageLevel,
PsiClassType delegate,
CorrectedResolveResult resolveResult) {
super(languageLevel, delegate.getAnnotations());
super(languageLevel, delegate.getAnnotationProvider());
myDelegate = delegate;
myResolveResult = resolveResult;
}
@@ -42,6 +42,11 @@ public class PsiClassReferenceType extends PsiClassType.Stub {
myReference = reference;
}
public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, @NotNull TypeAnnotationProvider annotations) {
super(level, annotations);
myReference = reference;
}
private static PsiAnnotation[] collectAnnotations(PsiJavaCodeReferenceElement reference) {
List<PsiAnnotation> result = null;
for (PsiElement child = reference.getFirstChild(); child != null; child = child.getNextSibling()) {
@@ -80,7 +85,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub {
@Override
public PsiClassType setLanguageLevel(@NotNull final LanguageLevel languageLevel) {
if (languageLevel.equals(myLanguageLevel)) return this;
return new PsiClassReferenceType(myReference, languageLevel, getAnnotations());
return new PsiClassReferenceType(myReference, languageLevel, getAnnotationProvider());
}
@Override
@@ -151,13 +156,13 @@ public class PsiClassReferenceType extends PsiClassType.Stub {
PsiManager manager = myReference.getManager();
final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
final PsiSubstitutor rawSubstitutor = factory.createRawSubstitutor(aClass);
return factory.createType(aClass, rawSubstitutor, getLanguageLevel(), getAnnotations());
return new PsiImmediateClassType(aClass, rawSubstitutor, getLanguageLevel(), getAnnotationProvider());
}
String qualifiedName = myReference.getQualifiedName();
String name = myReference.getReferenceName();
if (name == null) name = "";
LightClassReference reference = new LightClassReference(myReference.getManager(), name, qualifiedName, myReference.getResolveScope());
return new PsiClassReferenceType(reference, null, getAnnotations());
return new PsiClassReferenceType(reference, null, getAnnotationProvider());
}
@Override
@@ -96,6 +96,16 @@ public class PsiImmediateClassType extends PsiClassType.Stub {
mySubstitutor = substitutor;
assert substitutor.isValid();
}
public PsiImmediateClassType(@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@Nullable LanguageLevel level,
@NotNull TypeAnnotationProvider annotations) {
super(level, annotations);
myClass = aClass;
myManager = aClass.getManager();
mySubstitutor = substitutor;
assert substitutor.isValid();
}
@Override
public PsiClass resolve() {
@@ -298,6 +308,6 @@ public class PsiImmediateClassType extends PsiClassType.Stub {
@NotNull
@Override
public PsiClassType setLanguageLevel(@NotNull LanguageLevel level) {
return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotations());
return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotationProvider());
}
}
@@ -16,9 +16,11 @@
package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiJavaParserFacadeImpl;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
@@ -34,6 +36,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeElement {
@@ -77,7 +81,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
}
PsiType type = null;
SmartList<PsiAnnotation> annotations = new SmartList<PsiAnnotation>();
final SmartList<PsiAnnotation> annotations = new SmartList<PsiAnnotation>();
for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) {
if (child instanceof PsiComment || child instanceof PsiWhiteSpace) continue;
@@ -97,25 +101,23 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
}
else if (PsiUtil.isJavaToken(child, ElementType.PRIMITIVE_TYPE_BIT_SET)) {
assert type == null : this;
addTypeUseAnnotations(annotations);
PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
type = JavaPsiFacade.getInstance(getProject()).getElementFactory().createPrimitiveType(child.getText(), array);
addTypeUseCandidateAnnotations(annotations);
String text = child.getText();
type = annotations.isEmpty() ? PsiJavaParserFacadeImpl.getPrimitiveType(text)
: new PsiPrimitiveType(text, filterTypeUse(annotations));
}
else if (child instanceof PsiJavaCodeReferenceElement) {
assert type == null : this;
addTypeUseAnnotations(annotations);
PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
type = new PsiClassReferenceType((PsiJavaCodeReferenceElement)child, null, array);
addTypeUseCandidateAnnotations(annotations);
type = new PsiClassReferenceType((PsiJavaCodeReferenceElement)child, null, filterTypeUse(annotations));
}
else if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET)) {
assert type != null : this;
PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
type = type.createArrayType(array);
type = new PsiArrayType(type, filterTypeUse(annotations));
}
else if (PsiUtil.isJavaToken(child, JavaTokenType.ELLIPSIS)) {
assert type != null : this;
PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
type = PsiEllipsisType.createEllipsis(type, array);
type = new PsiEllipsisType(type, filterTypeUse(annotations));
}
if (PsiUtil.isJavaToken(child, JavaTokenType.QUEST) ||
@@ -132,8 +134,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
else {
type = PsiWildcardType.createUnbounded(getManager());
}
PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true);
type = ((PsiWildcardType)type).annotate(array);
type = ((PsiWildcardType)type).annotate(filterTypeUse(annotations));
break;
}
@@ -155,13 +156,39 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
return type == null ? PsiType.NULL : type;
}
private void addTypeUseAnnotations(List<PsiAnnotation> annotations) {
@NotNull
private static TypeAnnotationProvider filterTypeUse(List<PsiAnnotation> annotations) {
if (annotations.isEmpty()) return TypeAnnotationProvider.EMPTY;
final ArrayList<PsiAnnotation> copy = ContainerUtil.newArrayList(annotations);
annotations.clear();
return new TypeAnnotationProvider() {
private volatile PsiAnnotation[] myCached;
@NotNull
@Override
public PsiAnnotation[] getAnnotations() {
PsiAnnotation[] result = myCached;
if (result == null) {
List<PsiAnnotation> filtered = ContainerUtil.filter(copy, new Condition<PsiAnnotation>() {
@Override
public boolean value(PsiAnnotation annotation) {
return PsiImplUtil.isTypeAnnotation(annotation);
}
});
myCached = result = filtered.isEmpty() ? PsiAnnotation.EMPTY_ARRAY : filtered.toArray(new PsiAnnotation[filtered.size()]);
}
return result;
}
};
}
private void addTypeUseCandidateAnnotations(List<PsiAnnotation> annotations) {
PsiElement parent = this;
while (parent instanceof PsiTypeElement) {
PsiElement left = PsiTreeUtil.skipSiblingsBackward(parent, INTERMEDIATES);
if (left instanceof PsiModifierList) {
PsiImplUtil.collectTypeUseAnnotations((PsiModifierList)left, annotations);
Collections.addAll(annotations, ((PsiModifierList)left).getAnnotations());
break;
}
@@ -220,7 +247,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
@NotNull
public PsiAnnotation[] getApplicableAnnotations() {
List<PsiAnnotation> annotations = PsiTreeUtil.getChildrenOfTypeAsList(this, PsiAnnotation.class);
addTypeUseAnnotations(annotations);
addTypeUseCandidateAnnotations(annotations);
return annotations.toArray(PsiAnnotation.ARRAY_FACTORY.create(annotations.size()));
}
@@ -98,7 +98,7 @@ public class JavaResolveCache {
type = psiClass == null
? type // for type with unresolved reference, leave it in the cache
// for clients still might be able to retrieve its getCanonicalText() from the reference text
: new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotations());
: new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotationProvider());
}
}