Skip to content

Commit c946009

Browse files
committed
#99 - Recursive annotations lead to StackOverflowError
1 parent 9be40cb commit c946009

File tree

4 files changed

+148
-5
lines changed

4 files changed

+148
-5
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.annotations;
6+
7+
import java.lang.annotation.ElementType;
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.RetentionPolicy;
10+
import java.lang.annotation.Target;
11+
12+
import org.hibernate.models.spi.AnnotationDescriptor;
13+
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
14+
import org.hibernate.models.spi.ClassDetails;
15+
import org.hibernate.models.spi.ClassDetailsRegistry;
16+
import org.hibernate.models.spi.SourceModelBuildingContext;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import org.jboss.jandex.Index;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
import static org.hibernate.models.SourceModelTestHelper.buildJandexIndex;
24+
import static org.hibernate.models.SourceModelTestHelper.createBuildingContext;
25+
26+
/**
27+
* @author Steve Ebersole
28+
*/
29+
public class AnnotationCycleTests {
30+
@Test
31+
void testWithJandex() {
32+
testAnnotationCycle( buildJandexIndex( SelfReferenceTests.SimpleClass.class ) );
33+
}
34+
35+
@Test
36+
void testWithoutJandex() {
37+
testAnnotationCycle( null );
38+
}
39+
40+
private void testAnnotationCycle(Index jandexIndex) {
41+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, SimpleClass.class );
42+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
43+
final AnnotationDescriptorRegistry descriptorRegistry = buildingContext.getAnnotationDescriptorRegistry();
44+
45+
final ClassDetails classDetails = classDetailsRegistry.getClassDetails( SimpleClass.class.getName() );
46+
assertThat( classDetails.hasDirectAnnotationUsage( A.class ) ).isTrue();
47+
assertThat( classDetails.hasDirectAnnotationUsage( B.class ) ).isTrue();
48+
49+
final AnnotationDescriptor<A> aDescriptor = descriptorRegistry.getDescriptor( A.class );
50+
final AnnotationDescriptor<B> bDescriptor = descriptorRegistry.getDescriptor( B.class );
51+
assertThat( aDescriptor.hasDirectAnnotationUsage( B.class ) ).isTrue();
52+
assertThat( bDescriptor.hasDirectAnnotationUsage( A.class ) ).isTrue();
53+
}
54+
55+
@B
56+
@Retention(RetentionPolicy.RUNTIME)
57+
@Target(ElementType.TYPE)
58+
public @interface A {
59+
}
60+
61+
@A
62+
@Retention(RetentionPolicy.RUNTIME)
63+
@Target(ElementType.TYPE)
64+
public @interface B {
65+
}
66+
67+
@A @B
68+
public static class SimpleClass {
69+
}
70+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.annotations;
6+
7+
import org.hibernate.models.spi.AnnotationDescriptor;
8+
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
9+
import org.hibernate.models.spi.ClassDetails;
10+
import org.hibernate.models.spi.ClassDetailsRegistry;
11+
import org.hibernate.models.spi.SourceModelBuildingContext;
12+
13+
import org.junit.jupiter.api.Test;
14+
15+
import org.jboss.jandex.IndexView;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.hibernate.models.SourceModelTestHelper.buildJandexIndex;
19+
import static org.hibernate.models.SourceModelTestHelper.createBuildingContext;
20+
21+
/**
22+
* Tests for annotations that use themselves
23+
*
24+
* @author Steve Ebersole
25+
*/
26+
public class SelfReferenceTests {
27+
@Test
28+
void testWithJandex() {
29+
testSelfReferencingAnnotation( buildJandexIndex( SimpleClass.class ) );
30+
}
31+
32+
@Test
33+
void testWithoutJandex() {
34+
testSelfReferencingAnnotation( null );
35+
}
36+
37+
private void testSelfReferencingAnnotation(IndexView jandexIndex) {
38+
final SourceModelBuildingContext buildingContext = createBuildingContext( jandexIndex, SimpleClass.class );
39+
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
40+
final AnnotationDescriptorRegistry descriptorRegistry = buildingContext.getAnnotationDescriptorRegistry();
41+
42+
final ClassDetails classDetails = classDetailsRegistry.getClassDetails( SimpleClass.class.getName() );
43+
assertThat( classDetails.hasAnnotationUsage( SelfReferencingAnnotation.class, buildingContext ) ).isTrue();
44+
45+
final AnnotationDescriptor<SelfReferencingAnnotation> descriptor = descriptorRegistry.getDescriptor( SelfReferencingAnnotation.class );
46+
assertThat( descriptor.hasAnnotationUsage( SelfReferencingAnnotation.class, buildingContext ) ).isTrue();
47+
}
48+
49+
@SelfReferencingAnnotation
50+
public static class SimpleClass {
51+
}
52+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.annotations;
6+
7+
import java.lang.annotation.ElementType;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
import java.lang.annotation.Retention;
11+
12+
/**
13+
* @author Steve Ebersole
14+
*/
15+
@Target(ElementType.TYPE)
16+
@Retention(RetentionPolicy.RUNTIME)
17+
@SelfReferencingAnnotation
18+
public @interface SelfReferencingAnnotation {
19+
}

hibernate-models/src/main/java/org/hibernate/models/internal/StandardAnnotationDescriptor.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
/*
2-
* Hibernate, Relational Persistence for Idiomatic Java
3-
*
42
* SPDX-License-Identifier: Apache-2.0
53
* Copyright: Red Hat Inc. and Hibernate Authors
64
*/
7-
85
package org.hibernate.models.internal;
96

107
import java.lang.annotation.Annotation;
@@ -28,7 +25,9 @@
2825
*/
2926
public class StandardAnnotationDescriptor<A extends Annotation> extends AbstractAnnotationDescriptor<A> {
3027
private final List<AttributeDescriptor<?>> attributeDescriptors;
31-
private final Map<Class<? extends Annotation>, ? extends Annotation> usagesMap;
28+
private final SourceModelBuildingContext buildingContext;
29+
30+
private Map<Class<? extends Annotation>, ? extends Annotation> usagesMap;
3231

3332
public StandardAnnotationDescriptor(
3433
Class<A> annotationType,
@@ -42,12 +41,15 @@ public StandardAnnotationDescriptor(
4241
SourceModelBuildingContext buildingContext) {
4342
super( annotationType, AnnotationHelper.extractTargets( annotationType ), AnnotationHelper.isInherited( annotationType ), repeatableContainer );
4443

44+
this.buildingContext = buildingContext;
4545
this.attributeDescriptors = AnnotationDescriptorBuilding.extractAttributeDescriptors( annotationType );
46-
this.usagesMap = buildUsagesMap( annotationType, buildingContext );
4746
}
4847

4948
@Override
5049
public Map<Class<? extends Annotation>, ? extends Annotation> getUsageMap() {
50+
if ( usagesMap == null ) {
51+
usagesMap = buildUsagesMap( getAnnotationType(), buildingContext );
52+
}
5153
return usagesMap;
5254
}
5355

0 commit comments

Comments
 (0)