/* * 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); } }