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