    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}