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
16package javassist.bytecode.stackmap;
17
18import javassist.ClassPool;
19import javassist.CtClass;
20import javassist.NotFoundException;
21import javassist.bytecode.ConstPool;
22import javassist.bytecode.StackMapTable;
23import javassist.bytecode.BadBytecode;
24import java.util.ArrayList;
25
26public 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