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.description.annotation.AnnotationDescription;
19 import net.bytebuddy.description.method.MethodDescription;
20 import net.bytebuddy.description.method.ParameterDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.implementation.Implementation;
23 import net.bytebuddy.implementation.bind.ArgumentTypeResolver;
24 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
25 import net.bytebuddy.implementation.bytecode.StackManipulation;
26 import net.bytebuddy.implementation.bytecode.assign.Assigner;
27 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
28
29 import java.lang.annotation.*;
30
31 /**
32  * Parameters that are annotated with this annotation will be assigned the value of the parameter of the source method
33  * with the given parameter. For example, if source method {@code foo(String, Integer)} is bound to target method
34  * {@code bar(@Argument(1) Integer)}, the second parameter of {@code foo} will be bound to the first argument of
35  * {@code bar}.
36  * <p>&nbsp;</p>
37  * If a source method has less parameters than specified by {@link Argument#value()}, the method carrying this parameter
38  * annotation is excluded from the list of possible binding candidates to this particular source method. The same happens,
39  * if the source method parameter at the specified index is not assignable to the annotated parameter.
40  *
41  * @see net.bytebuddy.implementation.MethodDelegation
42  * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
43  * @see net.bytebuddy.implementation.bind.annotation.RuntimeType
44  */

45 @Documented
46 @Retention(RetentionPolicy.RUNTIME)
47 @Target(ElementType.PARAMETER)
48 public @interface Argument {
49
50     /**
51      * The index of the parameter of the source method that should be bound to this parameter.
52      *
53      * @return The required parameter index.
54      */

55     int value();
56
57     /**
58      * Determines if the argument binding is to be considered by a
59      * {@link net.bytebuddy.implementation.bind.ArgumentTypeResolver}
60      * for resolving ambiguous bindings of two methods. If
61      * {@link net.bytebuddy.implementation.bind.annotation.Argument.BindingMechanic#UNIQUE},
62      * of two bindable target methods such as for example {@code foo(String)} and {@code bar(Object)}, the {@code foo}
63      * method would be considered as dominant over the {@code bar} method because of its more specific argument type. As
64      * a side effect, only one parameter of any target method can be bound to a source method parameter with a given
65      * index unless the {@link net.bytebuddy.implementation.bind.annotation.Argument.BindingMechanic#ANONYMOUS}
66      * option is used for any other binding.
67      *
68      * @return The binding type that should be applied to this parameter binding.
69      * @see net.bytebuddy.implementation.bind.ArgumentTypeResolver
70      */

71     BindingMechanic bindingMechanic() default BindingMechanic.UNIQUE;
72
73     /**
74      * Determines if a parameter binding should be considered for resolving ambiguous method bindings.
75      *
76      * @see Argument#bindingMechanic()
77      * @see net.bytebuddy.implementation.bind.ArgumentTypeResolver
78      */

79     enum BindingMechanic {
80
81         /**
82          * The binding is unique, i.e. only one such binding must be present among all parameters of a method. As a
83          * consequence, the binding can be latter identified by an
84          * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}.
85          */

86         UNIQUE {
87             @Override
88             protected MethodDelegationBinder.ParameterBinding<?> makeBinding(TypeDescription.Generic source,
89                                                                              TypeDescription.Generic target,
90                                                                              int sourceParameterIndex,
91                                                                              Assigner assigner,
92                                                                              Assigner.Typing typing,
93                                                                              int parameterOffset) {
94                 return MethodDelegationBinder.ParameterBinding.Unique.of(
95                         new StackManipulation.Compound(
96                                 MethodVariableAccess.of(source).loadFrom(parameterOffset),
97                                 assigner.assign(source, target, typing)),
98                         new ArgumentTypeResolver.ParameterIndexToken(sourceParameterIndex)
99                 );
100             }
101         },
102
103         /**
104          * The binding is anonymous, i.e. it can be present on several parameters of the same method.
105          */

106         ANONYMOUS {
107             @Override
108             protected MethodDelegationBinder.ParameterBinding<?> makeBinding(TypeDescription.Generic source,
109                                                                              TypeDescription.Generic target,
110                                                                              int sourceParameterIndex,
111                                                                              Assigner assigner,
112                                                                              Assigner.Typing typing,
113                                                                              int parameterOffset) {
114                 return new MethodDelegationBinder.ParameterBinding.Anonymous(
115                         new StackManipulation.Compound(MethodVariableAccess.of(source).loadFrom(parameterOffset), assigner.assign(source, target, typing))
116                 );
117             }
118         };
119
120         /**
121          * Creates a binding that corresponds to this binding mechanic.
122          *
123          * @param source               The source type to be bound.
124          * @param target               The target type the {@code sourceType} is to be bound to.
125          * @param sourceParameterIndex The index of the source parameter.
126          * @param assigner             The assigner that is used to perform the assignment.
127          * @param typing               Indicates if dynamic type castings should be attempted for incompatible assignments.
128          * @param parameterOffset      The offset of the source method's parameter.
129          * @return A binding considering the chosen binding mechanic.
130          */

131         protected abstract MethodDelegationBinder.ParameterBinding<?> makeBinding(TypeDescription.Generic source,
132                                                                                   TypeDescription.Generic target,
133                                                                                   int sourceParameterIndex,
134                                                                                   Assigner assigner,
135                                                                                   Assigner.Typing typing,
136                                                                                   int parameterOffset);
137     }
138
139     /**
140      * A binder for handling the
141      * {@link net.bytebuddy.implementation.bind.annotation.Argument}
142      * annotation.
143      *
144      * @see TargetMethodAnnotationDrivenBinder
145      */

146     enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Argument> {
147
148         /**
149          * The singleton instance.
150          */

151         INSTANCE;
152
153         /**
154          * {@inheritDoc}
155          */

156         public Class<Argument> getHandledType() {
157             return Argument.class;
158         }
159
160         /**
161          * {@inheritDoc}
162          */

163         public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Argument> annotation,
164                                                                MethodDescription source,
165                                                                ParameterDescription target,
166                                                                Implementation.Target implementationTarget,
167                                                                Assigner assigner,
168                                                                Assigner.Typing typing) {
169             Argument argument = annotation.load();
170             if (argument.value() < 0) {
171                 throw new IllegalArgumentException("@Argument annotation on " + target + " specifies negative index");
172             } else if (source.getParameters().size() <= argument.value()) {
173                 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
174             }
175             return argument.bindingMechanic().makeBinding(source.getParameters().get(argument.value()).getType(),
176                     target.getType(),
177                     argument.value(),
178                     assigner,
179                     typing,
180                     source.getParameters().get(argument.value()).getOffset());
181         }
182     }
183 }
184