blob: 8224584eda12d4a53e97709d892e8591a9a32daf [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.dx.rop.code;
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.Hex;
/**
* Class that describes all the immutable parts of register-based operations.
*/
public final class Rop {
/** minimum {@code BRANCH_*} value */
public static final int BRANCH_MIN = 1;
/** indicates a non-branching op */
public static final int BRANCH_NONE = 1;
/** indicates a function/method return */
public static final int BRANCH_RETURN = 2;
/** indicates an unconditional goto */
public static final int BRANCH_GOTO = 3;
/** indicates a two-way branch */
public static final int BRANCH_IF = 4;
/** indicates a switch-style branch */
public static final int BRANCH_SWITCH = 5;
/** indicates a throw-style branch (both always-throws and may-throw) */
public static final int BRANCH_THROW = 6;
/** maximum {@code BRANCH_*} value */
public static final int BRANCH_MAX = 6;
/** the opcode; one of the constants in {@link RegOps} */
private final int opcode;
/**
* {@code non-null;} result type of this operation; {@link Type#VOID} for
* no-result operations
*/
private final Type result;
/** {@code non-null;} types of all the sources of this operation */
private final TypeList sources;
/** {@code non-null;} list of possible types thrown by this operation */
private final TypeList exceptions;
/**
* the branchingness of this op; one of the {@code BRANCH_*}
* constants in this class
*/
private final int branchingness;
/** whether this is a function/method call op or similar */
private final boolean isCallLike;
/** {@code null-ok;} nickname, if specified (used for debugging) */
private final String nickname;
/**
* Constructs an instance. This method is private. Use one of the
* public constructors.
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param result {@code non-null;} result type of this operation; {@link
* Type#VOID} for no-result operations
* @param sources {@code non-null;} types of all the sources of this operation
* @param exceptions {@code non-null;} list of possible types thrown by this
* operation
* @param branchingness the branchingness of this op; one of the
* {@code BRANCH_*} constants
* @param isCallLike whether the op is a function/method call or similar
* @param nickname {@code null-ok;} optional nickname (used for debugging)
*/
public Rop(int opcode, Type result, TypeList sources,
TypeList exceptions, int branchingness, boolean isCallLike,
String nickname) {
if (result == null) {
throw new NullPointerException("result == null");
}
if (sources == null) {
throw new NullPointerException("sources == null");
}
if (exceptions == null) {
throw new NullPointerException("exceptions == null");
}
if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
throw new IllegalArgumentException("bogus branchingness");
}
if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
throw new IllegalArgumentException("exceptions / branchingness " +
"mismatch");
}
this.opcode = opcode;
this.result = result;
this.sources = sources;
this.exceptions = exceptions;
this.branchingness = branchingness;
this.isCallLike = isCallLike;
this.nickname = nickname;
}
/**
* Constructs an instance. The constructed instance is never a
* call-like op (see {@link #isCallLike}).
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param result {@code non-null;} result type of this operation; {@link
* Type#VOID} for no-result operations
* @param sources {@code non-null;} types of all the sources of this operation
* @param exceptions {@code non-null;} list of possible types thrown by this
* operation
* @param branchingness the branchingness of this op; one of the
* {@code BRANCH_*} constants
* @param nickname {@code null-ok;} optional nickname (used for debugging)
*/
public Rop(int opcode, Type result, TypeList sources,
TypeList exceptions, int branchingness, String nickname) {
this(opcode, result, sources, exceptions, branchingness, false,
nickname);
}
/**
* Constructs a no-exception instance. The constructed instance is never a
* call-like op (see {@link #isCallLike}).
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param result {@code non-null;} result type of this operation; {@link
* Type#VOID} for no-result operations
* @param sources {@code non-null;} types of all the sources of this operation
* @param branchingness the branchingness of this op; one of the
* {@code BRANCH_*} constants
* @param nickname {@code null-ok;} optional nickname (used for debugging)
*/
public Rop(int opcode, Type result, TypeList sources, int branchingness,
String nickname) {
this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
nickname);
}
/**
* Constructs a non-branching no-exception instance. The
* {@code branchingness} is always {@code BRANCH_NONE},
* and it is never a call-like op (see {@link #isCallLike}).
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param result {@code non-null;} result type of this operation; {@link
* Type#VOID} for no-result operations
* @param sources {@code non-null;} types of all the sources of this operation
* @param nickname {@code null-ok;} optional nickname (used for debugging)
*/
public Rop(int opcode, Type result, TypeList sources, String nickname) {
this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
false, nickname);
}
/**
* Constructs a non-empty exceptions instance. Its
* {@code branchingness} is always {@code BRANCH_THROW},
* but it is never a call-like op (see {@link #isCallLike}).
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param result {@code non-null;} result type of this operation; {@link
* Type#VOID} for no-result operations
* @param sources {@code non-null;} types of all the sources of this operation
* @param exceptions {@code non-null;} list of possible types thrown by this
* operation
* @param nickname {@code null-ok;} optional nickname (used for debugging)
*/
public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
String nickname) {
this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
nickname);
}
/**
* Constructs a non-nicknamed instance with non-empty exceptions, which
* is always a call-like op (see {@link #isCallLike}). Its
* {@code branchingness} is always {@code BRANCH_THROW}.
*
* @param opcode the opcode; one of the constants in {@link RegOps}
* @param sources {@code non-null;} types of all the sources of this operation
* @param exceptions {@code non-null;} list of possible types thrown by this
* operation
*/
public Rop(int opcode, TypeList sources, TypeList exceptions) {
this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
null);
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (this == other) {
// Easy out.
return true;
}
if (!(other instanceof Rop)) {
return false;
}
Rop rop = (Rop) other;
return (opcode == rop.opcode) &&
(branchingness == rop.branchingness) &&
(result == rop.result) &&
sources.equals(rop.sources) &&
exceptions.equals(rop.exceptions);
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int h = (opcode * 31) + branchingness;
h = (h * 31) + result.hashCode();
h = (h * 31) + sources.hashCode();
h = (h * 31) + exceptions.hashCode();
return h;
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sb = new StringBuffer(40);
sb.append("Rop{");
sb.append(RegOps.opName(opcode));
if (result != Type.VOID) {
sb.append(" ");
sb.append(result);
} else {
sb.append(" .");
}
sb.append(" <-");
int sz = sources.size();
if (sz == 0) {
sb.append(" .");
} else {
for (int i = 0; i < sz; i++) {
sb.append(' ');
sb.append(sources.getType(i));
}
}
if (isCallLike) {
sb.append(" call");
}
sz = exceptions.size();
if (sz != 0) {
sb.append(" throws");
for (int i = 0; i < sz; i++) {
sb.append(' ');
Type one = exceptions.getType(i);
if (one == Type.THROWABLE) {
sb.append("<any>");
} else {
sb.append(exceptions.getType(i));
}
}
} else {
switch (branchingness) {
case BRANCH_NONE: sb.append(" flows"); break;
case BRANCH_RETURN: sb.append(" returns"); break;
case BRANCH_GOTO: sb.append(" gotos"); break;
case BRANCH_IF: sb.append(" ifs"); break;
case BRANCH_SWITCH: sb.append(" switches"); break;
default: sb.append(" " + Hex.u1(branchingness)); break;
}
}
sb.append('}');
return sb.toString();
}
/**
* Gets the opcode.
*
* @return the opcode
*/
public int getOpcode() {
return opcode;
}
/**
* Gets the result type. A return value of {@link Type#VOID}
* means this operation returns nothing.
*
* @return {@code null-ok;} the result spec
*/
public Type getResult() {
return result;
}
/**
* Gets the source types.
*
* @return {@code non-null;} the source types
*/
public TypeList getSources() {
return sources;
}
/**
* Gets the list of exception types that might be thrown.
*
* @return {@code non-null;} the list of exception types
*/
public TypeList getExceptions() {
return exceptions;
}
/**
* Gets the branchingness of this instance.
*
* @return the branchingness
*/
public int getBranchingness() {
return branchingness;
}
/**
* Gets whether this opcode is a function/method call or similar.
*
* @return {@code true} iff this opcode is call-like
*/
public boolean isCallLike() {
return isCallLike;
}
/**
* Gets whether this opcode is commutative (the order of its sources are
* unimportant) or not. All commutative Rops have exactly two sources and
* have no branchiness.
*
* @return true if rop is commutative
*/
public boolean isCommutative() {
switch (opcode) {
case RegOps.AND:
case RegOps.OR:
case RegOps.XOR:
case RegOps.ADD:
case RegOps.MUL:
return true;
default:
return false;
}
}
/**
* Gets the nickname. If this instance has no nickname, this returns
* the result of calling {@link #toString}.
*
* @return {@code non-null;} the nickname
*/
public String getNickname() {
if (nickname != null) {
return nickname;
}
return toString();
}
/**
* Gets whether this operation can possibly throw an exception. This
* is just a convenient wrapper for
* {@code getExceptions().size() != 0}.
*
* @return {@code true} iff this operation can possibly throw
*/
public final boolean canThrow() {
return (exceptions.size() != 0);
}
}