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