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.attribute;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.annotation.AnnotationDescription;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.method.ParameterDescription;
22 import net.bytebuddy.description.method.ParameterList;
23 import net.bytebuddy.description.type.TypeDescription;
24 import net.bytebuddy.jar.asm.MethodVisitor;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29
30 import static net.bytebuddy.matcher.ElementMatchers.*;
31
32 /**
33  * An appender that writes attributes or annotations to a given ASM {@link net.bytebuddy.jar.asm.MethodVisitor}.
34  */

35 public interface MethodAttributeAppender {
36
37     /**
38      * Applies this attribute appender to a given method visitor.
39      *
40      * @param methodVisitor         The method visitor to which the attributes that are represented by this attribute
41      *                              appender are written to.
42      * @param methodDescription     The description of the method for which the given method visitor creates an
43      *                              instrumentation for.
44      * @param annotationValueFilter The annotation value filter to apply when the annotations are written.
45      */

46     void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter);
47
48     /**
49      * A method attribute appender that does not append any attributes.
50      */

51     enum NoOp implements MethodAttributeAppender, Factory {
52
53         /**
54          * The singleton instance.
55          */

56         INSTANCE;
57
58         /**
59          * {@inheritDoc}
60          */

61         public MethodAttributeAppender make(TypeDescription typeDescription) {
62             return this;
63         }
64
65         /**
66          * {@inheritDoc}
67          */

68         public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
69             /* do nothing */
70         }
71     }
72
73     /**
74      * A factory that creates method attribute appenders for a given type.
75      */

76     interface Factory {
77
78         /**
79          * Returns a method attribute appender that is applicable for a given type description.
80          *
81          * @param typeDescription The type for which a method attribute appender is to be applied for.
82          * @return The method attribute appender which should be applied for the given type.
83          */

84         MethodAttributeAppender make(TypeDescription typeDescription);
85
86         /**
87          * A method attribute appender factory that combines several method attribute appender factories to be
88          * represented as a single factory.
89          */

90         @HashCodeAndEqualsPlugin.Enhance
91         class Compound implements Factory {
92
93             /**
94              * The factories this compound factory represents in their application order.
95              */

96             private final List<Factory> factories;
97
98             /**
99              * Creates a new compound method attribute appender factory.
100              *
101              * @param factory The factories that are to be combined by this compound factory in the order of their application.
102              */

103             public Compound(Factory... factory) {
104                 this(Arrays.asList(factory));
105             }
106
107             /**
108              * Creates a new compound method attribute appender factory.
109              *
110              * @param factories The factories that are to be combined by this compound factory in the order of their application.
111              */

112             public Compound(List<? extends Factory> factories) {
113                 this.factories = new ArrayList<Factory>();
114                 for (Factory factory : factories) {
115                     if (factory instanceof Compound) {
116                         this.factories.addAll(((Compound) factory).factories);
117                     } else if (!(factory instanceof NoOp)) {
118                         this.factories.add(factory);
119                     }
120                 }
121             }
122
123             /**
124              * {@inheritDoc}
125              */

126             public MethodAttributeAppender make(TypeDescription typeDescription) {
127                 List<MethodAttributeAppender> methodAttributeAppenders = new ArrayList<MethodAttributeAppender>(factories.size());
128                 for (Factory factory : factories) {
129                     methodAttributeAppenders.add(factory.make(typeDescription));
130                 }
131                 return new MethodAttributeAppender.Compound(methodAttributeAppenders);
132             }
133         }
134     }
135
136     /**
137      * <p>
138      * Implementation of a method attribute appender that writes all annotations of the instrumented method to the
139      * method that is being created. This includes method and parameter annotations.
140      * </p>
141      * <p>
142      * <b>Important</b>: This attribute appender does not apply for annotation types within the {@code jdk.internal.} namespace
143      * which are silently ignored. If such annotations should be inherited, they need to be added explicitly.
144      * </p>
145      */

