[java-stubs] TypeInfoProvider; remove most of string-centered API from SignatureParsing

GitOrigin-RevId: ea132fb685c8ef26d442c04a14969a85feb09294
This commit is contained in:
Tagir Valeev
2023-09-14 11:20:09 +02:00
committed by intellij-monorepo-bot
parent 9478b2519d
commit 06f32dedad
8 changed files with 63 additions and 211 deletions

View File

@@ -2,21 +2,22 @@
package com.intellij.psi.impl.compiled;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.compiled.SignatureParsing.TypeInfoProvider;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
final class AnnotationTextCollector extends AnnotationVisitor {
private final StringBuilder myBuilder = new StringBuilder();
private final Function<? super String, String> myMapping;
private final @NotNull StringBuilder myBuilder = new StringBuilder();
private final @NotNull TypeInfoProvider myMapping;
private final Consumer<? super String> myCallback;
private boolean hasPrefix;
private boolean hasParams;
AnnotationTextCollector(@Nullable String desc, Function<? super String, String> mapping, Consumer<? super String> callback) {
AnnotationTextCollector(@Nullable String desc, @NotNull TypeInfoProvider mapping, Consumer<? super String> callback) {
super(Opcodes.API_VERSION);
myMapping = mapping;
myCallback = callback;

View File

@@ -21,7 +21,7 @@ import static com.intellij.util.BitUtil.isSet;
/**
* Information retrieved during the first pass of a class file parsing
*/
class FirstPassData implements Function<@NotNull String, @NotNull String> {
class FirstPassData implements SignatureParsing.TypeInfoProvider {
private static final Logger LOG = Logger.getInstance(FirstPassData.class);
private abstract static class InnerClassEntry {}
@@ -69,11 +69,6 @@ class FirstPassData implements Function<@NotNull String, @NotNull String> {
mySealed = sealed;
}
@Override
public @NotNull String fun(@NotNull String jvmName) {
return toTypeInfo(jvmName).text();
}
/**
* @param componentName record component name
* @return true if given component is var-arg
@@ -112,13 +107,14 @@ class FirstPassData implements Function<@NotNull String, @NotNull String> {
* @param jvmName JVM class name like java/util/Map$Entry
* @return Java class name like java.util.Map.Entry
*/
@NotNull RefTypeInfo toTypeInfo(@NotNull String jvmName) {
@Override
public @NotNull RefTypeInfo toTypeInfo(@NotNull String jvmName) {
return toTypeInfo(jvmName, true);
}
/**
* @param jvmName JVM class name like java/util/Map$Entry
* @param useGuesser if true, {@link StubBuildingVisitor#GUESSING_MAPPER} will be used in case if the entry was absent in
* @param useGuesser if true, {@link StubBuildingVisitor#GUESSING_PROVIDER} will be used in case if the entry was absent in
* InnerClasses table.
* @return Java class name like java.util.Map.Entry
*/
@@ -136,7 +132,7 @@ class FirstPassData implements Function<@NotNull String, @NotNull String> {
return new RefTypeInfo(((TypeInfoInnerClassEntry)p).myInnerName, ((TypeInfoInnerClassEntry)p).myOuterType);
}
else if (!jvmName.equals(myTopLevelName) && (useGuesser || !myTrustInnerClasses)) {
return new RefTypeInfo(StubBuildingVisitor.GUESSING_MAPPER.fun(jvmName));
return StubBuildingVisitor.GUESSING_PROVIDER.toTypeInfo(jvmName);
}
}

View File

@@ -1,7 +1,6 @@
// 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.text.StringUtil;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.cache.TypeInfo.TypeKind;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
@@ -25,6 +24,24 @@ import java.util.List;
public final class SignatureParsing {
private SignatureParsing() { }
/**
* A function to map JVM class names to {@link com.intellij.psi.impl.cache.TypeInfo.RefTypeInfo}.
* Normally, this function should take into account probable inner classes. This is done by {@link FirstPassData} implementation.
* If inner classes information is unavailable, use {@link StubBuildingVisitor#GUESSING_PROVIDER} for heuristic-based mapping
*/
@FunctionalInterface
public interface TypeInfoProvider {
@NotNull TypeInfo.RefTypeInfo toTypeInfo(@NotNull String jvmClassName);
/**
* @param fn function that returns Java-style FQN by JVM class name
* @return a provider that delegates to the supplied function. Note that the returned provider never has the inner classes' information.
*/
static TypeInfoProvider from(Function<? super String, String> fn) {
return internalName -> new TypeInfo.RefTypeInfo(fn.apply(internalName));
}
}
public static final class CharIterator {
static final int DONE = '\uFFFF';
@@ -123,7 +140,7 @@ public final class SignatureParsing {
}
@NotNull
static TypeParametersDeclaration parseTypeParametersDeclaration(CharIterator signature, FirstPassData mapping) throws ClsFormatException {
static TypeParametersDeclaration parseTypeParametersDeclaration(CharIterator signature, TypeInfoProvider mapping) throws ClsFormatException {
if (signature.current() != '<') {
return TypeParametersDeclaration.EMPTY;
}
@@ -137,7 +154,7 @@ public final class SignatureParsing {
return new TypeParametersDeclaration(typeParameters);
}
private static TypeParameterDeclaration parseTypeParameter(CharIterator signature, FirstPassData mapping) throws ClsFormatException {
private static TypeParameterDeclaration parseTypeParameter(CharIterator signature, TypeInfoProvider mapping) throws ClsFormatException {
int from = signature.pos();
while (signature.current() != ':' && signature.current() != CharIterator.DONE) {
signature.next();
@@ -146,7 +163,7 @@ public final class SignatureParsing {
if (signature.current() == CharIterator.DONE) {
throw new ClsFormatException();
}
String parameterName = mapping.fun(name);
String parameterName = mapping.toTypeInfo(name).text();
List<TypeInfo> bounds = new SmartList<>();
while (signature.current() == ':') {
@@ -160,7 +177,7 @@ public final class SignatureParsing {
}
@Nullable
static TypeInfo parseTopLevelClassRefSignatureToTypeInfo(CharIterator signature, FirstPassData mapping) throws ClsFormatException {
static TypeInfo parseTopLevelClassRefSignatureToTypeInfo(CharIterator signature, TypeInfoProvider mapping) throws ClsFormatException {
switch (signature.current()) {
case 'L':
return parseParameterizedClassRefSignatureToTypeInfo(signature, mapping);
@@ -189,7 +206,7 @@ public final class SignatureParsing {
return id;
}
private static @NotNull TypeInfo parseParameterizedClassRefSignatureToTypeInfo(CharIterator signature, FirstPassData mapping)
private static @NotNull TypeInfo parseParameterizedClassRefSignatureToTypeInfo(CharIterator signature, TypeInfoProvider mapping)
throws ClsFormatException {
signature.next();
int start = signature.pos();
@@ -244,82 +261,7 @@ public final class SignatureParsing {
}
}
private static String parseParameterizedClassRefSignature(CharIterator signature, Function<? super String, String> mapping) throws ClsFormatException {
StringBuilder canonicalText = null;
signature.next();
int start = signature.pos();
boolean hasSpace = false;
while (true) {
char c = signature.current();
switch (c) {
case ';': {
String javaName;
if (canonicalText == null) {
String jvmName = signature.substring(start);
if (hasSpace) {
jvmName = jvmName.replace(" ", "");
}
javaName = mapping.fun(jvmName);
}
else {
javaName = canonicalText.toString();
}
signature.next();
return javaName;
}
case CharIterator.DONE:
throw new ClsFormatException();
case '<': {
if (canonicalText == null) {
String jvmName = signature.substring(start);
if (hasSpace) {
jvmName = jvmName.replace(" ", "");
}
String javaName = mapping.fun(jvmName);
canonicalText = new StringBuilder(javaName);
}
boolean firstArg = true;
signature.next();
do {
canonicalText.append(firstArg ? '<' : ',').append(parseClassOrTypeVariableElement(signature, mapping));
firstArg = false;
}
while (signature.current() != '>');
canonicalText.append('>');
break;
}
case ' ':
hasSpace = true;
break;
default:
if (canonicalText != null) {
canonicalText.append(c);
}
}
signature.next();
}
}
private static String parseClassOrTypeVariableElement(CharIterator signature, Function<? super String, String> mapping) throws ClsFormatException {
char variance = parseVariance(signature);
if (variance == '*') {
return decorateTypeText(null, variance);
}
int dimensions = parseDimensions(signature);
String text = parseTypeWithoutVariance(signature, mapping);
if (text == null) throw new ClsFormatException();
if (dimensions > 0) {
text += StringUtil.repeat("[]", dimensions);
}
return decorateTypeText(text, variance);
}
private static @NotNull TypeInfo parseClassOrTypeVariableElementToTypeInfo(@NotNull CharIterator signature, FirstPassData mapping)
private static @NotNull TypeInfo parseClassOrTypeVariableElementToTypeInfo(@NotNull CharIterator signature, TypeInfoProvider mapping)
throws ClsFormatException {
char variance = parseVariance(signature);
if (variance == '*') {
@@ -353,25 +295,6 @@ public final class SignatureParsing {
private static final char VARIANCE_NONE = '\0';
private static final char VARIANCE_EXTENDS = '+';
private static final char VARIANCE_SUPER = '-';
private static final char VARIANCE_INVARIANT = '*';
private static final String VARIANCE_EXTENDS_PREFIX = "? extends ";
private static final String VARIANCE_SUPER_PREFIX = "? super ";
private static String decorateTypeText(String canonical, char variance) {
switch (variance) {
case VARIANCE_NONE:
return canonical;
case VARIANCE_EXTENDS:
return VARIANCE_EXTENDS_PREFIX + canonical;
case VARIANCE_SUPER:
return VARIANCE_SUPER_PREFIX + canonical;
case VARIANCE_INVARIANT:
return "?";
default:
assert false : "unknown variance";
return null;
}
}
private static char parseVariance(CharIterator signature) {
char variance;
@@ -402,7 +325,8 @@ public final class SignatureParsing {
}
/**
* @deprecated use {@link #parseTypeString(CharIterator, Function)}, it's more optimal
* @deprecated use {@link #parseTypeStringToTypeInfo(CharIterator, TypeInfoProvider)}, it's more optimal and produces structured
* object instead of simple string
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
@@ -419,26 +343,18 @@ public final class SignatureParsing {
signature.next();
}
CharIterator iterator = new CharIterator(sb.toString());
String result = parseTypeString(iterator, mapping);
String result = parseTypeStringToTypeInfo(iterator, TypeInfoProvider.from(mapping)).text();
signature.setIndex(iterator.pos() + pos);
return result;
}
@NotNull
public static String parseTypeString(CharIterator signature, Function<? super String, String> mapping) throws ClsFormatException {
int dimensions = parseDimensions(signature);
String text = parseTypeWithoutVariance(signature, mapping);
if (text == null) throw new ClsFormatException();
if (dimensions > 0) {
text += StringUtil.repeat("[]", dimensions);
}
return text;
}
static @NotNull TypeInfo parseTypeStringToTypeInfo(@NotNull CharIterator signature, @NotNull FirstPassData mapping) throws ClsFormatException {
/**
* @param signature iterator for signature. It will be moved to the end of parsed signature
* @param mapping provider to map JVM types to {@link TypeInfo} objects
* @return a parsed {@link TypeInfo} object
* @throws ClsFormatException if signature cannot be parsed
*/
public static @NotNull TypeInfo parseTypeStringToTypeInfo(@NotNull CharIterator signature, @NotNull TypeInfoProvider mapping) throws ClsFormatException {
int dimensions = parseDimensions(signature);
TypeInfo type = parseTypeWithoutVarianceToTypeInfo(signature, mapping);
@@ -452,69 +368,7 @@ public final class SignatureParsing {
}
@Nullable
private static String parseTypeWithoutVariance(CharIterator signature, Function<? super String, String> mapping) throws ClsFormatException {
String text = null;
switch (signature.current()) {
case 'L':
text = parseParameterizedClassRefSignature(signature, mapping);
break;
case 'T':
text = parseTypeVariableRefSignature(signature);
break;
case 'B':
text = "byte";
signature.next();
break;
case 'C':
text = "char";
signature.next();
break;
case 'D':
text = "double";
signature.next();
break;
case 'F':
text = "float";
signature.next();
break;
case 'I':
text = "int";
signature.next();
break;
case 'J':
text = "long";
signature.next();
break;
case 'S':
text = "short";
signature.next();
break;
case 'Z':
text = "boolean";
signature.next();
break;
case 'V':
text = "void";
signature.next();
break;
}
return text;
}
@Nullable
private static TypeInfo parseTypeWithoutVarianceToTypeInfo(CharIterator signature, FirstPassData mapping) throws ClsFormatException {
private static TypeInfo parseTypeWithoutVarianceToTypeInfo(CharIterator signature, TypeInfoProvider mapping) throws ClsFormatException {
switch (signature.current()) {
case 'L':
return parseParameterizedClassRefSignatureToTypeInfo(signature, mapping);

View File

@@ -4,9 +4,9 @@ package com.intellij.psi.impl.compiled;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
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.TypeInfoProvider;
import com.intellij.psi.impl.compiled.SignatureParsing.TypeParametersDeclaration;
import com.intellij.psi.impl.java.stubs.*;
import com.intellij.psi.impl.java.stubs.impl.*;
@@ -707,7 +707,7 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
}
@Nullable
static String constToString(@Nullable Object value, TypeInfo type, boolean anno, Function<? super String, String> mapping) {
static String constToString(@Nullable Object value, TypeInfo type, boolean anno, TypeInfoProvider mapping) {
if (value == null) return null;
if (value instanceof String) {
@@ -781,13 +781,13 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
return null;
}
static String toJavaType(Type type, @NotNull Function<? super String, String> mapping) {
static String toJavaType(Type type, @NotNull TypeInfoProvider mapping) {
int dimensions = 0;
if (type.getSort() == Type.ARRAY) {
dimensions = type.getDimensions();
type = type.getElementType();
}
String text = type.getSort() == Type.OBJECT ? mapping.fun(type.getInternalName()) : type.getClassName();
String text = type.getSort() == Type.OBJECT ? mapping.toTypeInfo(type.getInternalName()).text() : type.getClassName();
if (dimensions > 0) text += StringUtil.repeat("[]", dimensions);
return text;
}
@@ -829,7 +829,9 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
return canonicalText.replace('/', '.');
};
public static final TypeInfoProvider GUESSING_PROVIDER = TypeInfoProvider.from(GUESSING_MAPPER);
public static AnnotationVisitor getAnnotationTextCollector(String desc, Consumer<? super String> consumer) {
return new AnnotationTextCollector(desc, GUESSING_MAPPER, consumer);
return new AnnotationTextCollector(desc, GUESSING_PROVIDER, consumer);
}
}

View File

@@ -5,6 +5,7 @@ import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.compiled.SignatureParsing;
import com.intellij.psi.impl.compiled.StubBuildingVisitor;
import com.intellij.util.cls.ClsFormatException;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.text.StringCharacterIterator;
@@ -38,8 +39,8 @@ public class SignatureParsingTest {
private static String parseTypeString(String signature) throws ClsFormatException {
String oldStyle = SignatureParsing.parseTypeString(new StringCharacterIterator(signature), StubBuildingVisitor.GUESSING_MAPPER);
String newStyle = SignatureParsing.parseTypeString(new SignatureParsing.CharIterator(signature), StubBuildingVisitor.GUESSING_MAPPER);
assertEquals(oldStyle, newStyle);
return newStyle;
TypeInfo newStyle = SignatureParsing.parseTypeStringToTypeInfo(new SignatureParsing.CharIterator(signature), StubBuildingVisitor.GUESSING_PROVIDER);
assertEquals(oldStyle, newStyle.text());
return oldStyle;
}
}

View File

@@ -19,7 +19,6 @@ import org.jetbrains.org.objectweb.asm.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -142,7 +141,7 @@ public class GroovyTraitFieldsFileIndex
values.add(new TraitFieldDescriptor(flags, typeString, name, annotations));
}
private Pair<Boolean, String> parse(String prefix, String prefix2, String input) {
private static Pair<Boolean, String> parse(String prefix, String prefix2, String input) {
if (input.startsWith(prefix)) {
return Pair.create(true, input.substring(prefix.length()));
}
@@ -154,10 +153,11 @@ public class GroovyTraitFieldsFileIndex
}
}
private String fieldType(String desc, String signature) {
private static String fieldType(String desc, String signature) {
if (signature != null) {
try {
return SignatureParsing.parseTypeString(new SignatureParsing.CharIterator(signature), StubBuildingVisitor.GUESSING_MAPPER);
return SignatureParsing.parseTypeStringToTypeInfo(new SignatureParsing.CharIterator(signature),
StubBuildingVisitor.GUESSING_PROVIDER).text();
}
catch (ClsFormatException ignored) { }
}

View File

@@ -86,7 +86,7 @@ private class ReflectionCallMethodVisitor(
}
val iterator = SignatureParsing.CharIterator(type.descriptor)
val classType = SignatureParsing.parseTypeString(iterator, StubBuildingVisitor.GUESSING_MAPPER)
val classType = SignatureParsing.parseTypeStringToTypeInfo(iterator, StubBuildingVisitor.GUESSING_PROVIDER).text()
return JavaPsiFacade.getInstance(project).findClass(classType, scope)
}

View File

@@ -7,7 +7,6 @@ import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.psi.*
import com.intellij.psi.impl.cache.TypeInfo
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.psi.impl.compiled.StubBuildingVisitor
@@ -170,9 +169,8 @@ internal fun KotlinType.toPsiType(
val signature = SignatureParsing.CharIterator(signatureWriter.toString())
val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER)
val typeInfo = TypeInfo.fromString(javaType)
val typeText = TypeInfo.createTypeText(typeInfo) ?: return UastErrorType
val typeInfo = SignatureParsing.parseTypeStringToTypeInfo(signature, StubBuildingVisitor.GUESSING_PROVIDER)
val typeText = typeInfo.text() ?: return UastErrorType
val psiTypeParent: PsiElement = containingLightDeclaration ?: context
if (psiTypeParent.containingFile == null) {