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}