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