1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.bytecode.stackmap; 17 18 import javassist.ClassPool; 19 import javassist.CtClass; 20 import javassist.NotFoundException; 21 import javassist.bytecode.ConstPool; 22 import javassist.bytecode.StackMapTable; 23 import javassist.bytecode.BadBytecode; 24 import java.util.ArrayList; 25 26 public abstract class TypeData { 27 /* Memo: 28 * array type is a subtype of Cloneable and Serializable 29 */ 30 31 protected TypeData() {} 32 33 public abstract void merge(TypeData neighbor); 34 35 /** 36 * Sets the type name of this object type. If the given type name is 37 * a subclass of the current type name, then the given name becomes 38 * the name of this object type. 39 * 40 * @param className dot-separated name unless the type is an array type. 41 */ 42 static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { 43 if (td == TypeTag.TOP) 44 throw new BadBytecode("unset variable"); 45 else 46 td.setType(className, cp); 47 } 48 49 public abstract boolean equals(Object obj); 50 51 public abstract int getTypeTag(); 52 public abstract int getTypeData(ConstPool cp); 53 54 /* 55 * See UninitData.getSelf(). 56 */ 57 public TypeData getSelf() { return this; } 58 59 /* An operand value is copied when it is stored in a 60 * local variable. 61 */ 62 public abstract TypeData copy(); 63 64 public abstract boolean isObjectType(); 65 public boolean is2WordType() { return false; } 66 public boolean isNullType() { return false; } 67 68 public abstract String getName() throws BadBytecode; 69 protected abstract void setType(String s, ClassPool cp) throws BadBytecode; 70 public abstract void evalExpectedType(ClassPool cp) throws BadBytecode; 71 public abstract String getExpected() throws BadBytecode; 72 73 /** 74 * Primitive types. 75 */ 76 protected static class BasicType extends TypeData { 77 private String name; 78 private int typeTag; 79 80 public BasicType(String type, int tag) { 81 name = type; 82 typeTag = tag; 83 } 84 85 public void merge(TypeData neighbor) {} 86 87 public boolean equals(Object obj) { 88 return this == obj; 89 } 90 91 public int getTypeTag() { return typeTag; } 92 public int getTypeData(ConstPool cp) { return 0; } 93 94 public boolean isObjectType() { return false; } 95 96 public boolean is2WordType() { 97 return typeTag == StackMapTable.LONG 98 || typeTag == StackMapTable.DOUBLE; 99 } 100 101 public TypeData copy() { 102 return this; 103 } 104 105 public void evalExpectedType(ClassPool cp) throws BadBytecode {} 106 107 public String getExpected() throws BadBytecode { 108 return name; 109 } 110 111 public String getName() { 112 return name; 113 } 114 115 protected void setType(String s, ClassPool cp) throws BadBytecode { 116 throw new BadBytecode("conflict: " + name + " and " + s); 117 } 118 119 public String toString() { return name; } 120 } 121 122 protected static abstract class TypeName extends TypeData { 123 protected ArrayList equivalences; 124 125 protected String expectedName; 126 private CtClass cache; 127 private boolean evalDone; 128 129 protected TypeName() { 130 equivalences = new ArrayList(); 131 equivalences.add(this); 132 expectedName = null; 133 cache = null; 134 evalDone = false; 135 } 136 137 public void merge(TypeData neighbor) { 138 if (this == neighbor) 139 return; 140 141 if (!(neighbor instanceof TypeName)) 142 return; // neighbor might be UninitData 143 144 TypeName neighbor2 = (TypeName)neighbor; 145 ArrayList list = equivalences; 146 ArrayList list2 = neighbor2.equivalences; 147 if (list == list2) 148 return; 149 150 int n = list2.size(); 151 for (int i = 0; i < n; i++) { 152 TypeName tn = (TypeName)list2.get(i); 153 add(list, tn); 154 tn.equivalences = list; 155 } 156 } 157 158 private static void add(ArrayList list, TypeData td) { 159 int n = list.size(); 160 for (int i = 0; i < n; i++) 161 if (list.get(i) == td) 162 return; 163 164 list.add(td); 165 } 166 167 /* NullType overrides this method. 168 */ 169 public int getTypeTag() { return StackMapTable.OBJECT; } 170 171 public int getTypeData(ConstPool cp) { 172 String type; 173 try { 174 type = getExpected(); 175 } catch (BadBytecode e) { 176 throw new RuntimeException("fatal error: ", e); 177 } 178 179 return getTypeData2(cp, type); 180 } 181 182 /* NullType overrides this method. 183 */ 184 protected int getTypeData2(ConstPool cp, String type) { 185 return cp.addClassInfo(type); 186 } 187 188 public boolean equals(Object obj) { 189 if (obj instanceof TypeName) { 190 try { 191 TypeName tn = (TypeName)obj; 192 return getExpected().equals(tn.getExpected()); 193 } 194 catch (BadBytecode e) {} 195 } 196 197 return false; 198 } 199 200 public boolean isObjectType() { return true; } 201 202 protected void setType(String typeName, ClassPool cp) throws BadBytecode { 203 if (update(cp, expectedName, typeName)) 204 expectedName = typeName; 205 } 206 207 public void evalExpectedType(ClassPool cp) throws BadBytecode { 208 if (this.evalDone) 209 return; 210 211 ArrayList equiv = this.equivalences; 212 int n = equiv.size(); 213 String name = evalExpectedType2(equiv, n); 214 if (name == null) { 215 name = this.expectedName; 216 for (int i = 0; i < n; i++) { 217 TypeData td = (TypeData)equiv.get(i); 218 if (td instanceof TypeName) { 219 TypeName tn = (TypeName)td; 220 if (update(cp, name, tn.expectedName)) 221 name = tn.expectedName; 222 } 223 } 224 } 225 226 for (int i = 0; i < n; i++) { 227 TypeData td = (TypeData)equiv.get(i); 228 if (td instanceof TypeName) { 229 TypeName tn = (TypeName)td; 230 tn.expectedName = name; 231 tn.cache = null; 232 tn.evalDone = true; 233 } 234 } 235 } 236 237 private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode { 238 String origName = null; 239 for (int i = 0; i < n; i++) { 240 TypeData td = (TypeData)equiv.get(i); 241 if (!td.isNullType()) 242 if (origName == null) 243 origName = td.getName(); 244 else if (!origName.equals(td.getName())) 245 return null; 246 } 247 248 return origName; 249 } 250 251 protected boolean isTypeName() { return true; } 252 253 private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode { 254 if (typeName == null) 255 return false; 256 else if (oldName == null) 257 return true; 258 else if (oldName.equals(typeName)) 259 return false; 260 else if (typeName.charAt(0) == '[' 261 && oldName.equals("[Ljava.lang.Object;")) { 262 /* this rule is not correct but Tracer class sets the type 263 of the operand of arraylength to java.lang.Object[]. 264 Thus, int[] etc. must be a subtype of java.lang.Object[]. 265 */ 266 return true; 267 } 268 269 try { 270 if (cache == null) 271 cache = cp.get(oldName); 272 273 CtClass cache2 = cp.get(typeName); 274 if (cache2.subtypeOf(cache)) { 275 cache = cache2; 276 return true; 277 } 278 else 279 return false; 280 } 281 catch (NotFoundException e) { 282 throw new BadBytecode("cannot find " + e.getMessage()); 283 } 284 } 285 286 /* See also NullType.getExpected(). 287 */ 288 public String getExpected() throws BadBytecode { 289 ArrayList equiv = equivalences; 290 if (equiv.size() == 1) 291 return getName(); 292 else { 293 String en = expectedName; 294 if (en == null) 295 return "java.lang.Object"; 296 else 297 return en; 298 } 299 } 300 301 public String toString() { 302 try { 303 String en = expectedName; 304 if (en != null) 305 return en; 306 307 String name = getName(); 308 if (equivalences.size() == 1) 309 return name; 310 else 311 return name + "?"; 312 } 313 catch (BadBytecode e) { 314 return "<" + e.getMessage() + ">"; 315 } 316 } 317 } 318 319 /** 320 * Type data for OBJECT. 321 */ 322 public static class ClassName extends TypeName { 323 private String name; // dot separated. null if this object is a copy of another. 324 325 public ClassName(String n) { 326 name = n; 327 } 328 329 public TypeData copy() { 330 return new ClassName(name); 331 } 332 333 public String getName() { // never returns null. 334 return name; 335 } 336 } 337 338 /** 339 * Type data for NULL or OBJECT. 340 * The types represented by the instances of this class are 341 * initially NULL but will be OBJECT. 342 */ 343 public static class NullType extends ClassName { 344 public NullType() { 345 super("null"); // type name 346 } 347 348 public TypeData copy() { 349 return new NullType(); 350 } 351 352 public boolean isNullType() { return true; } 353 354 public int getTypeTag() { 355 try { 356 if ("null".equals(getExpected())) 357 return StackMapTable.NULL; 358 else 359 return super.getTypeTag(); 360 } 361 catch (BadBytecode e) { 362 throw new RuntimeException("fatal error: ", e); 363 } 364 } 365 366 protected int getTypeData2(ConstPool cp, String type) { 367 if ("null".equals(type)) 368 return 0; 369 else 370 return super.getTypeData2(cp, type); 371 } 372 373 public String getExpected() throws BadBytecode { 374 String en = expectedName; 375 if (en == null) { 376 // ArrayList equiv = equivalences; 377 // if (equiv.size() == 1) 378 // return getName(); 379 // else 380 return "java.lang.Object"; 381 } 382 else 383 return en; 384 } 385 } 386 387 /** 388 * Type data for OBJECT if the type is an object type and is 389 * derived as an element type from an array type by AALOAD. 390 */ 391 public static class ArrayElement extends TypeName { 392 TypeData array; 393 394 public ArrayElement(TypeData a) { // a is never null 395 array = a; 396 } 397 398 public TypeData copy() { 399 return new ArrayElement(array); 400 } 401 402 protected void setType(String typeName, ClassPool cp) throws BadBytecode { 403 super.setType(typeName, cp); 404 array.setType(getArrayType(typeName), cp); 405 } 406 407 public String getName() throws BadBytecode { 408 String name = array.getName(); 409 if (name.length() > 1 && name.charAt(0) == '[') { 410 char c = name.charAt(1); 411 if (c == 'L') 412 return name.substring(2, name.length() - 1).replace('/', '.'); 413 else if (c == '[') 414 return name.substring(1); 415 } 416 417 throw new BadBytecode("bad array type for AALOAD: " 418 + name); 419 } 420 421 public static String getArrayType(String elementType) { 422 if (elementType.charAt(0) == '[') 423 return "[" + elementType; 424 else 425 return "[L" + elementType.replace('.', '/') + ";"; 426 } 427 428 public static String getElementType(String arrayType) { 429 char c = arrayType.charAt(1); 430 if (c == 'L') 431 return arrayType.substring(2, arrayType.length() - 1).replace('/', '.'); 432 else if (c == '[') 433 return arrayType.substring(1); 434 else 435 return arrayType; 436 } 437 } 438 439 /** 440 * Type data for UNINIT. 441 */ 442 public static class UninitData extends TypeData { 443 String className; 444 int offset; 445 boolean initialized; 446 447 UninitData(int offset, String className) { 448 this.className = className; 449 this.offset = offset; 450 this.initialized = false; 451 } 452 453 public void merge(TypeData neighbor) {} 454 455 public int getTypeTag() { return StackMapTable.UNINIT; } 456 public int getTypeData(ConstPool cp) { return offset; } 457 458 public boolean equals(Object obj) { 459 if (obj instanceof UninitData) { 460 UninitData ud = (UninitData)obj; 461 return offset == ud.offset && className.equals(ud.className); 462 } 463 else 464 return false; 465 } 466 467 public TypeData getSelf() { 468 if (initialized) 469 return copy(); 470 else 471 return this; 472 } 473 474 public TypeData copy() { 475 return new ClassName(className); 476 } 477 478 public boolean isObjectType() { return true; } 479 480 protected void setType(String typeName, ClassPool cp) throws BadBytecode { 481 initialized = true; 482 } 483 484 public void evalExpectedType(ClassPool cp) throws BadBytecode {} 485 486 public String getName() { 487 return className; 488 } 489 490 public String getExpected() { return className; } 491 492 public String toString() { return "uninit:" + className + "@" + offset; } 493 } 494 495 public static class UninitThis extends UninitData { 496 UninitThis(String className) { 497 super(-1, className); 498 } 499 500 public int getTypeTag() { return StackMapTable.THIS; } 501 public int getTypeData(ConstPool cp) { return 0; } 502 503 public boolean equals(Object obj) { 504 return obj instanceof UninitThis; 505 } 506 507 public String toString() { return "uninit:this"; } 508 } 509 } 510