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