Skip to content

Commit

Permalink
Merge pull request #270 from Ladicek/inner-constructor-type-annotations
Browse files Browse the repository at this point in the history
Fix parsing type annotations on constructors of inner classes
  • Loading branch information
Ladicek authored Nov 7, 2022
2 parents f962f13 + 75372c1 commit 8536dd6
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 15 deletions.
3 changes: 1 addition & 2 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Contributor Covenant Code of Conduct

## Our Pledge
Expand Down Expand Up @@ -129,4 +128,4 @@ For answers to common questions about this code of conduct, see the FAQ at
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
[translations]: https://www.contributor-covenant.org/translations
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Jandex is a space efficient Java class file indexer and offline reflection library.
See the [documentation](https://smallrye.io/jandex/).

# Getting Help
## Getting Help

Issues can be reported in [GitHub Issues](https://github.com/smallrye/jandex/issues).

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Please report any suspected security vulnerability in this project to Red Hat Pr

To report an issue in any Red Hat branded website or online service, please contact Red Hat Information Security at site-security@redhat.com.

https://access.redhat.com/security/team/contact
https://access.redhat.com/security/team/contact
4 changes: 2 additions & 2 deletions core/src/main/java/org/jboss/jandex/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -711,9 +711,9 @@ public final List<MethodInfo> unsortedMethods() {
* @return the list of constructors declared in this class
*/
public final List<MethodInfo> constructors() {
List<MethodInfo> constructors = new ArrayList<MethodInfo>(1);
List<MethodInfo> constructors = new ArrayList<>(1);
for (MethodInfo method : methods()) {
if ("<init>".equals(method.name())) {
if (method.isConstructor()) {
constructors.add(method);
}
}
Expand Down
3 changes: 1 addition & 2 deletions core/src/main/java/org/jboss/jandex/IndexReaderV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ final class IndexReaderV2 extends IndexReaderImpl {
private static final int AVALUE_ARRAY = 12;
private static final int AVALUE_NESTED = 13;
private static final int HAS_ENCLOSING_METHOD = 1;
private final static byte[] INIT_METHOD_NAME = Utils.toUTF8("<init>");

private final PackedDataInputStream input;
private final int version;
Expand Down Expand Up @@ -825,7 +824,7 @@ private MethodInternal[] readClassMethods(PackedDataInputStream stream, ClassInf
methods[i] = method;

if (version < 11 && method.parameterTypesArray().length == 0
&& Arrays.equals(INIT_METHOD_NAME, method.nameBytes())) {
&& Arrays.equals(Utils.INIT_METHOD_NAME, method.nameBytes())) {
clazz.setHasNoArgsConstructor(true);
}
}
Expand Down
25 changes: 19 additions & 6 deletions core/src/main/java/org/jboss/jandex/Indexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,6 @@ public final class Indexer {
private final static int HAS_RUNTIME_INVISIBLE_PARAM_ANNOTATION = 17;
private final static int HAS_RUNTIME_INVISIBLE_TYPE_ANNOTATION = 18;

private final static byte[] INIT_METHOD_NAME = Utils.toUTF8("<init>");

private static class InnerClassInfo {
private InnerClassInfo(DotName innerClass, DotName enclosingClass, String simpleName, int flags) {
this.innerClass = innerClass;
Expand Down Expand Up @@ -396,7 +394,7 @@ private void processMethodInfo(DataInputStream data) throws IOException {
MethodInfo method = new MethodInfo(currentClass, name, MethodInternal.EMPTY_PARAMETER_NAMES, parameters, returnType,
flags);

if (parameters.length == 0 && Arrays.equals(INIT_METHOD_NAME, name)) {
if (parameters.length == 0 && Arrays.equals(Utils.INIT_METHOD_NAME, name)) {
currentClass.setHasNoArgsConstructor(true);
}
methodParameterNames = debugParameterNames = null;
Expand Down Expand Up @@ -938,7 +936,7 @@ private void adjustMethodParameters() {
}

private static boolean isEnumConstructor(MethodInfo method) {
return method.declaringClass().isEnum() && Arrays.equals(INIT_METHOD_NAME, method.methodInternal().nameBytes());
return method.declaringClass().isEnum() && method.isConstructor();
}

private void resolveTypeAnnotations() {
Expand Down Expand Up @@ -1015,7 +1013,7 @@ private static void setTypeParameters(AnnotationTarget target, Type[] typeParame
}

private boolean isInnerConstructor(MethodInfo method) {
if (!Arrays.equals(INIT_METHOD_NAME, method.methodInternal().nameBytes())) {
if (!method.isConstructor()) {
return false;
}

Expand Down Expand Up @@ -1110,7 +1108,19 @@ private void resolveTypeAnnotation(AnnotationTarget target, TypeAnnotationState
if (skipBridge(typeAnnotationState, method)) {
return;
}
method.setReturnType(resolveTypePath(returnType, typeAnnotationState));
if (!method.isConstructor()) {
method.setReturnType(resolveTypePath(returnType, typeAnnotationState));
} else {
// create a synthetic `ClassType` for the purpose of resolving the type path,
// which would fail on a `VoidType` if the path points to a nested type
// (this happens on inner class constructors with type annotations)
Type newType = new ClassType(method.declaringClass().name());
newType = resolveTypePath(newType, typeAnnotationState);
returnType = returnType.copyType(newType.annotationArray());
// fixup, `resolveTypePath` sets `typeAnnotationState.target` to the synthetic `ClassType`
typeAnnotationState.target.setTarget(returnType);
method.setReturnType(returnType);
}
}
} else if (typeTarget.usage() == TypeTarget.Usage.EMPTY && target instanceof RecordComponentInfo) {
RecordComponentInfo recordComponent = (RecordComponentInfo) target;
Expand Down Expand Up @@ -1279,6 +1289,9 @@ private void updateTypeTarget(AnnotationTarget enclosingTarget, TypeAnnotationSt
if (skipBridge(typeAnnotationState, method)) {
return;
}
if (method.isConstructor()) {
return;
}
}
break;
}
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/org/jboss/jandex/MethodInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -578,6 +579,13 @@ public final boolean isSynthetic() {
return Modifiers.isSynthetic(methodInternal.flags());
}

/**
* @return {@code true} if this method is a constructor
*/
public boolean isConstructor() {
return Arrays.equals(Utils.INIT_METHOD_NAME, methodInternal.nameBytes());
}

/**
* Returns a string representation describing this field. It is similar although not
* necessarily identical to a Java source code expression representing this field.
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/jboss/jandex/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
* @author Jason T. Greene
*/
class Utils {
static final byte[] INIT_METHOD_NAME = Utils.toUTF8("<init>");

static byte[] toUTF8(String string) {
return string.getBytes(StandardCharsets.UTF_8);
}
Expand Down
6 changes: 6 additions & 0 deletions core/src/test/java/org/jboss/jandex/test/BasicTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ private void verifyDummy(Index index, boolean v2features) {
assertEquals("somethingelse", overrideValue.asString());

assertNotNull(method);
assertFalse(method.isConstructor());
assertEquals(3, method.annotations().size());
assertEquals(MethodAnnotation1.class.getName(),
method.annotation(DotName.createSimple(MethodAnnotation1.class.getName())).name().toString());
Expand All @@ -638,13 +639,15 @@ private void verifyDummy(Index index, boolean v2features) {
assertNotNull(nested);
MethodInfo nestedConstructor1 = nested.method("<init>", PrimitiveType.INT);
assertNotNull(nestedConstructor1);
assertTrue(nestedConstructor1.isConstructor());
assertEquals(1, nestedConstructor1.parametersCount());
assertEquals(1, nestedConstructor1.parameterTypes().size());
assertEquals(1, nestedConstructor1.parameters().size());
assertEquals("noAnnotation", nestedConstructor1.parameterName(0));

MethodInfo nestedConstructor2 = nested.method("<init>", PrimitiveType.BYTE);
assertNotNull(nestedConstructor2);
assertTrue(nestedConstructor2.isConstructor());
// synthetic param counts here
assertEquals(1, nestedConstructor2.parametersCount());
assertEquals(1, nestedConstructor2.parameterTypes().size());
Expand Down Expand Up @@ -681,11 +684,13 @@ private void verifyDummy(Index index, boolean v2features) {
assertNotNull(enumClass);
MethodInfo enumConstructor1 = enumClass.method("<init>", PrimitiveType.INT);
assertNotNull(enumConstructor1);
assertTrue(enumConstructor1.isConstructor());
assertEquals(1, enumConstructor1.parametersCount());
assertEquals("noAnnotation", enumConstructor1.parameterName(0));

MethodInfo enumConstructor2 = enumClass.method("<init>", PrimitiveType.BYTE);
assertNotNull(enumConstructor2);
assertTrue(enumConstructor2.isConstructor());
assertEquals(1, enumConstructor2.parametersCount());
assertEquals("annotated", enumConstructor2.parameterName(0));

Expand All @@ -699,6 +704,7 @@ private void verifyDummy(Index index, boolean v2features) {
assertNotNull(enumWithGenericConstructorClass);
MethodInfo ctor = enumWithGenericConstructorClass.firstMethod("<init>");
assertNotNull(ctor);
assertTrue(ctor.isConstructor());
assertEquals(1, ctor.parametersCount());
assertEquals(Type.Kind.PARAMETERIZED_TYPE, ctor.parameterType(0).kind());
assertEquals("java.util.List", ctor.parameterType(0).asParameterizedType().name().toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.jboss.jandex.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.Index;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.jandex.TypeTarget;
import org.jboss.jandex.test.util.IndexingUtil;
import org.junit.jupiter.api.Test;

public class TypeAnnotationOnInnerClassConstructorTest {
@MyAnnotation("top-level")
TypeAnnotationOnInnerClassConstructorTest() {
}

class InnerClass {
@MyAnnotation("inner")
public InnerClass() {
}

class InnerInnerClass {
@MyAnnotation("inner-inner")
public InnerInnerClass() {
}
}
}

@Test
public void test() throws IOException {
Index index = Index.of(TypeAnnotationOnInnerClassConstructorTest.class, InnerClass.class,
InnerClass.InnerInnerClass.class);
test(index);
test(IndexingUtil.roundtrip(index));
}

private void test(Index index) {
testConstructor(index.getClassByName(TypeAnnotationOnInnerClassConstructorTest.class), "top-level");
testConstructor(index.getClassByName(InnerClass.class), "inner");
testConstructor(index.getClassByName(InnerClass.InnerInnerClass.class), "inner-inner");

for (AnnotationInstance annotation : index.getAnnotations(MyAnnotation.DOT_NAME)) {
assertTrue(annotation.target().kind() == AnnotationTarget.Kind.METHOD
|| annotation.target().kind() == AnnotationTarget.Kind.TYPE);

if (annotation.target().kind() == AnnotationTarget.Kind.TYPE) {
TypeTarget typeAnnotationTarget = annotation.target().asType();
assertEquals(AnnotationTarget.Kind.METHOD, typeAnnotationTarget.enclosingTarget().kind());
assertTrue(typeAnnotationTarget.enclosingTarget().asMethod().isConstructor());
assertEquals(Type.Kind.VOID, typeAnnotationTarget.target().kind());
}
}
}

private void testConstructor(ClassInfo clazz, String annotationValue) {
assertEquals(1, clazz.constructors().size());

MethodInfo ctor = clazz.constructors().get(0);
assertTrue(ctor.hasAnnotation(MyAnnotation.DOT_NAME));
assertEquals(annotationValue, ctor.annotation(MyAnnotation.DOT_NAME).value().asString());

Type ctorType = ctor.returnType();
assertTrue(ctorType.hasAnnotation(MyAnnotation.DOT_NAME));
assertEquals(annotationValue, ctorType.annotation(MyAnnotation.DOT_NAME).value().asString());
}
}
2 changes: 1 addition & 1 deletion dco.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ By making a contribution to this project, I certify that:
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
this project or the open source license(s) involved.

0 comments on commit 8536dd6

Please sign in to comment.