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