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