• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dexgen.dex.file;
18 
19 import com.android.dexgen.dex.code.CatchTable;
20 import com.android.dexgen.dex.code.CstInsn;
21 import com.android.dexgen.dex.code.DalvCode;
22 import com.android.dexgen.dex.code.DalvInsn;
23 import com.android.dexgen.dex.code.DalvInsnList;
24 import com.android.dexgen.dex.code.LocalList;
25 import com.android.dexgen.dex.code.PositionList;
26 import com.android.dexgen.rop.cst.Constant;
27 import com.android.dexgen.rop.cst.CstMemberRef;
28 import com.android.dexgen.rop.cst.CstMethodRef;
29 import com.android.dexgen.rop.cst.CstType;
30 import com.android.dexgen.rop.type.StdTypeList;
31 import com.android.dexgen.rop.type.Type;
32 import com.android.dexgen.rop.type.TypeList;
33 import com.android.dexgen.util.AnnotatedOutput;
34 import com.android.dexgen.util.ExceptionWithContext;
35 import com.android.dexgen.util.Hex;
36 
37 import java.io.PrintWriter;
38 import java.util.HashSet;
39 
40 /**
41  * Representation of all the parts needed for concrete methods in a
42  * {@code dex} file.
43  */
44 public 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      */
CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic, TypeList throwsList)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
itemType()111     public ItemType itemType() {
112         return ItemType.TYPE_CODE_ITEM;
113     }
114 
115     /** {@inheritDoc} */
addContents(DexFile file)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
toString()139     public String toString() {
140         return "CodeItem{" + toHuman() + "}";
141     }
142 
143     /** {@inheritDoc} */
144     @Override
toHuman()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      */
getRef()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      */
debugPrint(PrintWriter out, String prefix, boolean verbose)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
place0(Section addedTo, int offset)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
writeTo0(DexFile file, AnnotatedOutput out)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      */
writeCodes(DexFile file, AnnotatedOutput out)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      */
getInsSize()313     private int getInsSize() {
314         return ref.getParameterWordCount(isStatic);
315     }
316 
317     /**
318      * Get the out registers count.
319      *
320      * @return the count
321      */
getOutsSize()322     private int getOutsSize() {
323         return code.getInsns().getOutsSize();
324     }
325 
326     /**
327      * Get the total registers count.
328      *
329      * @return the count
330      */
getRegistersSize()331     private int getRegistersSize() {
332         return code.getInsns().getRegistersSize();
333     }
334 }
335