/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.dex.file; import com.android.dx.dex.code.CstInsn; import com.android.dx.dex.code.CatchTable; import com.android.dx.dex.code.DalvCode; import com.android.dx.dex.code.DalvInsn; import com.android.dx.dex.code.DalvInsnList; import com.android.dx.dex.code.LocalList; import com.android.dx.dex.code.PositionList; import com.android.dx.rop.cst.Constant; import com.android.dx.rop.cst.CstMemberRef; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.StdTypeList; import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeList; import com.android.dx.util.AnnotatedOutput; import com.android.dx.util.ExceptionWithContext; import com.android.dx.util.Hex; import java.io.PrintWriter; import java.util.HashSet; /** * Representation of all the parts needed for concrete methods in a * {@code dex} file. */ public final class CodeItem extends OffsettedItem { /** file alignment of this class, in bytes */ private static final int ALIGNMENT = 4; /** write size of the header of this class, in bytes */ private static final int HEADER_SIZE = 16; /** {@code non-null;} method that this code implements */ private final CstMethodRef ref; /** {@code non-null;} the bytecode instructions and associated data */ private final DalvCode code; /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */ private CatchStructs catches; /** whether this instance is for a {@code static} method */ private final boolean isStatic; /** * {@code non-null;} list of possibly-thrown exceptions; just used in * generating debugging output (listings) */ private final TypeList throwsList; /** * {@code null-ok;} the debug info or {@code null} if there is none; * set in {@link #addContents} */ private DebugInfoItem debugInfo; /** * Constructs an instance. * * @param ref {@code non-null;} method that this code implements * @param code {@code non-null;} the underlying code * @param isStatic whether this instance is for a {@code static} * method * @param throwsList {@code non-null;} list of possibly-thrown exceptions, * just used in generating debugging output (listings) */ public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic, TypeList throwsList) { super(ALIGNMENT, -1); if (ref == null) { throw new NullPointerException("ref == null"); } if (code == null) { throw new NullPointerException("code == null"); } if (throwsList == null) { throw new NullPointerException("throwsList == null"); } this.ref = ref; this.code = code; this.isStatic = isStatic; this.throwsList = throwsList; this.catches = null; this.debugInfo = null; } /** {@inheritDoc} */ @Override public ItemType itemType() { return ItemType.TYPE_CODE_ITEM; } /** {@inheritDoc} */ public void addContents(DexFile file) { MixedItemSection byteData = file.getByteData(); TypeIdsSection typeIds = file.getTypeIds(); if (code.hasPositions() || code.hasLocals()) { debugInfo = new DebugInfoItem(code, isStatic, ref); byteData.add(debugInfo); } if (code.hasAnyCatches()) { for (Type type : code.getCatchTypes()) { typeIds.intern(type); } catches = new CatchStructs(code); } for (Constant c : code.getInsnConstants()) { file.internIfAppropriate(c); } } /** {@inheritDoc} */ @Override public String toString() { return "CodeItem{" + toHuman() + "}"; } /** {@inheritDoc} */ @Override public String toHuman() { return ref.toHuman(); } /** * Gets the reference to the method this instance implements. * * @return {@code non-null;} the method reference */ public CstMethodRef getRef() { return ref; } /** * Does a human-friendly dump of this instance. * * @param out {@code non-null;} where to dump * @param prefix {@code non-null;} per-line prefix to use * @param verbose whether to be verbose with the output */ public void debugPrint(PrintWriter out, String prefix, boolean verbose) { out.println(ref.toHuman() + ":"); DalvInsnList insns = code.getInsns(); out.println("regs: " + Hex.u2(getRegistersSize()) + "; ins: " + Hex.u2(getInsSize()) + "; outs: " + Hex.u2(getOutsSize())); insns.debugPrint(out, prefix, verbose); String prefix2 = prefix + " "; if (catches != null) { out.print(prefix); out.println("catches"); catches.debugPrint(out, prefix2); } if (debugInfo != null) { out.print(prefix); out.println("debug info"); debugInfo.debugPrint(out, prefix2); } } /** {@inheritDoc} */ @Override protected void place0(Section addedTo, int offset) { final DexFile file = addedTo.getFile(); int catchesSize; /* * In order to get the catches and insns, all the code's * constants need to be assigned indices. */ code.assignIndices(new DalvCode.AssignIndicesCallback() { public int getIndex(Constant cst) { IndexedItem item = file.findItemOrNull(cst); if (item == null) { return -1; } return item.getIndex(); } }); if (catches != null) { catches.encode(file); catchesSize = catches.writeSize(); } else { catchesSize = 0; } /* * The write size includes the header, two bytes per code * unit, post-code padding if necessary, and however much * space the catches need. */ int insnsSize = code.getInsns().codeSize(); if ((insnsSize & 1) != 0) { insnsSize++; } setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize); } /** {@inheritDoc} */ @Override protected void writeTo0(DexFile file, AnnotatedOutput out) { boolean annotates = out.annotates(); int regSz = getRegistersSize(); int outsSz = getOutsSize(); int insSz = getInsSize(); int insnsSz = code.getInsns().codeSize(); boolean needPadding = (insnsSz & 1) != 0; int triesSz = (catches == null) ? 0 : catches.triesSize(); int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset(); if (annotates) { out.annotate(0, offsetString() + ' ' + ref.toHuman()); out.annotate(2, " registers_size: " + Hex.u2(regSz)); out.annotate(2, " ins_size: " + Hex.u2(insSz)); out.annotate(2, " outs_size: " + Hex.u2(outsSz)); out.annotate(2, " tries_size: " + Hex.u2(triesSz)); out.annotate(4, " debug_off: " + Hex.u4(debugOff)); out.annotate(4, " insns_size: " + Hex.u4(insnsSz)); // This isn't represented directly here, but it is useful to see. int size = throwsList.size(); if (size != 0) { out.annotate(0, " throws " + StdTypeList.toHuman(throwsList)); } } out.writeShort(regSz); out.writeShort(insSz); out.writeShort(outsSz); out.writeShort(triesSz); out.writeInt(debugOff); out.writeInt(insnsSz); writeCodes(file, out); if (catches != null) { if (needPadding) { if (annotates) { out.annotate(2, " padding: 0"); } out.writeShort(0); } catches.writeTo(file, out); } if (annotates) { /* * These are pointed at in the code header (above), but it's less * distracting to expand on them at the bottom of the code. */ if (debugInfo != null) { out.annotate(0, " debug info"); debugInfo.annotateTo(file, out, " "); } } } /** * Helper for {@link #writeTo0} which writes out the actual bytecode. * * @param file {@code non-null;} file we are part of * @param out {@code non-null;} where to write to */ private void writeCodes(DexFile file, AnnotatedOutput out) { DalvInsnList insns = code.getInsns(); try { insns.writeTo(out); } catch (RuntimeException ex) { throw ExceptionWithContext.withContext(ex, "...while writing " + "instructions for " + ref.toHuman()); } } /** * Get the in registers count. * * @return the count */ private int getInsSize() { return ref.getParameterWordCount(isStatic); } /** * Get the out registers count. * * @return the count */ private int getOutsSize() { return code.getInsns().getOutsSize(); } /** * Get the total registers count. * * @return the count */ private int getRegistersSize() { return code.getInsns().getRegistersSize(); } }