1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999- 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 * or the Apache License Version 2.0. 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 */ 16 17 package javassist.bytecode.stackmap; 18 19 import java.util.ArrayList; 20 import java.util.HashSet; 21 import java.util.Iterator; 22 import java.util.List; 23 import java.util.Set; 24 25 import javassist.ClassPool; 26 import javassist.CtClass; 27 import javassist.NotFoundException; 28 import javassist.bytecode.BadBytecode; 29 import javassist.bytecode.ConstPool; 30 import javassist.bytecode.Descriptor; 31 import javassist.bytecode.StackMapTable; 32 33 public abstract class TypeData { 34 /* Memo: 35 * array type is a subtype of Cloneable and Serializable 36 */ 37 make(int size)38 public static TypeData[] make(int size) { 39 TypeData[] array = new TypeData[size]; 40 for (int i = 0; i < size; i++) 41 array[i] = TypeTag.TOP; 42 43 return array; 44 } 45 TypeData()46 protected TypeData() {} 47 48 /** 49 * Sets the type name of this object type. If the given type name is 50 * a subclass of the current type name, then the given name becomes 51 * the name of this object type. 52 * 53 * @param className dot-separated name unless the type is an array type. 54 */ 55 @SuppressWarnings("unused") setType(TypeData td, String className, ClassPool cp)56 private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { 57 td.setType(className, cp); 58 } 59 getTypeTag()60 public abstract int getTypeTag(); getTypeData(ConstPool cp)61 public abstract int getTypeData(ConstPool cp); 62 join()63 public TypeData join() { return new TypeVar(this); } 64 65 /** 66 * If the type is a basic type, this method normalizes the type 67 * and returns a BasicType object. Otherwise, it returns null. 68 */ isBasicType()69 public abstract BasicType isBasicType(); 70 is2WordType()71 public abstract boolean is2WordType(); 72 73 /** 74 * Returns false if getName() returns a valid type name. 75 */ isNullType()76 public boolean isNullType() { return false; } 77 isUninit()78 public boolean isUninit() { return false; } 79 eq(TypeData d)80 public abstract boolean eq(TypeData d); 81 getName()82 public abstract String getName(); setType(String s, ClassPool cp)83 public abstract void setType(String s, ClassPool cp) throws BadBytecode; 84 85 /** 86 * @param dim array dimension. It may be negative. 87 */ getArrayType(int dim)88 public abstract TypeData getArrayType(int dim) throws NotFoundException; 89 90 /** 91 * Depth-first search by Tarjan's algorithm 92 * 93 * @param order a node stack in the order in which nodes are visited. 94 * @param index the index used by the algorithm. 95 */ dfs(List<TypeData> order, int index, ClassPool cp)96 public int dfs(List<TypeData> order, int index, ClassPool cp) 97 throws NotFoundException 98 { 99 return index; 100 } 101 102 /** 103 * Returns this if it is a TypeVar or a TypeVar that this 104 * type depends on. Otherwise, this method returns null. 105 * It is used by dfs(). 106 * 107 * @param dim dimension 108 */ toTypeVar(int dim)109 protected TypeVar toTypeVar(int dim) { return null; } 110 111 // see UninitTypeVar and UninitData constructorCalled(int offset)112 public void constructorCalled(int offset) {} 113 114 @Override toString()115 public String toString() { 116 return super.toString() + "(" + toString2(new HashSet<TypeData>()) + ")"; 117 } 118 toString2(Set<TypeData> set)119 abstract String toString2(Set<TypeData> set); 120 121 /** 122 * Primitive types. 123 */ 124 protected static class BasicType extends TypeData { 125 private String name; 126 private int typeTag; 127 private char decodedName; 128 BasicType(String type, int tag, char decoded)129 public BasicType(String type, int tag, char decoded) { 130 name = type; 131 typeTag = tag; 132 decodedName = decoded; 133 } 134 135 @Override getTypeTag()136 public int getTypeTag() { return typeTag; } 137 @Override getTypeData(ConstPool cp)138 public int getTypeData(ConstPool cp) { return 0; } 139 140 @Override join()141 public TypeData join() { 142 if (this == TypeTag.TOP) 143 return this; 144 return super.join(); 145 } 146 147 @Override isBasicType()148 public BasicType isBasicType() { return this; } 149 150 @Override is2WordType()151 public boolean is2WordType() { 152 return typeTag == StackMapTable.LONG 153 || typeTag == StackMapTable.DOUBLE; 154 } 155 156 @Override eq(TypeData d)157 public boolean eq(TypeData d) { return this == d; } 158 159 @Override getName()160 public String getName() { 161 return name; 162 } 163 getDecodedName()164 public char getDecodedName() { return decodedName; } 165 166 @Override setType(String s, ClassPool cp)167 public void setType(String s, ClassPool cp) throws BadBytecode { 168 throw new BadBytecode("conflict: " + name + " and " + s); 169 } 170 171 /** 172 * @param dim array dimension. It may be negative. 173 */ 174 @Override getArrayType(int dim)175 public TypeData getArrayType(int dim) throws NotFoundException { 176 if (this == TypeTag.TOP) 177 return this; 178 else if (dim < 0) 179 throw new NotFoundException("no element type: " + name); 180 else if (dim == 0) 181 return this; 182 else { 183 char[] name = new char[dim + 1]; 184 for (int i = 0; i < dim; i++) 185 name[i] = '['; 186 187 name[dim] = decodedName; 188 return new ClassName(new String(name)); 189 } 190 } 191 192 @Override toString2(Set<TypeData> set)193 String toString2(Set<TypeData> set) { return name; } 194 } 195 196 // a type variable 197 public static abstract class AbsTypeVar extends TypeData { AbsTypeVar()198 public AbsTypeVar() {} merge(TypeData t)199 public abstract void merge(TypeData t); 200 @Override getTypeTag()201 public int getTypeTag() { return StackMapTable.OBJECT; } 202 203 @Override getTypeData(ConstPool cp)204 public int getTypeData(ConstPool cp) { 205 return cp.addClassInfo(getName()); 206 } 207 208 @Override eq(TypeData d)209 public boolean eq(TypeData d) { return getName().equals(d.getName()); } 210 } 211 212 /* a type variable representing a class type or a basic type. 213 */ 214 public static class TypeVar extends AbsTypeVar { 215 protected List<TypeData> lowers;// lower bounds of this type. ArrayList<TypeData> 216 protected List<TypeData> usedBy;// reverse relations of lowers 217 protected List<String> uppers; // upper bounds of this type. 218 protected String fixedType; 219 private boolean is2WordType; // cache 220 TypeVar(TypeData t)221 public TypeVar(TypeData t) { 222 uppers = null; 223 lowers = new ArrayList<TypeData>(2); 224 usedBy = new ArrayList<TypeData>(2); 225 merge(t); 226 fixedType = null; 227 is2WordType = t.is2WordType(); 228 } 229 230 @Override getName()231 public String getName() { 232 if (fixedType == null) 233 return lowers.get(0).getName(); 234 return fixedType; 235 } 236 237 @Override isBasicType()238 public BasicType isBasicType() { 239 if (fixedType == null) 240 return lowers.get(0).isBasicType(); 241 return null; 242 } 243 244 @Override is2WordType()245 public boolean is2WordType() { 246 if (fixedType == null) { 247 return is2WordType; 248 // return ((TypeData)lowers.get(0)).is2WordType(); 249 } 250 return false; 251 } 252 253 @Override isNullType()254 public boolean isNullType() { 255 if (fixedType == null) 256 return lowers.get(0).isNullType(); 257 return false; 258 } 259 260 @Override isUninit()261 public boolean isUninit() { 262 if (fixedType == null) 263 return lowers.get(0).isUninit(); 264 return false; 265 } 266 267 @Override merge(TypeData t)268 public void merge(TypeData t) { 269 lowers.add(t); 270 if (t instanceof TypeVar) 271 ((TypeVar)t).usedBy.add(this); 272 } 273 274 @Override getTypeTag()275 public int getTypeTag() { 276 /* If fixedType is null after calling dfs(), then this 277 type is NULL, Uninit, or a basic type. So call 278 getTypeTag() on the first element of lowers. */ 279 if (fixedType == null) 280 return lowers.get(0).getTypeTag(); 281 return super.getTypeTag(); 282 } 283 284 @Override getTypeData(ConstPool cp)285 public int getTypeData(ConstPool cp) { 286 if (fixedType == null) 287 return lowers.get(0).getTypeData(cp); 288 return super.getTypeData(cp); 289 } 290 291 @Override setType(String typeName, ClassPool cp)292 public void setType(String typeName, ClassPool cp) throws BadBytecode { 293 if (uppers == null) 294 uppers = new ArrayList<String>(); 295 296 uppers.add(typeName); 297 } 298 299 private int visited = 0; 300 private int smallest = 0; 301 private boolean inList = false; 302 private int dimension = 0; 303 304 @Override toTypeVar(int dim)305 protected TypeVar toTypeVar(int dim) { 306 dimension = dim; 307 return this; 308 } 309 310 /* When fixTypes() is called, getName() will return the correct 311 * (i.e. fixed) type name. 312 */ 313 @Override getArrayType(int dim)314 public TypeData getArrayType(int dim) throws NotFoundException { 315 if (dim == 0) 316 return this; 317 BasicType bt = isBasicType(); 318 if (bt == null) 319 if (isNullType()) 320 return new NullType(); 321 else 322 return new ClassName(getName()).getArrayType(dim); 323 return bt.getArrayType(dim); 324 } 325 326 // depth-first serach 327 @Override dfs(List<TypeData> preOrder, int index, ClassPool cp)328 public int dfs(List<TypeData> preOrder, int index, ClassPool cp) throws NotFoundException { 329 if (visited > 0) 330 return index; // MapMaker.make() may call an already visited node. 331 332 visited = smallest = ++index; 333 preOrder.add(this); 334 inList = true; 335 int n = lowers.size(); 336 for (int i = 0; i < n; i++) { 337 TypeVar child = lowers.get(i).toTypeVar(dimension); 338 if (child != null) 339 if (child.visited == 0) { 340 index = child.dfs(preOrder, index, cp); 341 if (child.smallest < smallest) 342 smallest = child.smallest; 343 } 344 else if (child.inList) 345 if (child.visited < smallest) 346 smallest = child.visited; 347 } 348 349 if (visited == smallest) { 350 List<TypeData> scc = new ArrayList<TypeData>(); // strongly connected component 351 TypeVar cv; 352 do { 353 cv = (TypeVar)preOrder.remove(preOrder.size() - 1); 354 cv.inList = false; 355 scc.add(cv); 356 } while (cv != this); 357 fixTypes(scc, cp); 358 } 359 360 return index; 361 } 362 fixTypes(List<TypeData> scc, ClassPool cp)363 private void fixTypes(List<TypeData> scc, ClassPool cp) throws NotFoundException { 364 Set<String> lowersSet = new HashSet<String>(); 365 boolean isBasicType = false; 366 TypeData kind = null; 367 int size = scc.size(); 368 for (int i = 0; i < size; i++) { 369 TypeVar tvar = (TypeVar)scc.get(i); 370 List<TypeData> tds = tvar.lowers; 371 int size2 = tds.size(); 372 for (int j = 0; j < size2; j++) { 373 TypeData td = tds.get(j); 374 TypeData d = td.getArrayType(tvar.dimension); 375 BasicType bt = d.isBasicType(); 376 if (kind == null) { 377 if (bt == null) { 378 isBasicType = false; 379 kind = d; 380 /* If scc has only an UninitData, fixedType is kept null. 381 So lowerSet must be empty. If scc has not only an UninitData 382 but also another TypeData, an error must be thrown but this 383 error detection has not been implemented. */ 384 if (d.isUninit()) 385 break; 386 } 387 else { 388 isBasicType = true; 389 kind = bt; 390 } 391 } 392 else { 393 if ((bt == null && isBasicType) || (bt != null && kind != bt)) { 394 isBasicType = true; 395 kind = TypeTag.TOP; 396 break; 397 } 398 } 399 400 if (bt == null && !d.isNullType()) 401 lowersSet.add(d.getName()); 402 } 403 } 404 405 if (isBasicType) { 406 is2WordType = kind.is2WordType(); // necessary? 407 fixTypes1(scc, kind); 408 } 409 else { 410 String typeName = fixTypes2(scc, lowersSet, cp); 411 fixTypes1(scc, new ClassName(typeName)); 412 } 413 } 414 fixTypes1(List<TypeData> scc, TypeData kind)415 private void fixTypes1(List<TypeData> scc, TypeData kind) throws NotFoundException { 416 int size = scc.size(); 417 for (int i = 0; i < size; i++) { 418 TypeVar cv = (TypeVar)scc.get(i); 419 TypeData kind2 = kind.getArrayType(-cv.dimension); 420 if (kind2.isBasicType() == null) 421 cv.fixedType = kind2.getName(); 422 else { 423 cv.lowers.clear(); 424 cv.lowers.add(kind2); 425 cv.is2WordType = kind2.is2WordType(); 426 } 427 } 428 } 429 fixTypes2(List<TypeData> scc, Set<String> lowersSet, ClassPool cp)430 private String fixTypes2(List<TypeData> scc, Set<String> lowersSet, ClassPool cp) throws NotFoundException { 431 Iterator<String> it = lowersSet.iterator(); 432 if (lowersSet.size() == 0) 433 return null; // only NullType 434 else if (lowersSet.size() == 1) 435 return it.next(); 436 else { 437 CtClass cc = cp.get(it.next()); 438 while (it.hasNext()) 439 cc = commonSuperClassEx(cc, cp.get(it.next())); 440 441 if (cc.getSuperclass() == null || isObjectArray(cc)) 442 cc = fixByUppers(scc, cp, new HashSet<TypeData>(), cc); 443 444 if (cc.isArray()) 445 return Descriptor.toJvmName(cc); 446 447 return cc.getName(); 448 } 449 } 450 isObjectArray(CtClass cc)451 private static boolean isObjectArray(CtClass cc) throws NotFoundException { 452 return cc.isArray() && cc.getComponentType().getSuperclass() == null; 453 } 454 fixByUppers(List<TypeData> users, ClassPool cp, Set<TypeData> visited, CtClass type)455 private CtClass fixByUppers(List<TypeData> users, ClassPool cp, Set<TypeData> visited, CtClass type) 456 throws NotFoundException 457 { 458 if (users == null) 459 return type; 460 461 int size = users.size(); 462 for (int i = 0; i < size; i++) { 463 TypeVar t = (TypeVar)users.get(i); 464 if (!visited.add(t)) 465 return type; 466 467 if (t.uppers != null) { 468 int s = t.uppers.size(); 469 for (int k = 0; k < s; k++) { 470 CtClass cc = cp.get(t.uppers.get(k)); 471 if (cc.subtypeOf(type)) 472 type = cc; 473 } 474 } 475 476 type = fixByUppers(t.usedBy, cp, visited, type); 477 } 478 479 return type; 480 } 481 482 @Override toString2(Set<TypeData> hash)483 String toString2(Set<TypeData> hash) { 484 hash.add(this); 485 if (lowers.size() > 0) { 486 TypeData e = lowers.get(0); 487 if (e != null && !hash.contains(e)) 488 return e.toString2(hash); 489 } 490 491 return "?"; 492 } 493 } 494 495 /** 496 * Finds the most specific common super class of the given classes 497 * by considering array types. 498 */ commonSuperClassEx(CtClass one, CtClass two)499 public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException { 500 if (one == two) 501 return one; 502 else if (one.isArray() && two.isArray()) { 503 CtClass ele1 = one.getComponentType(); 504 CtClass ele2 = two.getComponentType(); 505 CtClass element = commonSuperClassEx(ele1, ele2); 506 if (element == ele1) 507 return one; 508 else if (element == ele2) 509 return two; 510 else 511 return one.getClassPool().get(element == null ? "java.lang.Object" 512 : element.getName() + "[]"); 513 } 514 else if (one.isPrimitive() || two.isPrimitive()) 515 return null; // TOP 516 else if (one.isArray() || two.isArray()) // but !(one.isArray() && two.isArray()) 517 return one.getClassPool().get("java.lang.Object"); 518 else 519 return commonSuperClass(one, two); 520 } 521 522 /** 523 * Finds the most specific common super class of the given classes. 524 * This method is a copy from javassist.bytecode.analysis.Type. 525 */ commonSuperClass(CtClass one, CtClass two)526 public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException { 527 CtClass deep = one; 528 CtClass shallow = two; 529 CtClass backupShallow = shallow; 530 CtClass backupDeep = deep; 531 532 // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly 533 for (;;) { 534 // In case we get lucky, and find a match early 535 if (eq(deep, shallow) && deep.getSuperclass() != null) 536 return deep; 537 538 CtClass deepSuper = deep.getSuperclass(); 539 CtClass shallowSuper = shallow.getSuperclass(); 540 541 if (shallowSuper == null) { 542 // right, now reset shallow 543 shallow = backupShallow; 544 break; 545 } 546 547 if (deepSuper == null) { 548 // wrong, swap them, since deep is now useless, its our tmp before we swap it 549 deep = backupDeep; 550 backupDeep = backupShallow; 551 backupShallow = deep; 552 553 deep = shallow; 554 shallow = backupShallow; 555 break; 556 } 557 558 deep = deepSuper; 559 shallow = shallowSuper; 560 } 561 562 // Phase 2 - Move deepBackup up by (deep end - deep) 563 for (;;) { 564 deep = deep.getSuperclass(); 565 if (deep == null) 566 break; 567 568 backupDeep = backupDeep.getSuperclass(); 569 } 570 571 deep = backupDeep; 572 573 // Phase 3 - The hierarchy positions are now aligned 574 // The common super class is easy to find now 575 while (!eq(deep, shallow)) { 576 deep = deep.getSuperclass(); 577 shallow = shallow.getSuperclass(); 578 } 579 580 return deep; 581 } 582 eq(CtClass one, CtClass two)583 static boolean eq(CtClass one, CtClass two) { 584 return one == two || (one != null && two != null && one.getName().equals(two.getName())); 585 } 586 aastore(TypeData array, TypeData value, ClassPool cp)587 public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode { 588 if (array instanceof AbsTypeVar) 589 if (!value.isNullType()) 590 ((AbsTypeVar)array).merge(ArrayType.make(value)); 591 592 if (value instanceof AbsTypeVar) 593 if (array instanceof AbsTypeVar) 594 ArrayElement.make(array); // should call value.setType() later. 595 else if (array instanceof ClassName) { 596 if (!array.isNullType()) { 597 String type = ArrayElement.typeName(array.getName()); 598 value.setType(type, cp); 599 } 600 } 601 else 602 throw new BadBytecode("bad AASTORE: " + array); 603 } 604 605 /* A type variable representing an array type. 606 * It is a decorator of another type variable. 607 */ 608 public static class ArrayType extends AbsTypeVar { 609 private AbsTypeVar element; 610 ArrayType(AbsTypeVar elementType)611 private ArrayType(AbsTypeVar elementType) { 612 element = elementType; 613 } 614 make(TypeData element)615 static TypeData make(TypeData element) throws BadBytecode { 616 if (element instanceof ArrayElement) 617 return ((ArrayElement)element).arrayType(); 618 else if (element instanceof AbsTypeVar) 619 return new ArrayType((AbsTypeVar)element); 620 else if (element instanceof ClassName) 621 if (!element.isNullType()) 622 return new ClassName(typeName(element.getName())); 623 624 throw new BadBytecode("bad AASTORE: " + element); 625 } 626 627 @Override merge(TypeData t)628 public void merge(TypeData t) { 629 try { 630 if (!t.isNullType()) 631 element.merge(ArrayElement.make(t)); 632 } 633 catch (BadBytecode e) { 634 // never happens 635 throw new RuntimeException("fatal: " + e); 636 } 637 } 638 639 @Override getName()640 public String getName() { 641 return typeName(element.getName()); 642 } 643 elementType()644 public AbsTypeVar elementType() { return element; } 645 646 @Override isBasicType()647 public BasicType isBasicType() { return null; } 648 @Override is2WordType()649 public boolean is2WordType() { return false; } 650 651 /* elementType must be a class name. Basic type names 652 * are not allowed. 653 */ typeName(String elementType)654 public static String typeName(String elementType) { 655 if (elementType.charAt(0) == '[') 656 return "[" + elementType; 657 return "[L" + elementType.replace('.', '/') + ";"; 658 } 659 660 @Override setType(String s, ClassPool cp)661 public void setType(String s, ClassPool cp) throws BadBytecode { 662 element.setType(ArrayElement.typeName(s), cp); 663 } 664 665 @Override toTypeVar(int dim)666 protected TypeVar toTypeVar(int dim) { return element.toTypeVar(dim + 1); } 667 668 @Override getArrayType(int dim)669 public TypeData getArrayType(int dim) throws NotFoundException { 670 return element.getArrayType(dim + 1); 671 } 672 673 @Override dfs(List<TypeData> order, int index, ClassPool cp)674 public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException { 675 return element.dfs(order, index, cp); 676 } 677 678 @Override toString2(Set<TypeData> set)679 String toString2(Set<TypeData> set) { 680 return "[" + element.toString2(set); 681 } 682 } 683 684 /* A type variable representing an array-element type. 685 * It is a decorator of another type variable. 686 */ 687 public static class ArrayElement extends AbsTypeVar { 688 private AbsTypeVar array; 689 ArrayElement(AbsTypeVar a)690 private ArrayElement(AbsTypeVar a) { // a is never null 691 array = a; 692 } 693 make(TypeData array)694 public static TypeData make(TypeData array) throws BadBytecode { 695 if (array instanceof ArrayType) 696 return ((ArrayType)array).elementType(); 697 else if (array instanceof AbsTypeVar) 698 return new ArrayElement((AbsTypeVar)array); 699 else if (array instanceof ClassName) 700 if (!array.isNullType()) 701 return new ClassName(typeName(array.getName())); 702 703 throw new BadBytecode("bad AASTORE: " + array); 704 } 705 706 @Override merge(TypeData t)707 public void merge(TypeData t) { 708 try { 709 if (!t.isNullType()) 710 array.merge(ArrayType.make(t)); 711 } 712 catch (BadBytecode e) { 713 // never happens 714 throw new RuntimeException("fatal: " + e); 715 } 716 } 717 718 @Override getName()719 public String getName() { 720 return typeName(array.getName()); 721 } 722 arrayType()723 public AbsTypeVar arrayType() { return array; } 724 725 /* arrayType must be a class name. Basic type names are 726 * not allowed. 727 */ 728 729 @Override isBasicType()730 public BasicType isBasicType() { return null; } 731 732 @Override is2WordType()733 public boolean is2WordType() { return false; } 734 typeName(String arrayType)735 private static String typeName(String arrayType) { 736 if (arrayType.length() > 1 && arrayType.charAt(0) == '[') { 737 char c = arrayType.charAt(1); 738 if (c == 'L') 739 return arrayType.substring(2, arrayType.length() - 1).replace('/', '.'); 740 else if (c == '[') 741 return arrayType.substring(1); 742 } 743 744 return "java.lang.Object"; // the array type may be NullType 745 } 746 747 @Override setType(String s, ClassPool cp)748 public void setType(String s, ClassPool cp) throws BadBytecode { 749 array.setType(ArrayType.typeName(s), cp); 750 } 751 752 @Override toTypeVar(int dim)753 protected TypeVar toTypeVar(int dim) { return array.toTypeVar(dim - 1); } 754 755 @Override getArrayType(int dim)756 public TypeData getArrayType(int dim) throws NotFoundException { 757 return array.getArrayType(dim - 1); 758 } 759 760 @Override dfs(List<TypeData> order, int index, ClassPool cp)761 public int dfs(List<TypeData> order, int index, ClassPool cp) throws NotFoundException { 762 return array.dfs(order, index, cp); 763 } 764 765 @Override toString2(Set<TypeData> set)766 String toString2(Set<TypeData> set) { 767 return "*" + array.toString2(set); 768 } 769 } 770 771 public static class UninitTypeVar extends AbsTypeVar { 772 protected TypeData type; // UninitData or TOP 773 UninitTypeVar(UninitData t)774 public UninitTypeVar(UninitData t) { type = t; } 775 @Override getTypeTag()776 public int getTypeTag() { return type.getTypeTag(); } 777 @Override getTypeData(ConstPool cp)778 public int getTypeData(ConstPool cp) { return type.getTypeData(cp); } 779 @Override isBasicType()780 public BasicType isBasicType() { return type.isBasicType(); } 781 @Override is2WordType()782 public boolean is2WordType() { return type.is2WordType(); } 783 @Override isUninit()784 public boolean isUninit() { return type.isUninit(); } 785 @Override eq(TypeData d)786 public boolean eq(TypeData d) { return type.eq(d); } 787 @Override getName()788 public String getName() { return type.getName(); } 789 790 @Override toTypeVar(int dim)791 protected TypeVar toTypeVar(int dim) { return null; } 792 @Override join()793 public TypeData join() { return type.join(); } 794 795 @Override setType(String s, ClassPool cp)796 public void setType(String s, ClassPool cp) throws BadBytecode { 797 type.setType(s, cp); 798 } 799 800 @Override merge(TypeData t)801 public void merge(TypeData t) { 802 if (!t.eq(type)) 803 type = TypeTag.TOP; 804 } 805 806 @Override constructorCalled(int offset)807 public void constructorCalled(int offset) { 808 type.constructorCalled(offset); 809 } 810 offset()811 public int offset() { 812 if (type instanceof UninitData) 813 return ((UninitData)type).offset; 814 throw new RuntimeException("not available"); 815 } 816 817 @Override getArrayType(int dim)818 public TypeData getArrayType(int dim) throws NotFoundException { 819 return type.getArrayType(dim); 820 } 821 822 @Override toString2(Set<TypeData> set)823 String toString2(Set<TypeData> set) { return ""; } 824 } 825 826 /** 827 * Type data for OBJECT. 828 */ 829 public static class ClassName extends TypeData { 830 private String name; // dot separated. 831 ClassName(String n)832 public ClassName(String n) { 833 name = n; 834 } 835 836 @Override getName()837 public String getName() { 838 return name; 839 } 840 841 @Override isBasicType()842 public BasicType isBasicType() { return null; } 843 844 @Override is2WordType()845 public boolean is2WordType() { return false; } 846 847 @Override getTypeTag()848 public int getTypeTag() { return StackMapTable.OBJECT; } 849 850 @Override getTypeData(ConstPool cp)851 public int getTypeData(ConstPool cp) { 852 return cp.addClassInfo(getName()); 853 } 854 855 @Override eq(TypeData d)856 public boolean eq(TypeData d) { return name.equals(d.getName()); } 857 858 @Override setType(String typeName, ClassPool cp)859 public void setType(String typeName, ClassPool cp) throws BadBytecode {} 860 861 @Override getArrayType(int dim)862 public TypeData getArrayType(int dim) throws NotFoundException { 863 if (dim == 0) 864 return this; 865 else if (dim > 0) { 866 char[] dimType = new char[dim]; 867 for (int i = 0; i < dim; i++) 868 dimType[i] = '['; 869 870 String elementType = getName(); 871 if (elementType.charAt(0) != '[') 872 elementType = "L" + elementType.replace('.', '/') + ";"; 873 874 return new ClassName(new String(dimType) + elementType); 875 } 876 else { 877 for (int i = 0; i < -dim; i++) 878 if (name.charAt(i) != '[') 879 throw new NotFoundException("no " + dim + " dimensional array type: " + getName()); 880 881 char type = name.charAt(-dim); 882 if (type == '[') 883 return new ClassName(name.substring(-dim)); 884 else if (type == 'L') 885 return new ClassName(name.substring(-dim + 1, name.length() - 1).replace('/', '.')); 886 else if (type == TypeTag.DOUBLE.decodedName) 887 return TypeTag.DOUBLE; 888 else if (type == TypeTag.FLOAT.decodedName) 889 return TypeTag.FLOAT; 890 else if (type == TypeTag.LONG.decodedName) 891 return TypeTag.LONG; 892 else 893 return TypeTag.INTEGER; 894 } 895 } 896 897 @Override toString2(Set<TypeData> set)898 String toString2(Set<TypeData> set) { 899 return name; 900 } 901 } 902 903 /** 904 * Type data for NULL or OBJECT. 905 * The types represented by the instances of this class are 906 * initially NULL but will be OBJECT. 907 */ 908 public static class NullType extends ClassName { NullType()909 public NullType() { 910 super("null-type"); // type name 911 } 912 913 @Override getTypeTag()914 public int getTypeTag() { 915 return StackMapTable.NULL; 916 } 917 918 @Override isNullType()919 public boolean isNullType() { return true; } 920 @Override getTypeData(ConstPool cp)921 public int getTypeData(ConstPool cp) { return 0; } 922 923 @Override getArrayType(int dim)924 public TypeData getArrayType(int dim) { return this; } 925 } 926 927 /** 928 * Type data for UNINIT. 929 */ 930 public static class UninitData extends ClassName { 931 int offset; 932 boolean initialized; 933 UninitData(int offset, String className)934 UninitData(int offset, String className) { 935 super(className); 936 this.offset = offset; 937 this.initialized = false; 938 } 939 copy()940 public UninitData copy() { return new UninitData(offset, getName()); } 941 942 @Override getTypeTag()943 public int getTypeTag() { 944 return StackMapTable.UNINIT; 945 } 946 947 @Override getTypeData(ConstPool cp)948 public int getTypeData(ConstPool cp) { 949 return offset; 950 } 951 952 @Override join()953 public TypeData join() { 954 if (initialized) 955 return new TypeVar(new ClassName(getName())); 956 return new UninitTypeVar(copy()); 957 } 958 959 @Override isUninit()960 public boolean isUninit() { return true; } 961 962 @Override eq(TypeData d)963 public boolean eq(TypeData d) { 964 if (d instanceof UninitData) { 965 UninitData ud = (UninitData)d; 966 return offset == ud.offset && getName().equals(ud.getName()); 967 } 968 return false; 969 } 970 offset()971 public int offset() { return offset; } 972 973 @Override constructorCalled(int offset)974 public void constructorCalled(int offset) { 975 if (offset == this.offset) 976 initialized = true; 977 } 978 979 @Override toString2(Set<TypeData> set)980 String toString2(Set<TypeData> set) { return getName() + "," + offset; } 981 } 982 983 public static class UninitThis extends UninitData { UninitThis(String className)984 UninitThis(String className) { 985 super(-1, className); 986 } 987 988 @Override copy()989 public UninitData copy() { return new UninitThis(getName()); } 990 991 @Override getTypeTag()992 public int getTypeTag() { 993 return StackMapTable.THIS; 994 } 995 996 @Override getTypeData(ConstPool cp)997 public int getTypeData(ConstPool cp) { 998 return 0; 999 } 1000 1001 @Override toString2(Set<TypeData> set)1002 String toString2(Set<TypeData> set) { return "uninit:this"; } 1003 } 1004 } 1005