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}