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.code.SourcePosition; 22import com.android.dx.util.AnnotatedOutput; 23import com.android.dx.util.Hex; 24import com.android.dx.util.TwoColumnOutput; 25 26/** 27 * Base class for Dalvik instructions. 28 */ 29public abstract class DalvInsn { 30 /** 31 * the actual output address of this instance, if known, or 32 * <code>-1</code> if not 33 */ 34 private int address; 35 36 /** the opcode; one of the constants from {@link Dops} */ 37 private final Dop opcode; 38 39 /** non-null; source position */ 40 private final SourcePosition position; 41 42 /** non-null; list of register arguments */ 43 private final RegisterSpecList registers; 44 45 /** 46 * Makes a move instruction, appropriate and ideal for the given arguments. 47 * 48 * @param position non-null; source position information 49 * @param dest non-null; destination register 50 * @param src non-null; source register 51 * @return non-null; an appropriately-constructed instance 52 */ 53 public static SimpleInsn makeMove(SourcePosition position, 54 RegisterSpec dest, RegisterSpec src) { 55 boolean category1 = dest.getCategory() == 1; 56 boolean reference = dest.getType().isReference(); 57 int destReg = dest.getReg(); 58 int srcReg = src.getReg(); 59 Dop opcode; 60 61 if ((srcReg | destReg) < 16) { 62 opcode = reference ? Dops.MOVE_OBJECT : 63 (category1 ? Dops.MOVE : Dops.MOVE_WIDE); 64 } else if (destReg < 256) { 65 opcode = reference ? Dops.MOVE_OBJECT_FROM16 : 66 (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16); 67 } else { 68 opcode = reference ? Dops.MOVE_OBJECT_16 : 69 (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16); 70 } 71 72 return new SimpleInsn(opcode, position, 73 RegisterSpecList.make(dest, src)); 74 } 75 76 /** 77 * Constructs an instance. The output address of this instance is initially 78 * unknown (<code>-1</code>). 79 * 80 * <p><b>Note:</b> In the unlikely event that an instruction takes 81 * absolutely no registers (e.g., a <code>nop</code> or a 82 * no-argument no-result static method call), then the given 83 * register list may be passed as {@link 84 * RegisterSpecList#EMPTY}.</p> 85 * 86 * @param opcode the opcode; one of the constants from {@link Dops} 87 * @param position non-null; source position 88 * @param registers non-null; register list, including a 89 * result register if appropriate (that is, registers may be either 90 * ins and outs) 91 */ 92 public DalvInsn(Dop opcode, SourcePosition position, 93 RegisterSpecList registers) { 94 if (opcode == null) { 95 throw new NullPointerException("opcode == null"); 96 } 97 98 if (position == null) { 99 throw new NullPointerException("position == null"); 100 } 101 102 if (registers == null) { 103 throw new NullPointerException("registers == null"); 104 } 105 106 this.address = -1; 107 this.opcode = opcode; 108 this.position = position; 109 this.registers = registers; 110 } 111 112 /** {@inheritDoc} */ 113 @Override 114 public final String toString() { 115 StringBuffer sb = new StringBuffer(100); 116 117 sb.append(identifierString()); 118 sb.append(' '); 119 sb.append(position); 120 121 sb.append(": "); 122 sb.append(opcode.getName()); 123 124 boolean needComma = false; 125 if (registers.size() != 0) { 126 sb.append(registers.toHuman(" ", ", ", null)); 127 needComma = true; 128 } 129 130 String extra = argString(); 131 if (extra != null) { 132 if (needComma) { 133 sb.append(','); 134 } 135 sb.append(' '); 136 sb.append(extra); 137 } 138 139 return sb.toString(); 140 } 141 142 /** 143 * Gets whether the address of this instruction is known. 144 * 145 * @see #getAddress 146 * @see #setAddress 147 */ 148 public final boolean hasAddress() { 149 return (address >= 0); 150 } 151 152 /** 153 * Gets the output address of this instruction, if it is known. This throws 154 * a <code>RuntimeException</code> if it has not yet been set. 155 * 156 * @see #setAddress 157 * 158 * @return >= 0; the output address 159 */ 160 public final int getAddress() { 161 if (address < 0) { 162 throw new RuntimeException("address not yet known"); 163 } 164 165 return address; 166 } 167 168 /** 169 * Gets the opcode. 170 * 171 * @return non-null; the opcode 172 */ 173 public final Dop getOpcode() { 174 return opcode; 175 } 176 177 /** 178 * Gets the source position. 179 * 180 * @return non-null; the source position 181 */ 182 public final SourcePosition getPosition() { 183 return position; 184 } 185 186 /** 187 * Gets the register list for this instruction. 188 * 189 * @return non-null; the registers 190 */ 191 public final RegisterSpecList getRegisters() { 192 return registers; 193 } 194 195 /** 196 * Returns whether this instance's opcode uses a result register. 197 * This method is a convenient shorthand for 198 * <code>getOpcode().hasResult()</code>. 199 * 200 * @return <code>true</code> iff this opcode uses a result register 201 */ 202 public final boolean hasResult() { 203 return opcode.hasResult(); 204 } 205 206 /** 207 * Gets the minimum distinct registers required for this instruction. 208 * This assumes that the result (if any) can share registers with the 209 * sources (if any), that each source register is unique, and that 210 * (to be explicit here) category-2 values take up two consecutive 211 * registers. 212 * 213 * @return >= 0; the minimum distinct register requirement 214 */ 215 public final int getMinimumRegisterRequirement() { 216 boolean hasResult = hasResult(); 217 int regSz = registers.size(); 218 int resultRequirement = hasResult ? registers.get(0).getCategory() : 0; 219 int sourceRequirement = 0; 220 221 for (int i = hasResult ? 1 : 0; i < regSz; i++) { 222 sourceRequirement += registers.get(i).getCategory(); 223 } 224 225 return Math.max(sourceRequirement, resultRequirement); 226 } 227 228 /** 229 * Gets the instruction prefix required, if any, to use in a high 230 * register transformed version of this instance. 231 * 232 * @see #hrVersion 233 * 234 * @return null-ok; the prefix, if any 235 */ 236 public DalvInsn hrPrefix() { 237 RegisterSpecList regs = registers; 238 int sz = regs.size(); 239 240 if (hasResult()) { 241 if (sz == 1) { 242 return null; 243 } 244 regs = regs.withoutFirst(); 245 } else if (sz == 0) { 246 return null; 247 } 248 249 return new HighRegisterPrefix(position, regs); 250 } 251 252 /** 253 * Gets the instruction suffix required, if any, to use in a high 254 * register transformed version of this instance. 255 * 256 * @see #hrVersion 257 * 258 * @return null-ok; the suffix, if any 259 */ 260 public DalvInsn hrSuffix() { 261 if (hasResult()) { 262 RegisterSpec r = registers.get(0); 263 return makeMove(position, r, r.withReg(0)); 264 } else { 265 return null; 266 } 267 } 268 269 /** 270 * Gets the instruction that is equivalent to this one, except that 271 * uses sequential registers starting at <code>0</code> (storing 272 * the result, if any, in register <code>0</code> as well). The 273 * sequence of instructions from {@link #hrPrefix} and {@link 274 * #hrSuffix} (if non-null) surrounding the result of a call to 275 * this method are the high register transformation of this 276 * instance, and it is guaranteed that the number of low registers 277 * used will be the number returned by {@link 278 * #getMinimumRegisterRequirement}. 279 * 280 * @return non-null; the replacement 281 */ 282 public DalvInsn hrVersion() { 283 RegisterSpecList regs = 284 registers.withSequentialRegisters(0, hasResult()); 285 return withRegisters(regs); 286 } 287 288 /** 289 * Gets the short identifier for this instruction. This is its 290 * address, if assigned, or its identity hashcode if not. 291 * 292 * @return non-null; the identifier 293 */ 294 public final String identifierString() { 295 if (address != -1) { 296 return String.format("%04x", address); 297 } 298 299 return Hex.u4(System.identityHashCode(this)); 300 } 301 302 /** 303 * Returns the string form of this instance suitable for inclusion in 304 * a human-oriented listing dump. This method will return <code>null</code> 305 * if this instance should not appear in a listing. 306 * 307 * @param prefix non-null; prefix before the address; each follow-on 308 * line will be indented to match as well 309 * @param width >= 0; the width of the output or <code>0</code> for 310 * unlimited width 311 * @param noteIndices whether to include an explicit notation of 312 * constant pool indices 313 * @return null-ok; the string form or <code>null</code> if this 314 * instance should not appear in a listing 315 */ 316 public final String listingString(String prefix, int width, 317 boolean noteIndices) { 318 String insnPerSe = listingString0(noteIndices); 319 320 if (insnPerSe == null) { 321 return null; 322 } 323 324 String addr = prefix + identifierString() + ": "; 325 int w1 = addr.length(); 326 int w2 = (width == 0) ? insnPerSe.length() : (width - w1); 327 328 return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2); 329 } 330 331 /** 332 * Sets the output address. 333 * 334 * @param address >= 0; the output address 335 */ 336 public final void setAddress(int address) { 337 if (address < 0) { 338 throw new IllegalArgumentException("address < 0"); 339 } 340 341 this.address = address; 342 } 343 344 /** 345 * Gets the address immediately after this instance. This is only 346 * calculable if this instance's address is known, and it is equal 347 * to the address plus the length of the instruction format of this 348 * instance's opcode. 349 * 350 * @return >= 0; the next address 351 */ 352 public final int getNextAddress() { 353 return getAddress() + codeSize(); 354 } 355 356 /** 357 * Gets the size of this instruction, in 16-bit code units. 358 * 359 * @return >= 0; the code size of this instruction 360 */ 361 public abstract int codeSize(); 362 363 /** 364 * Writes this instance to the given output. This method should 365 * never annotate the output. 366 * 367 * @param out non-null; where to write to 368 */ 369 public abstract void writeTo(AnnotatedOutput out); 370 371 /** 372 * Returns an instance that is just like this one, except that its 373 * opcode is replaced by the one given, and its address is reset. 374 * 375 * @param opcode non-null; the new opcode 376 * @return non-null; an appropriately-constructed instance 377 */ 378 public abstract DalvInsn withOpcode(Dop opcode); 379 380 /** 381 * Returns an instance that is just like this one, except that all 382 * register references have been offset by the given delta, and its 383 * address is reset. 384 * 385 * @param delta the amount to offset register references by 386 * @return non-null; an appropriately-constructed instance 387 */ 388 public abstract DalvInsn withRegisterOffset(int delta); 389 390 /** 391 * Returns an instance that is just like this one, except that the 392 * register list is replaced by the given one, and its address is 393 * reset. 394 * 395 * @param registers non-null; new register list 396 * @return non-null; an appropriately-constructed instance 397 */ 398 public abstract DalvInsn withRegisters(RegisterSpecList registers); 399 400 /** 401 * Gets the string form for any arguments to this instance. Subclasses 402 * must override this. 403 * 404 * @return null-ok; the string version of any arguments or 405 * <code>null</code> if there are none 406 */ 407 protected abstract String argString(); 408 409 /** 410 * Helper for {@link #listingString}, which returns the string 411 * form of this instance suitable for inclusion in a 412 * human-oriented listing dump, not including the instruction 413 * address and without respect for any output formatting. This 414 * method should return <code>null</code> if this instance should 415 * not appear in a listing. 416 * 417 * @param noteIndices whether to include an explicit notation of 418 * constant pool indices 419 * @return null-ok; the listing string 420 */ 421 protected abstract String listingString0(boolean noteIndices); 422} 423