001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.generic; 020 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.List; 024import java.util.Objects; 025 026import org.apache.bcel.Const; 027import org.apache.bcel.classfile.ClassFormatException; 028import org.apache.bcel.classfile.InvalidMethodSignatureException; 029import org.apache.bcel.classfile.Utility; 030import org.apache.commons.lang3.StringUtils; 031import org.apache.commons.lang3.Strings; 032 033/** 034 * Abstract super class for all possible Java types, namely basic types such as int, object types like String and array 035 * types, for example int[] 036 */ 037public abstract class Type { 038 039 /** 040 * Predefined constants 041 */ 042 public static final BasicType VOID = new BasicType(Const.T_VOID); 043 044 /** Predefined constant for boolean type. */ 045 public static final BasicType BOOLEAN = new BasicType(Const.T_BOOLEAN); 046 047 /** Predefined constant for int type. */ 048 public static final BasicType INT = new BasicType(Const.T_INT); 049 050 /** Predefined constant for short type. */ 051 public static final BasicType SHORT = new BasicType(Const.T_SHORT); 052 053 /** Predefined constant for byte type. */ 054 public static final BasicType BYTE = new BasicType(Const.T_BYTE); 055 056 /** Predefined constant for long type. */ 057 public static final BasicType LONG = new BasicType(Const.T_LONG); 058 059 /** Predefined constant for double type. */ 060 public static final BasicType DOUBLE = new BasicType(Const.T_DOUBLE); 061 062 /** Predefined constant for float type. */ 063 public static final BasicType FLOAT = new BasicType(Const.T_FLOAT); 064 065 /** Predefined constant for char type. */ 066 public static final BasicType CHAR = new BasicType(Const.T_CHAR); 067 068 /** Predefined constant for Object type. */ 069 public static final ObjectType OBJECT = new ObjectType("java.lang.Object"); 070 071 /** Predefined constant for Class type. */ 072 public static final ObjectType CLASS = new ObjectType("java.lang.Class"); 073 074 /** Predefined constant for String type. */ 075 public static final ObjectType STRING = new ObjectType("java.lang.String"); 076 077 /** Predefined constant for StringBuffer type. */ 078 public static final ObjectType STRINGBUFFER = new ObjectType("java.lang.StringBuffer"); 079 080 /** Predefined constant for Throwable type. */ 081 public static final ObjectType THROWABLE = new ObjectType("java.lang.Throwable"); 082 083 /** 084 * Empty array. 085 */ 086 public static final Type[] NO_ARGS = {}; 087 /** Predefined constant for null type. */ 088 public static final ReferenceType NULL = new ReferenceType() { 089 }; 090 091 /** Predefined constant for unknown type. */ 092 public static final Type UNKNOWN = new Type(Const.T_UNKNOWN, "<unknown object>") { 093 }; 094 095 private static final ThreadLocal<Integer> CONSUMED_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0)); 096 097 // int consumed_chars=0; // Remember position in string, see getArgumentTypes 098 static int consumed(final int coded) { 099 return coded >> 2; 100 } 101 102 static int encode(final int size, final int consumed) { 103 return consumed << 2 | size; 104 } 105 106 /** 107 * Convert arguments of a method (signature) to an array of Type objects. 108 * 109 * @param signature signature string such as (Ljava/lang/String;)V. 110 * @return array of argument types. 111 */ 112 public static Type[] getArgumentTypes(final String signature) { 113 final List<Type> vec = new ArrayList<>(); 114 int index; 115 try { 116 // Skip any type arguments to read argument declarations between '(' and ')' 117 index = signature.indexOf('(') + 1; 118 if (index <= 0) { 119 throw new InvalidMethodSignatureException(signature); 120 } 121 while (signature.charAt(index) != ')') { 122 vec.add(getType(signature.substring(index))); 123 // corrected concurrent private static field access 124 index += unwrap(CONSUMED_CHARS); // update position 125 } 126 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 127 throw new InvalidMethodSignatureException(signature, e); 128 } 129 final Type[] types = new Type[vec.size()]; 130 vec.toArray(types); 131 return types; 132 } 133 134 static int getArgumentTypesSize(final String signature) { 135 int res = 0; 136 int index; 137 try { 138 // Skip any type arguments to read argument declarations between '(' and ')' 139 index = signature.indexOf('(') + 1; 140 if (index <= 0) { 141 throw new InvalidMethodSignatureException(signature); 142 } 143 while (signature.charAt(index) != ')') { 144 final int coded = getTypeSize(signature.substring(index)); 145 res += size(coded); 146 index += consumed(coded); 147 } 148 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 149 throw new InvalidMethodSignatureException(signature, e); 150 } 151 return res; 152 } 153 154 /** 155 * Convert type to Java method signature, for example int[] f(java.lang.String x) becomes (Ljava/lang/String;)[I 156 * 157 * @param returnType what the method returns. 158 * @param argTypes what are the argument types. 159 * @return method signature for given type(s). 160 */ 161 public static String getMethodSignature(final Type returnType, final Type[] argTypes) { 162 final StringBuilder buf = new StringBuilder("("); 163 if (argTypes != null) { 164 for (final Type argType : argTypes) { 165 buf.append(argType.getSignature()); 166 } 167 } 168 buf.append(')'); 169 buf.append(returnType.getSignature()); 170 return buf.toString(); 171 } 172 173 /** 174 * Convert return value of a method (signature) to a Type object. 175 * 176 * @param signature signature string such as (Ljava/lang/String;)V. 177 * @return return type. 178 */ 179 public static Type getReturnType(final String signature) { 180 try { 181 // Read return type after ')' 182 final int index = signature.lastIndexOf(')') + 1; 183 return getType(signature.substring(index)); 184 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 185 throw new InvalidMethodSignatureException(signature, e); 186 } 187 } 188 189 static int getReturnTypeSize(final String signature) { 190 final int index = signature.lastIndexOf(')') + 1; 191 return size(getTypeSize(signature.substring(index))); 192 } 193 194 /** 195 * Gets the signature for a method. 196 * 197 * @param meth The method. 198 * @return The method signature. 199 */ 200 public static String getSignature(final java.lang.reflect.Method meth) { 201 final StringBuilder sb = new StringBuilder("("); 202 final Class<?>[] params = meth.getParameterTypes(); // avoid clone 203 for (final Class<?> param : params) { 204 sb.append(getType(param).getSignature()); 205 } 206 sb.append(")"); 207 sb.append(getType(meth.getReturnType()).getSignature()); 208 return sb.toString(); 209 } 210 211 /** 212 * Convert runtime {@link Class} to BCEL Type object. 213 * 214 * @param cls Java class. 215 * @return corresponding Type object. 216 */ 217 public static Type getType(final Class<?> cls) { 218 Objects.requireNonNull(cls, "cls"); 219 /* 220 * That's an amazingly easy case, because getName() returns the signature. That's what we would have liked anyway. 221 */ 222 if (cls.isArray()) { 223 return getType(cls.getName()); 224 } 225 if (!cls.isPrimitive()) { // "Real" class 226 return ObjectType.getInstance(cls.getName()); 227 } 228 if (cls == Integer.TYPE) { 229 return INT; 230 } 231 if (cls == Void.TYPE) { 232 return VOID; 233 } 234 if (cls == Double.TYPE) { 235 return DOUBLE; 236 } 237 if (cls == Float.TYPE) { 238 return FLOAT; 239 } 240 if (cls == Boolean.TYPE) { 241 return BOOLEAN; 242 } 243 if (cls == Byte.TYPE) { 244 return BYTE; 245 } 246 if (cls == Short.TYPE) { 247 return SHORT; 248 } 249 if (cls == Long.TYPE) { 250 return LONG; 251 } 252 if (cls == Character.TYPE) { 253 return CHAR; 254 } 255 throw new IllegalStateException("Unknown primitive type " + cls); 256 } 257 258 /** 259 * Convert signature to a Type object. 260 * 261 * @param signature signature string such as Ljava/lang/String;. 262 * @return type object. 263 */ 264 public static Type getType(final String signature) throws StringIndexOutOfBoundsException { 265 final byte type = Utility.typeOfSignature(signature); 266 if (type <= Const.T_VOID) { 267 // corrected concurrent private static field access 268 wrap(CONSUMED_CHARS, 1); 269 return BasicType.getType(type); 270 } 271 if (type != Const.T_ARRAY) { // type == T_REFERENCE 272 // Utility.typeSignatureToString understands how to parse generic types. 273 final String parsedSignature = Utility.typeSignatureToString(signature, false); 274 wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" 'L' and ';' are removed 275 return ObjectType.getInstance(Utility.pathToPackage(parsedSignature)); 276 } 277 int dim = 0; 278 do { // Count dimensions 279 dim++; 280 } while (signature.charAt(dim) == '['); 281 // Recurse, but just once, if the signature is ok 282 final Type t = getType(signature.substring(dim)); 283 // corrected concurrent private static field access 284 // consumed_chars += dim; // update counter - is replaced by 285 final int temp = unwrap(CONSUMED_CHARS) + dim; 286 wrap(CONSUMED_CHARS, temp); 287 return new ArrayType(t, dim); 288 } 289 290 /** 291 * Convert runtime {@code java.lang.Class[]} to BCEL Type objects. 292 * 293 * @param classes An array of runtime class objects. 294 * @return array of corresponding Type objects. 295 */ 296 public static Type[] getTypes(final Class<?>[] classes) { 297 final Type[] ret = new Type[classes.length]; 298 Arrays.setAll(ret, i -> getType(classes[i])); 299 return ret; 300 } 301 302 static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException { 303 final byte type = Utility.typeOfSignature(signature); 304 if (type <= Const.T_VOID) { 305 return encode(BasicType.getType(type).getSize(), 1); 306 } 307 if (type == Const.T_ARRAY) { 308 int dim = 0; 309 do { // Count dimensions 310 dim++; 311 } while (signature.charAt(dim) == '['); 312 // Recurse, but just once, if the signature is ok 313 final int consumed = consumed(getTypeSize(signature.substring(dim))); 314 return encode(1, dim + consumed); 315 } 316 final int index = signature.indexOf(';'); // Look for closing ';' 317 if (index < 0) { 318 throw new ClassFormatException("Invalid signature: " + signature); 319 } 320 return encode(1, index + 1); 321 } 322 323 static String internalTypeNameToSignature(final String internalTypeName) { 324 if (StringUtils.isEmpty(internalTypeName) || Strings.CS.equalsAny(internalTypeName, Const.SHORT_TYPE_NAMES)) { 325 return internalTypeName; 326 } 327 switch (internalTypeName.charAt(0)) { 328 case '[': 329 return internalTypeName; 330 case 'L': 331 case 'T': 332 if (internalTypeName.charAt(internalTypeName.length() - 1) == ';') { 333 return internalTypeName; 334 } 335 return 'L' + internalTypeName + ';'; 336 default: 337 return 'L' + internalTypeName + ';'; 338 } 339 } 340 341 static int size(final int coded) { 342 return coded & 3; 343 } 344 345 private static int unwrap(final ThreadLocal<Integer> tl) { 346 return tl.get().intValue(); 347 } 348 349 private static void wrap(final ThreadLocal<Integer> tl, final int value) { 350 tl.set(Integer.valueOf(value)); 351 } 352 353 /** 354 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 355 */ 356 @Deprecated 357 protected byte type; // TODO should be final (and private) 358 359 /** 360 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 361 */ 362 @Deprecated 363 protected String signature; // signature for the type TODO should be private 364 365 /** 366 * Constructs a Type. 367 * 368 * @param type The type constant. 369 * @param signature The type signature. 370 */ 371 protected Type(final byte type, final String signature) { 372 this.type = type; 373 this.signature = signature; 374 } 375 376 /** 377 * @return whether the Types are equal. 378 */ 379 @Override 380 public boolean equals(final Object o) { 381 if (o instanceof Type) { 382 final Type t = (Type) o; 383 return type == t.type && signature.equals(t.signature); 384 } 385 return false; 386 } 387 388 /** 389 * Gets the class name. 390 * 391 * @return The class name. 392 */ 393 public String getClassName() { 394 return toString(); 395 } 396 397 /** 398 * Gets the signature for this type. 399 * 400 * @return signature for given type. 401 */ 402 public String getSignature() { 403 return signature; 404 } 405 406 /** 407 * Gets the stack size of this type. 408 * 409 * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise). 410 */ 411 public int getSize() { 412 switch (type) { 413 case Const.T_DOUBLE: 414 case Const.T_LONG: 415 return 2; 416 case Const.T_VOID: 417 return 0; 418 default: 419 return 1; 420 } 421 } 422 423 /** 424 * Gets the type as defined in Constants. 425 * 426 * @return type as defined in Constants. 427 */ 428 public byte getType() { 429 return type; 430 } 431 432 /** 433 * Gets the hash code of this Type. 434 * 435 * @return hash code of Type. 436 */ 437 @Override 438 public int hashCode() { 439 return type ^ signature.hashCode(); 440 } 441 442 /** 443 * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link #INT} 444 * for {@link #BOOLEAN}, {@link #SHORT} or {@link #CHAR}, otherwise returns the given type. 445 * 446 * @return The normalized type. 447 * @since 6.0 448 */ 449 public Type normalizeForStackOrLocal() { 450 if (this == BOOLEAN || this == BYTE || this == SHORT || this == CHAR) { 451 return INT; 452 } 453 return this; 454 } 455 456 /** 457 * @return Type string, for example 'int[]'. 458 */ 459 @Override 460 public String toString() { 461 return equals(NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false); 462 } 463}