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.dex.util.ExceptionWithContext;
  import com.taobao.android.dx.io.Opcodes;
  import com.taobao.android.dx.rop.cst.Constant;
  import com.taobao.android.dx.rop.cst.CstBaseMethodRef;
  import com.taobao.android.dx.util.AnnotatedOutput;
  import com.taobao.android.dx.util.FixedSizeList;
  import com.taobao.android.dx.util.IndentingWriter;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.OutputStreamWriter;
  import java.io.Writer;
  import java.util.ArrayList;
  
  /**
   * List of {@link DalvInsn} instances.
   */
  public final class DalvInsnList extends FixedSizeList {
  
      /**
       * The amount of register space, in register units, required for this
       * code block. This may be greater than the largest observed register+
       * category because the method this code block exists in may
       * specify arguments that are unused by the method.
       */
      private final int regCount;
  
      /**
       * Constructs and returns an immutable instance whose elements are
       * identical to the ones in the given list, in the same order.
       *
       * @param list {@code non-null;} the list to use for elements
       * @param regCount count, in register-units, of the number of registers
       * this code block requires.
       * @return {@code non-null;} an appropriately-constructed instance of this
       * class
       */
      public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
              int regCount) {
          int size = list.size();
          DalvInsnList result = new DalvInsnList(size, regCount);
  
          for (int i = 0; i < size; i++) {
              result.set(i, list.get(i));
          }
  
          result.setImmutable();
          return result;
      }
  
      /**
       * Constructs an instance. All indices initially contain {@code null}.
       *
       * @param size the size of the list
       */
      public DalvInsnList(int size, int regCount) {
          super(size);
          this.regCount = regCount;
      }
  
      /**
       * Gets the element at the given index. It is an error to call
       * this with the index for an element which was never set; if you
       * do that, this will throw {@code NullPointerException}.
       *
       * @param n {@code >= 0, < size();} which index
       * @return {@code non-null;} element at that index
       */
      public DalvInsn get(int n) {
          return (DalvInsn) get0(n);
      }
  
      /**
       * Sets the instruction at the given index.
       *
       * @param n {@code >= 0, < size();} which index
       * @param insn {@code non-null;} the instruction to set at {@code n}
       */
      public void set(int n, DalvInsn insn) {
          set0(n, insn);
      }
  
      /**
       * Gets the size of this instance, in 16-bit code units. This will only
       * return a meaningful result if the instructions in this instance all
       * have valid addresses.
       *
       * @return {@code >= 0;} the size
       */
      public int codeSize() {
          int sz = size();
  
          if (sz == 0) {
              return 0;
          }
  
          DalvInsn last = get(sz - 1);
          return last.getNextAddress();
      }
  
      /**
       * Writes all the instructions in this instance to the given output
       * destination.
       *
       * @param out {@code non-null;} where to write to
       */
      public void writeTo(AnnotatedOutput out) {
          int startCursor = out.getCursor();
          int sz = size();
  
          if (out.annotates()) {
              boolean verbose = out.isVerbose();
  
              for (int i = 0; i < sz; i++) {
                  DalvInsn insn = (DalvInsn) get0(i);
                  int codeBytes = insn.codeSize() * 2;
                  String s;
  
                  if ((codeBytes != 0) || verbose) {
                      s = insn.listingString("  ", out.getAnnotationWidth(),
                              true);
                  } else {
                      s = null;
                  }
  
                  if (s != null) {
                      out.annotate(codeBytes, s);
                  } else if (codeBytes != 0) {
                      out.annotate(codeBytes, "");
                  }
              }
          }
  
          for (int i = 0; i < sz; i++) {
              DalvInsn insn = (DalvInsn) get0(i);
              try {
                  insn.writeTo(out);
              } catch (RuntimeException ex) {
                  throw ExceptionWithContext.withContext(ex,
                          "...while writing " + insn);
              }
          }
  
          // Sanity check of the amount written.
          int written = (out.getCursor() - startCursor) / 2;
          if (written != codeSize()) {
              throw new RuntimeException("write length mismatch; expected " +
                      codeSize() + " but actually wrote " + written);
          }
      }
  
      /**
       * Gets the minimum required register count implied by this
       * instance.  This includes any unused parameters that could
       * potentially be at the top of the register space.
       * @return {@code >= 0;} the required registers size
       */
      public int getRegistersSize() {
          return regCount;
      }
  
      /**
       * Gets the size of the outgoing arguments area required by this
       * method. This is equal to the largest argument word count of any
       * method referred to by this instance.
       *
       * @return {@code >= 0;} the required outgoing arguments size
       */
      public int getOutsSize() {
          int sz = size();
          int result = 0;
  
          for (int i = 0; i < sz; i++) {
              DalvInsn insn = (DalvInsn) get0(i);
  
              if (!(insn instanceof CstInsn)) {
                  continue;
              }
  
              Constant cst = ((CstInsn) insn).getConstant();
  
              if (!(cst instanceof CstBaseMethodRef)) {
                  continue;
              }
  
              boolean isStatic =
                  (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC);
              int count =
                  ((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
  
              if (count > result) {
                  result = count;
              }
          }
  
          return result;
      }
  
      /**
       * Does a human-friendly dump of this instance.
       *
       * @param out {@code non-null;} where to dump
       * @param prefix {@code non-null;} prefix to attach to each line of output
       * @param verbose whether to be verbose; verbose output includes
       * lines for zero-size instructions and explicit constant pool indices
       */
      public void debugPrint(Writer out, String prefix, boolean verbose) {
          IndentingWriter iw = new IndentingWriter(out, 0, prefix);
          int sz = size();
  
          try {
              for (int i = 0; i < sz; i++) {
                  DalvInsn insn = (DalvInsn) get0(i);
                  String s;
  
                  if ((insn.codeSize() != 0) || verbose) {
                      s = insn.listingString("", 0, verbose);
                  } else {
                      s = null;
                  }
  
                  if (s != null) {
                      iw.write(s);
                  }
              }
  
              iw.flush();
          } catch (IOException ex) {
              throw new RuntimeException(ex);
          }
      }
  
      /**
       * Does a human-friendly dump of this instance.
       *
       * @param out {@code non-null;} where to dump
       * @param prefix {@code non-null;} prefix to attach to each line of output
       * @param verbose whether to be verbose; verbose output includes
       * lines for zero-size instructions
       */
      public void debugPrint(OutputStream out, String prefix, boolean verbose) {
          Writer w = new OutputStreamWriter(out);
          debugPrint(w, prefix, verbose);
  
          try {
              w.flush();
          } catch (IOException ex) {
              throw new RuntimeException(ex);
          }
      }
  }
  