• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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