RegisterType.java revision b6c52bc254b4c5b4fbfd9eabe7b6e4218b4f6183
1/* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32package org.jf.dexlib2.analysis; 33 34 35import org.jf.util.ExceptionWithContext; 36 37import javax.annotation.Nonnull; 38import javax.annotation.Nullable; 39import java.io.IOException; 40import java.io.Writer; 41 42public class RegisterType { 43 public final byte category; 44 @Nullable public final TypeProto type; 45 46 private RegisterType(byte category, @Nullable TypeProto type) { 47 assert ((category == REFERENCE || category == UNINIT_REF || category == UNINIT_THIS) && type != null) || 48 ((category != REFERENCE && category != UNINIT_REF && category != UNINIT_THIS) && type == null); 49 50 this.category = category; 51 this.type = type; 52 } 53 54 @Override 55 public String toString() { 56 return "(" + CATEGORY_NAMES[category] + (type==null?"":("," + type)) + ")"; 57 } 58 59 public void writeTo(Writer writer) throws IOException { 60 writer.write('('); 61 writer.write(CATEGORY_NAMES[category]); 62 if (type != null) { 63 writer.write(','); 64 writer.write(type.getType()); 65 } 66 writer.write(')'); 67 } 68 69 @Override 70 public boolean equals(Object o) { 71 if (this == o) return true; 72 if (o == null || getClass() != o.getClass()) return false; 73 74 RegisterType that = (RegisterType) o; 75 76 if (category != that.category) { 77 return false; 78 } 79 return (type != null ? type.equals(that.type) : that.type == null); 80 } 81 82 @Override 83 public int hashCode() { 84 int result = category; 85 result = 31 * result + (type != null ? type.hashCode() : 0); 86 return result; 87 } 88 89 // The Unknown category denotes a register type that hasn't been determined yet 90 public static final byte UNKNOWN = 0; 91 // The Uninit category is for registers that haven't been set yet. e.g. the non-parameter registers in a method 92 // start out as unint 93 public static final byte UNINIT = 1; 94 public static final byte NULL = 2; 95 public static final byte ONE = 3; 96 public static final byte BOOLEAN = 4; 97 public static final byte BYTE = 5; 98 public static final byte POS_BYTE = 6; 99 public static final byte SHORT = 7; 100 public static final byte POS_SHORT = 8; 101 public static final byte CHAR = 9; 102 public static final byte INTEGER = 10; 103 public static final byte FLOAT = 11; 104 public static final byte LONG_LO = 12; 105 public static final byte LONG_HI = 13; 106 public static final byte DOUBLE_LO = 14; 107 public static final byte DOUBLE_HI = 15; 108 // The UninitRef category is used after a new-instance operation, and before the corresponding <init> is called 109 public static final byte UNINIT_REF = 16; 110 // The UninitThis category is used the "this" register inside an <init> method, before the superclass' <init> 111 // method is called 112 public static final byte UNINIT_THIS = 17; 113 public static final byte REFERENCE = 18; 114 // This is used when there are multiple incoming execution paths that have incompatible register types. For 115 // example if the register's type is an Integer on one incoming code path, but is a Reference type on another 116 // incomming code path. There is no register type that can hold either an Integer or a Reference. 117 public static final byte CONFLICTED = 19; 118 119 public static final String[] CATEGORY_NAMES = new String[] { 120 "Unknown", 121 "Uninit", 122 "Null", 123 "One", 124 "Boolean", 125 "Byte", 126 "PosByte", 127 "Short", 128 "PosShort", 129 "Char", 130 "Integer", 131 "Float", 132 "LongLo", 133 "LongHi", 134 "DoubleLo", 135 "DoubleHi", 136 "UninitRef", 137 "UninitThis", 138 "Reference", 139 "Conflicted" 140 }; 141 142 //this table is used when merging register types. For example, if a particular register can be either a BYTE 143 //or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can 144 //could hold either type of value. 145 protected static byte[][] mergeTable = 146 { 147 /* UNKNOWN UNINIT NULL ONE, BOOLEAN BYTE POS_BYTE SHORT POS_SHORT CHAR INTEGER, FLOAT, LONG_LO LONG_HI DOUBLE_LO DOUBLE_HI UNINIT_REF UNINIT_THIS REFERENCE CONFLICTED*/ 148 /*UNKNOWN*/ {UNKNOWN, UNINIT, NULL, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, LONG_LO, LONG_HI, DOUBLE_LO, DOUBLE_HI, UNINIT_REF, UNINIT_THIS,REFERENCE, CONFLICTED}, 149 /*UNINIT*/ {UNINIT, UNINIT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 150 /*NULL*/ {NULL, CONFLICTED, NULL, BOOLEAN, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, REFERENCE, CONFLICTED}, 151 /*ONE*/ {ONE, CONFLICTED, BOOLEAN, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 152 /*BOOLEAN*/ {BOOLEAN, CONFLICTED, BOOLEAN, BOOLEAN, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 153 /*BYTE*/ {BYTE, CONFLICTED, BYTE, BYTE, BYTE, BYTE, BYTE, SHORT, SHORT, INTEGER, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 154 /*POS_BYTE*/ {POS_BYTE, CONFLICTED, POS_BYTE, POS_BYTE, POS_BYTE, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 155 /*SHORT*/ {SHORT, CONFLICTED, SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, INTEGER, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 156 /*POS_SHORT*/ {POS_SHORT, CONFLICTED, POS_SHORT, POS_SHORT, POS_SHORT, SHORT, POS_SHORT, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 157 /*CHAR*/ {CHAR, CONFLICTED, CHAR, CHAR, CHAR, INTEGER, CHAR, INTEGER, CHAR, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 158 /*INTEGER*/ {INTEGER, CONFLICTED, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 159 /*FLOAT*/ {FLOAT, CONFLICTED, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 160 /*LONG_LO*/ {LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 161 /*LONG_HI*/ {LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 162 /*DOUBLE_LO*/ {DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 163 /*DOUBLE_HI*/ {DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 164 /*UNINIT_REF*/ {UNINIT_REF, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, 165 /*UNINIT_THIS*/{UNINIT_THIS, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, UNINIT_THIS,CONFLICTED, CONFLICTED}, 166 /*REFERENCE*/ {REFERENCE, CONFLICTED, REFERENCE, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, REFERENCE, CONFLICTED}, 167 /*CONFLICTED*/ {CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED} 168 }; 169 170 171 public static final RegisterType UNKNOWN_TYPE = new RegisterType(UNKNOWN, null); 172 public static final RegisterType UNINIT_TYPE = new RegisterType(UNINIT, null); 173 public static final RegisterType NULL_TYPE = new RegisterType(NULL, null); 174 public static final RegisterType ONE_TYPE = new RegisterType(ONE, null); 175 public static final RegisterType BOOLEAN_TYPE = new RegisterType(BOOLEAN, null); 176 public static final RegisterType BYTE_TYPE = new RegisterType(BYTE, null); 177 public static final RegisterType POS_BYTE_TYPE = new RegisterType(POS_BYTE, null); 178 public static final RegisterType SHORT_TYPE = new RegisterType(SHORT, null); 179 public static final RegisterType POS_SHORT_TYPE = new RegisterType(POS_SHORT, null); 180 public static final RegisterType CHAR_TYPE = new RegisterType(CHAR, null); 181 public static final RegisterType INTEGER_TYPE = new RegisterType(INTEGER, null); 182 public static final RegisterType FLOAT_TYPE = new RegisterType(FLOAT, null); 183 public static final RegisterType LONG_LO_TYPE = new RegisterType(LONG_LO, null); 184 public static final RegisterType LONG_HI_TYPE = new RegisterType(LONG_HI, null); 185 public static final RegisterType DOUBLE_LO_TYPE = new RegisterType(DOUBLE_LO, null); 186 public static final RegisterType DOUBLE_HI_TYPE = new RegisterType(DOUBLE_HI, null); 187 public static final RegisterType CONFLICTED_TYPE = new RegisterType(CONFLICTED, null); 188 189 @Nonnull 190 public static RegisterType getWideRegisterType(@Nonnull CharSequence type, boolean firstRegister) { 191 switch (type.charAt(0)) { 192 case 'J': 193 if (firstRegister) { 194 return getRegisterType(LONG_LO, null); 195 } else { 196 return getRegisterType(LONG_HI, null); 197 } 198 case 'D': 199 if (firstRegister) { 200 return getRegisterType(DOUBLE_LO, null); 201 } else { 202 return getRegisterType(DOUBLE_HI, null); 203 } 204 default: 205 throw new ExceptionWithContext("Cannot use this method for narrow register type: %s", type); 206 } 207 } 208 209 @Nonnull 210 public static RegisterType getRegisterType(@Nonnull ClassPath classPath, @Nonnull CharSequence type) { 211 switch (type.charAt(0)) { 212 case 'Z': 213 return BOOLEAN_TYPE; 214 case 'B': 215 return BYTE_TYPE; 216 case 'S': 217 return SHORT_TYPE; 218 case 'C': 219 return CHAR_TYPE; 220 case 'I': 221 return INTEGER_TYPE; 222 case 'F': 223 return FLOAT_TYPE; 224 case 'J': 225 return LONG_LO_TYPE; 226 case 'D': 227 return DOUBLE_LO_TYPE; 228 case 'L': 229 case '[': 230 return getRegisterType(REFERENCE, classPath.getClass(type)); 231 default: 232 throw new ExceptionWithContext("Invalid type: " + type); 233 } 234 } 235 236 @Nonnull 237 public static RegisterType getRegisterTypeForLiteral(int literalValue) { 238 if (literalValue < -32768) { 239 return INTEGER_TYPE; 240 } 241 if (literalValue < -128) { 242 return SHORT_TYPE; 243 } 244 if (literalValue < 0) { 245 return BYTE_TYPE; 246 } 247 if (literalValue == 0) { 248 return NULL_TYPE; 249 } 250 if (literalValue == 1) { 251 return ONE_TYPE; 252 } 253 if (literalValue < 128) { 254 return POS_BYTE_TYPE; 255 } 256 if (literalValue < 32768) { 257 return POS_SHORT_TYPE; 258 } 259 if (literalValue < 65536) { 260 return CHAR_TYPE; 261 } 262 return INTEGER_TYPE; 263 } 264 265 @Nonnull 266 public RegisterType merge(@Nonnull RegisterType other) { 267 if (other == this) { 268 return this; 269 } 270 271 byte mergedCategory = mergeTable[this.category][other.category]; 272 273 TypeProto mergedType = null; 274 if (mergedCategory == REFERENCE) { 275 TypeProto type = this.type; 276 if (type != null) { 277 if (other.type != null) { 278 mergedType = type.getCommonSuperclass(other.type); 279 } else { 280 mergedType = type; 281 } 282 } else { 283 mergedType = other.type; 284 } 285 } else if (mergedCategory == UNINIT_REF || mergedCategory == UNINIT_THIS) { 286 if (this.category == UNKNOWN) { 287 return other; 288 } 289 assert other.category == UNKNOWN; 290 return this; 291 } 292 293 if (mergedType != null) { 294 if (mergedType.equals(this.type)) { 295 return this; 296 } 297 if (mergedType.equals(other.type)) { 298 return other; 299 } 300 } 301 return RegisterType.getRegisterType(mergedCategory, mergedType); 302 } 303 304 // TODO: consider making TypeProto extend/implement RegisterType? 305 // TODO: add a getReferenceRegisterType convenience method 306 307 @Nonnull 308 public static RegisterType getRegisterType(byte category, @Nullable TypeProto typeProto) { 309 switch (category) { 310 case UNKNOWN: 311 return UNKNOWN_TYPE; 312 case UNINIT: 313 return UNINIT_TYPE; 314 case NULL: 315 return NULL_TYPE; 316 case ONE: 317 return ONE_TYPE; 318 case BOOLEAN: 319 return BOOLEAN_TYPE; 320 case BYTE: 321 return BYTE_TYPE; 322 case POS_BYTE: 323 return POS_BYTE_TYPE; 324 case SHORT: 325 return SHORT_TYPE; 326 case POS_SHORT: 327 return POS_SHORT_TYPE; 328 case CHAR: 329 return CHAR_TYPE; 330 case INTEGER: 331 return INTEGER_TYPE; 332 case FLOAT: 333 return FLOAT_TYPE; 334 case LONG_LO: 335 return LONG_LO_TYPE; 336 case LONG_HI: 337 return LONG_HI_TYPE; 338 case DOUBLE_LO: 339 return DOUBLE_LO_TYPE; 340 case DOUBLE_HI: 341 return DOUBLE_HI_TYPE; 342 case CONFLICTED: 343 return CONFLICTED_TYPE; 344 } 345 346 return new RegisterType(category, typeProto); 347 } 348} 349