diff --git a/core/src/main/java/org/jboss/jandex/AnnotationInstance.java b/core/src/main/java/org/jboss/jandex/AnnotationInstance.java index 87443da9..29d45766 100644 --- a/core/src/main/java/org/jboss/jandex/AnnotationInstance.java +++ b/core/src/main/java/org/jboss/jandex/AnnotationInstance.java @@ -29,7 +29,7 @@ /** * An annotation instance represents a specific usage of an annotation on a - * target. It contains a set of values, as well as a reference to the target + * target. It contains a set of members, as well as a reference to the target * itself (e.g. class, field, method, etc). * *

@@ -131,12 +131,12 @@ public static AnnotationInstanceBuilder builder(DotName annotationType, boolean } /** - * Construct a new mock annotation instance. The passed values array will be defensively copied. + * Construct a new mock annotation instance. The passed {@code values} array will be defensively copied. * It is assumed that the annotation is {@link #runtimeVisible()}. * * @param name the name of the annotation instance * @param target the thing the annotation is declared on - * @param values the values of this annotation instance + * @param values the members of this annotation instance * @return the new mock Annotation Instance */ public static AnnotationInstance create(DotName name, AnnotationTarget target, AnnotationValue[] values) { @@ -144,12 +144,12 @@ public static AnnotationInstance create(DotName name, AnnotationTarget target, A } /** - * Construct a new mock annotation instance. The passed values array will be defensively copied. + * Construct a new mock annotation instance. The passed {@code values} array will be defensively copied. * * @param name the name of the annotation instance * @param visible whether the annotation is visible at runtime via the reflection API * @param target the thing the annotation is declared on - * @param values the values of this annotation instance + * @param values the members of this annotation instance * @return the new mock Annotation Instance */ public static AnnotationInstance create(DotName name, boolean visible, AnnotationTarget target, @@ -173,12 +173,12 @@ public int compare(AnnotationValue o1, AnnotationValue o2) { } /** - * Construct a new mock annotation instance. The passed values list will be defensively copied. + * Construct a new mock annotation instance. The passed {@code values} list will be defensively copied. * It is assumed that the annotation is {@link #runtimeVisible()}. * * @param name the name of the annotation instance * @param target the thing the annotation is declared on - * @param values the values of this annotation instance + * @param values the members of this annotation instance * @return the new mock Annotation Instance */ public static AnnotationInstance create(DotName name, AnnotationTarget target, List values) { @@ -186,12 +186,12 @@ public static AnnotationInstance create(DotName name, AnnotationTarget target, L } /** - * Construct a new mock annotation instance. The passed values list will be defensively copied. + * Construct a new mock annotation instance. The passed {@code values} list will be defensively copied. * * @param name the name of the annotation instance * @param visible whether the annotation is visible at runtime via the reflection API * @param target the thing the annotation is declared on - * @param values the values of this annotation instance + * @param values the members of this annotation instance * @return the new mock Annotation Instance */ public static AnnotationInstance create(DotName name, boolean visible, AnnotationTarget target, @@ -222,10 +222,10 @@ public DotName name() { } /** - * The Java element that this annotation was declared on. This can be - * a class, a field, a method, or a method parameter. In addition it may - * be null if this instance is a nested annotation, in which case there is - * no target. + * The program element that this annotation was declared on. This can be + * a class, a field, a method, a method parameter, or a type in case of + * type annotations. In addition, it may be {@code null} if this instance + * is a nested annotation, in which case there is no target. * * @return the target this annotation instance refers to */ @@ -234,13 +234,13 @@ public AnnotationTarget target() { } /** - * Returns a value that corresponds with the specified parameter name. - * If the parameter was not specified by this instance then null is - * returned. Note that this also applies to a defaulted parameter, - * which is not recorded in the target class. + * Returns the member of this annotation that has the specified name. + * If the member was not specified by this instance, {@code null} is + * returned. Note that this also applies to a defaulted member, + * whose value is not recorded in the target class file. * - * @param name the parameter name - * @return the value of the specified parameter, or null if not provided + * @param name the name of the annotation member + * @return the annotation member with specified name, or {@code null} */ public AnnotationValue value(final String name) { int result = Arrays.binarySearch(values, name, new Comparator() { @@ -252,34 +252,32 @@ public int compare(Object o1, Object o2) { } /** - * Returns the value that is associated with the special default "value" - * parameter. + * Returns the member that has the special default name {@code value}. * - * @return the "value" value + * @return the {@code value} member */ public AnnotationValue value() { return value("value"); } /** - * Returns a value that corresponds with the specified parameter name, + * Returns the member of this annotation that has the specified name, * accounting for its default value. Since an annotation's defaults are * only stored on the annotation's defining class, and not usages of the - * annotation, an index containing the Annotation class must be provided + * annotation, an index containing the annotation class must be provided * as a parameter. If the index does not contain the defining annotation - * class, then an IllegalArgumentException will be thrown to + * class, then an {@code IllegalArgumentException} will be thrown to * prevent non-deterministic results. * *

- * If the parameter was not specified by this instance, then the - * annotation's ClassInfo is checked for a default value. - * If there is a default, that value is returned. Otherwise null is - * returned. + * If the member was not specified by this annotation instance, then the + * annotation's {@code ClassInfo} is checked for a default value. If there + * is a default, that value is returned. Otherwise {@code null} is returned. *

* * @param index the index containing the defining annotation class - * @param name the name of the annotation parameter - * @return the value of the specified parameter, the default, or null + * @param name the name of the annotation member + * @return the annotation member with specified name, or {@code null} * @throws IllegalArgumentException if index does not contain the defining * annotation class * @since 2.1 @@ -300,23 +298,22 @@ public AnnotationValue valueWithDefault(IndexView index, String name) { } /** - * Returns the value that is associated with the special default "value" - * parameter, also accounting for a value default. Since an annotation's - * defaults are only stored on the annotation's defining class, and not - * usages of the annotation, an index containing the Annotation class must - * be provided as a parameter. If the index does not contain the defining - * annotation class, then an IllegalArgumentException will be - * thrown to prevent non-deterministic results. + * Returns the member of this annotation that has special name {@code value}, + * accounting for its default value. Since an annotation's defaults are only + * stored on the annotation's defining class, and not usages of the annotation, + * an index containing the annotation class must be provided as a parameter. + * If the index does not contain the defining annotation class, then an + * {@code IllegalArgumentException} will be thrown to prevent non-deterministic + * results. * *

- * If the "value" parameter was not specified by this instance, then the - * annotation's ClassInfo is checked for a default value. - * If there is a default, that value is returned. Otherwise null is - * returned. + * If the {@code value} member was not specified by this instance, then the + * annotation's {@code ClassInfo} is checked for a default value. If there + * is a default, that value is returned. Otherwise {@code null} is returned. *

* * @param index the index containing the defining annotation class - * @return the "value" value, or its default, or null + * @return the {@code value} annotation member, or {@code null} * @throws IllegalArgumentException if index does not contain the defining * annotation class * @since 2.1 @@ -326,19 +323,19 @@ public AnnotationValue valueWithDefault(IndexView index) { } /** - * Returns a list of all parameter values on this annotation instance, - * including default values id defined. Since an annotation's defaults are - * only stored on the annotation's defining class, and not usages of the - * annotation, an index containing the Annotation class must be provided as - * a parameter. If the index does not contain the defining annotation class, - * then an IllegalArgumentException will be thrown to prevent - * non-deterministic results. + * Returns a list of all members of this annotation instance, including default + * values if defined. Since an annotation's defaults are only stored on the + * annotation's defining class, and not usages of the annotation, an index + * containing the annotation class must be provided as a parameter. If the index + * does not contain the defining annotation class, then an + * {@code IllegalArgumentException} will be thrown to prevent non-deterministic + * results. * *

* The order of this list is undefined. *

* - * @return the parameter values of this annotation + * @return immutable list of this annotation's members * @throws IllegalArgumentException if index does not contain the defining * annotation class * @since 2.1 @@ -365,15 +362,15 @@ public List valuesWithDefaults(IndexView index) { } /** - * Returns a list of all parameter values on this annotation instance. + * Returns an immutable list of all members of this annotation instance. * While random access is allowed, the ordering algorithm * of the list should not be relied upon. Although it will * be consistent for the life of this instance. * - * @return the parameter values of this annotation + * @return immutable list of this annotation's members */ public List values() { - return Collections.unmodifiableList(Arrays.asList(values)); + return new ImmutableArrayList<>(values); } AnnotationValue[] valueArray() { diff --git a/core/src/main/java/org/jboss/jandex/ClassInfo.java b/core/src/main/java/org/jboss/jandex/ClassInfo.java index 55657a5b..451396dd 100644 --- a/core/src/main/java/org/jboss/jandex/ClassInfo.java +++ b/core/src/main/java/org/jboss/jandex/ClassInfo.java @@ -62,18 +62,15 @@ public final class ClassInfo implements AnnotationTarget { // Not final to allow lazy initialization, immutable once published private short flags; + private boolean hasNoArgsConstructor; private Type[] interfaceTypes; private Type superClassType; - private Type[] typeParameters; private MethodInternal[] methods; private FieldInternal[] fields; - private RecordComponentInternal[] recordComponents; private byte[] methodPositions = EMPTY_POSITIONS; private byte[] fieldPositions = EMPTY_POSITIONS; - private byte[] recordComponentPositions = EMPTY_POSITIONS; - private boolean hasNoArgsConstructor; private NestingInfo nestingInfo; - private Set memberClasses; + private ExtraInfo extra; /** Describes the form of nesting used by a class */ public enum NestingType { @@ -92,16 +89,19 @@ public enum NestingType { ANONYMOUS } + // contains fields that are only seldom used, to make the `ClassInfo` class smaller + private static final class ExtraInfo { + private Type[] typeParameters; + private RecordComponentInternal[] recordComponents; + private byte[] recordComponentPositions = EMPTY_POSITIONS; + private Set memberClasses; + private ModuleInfo module; + } + private static final class NestingInfo { private DotName enclosingClass; private String simpleName; private EnclosingMethodInfo enclosingMethod; - - // non-null if the class is in fact a module descriptor - // this field would naturally belong to ClassInfo, but is present here - // to make the ClassInfo object smaller in the most common case: - // non-nested class that is not a module descriptor - private ModuleInfo module; } /** @@ -139,7 +139,7 @@ public Type returnType() { * @return the list of parameters. */ public List parameters() { - return Collections.unmodifiableList(Arrays.asList(parameters)); + return new ImmutableArrayList<>(parameters); } Type[] parametersArray() { @@ -186,7 +186,6 @@ public String toString() { this.flags = flags; this.interfaceTypes = interfaceTypes.length == 0 ? Type.EMPTY_ARRAY : interfaceTypes; this.hasNoArgsConstructor = hasNoArgsConstructor; - this.typeParameters = Type.EMPTY_ARRAY; this.methods = MethodInternal.EMPTY_ARRAY; this.fields = FieldInternal.EMPTY_ARRAY; } @@ -834,12 +833,16 @@ final byte[] fieldPositionArray() { * @return the record component */ public final RecordComponentInfo recordComponent(String name) { + if (extra == null || extra.recordComponents == null) { + return null; + } + RecordComponentInternal key = new RecordComponentInternal(Utils.toUTF8(name), VoidType.VOID); - int i = Arrays.binarySearch(recordComponents, key, RecordComponentInternal.NAME_COMPARATOR); + int i = Arrays.binarySearch(extra.recordComponents, key, RecordComponentInternal.NAME_COMPARATOR); if (i < 0) { return null; } - return new RecordComponentInfo(this, recordComponents[i]); + return new RecordComponentInfo(this, extra.recordComponents[i]); } /** @@ -849,7 +852,11 @@ public final RecordComponentInfo recordComponent(String name) { * @return a list of record components */ public final List recordComponents() { - return new RecordComponentInfoGenerator(this, recordComponents, EMPTY_POSITIONS); + if (extra == null || extra.recordComponents == null) { + return Collections.emptyList(); + } + + return new RecordComponentInfoGenerator(this, extra.recordComponents, EMPTY_POSITIONS); } /** @@ -866,15 +873,19 @@ public final List recordComponents() { * @since 2.4 */ public final List unsortedRecordComponents() { - return new RecordComponentInfoGenerator(this, recordComponents, recordComponentPositions); + if (extra == null || extra.recordComponents == null) { + return Collections.emptyList(); + } + + return new RecordComponentInfoGenerator(this, extra.recordComponents, extra.recordComponentPositions); } final RecordComponentInternal[] recordComponentArray() { - return recordComponents; + return extra != null && extra.recordComponents != null ? extra.recordComponents : RecordComponentInternal.EMPTY_ARRAY; } final byte[] recordComponentPositionArray() { - return recordComponentPositions; + return extra != null && extra.recordComponentPositions != null ? extra.recordComponentPositions : EMPTY_POSITIONS; } /** @@ -957,7 +968,7 @@ public int size() { * @return immutable list of types declared in the {@code implements} clause of this class */ public final List interfaceTypes() { - return Collections.unmodifiableList(Arrays.asList(interfaceTypes)); + return new ImmutableArrayList<>(interfaceTypes); } final Type[] interfaceTypeArray() { @@ -984,13 +995,20 @@ public final Type superClassType() { * @return immutable list of generic type parameters of this class */ public final List typeParameters() { - @SuppressWarnings({ "unchecked", "rawtypes" }) - List list = (List) Arrays.asList(typeParameters); - return Collections.unmodifiableList(list); + if (extra == null || extra.typeParameters == null) { + return Collections.emptyList(); + } + + // type parameters are always `TypeVariable` + return new ImmutableArrayList(extra.typeParameters); } final Type[] typeParameterArray() { - return typeParameters; + if (extra == null || extra.typeParameters == null) { + return Type.EMPTY_ARRAY; + } + + return extra.typeParameters; } /** @@ -1022,7 +1040,7 @@ public final boolean hasNoArgsConstructor() { * @return the nesting type of this class */ public NestingType nestingType() { - if (nestingInfo == null || nestingInfo.module != null) { + if (nestingInfo == null) { return NestingType.TOP_LEVEL; } else if (nestingInfo.enclosingClass != null) { return NestingType.INNER; @@ -1081,10 +1099,10 @@ public EnclosingMethodInfo enclosingMethod() { * @return immutable set of names of this class's member classes, never {@code null} */ public Set memberClasses() { - if (memberClasses == null) { + if (extra == null || extra.memberClasses == null) { return Collections.emptySet(); } - return Collections.unmodifiableSet(memberClasses); + return Collections.unmodifiableSet(extra.memberClasses); } /** @@ -1093,7 +1111,7 @@ public Set memberClasses() { * @return the module descriptor for module classes, otherwise {@code null} */ public ModuleInfo module() { - return nestingInfo != null ? nestingInfo.module : null; + return extra != null ? extra.module : null; } @Override @@ -1187,31 +1205,50 @@ void setMethods(List methods, NameTable names) { } void setRecordComponentArray(RecordComponentInternal[] recordComponents) { - this.recordComponents = recordComponents; + if (recordComponents.length == 0) { + return; + } + + if (extra == null) { + extra = new ExtraInfo(); + } + + extra.recordComponents = recordComponents; } void setRecordComponentPositionArray(byte[] recordComponentPositions) { - this.recordComponentPositions = recordComponentPositions; + if (recordComponentPositions.length == 0) { + return; + } + + if (extra == null) { + extra = new ExtraInfo(); + } + + extra.recordComponentPositions = recordComponentPositions; } void setRecordComponents(List recordComponents, NameTable names) { final int size = recordComponents.size(); if (size == 0) { - this.recordComponents = RecordComponentInternal.EMPTY_ARRAY; return; } - this.recordComponents = new RecordComponentInternal[size]; + if (extra == null) { + extra = new ExtraInfo(); + } + + extra.recordComponents = new RecordComponentInternal[size]; for (int i = 0; i < size; i++) { RecordComponentInfo recordComponentInfo = recordComponents.get(i); RecordComponentInternal internal = names.intern(recordComponentInfo.recordComponentInternal()); recordComponentInfo.setRecordComponentInternal(internal); - this.recordComponents[i] = internal; + extra.recordComponents[i] = internal; } - this.recordComponentPositions = sortAndGetPositions(this.recordComponents, RecordComponentInternal.NAME_COMPARATOR, + extra.recordComponentPositions = sortAndGetPositions(extra.recordComponents, RecordComponentInternal.NAME_COMPARATOR, names); } @@ -1265,7 +1302,15 @@ void setInterfaceTypes(Type[] interfaceTypes) { } void setTypeParameters(Type[] typeParameters) { - this.typeParameters = typeParameters.length == 0 ? Type.EMPTY_ARRAY : typeParameters; + if (typeParameters.length == 0) { + return; + } + + if (extra == null) { + extra = new ExtraInfo(); + } + + extra.typeParameters = typeParameters; } void setInnerClassInfo(DotName enclosingClass, String simpleName, boolean knownInnerClass) { @@ -1286,7 +1331,15 @@ void setInnerClassInfo(DotName enclosingClass, String simpleName, boolean knownI } void setMemberClasses(Set memberClasses) { - this.memberClasses = memberClasses; + if (memberClasses == null || memberClasses.isEmpty()) { + return; + } + + if (extra == null) { + extra = new ExtraInfo(); + } + + extra.memberClasses = memberClasses; } void setEnclosingMethod(EnclosingMethodInfo enclosingMethod) { @@ -1306,11 +1359,11 @@ void setModule(ModuleInfo module) { return; } - if (nestingInfo == null) { - nestingInfo = new NestingInfo(); + if (extra == null) { + extra = new ExtraInfo(); } - nestingInfo.module = module; + extra.module = module; } void setFlags(short flags) { diff --git a/core/src/main/java/org/jboss/jandex/FieldInternal.java b/core/src/main/java/org/jboss/jandex/FieldInternal.java index b00ab8a5..e1162bd9 100644 --- a/core/src/main/java/org/jboss/jandex/FieldInternal.java +++ b/core/src/main/java/org/jboss/jandex/FieldInternal.java @@ -19,7 +19,6 @@ package org.jboss.jandex; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -151,7 +150,7 @@ final Type type() { } final List annotations() { - return Collections.unmodifiableList(Arrays.asList(annotations)); + return new ImmutableArrayList<>(annotations); } final AnnotationInstance[] annotationArray() { diff --git a/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java b/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java new file mode 100644 index 00000000..ffa60b4d --- /dev/null +++ b/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java @@ -0,0 +1,21 @@ +package org.jboss.jandex; + +import java.util.AbstractList; + +final class ImmutableArrayList extends AbstractList { + private final T[] array; + + ImmutableArrayList(T[] array) { + this.array = array; + } + + @Override + public T get(int index) { + return array[index]; + } + + @Override + public int size() { + return array.length; + } +} diff --git a/core/src/main/java/org/jboss/jandex/Index.java b/core/src/main/java/org/jboss/jandex/Index.java index 578a315d..ba20c50b 100644 --- a/core/src/main/java/org/jboss/jandex/Index.java +++ b/core/src/main/java/org/jboss/jandex/Index.java @@ -18,6 +18,8 @@ package org.jboss.jandex; +import static org.jboss.jandex.Utils.unfold; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileFilter; @@ -67,28 +69,28 @@ public final class Index implements IndexView { static final DotName REPEATABLE = DotName.createSimple("java.lang.annotation.Repeatable"); - final Map> annotations; - final Map> subclasses; - final Map> subinterfaces; - final Map> implementors; + final Map annotations; + final Map subclasses; + final Map subinterfaces; + final Map implementors; final Map classes; final Map modules; - final Map> users; + final Map users; // populated lazily volatile Map> classesInPackage; volatile Map> subpackages; - Index(Map> annotations, Map> subclasses, - Map> subinterfaces, Map> implementors, - Map classes, Map modules, Map> users) { - this.annotations = Collections.unmodifiableMap(annotations); - this.classes = Collections.unmodifiableMap(classes); - this.subclasses = Collections.unmodifiableMap(subclasses); - this.subinterfaces = Collections.unmodifiableMap(subinterfaces); - this.implementors = Collections.unmodifiableMap(implementors); - this.modules = Collections.unmodifiableMap(modules); - this.users = Collections.unmodifiableMap(users); + Index(Map annotations, Map subclasses, + Map subinterfaces, Map implementors, + Map classes, Map modules, Map users) { + this.annotations = annotations; + this.classes = classes; + this.subclasses = subclasses; + this.subinterfaces = subinterfaces; + this.implementors = implementors; + this.modules = modules; + this.users = users; } /** @@ -105,7 +107,12 @@ public final class Index implements IndexView { */ public static Index create(Map> annotations, Map> subclasses, Map> implementors, Map classes) { - return new Index(annotations, subclasses, Collections.emptyMap(), implementors, classes, Collections.emptyMap(), + return new Index(unfold(annotations, AnnotationInstance.class), + unfold(subclasses, ClassInfo.class), + Collections.emptyMap(), + unfold(implementors, ClassInfo.class), + classes, + Collections.emptyMap(), Collections.emptyMap()); } @@ -124,7 +131,13 @@ public static Index create(Map> annotations, M */ public static Index create(Map> annotations, Map> subclasses, Map> implementors, Map classes, Map> users) { - return new Index(annotations, subclasses, Collections.emptyMap(), implementors, classes, Collections.emptyMap(), users); + return new Index(unfold(annotations, AnnotationInstance.class), + unfold(subclasses, ClassInfo.class), + Collections.emptyMap(), + unfold(implementors, ClassInfo.class), + classes, + Collections.emptyMap(), + unfold(users, ClassInfo.class)); } /** @@ -144,7 +157,40 @@ public static Index create(Map> annotations, M public static Index create(Map> annotations, Map> subclasses, Map> subinterfaces, Map> implementors, Map classes, Map> users) { - return new Index(annotations, subclasses, subinterfaces, implementors, classes, Collections.emptyMap(), users); + return new Index(unfold(annotations, AnnotationInstance.class), + unfold(subclasses, ClassInfo.class), + unfold(subinterfaces, ClassInfo.class), + unfold(implementors, ClassInfo.class), + classes, + Collections.emptyMap(), + unfold(users, ClassInfo.class)); + } + + /** + * Constructs a "mock" Index using the passed values. All passed values MUST NOT BE MODIFIED AFTER THIS CALL. + * Otherwise the resulting object would not conform to the contract outlined above. Also, to conform to the + * memory efficiency contract this method should be passed componentized DotNames, which all share common root + * instances. Of course for testing code this doesn't really matter. + * + * @param annotations A map to lookup annotation instances by class name + * @param subclasses A map to lookup subclasses by super class name + * @param subinterfaces A map to lookup subinterfaces by super interface name + * @param implementors A map to lookup implementing classes by interface name + * @param classes A map to lookup classes by class name + * @param modules A map to lookup modules by name + * @param users A map to lookup class users + * @return the index + */ + public static Index create(Map> annotations, Map> subclasses, + Map> subinterfaces, Map> implementors, + Map classes, Map modules, Map> users) { + return new Index(unfold(annotations, AnnotationInstance.class), + unfold(subclasses, ClassInfo.class), + unfold(subinterfaces, ClassInfo.class), + unfold(implementors, ClassInfo.class), + classes, + modules, + unfold(users, ClassInfo.class)); } /** @@ -269,8 +315,8 @@ public static ClassInfo singleClass(InputStream classData) throws IOException { */ @Override public List getAnnotations(DotName annotationName) { - List list = annotations.get(annotationName); - return list == null ? EMPTY_ANNOTATION_LIST : Collections.unmodifiableList(list); + AnnotationInstance[] list = annotations.get(annotationName); + return list == null ? EMPTY_ANNOTATION_LIST : new ImmutableArrayList<>(list); } /** @@ -311,8 +357,8 @@ private Collection getRepeatableAnnotations(DotName annotati */ @Override public List getKnownDirectSubclasses(DotName className) { - List list = subclasses.get(className); - return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list); + ClassInfo[] list = subclasses.get(className); + return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list); } @Override @@ -354,8 +400,8 @@ private void getAllKnownSubClasses(DotName name, Set allKnown, Set getKnownDirectSubinterfaces(DotName interfaceName) { - List list = subinterfaces.get(interfaceName); - return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list); + ClassInfo[] list = subinterfaces.get(interfaceName); + return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list); } /** @@ -389,8 +435,8 @@ public Collection getAllKnownSubinterfaces(DotName interfaceName) { */ @Override public List getKnownDirectImplementors(DotName className) { - List list = implementors.get(className); - return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list); + ClassInfo[] list = implementors.get(className); + return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list); } /** @@ -470,11 +516,8 @@ public ModuleInfo getModuleByName(DotName moduleName) { */ @Override public List getKnownUsers(DotName className) { - List ret = users.get(className); - if (ret == null) { - return EMPTY_CLASSINFO_LIST; - } - return Collections.unmodifiableList(ret); + ClassInfo[] ret = users.get(className); + return ret == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(ret); } /** @@ -534,7 +577,7 @@ public Set getSubpackages(DotName packageName) { */ public void printAnnotations() { System.out.println("Annotations:"); - for (Map.Entry> e : annotations.entrySet()) { + for (Map.Entry e : annotations.entrySet()) { System.out.println(e.getKey() + ":"); for (AnnotationInstance instance : e.getValue()) { AnnotationTarget target = instance.target(); @@ -571,7 +614,7 @@ public void printAnnotations() { */ public void printSubclasses() { System.out.println("Subclasses:"); - for (Map.Entry> entry : subclasses.entrySet()) { + for (Map.Entry entry : subclasses.entrySet()) { System.out.println(entry.getKey() + ":"); for (ClassInfo clazz : entry.getValue()) System.out.println(" " + clazz.name()); diff --git a/core/src/main/java/org/jboss/jandex/IndexReaderV2.java b/core/src/main/java/org/jboss/jandex/IndexReaderV2.java index b0c0ff7a..2265340d 100644 --- a/core/src/main/java/org/jboss/jandex/IndexReaderV2.java +++ b/core/src/main/java/org/jboss/jandex/IndexReaderV2.java @@ -778,7 +778,7 @@ private List convertToList(AnnotationInstance[] annotationIn return Collections.emptyList(); } - return Collections.unmodifiableList(Arrays.asList(annotationInstances)); + return new ImmutableArrayList<>(annotationInstances); } private void addClassToMap(HashMap> map, DotName name, ClassInfo currentClass) { @@ -903,7 +903,7 @@ private Index readClasses(PackedDataInputStream stream, Map modules = (version >= 10) ? readModules(stream, masterAnnotations) : Collections. emptyMap(); - return new Index(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users); + return Index.create(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users); } private Map readModules(PackedDataInputStream stream, diff --git a/core/src/main/java/org/jboss/jandex/IndexWriterV2.java b/core/src/main/java/org/jboss/jandex/IndexWriterV2.java index ead26ff4..debb06f8 100644 --- a/core/src/main/java/org/jboss/jandex/IndexWriterV2.java +++ b/core/src/main/java/org/jboss/jandex/IndexWriterV2.java @@ -226,15 +226,15 @@ int write(Index index) throws IOException { return stream.size(); } - private void writeUsersTable(PackedDataOutputStream stream, Map> users) throws IOException { - for (Entry> entry : users.entrySet()) { + private void writeUsersTable(PackedDataOutputStream stream, Map users) throws IOException { + for (Entry entry : users.entrySet()) { writeUsersSet(stream, entry.getKey(), entry.getValue()); } } - private void writeUsersSet(PackedDataOutputStream stream, DotName user, List uses) throws IOException { + private void writeUsersSet(PackedDataOutputStream stream, DotName user, ClassInfo[] uses) throws IOException { stream.writePackedU32(positionOf(user)); - stream.writePackedU32(uses.size()); + stream.writePackedU32(uses.length); for (ClassInfo use : uses) { stream.writePackedU32(positionOf(use.name())); } @@ -895,7 +895,7 @@ private void buildTables(Index index) { } if (index.users != null) { - for (Entry> entry : index.users.entrySet()) { + for (Entry entry : index.users.entrySet()) { addClassName(entry.getKey()); for (ClassInfo classInfo : entry.getValue()) { addClassName(classInfo.name()); diff --git a/core/src/main/java/org/jboss/jandex/Indexer.java b/core/src/main/java/org/jboss/jandex/Indexer.java index 1463adea..80a492fc 100644 --- a/core/src/main/java/org/jboss/jandex/Indexer.java +++ b/core/src/main/java/org/jboss/jandex/Indexer.java @@ -2403,7 +2403,7 @@ public Index complete() { propagateTypeVariables(); try { - return new Index(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users); + return Index.create(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users); } finally { masterAnnotations = null; subclasses = null; diff --git a/core/src/main/java/org/jboss/jandex/MethodInternal.java b/core/src/main/java/org/jboss/jandex/MethodInternal.java index 6521249d..59ab5d5f 100644 --- a/core/src/main/java/org/jboss/jandex/MethodInternal.java +++ b/core/src/main/java/org/jboss/jandex/MethodInternal.java @@ -32,7 +32,7 @@ final class MethodInternal implements Interned { static final NameAndParameterComponentComparator NAME_AND_PARAMETER_COMPONENT_COMPARATOR = new NameAndParameterComponentComparator(); static final byte[][] EMPTY_PARAMETER_NAMES = new byte[0][]; - static class NameAndParameterComponentComparator implements Comparator { + private static final class NameAndParameterComponentComparator implements Comparator { private int compare(byte[] left, byte[] right) { for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) { int a = (left[i] & 0xff); @@ -72,16 +72,21 @@ public int compare(MethodInternal instance, MethodInternal instance2) { } } + // contains fields that are only seldom used, to make the `MethodInternal` class smaller + private static final class ExtraInfo { + Type receiverType; + Type[] typeParameters; + AnnotationValue defaultValue; + AnnotationInstance[] annotations; + } + private byte[] name; private byte[][] parameterNames; private Type[] parameterTypes; private Type returnType; private Type[] exceptions; - private Type receiverType; - private Type[] typeParameters; - private AnnotationInstance[] annotations; - private AnnotationValue defaultValue; private short flags; + private ExtraInfo extra; private final Type[] descriptorParameterTypes; @@ -103,11 +108,31 @@ public int compare(MethodInternal instance, MethodInternal instance2) { this.parameterTypes = parameterTypes.length == 0 ? Type.EMPTY_ARRAY : parameterTypes; this.returnType = returnType; this.flags = flags; - this.annotations = annotations; this.exceptions = exceptions; - this.typeParameters = typeParameters; - this.receiverType = receiverType; - this.defaultValue = defaultValue; + if (annotations != null && annotations.length > 0) { + if (extra == null) { + extra = new ExtraInfo(); + } + extra.annotations = annotations; + } + if (typeParameters != null && typeParameters.length > 0) { + if (extra == null) { + extra = new ExtraInfo(); + } + extra.typeParameters = typeParameters; + } + if (receiverType != null) { + if (extra == null) { + extra = new ExtraInfo(); + } + extra.receiverType = receiverType; + } + if (defaultValue != null) { + if (extra == null) { + extra = new ExtraInfo(); + } + extra.defaultValue = defaultValue; + } this.descriptorParameterTypes = this.parameterTypes; } @@ -123,10 +148,14 @@ public boolean equals(Object o) { MethodInternal methodInternal = (MethodInternal) o; + if (extra != null && methodInternal.extra == null || extra == null && methodInternal.extra != null) { + return false; + } + if (flags != methodInternal.flags) { return false; } - if (!Arrays.equals(annotations, methodInternal.annotations)) { + if (extra != null && !Arrays.equals(extra.annotations, methodInternal.extra.annotations)) { return false; } if (!Arrays.equals(exceptions, methodInternal.exceptions)) { @@ -144,16 +173,21 @@ public boolean equals(Object o) { if (!Arrays.equals(descriptorParameterTypes, methodInternal.descriptorParameterTypes)) { return false; } - if (receiverType != null ? !receiverType.equals(methodInternal.receiverType) : methodInternal.receiverType != null) { + if (extra != null && (extra.receiverType != null ? !extra.receiverType.equals(methodInternal.extra.receiverType) + : methodInternal.extra.receiverType != null)) { return false; } if (!returnType.equals(methodInternal.returnType)) { return false; } - if (defaultValue != null ? !defaultValue.equals(methodInternal.defaultValue) : methodInternal.defaultValue != null) { + if (extra != null && (extra.defaultValue != null ? !extra.defaultValue.equals(methodInternal.extra.defaultValue) + : methodInternal.extra.defaultValue != null)) { return false; } - return Arrays.equals(typeParameters, methodInternal.typeParameters); + if (extra != null && !Arrays.equals(extra.typeParameters, methodInternal.extra.typeParameters)) { + return false; + } + return true; } @Override @@ -164,11 +198,11 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(descriptorParameterTypes); result = 31 * result + returnType.hashCode(); result = 31 * result + Arrays.hashCode(exceptions); - result = 31 * result + (receiverType != null ? receiverType.hashCode() : 0); - result = 31 * result + Arrays.hashCode(typeParameters); - result = 31 * result + Arrays.hashCode(annotations); + result = 31 * result + (extra != null && extra.receiverType != null ? extra.receiverType.hashCode() : 0); + result = 31 * result + (extra != null ? Arrays.hashCode(extra.typeParameters) : 0); + result = 31 * result + (extra != null ? Arrays.hashCode(extra.annotations) : 0); + result = 31 * result + (extra != null && extra.defaultValue != null ? extra.defaultValue.hashCode() : 0); result = 31 * result + (int) flags; - result = 31 * result + (defaultValue != null ? defaultValue.hashCode() : 0); return result; } @@ -183,10 +217,14 @@ public boolean internEquals(Object o) { MethodInternal methodInternal = (MethodInternal) o; + if (extra != null && methodInternal.extra == null || extra == null && methodInternal.extra != null) { + return false; + } + if (flags != methodInternal.flags) { return false; } - if (!Arrays.equals(annotations, methodInternal.annotations)) { + if (extra != null && !Arrays.equals(extra.annotations, methodInternal.extra.annotations)) { return false; } if (!Interned.arrayEquals(exceptions, methodInternal.exceptions)) { @@ -204,17 +242,21 @@ public boolean internEquals(Object o) { if (!Interned.arrayEquals(descriptorParameterTypes, methodInternal.descriptorParameterTypes)) { return false; } - if (receiverType != null ? !receiverType.internEquals(methodInternal.receiverType) - : methodInternal.receiverType != null) { + if (extra != null && (extra.receiverType != null ? !extra.receiverType.internEquals(methodInternal.extra.receiverType) + : methodInternal.extra.receiverType != null)) { return false; } if (!returnType.internEquals(methodInternal.returnType)) { return false; } - if (defaultValue != null ? !defaultValue.equals(methodInternal.defaultValue) : methodInternal.defaultValue != null) { + if (extra != null && (extra.defaultValue != null ? !extra.defaultValue.equals(methodInternal.extra.defaultValue) + : methodInternal.extra.defaultValue != null)) { + return false; + } + if (extra != null && !Interned.arrayEquals(extra.typeParameters, methodInternal.extra.typeParameters)) { return false; } - return Interned.arrayEquals(typeParameters, methodInternal.typeParameters); + return true; } @Override @@ -225,11 +267,11 @@ public int internHashCode() { result = 31 * result + Interned.arrayHashCode(descriptorParameterTypes); result = 31 * result + returnType.internHashCode(); result = 31 * result + Interned.arrayHashCode(exceptions); - result = 31 * result + (receiverType != null ? receiverType.internHashCode() : 0); - result = 31 * result + Interned.arrayHashCode(typeParameters); - result = 31 * result + Arrays.hashCode(annotations); + result = 31 * result + (extra != null && extra.receiverType != null ? extra.receiverType.internHashCode() : 0); + result = 31 * result + (extra != null ? Interned.arrayHashCode(extra.typeParameters) : 0); + result = 31 * result + (extra != null ? Arrays.hashCode(extra.annotations) : 0); + result = 31 * result + (extra != null && extra.defaultValue != null ? extra.defaultValue.hashCode() : 0); result = 31 * result + (int) flags; - result = 31 * result + (defaultValue != null ? defaultValue.hashCode() : 0); return result; } @@ -268,11 +310,11 @@ final Type[] copyExceptions() { } final List parameterTypes() { - return Collections.unmodifiableList(Arrays.asList(parameterTypes)); + return new ImmutableArrayList<>(parameterTypes); } final List descriptorParameterTypes() { - return Collections.unmodifiableList(Arrays.asList(descriptorParameterTypes)); + return new ImmutableArrayList<>(descriptorParameterTypes); } final Type[] descriptorParameterTypesArray() { @@ -284,8 +326,8 @@ final Type returnType() { } final Type receiverType(ClassInfo clazz) { - if (receiverType != null) { - return receiverType; + if (extra != null && extra.receiverType != null) { + return extra.receiverType; } // don't assign the `receiverType` field here! receiver type declaration is only useful @@ -302,11 +344,11 @@ final Type receiverType(ClassInfo clazz) { } final Type receiverTypeField() { - return receiverType; + return extra != null ? extra.receiverType : null; } final List exceptions() { - return Collections.unmodifiableList(Arrays.asList(exceptions)); + return new ImmutableArrayList<>(exceptions); } final Type[] exceptionArray() { @@ -314,21 +356,22 @@ final Type[] exceptionArray() { } final List typeParameters() { - @SuppressWarnings("unchecked") // type parameters will always be TypeVariable[] - List list = (List) Arrays.asList(typeParameters); - return Collections.unmodifiableList(list); + // type parameters are always `TypeVariable` + return extra != null && extra.typeParameters != null ? new ImmutableArrayList(extra.typeParameters) + : Collections.emptyList(); } final List annotations() { - return Collections.unmodifiableList(Arrays.asList(annotations)); + return extra != null && extra.annotations != null ? new ImmutableArrayList<>(extra.annotations) + : Collections.emptyList(); } final AnnotationInstance[] annotationArray() { - return annotations; + return extra != null && extra.annotations != null ? extra.annotations : AnnotationInstance.EMPTY_ARRAY; } final AnnotationInstance annotation(DotName name) { - return AnnotationInstance.binarySearch(annotations, name); + return extra != null && extra.annotations != null ? AnnotationInstance.binarySearch(extra.annotations, name) : null; } final boolean hasAnnotation(DotName name) { @@ -336,11 +379,11 @@ final boolean hasAnnotation(DotName name) { } final Type[] typeParameterArray() { - return typeParameters; + return extra != null && extra.typeParameters != null ? extra.typeParameters : Type.EMPTY_ARRAY; } final AnnotationValue defaultValue() { - return defaultValue; + return extra != null ? extra.defaultValue : null; } final short flags() { @@ -352,8 +395,8 @@ public String toString() { StringBuilder builder = new StringBuilder(); String name = name(); builder.append(returnType.toString(true)).append(' ').append(name).append('('); - if (receiverType != null) { - builder.append(receiverType.toString(true)).append(" this"); + if (extra != null && extra.receiverType != null) { + builder.append(extra.receiverType.toString(true)).append(" this"); if (parameterTypes.length > 0) { builder.append(", "); } @@ -385,7 +428,10 @@ public String toString() { void setTypeParameters(Type[] typeParameters) { if (typeParameters.length > 0) { - this.typeParameters = typeParameters; + if (extra == null) { + extra = new ExtraInfo(); + } + extra.typeParameters = typeParameters; } } @@ -406,17 +452,26 @@ void setExceptions(Type[] exceptions) { } void setReceiverType(Type receiverType) { - this.receiverType = receiverType; + if (extra == null) { + extra = new ExtraInfo(); + } + extra.receiverType = receiverType; } void setAnnotations(List annotations) { - if (annotations.size() > 0) { - this.annotations = annotations.toArray(new AnnotationInstance[annotations.size()]); - Arrays.sort(this.annotations, AnnotationInstance.NAME_COMPARATOR); + if (!annotations.isEmpty()) { + if (extra == null) { + extra = new ExtraInfo(); + } + extra.annotations = annotations.toArray(new AnnotationInstance[0]); + Arrays.sort(extra.annotations, AnnotationInstance.NAME_COMPARATOR); } } void setDefaultValue(AnnotationValue defaultValue) { - this.defaultValue = defaultValue; + if (extra == null) { + extra = new ExtraInfo(); + } + extra.defaultValue = defaultValue; } } diff --git a/core/src/main/java/org/jboss/jandex/ParameterizedType.java b/core/src/main/java/org/jboss/jandex/ParameterizedType.java index 905d8551..ac315b07 100644 --- a/core/src/main/java/org/jboss/jandex/ParameterizedType.java +++ b/core/src/main/java/org/jboss/jandex/ParameterizedType.java @@ -18,7 +18,6 @@ package org.jboss.jandex; import java.util.Arrays; -import java.util.Collections; import java.util.List; /** @@ -99,7 +98,7 @@ public static ParameterizedType create(DotName name, Type[] arguments, Type owne * @return the list of type arguments, or empty if none */ public List arguments() { - return Collections.unmodifiableList(Arrays.asList(arguments)); + return new ImmutableArrayList<>(arguments); } Type[] argumentsArray() { diff --git a/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java b/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java index 034ea7d2..7656b060 100644 --- a/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java +++ b/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java @@ -19,7 +19,6 @@ package org.jboss.jandex; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -138,7 +137,7 @@ final Type type() { } final List annotations() { - return Collections.unmodifiableList(Arrays.asList(annotations)); + return new ImmutableArrayList<>(annotations); } final AnnotationInstance[] annotationArray() { diff --git a/core/src/main/java/org/jboss/jandex/Result.java b/core/src/main/java/org/jboss/jandex/Result.java index 75d94a92..c53b37a9 100644 --- a/core/src/main/java/org/jboss/jandex/Result.java +++ b/core/src/main/java/org/jboss/jandex/Result.java @@ -19,7 +19,6 @@ package org.jboss.jandex; import java.io.File; -import java.util.List; /** * The result from a jar indexing operation. @@ -44,8 +43,8 @@ public final class Result { instances = countInstances(index); classes = index.classes.size(); int usages = 0; - for (List usagesForOneClass : index.users.values()) { - usages += usagesForOneClass.size(); + for (ClassInfo[] usagesForOneClass : index.users.values()) { + usages += usagesForOneClass.length; } this.usages = usages; this.bytes = bytes; @@ -55,8 +54,8 @@ public final class Result { private int countInstances(Index index) { int c = 0; - for (List list : index.annotations.values()) - c += list.size(); + for (AnnotationInstance[] list : index.annotations.values()) + c += list.length; return c; } diff --git a/core/src/main/java/org/jboss/jandex/Type.java b/core/src/main/java/org/jboss/jandex/Type.java index 7d079cef..852e2f45 100644 --- a/core/src/main/java/org/jboss/jandex/Type.java +++ b/core/src/main/java/org/jboss/jandex/Type.java @@ -429,7 +429,7 @@ public final List annotationsWithRepeatable(DotName name, In * @since 2.0 */ public List annotations() { - return Collections.unmodifiableList(Arrays.asList(annotations)); + return new ImmutableArrayList<>(annotations); } Type addAnnotation(AnnotationInstance annotation) { diff --git a/core/src/main/java/org/jboss/jandex/TypeVariable.java b/core/src/main/java/org/jboss/jandex/TypeVariable.java index 94064a5c..3a4899a9 100644 --- a/core/src/main/java/org/jboss/jandex/TypeVariable.java +++ b/core/src/main/java/org/jboss/jandex/TypeVariable.java @@ -18,7 +18,6 @@ package org.jboss.jandex; import java.util.Arrays; -import java.util.Collections; import java.util.List; /** @@ -95,7 +94,7 @@ public String identifier() { } public List bounds() { - return Collections.unmodifiableList(Arrays.asList(bounds)); + return new ImmutableArrayList<>(bounds); } Type[] boundArray() { diff --git a/core/src/main/java/org/jboss/jandex/Utils.java b/core/src/main/java/org/jboss/jandex/Utils.java index d0c32236..c6f8a667 100644 --- a/core/src/main/java/org/jboss/jandex/Utils.java +++ b/core/src/main/java/org/jboss/jandex/Utils.java @@ -18,10 +18,11 @@ package org.jboss.jandex; +import java.lang.reflect.Array; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,16 +42,17 @@ static String fromUTF8(byte[] bytes) { return new String(bytes, StandardCharsets.UTF_8); } - static List emptyOrWrap(List list) { - return list.size() == 0 ? Collections. emptyList() : Collections.unmodifiableList(list); - } - - static Collection emptyOrWrap(Collection list) { - return list.size() == 0 ? Collections. emptyList() : Collections.unmodifiableCollection(list); - } - - static Map emptyOrWrap(Map map) { - return map.size() == 0 ? Collections. emptyMap() : Collections.unmodifiableMap(map); + static Map unfold(Map> map, Class listElementType) { + if (map.isEmpty()) { + return Collections.emptyMap(); + } + + Map result = new HashMap<>(); + map.forEach((key, value) -> { + V[] array = (V[]) Array.newInstance(listElementType, value.size()); + result.put(key, value.toArray(array)); + }); + return result; } static List listOfCapacity(int capacity) {