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