Skip to content

Commit

Permalink
Merge pull request #185 from Ladicek/member-classes
Browse files Browse the repository at this point in the history
add ClassInfo.memberClasses
  • Loading branch information
Ladicek authored Apr 14, 2022
2 parents a4f848f + 65fc782 commit ad74eb1
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
24 changes: 24 additions & 0 deletions core/src/main/java/org/jboss/jandex/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Represents a class entry in an index. A ClassInfo is only a partial view of a
Expand Down Expand Up @@ -74,6 +75,7 @@ public final class ClassInfo implements AnnotationTarget {
private byte[] recordComponentPositions = EMPTY_POSITIONS;
private boolean hasNoArgsConstructor;
private NestingInfo nestingInfo;
private Set<DotName> memberClasses;

/** Describes the form of nesting used by a class */
public enum NestingType {
Expand Down Expand Up @@ -964,6 +966,24 @@ public EnclosingMethodInfo enclosingMethod() {
return nestingInfo != null ? nestingInfo.enclosingMethod : null;
}

/**
* Returns a set of names of member classes declared in this class. Member classes
* are classes directly enclosed in another class. That is, local classes and
* anonymous classes are not member classes.
* <p>
* Member classes of member classes are not included in the returned set.
* <p>
* Never returns {@code null}, but may return an empty set.
*
* @return immutable set of names of this class's member classes, never {@code null}
*/
public Set<DotName> memberClasses() {
if (memberClasses == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(memberClasses);
}

/**
* Returns the module information from this class if it is a module descriptor, i.e. {@code module-info}.
*
Expand Down Expand Up @@ -1162,6 +1182,10 @@ void setInnerClassInfo(DotName enclosingClass, String simpleName, boolean knownI
nestingInfo.simpleName = simpleName;
}

void setMemberClasses(Set<DotName> memberClasses) {
this.memberClasses = memberClasses;
}

void setEnclosingMethod(EnclosingMethodInfo enclosingMethod) {
if (enclosingMethod == null) {
return;
Expand Down
14 changes: 14 additions & 0 deletions core/src/main/java/org/jboss/jandex/IndexReaderV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,17 @@ private ClassInfo readClassEntry(PackedDataInputStream stream,
enclosingMethod = hasEnclosingMethod ? readEnclosingMethod(stream) : null;
}

Set<DotName> memberClasses = null;
if (version >= 11) {
int memberClassesCount = stream.readPackedU32();
if (memberClassesCount > 0) {
memberClasses = new HashSet<>(memberClassesCount);
for (int i = 0; i < memberClassesCount; i++) {
memberClasses.add(nameTable[stream.readPackedU32()]);
}
}
}

int size = stream.readPackedU32();

Map<DotName, List<AnnotationInstance>> annotations = size > 0
Expand All @@ -604,6 +615,9 @@ private ClassInfo readClassEntry(PackedDataInputStream stream,
// whether or not it is an inner type
clazz.setInnerClassInfo(enclosingClass, simpleName, version >= 9);
}
if (memberClasses != null) {
clazz.setMemberClasses(memberClasses);
}

FieldInternal[] fields = readClassFields(stream, clazz);
clazz.setFieldArray(fields);
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/jboss/jandex/IndexWriterV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,13 @@ private void writeClassEntry(PackedDataOutputStream stream, ClassInfo clazz) thr
}
}

if (version >= 11) {
stream.writePackedU32(clazz.memberClasses().size());
for (DotName memberClass : clazz.memberClasses()) {
stream.writePackedU32(positionOf(memberClass));
}
}

// Annotation length is early to allow eager allocation in reader.
stream.writePackedU32(clazz.annotationsMap().size());

Expand Down Expand Up @@ -906,6 +913,10 @@ private void addClass(ClassInfo clazz) {
}
addEnclosingMethod(clazz.enclosingMethod());

for (DotName memberClass : clazz.memberClasses()) {
addClassName(memberClass);
}

addMethodList(clazz.methodArray());
names.intern(clazz.methodPositionArray());

Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/org/jboss/jandex/Indexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Analyzes and indexes the annotation and key structural information of a set
Expand Down Expand Up @@ -669,6 +671,7 @@ private void processInnerClasses(DataInputStream data, ClassInfo target) throws
int numClasses = data.readUnsignedShort();
innerClasses = numClasses > 0 ? new HashMap<DotName, InnerClassInfo>(numClasses)
: Collections.<DotName, InnerClassInfo> emptyMap();
Set<DotName> memberClasses = new HashSet<>();
for (int i = 0; i < numClasses; i++) {
DotName innerClass = decodeClassEntry(data.readUnsignedShort());
int outerIndex = data.readUnsignedShort();
Expand All @@ -681,9 +684,15 @@ private void processInnerClasses(DataInputStream data, ClassInfo target) throws
target.setInnerClassInfo(outerClass, simpleName, true);
target.setFlags((short) flags);
}
if (outerClass != null && outerClass.equals(target.name())) {
memberClasses.add(innerClass);
}

innerClasses.put(innerClass, new InnerClassInfo(innerClass, outerClass, simpleName, flags));
}
if (!memberClasses.isEmpty()) {
target.setMemberClasses(memberClasses);
}
}

private void processMethodParameters(DataInputStream data, MethodInfo target) throws IOException {
Expand Down
188 changes: 188 additions & 0 deletions core/src/test/java/org/jboss/jandex/test/NestedClassesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package org.jboss.jandex.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.test.util.IndexingUtil;
import org.junit.jupiter.api.Test;

public class NestedClassesTest {
static class A {
static class B {
static class C {
}
}

static class D {
}

static Class<?>[] localClass() {
class E {
class F {
}
}

return new Class<?>[] { E.class, E.F.class };
}

static Class<?>[] anonymousClass() {
return new Object() {
class G {
}

Class<?>[] get() {
return new Class<?>[] { this.getClass(), G.class };
}
}.get();
}

interface H {
class I {
}
}

enum J {
;
static class K {
}
}

@interface L {
class M {
}
}
}

@Test
public void test() throws IOException {
Index index = Index.of(A.class, A.B.class, A.B.C.class, A.D.class,
A.localClass()[0], A.localClass()[1], A.anonymousClass()[0], A.anonymousClass()[1],
A.H.class, A.H.I.class, A.J.class, A.J.K.class, A.L.class, A.L.M.class);
test(index);

test(IndexingUtil.roundtrip(index));
}

private void test(Index index) {
checkNestingType(index, A.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.class, "B", "D", "H", "J", "L");
checkEnclosingClass(index, A.class, "NestedClassesTest");
checkEnclosingMethod(index, A.class, null, null);

checkNestingType(index, A.B.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.B.class, "C");
checkEnclosingClass(index, A.B.class, "A");
checkEnclosingMethod(index, A.B.class, null, null);

checkNestingType(index, A.B.C.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.B.C.class); // empty
checkEnclosingClass(index, A.B.C.class, "B");
checkEnclosingMethod(index, A.B.C.class, null, null);

checkNestingType(index, A.D.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.D.class); // empty
checkEnclosingClass(index, A.D.class, "A");
checkEnclosingMethod(index, A.D.class, null, null);

checkNestingType(index, A.localClass()[0], ClassInfo.NestingType.LOCAL);
checkMemberClasses(index, A.localClass()[0], "F");
checkEnclosingClass(index, A.localClass()[0], null);
checkEnclosingMethod(index, A.localClass()[0], "localClass", "A");

checkNestingType(index, A.localClass()[1], ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.localClass()[1]); // empty
checkEnclosingClass(index, A.localClass()[1], "not-null");
checkEnclosingMethod(index, A.localClass()[1], null, null);

checkNestingType(index, A.anonymousClass()[0], ClassInfo.NestingType.ANONYMOUS);
checkMemberClasses(index, A.anonymousClass()[0], "G");
checkEnclosingClass(index, A.anonymousClass()[0], null);
checkEnclosingMethod(index, A.anonymousClass()[0], "anonymousClass", "A");

checkNestingType(index, A.anonymousClass()[1], ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.anonymousClass()[1]); // empty
checkEnclosingClass(index, A.anonymousClass()[1], "not-null");
checkEnclosingMethod(index, A.anonymousClass()[1], null, null);

checkNestingType(index, A.H.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.H.class, "I");
checkEnclosingClass(index, A.H.class, "A");
checkEnclosingMethod(index, A.H.class, null, null);

checkNestingType(index, A.H.I.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.H.I.class); // empty
checkEnclosingClass(index, A.H.I.class, "H");
checkEnclosingMethod(index, A.H.I.class, null, null);

checkNestingType(index, A.J.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.J.class, "K");
checkEnclosingClass(index, A.J.class, "A");
checkEnclosingMethod(index, A.J.class, null, null);

checkNestingType(index, A.J.K.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.J.K.class); // empty
checkEnclosingClass(index, A.J.K.class, "J");
checkEnclosingMethod(index, A.J.K.class, null, null);

checkNestingType(index, A.L.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.L.class, "M");
checkEnclosingClass(index, A.L.class, "A");
checkEnclosingMethod(index, A.L.class, null, null);

checkNestingType(index, A.L.M.class, ClassInfo.NestingType.INNER);
checkMemberClasses(index, A.L.M.class); // empty
checkEnclosingClass(index, A.L.M.class, "L");
checkEnclosingMethod(index, A.L.M.class, null, null);
}

private void checkNestingType(Index index, Class<?> clazz, ClassInfo.NestingType expectedNestingType) {
ClassInfo classInfo = index.getClassByName(DotName.createSimple(clazz.getName()));
assertEquals(expectedNestingType, classInfo.nestingType());
}

private void checkMemberClasses(Index index, Class<?> clazz, String... expectedMemberClasses) {
Set<DotName> foundMemberClasses = index.getClassByName(DotName.createSimple(clazz.getName())).memberClasses();
Set<String> names = new HashSet<>();
for (DotName foundMemberClass : foundMemberClasses) {
names.add(foundMemberClass.local());
}
assertEquals(new HashSet<>(Arrays.asList(expectedMemberClasses)), names);
}

private void checkEnclosingClass(Index index, Class<?> clazz, String expectedEnclosingClassName) {
ClassInfo classInfo = index.getClassByName(DotName.createSimple(clazz.getName()));
DotName enclosingClass = classInfo.enclosingClass();
if (expectedEnclosingClassName == null) {
assertNull(enclosingClass);
} else {
assertNotNull(enclosingClass);
if (!"not-null".equals(expectedEnclosingClassName)) {
assertEquals(expectedEnclosingClassName, enclosingClass.local());
}
}
}

private void checkEnclosingMethod(Index index, Class<?> clazz, String expectedEnclosingMethodName,
String expectedEnclosingClassName) {
ClassInfo classInfo = index.getClassByName(DotName.createSimple(clazz.getName()));
ClassInfo.EnclosingMethodInfo enclosingMethod = classInfo.enclosingMethod();

if (expectedEnclosingMethodName == null) {
assertNull(enclosingMethod);
} else {
assertNotNull(enclosingMethod);
assertEquals(expectedEnclosingMethodName, enclosingMethod.name());
assertNotNull(enclosingMethod.enclosingClass());
assertEquals(expectedEnclosingClassName, enclosingMethod.enclosingClass().local());
}
}
}

0 comments on commit ad74eb1

Please sign in to comment.