| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.rop.code; |
| |
| import com.android.dx.rop.cst.CstUtf8; |
| import com.android.dx.rop.cst.Constant; |
| import com.android.dx.rop.type.StdTypeList; |
| import com.android.dx.rop.type.Type; |
| import com.android.dx.rop.type.TypeList; |
| import com.android.dx.util.ToHuman; |
| |
| /** |
| * A register-based instruction. An instruction is the combination of |
| * an opcode (which specifies operation and source/result types), a |
| * list of actual sources and result registers/values, and additional |
| * information. |
| */ |
| public abstract class Insn implements ToHuman { |
| /** {@code non-null;} opcode */ |
| private final Rop opcode; |
| |
| /** {@code non-null;} source position */ |
| private final SourcePosition position; |
| |
| /** {@code null-ok;} spec for the result of this instruction, if any */ |
| private final RegisterSpec result; |
| |
| /** {@code non-null;} specs for all the sources of this instruction */ |
| private final RegisterSpecList sources; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param opcode {@code non-null;} the opcode |
| * @param position {@code non-null;} source position |
| * @param result {@code null-ok;} spec for the result, if any |
| * @param sources {@code non-null;} specs for all the sources |
| */ |
| public Insn(Rop opcode, SourcePosition position, RegisterSpec result, |
| RegisterSpecList sources) { |
| if (opcode == null) { |
| throw new NullPointerException("opcode == null"); |
| } |
| |
| if (position == null) { |
| throw new NullPointerException("position == null"); |
| } |
| |
| if (sources == null) { |
| throw new NullPointerException("sources == null"); |
| } |
| |
| this.opcode = opcode; |
| this.position = position; |
| this.result = result; |
| this.sources = sources; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Instances of this class compare by identity. That is, |
| * {@code x.equals(y)} is only true if {@code x == y}. |
| */ |
| @Override |
| public final boolean equals(Object other) { |
| return (this == other); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * This implementation returns the identity hashcode of this |
| * instance. This is proper, since instances of this class compare |
| * by identity (see {@link #equals}). |
| */ |
| @Override |
| public final int hashCode() { |
| return System.identityHashCode(this); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public String toString() { |
| return toStringWithInline(getInlineString()); |
| } |
| |
| /** |
| * Gets a human-oriented (and slightly lossy) string for this instance. |
| * |
| * @return {@code non-null;} the human string form |
| */ |
| public String toHuman() { |
| return toHumanWithInline(getInlineString()); |
| } |
| |
| /** |
| * Gets an "inline" string portion for toHuman(), if available. This |
| * is the portion that appears after the Rop opcode |
| * |
| * @return {@code null-ok;} if non-null, the inline text for toHuman() |
| */ |
| public String getInlineString() { |
| return null; |
| } |
| |
| /** |
| * Gets the opcode. |
| * |
| * @return {@code non-null;} the opcode |
| */ |
| public final Rop getOpcode() { |
| return opcode; |
| } |
| |
| /** |
| * Gets the source position. |
| * |
| * @return {@code non-null;} the source position |
| */ |
| public final SourcePosition getPosition() { |
| return position; |
| } |
| |
| /** |
| * Gets the result spec, if any. A return value of {@code null} |
| * means this instruction returns nothing. |
| * |
| * @return {@code null-ok;} the result spec, if any |
| */ |
| public final RegisterSpec getResult() { |
| return result; |
| } |
| |
| /** |
| * Gets the spec of a local variable assignment that occurs at this |
| * instruction, or null if no local variable assignment occurs. This |
| * may be the result register, or for {@code mark-local} insns |
| * it may be the source. |
| * |
| * @return {@code null-ok;} a named register spec or null |
| */ |
| public final RegisterSpec getLocalAssignment() { |
| RegisterSpec assignment; |
| if (opcode.getOpcode() == RegOps.MARK_LOCAL) { |
| assignment = sources.get(0); |
| } else { |
| assignment = result; |
| } |
| |
| if (assignment == null) { |
| return null; |
| } |
| |
| LocalItem localItem = assignment.getLocalItem(); |
| |
| if (localItem == null) { |
| return null; |
| } |
| |
| return assignment; |
| } |
| |
| /** |
| * Gets the source specs. |
| * |
| * @return {@code non-null;} the source specs |
| */ |
| public final RegisterSpecList getSources() { |
| return sources; |
| } |
| |
| /** |
| * Gets whether this instruction can possibly throw an exception. This |
| * is just a convenient wrapper for {@code getOpcode().canThrow()}. |
| * |
| * @return {@code true} iff this instruction can possibly throw |
| */ |
| public final boolean canThrow() { |
| return opcode.canThrow(); |
| } |
| |
| /** |
| * Gets the list of possibly-caught exceptions. This returns {@link |
| * StdTypeList#EMPTY} if this instruction has no handlers, |
| * which can be <i>either</i> if this instruction can't possibly |
| * throw or if it merely doesn't handle any of its possible |
| * exceptions. To determine whether this instruction can throw, |
| * use {@link #canThrow}. |
| * |
| * @return {@code non-null;} the catches list |
| */ |
| public abstract TypeList getCatches(); |
| |
| /** |
| * Calls the appropriate method on the given visitor, depending on the |
| * class of this instance. Subclasses must override this. |
| * |
| * @param visitor {@code non-null;} the visitor to call on |
| */ |
| public abstract void accept(Visitor visitor); |
| |
| /** |
| * Returns an instance that is just like this one, except that it |
| * has a catch list with the given item appended to the end. This |
| * method throws an exception if this instance can't possibly |
| * throw. To determine whether this instruction can throw, use |
| * {@link #canThrow}. |
| * |
| * @param type {@code non-null;} type to append to the catch list |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public abstract Insn withAddedCatch(Type type); |
| |
| /** |
| * Returns an instance that is just like this one, except that all |
| * register references have been offset by the given delta. |
| * |
| * @param delta the amount to offset register references by |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public abstract Insn withRegisterOffset(int delta); |
| |
| /** |
| * Returns an instance that is just like this one, except that, if |
| * possible, the insn is converted into a version in which the last |
| * source (if it is a constant) is represented directly rather than |
| * as a register reference. {@code this} is returned in cases where |
| * the translation is not possible. |
| * |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public Insn withLastSourceLiteral() { |
| return this; |
| } |
| |
| /** |
| * Returns an exact copy of this Insn |
| * |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public Insn copy() { |
| return withRegisterOffset(0); |
| } |
| |
| |
| /** |
| * Compares, handling nulls safely |
| * |
| * @param a first object |
| * @param b second object |
| * @return true if they're equal or both null. |
| */ |
| private static boolean equalsHandleNulls (Object a, Object b) { |
| return (a == b) || ((a != null) && a.equals(b)); |
| } |
| |
| /** |
| * Compares Insn contents, since {@code Insn.equals()} is defined |
| * to be an identity compare. Insn's are {@code contentEquals()} |
| * if they have the same opcode, registers, source position, and other |
| * metadata. |
| * |
| * @return true in the case described above |
| */ |
| public boolean contentEquals(Insn b) { |
| return opcode == b.getOpcode() |
| && position.equals(b.getPosition()) |
| && (getClass() == b.getClass()) |
| && equalsHandleNulls(result, b.getResult()) |
| && equalsHandleNulls(sources, b.getSources()) |
| && StdTypeList.equalContents(getCatches(), b.getCatches()); |
| } |
| |
| /** |
| * Returns an instance that is just like this one, except |
| * with new result and source registers. |
| * |
| * @param result {@code null-ok;} new result register |
| * @param sources {@code non-null;} new sources registers |
| * @return {@code non-null;} an appropriately-constructed instance |
| */ |
| public abstract Insn withNewRegisters(RegisterSpec result, |
| RegisterSpecList sources); |
| |
| /** |
| * Returns the string form of this instance, with the given bit added in |
| * the standard location for an inline argument. |
| * |
| * @param extra {@code null-ok;} the inline argument string |
| * @return {@code non-null;} the string form |
| */ |
| protected final String toStringWithInline(String extra) { |
| StringBuffer sb = new StringBuffer(80); |
| |
| sb.append("Insn{"); |
| sb.append(position); |
| sb.append(' '); |
| sb.append(opcode); |
| |
| if (extra != null) { |
| sb.append(' '); |
| sb.append(extra); |
| } |
| |
| sb.append(" :: "); |
| |
| if (result != null) { |
| sb.append(result); |
| sb.append(" <- "); |
| } |
| |
| sb.append(sources); |
| sb.append('}'); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Returns the human string form of this instance, with the given |
| * bit added in the standard location for an inline argument. |
| * |
| * @param extra {@code null-ok;} the inline argument string |
| * @return {@code non-null;} the human string form |
| */ |
| protected final String toHumanWithInline(String extra) { |
| StringBuffer sb = new StringBuffer(80); |
| |
| sb.append(position); |
| sb.append(": "); |
| sb.append(opcode.getNickname()); |
| |
| if (extra != null) { |
| sb.append("("); |
| sb.append(extra); |
| sb.append(")"); |
| } |
| |
| if (result == null) { |
| sb.append(" ."); |
| } else { |
| sb.append(" "); |
| sb.append(result.toHuman()); |
| } |
| |
| sb.append(" <-"); |
| |
| int sz = sources.size(); |
| if (sz == 0) { |
| sb.append(" ."); |
| } else { |
| for (int i = 0; i < sz; i++) { |
| sb.append(" "); |
| sb.append(sources.get(i).toHuman()); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Visitor interface for this (outer) class. |
| */ |
| public static interface Visitor { |
| /** |
| * Visits a {@link PlainInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitPlainInsn(PlainInsn insn); |
| |
| /** |
| * Visits a {@link PlainCstInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitPlainCstInsn(PlainCstInsn insn); |
| |
| /** |
| * Visits a {@link SwitchInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitSwitchInsn(SwitchInsn insn); |
| |
| /** |
| * Visits a {@link ThrowingCstInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitThrowingCstInsn(ThrowingCstInsn insn); |
| |
| /** |
| * Visits a {@link ThrowingInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitThrowingInsn(ThrowingInsn insn); |
| |
| /** |
| * Visits a {@link FillArrayDataInsn}. |
| * |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitFillArrayDataInsn(FillArrayDataInsn insn); |
| } |
| |
| /** |
| * Base implementation of {@link Visitor}, which has empty method |
| * bodies for all methods. |
| */ |
| public static class BaseVisitor implements Visitor { |
| /** {@inheritDoc} */ |
| public void visitPlainInsn(PlainInsn insn) { |
| // This space intentionally left blank. |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitPlainCstInsn(PlainCstInsn insn) { |
| // This space intentionally left blank. |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitSwitchInsn(SwitchInsn insn) { |
| // This space intentionally left blank. |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitThrowingCstInsn(ThrowingCstInsn insn) { |
| // This space intentionally left blank. |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitThrowingInsn(ThrowingInsn insn) { |
| // This space intentionally left blank. |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitFillArrayDataInsn(FillArrayDataInsn insn) { |
| // This space intentionally left blank. |
| } |
| } |
| } |