146     enum ForInstrumentedMethod implements MethodAttributeAppender, Factory {
147
148         /**
149          * Appends all annotations of the instrumented method but not the annotations of the method's receiver type if such a type exists.
150          */

151         EXCLUDING_RECEIVER {
152             @Override
153             protected AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
154                                                         AnnotationValueFilter annotationValueFilter,
155                                                         MethodDescription methodDescription) {
156                 return annotationAppender;
157             }
158         },
159
160         /**
161          * <p>
162          * Appends all annotations of the instrumented method including the annotations of the method's receiver type if such a type exists.
163          * </p>
164          * <p>
165          * If a method is overridden, the annotations can be misplaced if the overriding class does not expose a similar structure to
166          * the method that declared the method, i.e. the same amount of type variables and similar owner types. If this is not the case,
167          * type annotations are appended as if the overridden method was declared by the original type. This does not corrupt the resulting
168          * class file but it might result in type annotations not being visible via core reflection. This might however confuse other tools
169          * that parse the resulting class file manually.
170          * </p>
171          */

172         INCLUDING_RECEIVER {
173             @Override
174             protected AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
175                                                         AnnotationValueFilter annotationValueFilter,
176                                                         MethodDescription methodDescription) {
177                 TypeDescription.Generic receiverType = methodDescription.getReceiverType();
178                 return receiverType == null
179                         ? annotationAppender
180                         : receiverType.accept(AnnotationAppender.ForTypeAnnotations.ofReceiverType(annotationAppender, annotationValueFilter));
181             }
182         };
183
184         /**
185          * {@inheritDoc}
186          */

187         public MethodAttributeAppender make(TypeDescription typeDescription) {
188             return this;
189         }
190
191         /**
192          * {@inheritDoc}
193          */

194         public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
195             AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor));
196             annotationAppender = methodDescription.getReturnType().accept(AnnotationAppender.ForTypeAnnotations.ofMethodReturnType(annotationAppender,
197                     annotationValueFilter));
198             annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
199                     annotationValueFilter,
200                     AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_INVOKEABLE,
201                     methodDescription.getTypeVariables());
202             for (AnnotationDescription annotation : methodDescription.getDeclaredAnnotations().filter(not(annotationType(nameStartsWith("jdk.internal."))))) {
203                 annotationAppender = annotationAppender.append(annotation, annotationValueFilter);
204             }
205             for (ParameterDescription parameterDescription : methodDescription.getParameters()) {
206                 AnnotationAppender parameterAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethodParameter(methodVisitor,
207                         parameterDescription.getIndex()));
208                 parameterAppender = parameterDescription.getType().accept(AnnotationAppender.ForTypeAnnotations.ofMethodParameterType(parameterAppender,
209                         annotationValueFilter,
210                         parameterDescription.getIndex()));
211                 for (AnnotationDescription annotation : parameterDescription.getDeclaredAnnotations()) {
212                     parameterAppender = parameterAppender.append(annotation, annotationValueFilter);
213                 }
214             }
215             annotationAppender = appendReceiver(annotationAppender, annotationValueFilter, methodDescription);
216             int exceptionTypeIndex = 0;
217             for (TypeDescription.Generic exceptionType : methodDescription.getExceptionTypes()) {
218                 annotationAppender = exceptionType.accept(AnnotationAppender.ForTypeAnnotations.ofExceptionType(annotationAppender,
219                         annotationValueFilter,
220                         exceptionTypeIndex++));
221             }
222         }
223
224         /**
225          * Appends the annotations of the instrumented method's receiver type if this is enabled and such a type exists.
226          *
227          * @param annotationAppender    The annotation appender to use.
228          * @param annotationValueFilter The annotation value filter to apply when the annotations are written.
229          * @param methodDescription     The instrumented method.
230          * @return The resulting annotation appender.
231          */

232         protected abstract AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
233                                                              AnnotationValueFilter annotationValueFilter,
234                                                              MethodDescription methodDescription);
235     }
236
237     /**
238      * Appends an annotation to a method or method parameter. The visibility of the annotation is determined by the
239      * annotation type's {@link java.lang.annotation.RetentionPolicy} annotation.
240      */

