1 /*
   2  * Copyright (c) 2017, 2020, 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
  31  * such as int, object types like String and array types, e.g. int[]
  32  *
  33  * @LastModified: Jan 2020
  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 
  63     protected Type(final byte t, final String s) {
  64         type = t;
  65         signature = s;
  66     }
  67 
  68 
  69     /**
  70      * @return hashcode of Type
  71      */
  72     @Override
  73     public int hashCode() {
  74         return type ^ signature.hashCode();
  75     }
  76 
  77 
  78     /**
  79      * @return whether the Types are equal
  80      */
  81     @Override
  82     public boolean equals(final Object o) {
  83           if (o instanceof Type) {
  84               final Type t = (Type)o;
  85               return (type == t.type) && signature.equals(t.signature);
  86           }
  87           return false;
  88     }
  89 
  90 
  91     /**
  92      * @return signature for given type.
  93      */
  94     public String getSignature() {
  95         return signature;
  96     }
  97 
  98 
  99     /**
 100      * @return type as defined in Constants
 101      */
 102     public byte getType() {
 103         return type;
 104     }
 105 
 106     /**
 107      * boolean, short and char variable are considered as int in the stack or local variable area.
 108      * Returns {@link Type#INT} for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise
 109      * returns the given type.
 110      * @since 6.0
 111      */
 112     public Type normalizeForStackOrLocal() {
 113         if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) {
 114             return Type.INT;
 115         }
 116         return this;
 117     }
 118 
 119     /**
 120      * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise)
 121      */
 122     public int getSize() {
 123         switch (type) {
 124             case Const.T_DOUBLE:
 125             case Const.T_LONG:
 126                 return 2;
 127             case Const.T_VOID:
 128                 return 0;
 129             default:
 130                 return 1;
 131         }
 132     }
 133 
 134 
 135     /**
 136      * @return Type string, e.g. `int[]'
 137      */
 138     @Override
 139     public String toString() {
 140         return ((this.equals(Type.NULL) || (type >= Const.T_UNKNOWN))) ? signature : Utility
 141                 .signatureToString(signature, false);
 142     }
 143 
 144 
 145     /**
 146      * Convert type to Java method signature, e.g. int[] f(java.lang.String x)
 147      * becomes (Ljava/lang/String;)[I
 148      *
 149      * @param return_type what the method returns
 150      * @param arg_types what are the argument types
 151      * @return method signature for given type(s).
 152      */
 153     public static String getMethodSignature( final Type return_type, final Type[] arg_types ) {
 154         final StringBuilder buf = new StringBuilder("(");
 155         if (arg_types != null) {
 156             for (final Type arg_type : arg_types) {
 157                 buf.append(arg_type.getSignature());
 158             }
 159         }
 160         buf.append(')');
 161         buf.append(return_type.getSignature());
 162         return buf.toString();
 163     }
 164 
 165     private static final ThreadLocal<Integer> consumed_chars = new ThreadLocal<Integer>() {
 166 
 167         @Override
 168         protected Integer initialValue() {
 169             return Integer.valueOf(0);
 170         }
 171     };//int consumed_chars=0; // Remember position in string, see getArgumentTypes
 172 
 173 
 174     private static int unwrap( final ThreadLocal<Integer> tl ) {
 175         return tl.get().intValue();
 176     }
 177 
 178 
 179     private static void wrap( final ThreadLocal<Integer> tl, final int value ) {
 180         tl.set(Integer.valueOf(value));
 181     }
 182 
 183 
 184     /**
 185      * Convert signature to a Type object.
 186      * @param signature signature string such as Ljava/lang/String;
 187      * @return type object
 188      */
 189     // @since 6.0 no longer final
 190     public static Type getType( final String signature ) throws StringIndexOutOfBoundsException {
 191         final byte type = Utility.typeOfSignature(signature);
 192         if (type <= Const.T_VOID) {
 193             //corrected concurrent private static field acess
 194             wrap(consumed_chars, 1);
 195             return BasicType.getType(type);
 196         } else if (type == Const.T_ARRAY) {
 197             int dim = 0;
 198             do { // Count dimensions
 199                 dim++;
 200             } while (signature.charAt(dim) == '[');
 201             // Recurse, but just once, if the signature is ok
 202             final Type t = getType(signature.substring(dim));
 203             //corrected concurrent private static field acess
 204             //  consumed_chars += dim; // update counter - is replaced by
 205             final int _temp = unwrap(consumed_chars) + dim;
 206             wrap(consumed_chars, _temp);
 207             return new ArrayType(t, dim);
 208         } else { // type == T_REFERENCE
 209             // Utility.typeSignatureToString understands how to parse generic types.
 210             final String parsedSignature = Utility.typeSignatureToString(signature, false);
 211             wrap(consumed_chars, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed
 212             return ObjectType.getInstance(parsedSignature.replace('/', '.'));
 213         }
 214     }
 215 
 216 
 217     /**
 218      * Convert return value of a method (signature) to a Type object.
 219      *
 220      * @param signature signature string such as (Ljava/lang/String;)V
 221      * @return return type
 222      */
 223     public static Type getReturnType( final String signature ) {
 224         try {
 225             // Read return type after `)'
 226             final int index = signature.lastIndexOf(')') + 1;
 227             return getType(signature.substring(index));
 228         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 229             throw new ClassFormatException("Invalid method signature: " + signature, e);
 230         }
 231     }
 232 
 233 
 234     /**
 235      * Convert arguments of a method (signature) to an array of Type objects.
 236      * @param signature signature string such as (Ljava/lang/String;)V
 237      * @return array of argument types
 238      */
 239     public static Type[] getArgumentTypes( final String signature ) {
 240         final List<Type> vec = new ArrayList<>();
 241         int index;
 242         Type[] types;
 243         try {
 244             // Skip any type arguments to read argument declarations between `(' and `)'
 245             index = signature.indexOf('(') + 1;
 246             if (index <= 0) {
 247                 throw new ClassFormatException("Invalid method signature: " + signature);
 248             }
 249             while (signature.charAt(index) != ')') {
 250                 vec.add(getType(signature.substring(index)));
 251                 //corrected concurrent private static field acess
 252                 index += unwrap(consumed_chars); // update position
 253             }
 254         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 255             throw new ClassFormatException("Invalid method signature: " + signature, e);
 256         }
 257         types = new Type[vec.size()];
 258         vec.toArray(types);
 259         return types;
 260     }
 261 
 262 
 263     /** Convert runtime java.lang.Class to BCEL Type object.
 264      * @param cl Java class
 265      * @return corresponding Type object
 266      */
 267     public static Type getType( final java.lang.Class<?> cl ) {
 268         if (cl == null) {
 269             throw new IllegalArgumentException("Class must not be null");
 270         }
 271         /* That's an amzingly easy case, because getName() returns
 272          * the signature. That's what we would have liked anyway.
 273          */
 274         if (cl.isArray()) {
 275             return getType(cl.getName());
 276         } else if (cl.isPrimitive()) {
 277             if (cl == Integer.TYPE) {
 278                 return INT;
 279             } else if (cl == Void.TYPE) {
 280                 return VOID;
 281             } else if (cl == Double.TYPE) {
 282                 return DOUBLE;
 283             } else if (cl == Float.TYPE) {
 284                 return FLOAT;
 285             } else if (cl == Boolean.TYPE) {
 286                 return BOOLEAN;
 287             } else if (cl == Byte.TYPE) {
 288                 return BYTE;
 289             } else if (cl == Short.TYPE) {
 290                 return SHORT;
 291             } else if (cl == Byte.TYPE) {
 292                 return BYTE;
 293             } else if (cl == Long.TYPE) {
 294                 return LONG;
 295             } else if (cl == Character.TYPE) {
 296                 return CHAR;
 297             } else {
 298                 throw new IllegalStateException("Ooops, what primitive type is " + cl);
 299             }
 300         } else { // "Real" class
 301             return ObjectType.getInstance(cl.getName());
 302         }
 303     }
 304 
 305 
 306     /**
 307      * Convert runtime java.lang.Class[] to BCEL Type objects.
 308      * @param classes an array of runtime class objects
 309      * @return array of corresponding Type objects
 310      */
 311     public static Type[] getTypes( final java.lang.Class<?>[] classes ) {
 312         final Type[] ret = new Type[classes.length];
 313         for (int i = 0; i < ret.length; i++) {
 314             ret[i] = getType(classes[i]);
 315         }
 316         return ret;
 317     }
 318 
 319 
 320     public static String getSignature( final java.lang.reflect.Method meth ) {
 321         final StringBuilder sb = new StringBuilder("(");
 322         final Class<?>[] params = meth.getParameterTypes(); // avoid clone
 323         for (final Class<?> param : params) {
 324             sb.append(getType(param).getSignature());
 325         }
 326         sb.append(")");
 327         sb.append(getType(meth.getReturnType()).getSignature());
 328         return sb.toString();
 329     }
 330 
 331     static int size(final int coded) {
 332         return coded & 3;
 333     }
 334 
 335     static int consumed(final int coded) {
 336         return coded >> 2;
 337     }
 338 
 339     static int encode(final int size, final int consumed) {
 340         return consumed << 2 | size;
 341     }
 342 
 343     static int getArgumentTypesSize( final String signature ) {
 344         int res = 0;
 345         int index;
 346         try {
 347             // Skip any type arguments to read argument declarations between `(' and `)'
 348             index = signature.indexOf('(') + 1;
 349             if (index <= 0) {
 350                 throw new ClassFormatException("Invalid method signature: " + signature);
 351             }
 352             while (signature.charAt(index) != ')') {
 353                 final int coded = getTypeSize(signature.substring(index));
 354                 res += size(coded);
 355                 index += consumed(coded);
 356             }
 357         } catch (final StringIndexOutOfBoundsException e) { // Should never occur
 358             throw new ClassFormatException("Invalid method signature: " + signature, e);
 359         }
 360         return res;
 361     }
 362 
 363     static int getTypeSize( final String signature ) throws StringIndexOutOfBoundsException {
 364         final byte type = Utility.typeOfSignature(signature);
 365         if (type <= Const.T_VOID) {
 366             return encode(BasicType.getType(type).getSize(), 1);
 367         } else if (type == Const.T_ARRAY) {
 368             int dim = 0;
 369             do { // Count dimensions
 370                 dim++;
 371             } while (signature.charAt(dim) == '[');
 372             // Recurse, but just once, if the signature is ok
 373             final int consumed = consumed(getTypeSize(signature.substring(dim)));
 374             return encode(1, dim + consumed);
 375         } else { // type == T_REFERENCE
 376             final int index = signature.indexOf(';'); // Look for closing `;'
 377             if (index < 0) {
 378                 throw new ClassFormatException("Invalid signature: " + signature);
 379             }
 380             return encode(1, index + 1);
 381         }
 382     }
 383 
 384 
 385     static int getReturnTypeSize(final String signature) {
 386         final int index = signature.lastIndexOf(')') + 1;
 387         return Type.size(getTypeSize(signature.substring(index)));
 388     }
 389 
 390 
 391     /*
 392      * Currently only used by the ArrayType constructor.
 393      * The signature has a complicated dependency on other parameter
 394      * so it's tricky to do it in a call to the super ctor.
 395      */
 396     void setSignature(final String signature) {
 397         this.signature = signature;
 398     }
 399 }