• Home
  • History
  • Annotate
  • 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.dx.dex.code;
18 
19 import com.android.dex.util.ExceptionWithContext;
20 import com.android.dx.io.Opcodes;
21 import com.android.dx.rop.cst.Constant;
22 import com.android.dx.rop.cst.CstBaseMethodRef;
23 import com.android.dx.rop.cst.CstCallSiteRef;
24 import com.android.dx.rop.cst.CstProtoRef;
25 import com.android.dx.util.AnnotatedOutput;
26 import com.android.dx.util.FixedSizeList;
27 import com.android.dx.util.IndentingWriter;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.Writer;
32 import java.util.ArrayList;
33 
34 /**
35  * List of {@link DalvInsn} instances.
36  */
37 public final class DalvInsnList extends FixedSizeList {
38 
39     /**
40      * The amount of register space, in register units, required for this
41      * code block. This may be greater than the largest observed register+
42      * category because the method this code block exists in may
43      * specify arguments that are unused by the method.
44      */
45     private final int regCount;
46 
47     /**
48      * Constructs and returns an immutable instance whose elements are
49      * identical to the ones in the given list, in the same order.
50      *
51      * @param list {@code non-null;} the list to use for elements
52      * @param regCount count, in register-units, of the number of registers
53      * this code block requires.
54      * @return {@code non-null;} an appropriately-constructed instance of this
55      * class
56      */
makeImmutable(ArrayList<DalvInsn> list, int regCount)57     public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
58             int regCount) {
59         int size = list.size();
60         DalvInsnList result = new DalvInsnList(size, regCount);
61 
62         for (int i = 0; i < size; i++) {
63             result.set(i, list.get(i));
64         }
65 
66         result.setImmutable();
67         return result;
68     }
69 
70     /**
71      * Constructs an instance. All indices initially contain {@code null}.
72      *
73      * @param size the size of the list
74      * @param regCount count, in register-units, of the number of registers
75      * this code block requires.
76      */
DalvInsnList(int size, int regCount)77     public DalvInsnList(int size, int regCount) {
78         super(size);
79         this.regCount = regCount;
80     }
81 
82     /**
83      * Gets the element at the given index. It is an error to call
84      * this with the index for an element which was never set; if you
85      * do that, this will throw {@code NullPointerException}.
86      *
87      * @param n {@code >= 0, < size();} which index
88      * @return {@code non-null;} element at that index
89      */
get(int n)90     public DalvInsn get(int n) {
91         return (DalvInsn) get0(n);
92     }
93 
94     /**
95      * Sets the instruction at the given index.
96      *
97      * @param n {@code >= 0, < size();} which index
98      * @param insn {@code non-null;} the instruction to set at {@code n}
99      */
set(int n, DalvInsn insn)100     public void set(int n, DalvInsn insn) {
101         set0(n, insn);
102     }
103 
104     /**
105      * Gets the size of this instance, in 16-bit code units. This will only
106      * return a meaningful result if the instructions in this instance all
107      * have valid addresses.
108      *
109      * @return {@code >= 0;} the size
110      */
codeSize()111     public int codeSize() {
112         int sz = size();
113 
114         if (sz == 0) {
115             return 0;
116         }
117 
118         DalvInsn last = get(sz - 1);
119         return last.getNextAddress();
120     }
121 
122     /**
123      * Writes all the instructions in this instance to the given output
124      * destination.
125      *
126      * @param out {@code non-null;} where to write to
127      */
writeTo(AnnotatedOutput out)128     public void writeTo(AnnotatedOutput out) {
129         int startCursor = out.getCursor();
130         int sz = size();
131 
132         if (out.annotates()) {
133             boolean verbose = out.isVerbose();
134 
135             for (int i = 0; i < sz; i++) {
136                 DalvInsn insn = (DalvInsn) get0(i);
137                 int codeBytes = insn.codeSize() * 2;
138                 String s;
139 
140                 if ((codeBytes != 0) || verbose) {
141                     s = insn.listingString("  ", out.getAnnotationWidth(),
142                             true);
143                 } else {
144                     s = null;
145                 }
146 
147                 if (s != null) {
148                     out.annotate(codeBytes, s);
149                 } else if (codeBytes != 0) {
150                     out.annotate(codeBytes, "");
151                 }
152             }
153         }
154 
155         for (int i = 0; i < sz; i++) {
156             DalvInsn insn = (DalvInsn) get0(i);
157             try {
158                 insn.writeTo(out);
159             } catch (RuntimeException ex) {
160                 throw ExceptionWithContext.withContext(ex,
161                         "...while writing " + insn);
162             }
163         }
164 
165         // Sanity check of the amount written.
166         int written = (out.getCursor() - startCursor) / 2;
167         if (written != codeSize()) {
168             throw new RuntimeException("write length mismatch; expected " +
169                     codeSize() + " but actually wrote " + written);
170         }
171     }
172 
173     /**
174      * Gets the minimum required register count implied by this
175      * instance.  This includes any unused parameters that could
176      * potentially be at the top of the register space.
177      * @return {@code >= 0;} the required registers size
178      */
getRegistersSize()179     public int getRegistersSize() {
180         return regCount;
181     }
182 
183     /**
184      * Gets the size of the outgoing arguments area required by this
185      * method. This is equal to the largest argument word count of any
186      * method referred to by this instance.
187      *
188      * @return {@code >= 0;} the required outgoing arguments size
189      */
getOutsSize()190     public int getOutsSize() {
191         int sz = size();
192         int result = 0;
193 
194         for (int i = 0; i < sz; i++) {
195             DalvInsn insn = (DalvInsn) get0(i);
196             int count = 0;
197 
198             if (insn instanceof CstInsn) {
199                 Constant cst = ((CstInsn) insn).getConstant();
200                 if (cst instanceof CstBaseMethodRef) {
201                     CstBaseMethodRef methodRef = (CstBaseMethodRef) cst;
202                     boolean isStatic =
203                         (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC);
204                     count = methodRef.getParameterWordCount(isStatic);
205                 } else if (cst instanceof CstCallSiteRef) {
206                     CstCallSiteRef invokeDynamicRef = (CstCallSiteRef) cst;
207                     count = invokeDynamicRef.getPrototype().getParameterTypes().getWordCount();
208                 }
209             } else if (insn instanceof MultiCstInsn) {
210                 if (insn.getOpcode().getFamily() != Opcodes.INVOKE_POLYMORPHIC) {
211                     throw new RuntimeException("Expecting invoke-polymorphic");
212                 }
213                 MultiCstInsn mci = (MultiCstInsn) insn;
214                 // Invoke-polymorphic has two constants: [0] method-ref and
215                 // [1] call site prototype. The number of arguments is based
216                 // on the call site prototype since these are the arguments
217                 // presented. The method-ref is always MethodHandle.invoke(Object[])
218                 // or MethodHandle.invokeExact(Object[]).
219                 CstProtoRef proto = (CstProtoRef) mci.getConstant(1);
220                 count = proto.getPrototype().getParameterTypes().getWordCount();
221                 count = count + 1; // And one for receiver (method handle).
222             } else {
223                 continue;
224             }
225 
226             if (count > result) {
227                 result = count;
228             }
229         }
230 
231         return result;
232     }
233 
234     /**
235      * Does a human-friendly dump of this instance.
236      *
237      * @param out {@code non-null;} where to dump
238      * @param prefix {@code non-null;} prefix to attach to each line of output
239      * @param verbose whether to be verbose; verbose output includes
240      * lines for zero-size instructions and explicit constant pool indices
241      */
debugPrint(Writer out, String prefix, boolean verbose)242     public void debugPrint(Writer out, String prefix, boolean verbose) {
243         IndentingWriter iw = new IndentingWriter(out, 0, prefix);
244         int sz = size();
245 
246         try {
247             for (int i = 0; i < sz; i++) {
248                 DalvInsn insn = (DalvInsn) get0(i);
249                 String s;
250 
251                 if ((insn.codeSize() != 0) || verbose) {
252                     s = insn.listingString("", 0, verbose);
253                 } else {
254                     s = null;
255                 }
256 
257                 if (s != null) {
258                     iw.write(s);
259                 }
260             }
261 
262             iw.flush();
263         } catch (IOException ex) {
264             throw new RuntimeException(ex);
265         }
266     }
267 
268     /**
269      * Does a human-friendly dump of this instance.
270      *
271      * @param out {@code non-null;} where to dump
272      * @param prefix {@code non-null;} prefix to attach to each line of output
273      * @param verbose whether to be verbose; verbose output includes
274      * lines for zero-size instructions
275      */
debugPrint(OutputStream out, String prefix, boolean verbose)276     public void debugPrint(OutputStream out, String prefix, boolean verbose) {
277         Writer w = new OutputStreamWriter(out);
278         debugPrint(w, prefix, verbose);
279 
280         try {
281             w.flush();
282         } catch (IOException ex) {
283             throw new RuntimeException(ex);
284         }
285     }
286 }
287