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