From a1921fab71933ce4d40d0c8ced6a2933bd741c6d Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Wed, 27 Mar 2024 13:10:00 +0100 Subject: [PATCH 1/3] replace lists with arrays in Index --- .../src/main/java/org/jboss/jandex/Index.java | 109 ++++++++++++------ .../java/org/jboss/jandex/IndexReaderV2.java | 2 +- .../java/org/jboss/jandex/IndexWriterV2.java | 10 +- .../main/java/org/jboss/jandex/Indexer.java | 2 +- .../main/java/org/jboss/jandex/Result.java | 9 +- .../src/main/java/org/jboss/jandex/Utils.java | 22 ++-- 6 files changed, 99 insertions(+), 55 deletions(-) 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 12b227f6..2265340d 100644 --- a/core/src/main/java/org/jboss/jandex/IndexReaderV2.java +++ b/core/src/main/java/org/jboss/jandex/IndexReaderV2.java @@ -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 4c496211..6af4b758 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())); } @@ -905,7 +905,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 93d9e625..24fc9253 100644 --- a/core/src/main/java/org/jboss/jandex/Indexer.java +++ b/core/src/main/java/org/jboss/jandex/Indexer.java @@ -2557,7 +2557,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/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/Utils.java b/core/src/main/java/org/jboss/jandex/Utils.java index 08525520..262e9280 100644 --- a/core/src/main/java/org/jboss/jandex/Utils.java +++ b/core/src/main/java/org/jboss/jandex/Utils.java @@ -21,10 +21,11 @@ import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.InputStream; +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; import java.util.Objects; @@ -47,16 +48,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 unfold(Map> map, Class listElementType) { + if (map.isEmpty()) { + return Collections.emptyMap(); + } - static Map emptyOrWrap(Map map) { - return map.size() == 0 ? Collections. emptyMap() : Collections.unmodifiableMap(map); + 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) { From 683a48fe26e41a14cd785a4f0f7bc6a7aeef5922 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Wed, 27 Mar 2024 13:11:54 +0100 Subject: [PATCH 2/3] move seldom used fields of MethodInternal to a separate class --- .../java/org/jboss/jandex/MethodInternal.java | 143 ++++++++++++------ 1 file changed, 100 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/org/jboss/jandex/MethodInternal.java b/core/src/main/java/org/jboss/jandex/MethodInternal.java index 55970e1d..9725afd2 100644 --- a/core/src/main/java/org/jboss/jandex/MethodInternal.java +++ b/core/src/main/java/org/jboss/jandex/MethodInternal.java @@ -18,6 +18,7 @@ package org.jboss.jandex; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -31,7 +32,7 @@ final class MethodInternal { 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); @@ -74,16 +75,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; @@ -105,11 +111,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; } @@ -125,10 +151,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)) { @@ -146,16 +176,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 @@ -166,11 +201,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; } @@ -184,10 +219,14 @@ 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 (!TypeInterning.arrayEquals(exceptions, methodInternal.exceptions)) { @@ -205,17 +244,21 @@ boolean internEquals(Object o) { if (!TypeInterning.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 && !TypeInterning.arrayEquals(extra.typeParameters, methodInternal.extra.typeParameters)) { return false; } - return TypeInterning.arrayEquals(typeParameters, methodInternal.typeParameters); + return true; } int internHashCode() { @@ -225,11 +268,11 @@ int internHashCode() { result = 31 * result + TypeInterning.arrayHashCode(descriptorParameterTypes); result = 31 * result + returnType.internHashCode(); result = 31 * result + TypeInterning.arrayHashCode(exceptions); - result = 31 * result + (receiverType != null ? receiverType.internHashCode() : 0); - result = 31 * result + TypeInterning.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 ? TypeInterning.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; } @@ -284,8 +327,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,7 +345,7 @@ final Type receiverType(ClassInfo clazz) { } final Type receiverTypeField() { - return receiverType; + return extra != null ? extra.receiverType : null; } final List exceptions() { @@ -315,19 +358,21 @@ final Type[] exceptionArray() { final List typeParameters() { // type parameters are always `TypeVariable` - return new ImmutableArrayList(typeParameters); + return extra != null && extra.typeParameters != null ? new ImmutableArrayList(extra.typeParameters) + : Collections.emptyList(); } final List annotations() { - return new ImmutableArrayList<>(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) { @@ -335,11 +380,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() { @@ -351,8 +396,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(", "); } @@ -384,7 +429,10 @@ public String toString() { void setTypeParameters(Type[] typeParameters) { if (typeParameters.length > 0) { - this.typeParameters = typeParameters; + if (extra == null) { + extra = new ExtraInfo(); + } + extra.typeParameters = typeParameters; } } @@ -405,17 +453,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; } } From 261a82848668d777472c5d6fdc5bb9f99296ac10 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Thu, 28 Mar 2024 11:19:42 +0100 Subject: [PATCH 3/3] move seldom used fields of ClassInfo to a separate class --- .../main/java/org/jboss/jandex/ClassInfo.java | 124 +++++++++++++----- 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/org/jboss/jandex/ClassInfo.java b/core/src/main/java/org/jboss/jandex/ClassInfo.java index bcd8dc72..54970b07 100644 --- a/core/src/main/java/org/jboss/jandex/ClassInfo.java +++ b/core/src/main/java/org/jboss/jandex/ClassInfo.java @@ -63,18 +63,15 @@ public final class ClassInfo implements Declaration, Descriptor, GenericSignatur // 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 { @@ -93,16 +90,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; } /** @@ -187,7 +187,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; } @@ -853,12 +852,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]); } /** @@ -868,7 +871,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); } /** @@ -885,15 +892,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; } /** @@ -1003,12 +1014,20 @@ public final Type superClassType() { * @return immutable list of generic type parameters of this class */ public final List typeParameters() { + if (extra == null || extra.typeParameters == null) { + return Collections.emptyList(); + } + // type parameters are always `TypeVariable` - return new ImmutableArrayList(typeParameters); + return new ImmutableArrayList(extra.typeParameters); } final Type[] typeParameterArray() { - return typeParameters; + if (extra == null || extra.typeParameters == null) { + return Type.EMPTY_ARRAY; + } + + return extra.typeParameters; } /** @@ -1040,7 +1059,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; @@ -1099,10 +1118,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); } /** @@ -1111,7 +1130,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; } /** @@ -1252,31 +1271,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); } @@ -1330,7 +1368,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) { @@ -1351,7 +1397,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) { @@ -1371,11 +1425,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) {