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