241     @HashCodeAndEqualsPlugin.Enhance
242     class Explicit implements MethodAttributeAppender, Factory {
243
244         /**
245          * The target to which the annotations are written to.
246          */

247         private final Target target;
248
249         /**
250          * the annotations this method attribute appender is writing to its target.
251          */

252         private final List<? extends AnnotationDescription> annotations;
253
254         /**
255          * Creates a new appender for appending an annotation to a method.
256          *
257          * @param parameterIndex The index of the parameter to which the annotations should be written.
258          * @param annotations    The annotations that should be written.
259          */

260         public Explicit(int parameterIndex, List<? extends AnnotationDescription> annotations) {
261             this(new Target.OnMethodParameter(parameterIndex), annotations);
262         }
263
264         /**
265          * Creates a new appender for appending an annotation to a method.
266          *
267          * @param annotations The annotations that should be written.
268          */

269         public Explicit(List<? extends AnnotationDescription> annotations) {
270             this(Target.OnMethod.INSTANCE, annotations);
271         }
272
273         /**
274          * Creates an explicit annotation appender for a either a method or one of its parameters..
275          *
276          * @param target      The target to which the annotation should be written to.
277          * @param annotations The annotations to write.
278          */

279         protected Explicit(Target target, List<? extends AnnotationDescription> annotations) {
280             this.target = target;
281             this.annotations = annotations;
282         }
283
284         /**
285          * Creates a method attribute appender factory that writes all annotations of a given method, both the method
286          * annotations themselves and all annotations that are defined for every parameter.
287          *
288          * @param methodDescription The method from which to extract the annotations.
289          * @return A method attribute appender factory for an appender that writes all annotations of the supplied method.
290          */

291         public static Factory of(MethodDescription methodDescription) {
292             return new Factory.Compound(ofMethodAnnotations(methodDescription), ofParameterAnnotations(methodDescription));
293         }
294
295         /**
296          * Creates a method attribute appender factory that writes all method annotations that are defined on the given method.
297          *
298          * @param methodDescription The method from which to extract the method annotations.
299          * @return A method attribute appender factory for an appender that writes all method annotations of the supplied method.
300          */

301         public static Factory ofMethodAnnotations(MethodDescription methodDescription) {
302             return new Explicit(methodDescription.getDeclaredAnnotations());
303         }
304
305         /**
306          * Creates a method attribute appender factory that writes all annotations that are defined for every parameter
307          * of the given method.
308          *
309          * @param methodDescription The method from which to extract the parameter annotations.
310          * @return A method attribute appender factory for an appender that writes all parameter annotations of the supplied method.
311          */

312         public static Factory ofParameterAnnotations(MethodDescription methodDescription) {
313             ParameterList<?> parameters = methodDescription.getParameters();
314             List<MethodAttributeAppender.Factory> factories = new ArrayList<MethodAttributeAppender.Factory>(parameters.size());
315             for (ParameterDescription parameter : parameters) {
316                 factories.add(new Explicit(parameter.getIndex(), parameter.getDeclaredAnnotations()));
317             }
318             return new Factory.Compound(factories);
319         }
320
321         /**
322          * {@inheritDoc}
323          */

324         public MethodAttributeAppender make(TypeDescription typeDescription) {
325             return this;
326         }
327
328         /**
329          * {@inheritDoc}
330          */

331         public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
332             AnnotationAppender appender = new AnnotationAppender.Default(target.make(methodVisitor, methodDescription));
333             for (AnnotationDescription annotation : annotations) {
334                 appender = appender.append(annotation, annotationValueFilter);
335             }
336         }
337
338         /**
339          * Represents the target on which this method attribute appender should write its annotations to.
340          */

341         protected interface Target {
342
343             /**
344              * Materializes the target for a given creation process.
345              *
346              * @param methodVisitor     The method visitor to which the attributes that are represented by this
347              *                          attribute appender are written to.
348              * @param methodDescription The description of the method for which the given method visitor creates an
349              *                          instrumentation for.
350              * @return The target of the annotation appender this target represents.
351              */

352             AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription);
353
354             /**
355              * A method attribute appender target for writing annotations directly onto the method.
356              */

