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.dex.code;
  
  import com.taobao.android.dx.rop.code.RegisterSpec;
  import com.taobao.android.dx.rop.code.RegisterSpecList;
  import com.taobao.android.dx.rop.code.SourcePosition;
  import com.taobao.android.dx.ssa.RegisterMapper;
  import com.taobao.android.dx.util.AnnotatedOutput;
  import com.taobao.android.dx.util.Hex;
  import com.taobao.android.dx.util.TwoColumnOutput;
  
  import java.util.BitSet;
  
  /**
   * Base class for Dalvik instructions.
   */
  public abstract class DalvInsn {
      /**
       * the actual output address of this instance, if known, or
       * {@code -1} if not
       */
      private int address;
  
      /** the opcode; one of the constants from {@link Dops} */
      private final Dop opcode;
  
      /** {@code non-null;} source position */
      private final SourcePosition position;
  
      /** {@code non-null;} list of register arguments */
      private final RegisterSpecList registers;
  
      /**
       * Makes a move instruction, appropriate and ideal for the given arguments.
       *
       * @param position {@code non-null;} source position information
       * @param dest {@code non-null;} destination register
       * @param src {@code non-null;} source register
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public static SimpleInsn makeMove(SourcePosition position,
              RegisterSpec dest, RegisterSpec src) {
          boolean category1 = dest.getCategory() == 1;
          boolean reference = dest.getType().isReference();
          int destReg = dest.getReg();
          int srcReg = src.getReg();
          Dop opcode;
  
          if ((srcReg | destReg) < 16) {
              opcode = reference ? Dops.MOVE_OBJECT :
                  (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
          } else if (destReg < 256) {
              opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
                  (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
          } else {
              opcode = reference ? Dops.MOVE_OBJECT_16 :
                  (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
          }
  
          return new SimpleInsn(opcode, position,
                                RegisterSpecList.make(dest, src));
      }
  
      /**
       * Constructs an instance. The output address of this instance is initially
       * unknown ({@code -1}).
       *
       * <p><b>Note:</b> In the unlikely event that an instruction takes
       * absolutely no registers (e.g., a {@code nop} or a
       * no-argument no-result static method call), then the given
       * register list may be passed as {@link
       * RegisterSpecList#EMPTY}.</p>
       *
       * @param opcode the opcode; one of the constants from {@link Dops}
       * @param position {@code non-null;} source position
       * @param registers {@code non-null;} register list, including a
       * result register if appropriate (that is, registers may be either
       * ins and outs)
       */
      public DalvInsn(Dop opcode, SourcePosition position,
                      RegisterSpecList registers) {
          if (opcode == null) {
              throw new NullPointerException("opcode == null");
          }
  
          if (position == null) {
              throw new NullPointerException("position == null");
          }
  
          if (registers == null) {
              throw new NullPointerException("registers == null");
          }
  
          this.address = -1;
          this.opcode = opcode;
          this.position = position;
          this.registers = registers;
      }
  
      /** {@inheritDoc} */
      @Override
      public final String toString() {
          StringBuffer sb = new StringBuffer(100);
  
          sb.append(identifierString());
          sb.append(' ');
          sb.append(position);
  
          sb.append(": ");
          sb.append(opcode.getName());
  
          boolean needComma = false;
          if (registers.size() != 0) {
              sb.append(registers.toHuman(" ", ", ", null));
              needComma = true;
          }
  
          String extra = argString();
          if (extra != null) {
              if (needComma) {
                  sb.append(',');
              }
              sb.append(' ');
              sb.append(extra);
          }
  
          return sb.toString();
      }
  
      /**
       * Gets whether the address of this instruction is known.
       *
       * @see #getAddress
       * @see #setAddress
       */
      public final boolean hasAddress() {
          return (address >= 0);
      }
  
      /**
       * Gets the output address of this instruction, if it is known. This throws
       * a {@code RuntimeException} if it has not yet been set.
       *
       * @see #setAddress
       *
       * @return {@code >= 0;} the output address
       */
      public final int getAddress() {
          if (address < 0) {
              throw new RuntimeException("address not yet known");
          }
  
          return address;
      }
  
      /**
       * Gets the opcode.
       *
       * @return {@code non-null;} the opcode
       */
      public final Dop getOpcode() {
          return opcode;
      }
  
      /**
       * Gets the source position.
       *
       * @return {@code non-null;} the source position
       */
      public final SourcePosition getPosition() {
          return position;
      }
  
      /**
       * Gets the register list for this instruction.
       *
       * @return {@code non-null;} the registers
       */
      public final RegisterSpecList getRegisters() {
          return registers;
      }
  
      /**
       * Returns whether this instance's opcode uses a result register.
       * This method is a convenient shorthand for
       * {@code getOpcode().hasResult()}.
       *
       * @return {@code true} iff this opcode uses a result register
       */
      public final boolean hasResult() {
          return opcode.hasResult();
      }
  
      /**
       * Gets the minimum distinct registers required for this instruction.
       * Uses the given BitSet to determine which registers require
       * replacement, and ignores registers that are already compatible.
       * This assumes that the result (if any) can share registers with the
       * sources (if any), that each source register is unique, and that
       * (to be explicit here) category-2 values take up two consecutive
       * registers.
       *
       * @param compatRegs {@code non-null;} set of compatible registers
       * @return {@code >= 0;} the minimum distinct register requirement
       */
      public final int getMinimumRegisterRequirement(BitSet compatRegs) {
          boolean hasResult = hasResult();
          int regSz = registers.size();
          int resultRequirement = 0;
          int sourceRequirement = 0;
  
          if (hasResult && !compatRegs.get(0)) {
              resultRequirement = registers.get(0).getCategory();
          }
  
          for (int i = hasResult ? 1 : 0; i < regSz; i++) {
              if (!compatRegs.get(i)) {
                  sourceRequirement += registers.get(i).getCategory();
              }
          }
  
          return Math.max(sourceRequirement, resultRequirement);
      }
  
      /**
       * Gets the instruction that is equivalent to this one, except that
       * it uses sequential registers starting at {@code 0} (storing
       * the result, if any, in register {@code 0} as well).
       *
       * @return {@code non-null;} the replacement
       */
      public DalvInsn getLowRegVersion() {
          RegisterSpecList regs =
              registers.withExpandedRegisters(0, hasResult(), null);
          return withRegisters(regs);
      }
  
      /**
       * Gets the instruction prefix required, if any, to use in an expanded
       * version of this instance. Will not generate moves for registers
       * marked compatible to the format by the given BitSet.
       *
       * @see #expandedVersion
       *
       * @param compatRegs {@code non-null;} set of compatible registers
       * @return {@code null-ok;} the prefix, if any
       */
      public DalvInsn expandedPrefix(BitSet compatRegs) {
          RegisterSpecList regs = registers;
          boolean firstBit = compatRegs.get(0);
  
          if (hasResult()) compatRegs.set(0);
  
          regs = regs.subset(compatRegs);
  
          if (hasResult()) compatRegs.set(0, firstBit);
  
          if (regs.size() == 0) return null;
  
          return new HighRegisterPrefix(position, regs);
      }
  
      /**
       * Gets the instruction suffix required, if any, to use in an expanded
       * version of this instance. Will not generate a move for a register
       * marked compatible to the format by the given BitSet.
       *
       * @see #expandedVersion
       *
       * @param compatRegs {@code non-null;} set of compatible registers
       * @return {@code null-ok;} the suffix, if any
       */
      public DalvInsn expandedSuffix(BitSet compatRegs) {
          if (hasResult() && !compatRegs.get(0)) {
              RegisterSpec r = registers.get(0);
              return makeMove(position, r, r.withReg(0));
          } else {
              return null;
          }
      }
  
      /**
       * Gets the instruction that is equivalent to this one, except that
       * it replaces incompatible registers with sequential registers
       * starting at {@code 0} (storing the result, if any, in register
       * {@code 0} as well). The sequence of instructions from
       * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
       * surrounding the result of a call to this method are the expanded
       * transformation of this instance, and it is guaranteed that the
       * number of low registers used will be the number returned by
       * {@link #getMinimumRegisterRequirement}.
       *
       * @param compatRegs {@code non-null;} set of compatible registers
       * @return {@code non-null;} the replacement
       */
      public DalvInsn expandedVersion(BitSet compatRegs) {
          RegisterSpecList regs =
              registers.withExpandedRegisters(0, hasResult(), compatRegs);
          return withRegisters(regs);
      }
  
      /**
       * Gets the short identifier for this instruction. This is its
       * address, if assigned, or its identity hashcode if not.
       *
       * @return {@code non-null;} the identifier
       */
      public final String identifierString() {
          if (address != -1) {
              return String.format("%04x", address);
          }
  
          return Hex.u4(System.identityHashCode(this));
      }
  
      /**
       * Returns the string form of this instance suitable for inclusion in
       * a human-oriented listing dump. This method will return {@code null}
       * if this instance should not appear in a listing.
       *
       * @param prefix {@code non-null;} prefix before the address; each follow-on
       * line will be indented to match as well
       * @param width {@code >= 0;} the width of the output or {@code 0} for
       * unlimited width
       * @param noteIndices whether to include an explicit notation of
       * constant pool indices
       * @return {@code null-ok;} the string form or {@code null} if this
       * instance should not appear in a listing
       */
      public final String listingString(String prefix, int width,
              boolean noteIndices) {
          String insnPerSe = listingString0(noteIndices);
  
          if (insnPerSe == null) {
              return null;
          }
  
          String addr = prefix + identifierString() + ": ";
          int w1 = addr.length();
          int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
  
          return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
      }
  
      /**
       * Sets the output address.
       *
       * @param address {@code >= 0;} the output address
       */
      public final void setAddress(int address) {
          if (address < 0) {
              throw new IllegalArgumentException("address < 0");
          }
  
          this.address = address;
      }
  
      /**
       * Gets the address immediately after this instance. This is only
       * calculable if this instance's address is known, and it is equal
       * to the address plus the length of the instruction format of this
       * instance's opcode.
       *
       * @return {@code >= 0;} the next address
       */
      public final int getNextAddress() {
          return getAddress() + codeSize();
      }
  
      /**
       * Returns an instance that is just like this one, except that the
       * register list is mapped by using {@code mapper}.
       *
       * @param mapper {@code non-null;} used to map registers
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public DalvInsn withMapper(RegisterMapper mapper) {
        return withRegisters(mapper.map(getRegisters()));
      }
  
      /**
       * Gets the size of this instruction, in 16-bit code units.
       *
       * @return {@code >= 0;} the code size of this instruction
       */
      public abstract int codeSize();
  
      /**
       * Writes this instance to the given output. This method should
       * never annotate the output.
       *
       * @param out {@code non-null;} where to write to
       */
      public abstract void writeTo(AnnotatedOutput out);
  
      /**
       * Returns an instance that is just like this one, except that its
       * opcode is replaced by the one given, and its address is reset.
       *
       * @param opcode {@code non-null;} the new opcode
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public abstract DalvInsn withOpcode(Dop opcode);
  
      /**
       * Returns an instance that is just like this one, except that all
       * register references have been offset by the given delta, and its
       * address is reset.
       *
       * @param delta the amount to offset register references by
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public abstract DalvInsn withRegisterOffset(int delta);
  
      /**
       * Returns an instance that is just like this one, except that the
       * register list is replaced by the given one, and its address is
       * reset.
       *
       * @param registers {@code non-null;} new register list
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public abstract DalvInsn withRegisters(RegisterSpecList registers);
  
      /**
       * Gets the string form for any arguments to this instance. Subclasses
       * must override this.
       *
       * @return {@code null-ok;} the string version of any arguments or
       * {@code null} if there are none
       */
      protected abstract String argString();
  
      /**
       * Helper for {@link #listingString}, which returns the string
       * form of this instance suitable for inclusion in a
       * human-oriented listing dump, not including the instruction
       * address and without respect for any output formatting. This
       * method should return {@code null} if this instance should
       * not appear in a listing.
       *
       * @param noteIndices whether to include an explicit notation of
       * constant pool indices
       * @return {@code null-ok;} the listing string
       */
      protected abstract String listingString0(boolean noteIndices);
  }
  