diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/cache/TypeInfo.java b/java/java-psi-impl/src/com/intellij/psi/impl/cache/TypeInfo.java index 4725b042780d..f1de5d3341d6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/cache/TypeInfo.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/cache/TypeInfo.java @@ -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 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; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsReferenceListImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsReferenceListImpl.java index b75ac177c594..17f5ba2d5ec2 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsReferenceListImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsReferenceListImpl.java @@ -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 implements PsiReferenceList { - private static final ClsJavaCodeReferenceElementImpl[] EMPTY_REFS_ARRAY = new ClsJavaCodeReferenceElementImpl[0]; - - private final NotNullLazyValue myRefs; + private final NotNullLazyValue myRefs; public ClsReferenceListImpl(@NotNull PsiClassReferenceListStub stub) { super(stub); - myRefs = new AtomicNotNullLazyValue() { + myRefs = new AtomicNotNullLazyValue() { @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 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 ClsRepositoryPsiElementtreeToPsiNotNull(element).getReferenceElements()); + PsiJavaCodeReferenceElement[] mirrorRefs = SourceTreeToPsiMap.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 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 diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/FirstPassData.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/FirstPassData.java index abbf6c46cda0..cc609f68bbd5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/FirstPassData.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/FirstPassData.java @@ -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 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 diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java index 859406ce7a53..58eb1b8b26a9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java @@ -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 myDeclarations; - @NotNull - public static List> parseTypeParametersDeclaration(CharacterIterator signature, Function mapping) throws ClsFormatException { - if (signature.current() != '<') { - return Collections.emptyList(); + private TypeParametersDeclaration(List declarations) { + myDeclarations = declarations; } - List> 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 mapping) throws ClsFormatException { + if (signature.current() != '<') { + return TypeParametersDeclaration.EMPTY; + } + + List typeParameters = new ArrayList<>(); signature.next(); while (signature.current() != '>') { typeParameters.add(parseTypeParameter(signature, mapping)); } signature.next(); - return typeParameters; + return new TypeParametersDeclaration(typeParameters); } - private static Pair parseTypeParameter(CharacterIterator signature, Function mapping) throws ClsFormatException { + private static TypeParameterDeclaration parseTypeParameter(CharacterIterator signature, Function 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 bounds = null; - boolean jlo = false; + List 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 diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java index 893bc16c833a..492a5af6500c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java @@ -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 extends ClassVisitor { private PsiClassStub myResult; private PsiModifierListStub myModList; private PsiRecordHeaderStub myHeaderStub; + private Map myAnnoBuilders; + private ClassInfo myClassInfo; public StubBuildingVisitor(T classSource, InnerClassSourceStrategy innersStrategy, StubElement parent, int access, String shortName) { this(classSource, innersStrategy, parent, access, shortName, false, false); @@ -105,38 +107,37 @@ public class StubBuildingVisitor 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 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> parameters) { - PsiTypeParameterListStub listStub = new PsiTypeParameterListStubImpl(parent); - for (Pair 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 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 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 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 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 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 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> typeParameters; - private String superName; - private List interfaceNames; + private TypeParametersDeclaration typeParameters; + private TypeInfo superType; + private List interfaces; } private static class MethodInfo { - private List> typeParameters; + private TypeParametersDeclaration typeParameters; private String returnType; private List argTypes; - private List throwTypes; + private List throwTypes; } private static final class FieldAnnotationCollectingVisitor extends FieldVisitor { @@ -546,8 +571,8 @@ public class StubBuildingVisitor 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 extends ClassVisitor { private Map myAnnoBuilders; private MethodAnnotationCollectingVisitor(PsiMethodStub owner, + @NotNull MethodInfo methodInfo, @NotNull PsiModifierListStub modList, PsiParameterStubImpl[] paramStubs, int paramIgnoreCount, @@ -566,6 +592,7 @@ public class StubBuildingVisitor 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 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<>(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java index b904319702e6..d0d9bf65cec0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java @@ -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"); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/PsiClassReferenceListStub.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/PsiClassReferenceListStub.java index 6e2711b26d3a..627e7b3bc48b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/PsiClassReferenceListStub.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/PsiClassReferenceListStub.java @@ -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 { + /** + * @return types + */ PsiClassType @NotNull [] getReferencedTypes(); + + /** + * @return type names + */ String @NotNull [] getReferencedNames(); + + /** + * @return array of TypeInfo objects + */ + @NotNull TypeInfo @NotNull [] getTypes(); + @NotNull PsiReferenceList.Role getRole(); } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java index f2cd0a91dcae..fda57e3321df 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java @@ -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 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 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 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 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 im @Override public String toString() { - return "PsiRefListStub[" + getRole() + ':' + String.join(", ", myNames) + ']'; + return "PsiRefListStub[" + getRole() + ':' + String.join(", ", getReferencedNames()) + ']'; } } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java index eb357d793fa7..3d7b59f8f98f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; public class JavaFileElementType extends ILightStubFileElementType { - public static final int STUB_VERSION = 49; + public static final int STUB_VERSION = 50; public JavaFileElementType() { super("java.FILE", JavaLanguage.INSTANCE); diff --git a/java/java-tests/testData/psi/cls/mirror/ExtendsObjectAnnotated.txt b/java/java-tests/testData/psi/cls/mirror/ExtendsObjectAnnotated.txt new file mode 100644 index 000000000000..1205c70a8be1 --- /dev/null +++ b/java/java-tests/testData/psi/cls/mirror/ExtendsObjectAnnotated.txt @@ -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 */ } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/cls/mirror/TypeAnno.txt b/java/java-tests/testData/psi/cls/mirror/TypeAnno.txt index e7a13020b2fe..6738dbab0d21 100644 --- a/java/java-tests/testData/psi/cls/mirror/TypeAnno.txt +++ b/java/java-tests/testData/psi/cls/mirror/TypeAnno.txt @@ -4,7 +4,7 @@ package pkg; -public interface TypeAnno extends java.io.Serializable { +public interface TypeAnno 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 get1TypeParam(); @@ -31,9 +31,11 @@ public interface TypeAnno extends java.io.Serializable { java.util.@pkg.TA(0) List<@pkg.TA(1) ? super java.lang.@pkg.TA(2) String @pkg.TA(3) []> getSuperArrayInList(); - @pkg.TA(1) M getTypeParameter(); + @pkg.TA(1) M getTypeParameter(); - @pkg.TA(2) M getTypeParameter2(); + @pkg.TA(2) M getTypeParameter2(); + + , 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 extends java.io.Serializable { pkg.@pkg.TA(0) Outer.@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.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 list, java.util.@pkg.TA(2) List list1); diff --git a/java/java-tests/testData/psi/cls/mirror/pkg/ExtendsObjectAnnotated.class b/java/java-tests/testData/psi/cls/mirror/pkg/ExtendsObjectAnnotated.class new file mode 100644 index 000000000000..e230074d3c56 Binary files /dev/null and b/java/java-tests/testData/psi/cls/mirror/pkg/ExtendsObjectAnnotated.class differ diff --git a/java/java-tests/testData/psi/cls/mirror/pkg/TypeAnno.class b/java/java-tests/testData/psi/cls/mirror/pkg/TypeAnno.class index 7d6825ea7c72..327e73f7bf5d 100644 Binary files a/java/java-tests/testData/psi/cls/mirror/pkg/TypeAnno.class and b/java/java-tests/testData/psi/cls/mirror/pkg/TypeAnno.class differ diff --git a/java/java-tests/testData/psi/cls/mirror/src/pkg/ExtendsObjectAnnotated.java b/java/java-tests/testData/psi/cls/mirror/src/pkg/ExtendsObjectAnnotated.java new file mode 100644 index 000000000000..4f9de3d8162f --- /dev/null +++ b/java/java-tests/testData/psi/cls/mirror/src/pkg/ExtendsObjectAnnotated.java @@ -0,0 +1,8 @@ +package pkg; + +import java.lang.annotation.*; + +public class ExtendsObjectAnnotated extends @TA Object {} + +@Target(ElementType.TYPE_USE) +@interface TA {} \ No newline at end of file diff --git a/java/java-tests/testData/psi/cls/mirror/src/pkg/TypeAnno.java b/java/java-tests/testData/psi/cls/mirror/src/pkg/TypeAnno.java index ead515b467dc..1e9c90fcbe9d 100644 --- a/java/java-tests/testData/psi/cls/mirror/src/pkg/TypeAnno.java +++ b/java/java-tests/testData/psi/cls/mirror/src/pkg/TypeAnno.java @@ -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 @TA(1) M getTypeParameter(); @TA(2) M getTypeParameter2(); + + , N extends @TA(5) Object, P extends Object> @TA(6) M getTypeParameterComplex(); @TA(0) Outer.@TA(1) Middle.@TA(2) Inner getInner(); diff --git a/java/java-tests/testData/psi/cls/stubBuilder/TypeAnno.txt b/java/java-tests/testData/psi/cls/stubBuilder/TypeAnno.txt index fee6da0b7659..a088ebdf90f2 100644 --- a/java/java-tests/testData/psi/cls/stubBuilder/TypeAnno.txt +++ b/java/java-tests/testData/psi/cls/stubBuilder/TypeAnno.txt @@ -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=""] diff --git a/java/java-tests/testSrc/com/intellij/java/psi/ClsMirrorBuildingTest.java b/java/java-tests/testSrc/com/intellij/java/psi/ClsMirrorBuildingTest.java index 3dbe69c81b8c..e302860c6aa1 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/ClsMirrorBuildingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/ClsMirrorBuildingTest.java @@ -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() {