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