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