All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.annotations.AnnotatedMockMethodCollector Maven / Gradle / Ivy

/*
 * JMockit Annotations
 * Copyright (c) 2006-2010 Rogério Liesenfeld
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package mockit.internal.annotations;

import java.lang.reflect.*;

import mockit.*;
import mockit.external.asm.*;
import mockit.external.asm.commons.*;
import mockit.internal.*;

import static mockit.external.asm.Opcodes.*;

/**
 * Responsible for collecting the signatures of all methods and constructors defined in a given mock
 * class which are explicitly annotated as {@link mockit.Mock mocks}.
 */
public final class AnnotatedMockMethodCollector extends EmptyVisitor
{
   private static final int INVALID_FIELD_ACCESSES = ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC;
   private static final int INVALID_METHOD_ACCESSES = ACC_BRIDGE + ACC_SYNTHETIC + ACC_ABSTRACT + ACC_NATIVE;

   private final AnnotatedMockMethods mockMethods;
   private boolean collectingFromSuperClass;
   private String enclosingClassDescriptor;

   public AnnotatedMockMethodCollector(AnnotatedMockMethods mockMethods)
   {
      this.mockMethods = mockMethods;
   }

   public void collectMockMethods(Class mockClass)
   {
      Class classToCollectMocksFrom = mockClass;

      do {
         ClassReader mcReader = ClassFile.createClassFileReader(classToCollectMocksFrom.getName());
         mcReader.accept(this, true);
         classToCollectMocksFrom = classToCollectMocksFrom.getSuperclass();
         collectingFromSuperClass = true;
      }
      while (classToCollectMocksFrom != Object.class);
   }

   @Override
   public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
   {
      if (!collectingFromSuperClass) {
         mockMethods.setMockClassInternalName(name);

         int p = name.lastIndexOf('$');

         if (p > 0) {
            enclosingClassDescriptor = "(L" + name.substring(0, p) + ";)V";
         }
      }
   }

   @Override
   public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
   {
      if ((access & INVALID_FIELD_ACCESSES) == 0 && "it".equals(name)) {
         mockMethods.setWithItField(true);
      }

      return null;
   }

   /**
    * Adds the method or constructor specified to the set of mock methods, representing it as
    * name + desc, as long as it's appropriate for such method or constructor to be a
    * mock, as indicated by its access modifiers and by the presence of the {@link Mock} annotation.
    *
    * @param access access modifiers, indicating "public", "static", and so on
    * @param name the method or constructor name
    * @param methodDesc internal JVM description of parameters and return type
    * @param signature generic signature for a Java 5 generic method, ignored since redefinition
    * only needs to consider the "erased" signature
    * @param exceptions zero or more thrown exceptions in the method "throws" clause, also ignored
    */
   @Override
   public MethodVisitor visitMethod(
      final int access, final String name, final String methodDesc, String signature, String[] exceptions)
   {
      if ((access & INVALID_METHOD_ACCESSES) != 0) {
         return null;
      }

      if (
         !collectingFromSuperClass && enclosingClassDescriptor != null &&
         "".equals(name) && methodDesc.equals(enclosingClassDescriptor)
      ) {
         mockMethods.setInnerMockClass(true);
         enclosingClassDescriptor = null;
      }

      return new EmptyVisitor()
      {
         @Override
         public AnnotationVisitor visitAnnotation(String desc, boolean visible)
         {
            if ("Lmockit/Mock;".equals(desc)) {
               String nameAndDesc =
                  mockMethods.addMethod(collectingFromSuperClass, name, methodDesc, Modifier.isStatic(access));

               if (nameAndDesc != null) {
                  return new MockAnnotationVisitor(nameAndDesc);
               }
            }

            return this;
         }
      };
   }

   private final class MockAnnotationVisitor extends EmptyVisitor
   {
      private final String mockNameAndDesc;
      private MockState mockState;

      private MockAnnotationVisitor(String mockNameAndDesc)
      {
         this.mockNameAndDesc = mockNameAndDesc;
      }

      @Override
      public void visit(String name, Object value)
      {
         if ("invocations".equals(name)) {
            getMockState().expectedInvocations = (Integer) value;
         }
         else if ("minInvocations".equals(name)) {
            getMockState().minExpectedInvocations = (Integer) value;
         }
         else if ("maxInvocations".equals(name)) {
            getMockState().maxExpectedInvocations = (Integer) value;
         }
         else {
            boolean reentrant = (Boolean) value;

            if (reentrant) {
               getMockState().makeReentrant();
            }
         }
      }

      private MockState getMockState()
      {
         if (mockState == null) {
            mockState = new MockState(mockMethods.realClass, mockNameAndDesc);
         }

         return mockState;
      }

      @Override
      public void visitEnd()
      {
         if (mockState != null) {
            mockMethods.addMockState(mockState);
         }
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy