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.description.method;
17
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.build.CachedReturnPlugin;
20 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
21 import net.bytebuddy.description.ByteCodeElement;
22 import net.bytebuddy.description.ModifierReviewable;
23 import net.bytebuddy.description.NamedElement;
24 import net.bytebuddy.description.annotation.AnnotationDescription;
25 import net.bytebuddy.description.annotation.AnnotationList;
26 import net.bytebuddy.description.annotation.AnnotationSource;
27 import net.bytebuddy.description.type.TypeDefinition;
28 import net.bytebuddy.description.type.TypeDescription;
29 import net.bytebuddy.description.type.TypeList;
30 import net.bytebuddy.implementation.bytecode.StackSize;
31 import net.bytebuddy.matcher.ElementMatcher;
32
33 import java.lang.annotation.Annotation;
34 import java.lang.reflect.*;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.AbstractList;
38 import java.util.Collections;
39 import java.util.List;
40
41 /**
42 * Description of the parameter of a Java method or constructor.
43 */
44 public interface ParameterDescription extends AnnotationSource,
45 NamedElement.WithRuntimeName,
46 NamedElement.WithOptionalName,
47 ModifierReviewable.ForParameterDescription,
48 ByteCodeElement.TypeDependant<ParameterDescription.InDefinedShape, ParameterDescription.Token> {
49
50 /**
51 * The prefix for names of an unnamed parameter.
52 */
53 String NAME_PREFIX = "arg";
54
55 /**
56 * Returns the type of this parameter.
57 *
58 * @return The type of this parameter.
59 */
60 TypeDescription.Generic getType();
61
62 /**
63 * Returns the method that declares this parameter.
64 *
65 * @return The method that declares this parameter.
66 */
67 MethodDescription getDeclaringMethod();
68
69 /**
70 * Returns this parameter's index.
71 *
72 * @return The index of this parameter.
73 */
74 int getIndex();
75
76 /**
77 * Checks if this parameter has an explicit modifier. A parameter without a modifier is simply treated as
78 * if it had a modifier of zero.
79 *
80 * @return {@code true} if this parameter defines explicit modifiers.
81 */
82 boolean hasModifiers();
83
84 /**
85 * Returns the offset to the parameter value within the local method variable.
86 *
87 * @return The offset of this parameter's value.
88 */
89 int getOffset();
90
91 /**
92 * Represents a parameter description in its generic shape, i.e. in the shape it is defined by a generic or raw type.
93 */
94 interface InGenericShape extends ParameterDescription {
95
96 /**
97 * {@inheritDoc}
98 */
99 MethodDescription.InGenericShape getDeclaringMethod();
100 }
101
102 /**
103 * Represents a parameter in its defined shape, i.e. in the form it is defined by a class without its type variables being resolved.
104 */
105 interface InDefinedShape extends ParameterDescription {
106
107 /**
108 * {@inheritDoc}
109 */
110 MethodDescription.InDefinedShape getDeclaringMethod();
111
112 /**
113 * An abstract base implementation of a parameter description in its defined shape.
114 */
115 abstract class AbstractBase extends ParameterDescription.AbstractBase implements InDefinedShape {
116
117 /**
118 * {@inheritDoc}
119 */
120 public InDefinedShape asDefined() {
121 return this;
122 }
123 }
124 }
125
126 /**
127 * A base implementation of a method parameter description.
128 */
129 abstract class AbstractBase extends ModifierReviewable.AbstractBase implements ParameterDescription {
130
131 /**
132 * {@inheritDoc}
133 */
134 public String getName() {
135 return NAME_PREFIX.concat(String.valueOf(getIndex()));
136 }
137
138 /**
139 * {@inheritDoc}
140 */
141 public String getInternalName() {
142 return getName();
143 }
144
145 /**
146 * {@inheritDoc}
147 */
148 public String getActualName() {
149 return isNamed()
150 ? getName()
151 : EMPTY_NAME;
152 }
153
154 /**
155 * {@inheritDoc}
156 */
157 public int getModifiers() {
158 return EMPTY_MASK;
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 public int getOffset() {
165 TypeList parameterType = getDeclaringMethod().getParameters().asTypeList().asErasures();
166 int offset = getDeclaringMethod().isStatic()
167 ? StackSize.ZERO.getSize()
168 : StackSize.SINGLE.getSize();
169 for (int i = 0; i < getIndex(); i++) {
170 offset += parameterType.get(i).getStackSize().getSize();
171 }
172 return offset;
173 }
174
175 /**
176 * {@inheritDoc}
177 */
178 public Token asToken(ElementMatcher<? super TypeDescription> matcher) {
179 return new Token(getType().accept(new TypeDescription.Generic.Visitor.Substitutor.ForDetachment(matcher)),
180 getDeclaredAnnotations(),
181 isNamed()
182 ? getName()
183 : Token.NO_NAME,
184 hasModifiers()
185 ? (Integer) getModifiers()
186 : Token.NO_MODIFIERS);
187 }
188
189 @Override
190 @CachedReturnPlugin.Enhance
191 public int hashCode() {
192 return getDeclaringMethod().hashCode() ^ getIndex();
193 }
194
195 @Override
196 public boolean equals(Object other) {
197 if (this == other) {
198 return true;
199 } else if (!(other instanceof ParameterDescription)) {
200 return false;
201 }
202 ParameterDescription parameterDescription = (ParameterDescription) other;
203 return getDeclaringMethod().equals(parameterDescription.getDeclaringMethod()) && getIndex() == parameterDescription.getIndex();
204 }
205
206 @Override
207 public String toString() {
208 StringBuilder stringBuilder = new StringBuilder(Modifier.toString(getModifiers()));
209 if (getModifiers() != EMPTY_MASK) {
210 stringBuilder.append(' ');
211 }
212 stringBuilder.append(isVarArgs()
213 ? getType().asErasure().getName().replaceFirst("\\[\\]$", "...")
214 : getType().asErasure().getName());
215 return stringBuilder.append(' ').append(getName()).toString();
216 }
217 }
218
219 /**
220 * Description of a loaded parameter with support for the information exposed by {@code java.lang.reflect.Parameter}.
221 *
222 * @param <T> The type of the {@code java.lang.reflect.Executable} that this list represents.
223 */
224 abstract class ForLoadedParameter<T extends AccessibleObject> extends InDefinedShape.AbstractBase {
225
226 /**
227 * A dispatcher for reading properties from {@code java.lang.reflect.Executable} instances.
228 */
229 private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
230
231 /**
232 * The {@code java.lang.reflect.Executable} for which the parameter types are described.
233 */
234 protected final T executable;
235
236 /**
237 * The parameter's index.
238 */
239 protected final int index;
240
241 /**
242 * The parameter annotation source to query.
243 */
244 protected final ParameterAnnotationSource parameterAnnotationSource;
245
246 /**
247 * Creates a new description for a loaded parameter.
248 *
249 * @param executable The {@code java.lang.reflect.Executable} for which the parameter types are described.
250 * @param index The parameter's index.
251 * @param parameterAnnotationSource The parameter annotation source to query.
252 */
253 protected ForLoadedParameter(T executable, int index, ParameterAnnotationSource parameterAnnotationSource) {
254 this.executable = executable;
255 this.index = index;
256 this.parameterAnnotationSource = parameterAnnotationSource;
257 }
258
259 /**
260 * {@inheritDoc}
261 */
262 public String getName() {
263 return DISPATCHER.getName(executable, index);
264 }
265
266 /**
267 * {@inheritDoc}
268 */
269 public int getIndex() {
270 return index;
271 }
272
273 /**
274 * {@inheritDoc}
275 */
276 public boolean isNamed() {
277 return DISPATCHER.isNamePresent(executable, index);
278 }
279
280 /**
281 * {@inheritDoc}
282 */
283 public int getModifiers() {
284 return DISPATCHER.getModifiers(executable, index);
285 }
286
287 /**
288 * {@inheritDoc}
289 */
290 public boolean hasModifiers() {
291 // Rational: If a parameter is not named despite the information being attached,
292 // it is synthetic, i.e. it has non-default modifiers.
293 return isNamed() || getModifiers() != EMPTY_MASK;
294 }
295
296 /**
297 * A source for querying parameter annotations.
298 */
299 public interface ParameterAnnotationSource {
300
301 /**
302 * Returns the parameter annotations represented by this source.
303 *
304 * @return The parameter annotations as an array indexed by parameter index.
305 */
306 Annotation[][] getParameterAnnotations();
307
308 /**
309 * A source for a loaded constructor.
310 */
311 @HashCodeAndEqualsPlugin.Enhance
312 class ForLoadedConstructor implements ParameterAnnotationSource {
313
314 /**
315 * The constructor to query for annotations.
316 */
317 private final Constructor<?> constructor;
318
319 /**
320 * Creates a new parameter annotation source for a constructor.
321 *
322 * @param constructor The constructor to query for annotations.
323 */
324 public ForLoadedConstructor(Constructor<?> constructor) {
325 this.constructor = constructor;
326 }
327
328 /**
329 * {@inheritDoc}
330 */
331 public Annotation[][] getParameterAnnotations() {
332 return constructor.getParameterAnnotations();
333 }
334 }
335
336 /**
337 * A source for a loaded method.
338 */
339 @HashCodeAndEqualsPlugin.Enhance
340 class ForLoadedMethod implements ParameterAnnotationSource {
341
342 /**
343 * The method to query for annotations.
344 */
345 private final Method method;
346
347 /**
348 * Creates a new parameter annpotation source for a method.
349 *
350 * @param method The method to query for annotations.
351 */
352 public ForLoadedMethod(Method method) {
353 this.method = method;
354 }
355
356 /**
357 * {@inheritDoc}
358 */
359 public Annotation[][] getParameterAnnotations() {
360 return method.getParameterAnnotations();
361 }
362 }
363 }
364
365 /**
366 * A dispatcher creating parameter descriptions based on the API that is available for the current JVM.
367 */
368 protected interface Dispatcher {
369
370 /**
371 * Returns the given parameter's modifiers.
372 *
373 * @param executable The executable to introspect.
374 * @param index The parameter's index.
375 * @return The parameter's modifiers.
376 */
377 int getModifiers(AccessibleObject executable, int index);
378
379 /**
380 * Returns {@code true} if the given parameter has an explicit name.
381 *
382 * @param executable The parameter to introspect.
383 * @param index The parameter's index.
384 * @return {@code true} if the given parameter has an explicit name.
385 */
386 boolean isNamePresent(AccessibleObject executable, int index);
387
388 /**
389 * Returns the given parameter's implicit or explicit name.
390 *
391 * @param executable The parameter to introspect.
392 * @param index The parameter's index.
393 * @return The parameter's name.
394 */
395 String getName(AccessibleObject executable, int index);
396
397 /**
398 * A creation action for a dispatcher.
399 */
400 enum CreationAction implements PrivilegedAction<Dispatcher> {
401
402 /**
403 * The singleton instance.
404 */
405 INSTANCE;
406
407 /**
408 * {@inheritDoc}
409 */
410 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
411 public Dispatcher run() {
412 try {
413 Class<?> executableType = Class.forName("java.lang.reflect.Executable");
414 Class<?> parameterType = Class.forName("java.lang.reflect.Parameter");
415 return new Dispatcher.ForJava8CapableVm(executableType.getMethod("getParameters"),
416 parameterType.getMethod("getName"),
417 parameterType.getMethod("isNamePresent"),
418 parameterType.getMethod("getModifiers"));
419 } catch (Exception ignored) {
420 return Dispatcher.ForLegacyVm.INSTANCE;
421 }
422 }
423 }
424
425 /**
426 * A dispatcher for VMs that support the {@code java.lang.reflect.Parameter} API for Java 8+.
427 */
428 @HashCodeAndEqualsPlugin.Enhance
429 class ForJava8CapableVm implements Dispatcher {
430
431 /**
432 * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
433 */
434 private static final Object[] NO_ARGUMENTS = new Object[0];
435
436 /**
437 * A reference to {@code java.lang.reflect.Executable#getParameters}.
438 */
439 private final Method getParameters;
440
441 /**
442 * A reference to {@code java.lang.reflect.Parameter#getName}.
443 */
444 private final Method getName;
445
446 /**
447 * A reference to {@code java.lang.reflect.Parameter#isNamePresent}.
448 */
449 private final Method isNamePresent;
450
451 /**
452 * A reference to {@code java.lang.reflect.Parameter#getModifiers}.
453 */
454 private final Method getModifiers;
455
456 /**
457 * Creates a new dispatcher for a modern VM.
458 *
459 * @param getParameters A reference to {@code java.lang.reflect.Executable#getTypeArguments}.
460 * @param getName A reference to {@code java.lang.reflect.Parameter#getName}.
461 * @param isNamePresent A reference to {@code java.lang.reflect.Parameter#isNamePresent}.
462 * @param getModifiers A reference to {@code java.lang.reflect.Parameter#getModifiers}.
463 */
464 protected ForJava8CapableVm(Method getParameters, Method getName, Method isNamePresent, Method getModifiers) {
465 this.getParameters = getParameters;
466 this.getName = getName;
467 this.isNamePresent = isNamePresent;
468 this.getModifiers = getModifiers;
469 }
470
471 /**
472 * {@inheritDoc}
473 */
474 public int getModifiers(AccessibleObject executable, int index) {
475 try {
476 return (Integer) getModifiers.invoke(getParameter(executable, index), NO_ARGUMENTS);
477 } catch (IllegalAccessException exception) {
478 throw new IllegalStateException("Cannot access java.lang.reflect.Parameter#getModifiers", exception);
479 } catch (InvocationTargetException exception) {
480 throw new IllegalStateException("Error invoking java.lang.reflect.Parameter#getModifiers", exception.getCause());
481 }
482 }
483
484 /**
485 * {@inheritDoc}
486 */
487 public boolean isNamePresent(AccessibleObject executable, int index) {
488 try {
489 return (Boolean) isNamePresent.invoke(getParameter(executable, index), NO_ARGUMENTS);
490 } catch (IllegalAccessException exception) {
491 throw new IllegalStateException("Cannot access java.lang.reflect.Parameter#isNamePresent", exception);
492 } catch (InvocationTargetException exception) {
493 throw new IllegalStateException("Error invoking java.lang.reflect.Parameter#isNamePresent", exception.getCause());
494 }
495 }
496
497 /**
498 * {@inheritDoc}
499 */
500 public String getName(AccessibleObject executable, int index) {
501 try {
502 return (String) getName.invoke(getParameter(executable, index), NO_ARGUMENTS);
503 } catch (IllegalAccessException exception) {
504 throw new IllegalStateException("Cannot access java.lang.reflect.Parameter#getName", exception);
505 } catch (InvocationTargetException exception) {
506 throw new IllegalStateException("Error invoking java.lang.reflect.Parameter#getName", exception.getCause());
507 }
508 }
509
510 /**
511 * Returns the {@code java.lang.reflect.Parameter} of an executable at a given index.
512 *
513 * @param executable The executable for which a parameter should be read.
514 * @param index The index of the parameter.
515 * @return The parameter for the given index.
516 */
517 private Object getParameter(AccessibleObject executable, int index) {
518 try {
519 return Array.get(getParameters.invoke(executable, NO_ARGUMENTS), index);
520 } catch (IllegalAccessException exception) {
521 throw new IllegalStateException("Cannot access java.lang.reflect.Executable#getParameters", exception);
522 } catch (InvocationTargetException exception) {
523 throw new IllegalStateException("Error invoking java.lang.reflect.Executable#getParameters", exception.getCause());
524 }
525 }
526 }
527
528 /**
529 * A dispatcher for a legacy VM that does not know the {@code java.lang.reflect.Parameter} type that only throws
530 * exceptions on any property extraction.
531 */
532 enum ForLegacyVm implements Dispatcher {
533
534 /**
535 * The singleton instance.
536 */
537 INSTANCE;
538
539 /**
540 * {@inheritDoc}
541 */
542 public int getModifiers(AccessibleObject executable, int index) {
543 throw new UnsupportedOperationException("Cannot dispatch method for java.lang.reflect.Parameter");
544 }
545
546 /**
547 * {@inheritDoc}
548 */
549 public boolean isNamePresent(AccessibleObject executable, int index) {
550 throw new UnsupportedOperationException("Cannot dispatch method for java.lang.reflect.Parameter");
551 }
552
553 /**
554 * {@inheritDoc}
555 */
556 public String getName(AccessibleObject executable, int index) {
557 throw new UnsupportedOperationException("Cannot dispatch method for java.lang.reflect.Parameter");
558 }
559 }
560 }
561
562 /**
563 * A description of a loaded {@link Method} parameter for a modern VM.
564 */
565 protected static class OfMethod extends ForLoadedParameter<Method> {
566
567 /**
568 * Creates a new description for a loaded method.
569 *
570 * @param method The method for which a parameter is represented.
571 * @param index The index of the parameter.
572 * @param parameterAnnotationSource The parameter annotation source to query.
573 */
574 protected OfMethod(Method method, int index, ParameterAnnotationSource parameterAnnotationSource) {
575 super(method, index, parameterAnnotationSource);
576 }
577
578 /**
579 * {@inheritDoc}
580 */
581 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
582 public MethodDescription.InDefinedShape getDeclaringMethod() {
583 return new MethodDescription.ForLoadedMethod(executable);
584 }
585
586 /**
587 * {@inheritDoc}
588 */
589 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
590 public TypeDescription.Generic getType() {
591 if (TypeDescription.AbstractBase.RAW_TYPES) {
592 return TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(executable.getParameterTypes()[index]);
593 }
594 return new TypeDescription.Generic.LazyProjection.OfMethodParameter(executable, index, executable.getParameterTypes());
595 }
596
597 /**
598 * {@inheritDoc}
599 */
600 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
601 public AnnotationList getDeclaredAnnotations() {
602 return new AnnotationList.ForLoadedAnnotations(parameterAnnotationSource.getParameterAnnotations()[index]);
603 }
604 }
605
606 /**
607 * A description of a loaded {@link Constructor} parameter for a modern VM.
608 */
609 protected static class OfConstructor extends ForLoadedParameter<Constructor<?>> {
610
611 /**
612 * Creates a new description for a loaded constructor.
613 *
614 * @param constructor The constructor for which a parameter is represented.
615 * @param index The index of the parameter.
616 * @param parameterAnnotationSource The parameter annotation source to query.
617 */
618 protected OfConstructor(Constructor<?> constructor, int index, ParameterAnnotationSource parameterAnnotationSource) {
619 super(constructor, index, parameterAnnotationSource);
620 }
621
622 /**
623 * {@inheritDoc}
624 */
625 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
626 public MethodDescription.InDefinedShape getDeclaringMethod() {
627 return new MethodDescription.ForLoadedConstructor(executable);
628 }
629
630 /**
631 * {@inheritDoc}
632 */
633 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
634 public TypeDescription.Generic getType() {
635 if (TypeDescription.AbstractBase.RAW_TYPES) {
636 return TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(executable.getParameterTypes()[index]);
637 }
638 return new TypeDescription.Generic.LazyProjection.OfConstructorParameter(executable, index, executable.getParameterTypes());
639 }
640
641 /**
642 * {@inheritDoc}
643 */
644 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "The implicit field type casting is not understood by Findbugs")
645 public AnnotationList getDeclaredAnnotations() {
646 Annotation[][] annotation = parameterAnnotationSource.getParameterAnnotations();
647 MethodDescription.InDefinedShape declaringMethod = getDeclaringMethod();
648 if (annotation.length != declaringMethod.getParameters().size() && declaringMethod.getDeclaringType().isInnerClass()) {
649 return index == 0
650 ? new AnnotationList.Empty()
651 : new AnnotationList.ForLoadedAnnotations(annotation[index - 1]);
652 } else {
653 return new AnnotationList.ForLoadedAnnotations(annotation[index]);
654 }
655 }
656 }
657
658 /**
659 * Description of a loaded method's parameter on a virtual machine where {@code java.lang.reflect.Parameter}
660 * is not available.
661 */
662 protected static class OfLegacyVmMethod extends InDefinedShape.AbstractBase {
663
664 /**
665 * The method that declares this parameter.
666 */
667 private final Method method;
668
669 /**
670 * The index of this parameter.
671 */
672 private final int index;
673
674 /**
675 * The type erasures of the represented method.
676 */
677 private final Class<?>[] parameterType;
678
679 /**
680 * The parameter annotation source to query.
681 */
682 private final ParameterAnnotationSource parameterAnnotationSource;
683
684 /**
685 * Creates a legacy representation of a method's parameter.
686 *
687 * @param method The method that declares this parameter.
688 * @param index The index of this parameter.
689 * @param parameterType The type erasures of the represented method.
690 * @param parameterAnnotationSource The parameter annotation source to query.
691 */
692 protected OfLegacyVmMethod(Method method, int index, Class<?>[] parameterType, ParameterAnnotationSource parameterAnnotationSource) {
693 this.method = method;
694 this.index = index;
695 this.parameterType = parameterType;
696 this.parameterAnnotationSource = parameterAnnotationSource;
697 }
698
699 /**
700 * {@inheritDoc}
701 */
702 public TypeDescription.Generic getType() {
703 if (TypeDescription.AbstractBase.RAW_TYPES) {
704 return TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(parameterType[index]);
705 }
706 return new TypeDescription.Generic.LazyProjection.OfMethodParameter(method, index, parameterType);
707 }
708
709 /**
710 * {@inheritDoc}
711 */
712 public MethodDescription.InDefinedShape getDeclaringMethod() {
713 return new MethodDescription.ForLoadedMethod(method);
714 }
715
716 /**
717 * {@inheritDoc}
718 */
719 public int getIndex() {
720 return index;
721 }
722
723 /**
724 * {@inheritDoc}
725 */
726 public boolean isNamed() {
727 return false;
728 }
729
730 /**
731 * {@inheritDoc}
732 */
733 public boolean hasModifiers() {
734 return false;
735 }
736
737 /**
738 * {@inheritDoc}
739 */
740 public AnnotationList getDeclaredAnnotations() {
741 return new AnnotationList.ForLoadedAnnotations(parameterAnnotationSource.getParameterAnnotations()[index]);
742 }
743 }
744
745 /**
746 * Description of a loaded constructor's parameter on a virtual machine where {@code java.lang.reflect.Parameter}
747 * is not available.
748 */
749 protected static class OfLegacyVmConstructor extends InDefinedShape.AbstractBase {
750
751 /**
752 * The method that declares this parameter.
753 */
754 private final Constructor<?> constructor;
755
756 /**
757 * The index of this parameter.
758 */
759 private final int index;
760
761 /**
762 * The type erasures of the represented method.
763 */
764 private final Class<?>[] parameterType;
765
766 /**
767 * The parameter annotation source to query.
768 */
769 private final ParameterAnnotationSource parameterAnnotationSource;
770
771 /**
772 * Creates a legacy representation of a method's parameter.
773 *
774 * @param constructor The constructor that declares this parameter.
775 * @param index The index of this parameter.
776 * @param parameterType The type erasures of the represented method.
777 * @param parameterAnnotationSource The parameter annotation source to query.
778 */
779 protected OfLegacyVmConstructor(Constructor<?> constructor, int index, Class<?>[] parameterType, ParameterAnnotationSource parameterAnnotationSource) {
780 this.constructor = constructor;
781 this.index = index;
782 this.parameterType = parameterType;
783 this.parameterAnnotationSource = parameterAnnotationSource;
784 }
785
786 /**
787 * {@inheritDoc}
788 */
789 public TypeDescription.Generic getType() {
790 if (TypeDescription.AbstractBase.RAW_TYPES) {
791 return TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(parameterType[index]);
792 }
793 return new TypeDescription.Generic.LazyProjection.OfConstructorParameter(constructor, index, parameterType);
794 }
795
796 /**
797 * {@inheritDoc}
798 */
799 public MethodDescription.InDefinedShape getDeclaringMethod() {
800 return new MethodDescription.ForLoadedConstructor(constructor);
801 }
802
803 /**
804 * {@inheritDoc}
805 */
806 public int getIndex() {
807 return index;
808 }
809
810 /**
811 * {@inheritDoc}
812 */
813 public boolean isNamed() {
814 return false;
815 }
816
817 /**
818 * {@inheritDoc}
819 */
820 public boolean hasModifiers() {
821 return false;
822 }
823
824 /**
825 * {@inheritDoc}
826 */
827 public AnnotationList getDeclaredAnnotations() {
828 MethodDescription.InDefinedShape declaringMethod = getDeclaringMethod();
829 Annotation[][] parameterAnnotation = parameterAnnotationSource.getParameterAnnotations();
830 if (parameterAnnotation.length != declaringMethod.getParameters().size() && declaringMethod.getDeclaringType().isInnerClass()) {
831 return index == 0
832 ? new AnnotationList.Empty()
833 : new AnnotationList.ForLoadedAnnotations(parameterAnnotation[index - 1]);
834 } else {
835 return new AnnotationList.ForLoadedAnnotations(parameterAnnotation[index]);
836 }
837 }
838 }
839 }
840
841 /**
842 * A latent description of a parameter that is not attached to a method or constructor.
843 */
844 class Latent extends InDefinedShape.AbstractBase {
845
846 /**
847 * The method that is declaring the parameter.
848 */
849 private final MethodDescription.InDefinedShape declaringMethod;
850
851 /**
852 * The type of the parameter.
853 */
854 private final TypeDescription.Generic parameterType;
855
856 /**
857 * The annotations of the parameter.
858 */
859 private final List<? extends AnnotationDescription> declaredAnnotations;
860
861 /**
862 * The name of the parameter or {@code null} if no name is explicitly defined.
863 */
864 private final String name;
865
866 /**
867 * The modifiers of the parameter or {@code null} if no modifiers are explicitly defined.
868 */
869 private final Integer modifiers;
870
871 /**
872 * The index of the parameter.
873 */
874 private final int index;
875
876 /**
877 * The parameter's offset in the local method variables array.
878 */
879 private final int offset;
880
881 /**
882 * Creates a latent parameter description. All provided types are attached to this instance before they are returned.
883 *
884 * @param declaringMethod The method that is declaring the parameter.
885 * @param token The token describing the shape of the parameter.
886 * @param index The index of the parameter.
887 * @param offset The parameter's offset in the local method variables array.
888 */
889 public Latent(MethodDescription.InDefinedShape declaringMethod, Token token, int index, int offset) {
890 this(declaringMethod,
891 token.getType(),
892 token.getAnnotations(),
893 token.getName(),
894 token.getModifiers(),
895 index,
896 offset);
897 }
898
899 /**
900 * Creates a new latent parameter descriptions for a parameter without explicit meta data or annotations.
901 *
902 * @param declaringMethod The method declaring this parameter.
903 * @param parameterType The type of the parameter.
904 * @param index The index of the parameter.
905 * @param offset The offset of the parameter.
906 */
907 public Latent(MethodDescription.InDefinedShape declaringMethod,
908 TypeDescription.Generic parameterType,
909 int index,
910 int offset) {
911 this(declaringMethod,
912 parameterType,
913 Collections.<AnnotationDescription>emptyList(),
914 Token.NO_NAME,
915 Token.NO_MODIFIERS,
916 index,
917 offset);
918 }
919
920 /**
921 * Creates a latent parameter description. All provided types are attached to this instance before they are returned.
922 *
923 * @param declaringMethod The method that is declaring the parameter.
924 * @param parameterType The parameter's type.
925 * @param declaredAnnotations The annotations of the parameter.
926 * @param name The name of the parameter or {@code null} if no name is explicitly defined.
927 * @param modifiers The modifiers of the parameter or {@code null} if no modifiers are explicitly defined.
928 * @param index The index of the parameter.
929 * @param offset The parameter's offset in the local method variables array.
930 */
931 public Latent(MethodDescription.InDefinedShape declaringMethod,
932 TypeDescription.Generic parameterType,
933 List<? extends AnnotationDescription> declaredAnnotations,
934 String name,
935 Integer modifiers,
936 int index,
937 int offset) {
938 this.declaringMethod = declaringMethod;
939 this.parameterType = parameterType;
940 this.declaredAnnotations = declaredAnnotations;
941 this.name = name;
942 this.modifiers = modifiers;
943 this.index = index;
944 this.offset = offset;
945 }
946
947 /**
948 * {@inheritDoc}
949 */
950 public TypeDescription.Generic getType() {
951 return parameterType.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(this));
952 }
953
954 /**
955 * {@inheritDoc}
956 */
957 public MethodDescription.InDefinedShape getDeclaringMethod() {
958 return declaringMethod;
959 }
960
961 /**
962 * {@inheritDoc}
963 */
964 public int getIndex() {
965 return index;
966 }
967
968 /**
969 * {@inheritDoc}
970 */
971 public int getOffset() {
972 return offset;
973 }
974
975 /**
976 * {@inheritDoc}
977 */
978 public boolean isNamed() {
979 return name != null;
980 }
981
982 /**
983 * {@inheritDoc}
984 */
985 public boolean hasModifiers() {
986 return modifiers != null;
987 }
988
989 /**
990 * {@inheritDoc}
991 */
992 public String getName() {
993 return isNamed()
994 ? name
995 : super.getName();
996 }
997
998 /**
999 * {@inheritDoc}
1000 */
1001 public int getModifiers() {
1002 return hasModifiers()
1003 ? modifiers
1004 : super.getModifiers();
1005 }
1006
1007 /**
1008 * {@inheritDoc}
1009 */
1010 public AnnotationList getDeclaredAnnotations() {
1011 return new AnnotationList.Explicit(declaredAnnotations);
1012 }
1013 }
1014
1015 /**
1016 * <p>
1017 * A parameter description that represents a given parameter but with a substituted parameter type.
1018 * </p>
1019 * <p>
1020 * <b>Note</b>: The supplied visitor must assure to not substitute
1021 * </p>
1022 */
1023 class TypeSubstituting extends AbstractBase implements InGenericShape {
1024
1025 /**
1026 * The method that declares this type-substituted parameter.
1027 */
1028 private final MethodDescription.InGenericShape declaringMethod;
1029
1030 /**
1031 * The represented parameter.
1032 */
1033 private final ParameterDescription parameterDescription;
1034
1035 /**
1036 * A visitor that is applied to the parameter type.
1037 */
1038 private final TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor;
1039
1040 /**
1041 * Creates a new type substituting parameter.
1042 *
1043 * @param declaringMethod The method that declares this type-substituted parameter.
1044 * @param parameterDescription The represented parameter.
1045 * @param visitor A visitor that is applied to the parameter type.
1046 */
1047 public TypeSubstituting(MethodDescription.InGenericShape declaringMethod,
1048 ParameterDescription parameterDescription,
1049 TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
1050 this.declaringMethod = declaringMethod;
1051 this.parameterDescription = parameterDescription;
1052 this.visitor = visitor;
1053 }
1054
1055 /**
1056 * {@inheritDoc}
1057 */
1058 public TypeDescription.Generic getType() {
1059 return parameterDescription.getType().accept(visitor);
1060 }
1061
1062 /**
1063 * {@inheritDoc}
1064 */
1065 public MethodDescription.InGenericShape getDeclaringMethod() {
1066 return declaringMethod;
1067 }
1068
1069 /**
1070 * {@inheritDoc}
1071 */
1072 public int getIndex() {
1073 return parameterDescription.getIndex();
1074 }
1075
1076 /**
1077 * {@inheritDoc}
1078 */
1079 public boolean isNamed() {
1080 return parameterDescription.isNamed();
1081 }
1082
1083 /**
1084 * {@inheritDoc}
1085 */
1086 public boolean hasModifiers() {
1087 return parameterDescription.hasModifiers();
1088 }
1089
1090 /**
1091 * {@inheritDoc}
1092 */
1093 public int getOffset() {
1094 return parameterDescription.getOffset();
1095 }
1096
1097 /**
1098 * {@inheritDoc}
1099 */
1100 public String getName() {
1101 return parameterDescription.getName();
1102 }
1103
1104 /**
1105 * {@inheritDoc}
1106 */
1107 public int getModifiers() {
1108 return parameterDescription.getModifiers();
1109 }
1110
1111 /**
1112 * {@inheritDoc}
1113 */
1114 public AnnotationList getDeclaredAnnotations() {
1115 return parameterDescription.getDeclaredAnnotations();
1116 }
1117
1118 /**
1119 * {@inheritDoc}
1120 */
1121 public InDefinedShape asDefined() {
1122 return parameterDescription.asDefined();
1123 }
1124 }
1125
1126 /**
1127 * A token representing a parameter's properties detached from a type.
1128 */
1129 class Token implements ByteCodeElement.Token<Token> {
1130
1131 /**
1132 * Indicator for a method parameter without an explicit name.
1133 */
1134 public static final String NO_NAME = null;
1135
1136 /**
1137 * Indicator for a method parameter without explicit modifiers.
1138 */
1139 public static final Integer NO_MODIFIERS = null;
1140
1141 /**
1142 * The type of the represented parameter.
1143 */
1144 private final TypeDescription.Generic type;
1145
1146 /**
1147 * A list of parameter annotations.
1148 */
1149 private final List<? extends AnnotationDescription> annotations;
1150
1151 /**
1152 * The name of the parameter or {@code null} if no explicit name is defined.
1153 */
1154 private final String name;
1155
1156 /**
1157 * The modifiers of the parameter or {@code null} if no explicit modifiers is defined.
1158 */
1159 private final Integer modifiers;
1160
1161 /**
1162 * Creates a new parameter token without an explicit name, an explicit modifier or annotations.
1163 * The parameter type must be represented in its detached format.
1164 *
1165 * @param type The type of the represented parameter.
1166 */
1167 public Token(TypeDescription.Generic type) {
1168 this(type, Collections.<AnnotationDescription>emptyList());
1169 }
1170
1171 /**
1172 * Creates a new parameter token without an explicit name or an explicit modifier. The parameter type must be represented in its detached format.
1173 *
1174 * @param type The type of the represented parameter.
1175 * @param annotations The annotations of the parameter.
1176 */
1177 public Token(TypeDescription.Generic type, List<? extends AnnotationDescription> annotations) {
1178 this(type, annotations, NO_NAME, NO_MODIFIERS);
1179 }
1180
1181 /**
1182 * Creates a parameter token without annotations. The parameter type must be represented in its detached format.
1183 *
1184 * @param type The type of the represented parameter.
1185 * @param name The name of the parameter or {@code null} if no explicit name is defined.
1186 * @param modifiers The modifiers of the parameter or {@code null} if no explicit modifiers is defined.
1187 */
1188 public Token(TypeDescription.Generic type, String name, Integer modifiers) {
1189 this(type, Collections.<AnnotationDescription>emptyList(), name, modifiers);
1190 }
1191
1192 /**
1193 * Creates a new parameter token. The parameter type must be represented in its detached format.
1194 *
1195 * @param type The type of the represented parameter.
1196 * @param annotations The annotations of the parameter.
1197 * @param name The name of the parameter or {@code null} if no explicit name is defined.
1198 * @param modifiers The modifiers of the parameter or {@code null} if no explicit modifiers is defined.
1199 */
1200 public Token(TypeDescription.Generic type,
1201 List<? extends AnnotationDescription> annotations,
1202 String name,
1203 Integer modifiers) {
1204 this.type = type;
1205 this.annotations = annotations;
1206 this.name = name;
1207 this.modifiers = modifiers;
1208 }
1209
1210 /**
1211 * Returns the type of the represented method parameter.
1212 *
1213 * @return The type of the represented method parameter.
1214 */
1215 public TypeDescription.Generic getType() {
1216 return type;
1217 }
1218
1219 /**
1220 * Returns the annotations of the represented method parameter.
1221 *
1222 * @return The annotations of the represented method parameter.
1223 */
1224 public AnnotationList getAnnotations() {
1225 return new AnnotationList.Explicit(annotations);
1226 }
1227
1228 /**
1229 * Returns the name of the represented method parameter.
1230 *
1231 * @return The name of the parameter or {@code null} if no explicit name is defined.
1232 */
1233 public String getName() {
1234 return name;
1235 }
1236
1237 /**
1238 * Returns the modifiers of the represented method parameter.
1239 *
1240 * @return The modifiers of the parameter or {@code null} if no explicit modifiers is defined.
1241 */
1242 public Integer getModifiers() {
1243 return modifiers;
1244 }
1245
1246 /**
1247 * {@inheritDoc}
1248 */
1249 public Token accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
1250 return new Token(type.accept(visitor),
1251 annotations,
1252 name,
1253 modifiers);
1254 }
1255
1256 @Override
1257 @CachedReturnPlugin.Enhance
1258 public int hashCode() {
1259 int result = type.hashCode();
1260 result = 31 * result + annotations.hashCode();
1261 result = 31 * result + (name != null ? name.hashCode() : 0);
1262 result = 31 * result + (modifiers != null ? modifiers.hashCode() : 0);
1263 return result;
1264 }
1265
1266 @Override
1267 public boolean equals(Object other) {
1268 if (this == other) {
1269 return true;
1270 } else if (!(other instanceof Token)) {
1271 return false;
1272 }
1273 Token token = (Token) other;
1274 return type.equals(token.type)
1275 && annotations.equals(token.annotations)
1276 && (name != null ? name.equals(token.name) : token.name == null)
1277 && (modifiers != null ? modifiers.equals(token.modifiers) : token.modifiers == null);
1278 }
1279
1280 @Override
1281 public String toString() {
1282 return "ParameterDescription.Token{" +
1283 "type=" + type +
1284 ", annotations=" + annotations +
1285 ", name='" + name + '\'' +
1286 ", modifiers=" + modifiers +
1287 '}';
1288 }
1289
1290 /**
1291 * A list of types represented as a list of parameter tokens.
1292 */
1293 public static class TypeList extends AbstractList<Token> {
1294
1295 /**
1296 * The list of types to represent as parameter tokens.
1297 */
1298 private final List<? extends TypeDefinition> typeDescriptions;
1299
1300 /**
1301 * Creates a new list of types that represent parameters.
1302 *
1303 * @param typeDescriptions The types to represent.
1304 */
1305 public TypeList(List<? extends TypeDefinition> typeDescriptions) {
1306 this.typeDescriptions = typeDescriptions;
1307 }
1308
1309 /**
1310 * {@inheritDoc}
1311 */
1312 public Token get(int index) {
1313 return new Token(typeDescriptions.get(index).asGenericType());
1314 }
1315
1316 /**
1317 * {@inheritDoc}
1318 */
1319 public int size() {
1320 return typeDescriptions.size();
1321 }
1322 }
1323 }
1324 }
1325