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

mockit.asm.JavaType Maven / Gradle / Ivy

package mockit.asm;

import java.lang.reflect.*;
import javax.annotation.*;

/**
 * A Java field or method type. This class can be used to make it easier to manipulate type and method descriptors.
 */
@SuppressWarnings("ClassReferencesSubclass")
public abstract class JavaType
{
   private static final JavaType[] NO_ARGS = {};

   /**
    * The length of the internal name of this Java type.
    */
   @Nonnegative final int len;

   /**
    * Constructs a Java type.
    *
    * @param len the length of this descriptor.
    */
   JavaType(@Nonnegative int len) { this.len = len; }

   /**
    * Returns the Java type corresponding to the given type descriptor.
    *
    * @param typeDescriptor a field or method type descriptor.
    */
   @Nonnull
   public static JavaType getType(@Nonnull String typeDescriptor) {
      return getType(typeDescriptor.toCharArray(), 0);
   }

   /**
    * Returns the Java types corresponding to the argument types of the given method descriptor.
    */
   @Nonnull
   public static JavaType[] getArgumentTypes(@Nonnull String methodDescriptor) {
      char[] buf = methodDescriptor.toCharArray();
      int off = 1;
      int size = 0;

      while (true) {
         char c = buf[off++];

         if (c == ')') {
            break;
         }
         else if (c == 'L') {
            off = findNextTypeTerminatorCharacter(buf, off);
            size++;
         }
         else if (c != '[') {
            size++;
         }
      }

      return getArgumentTypes(buf, size);
   }

   @Nonnegative
   private static int findNextTypeTerminatorCharacter(@Nonnull char[] desc, @Nonnegative int i) {
      while (desc[i++] != ';') {}
      return i;
   }

   @Nonnull
   private static JavaType[] getArgumentTypes(@Nonnull char[] buf, @Nonnegative int argCount) {
      if (argCount == 0) {
         return NO_ARGS;
      }

      JavaType[] argTypes = new JavaType[argCount];
      int off = 1;

      for (int i = 0; buf[off] != ')'; i++) {
         JavaType argType = getType(buf, off);
         argTypes[i] = argType;
         off += argType.len + (argType instanceof ObjectType ? 2 : 0);
      }

      return argTypes;
   }

   /**
    * Returns the Java type corresponding to the return type of the given method descriptor.
    */
   @Nonnull
   public static JavaType getReturnType(@Nonnull String methodDescriptor) {
      char[] buf = methodDescriptor.toCharArray();
      return getType(buf, methodDescriptor.indexOf(')') + 1);
   }

   /**
    * Computes the size of the arguments and of the return value of a method.
    *
    * @param desc the descriptor of a method.
    * @return the size of the arguments of the method (plus one for the implicit this argument),
    * argSize, and the size of its return value, retSize, packed into a single
    * int i = (argSize << 2) | retSize (argSize is therefore equal to i >> 2, and
    * retSize to i & 0x03).
    */
   public static int getArgumentsAndReturnSizes(@Nonnull String desc) {
      int argSize = 1;
      int i = 1;

      while (true) {
         char c = desc.charAt(i++);

         if (c == ')') {
            c = desc.charAt(i);
            return argSize << 2 | (c == 'V' ? 0 : isDoubleSizePrimitiveType(c) ? 2 : 1);
         }
         else if (c == 'L') {
            i = findNextTypeTerminatorCharacter(desc, i);
            argSize++;
         }
         else if (c == '[') {
            i = findStartOfArrayElementType(desc, i);
            c = desc.charAt(i);

            if (isDoubleSizePrimitiveType(c)) {
               argSize--;
            }
         }
         else if (isDoubleSizePrimitiveType(c)) {
            argSize += 2;
         }
         else {
            argSize++;
         }
      }
   }

   private static boolean isDoubleSizePrimitiveType(char typeCode) { return typeCode == 'D' || typeCode == 'J'; }

   @Nonnegative
   private static int findNextTypeTerminatorCharacter(@Nonnull String desc, @Nonnegative int i) {
      while (desc.charAt(i++) != ';') {}
      return i;
   }

   @Nonnegative
   private static int findStartOfArrayElementType(@Nonnull String desc, @Nonnegative int i) {
      while (desc.charAt(i) == '[') { i++; }
      return i;
   }

