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