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 * @version $Id: Type.java 1749603 2016-06-21 20:50:19Z ggregory $
032 */
033public abstract class Type {
034
035    /**
036     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
037     */
038    @Deprecated
039    protected byte type; // TODO should be final (and private)
040
041    /**
042     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
043     */
044    @Deprecated
045    protected String signature; // signature for the type TODO should be private
046    /** Predefined constants
047     */
048    public static final BasicType VOID = new BasicType(Const.T_VOID);
049    public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN);
050    public static final BasicType INT = new BasicType(Const.T_INT);
051    public static final BasicType SHORT = new BasicType(Const.T_SHORT);
052    public static final BasicType BYTE = new BasicType(Const.T_BYTE);
053    public static final BasicType LONG = new BasicType(Const.T_LONG);
054    public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE);
055    public static final BasicType FLOAT = new BasicType(Const.T_FLOAT);
056    public static final BasicType CHAR = new BasicType(Const.T_CHAR);
057    public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
058    public static final ObjectType CLASS = new ObjectType("java.lang.Class");
059    public static final ObjectType STRING = new ObjectType("java.lang.String");
060    public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
061    public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");
062    public static final Type[] NO_ARGS = new Type[0]; // EMPTY, so immutable
063    public static final ReferenceType NULL = new ReferenceType() {
064    };
065    public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") {
066    };
067
068
069    protected Type(final byte t, final String s) {
070        type = t;
071        signature = s;
072    }
073
074
075    /**
076     * @return hashcode of Type
077     */
078    @Override
079    public int hashCode() {
080        return type ^ signature.hashCode();
081    }
082
083
084    /**
085     * @return whether the Types are equal
086     */
087    @Override
088    public boolean equals(final Object o) {
089          if (o instanceof Type) {
090              final Type t = (Type)o;
091              return (type == t.type) && signature.equals(t.signature);
092          }
093          return false;
094    }
095
096
097    /**
098     * @return signature for given type.
099     */
100    public String getSignature() {
101        return signature;
102    }
103
104
105    /**
106     * @return type as defined in Constants
107     */
108    public byte getType() {
109        return type;
110    }
111
112    /**
113     * boolean, short and char variable are considered as int in the stack or local variable area.
114     * Returns {@link Type#INT} for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise
115     * returns the given type.
116     * @since 6.0
117     */
118    public Type normalizeForStackOrLocal() {
119        if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) {
120            return Type.INT;
121        }
122        return this;
123    }
124
125    /**
126     * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
127     */
128    public int getSize() {
129        switch (type) {
130            case Const.T_DOUBLE:
131            case Const.T_LONG:
132                return 2;
133            case Const.T_VOID:
134                return 0;
135            default:
136                return 1;
137        }
138    }
139
140
141    /**
142     * @return Type string, e.g. `int[]'
143     */
144    @Override
145    public String toString() {
146        return ((this.equals(Type.NULL) || (type >= Const.T_UNKNOWN))) ? signature : Utility
147                .signatureToString(signature, false);
148    }
149
150
151    /**
152     * Convert type to Java method signature, e.g. int[] f(java.lang.String x)
153     * becomes (Ljava/lang/String;)[I
154     *
155     * @param return_type what the method returns
156     * @param arg_types what are the argument types
157     * @return method signature for given type(s).
158     */
159    public static String getMethodSignature( final Type return_type, final Type[] arg_types ) {
160        final StringBuilder buf = new StringBuilder("(");
161        if (arg_types != null) {
162            for (final Type arg_type : arg_types) {
163                buf.append(arg_type.getSignature());
164            }
165        }
166        buf.append(')');
167        buf.append(return_type.getSignature());
168        return buf.toString();
169    }
170
171    private static final ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
172
173        @Override
174        protected Integer initialValue() {
175            return Integer.valueOf(0);
176        }
177    };//int consumed_chars=0; // Remember position in string, see getArgumentTypes
178
179
180    private static int unwrap( final ThreadLocal<Integer> tl ) {
181        return tl.get().intValue();
182    }
183
184
185    private static void wrap( final ThreadLocal<Integer> tl, final int value ) {
186        tl.set(Integer.valueOf(value));
187    }
188
189
190    /**
191     * Convert signature to a Type object.
192     * @param signature signature string such as Ljava/lang/String;
193     * @return type object
194     */
195    // @since 6.0 no longer final
196    public static Type getType( final String signature ) throws StringIndexOutOfBoundsException {
197        final byte type = Utility.typeOfSignature(signature);
198        if (type <= Const.T_VOID) {
199            //corrected concurrent private static field acess
200            wrap(consumed_chars, 1);
201            return BasicType.getType(type);
202        } else if (type == Const.T_ARRAY) {
203            int dim = 0;
204            do { // Count dimensions
205                dim++;
206            } while (signature.charAt(dim) == '[');
207            // Recurse, but just once, if the signature is ok
208            final Type t = getType(signature.substring(dim));
209            //corrected concurrent private static field acess
210            //  consumed_chars += dim; // update counter - is replaced by
211            final int _temp = unwrap(consumed_chars) + dim;
212            wrap(consumed_chars, _temp);
213            return new ArrayType(t, dim);
214        } else { // type == T_REFERENCE
215            // Utility.signatureToString understands how to parse
216            // generic types.
217            final String parsedSignature = Utility.signatureToString(signature, false);
218            wrap(consumed_chars, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed
219            return ObjectType.getInstance(parsedSignature.replace('/', '.'));
220        }
221    }
222
223
224    /**
225     * Convert return value of a method (signature) to a Type object.
226     *
227     * @param signature signature string such as (Ljava/lang/String;)V
228     * @return return type
229     */
230    public static Type getReturnType( final String signature ) {
231        try {
232            // Read return type after `)'
233            final int index = signature.lastIndexOf(')') + 1;
234            return getType(signature.substring(index));
235        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
236            throw new ClassFormatException("Invalid method signature: " + signature, e);
237        }
238    }
239
240
241    /**
242     * Convert arguments of a method (signature) to an array of Type objects.
243     * @param signature signature string such as (Ljava/lang/String;)V
244     * @return array of argument types
245     */
246    public static Type[] getArgumentTypes( final String signature ) {
247        final List<Type> vec = new ArrayList<>();
248        int index;
249        Type[] types;
250        try { // Read all declarations between for `(' and `)'
251            if (signature.charAt(0) != '(') {
252                throw new ClassFormatException("Invalid method signature: " + signature);
253            }
254            index = 1; // current string position
255            while (signature.charAt(index) != ')') {
256                vec.add(getType(signature.substring(index)));
257                //corrected concurrent private static field acess
258                index += unwrap(consumed_chars); // update position
259            }
260        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
261            throw new ClassFormatException("Invalid method signature: " + signature, e);
262        }
263        types = new Type[vec.size()];
264        vec.toArray(types);
265        return types;
266    }
267
268
269    /** Convert runtime java.lang.Class to BCEL Type object.
270     * @param cl Java class
271     * @return corresponding Type object
272     */
273    public static Type getType( final java.lang.Class<?> cl ) {
274        if (cl == null) {
275            throw new IllegalArgumentException("Class must not be null");
276        }
277        /* That's an amzingly easy case, because getName() returns
278         * the signature. That's what we would have liked anyway.
279         */
280        if (cl.isArray()) {
281            return getType(cl.getName());
282        } else if (cl.isPrimitive()) {
283            if (cl == Integer.TYPE) {
284                return INT;
285            } else if (cl == Void.TYPE) {
286                return VOID;
287            } else if (cl == Double.TYPE) {
288                return DOUBLE;
289            } else if (cl == Float.TYPE) {
290                return FLOAT;
291            } else if (cl == Boolean.TYPE) {
292                return BOOLEAN;
293            } else if (cl == Byte.TYPE) {
294                return BYTE;
295            } else if (cl == Short.TYPE) {
296                return SHORT;
297            } else if (cl == Byte.TYPE) {
298                return BYTE;
299            } else if (cl == Long.TYPE) {
300                return LONG;
301            } else if (cl == Character.TYPE) {
302                return CHAR;
303            } else {
304                throw new IllegalStateException("Ooops, what primitive type is " + cl);
305            }
306        } else { // "Real" class
307            return ObjectType.getInstance(cl.getName());
308        }
309    }
310
311
312    /**
313     * Convert runtime java.lang.Class[] to BCEL Type objects.
314     * @param classes an array of runtime class objects
315     * @return array of corresponding Type objects
316     */
317    public static Type[] getTypes( final java.lang.Class<?>[] classes ) {
318        final Type[] ret = new Type[classes.length];
319        for (int i = 0; i < ret.length; i++) {
320            ret[i] = getType(classes[i]);
321        }
322        return ret;
323    }
324
325
326    public static String getSignature( final java.lang.reflect.Method meth ) {
327        final StringBuilder sb = new StringBuilder("(");
328        final Class<?>[] params = meth.getParameterTypes(); // avoid clone
329        for (final Class<?> param : params) {
330            sb.append(getType(param).getSignature());
331        }
332        sb.append(")");
333        sb.append(getType(meth.getReturnType()).getSignature());
334        return sb.toString();
335    }
336
337    static int size(final int coded) {
338        return coded & 3;
339    }
340
341    static int consumed(final int coded) {
342        return coded >> 2;
343    }
344
345    static int encode(final int size, final int consumed) {
346        return consumed << 2 | size;
347    }
348
349    static int getArgumentTypesSize( final String signature ) {
350        int res = 0;
351        int index;
352        try { // Read all declarations between for `(' and `)'
353            if (signature.charAt(0) != '(') {
354                throw new ClassFormatException("Invalid method signature: " + signature);
355            }
356            index = 1; // current string position
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}