InsnFormat.java revision 71eee1f0c2eb514585fdbee16730c9c2209e8f68
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 17package com.android.dx.dex.code; 18 19import com.android.dx.rop.code.RegisterSpec; 20import com.android.dx.rop.code.RegisterSpecList; 21import com.android.dx.rop.cst.Constant; 22import com.android.dx.rop.cst.CstInteger; 23import com.android.dx.rop.cst.CstKnownNull; 24import com.android.dx.rop.cst.CstLiteral64; 25import com.android.dx.rop.cst.CstLiteralBits; 26import com.android.dx.util.AnnotatedOutput; 27import com.android.dx.util.Hex; 28 29/** 30 * Base class for all instruction format handlers. Instruction format 31 * handlers know how to translate {@link DalvInsn} instances into 32 * streams of code units, as well as human-oriented listing strings 33 * representing such translations. 34 */ 35public abstract class InsnFormat { 36 /** 37 * flag to enable/disable the new extended opcode formats; meant as a 38 * temporary measure until VM support for the salient opcodes is 39 * added. TODO: Remove this declaration when the VM can deal. 40 */ 41 public static boolean ALLOW_EXTENDED_OPCODES = false; 42 43 /** 44 * Returns the string form, suitable for inclusion in a listing 45 * dump, of the given instruction. The instruction must be of this 46 * instance's format for proper operation. 47 * 48 * @param insn {@code non-null;} the instruction 49 * @param noteIndices whether to include an explicit notation of 50 * constant pool indices 51 * @return {@code non-null;} the string form 52 */ 53 public final String listingString(DalvInsn insn, boolean noteIndices) { 54 String op = insn.getOpcode().getName(); 55 String arg = insnArgString(insn); 56 String comment = insnCommentString(insn, noteIndices); 57 StringBuilder sb = new StringBuilder(100); 58 59 sb.append(op); 60 61 if (arg.length() != 0) { 62 sb.append(' '); 63 sb.append(arg); 64 } 65 66 if (comment.length() != 0) { 67 sb.append(" // "); 68 sb.append(comment); 69 } 70 71 return sb.toString(); 72 } 73 74 /** 75 * Returns the string form of the arguments to the given instruction. 76 * The instruction must be of this instance's format. If the instruction 77 * has no arguments, then the result should be {@code ""}, not 78 * {@code null}. 79 * 80 * <p>Subclasses must override this method.</p> 81 * 82 * @param insn {@code non-null;} the instruction 83 * @return {@code non-null;} the string form 84 */ 85 public abstract String insnArgString(DalvInsn insn); 86 87 /** 88 * Returns the associated comment for the given instruction, if any. 89 * The instruction must be of this instance's format. If the instruction 90 * has no comment, then the result should be {@code ""}, not 91 * {@code null}. 92 * 93 * <p>Subclasses must override this method.</p> 94 * 95 * @param insn {@code non-null;} the instruction 96 * @param noteIndices whether to include an explicit notation of 97 * constant pool indices 98 * @return {@code non-null;} the string form 99 */ 100 public abstract String insnCommentString(DalvInsn insn, 101 boolean noteIndices); 102 103 /** 104 * Gets the code size of instructions that use this format. The 105 * size is a number of 16-bit code units, not bytes. This should 106 * throw an exception if this format is of variable size. 107 * 108 * @return {@code >= 0;} the instruction length in 16-bit code units 109 */ 110 public abstract int codeSize(); 111 112 /** 113 * Returns whether or not the given instruction's arguments will 114 * fit in this instance's format. This includes such things as 115 * counting register arguments, checking register ranges, and 116 * making sure that additional arguments are of appropriate types 117 * and are in-range. If this format has a branch target but the 118 * instruction's branch offset is unknown, this method will simply 119 * not check the offset. 120 * 121 * <p>Subclasses must override this method.</p> 122 * 123 * @param insn {@code non-null;} the instruction to check 124 * @return {@code true} iff the instruction's arguments are 125 * appropriate for this instance, or {@code false} if not 126 */ 127 public abstract boolean isCompatible(DalvInsn insn); 128 129 /** 130 * Returns whether or not the given instruction's branch offset will 131 * fit in this instance's format. This always returns {@code false} 132 * for formats that don't include a branch offset. 133 * 134 * <p>The default implementation of this method always returns 135 * {@code false}. Subclasses must override this method if they 136 * include branch offsets.</p> 137 * 138 * @param insn {@code non-null;} the instruction to check 139 * @return {@code true} iff the instruction's branch offset is 140 * appropriate for this instance, or {@code false} if not 141 */ 142 public boolean branchFits(TargetInsn insn) { 143 return false; 144 } 145 146 /** 147 * Writes the code units for the given instruction to the given 148 * output destination. The instruction must be of this instance's format. 149 * 150 * <p>Subclasses must override this method.</p> 151 * 152 * @param out {@code non-null;} the output destination to write to 153 * @param insn {@code non-null;} the instruction to write 154 */ 155 public abstract void writeTo(AnnotatedOutput out, DalvInsn insn); 156 157 /** 158 * Helper method to return a register list string. 159 * 160 * @param list {@code non-null;} the list of registers 161 * @return {@code non-null;} the string form 162 */ 163 protected static String regListString(RegisterSpecList list) { 164 int sz = list.size(); 165 StringBuffer sb = new StringBuffer(sz * 5 + 2); 166 167 sb.append('{'); 168 169 for (int i = 0; i < sz; i++) { 170 if (i != 0) { 171 sb.append(", "); 172 } 173 sb.append(list.get(i).regString()); 174 } 175 176 sb.append('}'); 177 178 return sb.toString(); 179 } 180 181 /** 182 * Helper method to return a register range string. 183 * 184 * @param list {@code non-null;} the list of registers (which must be 185 * sequential) 186 * @return {@code non-null;} the string form 187 */ 188 protected static String regRangeString(RegisterSpecList list) { 189 int size = list.size(); 190 StringBuilder sb = new StringBuilder(30); 191 192 sb.append("{"); 193 194 switch (size) { 195 case 0: { 196 // Nothing to do. 197 break; 198 } 199 case 1: { 200 sb.append(list.get(0).regString()); 201 break; 202 } 203 default: { 204 RegisterSpec lastReg = list.get(size - 1); 205 if (lastReg.getCategory() == 2) { 206 /* 207 * Add one to properly represent a list-final 208 * category-2 register. 209 */ 210 lastReg = lastReg.withOffset(1); 211 } 212 213 sb.append(list.get(0).regString()); 214 sb.append(".."); 215 sb.append(lastReg.regString()); 216 } 217 } 218 219 sb.append("}"); 220 221 return sb.toString(); 222 } 223 224 /** 225 * Helper method to return a literal bits argument string. 226 * 227 * @param value the value 228 * @return {@code non-null;} the string form 229 */ 230 protected static String literalBitsString(CstLiteralBits value) { 231 StringBuffer sb = new StringBuffer(100); 232 233 sb.append('#'); 234 235 if (value instanceof CstKnownNull) { 236 sb.append("null"); 237 } else { 238 sb.append(value.typeName()); 239 sb.append(' '); 240 sb.append(value.toHuman()); 241 } 242 243 return sb.toString(); 244 } 245 246 /** 247 * Helper method to return a literal bits comment string. 248 * 249 * @param value the value 250 * @param width the width of the constant, in bits (used for displaying 251 * the uninterpreted bits; one of: {@code 4 8 16 32 64} 252 * @return {@code non-null;} the comment 253 */ 254 protected static String literalBitsComment(CstLiteralBits value, 255 int width) { 256 StringBuffer sb = new StringBuffer(20); 257 258 sb.append("#"); 259 260 long bits; 261 262 if (value instanceof CstLiteral64) { 263 bits = ((CstLiteral64) value).getLongBits(); 264 } else { 265 bits = value.getIntBits(); 266 } 267 268 switch (width) { 269 case 4: sb.append(Hex.uNibble((int) bits)); break; 270 case 8: sb.append(Hex.u1((int) bits)); break; 271 case 16: sb.append(Hex.u2((int) bits)); break; 272 case 32: sb.append(Hex.u4((int) bits)); break; 273 case 64: sb.append(Hex.u8(bits)); break; 274 default: { 275 throw new RuntimeException("shouldn't happen"); 276 } 277 } 278 279 return sb.toString(); 280 } 281 282 /** 283 * Helper method to return a branch address string. 284 * 285 * @param insn {@code non-null;} the instruction in question 286 * @return {@code non-null;} the string form of the instruction's 287 * branch target 288 */ 289 protected static String branchString(DalvInsn insn) { 290 TargetInsn ti = (TargetInsn) insn; 291 int address = ti.getTargetAddress(); 292 293 return (address == (char) address) ? Hex.u2(address) : Hex.u4(address); 294 } 295 296 /** 297 * Helper method to return the comment for a branch. 298 * 299 * @param insn {@code non-null;} the instruction in question 300 * @return {@code non-null;} the comment 301 */ 302 protected static String branchComment(DalvInsn insn) { 303 TargetInsn ti = (TargetInsn) insn; 304 int offset = ti.getTargetOffset(); 305 306 return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset); 307 } 308 309 /** 310 * Helper method to return the constant string for a {@link CstInsn} 311 * in human form. 312 * 313 * @param insn {@code non-null;} a constant-bearing instruction 314 * @return {@code non-null;} the human string form of the contained 315 * constant 316 */ 317 protected static String cstString(DalvInsn insn) { 318 CstInsn ci = (CstInsn) insn; 319 Constant cst = ci.getConstant(); 320 321 return cst.toHuman(); 322 } 323 324 /** 325 * Helper method to return an instruction comment for a constant. 326 * 327 * @param insn {@code non-null;} a constant-bearing instruction 328 * @return {@code non-null;} comment string representing the constant 329 */ 330 protected static String cstComment(DalvInsn insn) { 331 CstInsn ci = (CstInsn) insn; 332 333 if (! ci.hasIndex()) { 334 return ""; 335 } 336 337 StringBuilder sb = new StringBuilder(20); 338 int index = ci.getIndex(); 339 340 sb.append(ci.getConstant().typeName()); 341 sb.append('@'); 342 343 if (index < 65536) { 344 sb.append(Hex.u2(index)); 345 } else { 346 sb.append(Hex.u4(index)); 347 } 348 349 return sb.toString(); 350 } 351 352 /** 353 * Helper method to determine if a signed int value fits in a nibble. 354 * 355 * @param value the value in question 356 * @return {@code true} iff it's in the range -8..+7 357 */ 358 protected static boolean signedFitsInNibble(int value) { 359 return (value >= -8) && (value <= 7); 360 } 361 362 /** 363 * Helper method to determine if an unsigned int value fits in a nibble. 364 * 365 * @param value the value in question 366 * @return {@code true} iff it's in the range 0..0xf 367 */ 368 protected static boolean unsignedFitsInNibble(int value) { 369 return value == (value & 0xf); 370 } 371 372 /** 373 * Helper method to determine if a signed int value fits in a byte. 374 * 375 * @param value the value in question 376 * @return {@code true} iff it's in the range -0x80..+0x7f 377 */ 378 protected static boolean signedFitsInByte(int value) { 379 return (byte) value == value; 380 } 381 382 /** 383 * Helper method to determine if an unsigned int value fits in a byte. 384 * 385 * @param value the value in question 386 * @return {@code true} iff it's in the range 0..0xff 387 */ 388 protected static boolean unsignedFitsInByte(int value) { 389 return value == (value & 0xff); 390 } 391 392 /** 393 * Helper method to determine if a signed int value fits in a short. 394 * 395 * @param value the value in question 396 * @return {@code true} iff it's in the range -0x8000..+0x7fff 397 */ 398 protected static boolean signedFitsInShort(int value) { 399 return (short) value == value; 400 } 401 402 /** 403 * Helper method to determine if an unsigned int value fits in a short. 404 * 405 * @param value the value in question 406 * @return {@code true} iff it's in the range 0..0xffff 407 */ 408 protected static boolean unsignedFitsInShort(int value) { 409 return value == (value & 0xffff); 410 } 411 412 /** 413 * Helper method to determine if a list of registers are sequential, 414 * including degenerate cases for empty or single-element lists. 415 * 416 * @param list {@code non-null;} the list of registers 417 * @return {@code true} iff the list is sequentially ordered 418 */ 419 protected static boolean isRegListSequential(RegisterSpecList list) { 420 int sz = list.size(); 421 422 if (sz < 2) { 423 return true; 424 } 425 426 int first = list.get(0).getReg(); 427 int next = first; 428 429 for (int i = 0; i < sz; i++) { 430 RegisterSpec one = list.get(i); 431 if (one.getReg() != next) { 432 return false; 433 } 434 next += one.getCategory(); 435 } 436 437 return true; 438 } 439 440 /** 441 * Helper method to extract the callout-argument index from an 442 * appropriate instruction. 443 * 444 * @param insn {@code non-null;} the instruction 445 * @return {@code >= 0;} the callout argument index 446 */ 447 protected static int argIndex(DalvInsn insn) { 448 int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue(); 449 450 if (arg < 0) { 451 throw new IllegalArgumentException("bogus insn"); 452 } 453 454 return arg; 455 } 456 457 /** 458 * Helper method to combine an opcode and a second byte of data into 459 * the appropriate form for emitting into a code buffer. 460 * 461 * @param insn {@code non-null;} the instruction containing the opcode 462 * @param arg {@code 0..255;} arbitrary other byte value 463 * @return combined value 464 */ 465 protected static short opcodeUnit(DalvInsn insn, int arg) { 466 if ((arg & 0xff) != arg) { 467 throw new IllegalArgumentException("arg out of range 0..255"); 468 } 469 470 int opcode = insn.getOpcode().getOpcode(); 471 472 if ((opcode & 0xff) != opcode) { 473 throw new IllegalArgumentException("opcode out of range 0..255"); 474 } 475 476 return (short) (opcode | (arg << 8)); 477 } 478 479 /** 480 * Helper method to get an extended (16-bit) opcode out of an 481 * instruction, returning it as a code unit. The opcode 482 * <i>must</i> be an extended opcode. 483 * 484 * @param insn {@code non-null;} the instruction containing the 485 * extended opcode 486 * @return the opcode as a code unit 487 */ 488 protected static short opcodeUnit(DalvInsn insn) { 489 int opcode = insn.getOpcode().getOpcode(); 490 491 if ((opcode < 0xff) || (opcode > 0xffff)) { 492 throw new IllegalArgumentException( 493 "extended opcode out of range 255..65535"); 494 } 495 496 return (short) opcode; 497 } 498 499 /** 500 * Helper method to combine two bytes into a code unit. 501 * 502 * @param low {@code 0..255;} low byte 503 * @param high {@code 0..255;} high byte 504 * @return combined value 505 */ 506 protected static short codeUnit(int low, int high) { 507 if ((low & 0xff) != low) { 508 throw new IllegalArgumentException("low out of range 0..255"); 509 } 510 511 if ((high & 0xff) != high) { 512 throw new IllegalArgumentException("high out of range 0..255"); 513 } 514 515 return (short) (low | (high << 8)); 516 } 517 518 /** 519 * Helper method to combine four nibbles into a code unit. 520 * 521 * @param n0 {@code 0..15;} low nibble 522 * @param n1 {@code 0..15;} medium-low nibble 523 * @param n2 {@code 0..15;} medium-high nibble 524 * @param n3 {@code 0..15;} high nibble 525 * @return combined value 526 */ 527 protected static short codeUnit(int n0, int n1, int n2, int n3) { 528 if ((n0 & 0xf) != n0) { 529 throw new IllegalArgumentException("n0 out of range 0..15"); 530 } 531 532 if ((n1 & 0xf) != n1) { 533 throw new IllegalArgumentException("n1 out of range 0..15"); 534 } 535 536 if ((n2 & 0xf) != n2) { 537 throw new IllegalArgumentException("n2 out of range 0..15"); 538 } 539 540 if ((n3 & 0xf) != n3) { 541 throw new IllegalArgumentException("n3 out of range 0..15"); 542 } 543 544 return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12)); 545 } 546 547 /** 548 * Helper method to combine two nibbles into a byte. 549 * 550 * @param low {@code 0..15;} low nibble 551 * @param high {@code 0..15;} high nibble 552 * @return {@code 0..255;} combined value 553 */ 554 protected static int makeByte(int low, int high) { 555 if ((low & 0xf) != low) { 556 throw new IllegalArgumentException("low out of range 0..15"); 557 } 558 559 if ((high & 0xf) != high) { 560 throw new IllegalArgumentException("high out of range 0..15"); 561 } 562 563 return low | (high << 4); 564 } 565 566 /** 567 * Writes one code unit to the given output destination. 568 * 569 * @param out {@code non-null;} where to write to 570 * @param c0 code unit to write 571 */ 572 protected static void write(AnnotatedOutput out, short c0) { 573 out.writeShort(c0); 574 } 575 576 /** 577 * Writes two code units to the given output destination. 578 * 579 * @param out {@code non-null;} where to write to 580 * @param c0 code unit to write 581 * @param c1 code unit to write 582 */ 583 protected static void write(AnnotatedOutput out, short c0, short c1) { 584 out.writeShort(c0); 585 out.writeShort(c1); 586 } 587 588 /** 589 * Writes three code units to the given output destination. 590 * 591 * @param out {@code non-null;} where to write to 592 * @param c0 code unit to write 593 * @param c1 code unit to write 594 * @param c2 code unit to write 595 */ 596 protected static void write(AnnotatedOutput out, short c0, short c1, 597 short c2) { 598 out.writeShort(c0); 599 out.writeShort(c1); 600 out.writeShort(c2); 601 } 602 603 /** 604 * Writes four code units to the given output destination. 605 * 606 * @param out {@code non-null;} where to write to 607 * @param c0 code unit to write 608 * @param c1 code unit to write 609 * @param c2 code unit to write 610 * @param c3 code unit to write 611 */ 612 protected static void write(AnnotatedOutput out, short c0, short c1, 613 short c2, short c3) { 614 out.writeShort(c0); 615 out.writeShort(c1); 616 out.writeShort(c2); 617 out.writeShort(c3); 618 } 619 620 /** 621 * Writes five code units to the given output destination. 622 * 623 * @param out {@code non-null;} where to write to 624 * @param c0 code unit to write 625 * @param c1 code unit to write 626 * @param c2 code unit to write 627 * @param c3 code unit to write 628 * @param c4 code unit to write 629 */ 630 protected static void write(AnnotatedOutput out, short c0, short c1, 631 short c2, short c3, short c4) { 632 out.writeShort(c0); 633 out.writeShort(c1); 634 out.writeShort(c2); 635 out.writeShort(c3); 636 out.writeShort(c4); 637 } 638 639 /** 640 * Writes three code units to the given output destination, where the 641 * second and third are represented as single <code>int</code> and emitted 642 * in little-endian order. 643 * 644 * @param out {@code non-null;} where to write to 645 * @param c0 code unit to write 646 * @param c1c2 code unit pair to write 647 */ 648 protected static void write(AnnotatedOutput out, short c0, int c1c2) { 649 write(out, c0, (short) c1c2, (short) (c1c2 >> 16)); 650 } 651 652 /** 653 * Writes four code units to the given output destination, where the 654 * second and third are represented as single <code>int</code> and emitted 655 * in little-endian order. 656 * 657 * @param out {@code non-null;} where to write to 658 * @param c0 code unit to write 659 * @param c1c2 code unit pair to write 660 * @param c3 code unit to write 661 */ 662 protected static void write(AnnotatedOutput out, short c0, int c1c2, 663 short c3) { 664 write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3); 665 } 666 667 /** 668 * Writes five code units to the given output destination, where the 669 * second and third are represented as single <code>int</code> and emitted 670 * in little-endian order. 671 * 672 * @param out {@code non-null;} where to write to 673 * @param c0 code unit to write 674 * @param c1c2 code unit pair to write 675 * @param c3 code unit to write 676 * @param c4 code unit to write 677 */ 678 protected static void write(AnnotatedOutput out, short c0, int c1c2, 679 short c3, short c4) { 680 write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4); 681 } 682 683 /** 684 * Writes five code units to the given output destination, where the 685 * second through fifth are represented as single <code>long</code> 686 * and emitted in little-endian order. 687 * 688 * @param out {@code non-null;} where to write to 689 * @param c0 code unit to write 690 * @param c1c2c3c4 code unit quad to write 691 */ 692 protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) { 693 write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16), 694 (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48)); 695 } 696} 697