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  */
  20 package com.sun.org.apache.bcel.internal.generic;
  21 
  22 import java.util.ArrayList;
  23 import java.util.List;
  24 
  25 import com.sun.org.apache.bcel.internal.Const;
  26 import com.sun.org.apache.bcel.internal.classfile.ClassFormatException;
  27 import 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  */
  35 public 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 }