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> </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