1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.implementation.bind.annotation;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.annotation.AnnotationDescription;
20 import net.bytebuddy.description.enumeration.EnumerationDescription;
21 import net.bytebuddy.description.method.MethodDescription;
22 import net.bytebuddy.description.method.MethodList;
23 import net.bytebuddy.description.method.ParameterDescription;
24 import net.bytebuddy.description.type.TypeDescription;
25 import net.bytebuddy.dynamic.TargetType;
26 import net.bytebuddy.implementation.Implementation;
27 import net.bytebuddy.implementation.auxiliary.TypeProxy;
28 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
29 import net.bytebuddy.implementation.bytecode.StackManipulation;
30 import net.bytebuddy.implementation.bytecode.assign.Assigner;
31
32 import java.lang.annotation.*;
33 import java.util.Arrays;
34
35 import static net.bytebuddy.matcher.ElementMatchers.named;
36
37 /**
38 * Parameters that are annotated with this annotation are assigned an instance of an auxiliary proxy type that allows calling
39 * any {@code super} methods of the instrumented type where the parameter type must be a super type of the instrumented type.
40 * The proxy type will be a direct subclass of the parameter's type such as for example a specific interface.
41 * <p> </p>
42 * Obviously, the proxy type must be instantiated before it is assigned to the intercepting method's parameter. For this
43 * purpose, two strategies are available which can be specified by setting the {@link Super#strategy()} parameter which can
44 * be assigned:
45 * <ol>
46 * <li>{@link net.bytebuddy.implementation.bind.annotation.Super.Instantiation#CONSTRUCTOR}:
47 * A constructor call is made where {@link Super#constructorParameters()} determines the constructor's signature. Any constructor
48 * parameter is assigned the parameter's default value when the constructor is called. Calling the default constructor is the
49 * preconfigured strategy.</li>
50 * <li>{@link net.bytebuddy.implementation.bind.annotation.Super.Instantiation#UNSAFE}:
51 * The proxy is created by making use of Java's {@link sun.reflect.ReflectionFactory} which is however not a public API which
52 * is why it should be used with care. No constructor is called when this strategy is used. If this option is set, the
53 * {@link Super#constructorParameters()} parameter is ignored.</li>
54 * </ol>
55 * Note that when for example intercepting a type {@code Foo} that implements some interface {@code Bar}, the proxy type
56 * will only implement {@code Bar} and therefore extend {@link java.lang.Object} what allows for calling the default
57 * constructor on the proxy. This implies that an interception by some method {@code qux(@Super Baz baz, @Super Bar bar)}
58 * would cause the creation of two super call proxies, one extending {@code Baz}, the other extending {@code Bar}, give
59 * that both types are super types of {@code Foo}.
60 * <p> </p>
61 * As an exception, no method calls to {@link Object#finalize()} are delegated by calling this method on the {@code super}-call
62 * proxy by default. If this is absolutely necessary, this can however be enabled by setting {@link Super#ignoreFinalizer()}
63 * to {@code false}.
64 * <p> </p>
65 * If a method parameter is not a super type of the instrumented type, the method with the parameter that is annotated by
66 * #{@code Super} is not considered a possible delegation target.
67 *
68 * @see net.bytebuddy.implementation.MethodDelegation
69 * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
70 */
71 @Documented
72 @Retention(RetentionPolicy.RUNTIME)
73 @Target(ElementType.PARAMETER)
74 public @interface Super {
75
76 /**
77 * Determines how the {@code super}call proxy type is instantiated.
78 *
79 * @return The instantiation strategy for this proxy.
80 */
81 Instantiation strategy() default Instantiation.CONSTRUCTOR;
82
83 /**
84 * If {@code true}, the proxy type will not implement {@code super} calls to {@link Object#finalize()} or any overridden methods.
85 *
86 * @return {@code false} if finalizer methods should be considered for {@code super}-call proxy type delegation.
87 */
88 boolean ignoreFinalizer() default true;
89
90 /**
91 * Determines if the generated proxy should be {@link java.io.Serializable}. If the annotated type
92 * already is serializable, such an explicit specification is not required.
93 *
94 * @return {@code true} if the generated proxy should be {@link java.io.Serializable}.
95 */
96 boolean serializableProxy() default false;
97
98 /**
99 * Defines the parameter types of the constructor to be called for the created {@code super}-call proxy type.
100 *
101 * @return The parameter types of the constructor to be called.
102 */
103 Class<?>[] constructorParameters() default {};
104
105 /**
106 * Determines the type that is implemented by the proxy. When this value is set to its default value
107 * {@code void}, the proxy is created as an instance of the parameter's type. When it is set to
108 * {@link TargetType}, it is created as an instance of the generated class. Otherwise, the proxy type
109 * is set to the given value.
110 *
111 * @return The type of the proxy or an indicator type, i.e. {@code void} or {@link TargetType}.
112 */
113 Class<?> proxyType() default void.class;
114
115 /**
116 * Determines the instantiation of the proxy type.
117 *
118 * @see net.bytebuddy.implementation.bind.annotation.Super
119 */
120 enum Instantiation {
121
122 /**
123 * A proxy instance is instantiated by its constructor. For the constructor's arguments, the parameters default
124 * values are used. The constructor can be identified by setting {@link Super#constructorParameters()}.
125 */
126 CONSTRUCTOR {
127 @Override
128 protected StackManipulation proxyFor(TypeDescription parameterType,
129 Implementation.Target implementationTarget,
130 AnnotationDescription.Loadable<Super> annotation) {
131 return new TypeProxy.ForSuperMethodByConstructor(parameterType,
132 implementationTarget,
133 Arrays.asList(annotation.getValue(CONSTRUCTOR_PARAMETERS).resolve(TypeDescription[].class)),
134 annotation.getValue(IGNORE_FINALIZER).resolve(Boolean.class),
135 annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class));
136 }
137 },
138
139 /**
140 * A proxy is instantiated by calling JVM internal methods and without calling a constructor. This strategy
141 * might fail on exotic JVM implementations.
142 */
143 UNSAFE {
144 @Override
145 protected StackManipulation proxyFor(TypeDescription parameterType,
146 Implementation.Target implementationTarget,
147 AnnotationDescription.Loadable<Super> annotation) {
148 return new TypeProxy.ForSuperMethodByReflectionFactory(parameterType,
149 implementationTarget,
150 annotation.getValue(IGNORE_FINALIZER).resolve(Boolean.class),
151 annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class));
152 }
153 };
154
155 /**
156 * A reference to the ignore finalizer method.
157 */
158 private static final MethodDescription.InDefinedShape IGNORE_FINALIZER;
159
160 /**
161 * A reference to the serializable proxy method.
162 */
163 private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY;
164
165 /**
166 * A reference to the constructor parameters method.
167 */
168 private static final MethodDescription.InDefinedShape CONSTRUCTOR_PARAMETERS;
169
170 /*
171 * Extracts method references to the annotation methods.
172 */
173 static {
174 MethodList<MethodDescription.InDefinedShape> annotationProperties = TypeDescription.ForLoadedType.of(Super.class).getDeclaredMethods();
175 IGNORE_FINALIZER = annotationProperties.filter(named("ignoreFinalizer")).getOnly();
176 SERIALIZABLE_PROXY = annotationProperties.filter(named("serializableProxy")).getOnly();
177 CONSTRUCTOR_PARAMETERS = annotationProperties.filter(named("constructorParameters")).getOnly();
178 }
179
180 /**
181 * Creates a stack manipulation which loads a {@code super}-call proxy onto the stack.
182 *
183 * @param parameterType The type of the parameter that was annotated with
184 * {@link net.bytebuddy.implementation.bind.annotation.Super}
185 * @param implementationTarget The implementation target for the currently created type.
186 * @param annotation The annotation that caused this method call.
187 * @return A stack manipulation representing this instance's instantiation strategy.
188 */
189 protected abstract StackManipulation proxyFor(TypeDescription parameterType,
190 Implementation.Target implementationTarget,
191 AnnotationDescription.Loadable<Super> annotation);
192 }
193
194 /**
195 * A binder for handling the
196 * {@link net.bytebuddy.implementation.bind.annotation.Super}
197 * annotation.
198 *
199 * @see TargetMethodAnnotationDrivenBinder
200 */
201 enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Super> {
202
203 /**
204 * The singleton instance.
205 */
206 INSTANCE;
207
208 /**
209 * A method reference to the strategy property.
210 */
211 private static final MethodDescription.InDefinedShape STRATEGY;
212
213 /**
214 * A reference to the proxy type property.
215 */
216 private static final MethodDescription.InDefinedShape PROXY_TYPE;
217
218 /*
219 * Extracts method references of the super annotation.
220 */
221 static {
222 MethodList<MethodDescription.InDefinedShape> annotationProperties = TypeDescription.ForLoadedType.of(Super.class).getDeclaredMethods();
223 STRATEGY = annotationProperties.filter(named("strategy")).getOnly();
224 PROXY_TYPE = annotationProperties.filter(named("proxyType")).getOnly();
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 public Class<Super> getHandledType() {
231 return Super.class;
232 }
233
234 /**
235 * {@inheritDoc}
236 */
237 public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Super> annotation,
238 MethodDescription source,
239 ParameterDescription target,
240 Implementation.Target implementationTarget,
241 Assigner assigner,
242 Assigner.Typing typing) {
243 if (target.getType().isPrimitive() || target.getType().isArray()) {
244 throw new IllegalStateException(target + " uses the @Super annotation on an invalid type");
245 }
246 TypeDescription proxyType = TypeLocator.ForType
247 .of(annotation.getValue(PROXY_TYPE).resolve(TypeDescription.class))
248 .resolve(implementationTarget.getInstrumentedType(), target.getType());
249 if (proxyType.isFinal()) {
250 throw new IllegalStateException("Cannot extend final type as @Super proxy: " + proxyType);
251 } else if (source.isStatic() || !implementationTarget.getInstrumentedType().isAssignableTo(proxyType)) {
252 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
253 } else {
254 return new MethodDelegationBinder.ParameterBinding.Anonymous(annotation
255 .getValue(STRATEGY).resolve(EnumerationDescription.class).load(Instantiation.class)
256 .proxyFor(proxyType, implementationTarget, annotation));
257 }
258 }
259
260 /**
261 * Locates the type which should be the base type of the created proxy.
262 */
263 protected interface TypeLocator {
264
265 /**
266 * Resolves the target type.
267 *
268 * @param instrumentedType The instrumented type.
269 * @param parameterType The type of the target parameter.
270 * @return The proxy type.
271 */
272 TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType);
273
274 /**
275 * A type locator that yields the instrumented type.
276 */
277 enum ForInstrumentedType implements TypeLocator {
278
279 /**
280 * The singleton instance.
281 */
282 INSTANCE;
283
284 /**
285 * {@inheritDoc}
286 */
287 public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) {
288 return instrumentedType;
289 }
290 }
291
292 /**
293 * A type locator that yields the target parameter's type.
294 */
295 enum ForParameterType implements TypeLocator {
296
297 /**
298 * The singleton instance.
299 */
300 INSTANCE;
301
302 /**
303 * {@inheritDoc}
304 */
305 public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) {
306 TypeDescription erasure = parameterType.asErasure();
307 return erasure.equals(instrumentedType)
308 ? instrumentedType
309 : erasure;
310 }
311 }
312
313 /**
314 * A type locator that returns a given type.
315 */
316 @HashCodeAndEqualsPlugin.Enhance
317 class ForType implements TypeLocator {
318
319 /**
320 * The type to be returned upon resolution.
321 */
322 private final TypeDescription typeDescription;
323
324 /**
325 * Creates a new type locator for a given type.
326 *
327 * @param typeDescription The type to be returned upon resolution.
328 */
329 protected ForType(TypeDescription typeDescription) {
330 this.typeDescription = typeDescription;
331 }
332
333 /**
334 * Resolves a type locator based upon an annotation value.
335 *
336 * @param typeDescription The annotation's value.
337 * @return The appropriate type locator.
338 */
339 protected static TypeLocator of(TypeDescription typeDescription) {
340 if (typeDescription.represents(void.class)) {
341 return ForParameterType.INSTANCE;
342 } else if (typeDescription.represents(TargetType.class)) {
343 return ForInstrumentedType.INSTANCE;
344 } else if (typeDescription.isPrimitive() || typeDescription.isArray()) {
345 throw new IllegalStateException("Cannot assign proxy to " + typeDescription);
346 } else {
347 return new ForType(typeDescription);
348 }
349 }
350
351 /**
352 * {@inheritDoc}
353 */
354 public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) {
355 if (!typeDescription.isAssignableTo(parameterType.asErasure())) {
356 throw new IllegalStateException("Impossible to assign " + typeDescription + " to parameter of type " + parameterType);
357 }
358 return typeDescription;
359 }
360 }
361 }
362 }
363 }
364