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