    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("Unknown primitive type " + 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}