private void testLegalStrictBinding(boolean dynamicallyTyped, Annotation... targetAnnotation)
     throws Exception {
   when(annotation.value()).thenReturn(AllArguments.Assignment.STRICT);
   when(stackManipulation.isValid()).thenReturn(true);
   when(source.getParameters())
       .thenReturn(new ParameterList.Explicit.ForTypes(source, firstSourceType, secondSourceType));
   when(source.isStatic()).thenReturn(false);
   when(targetType.isArray()).thenReturn(true);
   when(targetType.getComponentType()).thenReturn(componentType);
   when(componentType.getStackSize()).thenReturn(StackSize.SINGLE);
   when(target.getType()).thenReturn(targetType);
   when(target.getDeclaredAnnotations())
       .thenReturn(new AnnotationList.ForLoadedAnnotations(targetAnnotation));
   MethodDelegationBinder.ParameterBinding<?> parameterBinding =
       AllArguments.Binder.INSTANCE.bind(
           annotationDescription, source, target, implementationTarget, assigner);
   assertThat(parameterBinding.isValid(), is(true));
   verify(source, atLeast(1)).getParameters();
   verify(source, atLeast(1)).isStatic();
   verify(target, atLeast(1)).getType();
   verify(target, atLeast(1)).getDeclaredAnnotations();
   verify(assigner).assign(firstSourceType, componentType, Assigner.Typing.of(dynamicallyTyped));
   verify(assigner).assign(secondSourceType, componentType, Assigner.Typing.of(dynamicallyTyped));
   verifyNoMoreInteractions(assigner);
 }
 @Test
 public void testLegalSlackBinding() throws Exception {
   when(target.getIndex()).thenReturn(1);
   when(annotation.value()).thenReturn(AllArguments.Assignment.SLACK);
   when(stackManipulation.isValid()).thenReturn(false);
   when(source.getParameters())
       .thenReturn(new ParameterList.Explicit.ForTypes(source, firstSourceType, secondSourceType));
   when(source.isStatic()).thenReturn(false);
   when(targetType.isArray()).thenReturn(true);
   when(targetType.getComponentType()).thenReturn(componentType);
   when(componentType.getStackSize()).thenReturn(StackSize.SINGLE);
   when(target.getType()).thenReturn(targetType);
   when(target.getDeclaredAnnotations()).thenReturn(new AnnotationList.Empty());
   when(rawComponentType.getInternalName()).thenReturn(FOO);
   MethodDelegationBinder.ParameterBinding<?> parameterBinding =
       AllArguments.Binder.INSTANCE.bind(
           annotationDescription, source, target, implementationTarget, assigner);
   MethodVisitor methodVisitor = mock(MethodVisitor.class);
   Implementation.Context implementationContext = mock(Implementation.Context.class);
   StackManipulation.Size size = parameterBinding.apply(methodVisitor, implementationContext);
   assertThat(size.getSizeImpact(), is(1));
   assertThat(size.getMaximalSize(), is(1));
   verify(methodVisitor).visitInsn(Opcodes.ICONST_0);
   verify(methodVisitor).visitTypeInsn(Opcodes.ANEWARRAY, FOO);
   verifyNoMoreInteractions(methodVisitor);
   verifyZeroInteractions(implementationContext);
   assertThat(parameterBinding.isValid(), is(true));
   verify(source, atLeast(1)).getParameters();
   verify(source, atLeast(1)).isStatic();
   verify(target, atLeast(1)).getType();
   verify(target, atLeast(1)).getDeclaredAnnotations();
   verify(assigner).assign(firstSourceType, componentType, Assigner.Typing.STATIC);
   verify(assigner).assign(secondSourceType, componentType, Assigner.Typing.STATIC);
   verifyNoMoreInteractions(assigner);
 }
 @Override
 public AnnotationAppender onWildcard(TypeDescription.Generic wildcard) {
   TypeList.Generic lowerBounds = wildcard.getLowerBounds();
   return (lowerBounds.isEmpty() ? wildcard.getUpperBounds().getOnly() : lowerBounds.getOnly())
       .accept(
           new ForTypeAnnotations(
               apply(wildcard, typePath),
               annotationValueFilter,
               typeReference,
               typePath + WILDCARD_TYPE_PATH));
 }
 @Test(expected = IllegalStateException.class)
 public void testNonArrayTypeBinding() throws Exception {
   when(target.getIndex()).thenReturn(0);
   TypeDescription.Generic targetType = mock(TypeDescription.Generic.class);
   TypeDescription rawTargetType = mock(TypeDescription.class);
   when(targetType.asErasure()).thenReturn(rawTargetType);
   when(targetType.isArray()).thenReturn(false);
   when(target.getType()).thenReturn(targetType);
   AllArguments.Binder.INSTANCE.bind(
       annotationDescription, source, target, implementationTarget, assigner);
 }
 @Override
 public void apply(
     MethodVisitor methodVisitor,
     MethodDescription methodDescription,
     AnnotationValueFilter annotationValueFilter) {
   AnnotationAppender annotationAppender =
       new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor));
   annotationAppender =
       methodDescription
           .getReturnType()
           .accept(
               AnnotationAppender.ForTypeAnnotations.ofMethodReturnType(
                   annotationAppender, annotationValueFilter));
   annotationAppender =
       AnnotationAppender.ForTypeAnnotations.ofTypeVariable(
           annotationAppender,
           annotationValueFilter,
           AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_INVOKEABLE,
           methodDescription.getTypeVariables());
   for (AnnotationDescription annotation : methodDescription.getDeclaredAnnotations()) {
     annotationAppender = annotationAppender.append(annotation, annotationValueFilter);
   }
   for (ParameterDescription parameterDescription : methodDescription.getParameters()) {
     AnnotationAppender parameterAppender =
         new AnnotationAppender.Default(
             new AnnotationAppender.Target.OnMethodParameter(
                 methodVisitor, parameterDescription.getIndex()));
     parameterAppender =
         parameterDescription
             .getType()
             .accept(
                 AnnotationAppender.ForTypeAnnotations.ofMethodParameterType(
                     parameterAppender, annotationValueFilter, parameterDescription.getIndex()));
     for (AnnotationDescription annotation : parameterDescription.getDeclaredAnnotations()) {
       parameterAppender = parameterAppender.append(annotation, annotationValueFilter);
     }
   }
   int exceptionTypeIndex = 0;
   for (TypeDescription.Generic exceptionType : methodDescription.getExceptionTypes()) {
     annotationAppender =
         exceptionType.accept(
             AnnotationAppender.ForTypeAnnotations.ofExceptionType(
                 annotationAppender, annotationValueFilter, exceptionTypeIndex++));
   }
 }
 @Override
 public AnnotationAppender onParameterizedType(TypeDescription.Generic parameterizedType) {
   StringBuilder typePath = new StringBuilder(this.typePath);
   for (int index = 0; index < parameterizedType.asErasure().getSegmentCount(); index++) {
     typePath = typePath.append(INNER_CLASS_PATH);
   }
   AnnotationAppender annotationAppender = apply(parameterizedType, typePath.toString());
   TypeDescription.Generic ownerType = parameterizedType.getOwnerType();
   if (ownerType != null) {
     annotationAppender =
         ownerType.accept(
             new ForTypeAnnotations(
                 annotationAppender, annotationValueFilter, typeReference, this.typePath));
   }
   int index = 0;
   for (TypeDescription.Generic typeArgument : parameterizedType.getTypeArguments()) {
     annotationAppender =
         typeArgument.accept(
             new ForTypeAnnotations(
                 annotationAppender,
                 annotationValueFilter,
                 typeReference,
                 typePath.toString() + index++ + INDEXED_TYPE_DELIMITER));
   }
   return annotationAppender;
 }
 /**
  * Writes all annotations of the supplied type to this instance's annotation appender.
  *
  * @param typeDescription The type of what all annotations should be written of.
  * @param typePath The type path to use.
  * @return The resulting annotation appender.
  */
 private AnnotationAppender apply(TypeDescription.Generic typeDescription, String typePath) {
   AnnotationAppender annotationAppender = this.annotationAppender;
   for (AnnotationDescription annotationDescription : typeDescription.getDeclaredAnnotations()) {
     annotationAppender =
         annotationAppender.append(
             annotationDescription, annotationValueFilter, typeReference, typePath);
   }
   return annotationAppender;
 }
 @Override
 public AnnotationAppender onNonGenericType(TypeDescription.Generic typeDescription) {
   StringBuilder typePath = new StringBuilder(this.typePath);
   for (int index = 0; index < typeDescription.asErasure().getSegmentCount(); index++) {
     typePath = typePath.append(INNER_CLASS_PATH);
   }
   AnnotationAppender annotationAppender = apply(typeDescription, typePath.toString());
   if (typeDescription.isArray()) {
     annotationAppender =
         typeDescription
             .getComponentType()
             .accept(
                 new ForTypeAnnotations(
                     annotationAppender,
                     annotationValueFilter,
                     typeReference,
                     this.typePath + COMPONENT_TYPE_PATH)); // Impossible to be inner class
   }
   return annotationAppender;
 }
 @Override
 public AnnotationAppender onGenericArray(TypeDescription.Generic genericArray) {
   return genericArray
       .getComponentType()
       .accept(
           new ForTypeAnnotations(
               apply(genericArray, typePath),
               annotationValueFilter,
               typeReference,
               typePath + COMPONENT_TYPE_PATH));
 }
 @Override
 @Before
 @SuppressWarnings("unchecked")
 public void setUp() throws Exception {
   super.setUp();
   when(firstSourceType.getStackSize()).thenReturn(StackSize.SINGLE);
   when(secondSourceType.getStackSize()).thenReturn(StackSize.SINGLE);
   when(componentType.asErasure()).thenReturn(rawComponentType);
   when(targetType.getComponentType()).thenReturn(componentType);
   when(targetType.asErasure()).thenReturn(rawTargetType);
   when(firstSourceType.asGenericType()).thenReturn(firstSourceType);
   when(firstSourceType.accept(any(TypeDescription.Generic.Visitor.class)))
       .thenReturn(firstSourceType);
   when(secondSourceType.asGenericType()).thenReturn(secondSourceType);
   when(secondSourceType.accept(any(TypeDescription.Generic.Visitor.class)))
       .thenReturn(secondSourceType);
 }
 /**
  * Appends all supplied type variables to the supplied method appender.
  *
  * @param annotationAppender The annotation appender to write any type annotation to.
  * @param annotationValueFilter The annotation value filter to apply.
  * @param variableOnType {@code true} if the type variables are declared by a type, {@code
  *     false} if they are declared by a method.
  * @param subListIndex The index of the first type variable to append. All previous type
  *     variables are ignored.
  * @param typeVariables The type variables to append.
  * @return The resulting annotation appender.
  */
 public static AnnotationAppender ofTypeVariable(
     AnnotationAppender annotationAppender,
     AnnotationValueFilter annotationValueFilter,
     boolean variableOnType,
     int subListIndex,
     List<? extends TypeDescription.Generic> typeVariables) {
   int typeVariableIndex = subListIndex, variableBaseReference, variableBoundBaseBase;
   if (variableOnType) {
     variableBaseReference = TypeReference.CLASS_TYPE_PARAMETER;
     variableBoundBaseBase = TypeReference.CLASS_TYPE_PARAMETER_BOUND;
   } else {
     variableBaseReference = TypeReference.METHOD_TYPE_PARAMETER;
     variableBoundBaseBase = TypeReference.METHOD_TYPE_PARAMETER_BOUND;
   }
   for (TypeDescription.Generic typeVariable :
       typeVariables.subList(subListIndex, typeVariables.size())) {
     int typeReference =
         TypeReference.newTypeParameterReference(variableBaseReference, typeVariableIndex)
             .getValue();
     for (AnnotationDescription annotationDescription : typeVariable.getDeclaredAnnotations()) {
       annotationAppender =
           annotationAppender.append(
               annotationDescription, annotationValueFilter, typeReference, EMPTY_TYPE_PATH);
     }
     int boundIndex =
         !typeVariable.getUpperBounds().get(0).getSort().isTypeVariable()
                 && typeVariable.getUpperBounds().get(0).asErasure().isInterface()
             ? 1
             : 0;
     for (TypeDescription.Generic typeBound : typeVariable.getUpperBounds()) {
       annotationAppender =
           typeBound.accept(
               new ForTypeAnnotations(
                   annotationAppender,
                   annotationValueFilter,
                   TypeReference.newTypeParameterBoundReference(
                       variableBoundBaseBase, typeVariableIndex, boundIndex++)));
     }
     typeVariableIndex++;
   }
   return annotationAppender;
 }