1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package com.sun.org.apache.bcel.internal.generic;
21
22import java.util.ArrayList;
23import java.util.List;
24
25import com.sun.org.apache.bcel.internal.Const;
26import com.sun.org.apache.bcel.internal.classfile.ClassFormatException;
27import com.sun.org.apache.bcel.internal.classfile.Utility;
28
29/**
30 * Abstract super class for all possible java types, namely basic types such as
31 * int, object types like String and array types, e.g. int[]
32 *
33 * @version $Id: Type.java 1749603 2016-06-21 20:50:19Z ggregory $
34 */
35public abstract class Type {
36
37    private final byte type;
38    private String signature; // signature for the type
39    /**
40     * Predefined constants
41     */
42    public static final BasicType VOID = new BasicType(Const.T_VOID);
43    public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN);
44    public static final BasicType INT = new BasicType(Const.T_INT);
45    public static final BasicType SHORT = new BasicType(Const.T_SHORT);
46    public static final BasicType BYTE = new BasicType(Const.T_BYTE);
47    public static final BasicType LONG = new BasicType(Const.T_LONG);
48    public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE);
49    public static final BasicType FLOAT = new BasicType(Const.T_FLOAT);
50    public static final BasicType CHAR = new BasicType(Const.T_CHAR);
51    public static final ObjectType OBJECT = new ObjectType("java.lang.Object");
52    public static final ObjectType CLASS = new ObjectType("java.lang.Class");
53    public static final ObjectType STRING = new ObjectType("java.lang.String");
54    public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer");
55    public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable");
56    public static final Type[] NO_ARGS = new Type[0]; // EMPTY, so immutable
57    public static final ReferenceType NULL = new ReferenceType() {
58    };
59    public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") {
60    };
61
62    protected Type(final byte t, final String s) {
63        type = t;
64        signature = s;
65    }
66
67    /**
68     * @return hashcode of Type
69     */
70    @Override
71    public int hashCode() {
72        return type ^ signature.hashCode();
73    }
74
75    /**
76     * @return whether the Types are equal
77     */
78    @Override
79    public boolean equals(final Object o) {
80        if (o instanceof Type) {
81            final Type t = (Type) o;
82            return (type == t.type) && signature.equals(t.signature);
83        }
84        return false;
85    }
86
87    /**
88     * @return signature for given type.
89     */
90    public String getSignature() {
91        return signature;
92    }
93
94    /**
95     * @return type as defined in Constants
96     */
97    public byte getType() {
98        return type;
99    }
100
101    /**
102     * boolean, short and char variable are considered as int in the stack or
103     * local variable area. Returns {@link Type#INT} for
104     * {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise
105     * returns the given type.
106     *
107     * @since 6.0
108     */
109    public Type normalizeForStackOrLocal() {
110        if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) {
111            return Type.INT;
112        }
113        return this;
114    }
115
116    /**
117     * @return stack size of this type (2 for long and double, 0 for void, 1
118     * otherwise)
119     */
120    public int getSize() {
121        switch (type) {
122            case Const.T_DOUBLE:
123            case Const.T_LONG:
124                return 2;
125            case Const.T_VOID:
126                return 0;
127            default:
128                return 1;
129        }
130    }
131
132    /**
133     * @return Type string, e.g. `int[]'
134     */
135    @Override
136    public String toString() {
137        return ((this.equals(Type.NULL) || (type >= Const.T_UNKNOWN))) ? signature : Utility
138                .signatureToString(signature, false);
139    }
140
141    /**
142     * Convert type to Java method signature, e.g. int[] f(java.lang.String x)
143     * becomes (Ljava/lang/String;)[I
144     *
145     * @param return_type what the method returns
146     * @param arg_types what are the argument types
147     * @return method signature for given type(s).
148     */
149    public static String getMethodSignature(final Type return_type, final Type[] arg_types) {
150        final StringBuilder buf = new StringBuilder("(");
151        if (arg_types != null) {
152            for (final Type arg_type : arg_types) {
153                buf.append(arg_type.getSignature());
154            }
155        }
156        buf.append(')');
157        buf.append(return_type.getSignature());
158        return buf.toString();
159    }
160
161    private static final ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
162
163        @Override
164        protected Integer initialValue() {
165            return Integer.valueOf(0);
166        }
167    };//int consumed_chars=0; // Remember position in string, see getArgumentTypes
168
169    private static int unwrap(final ThreadLocal<Integer> tl) {
170        return tl.get().intValue();
171    }
172
173    private static void wrap(final ThreadLocal<Integer> tl, final int value) {
174        tl.set(Integer.valueOf(value));
175    }
176
177    /**
178     * Convert signature to a Type object.
179     *
180     * @param signature signature string such as Ljava/lang/String;
181     * @return type object
182     */
183    // @since 6.0 no longer final
184    public static Type getType(final String signature) throws StringIndexOutOfBoundsException {
185        final byte type = Utility.typeOfSignature(signature);
186        if (type <= Const.T_VOID) {
187            //corrected concurrent private static field acess
188            wrap(consumed_chars, 1);
189            return BasicType.getType(type);
190        } else if (type == Const.T_ARRAY) {
191            int dim = 0;
192            do { // Count dimensions
193                dim++;
194            } while (signature.charAt(dim) == '[');
195            // Recurse, but just once, if the signature is ok
196            final Type t = getType(signature.substring(dim));
197            //corrected concurrent private static field acess
198            //  consumed_chars += dim; // update counter - is replaced by
199            final int _temp = unwrap(consumed_chars) + dim;
200            wrap(consumed_chars, _temp);
201            return new ArrayType(t, dim);
202        } else { // type == T_REFERENCE
203            // Utility.signatureToString understands how to parse
204            // generic types.
205            final String parsedSignature = Utility.signatureToString(signature, false);
206            wrap(consumed_chars, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed
207            return ObjectType.getInstance(parsedSignature.replace('/', '.'));
208        }
209    }
210
211    /**
212     * Convert return value of a method (signature) to a Type object.
213     *
214     * @param signature signature string such as (Ljava/lang/String;)V
215     * @return return type
216     */
217    public static Type getReturnType(final String signature) {
218        try {
219            // Read return type after `)'
220            final int index = signature.lastIndexOf(')') + 1;
221            return getType(signature.substring(index));
222        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
223            throw new ClassFormatException("Invalid method signature: " + signature, e);
224        }
225    }
226
227    /**
228     * Convert arguments of a method (signature) to an array of Type objects.
229     *
230     * @param signature signature string such as (Ljava/lang/String;)V
231     * @return array of argument types
232     */
233    public static Type[] getArgumentTypes(final String signature) {
234        final List<Type> vec = new ArrayList<>();
235        int index;
236        Type[] types;
237        try { // Read all declarations between for `(' and `)'
238            if (signature.charAt(0) != '(') {
239                throw new ClassFormatException("Invalid method signature: " + signature);
240            }
241            index = 1; // current string position
242            while (signature.charAt(index) != ')') {
243                vec.add(getType(signature.substring(index)));
244                //corrected concurrent private static field acess
245                index += unwrap(consumed_chars); // update position
246            }
247        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
248            throw new ClassFormatException("Invalid method signature: " + signature, e);
249        }
250        types = new Type[vec.size()];
251        vec.toArray(types);
252        return types;
253    }
254
255    /**
256     * Convert runtime java.lang.Class to BCEL Type object.
257     *
258     * @param cl Java class
259     * @return corresponding Type object
260     */
261    public static Type getType(final java.lang.Class<?> cl) {
262        if (cl == null) {
263            throw new IllegalArgumentException("Class must not be null");
264        }
265        /* That's an amzingly easy case, because getName() returns
266         * the signature. That's what we would have liked anyway.
267         */
268        if (cl.isArray()) {
269            return getType(cl.getName());
270        } else if (cl.isPrimitive()) {
271            if (cl == Integer.TYPE) {
272                return INT;
273            } else if (cl == Void.TYPE) {
274                return VOID;
275            } else if (cl == Double.TYPE) {
276                return DOUBLE;
277            } else if (cl == Float.TYPE) {
278                return FLOAT;
279            } else if (cl == Boolean.TYPE) {
280                return BOOLEAN;
281            } else if (cl == Byte.TYPE) {
282                return BYTE;
283            } else if (cl == Short.TYPE) {
284                return SHORT;
285            } else if (cl == Byte.TYPE) {
286                return BYTE;
287            } else if (cl == Long.TYPE) {
288                return LONG;
289            } else if (cl == Character.TYPE) {
290                return CHAR;
291            } else {
292                throw new IllegalStateException("Ooops, what primitive type is " + cl);
293            }
294        } else { // "Real" class
295            return ObjectType.getInstance(cl.getName());
296        }
297    }
298
299    /**
300     * Convert runtime java.lang.Class[] to BCEL Type objects.
301     *
302     * @param classes an array of runtime class objects
303     * @return array of corresponding Type objects
304     */
305    public static Type[] getTypes(final java.lang.Class<?>[] classes) {
306        final Type[] ret = new Type[classes.length];
307        for (int i = 0; i < ret.length; i++) {
308            ret[i] = getType(classes[i]);
309        }
310        return ret;
311    }
312
313    public static String getSignature(final java.lang.reflect.Method meth) {
314        final StringBuilder sb = new StringBuilder("(");
315        final Class<?>[] params = meth.getParameterTypes(); // avoid clone
316        for (final Class<?> param : params) {
317            sb.append(getType(param).getSignature());
318        }
319        sb.append(")");
320        sb.append(getType(meth.getReturnType()).getSignature());
321        return sb.toString();
322    }
323
324    static int size(final int coded) {
325        return coded & 3;
326    }
327
328    static int consumed(final int coded) {
329        return coded >> 2;
330    }
331
332    static int encode(final int size, final int consumed) {
333        return consumed << 2 | size;
334    }
335
336    static int getArgumentTypesSize(final String signature) {
337        int res = 0;
338        int index;
339        try { // Read all declarations between for `(' and `)'
340            if (signature.charAt(0) != '(') {
341                throw new ClassFormatException("Invalid method signature: " + signature);
342            }
343            index = 1; // current string position
344            while (signature.charAt(index) != ')') {
345                final int coded = getTypeSize(signature.substring(index));
346                res += size(coded);
347                index += consumed(coded);
348            }
349        } catch (final StringIndexOutOfBoundsException e) { // Should never occur
350            throw new ClassFormatException("Invalid method signature: " + signature, e);
351        }
352        return res;
353    }
354
355    static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException {
356        final byte type = Utility.typeOfSignature(signature);
357        if (type <= Const.T_VOID) {
358            return encode(BasicType.getType(type).getSize(), 1);
359        } else if (type == Const.T_ARRAY) {
360            int dim = 0;
361            do { // Count dimensions
362                dim++;
363            } while (signature.charAt(dim) == '[');
364            // Recurse, but just once, if the signature is ok
365            final int consumed = consumed(getTypeSize(signature.substring(dim)));
366            return encode(1, dim + consumed);
367        } else { // type == T_REFERENCE
368            final int index = signature.indexOf(';'); // Look for closing `;'
369            if (index < 0) {
370                throw new ClassFormatException("Invalid signature: " + signature);
371            }
372            return encode(1, index + 1);
373        }
374    }
375
376    static int getReturnTypeSize(final String signature) {
377        final int index = signature.lastIndexOf(')') + 1;
378        return Type.size(getTypeSize(signature.substring(index)));
379    }
380
381
382    /*
383     * Currently only used by the ArrayType constructor.
384     * The signature has a complicated dependency on other parameter
385     * so it's tricky to do it in a call to the super ctor.
386     */
387    void setSignature(final String signature) {
388        this.signature = signature;
389    }
390}
391