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