   /**
    * Returns the Java type corresponding to the given type descriptor. For method descriptors, buf is supposed
    * to contain nothing more than the descriptor itself.
    *
    * @param buf a buffer containing a type descriptor.
    * @param off the offset of this descriptor in the previous buffer.
    */
   @Nonnull
   static JavaType getType(@Nonnull char[] buf, @Nonnegative int off) {
      PrimitiveType primitiveType = PrimitiveType.getPrimitiveType(buf[off]);

      if (primitiveType != null) {
         return primitiveType;
      }

      return ReferenceType.getReferenceType(buf, off);
   }

   /**
    * Returns the binary name of the class corresponding to this type. This method must not be used on method types.
    */
   @Nonnull
   public abstract String getClassName();

   // ------------------------------------------------------------------------
   // Conversion to type descriptors
   // ------------------------------------------------------------------------

   /**
    * Returns the descriptor corresponding to this Java type.
    */
   @Nonnull
   public final String getDescriptor() {
      StringBuilder buf = new StringBuilder();
      getDescriptor(buf);
      return buf.toString();
   }

   /**
    * Appends the descriptor corresponding to this Java type to the given string buffer.
    *
    * @param buf the string builder to which the descriptor must be appended.
    */
   abstract void getDescriptor(@Nonnull StringBuilder buf);

   // -------------------------------------------------------------------------------------------------------
   // Direct conversion from classes to type descriptors, and vice-versa, without intermediate JavaType objects
   // -------------------------------------------------------------------------------------------------------

   /**
    * Returns the internal name of the given class. The internal name of a class is its fully qualified name, as
    * returned by Class.getName(), where '.' are replaced by '/'.
    *
    * @param aClass an object or array class.
    */
   @Nonnull
   public static String getInternalName(@Nonnull Class aClass) {
      return aClass.getName().replace('.', '/');
   }

   /**
    * Returns the descriptor corresponding to the given Java type.
    *
    * @param aClass an object class, a primitive class or an array class.
    */
   @Nonnull
   public static String getDescriptor(@Nonnull Class aClass) {
      StringBuilder buf = new StringBuilder();
      getDescriptor(buf, aClass);
      return buf.toString();
   }

   /**
    * Returns the descriptor corresponding to the given constructor.
    */
   @Nonnull
   public static String getConstructorDescriptor(@Nonnull Constructor constructor) {
      StringBuilder buf = getMemberDescriptor(constructor.getParameterTypes());
      buf.append('V');
      return buf.toString();
   }

   @Nonnull
   private static StringBuilder getMemberDescriptor(@Nonnull Class[] parameterTypes) {
      StringBuilder buf = new StringBuilder();
      buf.append('(');

      for (Class parameterType : parameterTypes) {
         getDescriptor(buf, parameterType);
      }

      buf.append(')');
      return buf;
   }

   /**
    * Returns the descriptor corresponding to the given method.
    */
   @Nonnull
   public static String getMethodDescriptor(@Nonnull Method method) {
      StringBuilder buf = getMemberDescriptor(method.getParameterTypes());
      getDescriptor(buf, method.getReturnType());
      return buf.toString();
   }

   /**
    * Appends the descriptor of the given class to the given string builder.
    */
   private static void getDescriptor(@Nonnull StringBuilder buf, @Nonnull Class aClass) {
      Class d = aClass;

      while (true) {
         if (d.isPrimitive()) {
            char typeCode = PrimitiveType.getPrimitiveType(d).getTypeCode();
            buf.append(typeCode);
            return;
         }
         else if (d.isArray()) {
            buf.append('[');
            d = d.getComponentType();
         }
         else {
            ReferenceType.getDescriptor(buf, d);
            return;
         }
      }
   }

   // ------------------------------------------------------------------------
   // Corresponding size and opcodes
   // ------------------------------------------------------------------------

   /**
    * Returns the size of values of this type. This method must not be used for method types.
    *
    * @return the size of values of this type, i.e., 2 for long and double, 0 for void and 1
    * otherwise.
    */
   @Nonnegative
   public abstract int getSize();

   /**
    * Returns a JVM instruction opcode adapted to this Java type. This method must not be used for method types.
    *
    * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, IASTORE, IADD, ISUB,
    *               IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
    * @return an opcode that is similar to the given opcode, but adapted to this Java type. For example, if this type is
    * float and opcode is IRETURN, this method returns FRETURN.
    */
   public abstract int getOpcode(int opcode);

   public abstract int getLoadOpcode();
   public abstract int getConstOpcode();

   /**
    * Returns a string representation of this type.
    *
    * @return the descriptor of this type.
    */
   @Override
   public final String toString() {
      return getDescriptor();
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy