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