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