View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.generic;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  import java.util.Objects;
25  
26  import org.apache.bcel.Const;
27  import org.apache.bcel.classfile.ClassFormatException;
28  import org.apache.bcel.classfile.InvalidMethodSignatureException;
29  import org.apache.bcel.classfile.Utility;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.commons.lang3.Strings;
32  
33  /**
34   * Abstract super class for all possible Java types, namely basic types such as int, object types like String and array
35   * types, for example int[]
36   */
37  public abstract class Type {
38  
39      /**
40       * Predefined constants
41       */
42      public static final BasicType VOID = new BasicType(Const.T_VOID);
43  
44      public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN);
45      public static final BasicType INT = new BasicType(Const.T_INT);
46      public static final BasicType SHORT = new BasicType(Const.T_SHORT);
47      public static final BasicType BYTE = new BasicType(Const.T_BYTE);
48      public static final BasicType LONG = new BasicType(Const.T_LONG);
49      public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE);
50      public static final BasicType FLOAT = new BasicType(Const.T_FLOAT);
51      public static final BasicType CHAR = new BasicType(Const.T_CHAR);
52      public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
53      public static final ObjectType CLASS = new ObjectType("java.lang.Class");
54      public static final ObjectType STRING = new ObjectType("java.lang.String");
55      public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
56      public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");
57  
58      /**
59       * Empty array.
60       */
61      public static final Type[] NO_ARGS = {};
62      public static final ReferenceType NULL = new ReferenceType() {
63      };
64  
65      public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") {
66      };
67  
68      private static final ThreadLocal<Integer> CONSUMED_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0));
69  
70      // int consumed_chars=0; // Remember position in string, see getArgumentTypes
71      static int consumed(final int coded) {
72          return coded >> 2;
73      }
74  
75      static int encode(final int size, final int consumed) {
76          return consumed << 2 | size;
77      }
78  
79      /**
80       * Convert arguments of a method (signature) to an array of Type objects.
81       *
82       * @param signature signature string such as (Ljava/lang/String;)V.
83       * @return array of argument types.
84       */
85      public static Type[] getArgumentTypes(final String signature) {
86          final List<Type> vec = new ArrayList<>();
87          int index;
88          try {
89              // Skip any type arguments to read argument declarations between '(' and ')'
90              index = signature.indexOf('(') + 1;
91              if (index <= 0) {
92                  throw new InvalidMethodSignatureException(signature);
93              }
94              while (signature.charAt(index) != ')') {
95                  vec.add(getType(signature.substring(index)));
96                  // corrected concurrent private static field access
97                  index += unwrap(CONSUMED_CHARS); // update position
98              }
99          } catch (final StringIndexOutOfBoundsException e) { // Should never occur
100             throw new InvalidMethodSignatureException(signature, e);
101         }
102         final Type[] types = new Type[vec.size()];
103         vec.toArray(types);
104         return types;
105     }
106 
107     static int getArgumentTypesSize(final String signature) {
108         int res = 0;
109         int index;
110         try {
111             // Skip any type arguments to read argument declarations between '(' and ')'
112             index = signature.indexOf('(') + 1;
113             if (index <= 0) {
114                 throw new InvalidMethodSignatureException(signature);
115             }
116             while (signature.charAt(index) != ')') {
117                 final int coded = getTypeSize(signature.substring(index));
118                 res += size(coded);
119                 index += consumed(coded);
120             }
121         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
122             throw new InvalidMethodSignatureException(signature, e);
123         }
124         return res;
125     }
126 
127     /**
128      * Convert type to Java method signature, for example int[] f(java.lang.String x) becomes (Ljava/lang/String;)[I
129      *
130      * @param returnType what the method returns.
131      * @param argTypes what are the argument types.
132      * @return method signature for given type(s).
133      */
134     public static String getMethodSignature(final Type returnType, final Type[] argTypes) {
135         final StringBuilder buf = new StringBuilder("(");
136         if (argTypes != null) {
137             for (final Type argType : argTypes) {
138                 buf.append(argType.getSignature());
139             }
140         }
141         buf.append(')');
142         buf.append(returnType.getSignature());
143         return buf.toString();
144     }
145 
146     /**
147      * Convert return value of a method (signature) to a Type object.
148      *
149      * @param signature signature string such as (Ljava/lang/String;)V.
150      * @return return type.
151      */
152     public static Type getReturnType(final String signature) {
153         try {
154             // Read return type after ')'
155             final int index = signature.lastIndexOf(')') + 1;
156             return getType(signature.substring(index));
157         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
158             throw new InvalidMethodSignatureException(signature, e);
159         }
160     }
161 
162     static int getReturnTypeSize(final String signature) {
163         final int index = signature.lastIndexOf(')') + 1;
164         return size(getTypeSize(signature.substring(index)));
165     }
166 
167     public static String getSignature(final java.lang.reflect.Method meth) {
168         final StringBuilder sb = new StringBuilder("(");
169         final Class<?>[] params = meth.getParameterTypes(); // avoid clone
170         for (final Class<?> param : params) {
171             sb.append(getType(param).getSignature());
172         }
173         sb.append(")");
174         sb.append(getType(meth.getReturnType()).getSignature());
175         return sb.toString();
176     }
177 
178     /**
179      * Convert runtime {@link Class} to BCEL Type object.
180      *
181      * @param cls Java class.
182      * @return corresponding Type object.
183      */
184     public static Type getType(final Class<?> cls) {
185         Objects.requireNonNull(cls, "cls");
186         /*
187          * That's an amazingly easy case, because getName() returns the signature. That's what we would have liked anyway.
188          */
189         if (cls.isArray()) {
190             return getType(cls.getName());
191         }
192         if (!cls.isPrimitive()) { // "Real" class
193             return ObjectType.getInstance(cls.getName());
194         }
195         if (cls == Integer.TYPE) {
196             return INT;
197         }
198         if (cls == Void.TYPE) {
199             return VOID;
200         }
201         if (cls == Double.TYPE) {
202             return DOUBLE;
203         }
204         if (cls == Float.TYPE) {
205             return FLOAT;
206         }
207         if (cls == Boolean.TYPE) {
208             return BOOLEAN;
209         }
210         if (cls == Byte.TYPE) {
211             return BYTE;
212         }
213         if (cls == Short.TYPE) {
214             return SHORT;
215         }
216         if (cls == Long.TYPE) {
217             return LONG;
218         }
219         if (cls == Character.TYPE) {
220             return CHAR;
221         }
222         throw new IllegalStateException("Unknown primitive type " + cls);
223     }
224 
225     /**
226      * Convert signature to a Type object.
227      *
228      * @param signature signature string such as Ljava/lang/String;.
229      * @return type object.
230      */
231     public static Type getType(final String signature) throws StringIndexOutOfBoundsException {
232         final byte type = Utility.typeOfSignature(signature);
233         if (type <= Const.T_VOID) {
234             // corrected concurrent private static field access
235             wrap(CONSUMED_CHARS, 1);
236             return BasicType.getType(type);
237         }
238         if (type != Const.T_ARRAY) { // type == T_REFERENCE
239             // Utility.typeSignatureToString understands how to parse generic types.
240             final String parsedSignature = Utility.typeSignatureToString(signature, false);
241             wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" 'L' and ';' are removed
242             return ObjectType.getInstance(Utility.pathToPackage(parsedSignature));
243         }
244         int dim = 0;
245         do { // Count dimensions
246             dim++;
247         } while (signature.charAt(dim) == '[');
248         // Recurse, but just once, if the signature is ok
249         final Type t = getType(signature.substring(dim));
250         // corrected concurrent private static field access
251         // consumed_chars += dim; // update counter - is replaced by
252         final int temp = unwrap(CONSUMED_CHARS) + dim;
253         wrap(CONSUMED_CHARS, temp);
254         return new ArrayType(t, dim);
255     }
256 
257     /**
258      * Convert runtime {@code java.lang.Class[]} to BCEL Type objects.
259      *
260      * @param classes an array of runtime class objects.
261      * @return array of corresponding Type objects.
262      */
263     public static Type[] getTypes(final Class<?>[] classes) {
264         final Type[] ret = new Type[classes.length];
265         Arrays.setAll(ret, i -> getType(classes[i]));
266         return ret;
267     }
268 
269     static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException {
270         final byte type = Utility.typeOfSignature(signature);
271         if (type <= Const.T_VOID) {
272             return encode(BasicType.getType(type).getSize(), 1);
273         }
274         if (type == Const.T_ARRAY) {
275             int dim = 0;
276             do { // Count dimensions
277                 dim++;
278             } while (signature.charAt(dim) == '[');
279             // Recurse, but just once, if the signature is ok
280             final int consumed = consumed(getTypeSize(signature.substring(dim)));
281             return encode(1, dim + consumed);
282         }
283         final int index = signature.indexOf(';'); // Look for closing ';'
284         if (index < 0) {
285             throw new ClassFormatException("Invalid signature: " + signature);
286         }
287         return encode(1, index + 1);
288     }
289 
290     static String internalTypeNameToSignature(final String internalTypeName) {
291         if (StringUtils.isEmpty(internalTypeName) || Strings.CS.equalsAny(internalTypeName, Const.SHORT_TYPE_NAMES)) {
292             return internalTypeName;
293         }
294         switch (internalTypeName.charAt(0)) {
295             case '[':
296                 return internalTypeName;
297             case 'L':
298             case 'T':
299                 if (internalTypeName.charAt(internalTypeName.length() - 1) == ';') {
300                     return internalTypeName;
301                 }
302                 return 'L' + internalTypeName + ';';
303             default:
304                 return 'L' + internalTypeName + ';';
305         }
306     }
307 
308     static int size(final int coded) {
309         return coded & 3;
310     }
311 
312     private static int unwrap(final ThreadLocal<Integer> tl) {
313         return tl.get().intValue();
314     }
315 
316     private static void wrap(final ThreadLocal<Integer> tl, final int value) {
317         tl.set(Integer.valueOf(value));
318     }
319 
320     /**
321      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
322      */
323     @Deprecated
324     protected byte type; // TODO should be final (and private)
325 
326     /**
327      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
328      */
329     @Deprecated
330     protected String signature; // signature for the type TODO should be private
331 
332     protected Type(final byte type, final String signature) {
333         this.type = type;
334         this.signature = signature;
335     }
336 
337     /**
338      * @return whether the Types are equal.
339      */
340     @Override
341     public boolean equals(final Object o) {
342         if (o instanceof Type) {
343             final Type t = (Type) o;
344             return type == t.type && signature.equals(t.signature);
345         }
346         return false;
347     }
348 
349     /**
350      * Gets the class name.
351      *
352      * @return the class name.
353      */
354     public String getClassName() {
355         return toString();
356     }
357 
358     /**
359      * Gets the signature for this type.
360      *
361      * @return signature for given type.
362      */
363     public String getSignature() {
364         return signature;
365     }
366 
367     /**
368      * Gets the stack size of this type.
369      *
370      * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise).
371      */
372     public int getSize() {
373         switch (type) {
374         case Const.T_DOUBLE:
375         case Const.T_LONG:
376             return 2;
377         case Const.T_VOID:
378             return 0;
379         default:
380             return 1;
381         }
382     }
383 
384     /**
385      * Gets the type as defined in Constants.
386      *
387      * @return type as defined in Constants.
388      */
389     public byte getType() {
390         return type;
391     }
392 
393     /**
394      * Gets the hash code of this Type.
395      *
396      * @return hash code of Type.
397      */
398     @Override
399     public int hashCode() {
400         return type ^ signature.hashCode();
401     }
402 
403     /**
404      * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link #INT}
405      * for {@link #BOOLEAN}, {@link #SHORT} or {@link #CHAR}, otherwise returns the given type.
406      *
407      * @return the normalized type.
408      * @since 6.0
409      */
410     public Type normalizeForStackOrLocal() {
411         if (this == BOOLEAN || this == BYTE || this == SHORT || this == CHAR) {
412             return INT;
413         }
414         return this;
415     }
416 
417     /**
418      * @return Type string, for example 'int[]'.
419      */
420     @Override
421     public String toString() {
422         return equals(NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false);
423     }
424 }