content: /*
   * 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.taobao.android.dx.rop.code;
  
  import com.taobao.android.dx.rop.type.StdTypeList;
  import com.taobao.android.dx.rop.type.Type;
  import com.taobao.android.dx.rop.type.TypeList;
  import com.taobao.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);
      }
  }
  