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