Type.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.generic;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.List;
  21. import java.util.Objects;

  22. import org.apache.bcel.Const;
  23. import org.apache.bcel.classfile.ClassFormatException;
  24. import org.apache.bcel.classfile.InvalidMethodSignatureException;
  25. import org.apache.bcel.classfile.Utility;
  26. import org.apache.commons.lang3.StringUtils;

  27. /**
  28.  * Abstract super class for all possible Java types, namely basic types such as int, object types like String and array
  29.  * types, e.g. int[]
  30.  */
  31. public abstract class Type {

  32.     /**
  33.      * Predefined constants
  34.      */
  35.     public static final BasicType VOID = new BasicType(Const.T_VOID);

  36.     public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN);
  37.     public static final BasicType INT = new BasicType(Const.T_INT);
  38.     public static final BasicType SHORT = new BasicType(Const.T_SHORT);
  39.     public static final BasicType BYTE = new BasicType(Const.T_BYTE);
  40.     public static final BasicType LONG = new BasicType(Const.T_LONG);
  41.     public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE);
  42.     public static final BasicType FLOAT = new BasicType(Const.T_FLOAT);
  43.     public static final BasicType CHAR = new BasicType(Const.T_CHAR);
  44.     public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
  45.     public static final ObjectType CLASS = new ObjectType("java.lang.Class");
  46.     public static final ObjectType STRING = new ObjectType("java.lang.String");
  47.     public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
  48.     public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");

  49.     /**
  50.      * Empty array.
  51.      */
  52.     public static final Type[] NO_ARGS = {};
  53.     public static final ReferenceType NULL = new ReferenceType() {
  54.     };

  55.     public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") {
  56.     };

  57.     private static final ThreadLocal<Integer> CONSUMED_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0));

  58.     // int consumed_chars=0; // Remember position in string, see getArgumentTypes
  59.     static int consumed(final int coded) {
  60.         return coded >> 2;
  61.     }

  62.     static int encode(final int size, final int consumed) {
  63.         return consumed << 2 | size;
  64.     }

  65.     /**
  66.      * Convert arguments of a method (signature) to an array of Type objects.
  67.      *
  68.      * @param signature signature string such as (Ljava/lang/String;)V
  69.      * @return array of argument types
  70.      */
  71.     public static Type[] getArgumentTypes(final String signature) {
  72.         final List<Type> vec = new ArrayList<>();
  73.         int index;
  74.         try {
  75.             // Skip any type arguments to read argument declarations between '(' and ')'
  76.             index = signature.indexOf('(') + 1;
  77.             if (index <= 0) {
  78.                 throw new InvalidMethodSignatureException(signature);
  79.             }
  80.             while (signature.charAt(index) != ')') {
  81.                 vec.add(getType(signature.substring(index)));
  82.                 // corrected concurrent private static field access
  83.                 index += unwrap(CONSUMED_CHARS); // update position
  84.             }
  85.         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
  86.             throw new InvalidMethodSignatureException(signature, e);
  87.         }
  88.         final Type[] types = new Type[vec.size()];
  89.         vec.toArray(types);
  90.         return types;
  91.     }

  92.     static int getArgumentTypesSize(final String signature) {
  93.         int res = 0;
  94.         int index;
  95.         try {
  96.             // Skip any type arguments to read argument declarations between '(' and ')'
  97.             index = signature.indexOf('(') + 1;
  98.             if (index <= 0) {
  99.                 throw new InvalidMethodSignatureException(signature);
  100.             }
  101.             while (signature.charAt(index) != ')') {
  102.                 final int coded = getTypeSize(signature.substring(index));
  103.                 res += size(coded);
  104.                 index += consumed(coded);
  105.             }
  106.         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
  107.             throw new InvalidMethodSignatureException(signature, e);
  108.         }
  109.         return res;
  110.     }

  111.     /**
  112.      * Convert type to Java method signature, e.g. int[] f(java.lang.String x) becomes (Ljava/lang/String;)[I
  113.      *
  114.      * @param returnType what the method returns
  115.      * @param argTypes what are the argument types
  116.      * @return method signature for given type(s).
  117.      */
  118.     public static String getMethodSignature(final Type returnType, final Type[] argTypes) {
  119.         final StringBuilder buf = new StringBuilder("(");
  120.         if (argTypes != null) {
  121.             for (final Type argType : argTypes) {
  122.                 buf.append(argType.getSignature());
  123.             }
  124.         }
  125.         buf.append(')');
  126.         buf.append(returnType.getSignature());
  127.         return buf.toString();
  128.     }

  129.     /**
  130.      * Convert return value of a method (signature) to a Type object.
  131.      *
  132.      * @param signature signature string such as (Ljava/lang/String;)V
  133.      * @return return type
  134.      */
  135.     public static Type getReturnType(final String signature) {
  136.         try {
  137.             // Read return type after ')'
  138.             final int index = signature.lastIndexOf(')') + 1;
  139.             return getType(signature.substring(index));
  140.         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
  141.             throw new InvalidMethodSignatureException(signature, e);
  142.         }
  143.     }

  144.     static int getReturnTypeSize(final String signature) {
  145.         final int index = signature.lastIndexOf(')') + 1;
  146.         return size(getTypeSize(signature.substring(index)));
  147.     }

  148.     public static String getSignature(final java.lang.reflect.Method meth) {
  149.         final StringBuilder sb = new StringBuilder("(");
  150.         final Class<?>[] params = meth.getParameterTypes(); // avoid clone
  151.         for (final Class<?> param : params) {
  152.             sb.append(getType(param).getSignature());
  153.         }
  154.         sb.append(")");
  155.         sb.append(getType(meth.getReturnType()).getSignature());
  156.         return sb.toString();
  157.     }

  158.     /**
  159.      * Convert runtime {@link Class} to BCEL Type object.
  160.      *
  161.      * @param cls Java class
  162.      * @return corresponding Type object
  163.      */
  164.     public static Type getType(final Class<?> cls) {
  165.         Objects.requireNonNull(cls, "cls");
  166.         /*
  167.          * That's an amazingly easy case, because getName() returns the signature. That's what we would have liked anyway.
  168.          */
  169.         if (cls.isArray()) {
  170.             return getType(cls.getName());
  171.         }
  172.         if (!cls.isPrimitive()) { // "Real" class
  173.             return ObjectType.getInstance(cls.getName());
  174.         }
  175.         if (cls == Integer.TYPE) {
  176.             return INT;
  177.         }
  178.         if (cls == Void.TYPE) {
  179.             return VOID;
  180.         }
  181.         if (cls == Double.TYPE) {
  182.             return DOUBLE;
  183.         }
  184.         if (cls == Float.TYPE) {
  185.             return FLOAT;
  186.         }
  187.         if (cls == Boolean.TYPE) {
  188.             return BOOLEAN;
  189.         }
  190.         if (cls == Byte.TYPE) {
  191.             return BYTE;
  192.         }
  193.         if (cls == Short.TYPE) {
  194.             return SHORT;
  195.         }
  196.         if (cls == Long.TYPE) {
  197.             return LONG;
  198.         }
  199.         if (cls == Character.TYPE) {
  200.             return CHAR;
  201.         }
  202.         throw new IllegalStateException("Unknown primitive type " + cls);
  203.     }

  204.     /**
  205.      * Convert signature to a Type object.
  206.      *
  207.      * @param signature signature string such as Ljava/lang/String;
  208.      * @return type object
  209.      */
  210.     public static Type getType(final String signature) throws StringIndexOutOfBoundsException {
  211.         final byte type = Utility.typeOfSignature(signature);
  212.         if (type <= Const.T_VOID) {
  213.             // corrected concurrent private static field access
  214.             wrap(CONSUMED_CHARS, 1);
  215.             return BasicType.getType(type);
  216.         }
  217.         if (type != Const.T_ARRAY) { // type == T_REFERENCE
  218.             // Utility.typeSignatureToString understands how to parse generic types.
  219.             final String parsedSignature = Utility.typeSignatureToString(signature, false);
  220.             wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" 'L' and ';' are removed
  221.             return ObjectType.getInstance(Utility.pathToPackage(parsedSignature));
  222.         }
  223.         int dim = 0;
  224.         do { // Count dimensions
  225.             dim++;
  226.         } while (signature.charAt(dim) == '[');
  227.         // Recurse, but just once, if the signature is ok
  228.         final Type t = getType(signature.substring(dim));
  229.         // corrected concurrent private static field access
  230.         // consumed_chars += dim; // update counter - is replaced by
  231.         final int temp = unwrap(CONSUMED_CHARS) + dim;
  232.         wrap(CONSUMED_CHARS, temp);
  233.         return new ArrayType(t, dim);
  234.     }

  235.     /**
  236.      * Convert runtime {@code java.lang.Class[]} to BCEL Type objects.
  237.      *
  238.      * @param classes an array of runtime class objects
  239.      * @return array of corresponding Type objects
  240.      */
  241.     public static Type[] getTypes(final Class<?>[] classes) {
  242.         final Type[] ret = new Type[classes.length];
  243.         Arrays.setAll(ret, i -> getType(classes[i]));
  244.         return ret;
  245.     }

  246.     static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException {
  247.         final byte type = Utility.typeOfSignature(signature);
  248.         if (type <= Const.T_VOID) {
  249.             return encode(BasicType.getType(type).getSize(), 1);
  250.         }
  251.         if (type == Const.T_ARRAY) {
  252.             int dim = 0;
  253.             do { // Count dimensions
  254.                 dim++;
  255.             } while (signature.charAt(dim) == '[');
  256.             // Recurse, but just once, if the signature is ok
  257.             final int consumed = consumed(getTypeSize(signature.substring(dim)));
  258.             return encode(1, dim + consumed);
  259.         }
  260.         final int index = signature.indexOf(';'); // Look for closing ';'
  261.         if (index < 0) {
  262.             throw new ClassFormatException("Invalid signature: " + signature);
  263.         }
  264.         return encode(1, index + 1);
  265.     }

  266.     static String internalTypeNameToSignature(final String internalTypeName) {
  267.         if (StringUtils.isEmpty(internalTypeName) || StringUtils.equalsAny(internalTypeName, Const.SHORT_TYPE_NAMES)) {
  268.             return internalTypeName;
  269.         }
  270.         switch (internalTypeName.charAt(0)) {
  271.             case '[':
  272.                 return internalTypeName;
  273.             case 'L':
  274.             case 'T':
  275.                 if (internalTypeName.charAt(internalTypeName.length() - 1) == ';') {
  276.                     return internalTypeName;
  277.                 }
  278.                 return 'L' + internalTypeName + ';';
  279.             default:
  280.                 return 'L' + internalTypeName + ';';
  281.         }
  282.     }

  283.     static int size(final int coded) {
  284.         return coded & 3;
  285.     }

  286.     private static int unwrap(final ThreadLocal<Integer> tl) {
  287.         return tl.get().intValue();
  288.     }

  289.     private static void wrap(final ThreadLocal<Integer> tl, final int value) {
  290.         tl.set(Integer.valueOf(value));
  291.     }

  292.     /**
  293.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  294.      */
  295.     @Deprecated
  296.     protected byte type; // TODO should be final (and private)

  297.     /**
  298.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  299.      */
  300.     @Deprecated
  301.     protected String signature; // signature for the type TODO should be private

  302.     protected Type(final byte type, final String signature) {
  303.         this.type = type;
  304.         this.signature = signature;
  305.     }

  306.     /**
  307.      * @return whether the Types are equal
  308.      */
  309.     @Override
  310.     public boolean equals(final Object o) {
  311.         if (o instanceof Type) {
  312.             final Type t = (Type) o;
  313.             return type == t.type && signature.equals(t.signature);
  314.         }
  315.         return false;
  316.     }

  317.     public String getClassName() {
  318.         return toString();
  319.     }

  320.     /**
  321.      * @return signature for given type.
  322.      */
  323.     public String getSignature() {
  324.         return signature;
  325.     }

  326.     /**
  327.      * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
  328.      */
  329.     public int getSize() {
  330.         switch (type) {
  331.         case Const.T_DOUBLE:
  332.         case Const.T_LONG:
  333.             return 2;
  334.         case Const.T_VOID:
  335.             return 0;
  336.         default:
  337.             return 1;
  338.         }
  339.     }

  340.     /**
  341.      * @return type as defined in Constants
  342.      */
  343.     public byte getType() {
  344.         return type;
  345.     }

  346.     /**
  347.      * @return hash code of Type
  348.      */
  349.     @Override
  350.     public int hashCode() {
  351.         return type ^ signature.hashCode();
  352.     }

  353.     /**
  354.      * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link #INT}
  355.      * for {@link #BOOLEAN}, {@link #SHORT} or {@link #CHAR}, otherwise returns the given type.
  356.      *
  357.      * @since 6.0
  358.      */
  359.     public Type normalizeForStackOrLocal() {
  360.         if (this == BOOLEAN || this == BYTE || this == SHORT || this == CHAR) {
  361.             return INT;
  362.         }
  363.         return this;
  364.     }

  365.     /**
  366.      * @return Type string, e.g. 'int[]'
  367.      */
  368.     @Override
  369.     public String toString() {
  370.         return equals(NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false);
  371.     }
  372. }