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