    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    public static Type getType(final String signature) throws StringIndexOutOfBoundsException {
    227        final byte type = Utility.typeOfSignature(signature);
    228        if (type <= Const.T_VOID) {
    229            // corrected concurrent private static field acess
    230            wrap(CONSUMED_CHARS, 1);
    231            return BasicType.getType(type);
    232        }
    233        if (type != Const.T_ARRAY) { // type == T_REFERENCE
    234            // Utility.typeSignatureToString understands how to parse generic types.
    235            final String parsedSignature = Utility.typeSignatureToString(signature, false);
    236            wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" 'L' and ';' are removed
    237            return ObjectType.getInstance(Utility.pathToPackage(parsedSignature));
    238        }
    239        int dim = 0;
    240        do { // Count dimensions
    241            dim++;
    242        } while (signature.charAt(dim) == '[');
    243        // Recurse, but just once, if the signature is ok
    244        final Type t = getType(signature.substring(dim));
    245        // corrected concurrent private static field acess
    246        // consumed_chars += dim; // update counter - is replaced by
    247        final int temp = unwrap(CONSUMED_CHARS) + dim;
    248        wrap(CONSUMED_CHARS, temp);
    249        return new ArrayType(t, dim);
    250    }
    251
    252    /**
    253     * Convert runtime java.lang.Class[] to BCEL Type objects.
    254     *
    255     * @param classes an array of runtime class objects
    256     * @return array of corresponding Type objects
    257     */
    258    public static Type[] getTypes(final Class<?>[] classes) {
    259        final Type[] ret = new Type[classes.length];
    260        Arrays.setAll(ret, i -> getType(classes[i]));
    261        return ret;
    262    }
    263
    264    static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException {
    265        final byte type = Utility.typeOfSignature(signature);
    266        if (type <= Const.T_VOID) {
    267            return encode(BasicType.getType(type).getSize(), 1);
    268        }
    269        if (type == Const.T_ARRAY) {
    270            int dim = 0;
    271            do { // Count dimensions
    272                dim++;
    273            } while (signature.charAt(dim) == '[');
    274            // Recurse, but just once, if the signature is ok
    275            final int consumed = consumed(getTypeSize(signature.substring(dim)));
    276            return encode(1, dim + consumed);
    277        }
    278        final int index = signature.indexOf(';'); // Look for closing ';'
    279        if (index < 0) {
    280            throw new ClassFormatException("Invalid signature: " + signature);
    281        }
    282        return encode(1, index + 1);
    283    }
    284
    285    static int size(final int coded) {
    286        return coded & 3;
    287    }
    288
    289    private static int unwrap(final ThreadLocal<Integer> tl) {
    290        return tl.get().intValue();
    291    }
    292
    293    private static void wrap(final ThreadLocal<Integer> tl, final int value) {
    294        tl.set(Integer.valueOf(value));
    295    }
    296
    297    /**
    298     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    299     */
    300    @Deprecated
    301    protected byte type; // TODO should be final (and private)
    302
    303    /**
    304     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
    305     */
    306    @Deprecated
    307    protected String signature; // signature for the type TODO should be private
    308
    309    protected Type(final byte type, final String signature) {
    310        this.type = type;
    311        this.signature = signature;
    312    }
    313
    314    /**
    315     * @return whether the Types are equal
    316     */
    317    @Override
    318    public boolean equals(final Object o) {
    319        if (o instanceof Type) {
    320            final Type t = (Type) o;
    321            return type == t.type && signature.equals(t.signature);
    322        }
    323        return false;
    324    }
    325
    326    public String getClassName() {
    327        return toString();
    328    }
    329
    330    /**
    331     * @return signature for given type.
    332     */
    333    public String getSignature() {
    334        return signature;
    335    }
    336
    337    /**
    338     * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
    339     */
    340    public int getSize() {
    341        switch (type) {
    342        case Const.T_DOUBLE:
    343        case Const.T_LONG:
    344            return 2;
    345        case Const.T_VOID:
    346            return 0;
    347        default:
    348            return 1;
    349        }
    350    }
    351
    352    /**
    353     * @return type as defined in Constants
    354     */
    355    public byte getType() {
    356        return type;
    357    }
    358
    359    /**
    360     * @return hashcode of Type
    361     */
    362    @Override
    363    public int hashCode() {
    364        return type ^ signature.hashCode();
    365    }
    366
    367    /**
    368     * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link Type#INT}
    369     * for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise returns the given type.
    370     *
    371     * @since 6.0
    372     */
    373    public Type normalizeForStackOrLocal() {
    374        if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) {
    375            return Type.INT;
    376        }
    377        return this;
    378    }
    379
    380    /*
    381     * Currently only used by the ArrayType constructor. The signature has a complicated dependency on other parameter so
    382     * it's tricky to do it in a call to the super ctor.
    383     */
    384    void setSignature(final String signature) {
    385        this.signature = signature;
    386    }
    387
    388    /**
    389     * @return Type string, e.g. 'int[]'
    390     */
    391    @Override
    392    public String toString() {
    393        return this.equals(Type.NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false);
    394    }
    395}