Home | History | Annotate | Download | only in stackmap
      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