357             enum OnMethod implements Target {
358
359                 /**
360                  * The singleton instance.
361                  */

362                 INSTANCE;
363
364                 /**
365                  * {@inheritDoc}
366                  */

367                 public AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription) {
368                     return new AnnotationAppender.Target.OnMethod(methodVisitor);
369                 }
370             }
371
372             /**
373              * A method attribute appender target for writing annotations onto a given method parameter.
374              */

375             @HashCodeAndEqualsPlugin.Enhance
376             class OnMethodParameter implements Target {
377
378                 /**
379                  * The index of the parameter to write the annotation to.
380                  */

381                 private final int parameterIndex;
382
383                 /**
384                  * Creates a target for a method attribute appender for a method parameter of the given index.
385                  *
386                  * @param parameterIndex The index of the target parameter.
387                  */

388                 protected OnMethodParameter(int parameterIndex) {
389                     this.parameterIndex = parameterIndex;
390                 }
391
392                 /**
393                  * {@inheritDoc}
394                  */

395                 public AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription) {
396                     if (parameterIndex >= methodDescription.getParameters().size()) {
397                         throw new IllegalArgumentException("Method " + methodDescription + " has less then " + parameterIndex + " parameters");
398                     }
399                     return new AnnotationAppender.Target.OnMethodParameter(methodVisitor, parameterIndex);
400                 }
401             }
402         }
403     }
404
405     /**
406      * A method attribute appender that writes a receiver type.
407      */

408     @HashCodeAndEqualsPlugin.Enhance
409     class ForReceiverType implements MethodAttributeAppender, Factory {
410
411         /**
412          * The receiver type for which annotations are appended to the instrumented method.
413          */

414         private final TypeDescription.Generic receiverType;
415
416         /**
417          * Creates a new attribute appender that writes a receiver type.
418          *
419          * @param receiverType The receiver type for which annotations are appended to the instrumented method.
420          */

421         public ForReceiverType(TypeDescription.Generic receiverType) {
422             this.receiverType = receiverType;
423         }
424
425         /**
426          * {@inheritDoc}
427          */

428         public MethodAttributeAppender make(TypeDescription typeDescription) {
429             return this;
430         }
431
432         /**
433          * {@inheritDoc}
434          */

435         public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
436             receiverType.accept(AnnotationAppender.ForTypeAnnotations.ofReceiverType(new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor)), annotationValueFilter));
437         }
438     }
439
440     /**
441      * A method attribute appender that combines several method attribute appenders to be represented as a single
442      * method attribute appender.
443      */

444     @HashCodeAndEqualsPlugin.Enhance
445     class Compound implements MethodAttributeAppender {
446
447         /**
448          * The method attribute appenders this compound appender represents in their application order.
449          */

450         private final List<MethodAttributeAppender> methodAttributeAppenders;
451
452         /**
453          * Creates a new compound method attribute appender.
454          *
455          * @param methodAttributeAppender The method attribute appenders that are to be combined by this compound appender
456          *                                in the order of their application.
457          */

458         public Compound(MethodAttributeAppender... methodAttributeAppender) {
459             this(Arrays.asList(methodAttributeAppender));
460         }
461
462         /**
463          * Creates a new compound method attribute appender.
464          *
465          * @param methodAttributeAppenders The method attribute appenders that are to be combined by this compound appender
466          *                                 in the order of their application.
467          */

468         public Compound(List<? extends MethodAttributeAppender> methodAttributeAppenders) {
469             this.methodAttributeAppenders = new ArrayList<MethodAttributeAppender>();
470             for (MethodAttributeAppender methodAttributeAppender : methodAttributeAppenders) {
471                 if (methodAttributeAppender instanceof Compound) {
472                     this.methodAttributeAppenders.addAll(((Compound) methodAttributeAppender).methodAttributeAppenders);
473                 } else if (!(methodAttributeAppender instanceof NoOp)) {
474                     this.methodAttributeAppenders.add(methodAttributeAppender);
475                 }
476             }
477         }
478
479         /**
480          * {@inheritDoc}
481          */

482         public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
483             for (MethodAttributeAppender methodAttributeAppender : methodAttributeAppenders) {
484                 methodAttributeAppender.apply(methodVisitor, methodDescription, annotationValueFilter);
485             }
486         }
487     }
488 }
489