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