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;
17
18 import net.bytebuddy.description.method.MethodDescription;
19 import net.bytebuddy.description.method.ParameterList;
20 import net.bytebuddy.description.type.TypeDescription;
21
22 /**
23  * Implementation of an
24  * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver}
25  * that resolves two conflicting bindings by considering most-specific types of target method parameters in the same manner
26  * as the Java compiler resolves bindings of overloaded method.
27  * <p>&nbsp;</p>
28  * This ambiguity resolver:
29  * <ol>
30  * <li>Checks for each parameter of the source method if a one-to-one parameter binding to both of the target methods exist.</li>
31  * <li>If any of the source method parameters were bound one-to-one to both target methods, the method with the most specific
32  * type is considered as dominant.</li>
33  * <li>If this result is dominant for both the left and the right target method, this resolver will consider the binding as
34  * ambiguous.</li>
35  * <li>If none of the methods is dominant and if the comparison did not result in an ambiguous resolution, the method that
36  * consists of the most one-to-one parameter bindings is considered dominant.</li>
37  * </ol>
38  * Primitive types are considered dominant in the same manner as by the Java compiler.
39  * <p>&nbsp;</p>
40  * For example: If a source method only parameter was successfully bound one-to-one to the only parameters of the target
41  * methods {@code foo(Object)} and {@code bar(String)}, this ambiguity resolver will detect that the {@code String} type
42  * is more specific than the {@code Object} type and determine {@code bar(String)} as the dominant binding.
43  */

