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.dx.dex.code; 18 19 import com.android.dx.rop.code.RegisterSpec; 20 import com.android.dx.rop.code.RegisterSpecList; 21 import com.android.dx.rop.cst.CstInteger; 22 import com.android.dx.rop.cst.CstKnownNull; 23 import com.android.dx.rop.cst.CstLiteral64; 24 import com.android.dx.rop.cst.CstLiteralBits; 25 import com.android.dx.util.AnnotatedOutput; 26 import com.android.dx.util.Hex; 27 import java.util.BitSet; 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 */ 35 public 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 final boolean ALLOW_EXTENDED_OPCODES = true; 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 */ listingString(DalvInsn insn, boolean noteIndices)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 */ insnArgString(DalvInsn insn)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 */ insnCommentString(DalvInsn insn, boolean noteIndices)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 */ codeSize()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 */ isCompatible(DalvInsn insn)127 public abstract boolean isCompatible(DalvInsn insn); 128 129 /** 130 * Returns which of a given instruction's registers will fit in 131 * this instance's format. 132 * 133 * <p>The default implementation of this method always returns 134 * an empty BitSet. Subclasses must override this method if they 135 * have registers.</p> 136 * 137 * @param insn {@code non-null;} the instruction to check 138 * @return {@code non-null;} a BitSet flagging registers in the 139 * register list that are compatible to this format 140 */ compatibleRegs(DalvInsn insn)141 public BitSet compatibleRegs(DalvInsn insn) { 142 return new BitSet(); 143 } 144 145 /** 146 * Returns whether or not the given instruction's branch offset will 147 * fit in this instance's format. This always returns {@code false} 148 * for formats that don't include a branch offset. 149 * 150 * <p>The default implementation of this method always returns 151 * {@code false}. Subclasses must override this method if they 152 * include branch offsets.</p> 153 * 154 * @param insn {@code non-null;} the instruction to check 155 * @return {@code true} iff the instruction's branch offset is 156 * appropriate for this instance, or {@code false} if not 157 */ branchFits(TargetInsn insn)158 public boolean branchFits(TargetInsn insn) { 159 return false; 160 } 161 162 /** 163 * Writes the code units for the given instruction to the given 164 * output destination. The instruction must be of this instance's format. 165 * 166 * <p>Subclasses must override this method.</p> 167 * 168 * @param out {@code non-null;} the output destination to write to 169 * @param insn {@code non-null;} the instruction to write 170 */ writeTo(AnnotatedOutput out, DalvInsn insn)171 public abstract void writeTo(AnnotatedOutput out, DalvInsn insn); 172 173 /** 174 * Helper method to return a register list string. 175 * 176 * @param list {@code non-null;} the list of registers 177 * @return {@code non-null;} the string form 178 */ regListString(RegisterSpecList list)179 protected static String regListString(RegisterSpecList list) { 180 int sz = list.size(); 181 StringBuilder sb = new StringBuilder(sz * 5 + 2); 182 183 sb.append('{'); 184 185 for (int i = 0; i < sz; i++) { 186 if (i != 0) { 187 sb.append(", "); 188 } 189 sb.append(list.get(i).regString()); 190 } 191 192 sb.append('}'); 193 194 return sb.toString(); 195 } 196 197 /** 198 * Helper method to return a register range string. 199 * 200 * @param list {@code non-null;} the list of registers (which must be 201 * sequential) 202 * @return {@code non-null;} the string form 203 */ regRangeString(RegisterSpecList list)204 protected static String regRangeString(RegisterSpecList list) { 205 int size = list.size(); 206 StringBuilder sb = new StringBuilder(30); 207 208 sb.append("{"); 209 210 switch (size) { 211 case 0: { 212 // Nothing to do. 213 break; 214 } 215 case 1: { 216 sb.append(list.get(0).regString()); 217 break; 218 } 219 default: { 220 RegisterSpec lastReg = list.get(size - 1); 221 if (lastReg.getCategory() == 2) { 222 /* 223 * Add one to properly represent a list-final 224 * category-2 register. 225 */ 226 lastReg = lastReg.withOffset(1); 227 } 228 229 sb.append(list.get(0).regString()); 230 sb.append(".."); 231 sb.append(lastReg.regString()); 232 } 233 } 234 235 sb.append("}"); 236 237 return sb.toString(); 238 } 239 240 /** 241 * Helper method to return a literal bits argument string. 242 * 243 * @param value the value 244 * @return {@code non-null;} the string form 245 */ literalBitsString(CstLiteralBits value)246 protected static String literalBitsString(CstLiteralBits value) { 247 StringBuilder sb = new StringBuilder(100); 248 249 sb.append('#'); 250 251 if (value instanceof CstKnownNull) { 252 sb.append("null"); 253 } else { 254 sb.append(value.typeName()); 255 sb.append(' '); 256 sb.append(value.toHuman()); 257 } 258 259 return sb.toString(); 260 } 261 262 /** 263 * Helper method to return a literal bits comment string. 264 * 265 * @param value the value 266 * @param width the width of the constant, in bits (used for displaying 267 * the uninterpreted bits; one of: {@code 4 8 16 32 64} 268 * @return {@code non-null;} the comment 269 */ literalBitsComment(CstLiteralBits value, int width)270 protected static String literalBitsComment(CstLiteralBits value, 271 int width) { 272 StringBuilder sb = new StringBuilder(20); 273 274 sb.append("#"); 275 276 long bits; 277 278 if (value instanceof CstLiteral64) { 279 bits = ((CstLiteral64) value).getLongBits(); 280 } else { 281 bits = value.getIntBits(); 282 } 283 284 switch (width) { 285 case 4: sb.append(Hex.uNibble((int) bits)); break; 286 case 8: sb.append(Hex.u1((int) bits)); break; 287 case 16: sb.append(Hex.u2((int) bits)); break; 288 case 32: sb.append(Hex.u4((int) bits)); break; 289 case 64: sb.append(Hex.u8(bits)); break; 290 default: { 291 throw new RuntimeException("shouldn't happen"); 292 } 293 } 294 295 return sb.toString(); 296 } 297 298 /** 299 * Helper method to return a branch address string. 300 * 301 * @param insn {@code non-null;} the instruction in question 302 * @return {@code non-null;} the string form of the instruction's 303 * branch target 304 */ branchString(DalvInsn insn)305 protected static String branchString(DalvInsn insn) { 306 TargetInsn ti = (TargetInsn) insn; 307 int address = ti.getTargetAddress(); 308 309 return (address == (char) address) ? Hex.u2(address) : Hex.u4(address); 310 } 311 312 /** 313 * Helper method to return the comment for a branch. 314 * 315 * @param insn {@code non-null;} the instruction in question 316 * @return {@code non-null;} the comment 317 */ branchComment(DalvInsn insn)318 protected static String branchComment(DalvInsn insn) { 319 TargetInsn ti = (TargetInsn) insn; 320 int offset = ti.getTargetOffset(); 321 322 return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset); 323 } 324 325 /** 326 * Helper method to determine if a signed int value fits in a nibble. 327 * 328 * @param value the value in question 329 * @return {@code true} iff it's in the range -8..+7 330 */ signedFitsInNibble(int value)331 protected static boolean signedFitsInNibble(int value) { 332 return (value >= -8) && (value <= 7); 333 } 334 335 /** 336 * Helper method to determine if an unsigned int value fits in a nibble. 337 * 338 * @param value the value in question 339 * @return {@code true} iff it's in the range 0..0xf 340 */ unsignedFitsInNibble(int value)341 protected static boolean unsignedFitsInNibble(int value) { 342 return value == (value & 0xf); 343 } 344 345 /** 346 * Helper method to determine if a signed int value fits in a byte. 347 * 348 * @param value the value in question 349 * @return {@code true} iff it's in the range -0x80..+0x7f 350 */ signedFitsInByte(int value)351 protected static boolean signedFitsInByte(int value) { 352 return (byte) value == value; 353 } 354 355 /** 356 * Helper method to determine if an unsigned int value fits in a byte. 357 * 358 * @param value the value in question 359 * @return {@code true} iff it's in the range 0..0xff 360 */ unsignedFitsInByte(int value)361 protected static boolean unsignedFitsInByte(int value) { 362 return value == (value & 0xff); 363 } 364 365 /** 366 * Helper method to determine if a signed int value fits in a short. 367 * 368 * @param value the value in question 369 * @return {@code true} iff it's in the range -0x8000..+0x7fff 370 */ signedFitsInShort(int value)371 protected static boolean signedFitsInShort(int value) { 372 return (short) value == value; 373 } 374 375 /** 376 * Helper method to determine if an unsigned int value fits in a short. 377 * 378 * @param value the value in question 379 * @return {@code true} iff it's in the range 0..0xffff 380 */ unsignedFitsInShort(int value)381 protected static boolean unsignedFitsInShort(int value) { 382 return value == (value & 0xffff); 383 } 384 385 /** 386 * Helper method to determine if a list of registers are sequential, 387 * including degenerate cases for empty or single-element lists. 388 * 389 * @param list {@code non-null;} the list of registers 390 * @return {@code true} iff the list is sequentially ordered 391 */ isRegListSequential(RegisterSpecList list)392 protected static boolean isRegListSequential(RegisterSpecList list) { 393 int sz = list.size(); 394 395 if (sz < 2) { 396 return true; 397 } 398 399 int first = list.get(0).getReg(); 400 int next = first; 401 402 for (int i = 0; i < sz; i++) { 403 RegisterSpec one = list.get(i); 404 if (one.getReg() != next) { 405 return false; 406 } 407 next += one.getCategory(); 408 } 409 410 return true; 411 } 412 413 /** 414 * Helper method to extract the callout-argument index from an 415 * appropriate instruction. 416 * 417 * @param insn {@code non-null;} the instruction 418 * @return {@code >= 0;} the callout argument index 419 */ argIndex(DalvInsn insn)420 protected static int argIndex(DalvInsn insn) { 421 int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue(); 422 423 if (arg < 0) { 424 throw new IllegalArgumentException("bogus insn"); 425 } 426 427 return arg; 428 } 429 430 /** 431 * Helper method to combine an opcode and a second byte of data into 432 * the appropriate form for emitting into a code buffer. 433 * 434 * @param insn {@code non-null;} the instruction containing the opcode 435 * @param arg {@code 0..255;} arbitrary other byte value 436 * @return combined value 437 */ opcodeUnit(DalvInsn insn, int arg)438 protected static short opcodeUnit(DalvInsn insn, int arg) { 439 if ((arg & 0xff) != arg) { 440 throw new IllegalArgumentException("arg out of range 0..255"); 441 } 442 443 int opcode = insn.getOpcode().getOpcode(); 444 445 if ((opcode & 0xff) != opcode) { 446 throw new IllegalArgumentException("opcode out of range 0..255"); 447 } 448 449 return (short) (opcode | (arg << 8)); 450 } 451 452 /** 453 * Helper method to get an extended (16-bit) opcode out of an 454 * instruction, returning it as a code unit. The opcode 455 * <i>must</i> be an extended opcode. 456 * 457 * @param insn {@code non-null;} the instruction containing the 458 * extended opcode 459 * @return the opcode as a code unit 460 */ opcodeUnit(DalvInsn insn)461 protected static short opcodeUnit(DalvInsn insn) { 462 int opcode = insn.getOpcode().getOpcode(); 463 464 if ((opcode < 0x100) || (opcode > 0xffff)) { 465 throw new IllegalArgumentException("opcode out of range 0..65535"); 466 } 467 468 return (short) opcode; 469 } 470 471 /** 472 * Helper method to combine two bytes into a code unit. 473 * 474 * @param low {@code 0..255;} low byte 475 * @param high {@code 0..255;} high byte 476 * @return combined value 477 */ codeUnit(int low, int high)478 protected static short codeUnit(int low, int high) { 479 if ((low & 0xff) != low) { 480 throw new IllegalArgumentException("low out of range 0..255"); 481 } 482 483 if ((high & 0xff) != high) { 484 throw new IllegalArgumentException("high out of range 0..255"); 485 } 486 487 return (short) (low | (high << 8)); 488 } 489 490 /** 491 * Helper method to combine four nibbles into a code unit. 492 * 493 * @param n0 {@code 0..15;} low nibble 494 * @param n1 {@code 0..15;} medium-low nibble 495 * @param n2 {@code 0..15;} medium-high nibble 496 * @param n3 {@code 0..15;} high nibble 497 * @return combined value 498 */ codeUnit(int n0, int n1, int n2, int n3)499 protected static short codeUnit(int n0, int n1, int n2, int n3) { 500 if ((n0 & 0xf) != n0) { 501 throw new IllegalArgumentException("n0 out of range 0..15"); 502 } 503 504 if ((n1 & 0xf) != n1) { 505 throw new IllegalArgumentException("n1 out of range 0..15"); 506 } 507 508 if ((n2 & 0xf) != n2) { 509 throw new IllegalArgumentException("n2 out of range 0..15"); 510 } 511 512 if ((n3 & 0xf) != n3) { 513 throw new IllegalArgumentException("n3 out of range 0..15"); 514 } 515 516 return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12)); 517 } 518 519 /** 520 * Helper method to combine two nibbles into a byte. 521 * 522 * @param low {@code 0..15;} low nibble 523 * @param high {@code 0..15;} high nibble 524 * @return {@code 0..255;} combined value 525 */ makeByte(int low, int high)526 protected static int makeByte(int low, int high) { 527 if ((low & 0xf) != low) { 528 throw new IllegalArgumentException("low out of range 0..15"); 529 } 530 531 if ((high & 0xf) != high) { 532 throw new IllegalArgumentException("high out of range 0..15"); 533 } 534 535 return low | (high << 4); 536 } 537 538 /** 539 * Writes one code unit to the given output destination. 540 * 541 * @param out {@code non-null;} where to write to 542 * @param c0 code unit to write 543 */ write(AnnotatedOutput out, short c0)544 protected static void write(AnnotatedOutput out, short c0) { 545 out.writeShort(c0); 546 } 547 548 /** 549 * Writes two code units to the given output destination. 550 * 551 * @param out {@code non-null;} where to write to 552 * @param c0 code unit to write 553 * @param c1 code unit to write 554 */ write(AnnotatedOutput out, short c0, short c1)555 protected static void write(AnnotatedOutput out, short c0, short c1) { 556 out.writeShort(c0); 557 out.writeShort(c1); 558 } 559 560 /** 561 * Writes three code units to the given output destination. 562 * 563 * @param out {@code non-null;} where to write to 564 * @param c0 code unit to write 565 * @param c1 code unit to write 566 * @param c2 code unit to write 567 */ write(AnnotatedOutput out, short c0, short c1, short c2)568 protected static void write(AnnotatedOutput out, short c0, short c1, 569 short c2) { 570 out.writeShort(c0); 571 out.writeShort(c1); 572 out.writeShort(c2); 573 } 574 575 /** 576 * Writes four code units to the given output destination. 577 * 578 * @param out {@code non-null;} where to write to 579 * @param c0 code unit to write 580 * @param c1 code unit to write 581 * @param c2 code unit to write 582 * @param c3 code unit to write 583 */ write(AnnotatedOutput out, short c0, short c1, short c2, short c3)584 protected static void write(AnnotatedOutput out, short c0, short c1, 585 short c2, short c3) { 586 out.writeShort(c0); 587 out.writeShort(c1); 588 out.writeShort(c2); 589 out.writeShort(c3); 590 } 591 592 /** 593 * Writes five code units to the given output destination. 594 * 595 * @param out {@code non-null;} where to write to 596 * @param c0 code unit to write 597 * @param c1 code unit to write 598 * @param c2 code unit to write 599 * @param c3 code unit to write 600 * @param c4 code unit to write 601 */ write(AnnotatedOutput out, short c0, short c1, short c2, short c3, short c4)602 protected static void write(AnnotatedOutput out, short c0, short c1, 603 short c2, short c3, short c4) { 604 out.writeShort(c0); 605 out.writeShort(c1); 606 out.writeShort(c2); 607 out.writeShort(c3); 608 out.writeShort(c4); 609 } 610 611 /** 612 * Writes three code units to the given output destination, where the 613 * second and third are represented as single <code>int</code> and emitted 614 * in little-endian order. 615 * 616 * @param out {@code non-null;} where to write to 617 * @param c0 code unit to write 618 * @param c1c2 code unit pair to write 619 */ write(AnnotatedOutput out, short c0, int c1c2)620 protected static void write(AnnotatedOutput out, short c0, int c1c2) { 621 write(out, c0, (short) c1c2, (short) (c1c2 >> 16)); 622 } 623 624 /** 625 * Writes four code units to the given output destination, where the 626 * second and third are represented as single <code>int</code> and emitted 627 * in little-endian order. 628 * 629 * @param out {@code non-null;} where to write to 630 * @param c0 code unit to write 631 * @param c1c2 code unit pair to write 632 * @param c3 code unit to write 633 */ write(AnnotatedOutput out, short c0, int c1c2, short c3)634 protected static void write(AnnotatedOutput out, short c0, int c1c2, 635 short c3) { 636 write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3); 637 } 638 639 /** 640 * Writes five 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 * @param c3 code unit to write 648 * @param c4 code unit to write 649 */ write(AnnotatedOutput out, short c0, int c1c2, short c3, short c4)650 protected static void write(AnnotatedOutput out, short c0, int c1c2, 651 short c3, short c4) { 652 write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4); 653 } 654 655 /** 656 * Writes five code units to the given output destination, where the 657 * second through fifth are represented as single <code>long</code> 658 * and emitted in little-endian order. 659 * 660 * @param out {@code non-null;} where to write to 661 * @param c0 code unit to write 662 * @param c1c2c3c4 code unit quad to write 663 */ write(AnnotatedOutput out, short c0, long c1c2c3c4)664 protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) { 665 write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16), 666 (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48)); 667 } 668 } 669