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