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.bytecode.assign.reference;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.type.TypeDescription;
20 import net.bytebuddy.description.type.TypeList;
21 import net.bytebuddy.implementation.bytecode.StackManipulation;
22 import net.bytebuddy.implementation.bytecode.assign.Assigner;
23 import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
24
25 import java.util.*;
26
27 /**
28  * <p>
29  * An assigner implementation that considers generic type assignability.
30  * </p>
31  * <p>
32  * <b>Important</b>: This implementation does not currently support method variables and their type inference.
33  * </p>
34  */

35 public enum GenericTypeAwareAssigner implements Assigner {
36
37     /**
38      * The singleton instance.
39      */

40     INSTANCE;
41
42     @Override
43     public StackManipulation assign(TypeDescription.Generic source, TypeDescription.Generic target, Typing typing) {
44         if (source.isPrimitive() || target.isPrimitive()) {
45             return source.equals(target)
46                     ? StackManipulation.Trivial.INSTANCE
47                     : StackManipulation.Illegal.INSTANCE;
48         } else if (source.accept(new IsAssignableToVisitor(target))) {
49             return StackManipulation.Trivial.INSTANCE;
50         } else if (typing.isDynamic()) {
51             return source.asErasure().isAssignableTo(target.asErasure())
52                     ? StackManipulation.Trivial.INSTANCE
53                     : TypeCasting.to(target);
54         } else {
55             return StackManipulation.Illegal.INSTANCE;
56         }
57     }
58
59     /**
60      * A visitor for generic types that determines assignability of such types.
61      */

62     @HashCodeAndEqualsPlugin.Enhance
63     protected static class IsAssignableToVisitor implements TypeDescription.Generic.Visitor<Boolean> {
64
65         /**
66          * The type to which another type is being assigned.
67          */

68         private final TypeDescription.Generic typeDescription;
69
70         /**
71          * {@code trueif the assignment is polymorphic.
72          */

73         private final boolean polymorphic;
74
75         /**
76          * Creates a new visitor to determine assignability of the supplied type.
77          *
78          * @param typeDescription The type to which another type is being assigned.
79          */

80         public IsAssignableToVisitor(TypeDescription.Generic typeDescription) {
81             this(typeDescription, true);
82         }
83
84         /**
85          * Creates a new visitor to determine assignability of the supplied type.
86          *
87          * @param typeDescription The type to which another type is being assigned.
88          * @param polymorphic     {@code trueif the assignment is polymorphic.
89          */

90         protected IsAssignableToVisitor(TypeDescription.Generic typeDescription, boolean polymorphic) {
91             this.typeDescription = typeDescription;
92             this.polymorphic = polymorphic;
93         }
94
95         /**
96          * {@inheritDoc}
97          */

98         public Boolean onGenericArray(TypeDescription.Generic genericArray) {
99             return typeDescription.accept(new OfGenericArray(genericArray, polymorphic));
100         }
101
102         /**
103          * {@inheritDoc}
104          */

105         public Boolean onWildcard(TypeDescription.Generic wildcard) {
106             return typeDescription.accept(new OfWildcard(wildcard));
107         }
108
109         /**
110          * {@inheritDoc}
111          */

112         public Boolean onParameterizedType(TypeDescription.Generic parameterizedType) {
113             return typeDescription.accept(new OfParameterizedType(parameterizedType, polymorphic));
114         }
115
116         /**
117          * {@inheritDoc}
118          */

119         public Boolean onTypeVariable(TypeDescription.Generic typeVariable) {
120             if (typeVariable.getTypeVariableSource().isInferrable()) {
121                 throw new UnsupportedOperationException("Assignability checks for type variables declared by methods are not currently supported");
122             } else if (typeVariable.equals(typeDescription)) {
123                 return true;
124             } else if (polymorphic) {
125                 Queue<TypeDescription.Generic> candidates = new LinkedList<TypeDescription.Generic>(typeVariable.getUpperBounds());
126                 while (!candidates.isEmpty()) {
127                     TypeDescription.Generic candidate = candidates.remove();
128                     if (candidate.accept(new IsAssignableToVisitor(typeDescription))) {
129                         return true;
130                     } else if (candidate.getSort().isTypeVariable()) {
131                         candidates.addAll(candidate.getUpperBounds());
132                     }
133                 }
134                 return false;
135             } else {
136                 return false;
137             }
138         }
139
140         /**
141          * {@inheritDoc}
142          */

143         public Boolean onNonGenericType(TypeDescription.Generic typeDescription) {
144             return this.typeDescription.accept(new OfNonGenericType(typeDescription, polymorphic));
145         }
146
147         /**
148          * An implementation of a assignability visitor that is applicable for any non-wildcard type.
149          */

150         @HashCodeAndEqualsPlugin.Enhance
151         protected abstract static class OfManifestType implements TypeDescription.Generic.Visitor<Boolean> {
152
153             /**
154              * The type being assigned to another type.
155              */

156             protected final TypeDescription.Generic typeDescription;
157
158             /**
159              * {@code trueif the assignment is polymorphic.
160              */

161             protected final boolean polymorphic;
162
163             /**
164              * Creates a new visitor for a manifest type.
165              *
166              * @param typeDescription The type being assigned to another type.
167              * @param polymorphic     {@code trueif the assignment is polymorphic.
168              */

169             protected OfManifestType(TypeDescription.Generic typeDescription, boolean polymorphic) {
170                 this.typeDescription = typeDescription;
171                 this.polymorphic = polymorphic;
172             }
173
174             /**
175              * {@inheritDoc}
176              */

177             public Boolean onWildcard(TypeDescription.Generic wildcard) {
178                 for (TypeDescription.Generic upperBound : wildcard.getUpperBounds()) {
179                     if (!typeDescription.accept(new IsAssignableToVisitor(upperBound))) {
180                         return false;
181                     }
182                 }
183                 for (TypeDescription.Generic lowerBound : wildcard.getLowerBounds()) {
184                     if (!lowerBound.accept(new IsAssignableToVisitor(typeDescription))) {
185                         return false;
186                     }
187                 }
188                 return true;
189             }
190
191             /**
192              * {@inheritDoc}
193              */

194             public Boolean onTypeVariable(TypeDescription.Generic typeVariable) {
195                 if (typeVariable.getTypeVariableSource().isInferrable()) {
196                     throw new UnsupportedOperationException("Assignability checks for type variables declared by methods arel not currently supported");
197                 } else {
198                     return false;
199                 }
200             }
201         }
202
203         /**
204          * A visitor for determining assignability of a type in a type hierarchy, i.e. a non-generic or parameterized type.
205          */

206         protected abstract static class OfSimpleType extends OfManifestType {
207
208             /**
209              * Creates a new visitor.
210              *
211              * @param typeDescription The type being assigned to another type.
212              * @param polymorphic     {@code trueif the assignment is polymorphic.
213              */

214             protected OfSimpleType(TypeDescription.Generic typeDescription, boolean polymorphic) {
215                 super(typeDescription, polymorphic);
216             }
217
218             /**
219              * {@inheritDoc}
220              */

221             public Boolean onParameterizedType(TypeDescription.Generic parameterizedType) {
222                 Queue<TypeDescription.Generic> candidates = new LinkedList<TypeDescription.Generic>(Collections.singleton(typeDescription));
223                 Set<TypeDescription> previous = new HashSet<TypeDescription>(Collections.singleton(typeDescription.asErasure()));
224                 do {
225                     TypeDescription.Generic candidate = candidates.remove();
226                     if (candidate.asErasure().equals(parameterizedType.asErasure())) {
227                         if (candidate.getSort().isNonGeneric()) {
228                             return true;
229                         } else /* if (candidate.getSort().isParameterized() */ {
230                             TypeList.Generic source = candidate.getTypeArguments(), target = parameterizedType.getTypeArguments();
231                             int size = target.size();
232                             if (source.size() != size) {
233                                 return false;
234                             }
235                             for (int index = 0; index < size; index++) {
236                                 if (!source.get(index).accept(new IsAssignableToVisitor(target.get(index), false))) {
237                                     return false;
238                                 }
239                             }
240                             TypeDescription.Generic ownerType = parameterizedType.getOwnerType();
241                             return ownerType == null || ownerType.accept(new IsAssignableToVisitor(parameterizedType.getOwnerType()));
242                         }
243                     } else if (polymorphic) {
244                         TypeDescription.Generic superClass = candidate.getSuperClass();
245                         if (superClass != null && previous.add(superClass.asErasure())) {
246                             candidates.add(superClass);
247                         }
248                         for (TypeDescription.Generic anInterface : candidate.getInterfaces()) {
249                             if (previous.add(anInterface.asErasure())) {
250                                 candidates.add(anInterface);
251                             }
252                         }
253                     }
254                 } while (!candidates.isEmpty());
255                 return false;
256             }
257
258             /**
259              * {@inheritDoc}
260              */

261             public Boolean onNonGenericType(TypeDescription.Generic typeDescription) {
262                 return polymorphic
263                         ? this.typeDescription.asErasure().isAssignableTo(typeDescription.asErasure())
264                         : this.typeDescription.asErasure().equals(typeDescription.asErasure());
265             }
266         }
267
268         /**
269          * A visitor for determining assignability of a generic array type.
270          */

271         protected static class OfGenericArray extends OfManifestType {
272
273             /**
274              * Creates a new visitor.
275              *
276              * @param typeDescription The type being assigned to another type.
277              * @param polymorphic     {@code trueif the assignment is polymorphic.
278              */

279             protected OfGenericArray(TypeDescription.Generic typeDescription, boolean polymorphic) {
280                 super(typeDescription, polymorphic);
281             }
282
283             /**
284              * {@inheritDoc}
285              */

286             public Boolean onGenericArray(TypeDescription.Generic genericArray) {
287                 TypeDescription.Generic source = typeDescription.getComponentType(), target = genericArray.getComponentType();
288                 while (source.getSort().isGenericArray() && target.getSort().isGenericArray()) {
289                     source = source.getComponentType();
290                     target = target.getComponentType();
291                 }
292                 return !source.getSort().isGenericArray() && !target.getSort().isGenericArray() && source.accept(new IsAssignableToVisitor(target));
293             }
294
295             /**
296              * {@inheritDoc}
297              */

298             public Boolean onParameterizedType(TypeDescription.Generic parameterizedType) {
299                 return false;
300             }
301
302             /**
303              * {@inheritDoc}
304              */

305             public Boolean onNonGenericType(TypeDescription.Generic typeDescription) {
306                 return polymorphic
307                         ? this.typeDescription.asErasure().isAssignableTo(typeDescription.asErasure())
308                         : this.typeDescription.asErasure().equals(typeDescription.asErasure());
309             }
310         }
311
312         /**
313          * A visitor to determine the assignability of a wildcard type.
314          */

315         @HashCodeAndEqualsPlugin.Enhance
316         protected static class OfWildcard implements TypeDescription.Generic.Visitor<Boolean> {
317
318             /**
319              * The wildcard type being assigned to another type.
320              */

321             private final TypeDescription.Generic wildcard;
322
323             /**
324              * Creates a visitor for a wildcard type assignment.
325              *
326              * @param wildcard The wildcard type being assigned to another type.
327              */

328             protected OfWildcard(TypeDescription.Generic wildcard) {
329                 this.wildcard = wildcard;
330             }
331
332             /**
333              * {@inheritDoc}
334              */

335             public Boolean onGenericArray(TypeDescription.Generic genericArray) {
336                 return false;
337             }
338
339             /**
340              * {@inheritDoc}
341              */

342             public Boolean onWildcard(TypeDescription.Generic wildcard) {
343                 boolean hasUpperBounds = false, hasLowerBounds = false;
344                 for (TypeDescription.Generic target : wildcard.getUpperBounds()) {
345                     for (TypeDescription.Generic source : this.wildcard.getUpperBounds()) {
346                         if (!source.accept(new IsAssignableToVisitor(target))) {
347                             return false;
348                         }
349                     }
350                     hasUpperBounds = hasUpperBounds || !target.represents(Object.class);
351                 }
352                 for (TypeDescription.Generic target : wildcard.getLowerBounds()) {
353                     for (TypeDescription.Generic source : this.wildcard.getLowerBounds()) {
354                         if (!target.accept(new IsAssignableToVisitor(source))) {
355                             return false;
356                         }
357                     }
358                     hasLowerBounds = true;
359                 }
360                 if (hasUpperBounds) {
361                     return this.wildcard.getLowerBounds().isEmpty();
362                 } else if (hasLowerBounds) {
363                     TypeList.Generic upperBounds = this.wildcard.getUpperBounds();
364                     return upperBounds.size() == 0 || upperBounds.size() == 1 && upperBounds.getOnly().represents(Object.class);
365                 } else {
366                     return true;
367                 }
368             }
369
370             /**
371              * {@inheritDoc}
372              */

373             public Boolean onParameterizedType(TypeDescription.Generic parameterizedType) {
374                 return false;
375             }
376
377             /**
378              * {@inheritDoc}
379              */

380             public Boolean onTypeVariable(TypeDescription.Generic typeVariable) {
381                 return false;
382             }
383
384             /**
385              * {@inheritDoc}
386              */

387             public Boolean onNonGenericType(TypeDescription.Generic typeDescription) {
388                 return false;
389             }
390         }
391
392         /**
393          * A visitor for determining the assignability of a parameterized type.
394          */

395         protected static class OfParameterizedType extends OfSimpleType {
396
397             /**
398              * Creates a new visitor.
399              *
400              * @param typeDescription The type being assigned to another type.
401              * @param polymorphic     {@code trueif the assignment is polymorphic.
402              */

403             protected OfParameterizedType(TypeDescription.Generic typeDescription, boolean polymorphic) {
404                 super(typeDescription, polymorphic);
405             }
406
407             /**
408              * {@inheritDoc}
409              */

410             public Boolean onGenericArray(TypeDescription.Generic genericArray) {
411                 return false;
412             }
413         }
414
415         /**
416          * A visitor for determining assignability of a non-generic type.
417          */

418         protected static class OfNonGenericType extends OfSimpleType {
419
420             /**
421              * Creates a new visitor.
422              *
423              * @param typeDescription The type being assigned to another type.
424              * @param polymorphic     {@code trueif the assignment is polymorphic.
425              */

426             protected OfNonGenericType(TypeDescription.Generic typeDescription, boolean polymorphic) {
427                 super(typeDescription, polymorphic);
428             }
429
430             /**
431              * {@inheritDoc}
432              */

433             public Boolean onGenericArray(TypeDescription.Generic genericArray) {
434                 return polymorphic
435                         ? typeDescription.asErasure().isAssignableTo(genericArray.asErasure())
436                         : typeDescription.asErasure().equals(genericArray.asErasure());
437             }
438         }
439     }
440 }
441