Skip to content

Commit bb96f1e

Browse files
committed
#80 - Investigate use of MethodHandles instead of Constructor and Method references
# Conflicts: # hibernate-models/src/main/java/org/hibernate/models/internal/MethodInvocationException.java # hibernate-models/src/main/java/org/hibernate/models/internal/MethodResolutionException.java # hibernate-models/src/main/java/org/hibernate/models/internal/OrmAnnotationDescriptor.java # hibernate-models/src/test/java/org/hibernate/models/testing/tests/MethodHandleSmokeTests.java # src/main/java/org/hibernate/models/internal/AnnotationHelper.java # src/main/java/org/hibernate/models/spi/SharedNamedAnnotationScope.java
1 parent 02c1f2a commit bb96f1e

File tree

6 files changed

+78
-53
lines changed

6 files changed

+78
-53
lines changed

hibernate-models-jandex/src/main/java/org/hibernate/models/jandex/internal/JandexValueHelper.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ public static <V> V extractOptionalValue(
6060
SourceModelBuildingContext modelContext) {
6161
final AnnotationValue value = usage.value( attributeDescriptor.getName() );
6262
if ( value == null ) {
63-
//noinspection unchecked
64-
return (V) attributeDescriptor.getAttributeMethod().getDefaultValue();
63+
return attributeDescriptor.getDefaultValue();
6564
}
6665

6766
return modelContext.as( JandexModelContext.class )

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

+5-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.hibernate.models.spi.AnnotationDescriptor;
1515
import org.hibernate.models.spi.AttributeDescriptor;
1616
import org.hibernate.models.spi.SourceModelBuildingContext;
17-
import org.hibernate.models.spi.SourceModelContext;
1817

1918
/**
2019
* @see AnnotationHelper
@@ -102,7 +101,7 @@ public static <A extends Annotation, C extends Annotation> void forEachRepeatedA
102101
if ( container != null ) {
103102
final AnnotationDescriptor<C> containerDescriptor = modelContext.getAnnotationDescriptorRegistry().getDescriptor( containerType );
104103
final AttributeDescriptor<A[]> attribute = containerDescriptor.getAttribute( "value" );
105-
final A[] repetitions = AnnotationHelper.extractValue( container, attribute );
104+
final A[] repetitions = AnnotationHelper.extractValue( container, attribute, modelContext );
106105
CollectionHelper.forEach( repetitions, consumer );
107106
}
108107
}
@@ -123,7 +122,7 @@ public static <A extends Annotation> void forEachRepeatedAnnotationUsages(
123122
if ( container != null ) {
124123
final AnnotationDescriptor<?> containerDescriptor = modelContext.getAnnotationDescriptorRegistry().getDescriptor( containerType );
125124
final AttributeDescriptor<A[]> attribute = containerDescriptor.getAttribute( "value" );
126-
final A[] repetitions = AnnotationHelper.extractValue( container, attribute );
125+
final A[] repetitions = AnnotationHelper.extractValue( container, attribute, modelContext );
127126
CollectionHelper.forEach( repetitions, consumer );
128127
}
129128
}
@@ -141,7 +140,7 @@ public static <A extends Annotation> A[] getRepeatedUsages(
141140

142141
if ( containerUsage != null ) {
143142
final AttributeDescriptor<A[]> attribute = type.getRepeatableContainer().getAttribute( "value" );
144-
final A[] repeatableValues = AnnotationHelper.extractValue( containerUsage, attribute );
143+
final A[] repeatableValues = AnnotationHelper.extractValue( containerUsage, attribute, modelContext );
145144

146145
if ( CollectionHelper.isNotEmpty( repeatableValues ) ) {
147146
if ( usage != null ) {
@@ -206,9 +205,9 @@ private static <A extends Annotation> boolean nameMatches(
206205
AnnotationDescriptor<A> descriptor,
207206
String matchValue,
208207
String attributeToMatch,
209-
SourceModelContext modelContext) {
208+
SourceModelBuildingContext modelContext) {
210209
final AttributeDescriptor<String> attributeDescriptor = descriptor.getAttribute( attributeToMatch );
211-
final String usageName = AnnotationHelper.extractValue( annotationUsage, attributeDescriptor );
210+
final String usageName = AnnotationHelper.extractValue( annotationUsage, attributeDescriptor, modelContext );
212211
return matchValue.equals( usageName );
213212
}
214213

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

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ public ValueTypeDescriptor<T> getTypeDescriptor() {
5050
return typeDescriptor;
5151
}
5252

53+
@Override
54+
public T getDefaultValue() {
55+
//noinspection unchecked
56+
return (T) method.getDefaultValue();
57+
}
58+
5359
@Override
5460
public Method getAttributeMethod() {
5561
return method;

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

+53-40
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
package org.hibernate.models.internal;
66

77
import java.lang.annotation.Annotation;
8-
import java.lang.reflect.Constructor;
9-
import java.lang.reflect.InvocationTargetException;
8+
import java.lang.invoke.MethodHandle;
9+
import java.lang.invoke.MethodHandles;
10+
import java.lang.invoke.MethodType;
1011
import java.util.Collections;
1112
import java.util.List;
1213
import java.util.Map;
@@ -22,6 +23,9 @@
2223
* does not collect annotations from the annotation class as we never care about
2324
* meta-annotations in these cases.
2425
*
26+
* @implNote There are a few cases in Hibernate ORM e.g. where we do care about meta-annotations,
27+
* but those are handled specially there.
28+
*
2529
* @author Steve Ebersole
2630
*/
2731
public class OrmAnnotationDescriptor<A extends Annotation, C extends A>
@@ -30,8 +34,8 @@ public class OrmAnnotationDescriptor<A extends Annotation, C extends A>
3034
private final Class<C> concreteClass;
3135
private final List<AttributeDescriptor<?>> attributeDescriptors;
3236

33-
private DynamicCreator<A,C> dynamicCreator;
34-
private JdkCreator<A,C> jdkCreator;
37+
private final DynamicCreator<A,C> dynamicCreator;
38+
private final JdkCreator<A,C> jdkCreator;
3539
private DeTypedCreator<A,C> deTypedCreator;
3640

3741
public OrmAnnotationDescriptor(
@@ -53,6 +57,9 @@ public OrmAnnotationDescriptor(
5357

5458
this.concreteClass = concreteClass;
5559
this.attributeDescriptors = AnnotationDescriptorBuilding.extractAttributeDescriptors( annotationType );
60+
61+
this.dynamicCreator = new DynamicCreator<>( annotationType, concreteClass );
62+
this.jdkCreator = new JdkCreator<>( annotationType, concreteClass );
5663
}
5764

5865
@Override
@@ -69,17 +76,11 @@ public Class<C> getMutableAnnotationType() {
6976

7077
@Override
7178
public C createUsage(SourceModelBuildingContext context) {
72-
if ( dynamicCreator == null ) {
73-
dynamicCreator = new DynamicCreator<>( getAnnotationType(), concreteClass );
74-
}
7579
return dynamicCreator.createUsage( context );
7680
}
7781

7882
@Override
7983
public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) {
80-
if ( jdkCreator == null ) {
81-
jdkCreator = new JdkCreator<>( getAnnotationType(), concreteClass );
82-
}
8384
return jdkCreator.createUsage( jdkAnnotation, context );
8485
}
8586

@@ -102,93 +103,105 @@ public String toString() {
102103
}
103104

104105
public static class DynamicCreator<A extends Annotation, C extends A> {
105-
private final Constructor<C> constructor;
106+
private final MethodHandle constructor;
107+
private final Class<C> concreteClass;
106108

107109
public DynamicCreator(Class<A> annotationType, Class<C> concreteClass) {
108-
this( resolveConstructor( concreteClass ) );
110+
this( resolveConstructor( concreteClass ), concreteClass );
109111
}
110112

111-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(Class<C> concreteClass) {
113+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(Class<C> concreteClass) {
112114
try {
113-
return concreteClass.getDeclaredConstructor( SourceModelBuildingContext.class );
115+
final MethodType methodType = MethodType.methodType( void.class, SourceModelBuildingContext.class );
116+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
114117
}
115-
catch (NoSuchMethodException e) {
116-
throw new RuntimeException( e );
118+
catch (Exception e) {
119+
throw new MethodResolutionException( "Unable to locate default-variant constructor for `" + concreteClass.getName() + "`", e );
117120
}
118121
}
119122

120-
public DynamicCreator(Constructor<C> constructor) {
123+
public DynamicCreator(MethodHandle constructor, Class<C> concreteClass) {
121124
this.constructor = constructor;
125+
this.concreteClass = concreteClass;
122126
}
123127

124128
public C createUsage(SourceModelBuildingContext context) {
125129
try {
126-
return constructor.newInstance( context );
130+
//noinspection unchecked
131+
return (C) constructor.invoke( context );
127132
}
128-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
129-
throw new RuntimeException( e );
133+
catch (Throwable e) {
134+
throw new MethodInvocationException( "Unable to invoke default-variant constructor for `" + concreteClass.getName() + "`", e );
130135
}
131136
}
132137
}
133138

134139
public static class JdkCreator<A extends Annotation, C extends A> {
135-
private final Constructor<C> constructor;
140+
private final MethodHandle constructor;
141+
private final Class<C> concreteClass;
136142

137143
public JdkCreator(Class<A> annotationType, Class<C> concreteClass) {
138-
this( resolveConstructor( annotationType, concreteClass ) );
144+
this( resolveConstructor( annotationType, concreteClass ), concreteClass );
139145
}
140146

141-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(
147+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(
142148
Class<A> annotationType,
143149
Class<C> concreteClass) {
144150
try {
145-
return concreteClass.getDeclaredConstructor( annotationType, SourceModelBuildingContext.class );
151+
final MethodType methodType = MethodType.methodType( void.class, annotationType, SourceModelBuildingContext.class );
152+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
146153
}
147-
catch (NoSuchMethodException e) {
148-
throw new RuntimeException( e );
154+
catch (Exception e) {
155+
throw new MethodResolutionException( "Unable to locate JDK-variant constructor for `" + concreteClass.getName() + "`", e );
149156
}
150157
}
151158

152-
public JdkCreator(Constructor<C> constructor) {
159+
public JdkCreator(MethodHandle constructor, Class<C> concreteClass) {
153160
this.constructor = constructor;
161+
this.concreteClass = concreteClass;
154162
}
155163

156164
public C createUsage(A jdkAnnotation, SourceModelBuildingContext context) {
157165
try {
158-
return constructor.newInstance( jdkAnnotation, context );
166+
//noinspection unchecked
167+
return (C) constructor.invoke( jdkAnnotation, context );
159168
}
160-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
161-
throw new RuntimeException( e );
169+
catch (Throwable e) {
170+
throw new MethodInvocationException( "Unable to invoke JDK-variant constructor for `" + concreteClass.getName() + "`", e );
162171
}
163172
}
164173
}
165174

166175
public static class DeTypedCreator<A extends Annotation, C extends A> {
167-
private final Constructor<C> constructor;
176+
private final MethodHandle constructor;
177+
private final Class<C> concreteClass;
168178

169179
public DeTypedCreator(Class<A> annotationType, Class<C> concreteClass) {
170-
this( resolveConstructor( concreteClass ) );
180+
this( resolveConstructor( concreteClass ), concreteClass );
171181
}
172182

173-
private static <A extends Annotation, C extends A> Constructor<C> resolveConstructor(Class<C> concreteClass) {
183+
private static <A extends Annotation, C extends A> MethodHandle resolveConstructor(Class<C> concreteClass) {
174184
try {
175-
return concreteClass.getDeclaredConstructor( Map.class, SourceModelBuildingContext.class );
185+
final MethodType methodType = MethodType.methodType( void.class, AnnotationInstance.class, SourceModelBuildingContext.class );
186+
return MethodHandles.publicLookup().findConstructor( concreteClass, methodType );
176187
}
177-
catch (NoSuchMethodException e) {
178-
throw new RuntimeException( e );
188+
catch (Exception e) {
189+
throw new MethodResolutionException( "Unable to locate Jandex-variant constructor for `" + concreteClass.getName() + "`", e );
179190
}
180191
}
181192

182-
public DeTypedCreator(Constructor<C> constructor) {
193+
public DeTypedCreator(MethodHandle constructor, Class<C> concreteClass) {
183194
this.constructor = constructor;
195+
this.concreteClass = concreteClass;
184196
}
185197

186198
public C createUsage(Map<String,?> attributeValues, SourceModelBuildingContext context) {
187199
try {
188-
return constructor.newInstance( attributeValues, context );
200+
//noinspection unchecked
201+
return (C) constructor.invoke( attributeValues, context );
189202
}
190-
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
191-
throw new RuntimeException( e );
203+
catch (Throwable e) {
204+
throw new MethodInvocationException( "Unable to invoke Jandex-variant constructor for `" + concreteClass.getName() + "`", e );
192205
}
193206
}
194207
}

hibernate-models/src/main/java/org/hibernate/models/spi/AttributeDescriptor.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@ public interface AttributeDescriptor<T> {
2222
*/
2323
ValueTypeDescriptor<T> getTypeDescriptor();
2424

25+
/**
26+
* The attribute's type.
27+
*/
28+
default Class<T> getValueType() {
29+
return getTypeDescriptor().getValueType();
30+
}
31+
32+
/**
33+
* The attribute's defined default value, if one.
34+
*/
35+
T getDefaultValue();
36+
2537
/**
2638
* The attribute method.
2739
*/
2840
Method getAttributeMethod();
29-
30-
default boolean isMultiValued() {
31-
return getAttributeMethod().getReturnType().isArray();
32-
}
3341
}

version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.0-SNAPSHOT
1+
1.0.0-METHOD-HANDLE

0 commit comments

Comments
 (0)