1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dexgen.dex.code; 18 19 import com.android.dexgen.rop.code.RegisterSpecList; 20 import com.android.dexgen.rop.cst.Constant; 21 import com.android.dexgen.rop.cst.CstInteger; 22 import com.android.dexgen.rop.cst.CstKnownNull; 23 import com.android.dexgen.rop.cst.CstLiteral64; 24 import com.android.dexgen.rop.cst.CstLiteralBits; 25 import com.android.dexgen.util.AnnotatedOutput; 26 import com.android.dexgen.util.Hex; 27 28 /** 29 * Base class for all instruction format handlers. Instruction format 30 * handlers know how to translate {@link DalvInsn} instances into 31 * streams of code words, as well as human-oriented listing strings 32 * representing such translations. 33 */ 34 public abstract class InsnFormat { 35 /** 36 * Returns the string form, suitable for inclusion in a listing 37 * dump, of the given instruction. The instruction must be of this 38 * instance's format for proper operation. 39 * 40 * @param insn {@code non-null;} the instruction 41 * @param noteIndices whether to include an explicit notation of 42 * constant pool indices 43 * @return {@code non-null;} the string form 44 */ listingString(DalvInsn insn, boolean noteIndices)45 public final String listingString(DalvInsn insn, boolean noteIndices) { 46 String op = insn.getOpcode().getName(); 47 String arg = insnArgString(insn); 48 String comment = insnCommentString(insn, noteIndices); 49 StringBuilder sb = new StringBuilder(100); 50 51 sb.append(op); 52 53 if (arg.length() != 0) { 54 sb.append(' '); 55 sb.append(arg); 56 } 57 58 if (comment.length() != 0) { 59 sb.append(" // "); 60 sb.append(comment); 61 } 62 63 return sb.toString(); 64 } 65 66 /** 67 * Returns the string form of the arguments to the given instruction. 68 * The instruction must be of this instance's format. If the instruction 69 * has no arguments, then the result should be {@code ""}, not 70 * {@code null}. 71 * 72 * <p>Subclasses must override this method.</p> 73 * 74 * @param insn {@code non-null;} the instruction 75 * @return {@code non-null;} the string form 76 */ insnArgString(DalvInsn insn)77 public abstract String insnArgString(DalvInsn insn); 78 79 /** 80 * Returns the associated comment for the given instruction, if any. 81 * The instruction must be of this instance's format. If the instruction 82 * has no comment, then the result should be {@code ""}, not 83 * {@code null}. 84 * 85 * <p>Subclasses must override this method.</p> 86 * 87 * @param insn {@code non-null;} the instruction 88 * @param noteIndices whether to include an explicit notation of 89 * constant pool indices 90 * @return {@code non-null;} the string form 91 */ insnCommentString(DalvInsn insn, boolean noteIndices)92 public abstract String insnCommentString(DalvInsn insn, 93 boolean noteIndices); 94 95 /** 96 * Gets the code size of instructions that use this format. The 97 * size is a number of 16-bit code units, not bytes. This should 98 * throw an exception if this format is of variable size. 99 * 100 * @return {@code >= 0;} the instruction length in 16-bit code units 101 */ codeSize()102 public abstract int codeSize(); 103 104 /** 105 * Returns whether or not the given instruction's arguments will 106 * fit in this instance's format. This includes such things as 107 * counting register arguments, checking register ranges, and 108 * making sure that additional arguments are of appropriate types 109 * and are in-range. If this format has a branch target but the 110 * instruction's branch offset is unknown, this method will simply 111 * not check the offset. 112 * 113 * <p>Subclasses must override this method.</p> 114 * 115 * @param insn {@code non-null;} the instruction to check 116 * @return {@code true} iff the instruction's arguments are 117 * appropriate for this instance, or {@code false} if not 118 */ isCompatible(DalvInsn insn)119 public abstract boolean isCompatible(DalvInsn insn); 120 121 /** 122 * Returns whether or not the given instruction's branch offset will 123 * fit in this instance's format. This always returns {@code false} 124 * for formats that don't include a branch offset. 125 * 126 * <p>The default implementation of this method always returns 127 * {@code false}. Subclasses must override this method if they 128 * include branch offsets.</p> 129 * 130 * @param insn {@code non-null;} the instruction to check 131 * @return {@code true} iff the instruction's branch offset is 132 * appropriate for this instance, or {@code false} if not 133 */ branchFits(TargetInsn insn)134 public boolean branchFits(TargetInsn insn) { 135 return false; 136 } 137 138 /** 139 * Returns the next instruction format to try to match an instruction 140 * with, presuming that this instance isn't compatible, if any. 141 * 142 * <p>Subclasses must override this method.</p> 143 * 144 * @return {@code null-ok;} the next format to try, or {@code null} if 145 * there are no suitable alternatives 146 */ nextUp()147 public abstract InsnFormat nextUp(); 148 149 /** 150 * Writes the code units for the given instruction to the given 151 * output destination. The instruction must be of this instance's format. 152 * 153 * <p>Subclasses must override this method.</p> 154 * 155 * @param out {@code non-null;} the output destination to write to 156 * @param insn {@code non-null;} the instruction to write 157 */ writeTo(AnnotatedOutput out, DalvInsn insn)158 public abstract void writeTo(AnnotatedOutput out, DalvInsn insn); 159 160 /** 161 * Helper method to return a register list string. 162 * 163 * @param list {@code non-null;} the list of registers 164 * @return {@code non-null;} the string form 165 */ regListString(RegisterSpecList list)166 protected static String regListString(RegisterSpecList list) { 167 int sz = list.size(); 168 StringBuffer sb = new StringBuffer(sz * 5 + 2); 169 170 sb.append('{'); 171 172 for (int i = 0; i < sz; i++) { 173 if (i != 0) { 174 sb.append(", "); 175 } 176 sb.append(list.get(i).regString()); 177 } 178 179 sb.append('}'); 180 181 return sb.toString(); 182 } 183 184 /** 185 * Helper method to return a literal bits argument string. 186 * 187 * @param value the value 188 * @return {@code non-null;} the string form 189 */ literalBitsString(CstLiteralBits value)190 protected static String literalBitsString(CstLiteralBits value) { 191 StringBuffer sb = new StringBuffer(100); 192 193 sb.append('#'); 194 195 if (value instanceof CstKnownNull) { 196 sb.append("null"); 197 } else { 198 sb.append(value.typeName()); 199 sb.append(' '); 200 sb.append(value.toHuman()); 201 } 202 203 return sb.toString(); 204 } 205 206 /** 207 * Helper method to return a literal bits comment string. 208 * 209 * @param value the value 210 * @param width the width of the constant, in bits (used for displaying 211 * the uninterpreted bits; one of: {@code 4 8 16 32 64} 212 * @return {@code non-null;} the comment 213 */ literalBitsComment(CstLiteralBits value, int width)214 protected static String literalBitsComment(CstLiteralBits value, 215 int width) { 216 StringBuffer sb = new StringBuffer(20); 217 218 sb.append("#"); 219 220 long bits; 221 222 if (value instanceof CstLiteral64) { 223 bits = ((CstLiteral64) value).getLongBits(); 224 } else { 225 bits = value.getIntBits(); 226 } 227 228 switch (width) { 229 case 4: sb.append(Hex.uNibble((int) bits)); break; 230 case 8: sb.append(Hex.u1((int) bits)); break; 231 case 16: sb.append(Hex.u2((int) bits)); break; 232 case 32: sb.append(Hex.u4((int) bits)); break; 233 case 64: sb.append(Hex.u8(bits)); break; 234 default: { 235 throw new RuntimeException("shouldn't happen"); 236 } 237 } 238 239 return sb.toString(); 240 } 241 242 /** 243 * Helper method to return a branch address string. 244 * 245 * @param insn {@code non-null;} the instruction in question 246 * @return {@code non-null;} the string form of the instruction's branch target 247 */ branchString(DalvInsn insn)248 protected static String branchString(DalvInsn insn) { 249 TargetInsn ti = (TargetInsn) insn; 250 int address = ti.getTargetAddress(); 251 252 return (address == (char) address) ? Hex.u2(address) : Hex.u4(address); 253 } 254 255 /** 256 * Helper method to return the comment for a branch. 257 * 258 * @param insn {@code non-null;} the instruction in question 259 * @return {@code non-null;} the comment 260 */ branchComment(DalvInsn insn)261 protected static String branchComment(DalvInsn insn) { 262 TargetInsn ti = (TargetInsn) insn; 263 int offset = ti.getTargetOffset(); 264 265 return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset); 266 } 267 268 /** 269 * Helper method to return a constant string. 270 * 271 * @param insn {@code non-null;} a constant-bearing instruction 272 * @return {@code non-null;} the string form of the contained constant 273 */ cstString(DalvInsn insn)274 protected static String cstString(DalvInsn insn) { 275 CstInsn ci = (CstInsn) insn; 276 Constant cst = ci.getConstant(); 277 278 return cst.toHuman(); 279 } 280 281 /** 282 * Helper method to return an instruction comment for a constant. 283 * 284 * @param insn {@code non-null;} a constant-bearing instruction 285 * @return {@code non-null;} comment string representing the constant 286 */ cstComment(DalvInsn insn)287 protected static String cstComment(DalvInsn insn) { 288 CstInsn ci = (CstInsn) insn; 289 290 if (! ci.hasIndex()) { 291 return ""; 292 } 293 294 StringBuilder sb = new StringBuilder(20); 295 int index = ci.getIndex(); 296 297 sb.append(ci.getConstant().typeName()); 298 sb.append('@'); 299 300 if (index < 65536) { 301 sb.append(Hex.u2(index)); 302 } else { 303 sb.append(Hex.u4(index)); 304 } 305 306 return sb.toString(); 307 } 308 309 /** 310 * Helper method to determine if a signed int value fits in a nibble. 311 * 312 * @param value the value in question 313 * @return {@code true} iff it's in the range -8..+7 314 */ signedFitsInNibble(int value)315 protected static boolean signedFitsInNibble(int value) { 316 return (value >= -8) && (value <= 7); 317 } 318 319 /** 320 * Helper method to determine if an unsigned int value fits in a nibble. 321 * 322 * @param value the value in question 323 * @return {@code true} iff it's in the range 0..0xf 324 */ unsignedFitsInNibble(int value)325 protected static boolean unsignedFitsInNibble(int value) { 326 return value == (value & 0xf); 327 } 328 329 /** 330 * Helper method to determine if a signed int value fits in a byte. 331 * 332 * @param value the value in question 333 * @return {@code true} iff it's in the range -0x80..+0x7f 334 */ signedFitsInByte(int value)335 protected static boolean signedFitsInByte(int value) { 336 return (byte) value == value; 337 } 338 339 /** 340 * Helper method to determine if an unsigned int value fits in a byte. 341 * 342 * @param value the value in question 343 * @return {@code true} iff it's in the range 0..0xff 344 */ unsignedFitsInByte(int value)345 protected static boolean unsignedFitsInByte(int value) { 346 return value == (value & 0xff); 347 } 348 349 /** 350 * Helper method to determine if a signed int value fits in a short. 351 * 352 * @param value the value in question 353 * @return {@code true} iff it's in the range -0x8000..+0x7fff 354 */ signedFitsInShort(int value)355 protected static boolean signedFitsInShort(int value) { 356 return (short) value == value; 357 } 358 359 /** 360 * Helper method to determine if an unsigned int value fits in a short. 361 * 362 * @param value the value in question 363 * @return {@code true} iff it's in the range 0..0xffff 364 */ unsignedFitsInShort(int value)365 protected static boolean unsignedFitsInShort(int value) { 366 return value == (value & 0xffff); 367 } 368 369 /** 370 * Helper method to determine if a signed int value fits in three bytes. 371 * 372 * @param value the value in question 373 * @return {@code true} iff it's in the range -0x800000..+0x7fffff 374 */ signedFitsIn3Bytes(int value)375 protected static boolean signedFitsIn3Bytes(int value) { 376 return value == ((value << 8) >> 8); 377 } 378 379 /** 380 * Helper method to extract the callout-argument index from an 381 * appropriate instruction. 382 * 383 * @param insn {@code non-null;} the instruction 384 * @return {@code >= 0;} the callout argument index 385 */ argIndex(DalvInsn insn)386 protected static int argIndex(DalvInsn insn) { 387 int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue(); 388 389 if (arg < 0) { 390 throw new IllegalArgumentException("bogus insn"); 391 } 392 393 return arg; 394 } 395 396 /** 397 * Helper method to combine an opcode and a second byte of data into 398 * the appropriate form for emitting into a code buffer. 399 * 400 * @param insn {@code non-null;} the instruction containing the opcode 401 * @param arg {@code 0..255;} arbitrary other byte value 402 * @return combined value 403 */ opcodeUnit(DalvInsn insn, int arg)404 protected static short opcodeUnit(DalvInsn insn, int arg) { 405 if ((arg & 0xff) != arg) { 406 throw new IllegalArgumentException("arg out of range 0..255"); 407 } 408 409 int opcode = insn.getOpcode().getOpcode(); 410 411 if ((opcode & 0xff) != opcode) { 412 throw new IllegalArgumentException("opcode out of range 0..255"); 413 } 414 415 return (short) (opcode | (arg << 8)); 416 } 417 418 /** 419 * Helper method to combine two bytes into a code unit. 420 * 421 * @param low {@code 0..255;} low byte 422 * @param high {@code 0..255;} high byte 423 * @return combined value 424 */ codeUnit(int low, int high)425 protected static short codeUnit(int low, int high) { 426 if ((low & 0xff) != low) { 427 throw new IllegalArgumentException("low out of range 0..255"); 428 } 429 430 if ((high & 0xff) != high) { 431 throw new IllegalArgumentException("high out of range 0..255"); 432 } 433 434 return (short) (low | (high << 8)); 435 } 436 437 /** 438 * Helper method to combine four nibbles into a code unit. 439 * 440 * @param n0 {@code 0..15;} low nibble 441 * @param n1 {@code 0..15;} medium-low nibble 442 * @param n2 {@code 0..15;} medium-high nibble 443 * @param n3 {@code 0..15;} high nibble 444 * @return combined value 445 */ codeUnit(int n0, int n1, int n2, int n3)446 protected static short codeUnit(int n0, int n1, int n2, int n3) { 447 if ((n0 & 0xf) != n0) { 448 throw new IllegalArgumentException("n0 out of range 0..15"); 449 } 450 451 if ((n1 & 0xf) != n1) { 452 throw new IllegalArgumentException("n1 out of range 0..15"); 453 } 454 455 if ((n2 & 0xf) != n2) { 456 throw new IllegalArgumentException("n2 out of range 0..15"); 457 } 458 459 if ((n3 & 0xf) != n3) { 460 throw new IllegalArgumentException("n3 out of range 0..15"); 461 } 462 463 return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12)); 464 } 465 466 /** 467 * Helper method to combine two nibbles into a byte. 468 * 469 * @param low {@code 0..15;} low nibble 470 * @param high {@code 0..15;} high nibble 471 * @return {@code 0..255;} combined value 472 */ makeByte(int low, int high)473 protected static int makeByte(int low, int high) { 474 if ((low & 0xf) != low) { 475 throw new IllegalArgumentException("low out of range 0..15"); 476 } 477 478 if ((high & 0xf) != high) { 479 throw new IllegalArgumentException("high out of range 0..15"); 480 } 481 482 return low | (high << 4); 483 } 484 485 /** 486 * Writes one code unit to the given output destination. 487 * 488 * @param out {@code non-null;} where to write to 489 * @param c0 code unit to write 490 */ write(AnnotatedOutput out, short c0)491 protected static void write(AnnotatedOutput out, short c0) { 492 out.writeShort(c0); 493 } 494 495 /** 496 * Writes two code units to the given output destination. 497 * 498 * @param out {@code non-null;} where to write to 499 * @param c0 code unit to write 500 * @param c1 code unit to write 501 */ write(AnnotatedOutput out, short c0, short c1)502 protected static void write(AnnotatedOutput out, short c0, short c1) { 503 out.writeShort(c0); 504 out.writeShort(c1); 505 } 506 507 /** 508 * Writes three code units to the given output destination. 509 * 510 * @param out {@code non-null;} where to write to 511 * @param c0 code unit to write 512 * @param c1 code unit to write 513 * @param c2 code unit to write 514 */ write(AnnotatedOutput out, short c0, short c1, short c2)515 protected static void write(AnnotatedOutput out, short c0, short c1, 516 short c2) { 517 out.writeShort(c0); 518 out.writeShort(c1); 519 out.writeShort(c2); 520 } 521 522 /** 523 * Writes four code units to the given output destination. 524 * 525 * @param out {@code non-null;} where to write to 526 * @param c0 code unit to write 527 * @param c1 code unit to write 528 * @param c2 code unit to write 529 * @param c3 code unit to write 530 */ write(AnnotatedOutput out, short c0, short c1, short c2, short c3)531 protected static void write(AnnotatedOutput out, short c0, short c1, 532 short c2, short c3) { 533 out.writeShort(c0); 534 out.writeShort(c1); 535 out.writeShort(c2); 536 out.writeShort(c3); 537 } 538 539 /** 540 * Writes five code units to the given output destination. 541 * 542 * @param out {@code non-null;} where to write to 543 * @param c0 code unit to write 544 * @param c1 code unit to write 545 * @param c2 code unit to write 546 * @param c3 code unit to write 547 * @param c4 code unit to write 548 */ write(AnnotatedOutput out, short c0, short c1, short c2, short c3, short c4)549 protected static void write(AnnotatedOutput out, short c0, short c1, 550 short c2, short c3, short c4) { 551 out.writeShort(c0); 552 out.writeShort(c1); 553 out.writeShort(c2); 554 out.writeShort(c3); 555 out.writeShort(c4); 556 } 557 558 /** 559 * Writes six code units to the given output destination. 560 * 561 * @param out {@code non-null;} where to write to 562 * @param c0 code unit to write 563 * @param c1 code unit to write 564 * @param c2 code unit to write 565 * @param c3 code unit to write 566 * @param c4 code unit to write 567 * @param c5 code unit to write 568 */ write(AnnotatedOutput out, short c0, short c1, short c2, short c3, short c4, short c5)569 protected static void write(AnnotatedOutput out, short c0, short c1, 570 short c2, short c3, short c4, short c5) { 571 out.writeShort(c0); 572 out.writeShort(c1); 573 out.writeShort(c2); 574 out.writeShort(c3); 575 out.writeShort(c4); 576 out.writeShort(c5); 577 } 578 } 579