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

mockit.internal.classGeneration.BaseSubclassGenerator Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
/*
 * Copyright (c) 2006 Rogério Liesenfeld
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.classGeneration;

import java.lang.reflect.*;
import java.util.*;
import javax.annotation.*;
import static java.util.Arrays.*;

import mockit.external.asm.*;
import mockit.external.asm.JavaType.Sort;
import mockit.internal.*;
import mockit.internal.util.*;
import static mockit.external.asm.Opcodes.*;

public class BaseSubclassGenerator extends BaseClassModifier
{
   private static final int CLASS_ACCESS_MASK = 0xFFFF - Access.ABSTRACT;
   private static final int CONSTRUCTOR_ACCESS_MASK = Access.PUBLIC + Access.PROTECTED;

   // Fixed initial state:
   @Nonnull Class baseClass;
   @Nonnull private final String subclassName;
   @Nullable protected final MockedTypeInfo mockedTypeInfo;
   private final boolean copyConstructors;

   // Helper fields for mutable state:
   @Nonnull private final List implementedMethods;
   @Nullable private String superClassOfSuperClass;
   private Set superInterfaces;

   protected BaseSubclassGenerator(
      @Nonnull Class baseClass, @Nonnull ClassReader classReader,
      @Nullable Type genericMockedType, @Nonnull String subclassName,
      boolean copyConstructors)
   {
      super(classReader);
      this.baseClass = baseClass;
      this.subclassName = subclassName.replace('.', '/');
      mockedTypeInfo = genericMockedType == null ? null : new MockedTypeInfo(genericMockedType);
      this.copyConstructors = copyConstructors;
      implementedMethods = new ArrayList();
   }

   @Override
   public void visit(
      int version, int access, @Nonnull String name, @Nullable String signature, @Nullable String superName,
      @Nullable String[] interfaces)
   {
      int subclassAccess = access & CLASS_ACCESS_MASK | Access.FINAL;
      String subclassSignature = mockedTypeInfo == null ? signature : mockedTypeInfo.implementationSignature;

      super.visit(version, subclassAccess, subclassName, subclassSignature, name, null);

      superClassOfSuperClass = superName;
      superInterfaces = new HashSet();

      if (interfaces != null && interfaces.length > 0) {
         superInterfaces.addAll(asList(interfaces));
      }
   }

   @Override
   public final void visitInnerClass(
      @Nonnull String name, @Nullable String outerName, @Nullable String innerName, int access) {}

   @Override
   public final void visitOuterClass(@Nonnull String owner, @Nullable String name, @Nullable String desc) {}

   @Override
   public final void visitSource(@Nullable String source, @Nullable String debug) {}

   @Override @Nullable
   public final FieldVisitor visitField(
      int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable Object value)
   { return null; }

   @Override @Nullable
   public MethodVisitor visitMethod(
      int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable String[] exceptions)
   {
      if (copyConstructors && "".equals(name)) {
         if ((access & CONSTRUCTOR_ACCESS_MASK) != 0) {
            generateConstructorDelegatingToSuper(desc, signature, exceptions);
         }
      }
      else {
         // Inherits from super-class when non-abstract; otherwise, creates implementation for abstract method.
         generateImplementationIfAbstractMethod(superClassName, access, name, desc, signature, exceptions);
      }

      return null;
   }

   private void generateConstructorDelegatingToSuper(
      @Nonnull String desc, @Nullable String signature, @Nullable String[] exceptions)
   {
      mw = cw.visitMethod(Access.PUBLIC, "", desc, signature, exceptions);
      mw.visitVarInsn(ALOAD, 0);
      int varIndex = 1;

      for (JavaType paramType : JavaType.getArgumentTypes(desc)) {
         int loadOpcode = getLoadOpcodeForParameterType(paramType.getSort());
         mw.visitVarInsn(loadOpcode, varIndex);
         varIndex++;
      }

      mw.visitMethodInsn(INVOKESPECIAL, superClassName, "", desc, false);
      generateEmptyImplementation();
   }

   private static int getLoadOpcodeForParameterType(int paramType)
   {
      if (paramType <= Sort.INT) {
         return ILOAD;
      }

      switch (paramType) {
         case Sort.FLOAT:  return FLOAD;
         case Sort.LONG:   return LLOAD;
         case Sort.DOUBLE: return DLOAD;
         default: return ALOAD;
      }
   }

   private void generateImplementationIfAbstractMethod(
      String className, int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature,
      @Nullable String[] exceptions)
   {
      if (!"".equals(name)) {
         String methodNameAndDesc = name + desc;

         if (!implementedMethods.contains(methodNameAndDesc)) {
            if ((access & Access.ABSTRACT) != 0) {
               generateMethodImplementation(className, access, name, desc, signature, exceptions);
            }

            implementedMethods.add(methodNameAndDesc);
         }
      }
   }

   protected void generateMethodImplementation(
      String className, int access, @Nonnull String name, @Nonnull String desc,
      @Nullable String signature, @Nullable String[] exceptions)
   {
   }

   @Override
   public void visitEnd()
   {
      generateImplementationsForInheritedAbstractMethods(superClassOfSuperClass);

      while (!superInterfaces.isEmpty()) {
         String superInterface = superInterfaces.iterator().next();
         generateImplementationsForInterfaceMethods(superInterface);
         superInterfaces.remove(superInterface);
      }
   }

   private void generateImplementationsForInheritedAbstractMethods(@Nullable String superName)
   {
      if (superName != null && !"java/lang/Object".equals(superName)) {
         new MethodModifierForSuperclass(superName);
      }
   }

   private void generateImplementationsForInterfaceMethods(String superName)
   {
      if (!"java/lang/Object".equals(superName)) {
         new MethodModifierForImplementedInterface(superName);
      }
   }

   private static class BaseMethodModifier extends ClassVisitor
   {
      @Nonnull final String typeName;

      BaseMethodModifier(@Nonnull String typeName)
      {
         this.typeName = typeName;
         ClassFile.visitClass(typeName, this);
      }

      @Nullable @Override
      public final FieldVisitor visitField(
         int access, @Nonnull String name, @Nonnull String desc, String signature, Object value) { return null; }
   }

   private final class MethodModifierForSuperclass extends BaseMethodModifier
   {
      String superClassName;

      MethodModifierForSuperclass(@Nonnull String className) { super(className); }

      @Override
      public void visit(
         int version, int access, @Nonnull String name, @Nullable String signature, @Nullable String superName,
         @Nullable String[] interfaces)
      {
         superClassName = superName;

         if (interfaces != null) {
            superInterfaces.addAll(asList(interfaces));
         }
      }

      @Nullable @Override
      public MethodVisitor visitMethod(
         int access, @Nonnull String name, @Nonnull String desc, String signature, String[] exceptions)
      {
         generateImplementationIfAbstractMethod(typeName, access, name, desc, signature, exceptions);
         return null;
      }

      @Override
      public void visitEnd()
      {
         generateImplementationsForInheritedAbstractMethods(superClassName);
      }
   }

   private final class MethodModifierForImplementedInterface extends BaseMethodModifier
   {
      MethodModifierForImplementedInterface(String interfaceName) { super(interfaceName); }

      @Override
      public void visit(
         int version, int access, @Nonnull String name, @Nullable String signature, @Nullable String superName,
         @Nullable String[] interfaces)
      {
         assert interfaces != null;
         superInterfaces.addAll(asList(interfaces));
      }

      @Nullable @Override
      public MethodVisitor visitMethod(
         int access, @Nonnull String name, @Nonnull String desc, String signature, String[] exceptions)
      {
         generateImplementationForInterfaceMethodIfMissing(access, name, desc, signature, exceptions);
         return null;
      }

      private void generateImplementationForInterfaceMethodIfMissing(
         int access, @Nonnull String name, @Nonnull String desc, String signature, String[] exceptions)
      {
         String methodNameAndDesc = name + desc;

         if (!implementedMethods.contains(methodNameAndDesc)) {
            if (!hasMethodImplementation(name, desc)) {
               generateMethodImplementation(typeName, access, name, desc, signature, exceptions);
            }

            implementedMethods.add(methodNameAndDesc);
         }
      }

      private boolean hasMethodImplementation(@Nonnull String name, @Nonnull String desc)
      {
         Class[] paramTypes = TypeDescriptor.getParameterTypes(desc);

         try {
            Method method = baseClass.getMethod(name, paramTypes);
            return !method.getDeclaringClass().isInterface();
         }
         catch (NoSuchMethodException ignore) {
            return false;
         }
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy