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 // @since 6.0 no longer final 227 public static Type getType(final String signature) throws StringIndexOutOfBoundsException { 228 final byte type = Utility.typeOfSignature(signature); 229 if (type <= Const.T_VOID) { 230 // corrected concurrent private static field acess 231 wrap(CONSUMED_CHARS, 1); 232 return BasicType.getType(type); 233 } 234 if (type != Const.T_ARRAY) { // type == T_REFERENCE 235 // Utility.typeSignatureToString understands how to parse generic types. 236 final String parsedSignature = Utility.typeSignatureToString(signature, false); 237 wrap(CONSUMED_CHARS, parsedSignature.length() + 2); // "Lblabla;" `L' and `;' are removed 238 return ObjectType.getInstance(Utility.pathToPackage(parsedSignature)); 239 } 240 int dim = 0; 241 do { // Count dimensions 242 dim++; 243 } while (signature.charAt(dim) == '['); 244 // Recurse, but just once, if the signature is ok 245 final Type t = getType(signature.substring(dim)); 246 // corrected concurrent private static field acess 247 // consumed_chars += dim; // update counter - is replaced by 248 final int temp = unwrap(CONSUMED_CHARS) + dim; 249 wrap(CONSUMED_CHARS, temp); 250 return new ArrayType(t, dim); 251 } 252 253 /** 254 * Convert runtime java.lang.Class[] to BCEL Type objects. 255 * 256 * @param classes an array of runtime class objects 257 * @return array of corresponding Type objects 258 */ 259 public static Type[] getTypes(final Class<?>[] classes) { 260 final Type[] ret = new Type[classes.length]; 261 Arrays.setAll(ret, i -> getType(classes[i])); 262 return ret; 263 } 264 265 static int getTypeSize(final String signature) throws StringIndexOutOfBoundsException { 266 final byte type = Utility.typeOfSignature(signature); 267 if (type <= Const.T_VOID) { 268 return encode(BasicType.getType(type).getSize(), 1); 269 } 270 if (type == Const.T_ARRAY) { 271 int dim = 0; 272 do { // Count dimensions 273 dim++; 274 } while (signature.charAt(dim) == '['); 275 // Recurse, but just once, if the signature is ok 276 final int consumed = consumed(getTypeSize(signature.substring(dim))); 277 return encode(1, dim + consumed); 278 } 279 final int index = signature.indexOf(';'); // Look for closing `;' 280 if (index < 0) { 281 throw new ClassFormatException("Invalid signature: " + signature); 282 } 283 return encode(1, index + 1); 284 } 285 286 static int size(final int coded) { 287 return coded & 3; 288 } 289 290 private static int unwrap(final ThreadLocal<Integer> tl) { 291 return tl.get().intValue(); 292 } 293 294 private static void wrap(final ThreadLocal<Integer> tl, final int value) { 295 tl.set(Integer.valueOf(value)); 296 } 297 298 /** 299 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 300 */ 301 @Deprecated 302 protected byte type; // TODO should be final (and private) 303 304 /** 305 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter 306 */ 307 @Deprecated 308 protected String signature; // signature for the type TODO should be private 309 310 protected Type(final byte t, final String s) { 311 type = t; 312 signature = s; 313 } 314 315 /** 316 * @return whether the Types are equal 317 */ 318 @Override 319 public boolean equals(final Object o) { 320 if (o instanceof Type) { 321 final Type t = (Type) o; 322 return type == t.type && signature.equals(t.signature); 323 } 324 return false; 325 } 326 327 /** 328 * @return signature for given type. 329 */ 330 public String getSignature() { 331 return signature; 332 } 333 334 /** 335 * @return stack size of this type (2 for long and double, 0 for void, 1 otherwise) 336 */ 337 public int getSize() { 338 switch (type) { 339 case Const.T_DOUBLE: 340 case Const.T_LONG: 341 return 2; 342 case Const.T_VOID: 343 return 0; 344 default: 345 return 1; 346 } 347 } 348 349 /** 350 * @return type as defined in Constants 351 */ 352 public byte getType() { 353 return type; 354 } 355 356 /** 357 * @return hashcode of Type 358 */ 359 @Override 360 public int hashCode() { 361 return type ^ signature.hashCode(); 362 } 363 364 /** 365 * boolean, short and char variable are considered as int in the stack or local variable area. Returns {@link Type#INT} 366 * for {@link Type#BOOLEAN}, {@link Type#SHORT} or {@link Type#CHAR}, otherwise returns the given type. 367 * 368 * @since 6.0 369 */ 370 public Type normalizeForStackOrLocal() { 371 if (this == Type.BOOLEAN || this == Type.BYTE || this == Type.SHORT || this == Type.CHAR) { 372 return Type.INT; 373 } 374 return this; 375 } 376 377 /* 378 * Currently only used by the ArrayType constructor. The signature has a complicated dependency on other parameter so 379 * it's tricky to do it in a call to the super ctor. 380 */ 381 void setSignature(final String signature) { 382 this.signature = signature; 383 } 384 385 /** 386 * @return Type string, e.g. `int[]' 387 */ 388 @Override 389 public String toString() { 390 return this.equals(Type.NULL) || type >= Const.T_UNKNOWN ? signature : Utility.signatureToString(signature, false); 391 } 392}