44 public enum ArgumentTypeResolver implements MethodDelegationBinder.AmbiguityResolver {
45
46     /**
47      * The singleton instance.
48      */

49     INSTANCE;
50
51     /**
52      * Resolves two bindings by comparing their binding of similar arguments and determining their most specific types.
53      *
54      * @param sourceParameterType The parameter type of the source method
55      * @param leftParameterIndex  The index of the parameter of the left method.
56      * @param left                The left method's parameter binding.
57      * @param rightParameterIndex The index of the parameter of the right method.
58      * @param right               The right method's parameter binding.
59      * @return A resolution according to the given parameters.
60      */

61     private static Resolution resolveRivalBinding(TypeDescription sourceParameterType,
62                                                   int leftParameterIndex,
63                                                   MethodDelegationBinder.MethodBinding left,
64                                                   int rightParameterIndex,
65                                                   MethodDelegationBinder.MethodBinding right) {
66         TypeDescription leftParameterType = left.getTarget().getParameters().get(leftParameterIndex).getType().asErasure();
67         TypeDescription rightParameterType = right.getTarget().getParameters().get(rightParameterIndex).getType().asErasure();
68         if (!leftParameterType.equals(rightParameterType)) {
69             if (leftParameterType.isPrimitive() && rightParameterType.isPrimitive()) {
70                 return PrimitiveTypePrecedence.forPrimitive(leftParameterType).resolve(PrimitiveTypePrecedence.forPrimitive(rightParameterType));
71             } else if (leftParameterType.isPrimitive() /* && !rightParameterType.isPrimitive() */) {
72                 return sourceParameterType.isPrimitive() ? Resolution.LEFT : Resolution.RIGHT;
73             } else if (/* !leftParameterType.isPrimitive() && */ rightParameterType.isPrimitive()) {
74                 return sourceParameterType.isPrimitive() ? Resolution.RIGHT : Resolution.LEFT;
75             } else {
76                 // Note that leftParameterType != rightParameterType, thus both cannot be true.
77                 if (leftParameterType.isAssignableFrom(rightParameterType)) {
78                     return Resolution.RIGHT;
79                 } else if (rightParameterType.isAssignableFrom(leftParameterType)) {
80                     return Resolution.LEFT;
81                 } else {
82                     return Resolution.AMBIGUOUS;
83                 }
84             }
85         } else {
86             return Resolution.UNKNOWN;
87         }
88     }
89
90     /**
91      * Resolves the most specific method by their score. A method's score is calculated by the absolute number of
92      * parameters that were bound by using an explicit {@link net.bytebuddy.implementation.bind.annotation.Argument}
93      * annotation.
94      *
95      * @param boundParameterScore The difference of the scores of the left and the right method.
96      * @return A resolution according to this score.
97      */

98     private static Resolution resolveByScore(int boundParameterScore) {
99         if (boundParameterScore == 0) {
100             return Resolution.AMBIGUOUS;
101         } else if (boundParameterScore > 0) {
102             return Resolution.LEFT;
103         } else /* difference < 0*/ {
104             return Resolution.RIGHT;
105         }
106     }
107
108     /**
109      * {@inheritDoc}
110      */

111     public Resolution resolve(MethodDescription source,
112                               MethodDelegationBinder.MethodBinding left,
113                               MethodDelegationBinder.MethodBinding right) {
114         Resolution resolution = Resolution.UNKNOWN;
115         ParameterList<?> sourceParameters = source.getParameters();
116         int leftExtra = 0, rightExtra = 0;
117         for (int sourceParameterIndex = 0; sourceParameterIndex < sourceParameters.size(); sourceParameterIndex++) {
118             ParameterIndexToken parameterIndexToken = new ParameterIndexToken(sourceParameterIndex);
119             Integer leftParameterIndex = left.getTargetParameterIndex(parameterIndexToken);
120             Integer rightParameterIndex = right.getTargetParameterIndex(parameterIndexToken);
121             if (leftParameterIndex != null && rightParameterIndex != null) {
122                 resolution = resolution.merge(resolveRivalBinding(sourceParameters.get(sourceParameterIndex).getType().asErasure(),
123                         leftParameterIndex,
124                         left,
125                         rightParameterIndex,
126                         right));
127             } else if (leftParameterIndex != null /* && rightParameterIndex == null */) {
128                 leftExtra++;
129             } else if (/*leftParameterIndex == null && */ rightParameterIndex != null) {
130                 rightExtra++;
131             }
132         }
133         return resolution == Resolution.UNKNOWN
134                 ? resolveByScore(leftExtra - rightExtra)
135                 : resolution;
136     }
137
138     /**
139      * A representation of the precedence of a most specific primitive type in the Java programming language.
140      */

141     protected enum PrimitiveTypePrecedence {
142
143         /**
144          * The precedence of the {@code boolean} type.
145          */

146         BOOLEAN(0),
147
148         /**
149          * The precedence of the {@code byte} type.
150          */

151         BYTE(1),
152
153         /**
154          * The precedence of the {@code short} type.
155          */

156         SHORT(2),
157
158         /**
159          * The precedence of the {@code int} type.
160          */

161         INTEGER(3),
162
163         /**
164          * The precedence of the {@code char} type.
165          */

166         CHARACTER(4),
167
168         /**
169          * The precedence of the {@code long} type.
170          */

171         LONG(5),
172
173         /**
174          * The precedence of the {@code float} type.
175          */

176         FLOAT(6),
177
178         /**
179          * The precedence of the {@code double} type.
180          */

181         DOUBLE(7);
182
183         /**
184          * A score representing the precedence where a higher score represents a less specific type.
185          */

186         private final int score;
187
188         /**
189          * Creates a new primitive type precedence.
190          *
191          * @param score A score representing the precedence where a higher score represents a less specific type.
192          */

193         PrimitiveTypePrecedence(int score) {
194             this.score = score;
195         }
196
197         /**
198          * Locates the primitive type precedence for a given type.
199          *
200          * @param typeDescription The non-void, primitive type for which the precedence should be located.
201          * @return The corresponding primitive type precedence.
202          */

203         public static PrimitiveTypePrecedence forPrimitive(TypeDescription typeDescription) {
204             if (typeDescription.represents(boolean.class)) {
205                 return BOOLEAN;
206             } else if (typeDescription.represents(byte.class)) {
207                 return BYTE;
208             } else if (typeDescription.represents(short.class)) {
209                 return SHORT;
210             } else if (typeDescription.represents(int.class)) {
211                 return INTEGER;
212             } else if (typeDescription.represents(char.class)) {
213                 return CHARACTER;
214             } else if (typeDescription.represents(long.class)) {
215                 return LONG;
216             } else if (typeDescription.represents(float.class)) {
217                 return FLOAT;
218             } else if (typeDescription.represents(double.class)) {
219                 return DOUBLE;
220             } else {
221                 throw new IllegalArgumentException("Not a non-void, primitive type " + typeDescription);
222             }
223         }
224
225         /**
226          * Resolves the least specific type of two primitive type precedence with this instance representing a
227          * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#LEFT}
228          * resolution and the argument type representing the
229          * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.AmbiguityResolver.Resolution#RIGHT}
230          * resolution.
231          *
232          * @param right Another primitive type precedence against which this precedence should be resolved.
233          * @return The resolution of
234          */

235         public Resolution resolve(PrimitiveTypePrecedence right) {
236             if (score - right.score == 0) {
237                 return Resolution.UNKNOWN;
238             } else if (score - right.score > 0) {
239                 return Resolution.RIGHT;
240             } else /* score - right.score < 0 */ {
241                 return Resolution.LEFT;
242             }
243         }
244     }
245
246     /**
247      * This token is used to mark a one-to-one binding of a source method parameter to a target method parameter.
248      *
249      * @see net.bytebuddy.implementation.bind.MethodDelegationBinder.MethodBinding#getTargetParameterIndex(Object)
250      */

251     public static class ParameterIndexToken {
252
253         /**
254          * The parameter index that is represented by this token.
255          */

256         @SuppressWarnings("unused")
257         private final int parameterIndex;
258
259         /**
260          * Create a parameter index token for a given parameter of the source method.
261          *
262          * @param parameterIndex The parameter index of the source method which is mapped to a target method parameter.
263          */

264         public ParameterIndexToken(int parameterIndex) {
265             this.parameterIndex = parameterIndex;
266         }
267
268         @Override
269         public int hashCode() {
270             return parameterIndex;
271         }
272
273         @Override
274         public boolean equals(Object other) {
275             if (this == other) {
276                 return true;
277             } else if (other == null || getClass() != other.getClass()) {
278                 return false;
279             }
280             ParameterIndexToken parameterIndexToken = (ParameterIndexToken) other;
281             return parameterIndex == parameterIndexToken.parameterIndex;
282         }
283     }
284 }
285