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