001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.generic;
019
020import java.util.ArrayList;
021import java.util.List;
022
023import org.apache.bcel.Const;
024import org.apache.bcel.classfile.ClassFormatException;
025import org.apache.bcel.classfile.Utility;
026
027/**
028 * Abstract super class for all possible java types, namely basic types
029 * such as int, object types like String and array types, e.g. int[]
030 *
031 */
032public abstract class Type {
033
034    /**
035     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
036     */
037    @Deprecated
038    protected byte type; // TODO should be final (and private)
039
040    /**
041     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
042     */
043    @Deprecated
044    protected String signature; // signature for the type TODO should be private
045    /** Predefined constants
046     */
047    public static final BasicType VOID = new BasicType(Const.T_VOID);
048    public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN);
049    public static final BasicType INT = new BasicType(Const.T_INT);
050    public static final BasicType SHORT = new BasicType(Const.T_SHORT);
051    public static final BasicType BYTE = new BasicType(Const.T_BYTE);
052    public static final BasicType LONG = new BasicType(Const.T_LONG);
053    public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE);
054    public static final BasicType FLOAT = new BasicType(Const.T_FLOAT);
055    public static final BasicType CHAR = new BasicType(Const.T_CHAR);
056    public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
057    public static final ObjectType CLASS = new ObjectType("java.lang.Class");
058    public static final ObjectType STRING = new ObjectType("java.lang.String");
059    public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
060    public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");
061    public static final Type[] NO_ARGS = new Type[0]; // EMPTY, so immutable
062    public static final ReferenceType NULL = new ReferenceType() {
063    };
064    public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") {
065    };
066
067
068    protected Type(final byte t, final String s) {
069        type = t;
070        signature = s;
071    }
072
073
074    /**
075     * @return hashcode of Type
076     */
077    @Override
078    public int hashCode() {
079        return type ^ signature.hashCode();
080    }
081
082
083    /**
084     * @return whether the Types are equal
085     */
086    @Override
087    public boolean equals(final Object o) {
088          if (o instanceof Type) {
089              final Type t = (Type)o;
090              return (type == t.type) && signature.equals(t.signature);
091          }
092          return false;
093    }
094
095
096    /**
097     * @return signature for given type.
098     */
099    public String getSignature() {
100        return signature;
101    }
102
103
104    /**
105     * @return type as defined in Constants
106     */
107    public byte getType() {
108        return type;
109    }
110
111    /**
112     * boolean, short and char variable are considered as int in the stack or local variable area.
113     * Returns {@link Type#INT} for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise
114     * returns the given type.
115     * @since 6.0
116     */
117    public Type normalizeForStackOrLocal() {
118        if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) {
119            return Type.INT;
120        }
121        return this;
122    }
123
124    /**
125     * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
126     */
127    public int getSize() {
128        switch (type) {
129            case Const.T_DOUBLE:
130            case Const.T_LONG:
131                return 2;
132            case Const.T_VOID:
133                return 0;
134            default:
135                return 1;
136        }
137    }
138
139
140    /**
141     * @return Type string, e.g. `int[]'
142     */
143    @Override
144    public String toString() {
145        return ((this.equals(Type.NULL) || (type >= Const.T_UNKNOWN))) ? signature : Utility
146                .signatureToString(signature, false);
147    }
148
149
150    /**
151     * Convert type to Java method signature, e.g. int[] f(java.lang.String x)
152     * becomes (Ljava/lang/String;)[I
153     *
154     * @param return_type what the method returns
155     * @param arg_types what are the argument types
156     * @return method signature for given type(s).
157     */
158    public static String getMethodSignature( final Type return_type, final Type[] arg_types ) {
159        final StringBuilder buf = new StringBuilder("(");
160        if (arg_types != null) {
161            for (final Type arg_type : arg_types) {
162                buf.append(arg_type.getSignature());
163            }
164        }
165        buf.append(')');
166        buf.append(return_type.getSignature());
167        return buf.toString();
168    }
169
170    private static final ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
171
172        @Override
173        protected Integer initialValue() {
174            return Integer.valueOf(0);
175        }
176    };//int consumed_chars=0; // Remember position in string, see getArgumentTypes
177
178
179    private static int unwrap( final ThreadLocal<Integer> tl ) {
180        return tl.get().intValue();
181    }
182
183
184    private static void wrap( final ThreadLocal<Integer> tl, final int value ) {
185        tl.set(Integer.valueOf(value));
186    }
187
188
189    /**
190     * Convert signature to a Type object.
191     * @param signature signature string such as Ljava/lang/String;
192     * @return type object
193     */
194    // @since 6.0 no longer final
195    public static Type getType( final String signature ) throws StringIndexOutOfBoundsException {
196        final byte type = Utility.typeOfSignature(signature);
197        if (type <= Const.T_VOID) {
198            //corrected concurrent private static field acess
199            wrap(consumed_chars, 1);
200            return BasicType.getType(type);
201        } else if (type == Const.T_ARRAY) {
202            int dim = 0;
203            do { // Count dimensions
204                dim++;
205            } while (signature.charAt(dim) == '[');
206            // Recurse, but just once, if the signature is ok
207            final Type t = getType(signature.substring(dim));
208            //corrected concurrent private static field acess
209            //  consumed_chars += dim; // update counter - is replaced by
210            final int _temp = unwrap(consumed_chars) + dim;
211            wrap(consumed_chars, _temp);
212            return new ArrayType(t, dim);
213        } else { // type == T_REFERENCE
214            // Utility.typeSignatureToString understands how to parse generic types.
215            final String parsedSignature = Utility.typeSignatureToString(signature, false);
216            wrap(consumed_chars, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed
217            return ObjectType.getInstance(parsedSignature.replace('/', '.'));
218        }
219    }
220
221
222    /**
223     * Convert return value of a method (signature) to a Type object.
224     *
225     * @param signature signature string such as (Ljava/lang/String;)V
226     * @return return type
227     */
228    public static Type getReturnType( final String signature ) {
229        try {
230            // Read return type after `)'
231            final int index = signature.lastIndexOf(')') + 1;
232            return getType(signature.substring(index));
233        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
234            throw new ClassFormatException("Invalid method signature: " + signature, e);
235        }
236    }
237
238
239    /**
240     * Convert arguments of a method (signature) to an array of Type objects.
241     * @param signature signature string such as (Ljava/lang/String;)V
242     * @return array of argument types
243     */
244    public static Type[] getArgumentTypes( final String signature ) {
245        final List<Type> vec = new ArrayList<>();
246        int index;
247        Type[] types;
248        try {
249            // Skip any type arguments to read argument declarations between `(' and `)'
250            index = signature.indexOf('(') + 1;
251            if (index <= 0) {
252                throw new ClassFormatException("Invalid method signature: " + signature);
253            }
254            while (signature.charAt(index) != ')') {
255                vec.add(getType(signature.substring(index)));
256                //corrected concurrent private static field acess
257                index += unwrap(consumed_chars); // update position
258            }
259        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
260            throw new ClassFormatException("Invalid method signature: " + signature, e);
261        }
262        types = new Type[vec.size()];
263        vec.toArray(types);
264        return types;
265    }
266
267
268    /** Convert runtime java.lang.Class to BCEL Type object.
269     * @param cl Java class
270     * @return corresponding Type object
271     */
272    public static Type getType( final java.lang.Class<?> cl ) {
273        if (cl == null) {
274            throw new IllegalArgumentException("Class must not be null");
275        }
276        /* That's an amzingly easy case, because getName() returns
277         * the signature. That's what we would have liked anyway.
278         */
279        if (cl.isArray()) {
280            return getType(cl.getName());
281        } else if (cl.isPrimitive()) {
282            if (cl == Integer.TYPE) {
283                return INT;
284            } else if (cl == Void.TYPE) {
285                return VOID;
286            } else if (cl == Double.TYPE) {
287                return DOUBLE;
288            } else if (cl == Float.TYPE) {
289                return FLOAT;
290            } else if (cl == Boolean.TYPE) {
291                return BOOLEAN;
292            } else if (cl == Byte.TYPE) {
293                return BYTE;
294            } else if (cl == Short.TYPE) {
295                return SHORT;
296            } else if (cl == Byte.TYPE) {
297                return BYTE;
298            } else if (cl == Long.TYPE) {
299                return LONG;
300            } else if (cl == Character.TYPE) {
301                return CHAR;
302            } else {
303                throw new IllegalStateException("Ooops, what primitive type is " + cl);
304            }
305        } else { // "Real" class
306            return ObjectType.getInstance(cl.getName());
307        }
308    }
309
310
311    /**
312     * Convert runtime java.lang.Class[] to BCEL Type objects.
313     * @param classes an array of runtime class objects
314     * @return array of corresponding Type objects
315     */
316    public static Type[] getTypes( final java.lang.Class<?>[] classes ) {
317        final Type[] ret = new Type[classes.length];
318        for (int i = 0; i < ret.length; i++) {
319            ret[i] = getType(classes[i]);
320        }
321        return ret;
322    }
323
324
325    public static String getSignature( final java.lang.reflect.Method meth ) {
326        final StringBuilder sb = new StringBuilder("(");
327        final Class<?>[] params = meth.getParameterTypes(); // avoid clone
328        for (final Class<?> param : params) {
329            sb.append(getType(param).getSignature());
330        }
331        sb.append(")");
332        sb.append(getType(meth.getReturnType()).getSignature());
333        return sb.toString();
334    }
335
336    static int size(final int coded) {
337        return coded & 3;
338    }
339
340    static int consumed(final int coded) {
341        return coded >> 2;
342    }
343
344    static int encode(final int size, final int consumed) {
345        return consumed << 2 | size;
346    }
347
348    static int getArgumentTypesSize( final String signature ) {
349        int res = 0;
350        int index;
351        try {
352            // Skip any type arguments to read argument declarations between `(' and `)'
353            index = signature.indexOf('(') + 1;
354            if (index <= 0) {
355                throw new ClassFormatException("Invalid method signature: " + signature);
356            }
357            while (signature.charAt(index) != ')') {
358                final int coded = getTypeSize(signature.substring(index));
359                res += size(coded);
360                index += consumed(coded);
361            }
362        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
363            throw new ClassFormatException("Invalid method signature: " + signature, e);
364        }
365        return res;
366    }
367
368    static int getTypeSize( final String signature ) throws StringIndexOutOfBoundsException {
369        final byte type = Utility.typeOfSignature(signature);
370        if (type <= Const.T_VOID) {
371            return encode(BasicType.getType(type).getSize(), 1);
372        } else if (type == Const.T_ARRAY) {
373            int dim = 0;
374            do { // Count dimensions
375                dim++;
376            } while (signature.charAt(dim) == '[');
377            // Recurse, but just once, if the signature is ok
378            final int consumed = consumed(getTypeSize(signature.substring(dim)));
379            return encode(1, dim + consumed);
380        } else { // type == T_REFERENCE
381            final int index = signature.indexOf(';'); // Look for closing `;'
382            if (index < 0) {
383                throw new ClassFormatException("Invalid signature: " + signature);
384            }
385            return encode(1, index + 1);
386        }
387    }
388
389
390    static int getReturnTypeSize(final String signature) {
391        final int index = signature.lastIndexOf(')') + 1;
392        return Type.size(getTypeSize(signature.substring(index)));
393    }
394
395
396    /*
397     * Currently only used by the ArrayType constructor.
398     * The signature has a complicated dependency on other parameter
399     * so it's tricky to do it in a call to the super ctor.
400     */
401    void setSignature(final String signature) {
402        this.signature = signature;
403    }
404}