/* * 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.dex.code; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.CstBaseMethodRef; import com.android.dx.util.AnnotatedOutput; import com.android.dx.util.ExceptionWithContext; import com.android.dx.util.FixedSizeList; import com.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() == DalvOps.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); } } }