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.dynamic;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.annotation.AnnotationList;
20 import net.bytebuddy.description.annotation.AnnotationValue;
21 import net.bytebuddy.description.field.FieldDescription;
22 import net.bytebuddy.description.method.MethodDescription;
23 import net.bytebuddy.description.method.ParameterDescription;
24 import net.bytebuddy.description.method.ParameterList;
25 import net.bytebuddy.description.modifier.ModifierContributor;
26 import net.bytebuddy.description.type.TypeDefinition;
27 import net.bytebuddy.description.type.TypeDescription;
28 import net.bytebuddy.description.type.TypeList;
29
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33
34 import static net.bytebuddy.matcher.ElementMatchers.named;
35 import static net.bytebuddy.matcher.ElementMatchers.none;
36
37 /**
38 * A transformer is responsible for transforming an object into a compatible instance of the same type.
39 *
40 * @param <T> The type of the instance being transformed.
41 */
42 public interface Transformer<T> {
43
44 /**
45 * Transforms the supplied target.
46 *
47 * @param instrumentedType The instrumented type that declares the target being transformed.
48 * @param target The target entity that is being transformed.
49 * @return The transformed instance.
50 */
51 T transform(TypeDescription instrumentedType, T target);
52
53 /**
54 * A non-operational transformer that returns the received instance.
55 */
56 enum NoOp implements Transformer<Object> {
57
58 /**
59 * The singleton instance.
60 */
61 INSTANCE;
62
63 /**
64 * Creates a transformer in a type-safe manner.
65 *
66 * @param <T> The type of the transformed object.
67 * @return A non-operational transformer.
68 */
69 @SuppressWarnings("unchecked")
70 public static <T> Transformer<T> make() {
71 return (Transformer<T>) INSTANCE;
72 }
73
74 /**
75 * {@inheritDoc}
76 */
77 public Object transform(TypeDescription instrumentedType, Object target) {
78 return target;
79 }
80 }
81
82 /**
83 * A transformer for a field that delegates to another transformer that transforms a {@link net.bytebuddy.description.field.FieldDescription.Token}.
84 */
85 @HashCodeAndEqualsPlugin.Enhance
86 class ForField implements Transformer<FieldDescription> {
87
88 /**
89 * The token transformer to apply to a transformed field.
90 */
91 private final Transformer<FieldDescription.Token> transformer;
92
93 /**
94 * Creates a new simple field transformer.
95 *
96 * @param transformer The token transformer to apply to a transformed field.
97 */
98 public ForField(Transformer<FieldDescription.Token> transformer) {
99 this.transformer = transformer;
100 }
101
102 /**
103 * Creates a field transformer that patches the transformed field by the given modifier contributors.
104 *
105 * @param modifierContributor The modifier contributors to apply.
106 * @return A suitable field transformer.
107 */
108 public static Transformer<FieldDescription> withModifiers(ModifierContributor.ForField... modifierContributor) {
109 return withModifiers(Arrays.asList(modifierContributor));
110 }
111
112 /**
113 * Creates a field transformer that patches the transformed field by the given modifier contributors.
114 *
115 * @param modifierContributors The modifier contributors to apply.
116 * @return A suitable field transformer.
117 */
118 public static Transformer<FieldDescription> withModifiers(List<? extends ModifierContributor.ForField> modifierContributors) {
119 return new ForField(new FieldModifierTransformer(ModifierContributor.Resolver.of(modifierContributors)));
120 }
121
122 /**
123 * {@inheritDoc}
124 */
125 public FieldDescription transform(TypeDescription instrumentedType, FieldDescription fieldDescription) {
126 return new TransformedField(instrumentedType,
127 fieldDescription.getDeclaringType(),
128 transformer.transform(instrumentedType, fieldDescription.asToken(none())),
129 fieldDescription.asDefined());
130 }
131
132 /**
133 * A transformer for a field's modifiers.
134 */
135 @HashCodeAndEqualsPlugin.Enhance
136 protected static class FieldModifierTransformer implements Transformer<FieldDescription.Token> {
137
138 /**
139 * The resolver to apply for transforming the modifiers of a field.
140 */
141 private final ModifierContributor.Resolver<ModifierContributor.ForField> resolver;
142
143 /**
144 * Creates a new field token modifier for transforming a field's modifiers.
145 *
146 * @param resolver The resolver to apply for transforming the modifiers of a field.
147 */
148 protected FieldModifierTransformer(ModifierContributor.Resolver<ModifierContributor.ForField> resolver) {
149 this.resolver = resolver;
150 }
151
152 /**
153 * {@inheritDoc}
154 */
155 public FieldDescription.Token transform(TypeDescription instrumentedType, FieldDescription.Token target) {
156 return new FieldDescription.Token(target.getName(),
157 resolver.resolve(target.getModifiers()),
158 target.getType(),
159 target.getAnnotations());
160 }
161 }
162
163 /**
164 * An implementation of a transformed field.
165 */
166 protected static class TransformedField extends FieldDescription.AbstractBase {
167
168 /**
169 * The instrumented type for which this field is transformed.
170 */
171 private final TypeDescription instrumentedType;
172
173 /**
174 * The field's declaring type.
175 */
176 private final TypeDefinition declaringType;
177
178 /**
179 * A field token representing the transformed field.
180 */
181 private final FieldDescription.Token token;
182
183 /**
184 * The field's defined shape.
185 */
186 private final FieldDescription.InDefinedShape fieldDescription;
187
188 /**
189 * Creates a new transformed field.
190 *
191 * @param instrumentedType The instrumented type for which this field is transformed.
192 * @param declaringType The field's declaring type.
193 * @param token A field token representing the transformed field.
194 * @param fieldDescription The field's defined shape.
195 */
196 protected TransformedField(TypeDescription instrumentedType,
197 TypeDefinition declaringType,
198 Token token,
199 InDefinedShape fieldDescription) {
200 this.instrumentedType = instrumentedType;
201 this.declaringType = declaringType;
202 this.token = token;
203 this.fieldDescription = fieldDescription;
204 }
205
206 /**
207 * {@inheritDoc}
208 */
209 public TypeDescription.Generic getType() {
210 return token.getType().accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(instrumentedType));
211 }
212
213 /**
214 * {@inheritDoc}
215 */
216 public AnnotationList getDeclaredAnnotations() {
217 return token.getAnnotations();
218 }
219
220 /**
221 * {@inheritDoc}
222 */
223 public TypeDefinition getDeclaringType() {
224 return declaringType;
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 public int getModifiers() {
231 return token.getModifiers();
232 }
233
234 /**
235 * {@inheritDoc}
236 */
237 public InDefinedShape asDefined() {
238 return fieldDescription;
239 }
240
241 /**
242 * {@inheritDoc}
243 */
244 public String getName() {
245 return token.getName();
246 }
247 }
248 }
249
250 /**
251 * A transformer for a field that delegates to another transformer that transforms a {@link net.bytebuddy.description.method.MethodDescription.Token}.
252 */
253 @HashCodeAndEqualsPlugin.Enhance
254 class ForMethod implements Transformer<MethodDescription> {
255
256 /**
257 * The transformer to be applied.
258 */
259 private final Transformer<MethodDescription.Token> transformer;
260
261 /**
262 * Creates a new transforming method transformer.
263 *
264 * @param transformer The transformer to be applied.
265 */
266 public ForMethod(Transformer<MethodDescription.Token> transformer) {
267 this.transformer = transformer;
268 }
269
270 /**
271 * Creates a transformer that enforces the supplied modifier contributors. All ranges of each contributor is first cleared and then overridden
272 * by the specified modifiers in the order they are supplied.
273 *
274 * @param modifierContributor The modifier transformers in their application order.
275 * @return A method transformer where each method's modifiers are adapted to the given modifiers.
276 */
277 public static Transformer<MethodDescription> withModifiers(ModifierContributor.ForMethod... modifierContributor) {
278 return withModifiers(Arrays.asList(modifierContributor));
279 }
280
281 /**
282 * Creates a transformer that enforces the supplied modifier contributors. All ranges of each contributor is first cleared and then overridden
283 * by the specified modifiers in the order they are supplied.
284 *
285 * @param modifierContributors The modifier contributors in their application order.
286 * @return A method transformer where each method's modifiers are adapted to the given modifiers.
287 */
288 public static Transformer<MethodDescription> withModifiers(List<? extends ModifierContributor.ForMethod> modifierContributors) {
289 return new ForMethod(new MethodModifierTransformer(ModifierContributor.Resolver.of(modifierContributors)));
290 }
291
292 /**
293 * {@inheritDoc}
294 */
295 public MethodDescription transform(TypeDescription instrumentedType, MethodDescription methodDescription) {
296 return new TransformedMethod(instrumentedType,
297 methodDescription.getDeclaringType(),
298 transformer.transform(instrumentedType, methodDescription.asToken(none())),
299 methodDescription.asDefined());
300 }
301
302 /**
303 * A transformer for a method's modifiers.
304 */
305 @HashCodeAndEqualsPlugin.Enhance
306 protected static class MethodModifierTransformer implements Transformer<MethodDescription.Token> {
307
308 /**
309 * The resolver to apply onto the method's modifiers.
310 */
311 private final ModifierContributor.Resolver<ModifierContributor.ForMethod> resolver;
312
313 /**
314 * Creates a new modifier transformation.
315 *
316 * @param resolver The resolver to apply onto the method's modifiers.
317 */
318 protected MethodModifierTransformer(ModifierContributor.Resolver<ModifierContributor.ForMethod> resolver) {
319 this.resolver = resolver;
320 }
321
322 /**
323 * {@inheritDoc}
324 */
325 public MethodDescription.Token transform(TypeDescription instrumentedType, MethodDescription.Token target) {
326 return new MethodDescription.Token(target.getName(),
327 resolver.resolve(target.getModifiers()),
328 target.getTypeVariableTokens(),
329 target.getReturnType(),
330 target.getParameterTokens(),
331 target.getExceptionTypes(),
332 target.getAnnotations(),
333 target.getDefaultValue(),
334 target.getReceiverType());
335 }
336 }
337
338 /**
339 * The transformed method.
340 */
341 protected static class TransformedMethod extends MethodDescription.AbstractBase {
342
343 /**
344 * The instrumented type for which this method is transformed.
345 */
346 private final TypeDescription instrumentedType;
347
348 /**
349 * The method's declaring type.
350 */
351 private final TypeDefinition declaringType;
352
353 /**
354 * The method representing the transformed method.
355 */
356 private final MethodDescription.Token token;
357
358 /**
359 * The defined shape of the transformed method.
360 */
361 private final MethodDescription.InDefinedShape methodDescription;
362
363 /**
364 * Creates a new transformed method.
365 *
366 * @param instrumentedType The instrumented type for which this method is transformed.
367 * @param declaringType The method's declaring type.
368 * @param token The method representing the transformed method.
369 * @param methodDescription The defined shape of the transformed method.
370 */
371 protected TransformedMethod(TypeDescription instrumentedType,
372 TypeDefinition declaringType,
373 Token token,
374 InDefinedShape methodDescription) {
375 this.instrumentedType = instrumentedType;
376 this.declaringType = declaringType;
377 this.token = token;
378 this.methodDescription = methodDescription;
379 }
380
381 /**
382 * {@inheritDoc}
383 */
384 public TypeList.Generic getTypeVariables() {
385 return new TypeList.Generic.ForDetachedTypes.OfTypeVariables(this, token.getTypeVariableTokens(), new AttachmentVisitor());
386 }
387
388 /**
389 * {@inheritDoc}
390 */
391 public TypeDescription.Generic getReturnType() {
392 return token.getReturnType().accept(new AttachmentVisitor());
393 }
394
395 /**
396 * {@inheritDoc}
397 */
398 public ParameterList<?> getParameters() {
399 return new TransformedParameterList();
400 }
401
402 /**
403 * {@inheritDoc}
404 */
405 public TypeList.Generic getExceptionTypes() {
406 return new TypeList.Generic.ForDetachedTypes(token.getExceptionTypes(), new AttachmentVisitor());
407 }
408
409 /**
410 * {@inheritDoc}
411 */
412 public AnnotationList getDeclaredAnnotations() {
413 return token.getAnnotations();
414 }
415
416 /**
417 * {@inheritDoc}
418 */
419 public String getInternalName() {
420 return token.getName();
421 }
422
423 /**
424 * {@inheritDoc}
425 */
426 public TypeDefinition getDeclaringType() {
427 return declaringType;
428 }
429
430 /**
431 * {@inheritDoc}
432 */
433 public int getModifiers() {
434 return token.getModifiers();
435 }
436
437 /**
438 * {@inheritDoc}
439 */
440 public AnnotationValue<?, ?> getDefaultValue() {
441 return token.getDefaultValue();
442 }
443
444 /**
445 * {@inheritDoc}
446 */
447 public InDefinedShape asDefined() {
448 return methodDescription;
449 }
450
451 /**
452 * {@inheritDoc}
453 */
454 public TypeDescription.Generic getReceiverType() {
455 TypeDescription.Generic receiverType = token.getReceiverType();
456 return receiverType == null
457 ? TypeDescription.Generic.UNDEFINED
458 : receiverType.accept(new AttachmentVisitor());
459 }
460
461 /**
462 * A parameter list representing the transformed method's parameters.
463 */
464 protected class TransformedParameterList extends ParameterList.AbstractBase<ParameterDescription> {
465
466 /**
467 * {@inheritDoc}
468 */
469 public ParameterDescription get(int index) {
470 return new TransformedParameter(index, token.getParameterTokens().get(index));
471 }
472
473 /**
474 * {@inheritDoc}
475 */
476 public int size() {
477 return token.getParameterTokens().size();
478 }
479 }
480
481 /**
482 * A transformed method's parameter.
483 */
484 protected class TransformedParameter extends ParameterDescription.AbstractBase {
485
486 /**
487 * The index of the transformed method.
488 */
489 private final int index;
490
491 /**
492 * The token representing the transformed method parameter's properties.
493 */
494 private final ParameterDescription.Token parameterToken;
495
496 /**
497 * Creates a transformed parameter.
498 *
499 * @param index The index of the transformed method.
500 * @param parameterToken The token representing the transformed method parameter's properties.
501 */
502 protected TransformedParameter(int index, ParameterDescription.Token parameterToken) {
503 this.index = index;
504 this.parameterToken = parameterToken;
505 }
506
507 /**
508 * {@inheritDoc}
509 */
510 public TypeDescription.Generic getType() {
511 return parameterToken.getType().accept(new AttachmentVisitor());
512 }
513
514 /**
515 * {@inheritDoc}
516 */
517 public MethodDescription getDeclaringMethod() {
518 return TransformedMethod.this;
519 }
520
521 /**
522 * {@inheritDoc}
523 */
524 public int getIndex() {
525 return index;
526 }
527
528 /**
529 * {@inheritDoc}
530 */
531 public boolean isNamed() {
532 return parameterToken.getName() != null;
533 }
534
535 /**
536 * {@inheritDoc}
537 */
538 public boolean hasModifiers() {
539 return parameterToken.getModifiers() != null;
540 }
541
542 /**
543 * {@inheritDoc}
544 */
545 public String getName() {
546 return isNamed()
547 ? parameterToken.getName()
548 : super.getName();
549 }
550
551 /**
552 * {@inheritDoc}
553 */
554 public int getModifiers() {
555 return hasModifiers()
556 ? parameterToken.getModifiers()
557 : super.getModifiers();
558 }
559
560 /**
561 * {@inheritDoc}
562 */
563 public AnnotationList getDeclaredAnnotations() {
564 return parameterToken.getAnnotations();
565 }
566
567 /**
568 * {@inheritDoc}
569 */
570 public InDefinedShape asDefined() {
571 return methodDescription.getParameters().get(index);
572 }
573 }
574
575 /**
576 * A visitor that attaches type variables based on the transformed method's type variables and the instrumented type. Binding type
577 * variables directly for this method is not possible as type variables are already resolved for the instrumented type such
578 * that it is required to bind variables for the instrumented type directly.
579 */
580 @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
581 protected class AttachmentVisitor extends TypeDescription.Generic.Visitor.Substitutor.WithoutTypeSubstitution {
582
583 /**
584 * {@inheritDoc}
585 */
586 public TypeDescription.Generic onTypeVariable(TypeDescription.Generic typeVariable) {
587 TypeList.Generic candidates = getTypeVariables().filter(named(typeVariable.getSymbol()));
588 TypeDescription.Generic attached = candidates.isEmpty()
589 ? instrumentedType.findVariable(typeVariable.getSymbol())
590 : candidates.getOnly();
591 if (attached == null) {
592 throw new IllegalArgumentException("Cannot attach undefined variable: " + typeVariable);
593 } else {
594 return new TypeDescription.Generic.OfTypeVariable.WithAnnotationOverlay(attached, typeVariable);
595 }
596 }
597 }
598 }
599 }
600
601 /**
602 * A compound transformer.
603 *
604 * @param <S> The type of the transformed instance.
605 */
606 @HashCodeAndEqualsPlugin.Enhance
607 class Compound<S> implements Transformer<S> {
608
609 /**
610 * The list of transformers to apply in their application order.
611 */
612 private final List<Transformer<S>> transformers;
613
614 /**
615 * Creates a new compound transformer.
616 *
617 * @param transformer The list of transformers to apply in their application order.
618 */
619 @SuppressWarnings("unchecked") // In absence of @SafeVarargs
620 public Compound(Transformer<S>... transformer) {
621 this(Arrays.asList(transformer));
622 }
623
624 /**
625 * Creates a new compound transformer.
626 *
627 * @param transformers The list of transformers to apply in their application order.
628 */
629 public Compound(List<? extends Transformer<S>> transformers) {
630 this.transformers = new ArrayList<Transformer<S>>();
631 for (Transformer<S> transformer : transformers) {
632 if (transformer instanceof Compound) {
633 this.transformers.addAll(((Compound<S>) transformer).transformers);
634 } else if (!(transformer instanceof NoOp)) {
635 this.transformers.add(transformer);
636 }
637 }
638 }
639
640 /**
641 * {@inheritDoc}
642 */
643 public S transform(TypeDescription instrumentedType, S target) {
644 for (Transformer<S> transformer : transformers) {
645 target = transformer.transform(instrumentedType, target);
646 }
647 return target;
648 }
649 }
650 }
651