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