Home | History | Annotate | Download | only in Analysis
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib.Code.Analysis;
     30 
     31 import org.jf.dexlib.TypeIdItem;
     32 
     33 import java.io.IOException;
     34 import java.io.Writer;
     35 import java.util.HashMap;
     36 
     37 import static org.jf.dexlib.Code.Analysis.ClassPath.ClassDef;
     38 
     39 public class RegisterType {
     40     private final static HashMap<RegisterType, RegisterType> internedRegisterTypes =
     41             new HashMap<RegisterType, RegisterType>();
     42 
     43     public final Category category;
     44     public final ClassDef type;
     45 
     46     private RegisterType(Category category, ClassDef type) {
     47         assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
     48                     type != null) ||
     49                ((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
     50                     type == null);
     51 
     52         this.category = category;
     53         this.type = type;
     54     }
     55 
     56     @Override
     57     public String toString() {
     58         return "(" + category.name() + (type==null?"":("," + type.getClassType())) + ")";
     59     }
     60 
     61     public void writeTo(Writer writer) throws IOException {
     62         writer.write('(');
     63         writer.write(category.name());
     64         if (type != null) {
     65             writer.write(',');
     66             writer.write(type.getClassType());
     67         }
     68         writer.write(')');
     69     }
     70 
     71     @Override
     72     public boolean equals(Object o) {
     73         if (this == o) return true;
     74         if (o == null || getClass() != o.getClass()) return false;
     75 
     76         RegisterType that = (RegisterType) o;
     77 
     78         if (category != that.category) return false;
     79         if (type != null ? !type.equals(that.type) : that.type != null) return false;
     80 
     81         return true;
     82     }
     83 
     84     @Override
     85     public int hashCode() {
     86         int result = category.hashCode();
     87         result = 31 * result + (type != null ? type.hashCode() : 0);
     88         return result;
     89     }
     90 
     91     public static enum Category {
     92         //the Unknown category denotes a register type that hasn't been determined yet
     93         Unknown,
     94         Uninit,
     95         Null,
     96         One,
     97         Boolean,
     98         Byte,
     99         PosByte,
    100         Short,
    101         PosShort,
    102         Char,
    103         Integer,
    104         Float,
    105         LongLo,
    106         LongHi,
    107         DoubleLo,
    108         DoubleHi,
    109         //the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
    110         UninitRef,
    111         //the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
    112         //method is called
    113         UninitThis,
    114         Reference,
    115         //This is used when there are multiple incoming execution paths that have incompatible register types. For
    116         //example if the register's type is an Integer on one incomming code path, but is a Reference type on another
    117         //incomming code path. There is no register type that can hold either an Integer or a Reference.
    118         Conflicted;
    119 
    120         //this table is used when merging register types. For example, if a particular register can be either a Byte
    121         //or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can
    122         //could hold either type of value.
    123         protected static Category[][] mergeTable  =
    124         {
    125                 /*             Unknown      Uninit      Null        One,        Boolean     Byte        PosByte     Short       PosShort    Char        Integer,    Float,      LongLo      LongHi      DoubleLo    DoubleHi    UninitRef   UninitThis  Reference   Conflicted*/
    126                 /*Unknown*/    {Unknown,    Uninit,     Null,       One,        Boolean,    Byte,       PosByte,    Short,      PosShort,   Char,       Integer,    Float,      LongLo,     LongHi,     DoubleLo,   DoubleHi,   UninitRef,  UninitThis, Reference,  Conflicted},
    127                 /*Uninit*/     {Uninit,     Uninit,     Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    128                 /*Null*/       {Null,       Conflicted, Null,       Boolean,    Boolean,    Byte,       PosByte,    Short,      PosShort,   Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference,  Conflicted},
    129                 /*One*/        {One,        Conflicted, Boolean,    One,        Boolean,    Byte,       PosByte,    Short,      PosShort,   Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    130                 /*Boolean*/    {Boolean,    Conflicted, Boolean,    Boolean,    Boolean,    Byte,       PosByte,    Short,      PosShort,   Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    131                 /*Byte*/       {Byte,       Conflicted, Byte,       Byte,       Byte,       Byte,       Byte,       Short,      Short,      Integer,    Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    132                 /*PosByte*/    {PosByte,    Conflicted, PosByte,    PosByte,    PosByte,    Byte,       PosByte,    Short,      PosShort,   Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    133                 /*Short*/      {Short,      Conflicted, Short,      Short,      Short,      Short,      Short,      Short,      Short,      Integer,    Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    134                 /*PosShort*/   {PosShort,   Conflicted, PosShort,   PosShort,   PosShort,   Short,      PosShort,   Short,      PosShort,   Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    135                 /*Char*/       {Char,       Conflicted, Char,       Char,       Char,       Integer,    Char,       Integer,    Char,       Char,       Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    136                 /*Integer*/    {Integer,    Conflicted, Integer,    Integer,    Integer,    Integer,    Integer,    Integer,    Integer,    Integer,    Integer,    Integer,    Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    137                 /*Float*/      {Float,      Conflicted, Float,      Float,      Float,      Float,      Float,      Float,      Float,      Float,      Integer,    Float,      Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    138                 /*LongLo*/     {LongLo,     Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo,     Conflicted, LongLo,     Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    139                 /*LongHi*/     {LongHi,     Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi,     Conflicted, LongHi,     Conflicted, Conflicted, Conflicted, Conflicted},
    140                 /*DoubleLo*/   {DoubleLo,   Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo,     Conflicted, DoubleLo,   Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    141                 /*DoubleHi*/   {DoubleHi,   Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi,     Conflicted, DoubleHi,   Conflicted, Conflicted, Conflicted, Conflicted},
    142                 /*UninitRef*/  {UninitRef,  Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
    143                 /*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted},
    144                 /*Reference*/  {Reference,  Conflicted, Reference,  Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference,  Conflicted},
    145                 /*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
    146         };
    147 
    148         //this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
    149         //example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an
    150         //array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()]
    151         //Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to
    152         //check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
    153         protected static boolean[][] assigmentTable =
    154         {
    155                 /*             Unknown      Uninit      Null        One,        Boolean     Byte        PosByte     Short       PosShort    Char        Integer,    Float,      LongLo      LongHi      DoubleLo    DoubleHi    UninitRef   UninitThis  Reference   Conflicted  |slot type*/
    156                 /*Unknown*/    {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false},
    157                 /*Uninit*/     {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false},
    158                 /*Null*/       {false,      false,      true,       false,      true,       true,       true,       true,       true,       true,       true,       true,       false,      false,      false,      false,      false,      false,      true,       false},
    159                 /*One*/        {false,      false,      false,      true,       true,       true,       true,       true,       true,       true,       true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    160                 /*Boolean*/    {false,      false,      false,      false,      true,       true,       true,       true,       true,       true,       true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    161                 /*Byte*/       {false,      false,      false,      false,      false,      true,       false,      true,       true,       false,      true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    162                 /*PosByte*/    {false,      false,      false,      false,      false,      true,       true,       true,       true,       true,       true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    163                 /*Short*/      {false,      false,      false,      false,      false,      false,      false,      true,       false,      false,      true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    164                 /*PosShort*/   {false,      false,      false,      false,      false,      false,      false,      true,       true,       true,       true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    165                 /*Char*/       {false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    166                 /*Integer*/    {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    167                 /*Float*/      {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       true,       false,      false,      false,      false,      false,      false,      false,      false},
    168                 /*LongLo*/     {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       false,      true,       false,      false,      false,      false,      false},
    169                 /*LongHi*/     {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       false,      true,       false,      false,      false,      false},
    170                 /*DoubleLo*/   {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       false,      true,       false,      false,      false,      false,      false},
    171                 /*DoubleHi*/   {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       false,      true,       false,      false,      false,      false},
    172                 /*UninitRef*/  {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false},
    173                 /*UninitThis*/ {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false},
    174                 /*Reference*/  {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      true,       false},
    175                 /*Conflicted*/ {false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false,      false}
    176                 /*----------*/
    177                 /*value type*/
    178         };
    179 
    180     }
    181 
    182     public static RegisterType getRegisterTypeForType(String type) {
    183         switch (type.charAt(0)) {
    184             case 'V':
    185                 throw new ValidationException("The V type can only be used as a method return type");
    186             case 'Z':
    187                 return getRegisterType(Category.Boolean, null);
    188             case 'B':
    189                 return getRegisterType(Category.Byte, null);
    190             case 'S':
    191                 return getRegisterType(Category.Short, null);
    192             case 'C':
    193                 return getRegisterType(Category.Char, null);
    194             case 'I':
    195                 return getRegisterType(Category.Integer, null);
    196             case 'F':
    197                 return getRegisterType(Category.Float, null);
    198             case 'J':
    199                 return getRegisterType(Category.LongLo, null);
    200             case 'D':
    201                 return getRegisterType(Category.DoubleLo, null);
    202             case 'L':
    203             case '[':
    204                 return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
    205             default:
    206                 throw new RuntimeException("Invalid type: " + type);
    207         }
    208     }
    209 
    210     public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
    211         return getRegisterTypeForType(typeIdItem.getTypeDescriptor());
    212     }
    213 
    214     public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
    215         if (typeIdItem.getRegisterCount() == 1) {
    216             throw new RuntimeException("Cannot use this method for non-wide register type: " +
    217                     typeIdItem.getTypeDescriptor());
    218         }
    219 
    220         switch (typeIdItem.getTypeDescriptor().charAt(0)) {
    221             case 'J':
    222                 if (firstRegister) {
    223                     return getRegisterType(Category.LongLo, null);
    224                 } else {
    225                     return getRegisterType(Category.LongHi, null);
    226                 }
    227             case 'D':
    228                 if (firstRegister) {
    229                     return getRegisterType(Category.DoubleLo, null);
    230                 } else {
    231                     return getRegisterType(Category.DoubleHi, null);
    232                 }
    233             default:
    234                 throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor());
    235         }
    236     }
    237 
    238     public static RegisterType getRegisterTypeForLiteral(long literalValue) {
    239         if (literalValue < -32768) {
    240             return getRegisterType(Category.Integer, null);
    241         }
    242         if (literalValue < -128) {
    243             return getRegisterType(Category.Short, null);
    244         }
    245         if (literalValue < 0) {
    246             return getRegisterType(Category.Byte, null);
    247         }
    248         if (literalValue == 0) {
    249             return getRegisterType(Category.Null, null);
    250         }
    251         if (literalValue == 1) {
    252             return getRegisterType(Category.One, null);
    253         }
    254         if (literalValue < 128) {
    255             return getRegisterType(Category.PosByte, null);
    256         }
    257         if (literalValue < 32768) {
    258             return getRegisterType(Category.PosShort, null);
    259         }
    260         if (literalValue < 65536) {
    261             return getRegisterType(Category.Char, null);
    262         }
    263         return getRegisterType(Category.Integer, null);
    264     }
    265 
    266     public RegisterType merge(RegisterType type) {
    267         if (type == null || type == this) {
    268             return this;
    269         }
    270 
    271         Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
    272 
    273         ClassDef mergedType = null;
    274         if (mergedCategory == Category.Reference) {
    275             if (this.type instanceof ClassPath.UnresolvedClassDef ||
    276                 type.type instanceof ClassPath.UnresolvedClassDef) {
    277                 mergedType = ClassPath.getUnresolvedObjectClassDef();
    278             } else {
    279                 mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
    280             }
    281         } else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
    282             if (this.category == Category.Unknown) {
    283                 return type;
    284             }
    285             assert type.category == Category.Unknown;
    286             return this;
    287         }
    288         return RegisterType.getRegisterType(mergedCategory, mergedType);
    289     }
    290 
    291     public boolean canBeAssignedTo(RegisterType slotType) {
    292         if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) {
    293             if (this.category == Category.Reference && slotType.category == Category.Reference) {
    294                 if (!slotType.type.isInterface()) {
    295                     return this.type.extendsClass(slotType.type);
    296                 }
    297                 //for verification, we assume all objects implement all interfaces, so we don't verify the type if
    298                 //slotType is an interface
    299             }
    300             return true;
    301         }
    302         return false;
    303     }
    304 
    305     public static RegisterType getUnitializedReference(ClassDef classType) {
    306         //We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance
    307         //is used to track a specific uninitialized reference, so that if multiple registers contain the same
    308         //uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate
    309         //<init> is invoked
    310         return new RegisterType(Category.UninitRef, classType);
    311     }
    312 
    313     public static RegisterType getRegisterType(Category category, ClassDef classType) {
    314         RegisterType newRegisterType = new RegisterType(category, classType);
    315         RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
    316         if (internedRegisterType == null) {
    317             internedRegisterTypes.put(newRegisterType, newRegisterType);
    318             return newRegisterType;
    319         }
    320         return internedRegisterType;
    321     }
    322 }
    323