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.dexgen.dex.code; 18 19import com.android.dexgen.rop.code.RegisterSpecList; 20import com.android.dexgen.rop.cst.Constant; 21import com.android.dexgen.rop.cst.CstInteger; 22import com.android.dexgen.rop.cst.CstKnownNull; 23import com.android.dexgen.rop.cst.CstLiteral64; 24import com.android.dexgen.rop.cst.CstLiteralBits; 25import com.android.dexgen.util.AnnotatedOutput; 26import 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 */ 34public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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