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.rop.code; 18 19import com.android.dexgen.rop.cst.Constant; 20import com.android.dexgen.rop.cst.CstUtf8; 21import com.android.dexgen.rop.type.StdTypeList; 22import com.android.dexgen.rop.type.Type; 23import com.android.dexgen.rop.type.TypeList; 24import com.android.dexgen.util.ToHuman; 25 26/** 27 * A register-based instruction. An instruction is the combination of 28 * an opcode (which specifies operation and source/result types), a 29 * list of actual sources and result registers/values, and additional 30 * information. 31 */ 32public abstract class Insn implements ToHuman { 33 /** {@code non-null;} opcode */ 34 private final Rop opcode; 35 36 /** {@code non-null;} source position */ 37 private final SourcePosition position; 38 39 /** {@code null-ok;} spec for the result of this instruction, if any */ 40 private final RegisterSpec result; 41 42 /** {@code non-null;} specs for all the sources of this instruction */ 43 private final RegisterSpecList sources; 44 45 /** 46 * Constructs an instance. 47 * 48 * @param opcode {@code non-null;} the opcode 49 * @param position {@code non-null;} source position 50 * @param result {@code null-ok;} spec for the result, if any 51 * @param sources {@code non-null;} specs for all the sources 52 */ 53 public Insn(Rop opcode, SourcePosition position, RegisterSpec result, 54 RegisterSpecList sources) { 55 if (opcode == null) { 56 throw new NullPointerException("opcode == null"); 57 } 58 59 if (position == null) { 60 throw new NullPointerException("position == null"); 61 } 62 63 if (sources == null) { 64 throw new NullPointerException("sources == null"); 65 } 66 67 this.opcode = opcode; 68 this.position = position; 69 this.result = result; 70 this.sources = sources; 71 } 72 73 /** 74 * {@inheritDoc} 75 * 76 * Instances of this class compare by identity. That is, 77 * {@code x.equals(y)} is only true if {@code x == y}. 78 */ 79 @Override 80 public final boolean equals(Object other) { 81 return (this == other); 82 } 83 84 /** 85 * {@inheritDoc} 86 * 87 * This implementation returns the identity hashcode of this 88 * instance. This is proper, since instances of this class compare 89 * by identity (see {@link #equals}). 90 */ 91 @Override 92 public final int hashCode() { 93 return System.identityHashCode(this); 94 } 95 96 /** {@inheritDoc} */ 97 @Override 98 public String toString() { 99 return toStringWithInline(getInlineString()); 100 } 101 102 /** 103 * Gets a human-oriented (and slightly lossy) string for this instance. 104 * 105 * @return {@code non-null;} the human string form 106 */ 107 public String toHuman() { 108 return toHumanWithInline(getInlineString()); 109 } 110 111 /** 112 * Gets an "inline" string portion for toHuman(), if available. This 113 * is the portion that appears after the Rop opcode 114 * 115 * @return {@code null-ok;} if non-null, the inline text for toHuman() 116 */ 117 public String getInlineString() { 118 return null; 119 } 120 121 /** 122 * Gets the opcode. 123 * 124 * @return {@code non-null;} the opcode 125 */ 126 public final Rop getOpcode() { 127 return opcode; 128 } 129 130 /** 131 * Gets the source position. 132 * 133 * @return {@code non-null;} the source position 134 */ 135 public final SourcePosition getPosition() { 136 return position; 137 } 138 139 /** 140 * Gets the result spec, if any. A return value of {@code null} 141 * means this instruction returns nothing. 142 * 143 * @return {@code null-ok;} the result spec, if any 144 */ 145 public final RegisterSpec getResult() { 146 return result; 147 } 148 149 /** 150 * Gets the spec of a local variable assignment that occurs at this 151 * instruction, or null if no local variable assignment occurs. This 152 * may be the result register, or for {@code mark-local} insns 153 * it may be the source. 154 * 155 * @return {@code null-ok;} a named register spec or null 156 */ 157 public final RegisterSpec getLocalAssignment() { 158 RegisterSpec assignment; 159 if (opcode.getOpcode() == RegOps.MARK_LOCAL) { 160 assignment = sources.get(0); 161 } else { 162 assignment = result; 163 } 164 165 if (assignment == null) { 166 return null; 167 } 168 169 LocalItem localItem = assignment.getLocalItem(); 170 171 if (localItem == null) { 172 return null; 173 } 174 175 return assignment; 176 } 177 178 /** 179 * Gets the source specs. 180 * 181 * @return {@code non-null;} the source specs 182 */ 183 public final RegisterSpecList getSources() { 184 return sources; 185 } 186 187 /** 188 * Gets whether this instruction can possibly throw an exception. This 189 * is just a convenient wrapper for {@code getOpcode().canThrow()}. 190 * 191 * @return {@code true} iff this instruction can possibly throw 192 */ 193 public final boolean canThrow() { 194 return opcode.canThrow(); 195 } 196 197 /** 198 * Gets the list of possibly-caught exceptions. This returns {@link 199 * StdTypeList#EMPTY} if this instruction has no handlers, 200 * which can be <i>either</i> if this instruction can't possibly 201 * throw or if it merely doesn't handle any of its possible 202 * exceptions. To determine whether this instruction can throw, 203 * use {@link #canThrow}. 204 * 205 * @return {@code non-null;} the catches list 206 */ 207 public abstract TypeList getCatches(); 208 209 /** 210 * Calls the appropriate method on the given visitor, depending on the 211 * class of this instance. Subclasses must override this. 212 * 213 * @param visitor {@code non-null;} the visitor to call on 214 */ 215 public abstract void accept(Visitor visitor); 216 217 /** 218 * Returns an instance that is just like this one, except that it 219 * has a catch list with the given item appended to the end. This 220 * method throws an exception if this instance can't possibly 221 * throw. To determine whether this instruction can throw, use 222 * {@link #canThrow}. 223 * 224 * @param type {@code non-null;} type to append to the catch list 225 * @return {@code non-null;} an appropriately-constructed instance 226 */ 227 public abstract Insn withAddedCatch(Type type); 228 229 /** 230 * Returns an instance that is just like this one, except that all 231 * register references have been offset by the given delta. 232 * 233 * @param delta the amount to offset register references by 234 * @return {@code non-null;} an appropriately-constructed instance 235 */ 236 public abstract Insn withRegisterOffset(int delta); 237 238 /** 239 * Returns an instance that is just like this one, except that, if 240 * possible, the insn is converted into a version in which the last 241 * source (if it is a constant) is represented directly rather than 242 * as a register reference. {@code this} is returned in cases where 243 * the translation is not possible. 244 * 245 * @return {@code non-null;} an appropriately-constructed instance 246 */ 247 public Insn withLastSourceLiteral() { 248 return this; 249 } 250 251 /** 252 * Returns an exact copy of this Insn 253 * 254 * @return {@code non-null;} an appropriately-constructed instance 255 */ 256 public Insn copy() { 257 return withRegisterOffset(0); 258 } 259 260 261 /** 262 * Compares, handling nulls safely 263 * 264 * @param a first object 265 * @param b second object 266 * @return true if they're equal or both null. 267 */ 268 private static boolean equalsHandleNulls (Object a, Object b) { 269 return (a == b) || ((a != null) && a.equals(b)); 270 } 271 272 /** 273 * Compares Insn contents, since {@code Insn.equals()} is defined 274 * to be an identity compare. Insn's are {@code contentEquals()} 275 * if they have the same opcode, registers, source position, and other 276 * metadata. 277 * 278 * @return true in the case described above 279 */ 280 public boolean contentEquals(Insn b) { 281 return opcode == b.getOpcode() 282 && position.equals(b.getPosition()) 283 && (getClass() == b.getClass()) 284 && equalsHandleNulls(result, b.getResult()) 285 && equalsHandleNulls(sources, b.getSources()) 286 && StdTypeList.equalContents(getCatches(), b.getCatches()); 287 } 288 289 /** 290 * Returns an instance that is just like this one, except 291 * with new result and source registers. 292 * 293 * @param result {@code null-ok;} new result register 294 * @param sources {@code non-null;} new sources registers 295 * @return {@code non-null;} an appropriately-constructed instance 296 */ 297 public abstract Insn withNewRegisters(RegisterSpec result, 298 RegisterSpecList sources); 299 300 /** 301 * Returns the string form of this instance, with the given bit added in 302 * the standard location for an inline argument. 303 * 304 * @param extra {@code null-ok;} the inline argument string 305 * @return {@code non-null;} the string form 306 */ 307 protected final String toStringWithInline(String extra) { 308 StringBuffer sb = new StringBuffer(80); 309 310 sb.append("Insn{"); 311 sb.append(position); 312 sb.append(' '); 313 sb.append(opcode); 314 315 if (extra != null) { 316 sb.append(' '); 317 sb.append(extra); 318 } 319 320 sb.append(" :: "); 321 322 if (result != null) { 323 sb.append(result); 324 sb.append(" <- "); 325 } 326 327 sb.append(sources); 328 sb.append('}'); 329 330 return sb.toString(); 331 } 332 333 /** 334 * Returns the human string form of this instance, with the given 335 * bit added in the standard location for an inline argument. 336 * 337 * @param extra {@code null-ok;} the inline argument string 338 * @return {@code non-null;} the human string form 339 */ 340 protected final String toHumanWithInline(String extra) { 341 StringBuffer sb = new StringBuffer(80); 342 343 sb.append(position); 344 sb.append(": "); 345 sb.append(opcode.getNickname()); 346 347 if (extra != null) { 348 sb.append("("); 349 sb.append(extra); 350 sb.append(")"); 351 } 352 353 if (result == null) { 354 sb.append(" ."); 355 } else { 356 sb.append(" "); 357 sb.append(result.toHuman()); 358 } 359 360 sb.append(" <-"); 361 362 int sz = sources.size(); 363 if (sz == 0) { 364 sb.append(" ."); 365 } else { 366 for (int i = 0; i < sz; i++) { 367 sb.append(" "); 368 sb.append(sources.get(i).toHuman()); 369 } 370 } 371 372 return sb.toString(); 373 } 374 375 376 /** 377 * Visitor interface for this (outer) class. 378 */ 379 public static interface Visitor { 380 /** 381 * Visits a {@link PlainInsn}. 382 * 383 * @param insn {@code non-null;} the instruction to visit 384 */ 385 public void visitPlainInsn(PlainInsn insn); 386 387 /** 388 * Visits a {@link PlainCstInsn}. 389 * 390 * @param insn {@code non-null;} the instruction to visit 391 */ 392 public void visitPlainCstInsn(PlainCstInsn insn); 393 394 /** 395 * Visits a {@link SwitchInsn}. 396 * 397 * @param insn {@code non-null;} the instruction to visit 398 */ 399 public void visitSwitchInsn(SwitchInsn insn); 400 401 /** 402 * Visits a {@link ThrowingCstInsn}. 403 * 404 * @param insn {@code non-null;} the instruction to visit 405 */ 406 public void visitThrowingCstInsn(ThrowingCstInsn insn); 407 408 /** 409 * Visits a {@link ThrowingInsn}. 410 * 411 * @param insn {@code non-null;} the instruction to visit 412 */ 413 public void visitThrowingInsn(ThrowingInsn insn); 414 415 /** 416 * Visits a {@link FillArrayDataInsn}. 417 * 418 * @param insn {@code non-null;} the instruction to visit 419 */ 420 public void visitFillArrayDataInsn(FillArrayDataInsn insn); 421 } 422 423 /** 424 * Base implementation of {@link Visitor}, which has empty method 425 * bodies for all methods. 426 */ 427 public static class BaseVisitor implements Visitor { 428 /** {@inheritDoc} */ 429 public void visitPlainInsn(PlainInsn insn) { 430 // This space intentionally left blank. 431 } 432 433 /** {@inheritDoc} */ 434 public void visitPlainCstInsn(PlainCstInsn insn) { 435 // This space intentionally left blank. 436 } 437 438 /** {@inheritDoc} */ 439 public void visitSwitchInsn(SwitchInsn insn) { 440 // This space intentionally left blank. 441 } 442 443 /** {@inheritDoc} */ 444 public void visitThrowingCstInsn(ThrowingCstInsn insn) { 445 // This space intentionally left blank. 446 } 447 448 /** {@inheritDoc} */ 449 public void visitThrowingInsn(ThrowingInsn insn) { 450 // This space intentionally left blank. 451 } 452 453 /** {@inheritDoc} */ 454 public void visitFillArrayDataInsn(FillArrayDataInsn insn) { 455 // This space intentionally left blank. 456 } 457 } 458} 459