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