Skip to content

Commit 916b41b

Browse files
committed
Add possibility to define custom constructor resolver for super annotation.
1 parent 4a21948 commit 916b41b

File tree

6 files changed

+109
-27
lines changed

6 files changed

+109
-27
lines changed

byte-buddy-dep/src/main/java/net/bytebuddy/implementation/auxiliary/TypeProxy.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import net.bytebuddy.dynamic.scaffold.TypeValidation;
2929
import net.bytebuddy.implementation.Implementation;
3030
import net.bytebuddy.implementation.MethodAccessorFactory;
31+
import net.bytebuddy.implementation.bind.annotation.Super;
3132
import net.bytebuddy.implementation.bytecode.*;
3233
import net.bytebuddy.implementation.bytecode.constant.DefaultValue;
3334
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
@@ -395,16 +396,13 @@ public static class ForSuperMethodByConstructor extends StackManipulation.Abstra
395396
*/
396397
private final TypeDescription proxiedType;
397398

399+
private final MethodDescription.InDefinedShape constructor;
400+
398401
/**
399402
* The implementation target this type proxy is created for.
400403
*/
401404
private final Implementation.Target implementationTarget;
402405

403-
/**
404-
* The parameter types of the constructor that should be called.
405-
*/
406-
private final List<TypeDescription> constructorParameters;
407-
408406
/**
409407
* {@code true} if any finalizers should be ignored for the delegation.
410408
*/
@@ -420,18 +418,17 @@ public static class ForSuperMethodByConstructor extends StackManipulation.Abstra
420418
*
421419
* @param proxiedType The type for the type proxy to subclass or implement.
422420
* @param implementationTarget The implementation target this type proxy is created for.
423-
* @param constructorParameters The parameter types of the constructor that should be called.
424421
* @param ignoreFinalizer {@code true} if any finalizers should be ignored for the delegation.
425422
* @param serializableProxy Determines if the proxy should be serializable.
426423
*/
427424
public ForSuperMethodByConstructor(TypeDescription proxiedType,
425+
MethodDescription.InDefinedShape constructor,
428426
Implementation.Target implementationTarget,
429-
List<TypeDescription> constructorParameters,
430427
boolean ignoreFinalizer,
431428
boolean serializableProxy) {
432429
this.proxiedType = proxiedType;
430+
this.constructor = constructor;
433431
this.implementationTarget = implementationTarget;
434-
this.constructorParameters = constructorParameters;
435432
this.ignoreFinalizer = ignoreFinalizer;
436433
this.serializableProxy = serializableProxy;
437434
}
@@ -440,22 +437,23 @@ public ForSuperMethodByConstructor(TypeDescription proxiedType,
440437
* {@inheritDoc}
441438
*/
442439
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
443-
TypeDescription proxyType = implementationContext
444-
.register(new TypeProxy(proxiedType,
445-
implementationTarget,
446-
InvocationFactory.Default.SUPER_METHOD,
447-
ignoreFinalizer,
448-
serializableProxy));
449-
StackManipulation[] constructorValue = new StackManipulation[constructorParameters.size()];
440+
TypeDescription proxyType = implementationContext.register(new TypeProxy(proxiedType,
441+
implementationTarget,
442+
InvocationFactory.Default.SUPER_METHOD,
443+
ignoreFinalizer,
444+
serializableProxy));
445+
StackManipulation[] constructorValue = new StackManipulation[constructor.getParameters().size()];
450446
int index = 0;
451-
for (TypeDescription parameterType : constructorParameters) {
447+
for (TypeDescription parameterType : constructor.getParameters().asTypeList().asErasures()) {
452448
constructorValue[index++] = DefaultValue.of(parameterType);
453449
}
454450
return new Compound(
455451
TypeCreation.of(proxyType),
456452
Duplication.SINGLE,
457453
new Compound(constructorValue),
458-
MethodInvocation.invoke(proxyType.getDeclaredMethods().filter(isConstructor().and(takesArguments(constructorParameters))).getOnly()),
454+
MethodInvocation.invoke(proxyType.getDeclaredMethods()
455+
.filter(isConstructor().and(takesArguments(constructor.getParameters().asTypeList().asErasures())))
456+
.getOnly()),
459457
Duplication.SINGLE,
460458
MethodVariableAccess.loadThis(),
461459
FieldAccess.forField(proxyType.getDeclaredFields().filter((named(INSTANCE_FIELD))).getOnly()).write()

byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Super.java

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import net.bytebuddy.implementation.bytecode.assign.Assigner;
3131

3232
import java.lang.annotation.*;
33+
import java.lang.reflect.InvocationTargetException;
3334
import java.util.Arrays;
35+
import java.util.List;
3436

35-
import static net.bytebuddy.matcher.ElementMatchers.named;
37+
import static net.bytebuddy.matcher.ElementMatchers.*;
3638

3739
/**
3840
* Parameters that are annotated with this annotation are assigned an instance of an auxiliary proxy type that allows calling
@@ -102,6 +104,16 @@
102104
*/
103105
Class<?>[] constructorParameters() default {};
104106

107+
/**
108+
* Specifies a class to resolve a constructor of the proxied type to use for instantiation if
109+
* {@link Instantiation#CONSTRUCTOR} is used. Note that the specified class will be loaded and instantiated by
110+
* Byte Buddy in order to resolve the constructor. For this, the specified class requires a public
111+
* default constructor.
112+
*
113+
* @return The type of the {@link ConstructorResolver} to use.
114+
*/
115+
Class<? extends ConstructorResolver> constructorResolver() default ConstructorResolver.Default.class;
116+
105117
/**
106118
* Determines the type that is implemented by the proxy. When this value is set to its default value
107119
* {@code void}, the proxy is created as an instance of the parameter's type. When it is set to
@@ -112,6 +124,46 @@
112124
*/
113125
Class<?> proxyType() default void.class;
114126

127+
/**
128+
* A constructor resolver is responsible to specify the constructor to be used for creating a proxy.
129+
*/
130+
interface ConstructorResolver {
131+
132+
/**
133+
* Resolves the constructor to be used.
134+
*
135+
* @param proxiedType The type being proxied.
136+
* @param constructorParameters The types being specified on the annotation.
137+
* @return The constructor to invoke with default arguments for instantiation.
138+
*/
139+
MethodDescription.InDefinedShape resolve(TypeDescription proxiedType, List<TypeDescription> constructorParameters);
140+
141+
/**
142+
* A default constructor resolver that attempts to resolve a constructor with the given argument types.
143+
*/
144+
class Default implements ConstructorResolver {
145+
146+
/**
147+
* {@inheritDoc}
148+
*/
149+
public MethodDescription.InDefinedShape resolve(TypeDescription proxiedType, List<TypeDescription> constructorParameters) {
150+
if (proxiedType.isInterface()) {
151+
return TypeDescription.ForLoadedType.of(Object.class).getDeclaredMethods()
152+
.filter(isConstructor())
153+
.getOnly();
154+
}
155+
MethodList<MethodDescription.InDefinedShape> candidates = proxiedType.getDeclaredMethods().filter(isConstructor()
156+
.and(not(isPrivate()))
157+
.and(takesArguments(constructorParameters)));
158+
if (candidates.size() == 1) {
159+
return candidates.getOnly();
160+
} else {
161+
throw new IllegalStateException("Did not discover exactly one constructor on " + proxiedType + " with parameters " + constructorParameters);
162+
}
163+
}
164+
}
165+
}
166+
115167
/**
116168
* Determines the instantiation of the proxy type.
117169
*
@@ -125,12 +177,32 @@ enum Instantiation {
125177
*/
126178
CONSTRUCTOR {
127179
@Override
128-
protected StackManipulation proxyFor(TypeDescription parameterType,
180+
protected StackManipulation proxyFor(TypeDescription proxyType,
129181
Implementation.Target implementationTarget,
130182
AnnotationDescription.Loadable<Super> annotation) {
131-
return new TypeProxy.ForSuperMethodByConstructor(parameterType,
183+
MethodDescription.InDefinedShape constructor;
184+
try {
185+
@SuppressWarnings("unchecked")
186+
ConstructorResolver constructorResolver = (ConstructorResolver) annotation.getValue(CONSTRUCTOR_RESOLVER)
187+
.load(ConstructorResolver.class.getClassLoader())
188+
.resolve(Class.class)
189+
.getConstructor()
190+
.newInstance();
191+
constructor = constructorResolver.resolve(
192+
proxyType,
193+
Arrays.asList(annotation.getValue(CONSTRUCTOR_PARAMETERS).resolve(TypeDescription[].class)));
194+
} catch (NoSuchMethodException exception) {
195+
throw new IllegalStateException("No default constructor specified by " + annotation.getValue(CONSTRUCTOR_RESOLVER)
196+
.resolve(TypeDescription.class)
197+
.getName(), exception);
198+
} catch (InvocationTargetException exception) {
199+
throw new IllegalStateException("Failed to resolve constructor specified by " + annotation, exception.getTargetException());
200+
} catch (Exception exception) {
201+
throw new IllegalStateException("Failed to resolve constructor specified by " + annotation, exception);
202+
}
203+
return new TypeProxy.ForSuperMethodByConstructor(proxyType,
204+
constructor,
132205
implementationTarget,
133-
Arrays.asList(annotation.getValue(CONSTRUCTOR_PARAMETERS).resolve(TypeDescription[].class)),
134206
annotation.getValue(IGNORE_FINALIZER).resolve(Boolean.class),
135207
annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class));
136208
}
@@ -142,10 +214,10 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
142214
*/
143215
UNSAFE {
144216
@Override
145-
protected StackManipulation proxyFor(TypeDescription parameterType,
217+
protected StackManipulation proxyFor(TypeDescription proxyType,
146218
Implementation.Target implementationTarget,
147219
AnnotationDescription.Loadable<Super> annotation) {
148-
return new TypeProxy.ForSuperMethodByReflectionFactory(parameterType,
220+
return new TypeProxy.ForSuperMethodByReflectionFactory(proxyType,
149221
implementationTarget,
150222
annotation.getValue(IGNORE_FINALIZER).resolve(Boolean.class),
151223
annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class));
@@ -167,6 +239,11 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
167239
*/
168240
private static final MethodDescription.InDefinedShape CONSTRUCTOR_PARAMETERS;
169241

242+
/**
243+
* A reference to the constructor parameters resolver method.
244+
*/
245+
private static final MethodDescription.InDefinedShape CONSTRUCTOR_RESOLVER;
246+
170247
/*
171248
* Extracts method references to the annotation methods.
172249
*/
@@ -175,18 +252,19 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
175252
IGNORE_FINALIZER = annotationProperties.filter(named("ignoreFinalizer")).getOnly();
176253
SERIALIZABLE_PROXY = annotationProperties.filter(named("serializableProxy")).getOnly();
177254
CONSTRUCTOR_PARAMETERS = annotationProperties.filter(named("constructorParameters")).getOnly();
255+
CONSTRUCTOR_RESOLVER = annotationProperties.filter(named("constructorResolver")).getOnly();
178256
}
179257

180258
/**
181259
* Creates a stack manipulation which loads a {@code super}-call proxy onto the stack.
182260
*
183-
* @param parameterType The type of the parameter that was annotated with
261+
* @param proxyType The type of the proxy that is bound to the parameter annotated by
184262
* {@link net.bytebuddy.implementation.bind.annotation.Super}
185263
* @param implementationTarget The implementation target for the currently created type.
186264
* @param annotation The annotation that caused this method call.
187265
* @return A stack manipulation representing this instance's instantiation strategy.
188266
*/
189-
protected abstract StackManipulation proxyFor(TypeDescription parameterType,
267+
protected abstract StackManipulation proxyFor(TypeDescription proxyType,
190268
Implementation.Target implementationTarget,
191269
AnnotationDescription.Loadable<Super> annotation);
192270
}

byte-buddy-dep/src/test/java/net/bytebuddy/implementation/auxiliary/TypeProxyCreationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ public void testForConstructorConstruction() throws Exception {
222222
.thenReturn(new StackManipulation.Size(0, 0));
223223
when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod);
224224
StackManipulation stackManipulation = new TypeProxy.ForSuperMethodByConstructor(foo,
225+
new MethodDescription.ForLoadedConstructor(Foo.class.getConstructor(Void.class)),
225226
implementationTarget,
226-
Collections.singletonList((TypeDescription) TypeDescription.ForLoadedType.of(Void.class)),
227227
true,
228228
false);
229229
MethodVisitor methodVisitor = mock(MethodVisitor.class);

byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bind/annotation/SuperBinderTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package net.bytebuddy.implementation.bind.annotation;
22

3+
import net.bytebuddy.description.method.MethodDescription;
4+
import net.bytebuddy.description.method.MethodList;
35
import net.bytebuddy.description.type.TypeDescription;
46
import net.bytebuddy.implementation.bind.MethodDelegationBinder;
57
import net.bytebuddy.implementation.bytecode.assign.Assigner;
@@ -31,7 +33,10 @@ public void setUp() throws Exception {
3133
when(genericTargetType.asErasure()).thenReturn(targetType);
3234
when(annotation.strategy()).thenReturn(Super.Instantiation.CONSTRUCTOR);
3335
when(annotation.constructorParameters()).thenReturn(new Class<?>[0]);
36+
doReturn(Super.ConstructorResolver.Default.class).when(annotation).constructorResolver();
3437
when(targetType.asErasure()).thenReturn(targetType);
38+
when(targetType.getDeclaredMethods()).thenReturn(new MethodList.Explicit<MethodDescription.InDefinedShape>(
39+
new MethodDescription.ForLoadedConstructor(Object.class.getConstructor())));
3540
}
3641

3742
protected TargetMethodAnnotationDrivenBinder.ParameterBinder<Super> getSimpleBinder() {

byte-buddy/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@
422422
<plugin>
423423
<groupId>codes.rafael.bytecodeupdate</groupId>
424424
<artifactId>bytecode-update-maven-plugin</artifactId>
425-
<version>1.0-SNAPSHOT</version>
425+
<version>${version.plugin.bytecode-update}</version>
426426
<dependencies>
427427
<dependency>
428428
<groupId>org.ow2.asm</groupId>

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
<version.plugin.license>3.0</version.plugin.license>
108108
<version.plugin.japicmp>0.15.7</version.plugin.japicmp>
109109
<version.plugin.antrun>3.1.0</version.plugin.antrun>
110+
<version.plugin.bytecode-update>1.0</version.plugin.bytecode-update>
110111
<version.checkstyle>9.3</version.checkstyle>
111112
<version.android.sdk>4.1.1.4</version.android.sdk>
112113
<version.utility.findbugs>3.0.1</version.utility.findbugs>

0 commit comments

Comments
 (0)