[java-stubs] Part of IDEA-246761 Support type annotations in various places of class stubs

Supported:
1. Class extends list
2. Class implements list
3. Class type parameter bounds
4. Method type parameter bounds
5. Throws list

Not supported yet:
1. Class type parameters
2. Method type parameters
3. Method receiver type

GitOrigin-RevId: 7ca87689c52726ede9d22cb7ec683692af66ba06
This commit is contained in:
Tagir Valeev
2020-07-30 10:19:12 +00:00
committed by intellij-monorepo-bot
parent cff373e542
commit 119ec1ae16
18 changed files with 320 additions and 136 deletions
@@ -35,6 +35,8 @@ public class TypeInfo {
private static final int HAS_TYPE_ANNOTATIONS = 0x20;
private static final int HAS_ARRAY_COUNT = 0x40;
private static final int HAS_ELLIPSIS = 0x80;
public static final TypeInfo[] EMPTY_ARRAY = {};
private static final String[] ourIndexFrequentType;
private static final TObjectIntHashMap<String> ourFrequentTypeIndex;
@@ -54,7 +56,7 @@ public class TypeInfo {
assert ourFrequentTypeIndex.size() < FREQUENT_INDEX_MASK;
}
private static final TypeInfo NULL = new TypeInfo(null, (byte)0, false);
private static final TypeInfo NULL = new TypeInfo(null);
public final String text;
public final byte arrayCount;
@@ -73,6 +75,20 @@ public class TypeInfo {
}
}
/**
* Creates a non-array type info
*
* @param text type text (not array)
*/
public TypeInfo(String text) {
this(text, (byte)0, false);
}
/**
* @param text type text (not array)
* @param arrayCount number of array components (including vararg component, if any)
* @param ellipsis if true, the last array component should be interpreted as vararg
*/
public TypeInfo(String text, byte arrayCount, boolean ellipsis) {
this.text = text == null ? null : internFrequentType(text);
this.arrayCount = arrayCount;
@@ -80,7 +96,7 @@ public class TypeInfo {
}
/**
* @param typeAnnotations set type annotations. Could be called only once during the creation.
* @param typeAnnotations set type annotations. Could be called only once.
*/
public void setTypeAnnotations(@NotNull TypeAnnotationContainer typeAnnotations) {
if (myTypeAnnotations != null) {
@@ -89,6 +105,9 @@ public class TypeInfo {
myTypeAnnotations = typeAnnotations;
}
/**
* @return type annotations associated with this type.
*/
public @NotNull TypeAnnotationContainer getTypeAnnotations() {
return myTypeAnnotations == null ? TypeAnnotationContainer.EMPTY : myTypeAnnotations;
}
@@ -18,32 +18,27 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.psi.*;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
public class ClsReferenceListImpl extends ClsRepositoryPsiElement<PsiClassReferenceListStub> implements PsiReferenceList {
private static final ClsJavaCodeReferenceElementImpl[] EMPTY_REFS_ARRAY = new ClsJavaCodeReferenceElementImpl[0];
private final NotNullLazyValue<ClsJavaCodeReferenceElementImpl[]> myRefs;
private final NotNullLazyValue<PsiJavaCodeReferenceElement[]> myRefs;
public ClsReferenceListImpl(@NotNull PsiClassReferenceListStub stub) {
super(stub);
myRefs = new AtomicNotNullLazyValue<ClsJavaCodeReferenceElementImpl[]>() {
myRefs = new AtomicNotNullLazyValue<PsiJavaCodeReferenceElement[]>() {
@Override
protected ClsJavaCodeReferenceElementImpl @NotNull [] compute() {
String[] strings = getStub().getReferencedNames();
if (strings.length > 0) {
ClsJavaCodeReferenceElementImpl[] refs = new ClsJavaCodeReferenceElementImpl[strings.length];
for (int i = 0; i < strings.length; i++) {
refs[i] = new ClsJavaCodeReferenceElementImpl(ClsReferenceListImpl.this, strings[i]);
}
return refs;
}
else {
return EMPTY_REFS_ARRAY;
protected PsiJavaCodeReferenceElement @NotNull [] compute() {
TypeInfo[] types = getStub().getTypes();
if (types.length <= 0) {
return PsiJavaCodeReferenceElement.EMPTY_ARRAY;
}
return ContainerUtil.map2Array(types, PsiJavaCodeReferenceElement.class, info ->
new ClsJavaCodeReferenceElementImpl(ClsReferenceListImpl.this, info.text, info.getTypeAnnotations()));
}
};
}
@@ -70,10 +65,13 @@ public class ClsReferenceListImpl extends ClsRepositoryPsiElement<PsiClassRefere
@Override
public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
String[] names = getStub().getReferencedNames();
if (names.length != 0) {
switch (getRole()) {
PsiClassType @NotNull [] types = getStub().getReferencedTypes();
if (types.length != 0) {
Role role = getRole();
switch (role) {
case EXTENDS_BOUNDS_LIST:
buffer.append(' ').append(PsiKeyword.EXTENDS).append(' ');
break;
case EXTENDS_LIST:
buffer.append(PsiKeyword.EXTENDS).append(' ');
break;
@@ -87,9 +85,9 @@ public class ClsReferenceListImpl extends ClsRepositoryPsiElement<PsiClassRefere
buffer.append(PsiKeyword.WITH).append(' ');
break;
}
for (int i = 0; i < names.length; i++) {
if (i > 0) buffer.append(", ");
buffer.append(names[i]);
for (int i = 0; i < types.length; i++) {
if (i > 0) buffer.append(role == Role.EXTENDS_BOUNDS_LIST ? " & " : ", ");
buffer.append(types[i].getCanonicalText(true));
}
}
}
@@ -97,7 +95,13 @@ public class ClsReferenceListImpl extends ClsRepositoryPsiElement<PsiClassRefere
@Override
public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
setMirrorCheckingType(element, null);
setMirrors(getReferenceElements(), SourceTreeToPsiMap.<PsiReferenceList>treeToPsiNotNull(element).getReferenceElements());
PsiJavaCodeReferenceElement[] mirrorRefs = SourceTreeToPsiMap.<PsiReferenceList>treeToPsiNotNull(element).getReferenceElements();
PsiJavaCodeReferenceElement[] stubRefs = getReferenceElements();
if (mirrorRefs.length == 0 && stubRefs.length == 1 && CommonClassNames.JAVA_LANG_OBJECT.equals(stubRefs[0].getQualifiedName())) {
// annotated Object type is supported in stubs but not supported in decompiler yet
return;
}
setMirrors(stubRefs, mirrorRefs);
}
@Override
@@ -112,6 +116,6 @@ public class ClsReferenceListImpl extends ClsRepositoryPsiElement<PsiClassRefere
@Override
public String toString() {
return "PsiReferenceList";
return "PsiReferenceList:" + getRole();
}
}
@@ -280,15 +280,7 @@ public class ClsTypeParameterImpl extends ClsRepositoryPsiElement<PsiTypeParamet
@Override
public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) {
buffer.append(getName());
PsiJavaCodeReferenceElement[] bounds = getExtendsList().getReferenceElements();
if (bounds.length > 0) {
buffer.append(" extends ");
for (int i = 0; i < bounds.length; i++) {
if (i > 0) buffer.append(" & ");
buffer.append(bounds[i].getCanonicalText());
}
}
appendText(getExtendsList(), indentLevel, buffer);
}
@Override
@@ -3,9 +3,11 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.EntryStream;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.*;
@@ -65,11 +67,25 @@ class FirstPassData implements Function<@NotNull String, @NotNull String> {
}
return depth;
}
/**
* @param componentName record component name
* @return true if given component is var-arg
*/
public boolean isVarArgComponent(@NotNull String componentName) {
return componentName.equals(myVarArgRecordComponent);
}
/**
* @param jvmNames array JVM type names (e.g. throws list, implements list)
* @return list of TypeInfo objects that correspond to given types
*/
@Contract("null -> null; !null -> !null")
public List<TypeInfo> createTypes(String @Nullable [] jvmNames) {
return jvmNames == null ? null :
ContainerUtil.map(jvmNames, jvmName -> new TypeInfo(mapJvmClassNameToJava(jvmName)));
}
/**
* @param jvmName JVM class name like java/util/Map$Entry
* @return Java class name like java.util.Map.Entry
@@ -1,43 +1,91 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl.compiled;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.CommonClassNames;
import com.intellij.util.ArrayUtilRt;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiTypeParameterListStub;
import com.intellij.psi.impl.java.stubs.PsiTypeParameterStub;
import com.intellij.psi.impl.java.stubs.impl.PsiClassReferenceListStubImpl;
import com.intellij.psi.impl.java.stubs.impl.PsiTypeParameterListStubImpl;
import com.intellij.psi.impl.java.stubs.impl.PsiTypeParameterStubImpl;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
import com.intellij.util.cls.ClsFormatException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.TypeReference;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.intellij.openapi.util.Pair.pair;
public final class SignatureParsing {
private SignatureParsing() { }
public static class TypeParametersDeclaration {
static final TypeParametersDeclaration EMPTY = new TypeParametersDeclaration(Collections.emptyList());
private final List<TypeParameterDeclaration> myDeclarations;
@NotNull
public static List<Pair<String, String[]>> parseTypeParametersDeclaration(CharacterIterator signature, Function<String, String> mapping) throws ClsFormatException {
if (signature.current() != '<') {
return Collections.emptyList();
private TypeParametersDeclaration(List<TypeParameterDeclaration> declarations) {
myDeclarations = declarations;
}
List<Pair<String, String[]>> typeParameters = new ArrayList<>();
TypeInfo getBoundType(TypeReference ref) {
int typeParameterIndex = ref.getTypeParameterIndex();
int boundIndex = ref.getTypeParameterBoundIndex();
if (typeParameterIndex < myDeclarations.size()) {
TypeParameterDeclaration typeParam = myDeclarations.get(typeParameterIndex);
if (boundIndex < typeParam.myBounds.length) {
return typeParam.myBounds[boundIndex];
}
}
return null;
}
void createTypeParameterList(StubElement<?> parent) {
PsiTypeParameterListStub listStub = new PsiTypeParameterListStubImpl(parent);
for (TypeParameterDeclaration parameter : this.myDeclarations) {
PsiTypeParameterStub parameterStub = new PsiTypeParameterStubImpl(listStub, parameter.myTypeParameter);
TypeInfo[] info = parameter.myBounds;
if (info.length > 0 && info[0].text == null) {
info = Arrays.copyOfRange(info, 1, info.length);
}
new PsiClassReferenceListStubImpl(JavaStubElementTypes.EXTENDS_BOUND_LIST, parameterStub, info);
}
}
}
private static class TypeParameterDeclaration {
private final String myTypeParameter;
private final TypeInfo[] myBounds;
private TypeParameterDeclaration(String parameter, TypeInfo[] bounds) {
myTypeParameter = parameter;
myBounds = bounds;
}
}
@NotNull
public static TypeParametersDeclaration parseTypeParametersDeclaration(CharacterIterator signature, Function<String, String> mapping) throws ClsFormatException {
if (signature.current() != '<') {
return TypeParametersDeclaration.EMPTY;
}
List<TypeParameterDeclaration> typeParameters = new ArrayList<>();
signature.next();
while (signature.current() != '>') {
typeParameters.add(parseTypeParameter(signature, mapping));
}
signature.next();
return typeParameters;
return new TypeParametersDeclaration(typeParameters);
}
private static Pair<String, String[]> parseTypeParameter(CharacterIterator signature, Function<String, String> mapping) throws ClsFormatException {
private static TypeParameterDeclaration parseTypeParameter(CharacterIterator signature, Function<String, String> mapping) throws ClsFormatException {
StringBuilder name = new StringBuilder();
while (signature.current() != ':' && signature.current() != CharacterIterator.DONE) {
name.append(signature.current());
@@ -48,27 +96,15 @@ public final class SignatureParsing {
}
String parameterName = mapping.fun(name.toString());
// postpone list allocation till a second bound is seen; ignore sole Object bound
List<String> bounds = null;
boolean jlo = false;
List<TypeInfo> bounds = new SmartList<>();
while (signature.current() == ':') {
signature.next();
String bound = parseTopLevelClassRefSignature(signature, mapping);
if (bound == null) continue;
if (bounds == null) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(bound)) {
jlo = true;
continue;
}
bounds = new SmartList<>();
if (jlo) {
bounds.add(CommonClassNames.JAVA_LANG_OBJECT);
}
}
bounds.add(bound);
if (!bounds.isEmpty() && bound == null) continue;
bounds.add(new TypeInfo(bound));
}
return pair(parameterName, ArrayUtilRt.toStringArray(bounds));
return new TypeParameterDeclaration(parameterName, bounds.toArray(TypeInfo.EMPTY_ARRAY));
}
@Nullable
@@ -2,18 +2,17 @@
package com.intellij.psi.impl.compiled;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.impl.cache.ModifierFlags;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.compiled.SignatureParsing.TypeParametersDeclaration;
import com.intellij.psi.impl.java.stubs.*;
import com.intellij.psi.impl.java.stubs.impl.*;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
@@ -26,6 +25,7 @@ import org.jetbrains.org.objectweb.asm.*;
import java.lang.reflect.Array;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -58,6 +58,8 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
private PsiClassStub<?> myResult;
private PsiModifierListStub myModList;
private PsiRecordHeaderStub myHeaderStub;
private Map<TypeInfo, TypeAnnotationContainer.Builder> myAnnoBuilders;
private ClassInfo myClassInfo;
public StubBuildingVisitor(T classSource, InnerClassSourceStrategy<T> innersStrategy, StubElement<?> parent, int access, String shortName) {
this(classSource, innersStrategy, parent, access, shortName, false, false);
@@ -105,38 +107,37 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
myHeaderStub = new PsiRecordHeaderStubImpl(myResult);
}
ClassInfo info = null;
if (signature != null) {
try {
info = parseClassSignature(signature);
myClassInfo = parseClassSignature(signature);
}
catch (ClsFormatException e) {
if (LOG.isDebugEnabled()) LOG.debug("source=" + mySource + " signature=" + signature, e);
}
}
if (info == null) {
info = parseClassDescription(superName, interfaces);
if (myClassInfo == null) {
myClassInfo = parseClassDescription(superName, interfaces);
}
newTypeParameterList(myResult, info.typeParameters);
myClassInfo.typeParameters.createTypeParameterList(myResult);
if (myResult.isInterface()) {
if (info.interfaceNames != null && myResult.isAnnotationType()) {
info.interfaceNames.remove(CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION);
if (myClassInfo.interfaces != null && myResult.isAnnotationType()) {
myClassInfo.interfaces.removeIf(info -> info.text.equals(CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION));
}
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, ArrayUtilRt.toStringArray(info.interfaceNames));
newReferenceList(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, ArrayUtilRt.EMPTY_STRING_ARRAY);
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, myClassInfo.interfaces);
newReferenceList(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, Collections.emptyList());
}
else {
if (info.superName == null || "java/lang/Object".equals(superName) ||
if (myClassInfo.superType == null ||
myResult.isEnum() && "java/lang/Enum".equals(superName) ||
myResult.isRecord() && "java/lang/Record".equals(superName)) {
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, ArrayUtilRt.EMPTY_STRING_ARRAY);
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, Collections.emptyList());
}
else {
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, new String[]{info.superName});
newReferenceList(JavaStubElementTypes.EXTENDS_LIST, myResult, Collections.singletonList(myClassInfo.superType));
}
newReferenceList(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, ArrayUtilRt.toStringArray(info.interfaceNames));
newReferenceList(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, myClassInfo.interfaces);
}
}
@@ -157,34 +158,27 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
ClassInfo result = new ClassInfo();
CharacterIterator iterator = new StringCharacterIterator(signature);
result.typeParameters = SignatureParsing.parseTypeParametersDeclaration(iterator, myFirstPassData);
result.superName = SignatureParsing.parseTopLevelClassRefSignature(iterator, myFirstPassData);
String superName = SignatureParsing.parseTopLevelClassRefSignature(iterator, myFirstPassData);
result.superType = superName == null ? null : new TypeInfo(superName);
while (iterator.current() != CharacterIterator.DONE) {
String name = SignatureParsing.parseTopLevelClassRefSignature(iterator, myFirstPassData);
if (name == null) throw new ClsFormatException();
if (result.interfaceNames == null) result.interfaceNames = new SmartList<>();
result.interfaceNames.add(name);
if (result.interfaces == null) result.interfaces = new SmartList<>();
result.interfaces.add(new TypeInfo(name));
}
return result;
}
private ClassInfo parseClassDescription(String superClass, String[] superInterfaces) {
ClassInfo result = new ClassInfo();
result.typeParameters = ContainerUtil.emptyList();
result.superName = superClass != null ? myFirstPassData.mapJvmClassNameToJava(superClass) : null;
result.interfaceNames = superInterfaces == null ? null : ContainerUtil.map(superInterfaces, myFirstPassData);
result.typeParameters = TypeParametersDeclaration.EMPTY;
result.superType = superClass != null ? new TypeInfo(myFirstPassData.mapJvmClassNameToJava(superClass)) : null;
result.interfaces = myFirstPassData.createTypes(superInterfaces);
return result;
}
private static void newTypeParameterList(StubElement<?> parent, List<Pair<String, String[]>> parameters) {
PsiTypeParameterListStub listStub = new PsiTypeParameterListStubImpl(parent);
for (Pair<String, String[]> parameter : parameters) {
PsiTypeParameterStub parameterStub = new PsiTypeParameterStubImpl(listStub, parameter.first);
newReferenceList(JavaStubElementTypes.EXTENDS_BOUND_LIST, parameterStub, parameter.second);
}
}
private static void newReferenceList(@NotNull JavaClassReferenceListElementType type, StubElement<?> parent, String @NotNull [] types) {
new PsiClassReferenceListStubImpl(type, parent, types);
private static void newReferenceList(@NotNull JavaClassReferenceListElementType type, StubElement<?> parent, @Nullable List<TypeInfo> types) {
new PsiClassReferenceListStubImpl(type, parent, types == null ? TypeInfo.EMPTY_ARRAY : types.toArray(TypeInfo.EMPTY_ARRAY));
}
private static int packCommonFlags(int access) {
@@ -244,6 +238,37 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
return new AnnotationTextCollector(desc, myFirstPassData, text -> new PsiAnnotationStubImpl(myModList, text));
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
TypeReference ref = new TypeReference(typeRef);
TypeInfo info = null;
if (ref.getSort() == TypeReference.CLASS_TYPE_PARAMETER_BOUND) {
info = myClassInfo.typeParameters.getBoundType(ref);
}
else if (ref.getSort() == TypeReference.CLASS_EXTENDS) {
int index = ref.getSuperTypeIndex();
if (index == -1) {
info = myClassInfo.superType;
}
else if (index >= 0 && index < myClassInfo.interfaces.size()) {
info = myClassInfo.interfaces.get(index);
}
}
if (info == null) return null;
if (myAnnoBuilders == null) {
myAnnoBuilders = new HashMap<>();
}
return myAnnoBuilders.computeIfAbsent(info, typeInfo -> new TypeAnnotationContainer.Builder(typeInfo, myFirstPassData))
.collect(typePath, desc);
}
@Override
public void visitEnd() {
if (myAnnoBuilders != null) {
myAnnoBuilders.values().forEach(TypeAnnotationContainer.Builder::build);
}
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (isSet(access, Opcodes.ACC_SYNTHETIC)) return;
@@ -369,7 +394,7 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
PsiModifierListStub modList = new PsiModifierListStubImpl(stub, packMethodFlags(access, myResult.isInterface()));
newTypeParameterList(stub, info.typeParameters);
info.typeParameters.createTypeParameterList(stub);
boolean isEnumConstructor = isConstructor && isEnum;
boolean isInnerClassConstructor = isConstructor && !isEnum && isInner() && !isGroovyClosure(canonicalMethodName);
@@ -398,11 +423,11 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
new PsiModifierListStubImpl(parameterStub, 0);
}
newReferenceList(JavaStubElementTypes.THROWS_LIST, stub, ArrayUtilRt.toStringArray(info.throwTypes));
newReferenceList(JavaStubElementTypes.THROWS_LIST, stub, info.throwTypes);
int paramIgnoreCount = isEnumConstructor ? 2 : isInnerClassConstructor ? 1 : 0;
int localVarIgnoreCount = isEnumConstructor ? 3 : isInnerClassConstructor ? 2 : !isStatic ? 1 : 0;
return new MethodAnnotationCollectingVisitor(stub, modList, paramStubs, paramIgnoreCount, localVarIgnoreCount, myFirstPassData);
return new MethodAnnotationCollectingVisitor(stub, info, modList, paramStubs, paramIgnoreCount, localVarIgnoreCount, myFirstPassData);
}
private boolean isInner() {
@@ -447,11 +472,11 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
while (iterator.current() == '^') {
iterator.next();
if (result.throwTypes == null) result.throwTypes = new SmartList<>();
result.throwTypes.add(SignatureParsing.parseTypeString(iterator, myFirstPassData));
result.throwTypes.add(new TypeInfo(SignatureParsing.parseTypeString(iterator, myFirstPassData)));
}
if (exceptions != null && (result.throwTypes == null || exceptions.length > result.throwTypes.size())) {
// a signature may be inconsistent with exception list - in this case, the more complete list takes precedence
result.throwTypes = ContainerUtil.map(exceptions, myFirstPassData);
result.throwTypes = myFirstPassData.createTypes(exceptions);
}
return result;
@@ -459,25 +484,25 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
private MethodInfo parseMethodDescription(String desc, String[] exceptions) {
MethodInfo result = new MethodInfo();
result.typeParameters = ContainerUtil.emptyList();
result.typeParameters = TypeParametersDeclaration.EMPTY;
result.returnType = toJavaType(Type.getReturnType(desc), myFirstPassData);
result.argTypes = ContainerUtil.map(Type.getArgumentTypes(desc), type -> toJavaType(type, myFirstPassData));
result.throwTypes = exceptions == null ? null : ContainerUtil.map(exceptions, myFirstPassData);
result.throwTypes = myFirstPassData.createTypes(exceptions);
return result;
}
private static class ClassInfo {
private List<Pair<String, String[]>> typeParameters;
private String superName;
private List<String> interfaceNames;
private TypeParametersDeclaration typeParameters;
private TypeInfo superType;
private List<TypeInfo> interfaces;
}
private static class MethodInfo {
private List<Pair<String, String[]>> typeParameters;
private TypeParametersDeclaration typeParameters;
private String returnType;
private List<String> argTypes;
private List<String> throwTypes;
private List<TypeInfo> throwTypes;
}
private static final class FieldAnnotationCollectingVisitor extends FieldVisitor {
@@ -546,8 +571,8 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
private static final class MethodAnnotationCollectingVisitor extends MethodVisitor {
private final PsiMethodStub myOwner;
@NotNull
private final PsiModifierListStub myModList;
private final @NotNull MethodInfo myMethodInfo;
private final @NotNull PsiModifierListStub myModList;
private final PsiParameterStubImpl[] myParamStubs;
private final int myParamCount;
private final int myLocalVarIgnoreCount;
@@ -559,6 +584,7 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
private Map<TypeInfo, TypeAnnotationContainer.Builder> myAnnoBuilders;
private MethodAnnotationCollectingVisitor(PsiMethodStub owner,
@NotNull MethodInfo methodInfo,
@NotNull PsiModifierListStub modList,
PsiParameterStubImpl[] paramStubs,
int paramIgnoreCount,
@@ -566,6 +592,7 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
@NotNull FirstPassData firstPassData) {
super(ASM_API);
myOwner = owner;
myMethodInfo = methodInfo;
myModList = modList;
myParamStubs = paramStubs;
myParamCount = paramStubs.length;
@@ -609,6 +636,15 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
info = myParamStubs[parameterIndex].getType(false);
}
}
else if (ref.getSort() == TypeReference.METHOD_TYPE_PARAMETER_BOUND) {
info = myMethodInfo.typeParameters.getBoundType(ref);
}
else if (ref.getSort() == TypeReference.THROWS) {
int index = ref.getExceptionIndex();
if (index < myMethodInfo.throwTypes.size()) {
info = myMethodInfo.throwTypes.get(index);
}
}
if (info == null) return null;
if (myAnnoBuilders == null) {
myAnnoBuilders = new HashMap<>();
@@ -7,6 +7,8 @@ import com.intellij.lang.LighterASTNode;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.PsiReferenceList;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.compiled.TypeAnnotationContainer;
import com.intellij.psi.impl.java.stubs.impl.PsiClassReferenceListStubImpl;
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
import com.intellij.psi.impl.source.PsiReferenceListImpl;
@@ -18,6 +20,7 @@ import com.intellij.psi.stubs.StubInputStream;
import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@@ -56,10 +59,16 @@ public abstract class JavaClassReferenceListElementType extends JavaStubElementT
@Override
public void serialize(@NotNull PsiClassReferenceListStub stub, @NotNull StubOutputStream dataStream) throws IOException {
String[] names = stub.getReferencedNames();
dataStream.writeVarInt(names.length);
for (String name : names) {
dataStream.writeName(name);
TypeInfo[] types = stub.getTypes();
boolean hasAnnotations = ContainerUtil.exists(types, info -> !info.getTypeAnnotations().isEmpty());
dataStream.writeVarInt(hasAnnotations ? -types.length : types.length);
for (TypeInfo info : types) {
dataStream.writeName(info.text);
}
if (hasAnnotations) {
for (TypeInfo info : types) {
TypeAnnotationContainer.writeTypeAnnotations(dataStream, info.getTypeAnnotations());
}
}
}
@@ -67,11 +76,18 @@ public abstract class JavaClassReferenceListElementType extends JavaStubElementT
@Override
public PsiClassReferenceListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
int len = dataStream.readVarInt();
String[] names = ArrayUtil.newStringArray(len);
for (int i = 0; i < names.length; i++) {
names[i] = dataStream.readNameString();
boolean hasAnnotations = len < 0;
len = Math.abs(len);
TypeInfo[] infos = new TypeInfo[len];
for (int i = 0; i < infos.length; i++) {
infos[i] = new TypeInfo(dataStream.readNameString());
}
return new PsiClassReferenceListStubImpl(this, parentStub, names);
if (hasAnnotations) {
for (int i = 0; i < len; i++) {
infos[i].setTypeAnnotations(TypeAnnotationContainer.readTypeAnnotations(dataStream));
}
}
return new PsiClassReferenceListStubImpl(this, parentStub, infos);
}
@Override
@@ -87,9 +103,9 @@ public abstract class JavaClassReferenceListElementType extends JavaStubElementT
}
if (role == PsiReferenceList.Role.EXTENDS_LIST) {
StubElement parentStub = stub.getParentStub();
StubElement<?> parentStub = stub.getParentStub();
if (parentStub instanceof PsiClassStub) {
PsiClassStub psiClassStub = (PsiClassStub)parentStub;
PsiClassStub<?> psiClassStub = (PsiClassStub<?>)parentStub;
if (psiClassStub.isEnum()) {
sink.occurrence(JavaStubIndexKeys.SUPER_CLASSES, "Enum");
}
@@ -21,11 +21,25 @@ package com.intellij.psi.impl.java.stubs;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiReferenceList;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.stubs.StubElement;
import org.jetbrains.annotations.NotNull;
public interface PsiClassReferenceListStub extends StubElement<PsiReferenceList> {
/**
* @return types
*/
PsiClassType @NotNull [] getReferencedTypes();
/**
* @return type names
*/
String @NotNull [] getReferencedNames();
/**
* @return array of TypeInfo objects
*/
@NotNull TypeInfo @NotNull [] getTypes();
@NotNull PsiReferenceList.Role getRole();
}
@@ -1,26 +1,38 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl.java.stubs.impl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.compiled.ClsJavaCodeReferenceElementImpl;
import com.intellij.psi.impl.compiled.TypeAnnotationContainer;
import com.intellij.psi.impl.java.stubs.JavaClassReferenceListElementType;
import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
public class PsiClassReferenceListStubImpl extends StubBase<PsiReferenceList> implements PsiClassReferenceListStub {
private final String[] myNames;
private volatile PsiClassType[] myTypes;
private final TypeInfo @NotNull [] myInfos;
private volatile PsiClassType [] myTypes;
public PsiClassReferenceListStubImpl(@NotNull JavaClassReferenceListElementType type, StubElement parent, String @NotNull [] names) {
this(type, parent, ContainerUtil.map2Array(names, TypeInfo.class, TypeInfo::new));
}
public PsiClassReferenceListStubImpl(@NotNull JavaClassReferenceListElementType type, StubElement parent,
TypeInfo @NotNull [] infos) {
super(parent, type);
ObjectUtils.assertAllElementsNotNull(names);
myNames = names;
for (TypeInfo info : infos) {
if (info == null) throw new IllegalArgumentException();
if (info.text == null) throw new IllegalArgumentException();
}
myInfos = infos;
}
@Override
@@ -31,14 +43,24 @@ public class PsiClassReferenceListStubImpl extends StubBase<PsiReferenceList> im
}
return types.clone();
}
private boolean shouldSkipSoleObject() {
final boolean compiled = ((JavaClassReferenceListElementType)getStubType()).isCompiled(this);
return compiled && myInfos.length == 1 && myInfos[0].text.equals(CommonClassNames.JAVA_LANG_OBJECT) &&
myInfos[0].getTypeAnnotations().isEmpty();
}
private PsiClassType @NotNull [] createTypes() {
PsiClassType[] types = myNames.length == 0 ? PsiClassType.EMPTY_ARRAY : new PsiClassType[myNames.length];
PsiClassType[] types = myInfos.length == 0 ? PsiClassType.EMPTY_ARRAY : new PsiClassType[myInfos.length];
final boolean compiled = ((JavaClassReferenceListElementType)getStubType()).isCompiled(this);
if (compiled) {
if (shouldSkipSoleObject()) return PsiClassType.EMPTY_ARRAY;
for (int i = 0; i < types.length; i++) {
types[i] = new PsiClassReferenceType(new ClsJavaCodeReferenceElementImpl(getPsi(), myNames[i]), null);
TypeInfo info = myInfos[i];
TypeAnnotationContainer annotations = info.getTypeAnnotations();
ClsJavaCodeReferenceElementImpl reference = new ClsJavaCodeReferenceElementImpl(getPsi(), info.text, annotations);
types[i] = new PsiClassReferenceType(reference, null).annotate(annotations.getProvider(reference));
}
}
else {
@@ -48,7 +70,7 @@ public class PsiClassReferenceListStubImpl extends StubBase<PsiReferenceList> im
final PsiReferenceList psi = getPsi();
for (int i = 0; i < types.length; i++) {
try {
final PsiJavaCodeReferenceElement ref = factory.createReferenceFromText(myNames[i], psi);
final PsiJavaCodeReferenceElement ref = factory.createReferenceFromText(myInfos[i].text, psi);
((PsiJavaCodeReferenceElementImpl)ref).setKindWhenDummy(PsiJavaCodeReferenceElementImpl.Kind.CLASS_NAME_KIND);
types[i] = factory.createType(ref);
}
@@ -72,7 +94,14 @@ public class PsiClassReferenceListStubImpl extends StubBase<PsiReferenceList> im
@Override
public String @NotNull [] getReferencedNames() {
return myNames.clone();
if (myInfos.length == 0 || shouldSkipSoleObject()) return ArrayUtil.EMPTY_STRING_ARRAY;
return ContainerUtil.map2Array(myInfos, String.class, info -> info.text);
}
@Override
public @NotNull TypeInfo @NotNull [] getTypes() {
if (shouldSkipSoleObject()) return TypeInfo.EMPTY_ARRAY;
return myInfos.clone();
}
@NotNull
@@ -83,6 +112,6 @@ public class PsiClassReferenceListStubImpl extends StubBase<PsiReferenceList> im
@Override
public String toString() {
return "PsiRefListStub[" + getRole() + ':' + String.join(", ", myNames) + ']';
return "PsiRefListStub[" + getRole() + ':' + String.join(", ", getReferencedNames()) + ']';
}
}
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class JavaFileElementType extends ILightStubFileElementType<PsiJavaFileStub> {
public static final int STUB_VERSION = 49;
public static final int STUB_VERSION = 50;
public JavaFileElementType() {
super("java.FILE", JavaLanguage.INSTANCE);
@@ -0,0 +1,9 @@
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package pkg;
public class ExtendsObjectAnnotated extends java.lang.@pkg.TA Object {
public ExtendsObjectAnnotated() { /* compiled code */ }
}
@@ -4,7 +4,7 @@
package pkg;
public interface TypeAnno <T> extends java.io.Serializable {
public interface TypeAnno <T extends java.lang.@pkg.TA(11) Object> extends java.io.@pkg.TA(12) Serializable, java.lang.@pkg.TA(13) Comparable<@pkg.TA(14) T> {
java.lang.@pkg.TA(0) String field = "";
java.util.@pkg.TA(-1) List<java.lang.@pkg.TA String> get1TypeParam();
@@ -31,9 +31,11 @@ public interface TypeAnno <T> extends java.io.Serializable {
java.util.@pkg.TA(0) List<@pkg.TA(1) ? super java.lang.@pkg.TA(2) String @pkg.TA(3) []> getSuperArrayInList();
<M extends java.lang.CharSequence> @pkg.TA(1) M getTypeParameter();
<M extends java.lang.@pkg.TA(0) CharSequence> @pkg.TA(1) M getTypeParameter();
<M extends java.lang.CharSequence & java.io.Serializable> @pkg.TA(2) M getTypeParameter2();
<M extends java.lang.@pkg.TA(0) CharSequence & java.io.@pkg.TA(1) Serializable> @pkg.TA(2) M getTypeParameter2();
<M extends java.lang.@pkg.TA(0) Object & java.util.@pkg.TA(1) List<@pkg.TA(2) ? super java.lang.@pkg.TA(3) String @pkg.TA(4) []>, N extends java.lang.@pkg.TA(5) Object, P> @pkg.TA(6) M getTypeParameterComplex();
pkg.@pkg.TA(0) Outer.@pkg.TA(1) Middle.@pkg.TA(2) Inner getInner();
@@ -45,7 +47,7 @@ public interface TypeAnno <T> extends java.io.Serializable {
pkg.@pkg.TA(0) Outer<pkg.@pkg.TA(1) Outer @pkg.TA(2) []>.@pkg.TA(3) Middle<@pkg.TA(4) ? extends pkg.@pkg.TA(5) Outer.@pkg.TA(6) Middle.@pkg.TA(7) Inner @pkg.TA(8) []>.@pkg.TA(9) Inner<pkg.@pkg.TA(10) Outer<@pkg.TA(11) ?>.@pkg.TA(12) Middle<@pkg.TA(13) ? super pkg.@pkg.TA(14) Outer>.@pkg.TA(15) Inner<@pkg.TA(16) ?> @pkg.TA(17) []> @pkg.TA(18) [] getMixed();
void inThrows() throws java.lang.Exception, java.lang.Error;
void inThrows() throws java.lang.@pkg.TA(0) Exception, java.lang.@pkg.TA(1) Error;
void inParameter(java.util.@pkg.TA(0) List<java.lang.@pkg.TA(1) String> list, java.util.@pkg.TA(2) List<java.lang.@pkg.TA(3) String> list1);
@@ -0,0 +1,8 @@
package pkg;
import java.lang.annotation.*;
public class ExtendsObjectAnnotated extends @TA Object {}
@Target(ElementType.TYPE_USE)
@interface TA {}
@@ -6,7 +6,7 @@ import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
public interface TypeAnno<@TA(10) T extends @TA(11) Object> extends @TA(12) Serializable {
public interface TypeAnno<@TA(10) T extends @TA(11) Object> extends @TA(12) Serializable, @TA(13) Comparable<@TA(14) T> {
@TA(-1) List<@TA String> get1TypeParam();
@TA(-1) Map<@TA(0) String, @TA(1) String> get2TypeParams();
@@ -35,6 +35,8 @@ public interface TypeAnno<@TA(10) T extends @TA(11) Object> extends @TA(12) Seri
<M extends @TA(0) CharSequence> @TA(1) M getTypeParameter();
<M extends @TA(0) CharSequence & @TA(1) Serializable> @TA(2) M getTypeParameter2();
<M extends @TA(0) Object & @TA(1) List<@TA(2) ? super @TA(3) String @TA(4) []>, N extends @TA(5) Object, P extends Object> @TA(6) M getTypeParameterComplex();
@TA(0) Outer.@TA(1) Middle.@TA(2) Inner getInner();
@@ -3,7 +3,7 @@ PsiJavaFileStub []
PsiModifierListStub[mask=1025]
PsiTypeParameterListStub
PsiTypeParameter[T]
PsiRefListStub[EXTENDS_BOUNDS_LIST:]
PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object]
PsiRefListStub[EXTENDS_LIST:java.io.Serializable]
PsiRefListStub[IMPLEMENTS_LIST:]
PsiFieldStub[field:java.lang.String=""]
@@ -53,6 +53,7 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase {
public void testSuspiciousParameterNames() { doTest(); }
public void testTypeAnnotations() { doTest(); }
public void testTypeAnno() { doTest(); }
public void testExtendsObjectAnnotated() { doTest(); }
public void testRecordTest() { doTest(); }
public void testTextPsiMismatch() {