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