blob: 4bb10d2ffe766751e86f662274548b9a65a8b06e [file] [log] [blame]
/*
* 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.dexgen.rop.code;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.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.
}
}
}