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