CodeItem.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
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.file; 18 19import com.android.dx.dex.code.CstInsn; 20import com.android.dx.dex.code.CatchTable; 21import com.android.dx.dex.code.DalvCode; 22import com.android.dx.dex.code.DalvInsn; 23import com.android.dx.dex.code.DalvInsnList; 24import com.android.dx.dex.code.LocalList; 25import com.android.dx.dex.code.PositionList; 26import com.android.dx.rop.cst.Constant; 27import com.android.dx.rop.cst.CstMemberRef; 28import com.android.dx.rop.cst.CstMethodRef; 29import com.android.dx.rop.cst.CstType; 30import com.android.dx.rop.type.StdTypeList; 31import com.android.dx.rop.type.Type; 32import com.android.dx.rop.type.TypeList; 33import com.android.dx.util.AnnotatedOutput; 34import com.android.dx.util.ExceptionWithContext; 35import com.android.dx.util.Hex; 36 37import java.io.PrintWriter; 38import java.util.HashSet; 39 40/** 41 * Representation of all the parts needed for concrete methods in a 42 * <code>dex</code> file. 43 */ 44public final class CodeItem extends OffsettedItem { 45 /** file alignment of this class, in bytes */ 46 private static final int ALIGNMENT = 4; 47 48 /** write size of the header of this class, in bytes */ 49 private static final int HEADER_SIZE = 16; 50 51 /** non-null; method that this code implements */ 52 private final CstMethodRef ref; 53 54 /** non-null; the bytecode instructions and associated data */ 55 private final DalvCode code; 56 57 /** null-ok; the catches, if needed; set in {@link #addContents} */ 58 private CatchStructs catches; 59 60 /** whether this instance is for a <code>static</code> method */ 61 private final boolean isStatic; 62 63 /** 64 * non-null; list of possibly-thrown exceptions; just used in 65 * generating debugging output (listings) 66 */ 67 private final TypeList throwsList; 68 69 /** 70 * null-ok; the debug info or <code>null</code> if there is none; 71 * set in {@link #addContents} 72 */ 73 private DebugInfoItem debugInfo; 74 75 /** 76 * Constructs an instance. 77 * 78 * @param ref non-null; method that this code implements 79 * @param code non-null; the underlying code 80 * @param isStatic whether this instance is for a <code>static</code> 81 * method 82 * @param throwsList non-null; list of possibly-thrown exceptions, 83 * just used in generating debugging output (listings) 84 */ 85 public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic, 86 TypeList throwsList) { 87 super(ALIGNMENT, -1); 88 89 if (ref == null) { 90 throw new NullPointerException("ref == null"); 91 } 92 93 if (code == null) { 94 throw new NullPointerException("code == null"); 95 } 96 97 if (throwsList == null) { 98 throw new NullPointerException("throwsList == null"); 99 } 100 101 this.ref = ref; 102 this.code = code; 103 this.isStatic = isStatic; 104 this.throwsList = throwsList; 105 this.catches = null; 106 this.debugInfo = null; 107 } 108 109 /** {@inheritDoc} */ 110 @Override 111 public ItemType itemType() { 112 return ItemType.TYPE_CODE_ITEM; 113 } 114 115 /** {@inheritDoc} */ 116 public void addContents(DexFile file) { 117 MixedItemSection wordData = file.getWordData(); 118 MixedItemSection byteData = file.getByteData(); 119 TypeIdsSection typeIds = file.getTypeIds(); 120 121 if (code.hasPositions() || code.hasLocals()) { 122 debugInfo = new DebugInfoItem(code, isStatic, ref); 123 byteData.add(debugInfo); 124 } 125 126 if (code.hasAnyCatches()) { 127 for (Type type : code.getCatchTypes()) { 128 typeIds.intern(type); 129 } 130 catches = new CatchStructs(code); 131 } 132 133 for (Constant c : code.getInsnConstants()) { 134 file.internIfAppropriate(c); 135 } 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public String toString() { 141 return "CodeItem{" + toHuman() + "}"; 142 } 143 144 /** {@inheritDoc} */ 145 @Override 146 public String toHuman() { 147 return ref.toHuman(); 148 } 149 150 /** 151 * Gets the reference to the method this instance implements. 152 * 153 * @return non-null; the method reference 154 */ 155 public CstMethodRef getRef() { 156 return ref; 157 } 158 159 /** 160 * Does a human-friendly dump of this instance. 161 * 162 * @param out non-null; where to dump 163 * @param prefix non-null; per-line prefix to use 164 * @param verbose whether to be verbose with the output 165 */ 166 public void debugPrint(PrintWriter out, String prefix, boolean verbose) { 167 out.println(ref.toHuman() + ":"); 168 169 DalvInsnList insns = code.getInsns(); 170 out.println("regs: " + Hex.u2(getRegistersSize()) + 171 "; ins: " + Hex.u2(getInsSize()) + "; outs: " + 172 Hex.u2(getOutsSize())); 173 174 insns.debugPrint(out, prefix, verbose); 175 176 String prefix2 = prefix + " "; 177 178 if (catches != null) { 179 out.print(prefix); 180 out.println("catches"); 181 catches.debugPrint(out, prefix2); 182 } 183 184 if (debugInfo != null) { 185 out.print(prefix); 186 out.println("debug info"); 187 debugInfo.debugPrint(out, prefix2); 188 } 189 } 190 191 /** {@inheritDoc} */ 192 @Override 193 protected void place0(Section addedTo, int offset) { 194 final DexFile file = addedTo.getFile(); 195 int catchesSize; 196 197 /* 198 * In order to get the catches and insns, all the code's 199 * constants need to be assigned indices. 200 */ 201 code.assignIndices(new DalvCode.AssignIndicesCallback() { 202 public int getIndex(Constant cst) { 203 IndexedItem item = file.findItemOrNull(cst); 204 if (item == null) { 205 return -1; 206 } 207 return item.getIndex(); 208 } 209 }); 210 211 if (catches != null) { 212 catches.encode(file); 213 catchesSize = catches.writeSize(); 214 } else { 215 catchesSize = 0; 216 } 217 218 /* 219 * The write size includes the header, two bytes per code 220 * unit, post-code padding if necessary, and however much 221 * space the catches need. 222 */ 223 224 int insnsSize = code.getInsns().codeSize(); 225 if ((insnsSize & 1) != 0) { 226 insnsSize++; 227 } 228 229 setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize); 230 } 231 232 /** {@inheritDoc} */ 233 @Override 234 protected void writeTo0(DexFile file, AnnotatedOutput out) { 235 boolean annotates = out.annotates(); 236 int regSz = getRegistersSize(); 237 int outsSz = getOutsSize(); 238 int insSz = getInsSize(); 239 int insnsSz = code.getInsns().codeSize(); 240 boolean needPadding = (insnsSz & 1) != 0; 241 int triesSz = (catches == null) ? 0 : catches.triesSize(); 242 int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset(); 243 244 if (annotates) { 245 out.annotate(0, offsetString() + ' ' + ref.toHuman()); 246 out.annotate(2, " registers_size: " + Hex.u2(regSz)); 247 out.annotate(2, " ins_size: " + Hex.u2(insSz)); 248 out.annotate(2, " outs_size: " + Hex.u2(outsSz)); 249 out.annotate(2, " tries_size: " + Hex.u2(triesSz)); 250 out.annotate(4, " debug_off: " + Hex.u4(debugOff)); 251 out.annotate(4, " insns_size: " + Hex.u4(insnsSz)); 252 253 // This isn't represented directly here, but it is useful to see. 254 int size = throwsList.size(); 255 if (size != 0) { 256 out.annotate(0, " throws " + StdTypeList.toHuman(throwsList)); 257 } 258 } 259 260 out.writeShort(regSz); 261 out.writeShort(insSz); 262 out.writeShort(outsSz); 263 out.writeShort(triesSz); 264 out.writeInt(debugOff); 265 out.writeInt(insnsSz); 266 267 writeCodes(file, out); 268 269 if (catches != null) { 270 if (needPadding) { 271 if (annotates) { 272 out.annotate(2, " padding: 0"); 273 } 274 out.writeShort(0); 275 } 276 277 catches.writeTo(file, out); 278 } 279 280 if (annotates) { 281 /* 282 * These are pointed at in the code header (above), but it's less 283 * distracting to expand on them at the bottom of the code. 284 */ 285 if (debugInfo != null) { 286 out.annotate(0, " debug info"); 287 debugInfo.annotateTo(file, out, " "); 288 } 289 } 290 } 291 292 /** 293 * Helper for {@link #writeTo0} which writes out the actual bytecode. 294 * 295 * @param file non-null; file we are part of 296 * @param out non-null; where to write to 297 */ 298 private void writeCodes(DexFile file, AnnotatedOutput out) { 299 DalvInsnList insns = code.getInsns(); 300 301 try { 302 insns.writeTo(out); 303 } catch (RuntimeException ex) { 304 throw ExceptionWithContext.withContext(ex, "...while writing " + 305 "instructions for " + ref.toHuman()); 306 } 307 } 308 309 /** 310 * Get the in registers count. 311 * 312 * @return the count 313 */ 314 private int getInsSize() { 315 return ref.getParameterWordCount(isStatic); 316 } 317 318 /** 319 * Get the out registers count. 320 * 321 * @return the count 322 */ 323 private int getOutsSize() { 324 return code.getInsns().getOutsSize(); 325 } 326 327 /** 328 * Get the total registers count. 329 * 330 * @return the count 331 */ 332 private int getRegistersSize() { 333 return code.getInsns().getRegistersSize(); 334 } 335} 336