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