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