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.dx.dex.code; 18 19 import com.android.dex.util.ExceptionWithContext; 20 import com.android.dx.io.Opcodes; 21 import com.android.dx.rop.cst.Constant; 22 import com.android.dx.rop.cst.CstBaseMethodRef; 23 import com.android.dx.rop.cst.CstProtoRef; 24 import com.android.dx.util.AnnotatedOutput; 25 import com.android.dx.util.FixedSizeList; 26 import com.android.dx.util.IndentingWriter; 27 import java.io.IOException; 28 import java.io.OutputStream; 29 import java.io.OutputStreamWriter; 30 import java.io.Writer; 31 import java.util.ArrayList; 32 33 /** 34 * List of {@link DalvInsn} instances. 35 */ 36 public 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 * @param regCount count, in register-units, of the number of registers 74 * this code block requires. 75 */ 76 public DalvInsnList(int size, int regCount) { 77 super(size); 78 this.regCount = regCount; 79 } 80 81 /** 82 * Gets the element at the given index. It is an error to call 83 * this with the index for an element which was never set; if you 84 * do that, this will throw {@code NullPointerException}. 85 * 86 * @param n {@code >= 0, < size();} which index 87 * @return {@code non-null;} element at that index 88 */ 89 public DalvInsn get(int n) { 90 return (DalvInsn) get0(n); 91 } 92 93 /** 94 * Sets the instruction at the given index. 95 * 96 * @param n {@code >= 0, < size();} which index 97 * @param insn {@code non-null;} the instruction to set at {@code n} 98 */ 99 public void set(int n, DalvInsn insn) { 100 set0(n, insn); 101 } 102 103 /** 104 * Gets the size of this instance, in 16-bit code units. This will only 105 * return a meaningful result if the instructions in this instance all 106 * have valid addresses. 107 * 108 * @return {@code >= 0;} the size 109 */ 110 public int codeSize() { 111 int sz = size(); 112 113 if (sz == 0) { 114 return 0; 115 } 116 117 DalvInsn last = get(sz - 1); 118 return last.getNextAddress(); 119 } 120 121 /** 122 * Writes all the instructions in this instance to the given output 123 * destination. 124 * 125 * @param out {@code non-null;} where to write to 126 */ 127 public void writeTo(AnnotatedOutput out) { 128 int startCursor = out.getCursor(); 129 int sz = size(); 130 131 if (out.annotates()) { 132 boolean verbose = out.isVerbose(); 133 134 for (int i = 0; i < sz; i++) { 135 DalvInsn insn = (DalvInsn) get0(i); 136 int codeBytes = insn.codeSize() * 2; 137 String s; 138 139 if ((codeBytes != 0) || verbose) { 140 s = insn.listingString(" ", out.getAnnotationWidth(), 141 true); 142 } else { 143 s = null; 144 } 145 146 if (s != null) { 147 out.annotate(codeBytes, s); 148 } else if (codeBytes != 0) { 149 out.annotate(codeBytes, ""); 150 } 151 } 152 } 153 154 for (int i = 0; i < sz; i++) { 155 DalvInsn insn = (DalvInsn) get0(i); 156 try { 157 insn.writeTo(out); 158 } catch (RuntimeException ex) { 159 throw ExceptionWithContext.withContext(ex, 160 "...while writing " + insn); 161 } 162 } 163 164 // Sanity check of the amount written. 165 int written = (out.getCursor() - startCursor) / 2; 166 if (written != codeSize()) { 167 throw new RuntimeException("write length mismatch; expected " + 168 codeSize() + " but actually wrote " + written); 169 } 170 } 171 172 /** 173 * Gets the minimum required register count implied by this 174 * instance. This includes any unused parameters that could 175 * potentially be at the top of the register space. 176 * @return {@code >= 0;} the required registers size 177 */ 178 public int getRegistersSize() { 179 return regCount; 180 } 181 182 /** 183 * Gets the size of the outgoing arguments area required by this 184 * method. This is equal to the largest argument word count of any 185 * method referred to by this instance. 186 * 187 * @return {@code >= 0;} the required outgoing arguments size 188 */ 189 public int getOutsSize() { 190 int sz = size(); 191 int result = 0; 192 193 for (int i = 0; i < sz; i++) { 194 DalvInsn insn = (DalvInsn) get0(i); 195 int count = 0; 196 197 if (insn instanceof CstInsn) { 198 Constant cst = ((CstInsn) insn).getConstant(); 199 if (cst instanceof CstBaseMethodRef) { 200 CstBaseMethodRef methodRef = (CstBaseMethodRef) cst; 201 boolean isStatic = 202 (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); 203 count = methodRef.getParameterWordCount(isStatic); 204 } 205 } else if (insn instanceof MultiCstInsn) { 206 if (insn.getOpcode().getFamily() != Opcodes.INVOKE_POLYMORPHIC) { 207 throw new RuntimeException("Expecting invoke-polymorphic"); 208 } 209 MultiCstInsn mci = (MultiCstInsn) insn; 210 // Invoke-polymorphic has two constants: [0] method-ref and 211 // [1] call site prototype. The number of arguments is based 212 // on the call site prototype since these are the arguments 213 // presented. The method-ref is always MethodHandle.invoke(Object[]) 214 // or MethodHandle.invokeExact(Object[]). 215 CstProtoRef proto = (CstProtoRef) mci.getConstant(1); 216 count = proto.getPrototype().getParameterTypes().getWordCount(); 217 count = count + 1; // And one for receiver (method handle). 218 } else { 219 continue; 220 } 221 222 if (count > result) { 223 result = count; 224 } 225 } 226 227 return result; 228 } 229 230 /** 231 * Does a human-friendly dump of this instance. 232 * 233 * @param out {@code non-null;} where to dump 234 * @param prefix {@code non-null;} prefix to attach to each line of output 235 * @param verbose whether to be verbose; verbose output includes 236 * lines for zero-size instructions and explicit constant pool indices 237 */ 238 public void debugPrint(Writer out, String prefix, boolean verbose) { 239 IndentingWriter iw = new IndentingWriter(out, 0, prefix); 240 int sz = size(); 241 242 try { 243 for (int i = 0; i < sz; i++) { 244 DalvInsn insn = (DalvInsn) get0(i); 245 String s; 246 247 if ((insn.codeSize() != 0) || verbose) { 248 s = insn.listingString("", 0, verbose); 249 } else { 250 s = null; 251 } 252 253 if (s != null) { 254 iw.write(s); 255 } 256 } 257 258 iw.flush(); 259 } catch (IOException ex) { 260 throw new RuntimeException(ex); 261 } 262 } 263 264 /** 265 * Does a human-friendly dump of this instance. 266 * 267 * @param out {@code non-null;} where to dump 268 * @param prefix {@code non-null;} prefix to attach to each line of output 269 * @param verbose whether to be verbose; verbose output includes 270 * lines for zero-size instructions 271 */ 272 public void debugPrint(OutputStream out, String prefix, boolean verbose) { 273 Writer w = new OutputStreamWriter(out); 274 debugPrint(w, prefix, verbose); 275 276 try { 277 w.flush(); 278 } catch (IOException ex) { 279 throw new RuntimeException(ex); 280 } 281 } 282 } 283