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