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>&nbsp;</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>&nbsp;</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>&nbsp;</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 falseif 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 trueif 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