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