1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.code; 18 19import com.android.dx.io.Opcodes; 20import com.android.dx.rop.cst.Constant; 21import com.android.dx.rop.cst.CstBaseMethodRef; 22import com.android.dx.util.AnnotatedOutput; 23import com.android.dx.util.ExceptionWithContext; 24import com.android.dx.util.FixedSizeList; 25import com.android.dx.util.IndentingWriter; 26 27import java.io.IOException; 28import java.io.OutputStream; 29import java.io.OutputStreamWriter; 30import java.io.Writer; 31import java.util.ArrayList; 32 33/** 34 * List of {@link DalvInsn} instances. 35 */ 36public final class DalvInsnList extends FixedSizeList { 37 38 /** 39 * The amount of register space, in register units, required for this 40 * code block. This may be greater than the largest observed register+ 41 * category because the method this code block exists in may 42 * specify arguments that are unused by the method. 43 */ 44 private final int regCount; 45 46 /** 47 * Constructs and returns an immutable instance whose elements are 48 * identical to the ones in the given list, in the same order. 49 * 50 * @param list {@code non-null;} the list to use for elements 51 * @param regCount count, in register-units, of the number of registers 52 * this code block requires. 53 * @return {@code non-null;} an appropriately-constructed instance of this 54 * class 55 */ 56 public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list, 57 int regCount) { 58 int size = list.size(); 59 DalvInsnList result = new DalvInsnList(size, regCount); 60 61 for (int i = 0; i < size; i++) { 62 result.set(i, list.get(i)); 63 } 64 65 result.setImmutable(); 66 return result; 67 } 68 69 /** 70 * Constructs an instance. All indices initially contain {@code null}. 71 * 72 * @param size the size of the list 73 */ 74 public DalvInsnList(int size, int regCount) { 75 super(size); 76 this.regCount = regCount; 77 } 78 79 /** 80 * Gets the element at the given index. It is an error to call 81 * this with the index for an element which was never set; if you 82 * do that, this will throw {@code NullPointerException}. 83 * 84 * @param n {@code >= 0, < size();} which index 85 * @return {@code non-null;} element at that index 86 */ 87 public DalvInsn get(int n) { 88 return (DalvInsn) get0(n); 89 } 90 91 /** 92 * Sets the instruction at the given index. 93 * 94 * @param n {@code >= 0, < size();} which index 95 * @param insn {@code non-null;} the instruction to set at {@code n} 96 */ 97 public void set(int n, DalvInsn insn) { 98 set0(n, insn); 99 } 100 101 /** 102 * Gets the size of this instance, in 16-bit code units. This will only 103 * return a meaningful result if the instructions in this instance all 104 * have valid addresses. 105 * 106 * @return {@code >= 0;} the size 107 */ 108 public int codeSize() { 109 int sz = size(); 110 111 if (sz == 0) { 112 return 0; 113 } 114 115 DalvInsn last = get(sz - 1); 116 return last.getNextAddress(); 117 } 118 119 /** 120 * Writes all the instructions in this instance to the given output 121 * destination. 122 * 123 * @param out {@code non-null;} where to write to 124 */ 125 public void writeTo(AnnotatedOutput out) { 126 int startCursor = out.getCursor(); 127 int sz = size(); 128 129 if (out.annotates()) { 130 boolean verbose = out.isVerbose(); 131 132 for (int i = 0; i < sz; i++) { 133 DalvInsn insn = (DalvInsn) get0(i); 134 int codeBytes = insn.codeSize() * 2; 135 String s; 136 137 if ((codeBytes != 0) || verbose) { 138 s = insn.listingString(" ", out.getAnnotationWidth(), 139 true); 140 } else { 141 s = null; 142 } 143 144 if (s != null) { 145 out.annotate(codeBytes, s); 146 } else if (codeBytes != 0) { 147 out.annotate(codeBytes, ""); 148 } 149 } 150 } 151 152 for (int i = 0; i < sz; i++) { 153 DalvInsn insn = (DalvInsn) get0(i); 154 try { 155 insn.writeTo(out); 156 } catch (RuntimeException ex) { 157 throw ExceptionWithContext.withContext(ex, 158 "...while writing " + insn); 159 } 160 } 161 162 // Sanity check of the amount written. 163 int written = (out.getCursor() - startCursor) / 2; 164 if (written != codeSize()) { 165 throw new RuntimeException("write length mismatch; expected " + 166 codeSize() + " but actually wrote " + written); 167 } 168 } 169 170 /** 171 * Gets the minimum required register count implied by this 172 * instance. This includes any unused parameters that could 173 * potentially be at the top of the register space. 174 * @return {@code >= 0;} the required registers size 175 */ 176 public int getRegistersSize() { 177 return regCount; 178 } 179 180 /** 181 * Gets the size of the outgoing arguments area required by this 182 * method. This is equal to the largest argument word count of any 183 * method referred to by this instance. 184 * 185 * @return {@code >= 0;} the required outgoing arguments size 186 */ 187 public int getOutsSize() { 188 int sz = size(); 189 int result = 0; 190 191 for (int i = 0; i < sz; i++) { 192 DalvInsn insn = (DalvInsn) get0(i); 193 194 if (!(insn instanceof CstInsn)) { 195 continue; 196 } 197 198 Constant cst = ((CstInsn) insn).getConstant(); 199 200 if (!(cst instanceof CstBaseMethodRef)) { 201 continue; 202 } 203 204 boolean isStatic = 205 (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); 206 int count = 207 ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); 208 209 if (count > result) { 210 result = count; 211 } 212 } 213 214 return result; 215 } 216 217 /** 218 * Does a human-friendly dump of this instance. 219 * 220 * @param out {@code non-null;} where to dump 221 * @param prefix {@code non-null;} prefix to attach to each line of output 222 * @param verbose whether to be verbose; verbose output includes 223 * lines for zero-size instructions and explicit constant pool indices 224 */ 225 public void debugPrint(Writer out, String prefix, boolean verbose) { 226 IndentingWriter iw = new IndentingWriter(out, 0, prefix); 227 int sz = size(); 228 229 try { 230 for (int i = 0; i < sz; i++) { 231 DalvInsn insn = (DalvInsn) get0(i); 232 String s; 233 234 if ((insn.codeSize() != 0) || verbose) { 235 s = insn.listingString("", 0, verbose); 236 } else { 237 s = null; 238 } 239 240 if (s != null) { 241 iw.write(s); 242 } 243 } 244 245 iw.flush(); 246 } catch (IOException ex) { 247 throw new RuntimeException(ex); 248 } 249 } 250 251 /** 252 * Does a human-friendly dump of this instance. 253 * 254 * @param out {@code non-null;} where to dump 255 * @param prefix {@code non-null;} prefix to attach to each line of output 256 * @param verbose whether to be verbose; verbose output includes 257 * lines for zero-size instructions 258 */ 259 public void debugPrint(OutputStream out, String prefix, boolean verbose) { 260 Writer w = new OutputStreamWriter(out); 261 debugPrint(w, prefix, verbose); 262 263 try { 264 w.flush(); 265 } catch (IOException ex) { 266 throw new RuntimeException(ex); 267 } 268 } 269} 270