Code.java revision 3e7a2230ec75b59ae9b4aad292f51df2542ced7d
1b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher/*
2b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Copyright (C) 2011 The Android Open Source Project
3b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *
4b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Licensed under the Apache License, Version 2.0 (the "License");
5b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * you may not use this file except in compliance with the License.
6b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * You may obtain a copy of the License at
7b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *
8b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *      http://www.apache.org/licenses/LICENSE-2.0
9b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *
10b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Unless required by applicable law or agreed to in writing, software
11b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * distributed under the License is distributed on an "AS IS" BASIS,
12b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * See the License for the specific language governing permissions and
14b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * limitations under the License.
15674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak */
16674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak
17b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherpackage com.google.dexmaker;
180b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth
19b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.BasicBlockList;
20b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.Insn;
21b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.PlainCstInsn;
223574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport com.android.dx.rop.code.PlainInsn;
239d434dbff3eb0501efc3457acec2401afdffef2fEli Friedmanimport com.android.dx.rop.code.RegisterSpecList;
24dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hinesimport com.android.dx.rop.code.Rop;
25b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_GOTO;
26b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_NONE;
27b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_RETURN;
28b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.Rops;
29b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.SourcePosition;
30b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.ThrowingCstInsn;
313574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport com.android.dx.rop.code.ThrowingInsn;
3251004dff923259c90591621e7151408ad94e0eb8Nuno Lopesimport com.android.dx.rop.cst.CstInteger;
33b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.type.StdTypeList;
34a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_BYTE;
35a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_CHAR;
36a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_INT;
37a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_SHORT;
383574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport java.util.ArrayList;
39a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport java.util.Collections;
40b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport java.util.Iterator;
41b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport java.util.List;
42b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher
433574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow/**
4451004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * Builds a sequence of instructions.
45b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *
46386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * <h3>Locals</h3>
47386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * All data manipulation takes place in local variables. Each parameter gets its
483574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * own local by default; access these using {@link #getParameter
49386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * getParameter()}. Non-static methods and constructors also have a {@code this}
50b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * parameter; it's available as {@link #getThis getThis()}. Allocate a new local
51b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * variable using {@link #newLocal newLocal()}, and assign a default value to it
52b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * with {@link #loadConstant loadConstant()}. Copy a value from one local to
533574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * another with {@link #move move()}.
5451004dff923259c90591621e7151408ad94e0eb8Nuno Lopes *
557fa30b8e5d2774867787a4e4db17bf27358e5aacBenjamin Kramer * <p>Every local variable has a fixed type. This is either a primitive type (of
56bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * any size) or a reference type.  This class emits instructions appropriate to
57bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * the types they operate on. Not all operations are local on all types;
58bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * attempting to emit such an operation will fail with an unchecked exception.
593574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow *
6051004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * <h3>Math and Bit Operations</h3>
61b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Transform a single value into another related value using {@link
62a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * #op(UnaryOp,Local,Local) op(UnaryOp, Local, Local)}. Transform two values
63a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * into a third value using {@link #op(BinaryOp,Local,Local,Local) op(BinaryOp,
64a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * Local, Local, Local)}. In either overload the first {@code Local} parameter
65d826e65ef2a1af0851be0901b1afd227362a912cEvan Cheng * is where the result will be sent; the other {@code Local} parameters are the
663574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * inputs.
6751004dff923259c90591621e7151408ad94e0eb8Nuno Lopes *
68d826e65ef2a1af0851be0901b1afd227362a912cEvan Cheng * <h3>Comparisons</h3>
69b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * There are three different comparison operations each with different
70b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * constraints:
71b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <ul>
723574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow *     <li>{@link #compareLongs compareLongs()} compares two locals each
73b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         containing a {@code long} primitive. This is the only operation that
74b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         can compare longs. The result of the comparison is written to another
75b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         {@code int} local.</li>
763574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow *     <li>{@link #compareFloatingPoint compareFloatingPoint()} compares two
77b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         locals; both {@code float} primitives or both {@code double}
78b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         primitives. This is the only operation that can compare floating
79b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         point values. This comparison takes an extra parameter that sets
80b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         the desired result if either parameter is {@code NaN}. The result of
81b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         the comparison is wrtten to another {@code int} local.
82b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *     <li>{@link #compare compare()} compares two locals. The {@link
83b5ccb25bc229f096e8bc88d2233cd96af1552eeaBenjamin Kramer *         Comparison#EQ} and {@link Comparison#NE} options compare either
8499faa3b4ec6d03ac7808fe4ff3fbf3d04e375502Bill Wendling *         {@code int} primitives or references. The other options compare only
85b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         {@code int} primitives. This comparison takes a {@link Label} that
8636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines *         will be jumped to if the comparison is true. If the comparison is
8736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines *         false the next instruction in sequence will be executed.
8836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * </ul>
8936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * There's no single operation to compare longs and jump, or to compare ints and
9036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * store the result in a local. Accomplish these goals by chaining multiple
9136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * operations together.
9236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines *
9336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * <h3>Branches, Labels and Returns</h3>
94b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Basic control flow is expressed using jumps and labels. Each label must be
95b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * marked exactly once and may be jumped to any number of times. Create a label
963574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * using its constructor: {@code new Label()}, and mark it using {@link #mark
9751004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * mark(Label)}. All jumps to a label will execute instructions starting from
98b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * that label. You can jump to a label that hasn't yet been marked (jumping
99b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * forward) or to a label that has already been marked (jumping backward). Jump
100b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * unconditionally with {@link #jump jump(Label)} or conditionally based on a
1013574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * comparison using {@link #compare compare()}.
10251004dff923259c90591621e7151408ad94e0eb8Nuno Lopes *
103b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <p>Most methods should contain either a return instruction. Void methods
104b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * should use {@link #returnVoid()}; non-void methods should use {@link
105b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * #returnValue returnValue()} with a local whose return type matches the
10651004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * method's return type. Constructors are considered void methods and should
1073574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * call {@link #returnVoid()}. Methods may make multiple returns. Methods
108b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * containing no return statements must either loop infinitely or throw
109b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * unconditionally; it is not legal to end a sequence of instructions without a
110b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * jump, return or throw.
1113574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow *
11251004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * <h3>Throwing and Catching</h3>
113b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * This API uses labels to handle thrown exceptions, errors and throwables. Call
114b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * {@link #addCatchClause addCatchClause()} to register the target label and
115b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * throwable class. All statements that follow will jump to that catch clause if
11651004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * they throw a {@link Throwable} assignable to that type. Use {@link
1173574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * #removeCatchClause removeCatchClause()} to unregister the throwable class.
1180b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer *
1190b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * <p>Throw an throwable by first assigning it to a local and then calling
1200b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * {@link #throwValue throwValue()}. Control flow will jump to the nearest label
1210b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * assigned to a type assignable to the thrown type. In this context, "nearest"
1220b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * means the label requiring the fewest stack frames to be popped.
1230b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer *
1240b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * <h3>Calling methods</h3>
1250b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * A method's caller must know its return type, name, parameters, and invoke
1260b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * kind. Lookup a method on a type using {@link TypeId#getMethod
127dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines * TypeId.getMethod()}. This is more onerous than Java language invokes, which
1280b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * can infer the target method using the target object and parameters. There are
129a30b181c20710f0b3c511b7119c2d37859e0158dBenjamin Kramer * four invoke kinds:
1303574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * <ul>
1310b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer *     <li>{@link #invokeStatic invokeStatic()} is used for static methods.</li>
132b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *     <li>{@link #invokeDirect invokeDirect()} is used for private instance
133b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         methods and for constructors to call their superclass's
134b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher *         constructor.</li>
135 *     <li>{@link #invokeInterface invokeInterface()} is used to invoke a method
136 *         whose declaring type is an interface.</li>
137 *     <li>{@link #invokeVirtual invokeVirtual()} is used to invoke any other
138 *         method. The target must not be static, private, a constructor, or an
139 *         interface method.</li>
140 *     <li>{@link #invokeSuper invokeSuper()} is used to invoke the closest
141 *         superclass's virtual method. The target must not be static, private,
142 *         a constructor method, or an interface method.</li>
143 *     <li>{@link #newInstance newInstance()} is used to invoke a
144 *         constructor.</li>
145 * </ul>
146 * All invoke methods take a local for the return value. For void methods this
147 * local is unused and may be null.
148 *
149 * <h3>Field Access</h3>
150 * Read static fields using {@link #sget sget()}; write them using {@link
151 * #sput sput()}. For instance values you'll need to specify the declaring
152 * instance; use {@link #getThis getThis()} in an instance method to use {@code
153 * this}. Read instance values using {@link #iget iget()} and write them with
154 * {@link #iput iput()}.
155 *
156 * <h3>Array Access</h3>
157 * Allocate an array using {@link #newArray newArray()}. Read an array's length
158 * with {@link #arrayLength arrayLength()} and its elements with {@link #aget
159 * aget()}. Write an array's elements with {@link #aput aput()}.
160 *
161 * <h3>Types</h3>
162 * Use {@link #cast cast()} to perform either a <strong>numeric cast</strong> or
163 * a <strong>type cast</strong>. Interrogate the type of a value in a local
164 * using {@link #instanceOfType instanceOfType()}.
165 *
166 * <h3>Synchronization</h3>
167 * Acquire a monitor using {@link #monitorEnter monitorEnter()}; release it with
168 * {@link #monitorExit monitorExit()}. It is the caller's responsibility to
169 * guarantee that enter and exit calls are balanced, even in the presence of
170 * exceptions thrown.
171 *
172 * <strong>Warning:</strong> Even if a method has the {@code synchronized} flag,
173 * dex requires instructions to acquire and release monitors manually. A method
174 * declared with {@link java.lang.reflect.Modifier#SYNCHRONIZED SYNCHRONIZED}
175 * but without manual calls to {@code monitorEnter()} and {@code monitorExit()}
176 * will not be synchronized when executed.
177 */
178public final class Code {
179    private final MethodId<?, ?> method;
180    /**
181     * All allocated labels. Although the order of the labels in this list
182     * shouldn't impact behavior, it is used to determine basic block indices.
183     */
184    private final List<Label> labels = new ArrayList<Label>();
185
186    /**
187     * The label currently receiving instructions. This is null if the most
188     * recent instruction was a return or goto.
189     */
190    private Label currentLabel;
191
192    /** true once we've fixed the positions of the parameter registers */
193    private boolean localsInitialized;
194
195    private final Local<?> thisLocal;
196
197    /**
198     * The parameters on this method. If this is non-static, the first parameter
199     * is 'thisLocal' and we have to offset the user's indices by one.
200     */
201    private final List<Local<?>> parameters = new ArrayList<Local<?>>();
202    private final List<Local<?>> locals = new ArrayList<Local<?>>();
203    private SourcePosition sourcePosition = SourcePosition.NO_INFO;
204    private final List<TypeId<?>> catchTypes = new ArrayList<TypeId<?>>();
205    private final List<Label> catchLabels = new ArrayList<Label>();
206    private StdTypeList catches = StdTypeList.EMPTY;
207
208    Code(DexMaker.MethodDeclaration methodDeclaration) {
209        this.method = methodDeclaration.method;
210        if (methodDeclaration.isStatic()) {
211            thisLocal = null;
212        } else {
213            thisLocal = Local.get(this, method.declaringType);
214            parameters.add(thisLocal);
215        }
216        for (TypeId<?> parameter : method.parameters.types) {
217            parameters.add(Local.get(this, parameter));
218        }
219        this.currentLabel = new Label();
220        adopt(this.currentLabel);
221        this.currentLabel.marked = true;
222    }
223
224    /**
225     * Allocates a new local variable of type {@code type}. It is an error to
226     * allocate a local after instructions have been emitted.
227     */
228    public <T> Local<T> newLocal(TypeId<T> type) {
229        if (localsInitialized) {
230            throw new IllegalStateException("Cannot allocate locals after adding instructions");
231        }
232        Local<T> result = Local.get(this, type);
233        locals.add(result);
234        return result;
235    }
236
237    /**
238     * Returns the local for the parameter at index {@code index} and of type
239     * {@code type}.
240     */
241    public <T> Local<T> getParameter(int index, TypeId<T> type) {
242        if (thisLocal != null) {
243            index++; // adjust for the hidden 'this' parameter
244        }
245        return coerce(parameters.get(index), type);
246    }
247
248    /**
249     * Returns the local for {@code this} of type {@code type}. It is an error
250     * to call {@code getThis()} if this is a static method.
251     */
252    public <T> Local<T> getThis(TypeId<T> type) {
253        if (thisLocal == null) {
254            throw new IllegalStateException("static methods cannot access 'this'");
255        }
256        return coerce(thisLocal, type);
257    }
258
259    @SuppressWarnings("unchecked") // guarded by an equals check
260    private <T> Local<T> coerce(Local<?> local, TypeId<T> expectedType) {
261        if (!local.type.equals(expectedType)) {
262            throw new IllegalArgumentException(
263                    "requested " + expectedType + " but was " + local.type);
264        }
265        return (Local<T>) local;
266    }
267
268    /**
269     * Assigns registers to locals. From the spec:
270     *  "the N arguments to a method land in the last N registers of the
271     *   method's invocation frame, in order. Wide arguments consume two
272     *   registers. Instance methods are passed a this reference as their
273     *   first argument."
274     *
275     * In addition to assigning registers to each of the locals, this creates
276     * instructions to move parameters into their initial registers. These
277     * instructions are inserted before the code's first real instruction.
278     */
279    void initializeLocals() {
280        if (localsInitialized) {
281            throw new AssertionError();
282        }
283        localsInitialized = true;
284
285        int reg = 0;
286        for (Local<?> local : locals) {
287            reg += local.initialize(reg);
288        }
289        int firstParamReg = reg;
290        List<Insn> moveParameterInstructions = new ArrayList<Insn>();
291        for (Local<?> local : parameters) {
292            CstInteger paramConstant = CstInteger.make(reg - firstParamReg);
293            reg += local.initialize(reg);
294            moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType),
295                    sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant));
296        }
297        labels.get(0).instructions.addAll(0, moveParameterInstructions);
298    }
299
300    /**
301     * Returns the number of registers to hold the parameters. This includes the
302     * 'this' parameter if it exists.
303     */
304    int paramSize() {
305        int result = 0;
306        for (Local<?> local : parameters) {
307            result += local.size();
308        }
309        return result;
310    }
311
312    // labels
313
314    /**
315     * Assigns {@code target} to this code.
316     */
317    private void adopt(Label target) {
318        if (target.code == this) {
319            return; // already adopted
320        }
321        if (target.code != null) {
322            throw new IllegalArgumentException("Cannot adopt label; it belongs to another Code");
323        }
324        target.code = this;
325        labels.add(target);
326    }
327
328    /**
329     * Start defining instructions for the named label.
330     */
331    public void mark(Label label) {
332        adopt(label);
333        if (label.marked) {
334            throw new IllegalStateException("already marked");
335        }
336        label.marked = true;
337        if (currentLabel != null) {
338            jump(label); // blocks must end with a branch, return or throw
339        }
340        currentLabel = label;
341    }
342
343    /**
344     * Transfers flow control to the instructions at {@code target}. It is an
345     * error to jump to a label not marked on this {@code Code}.
346     */
347    public void jump(Label target) {
348        adopt(target);
349        addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
350                target);
351    }
352
353    /**
354     * Registers {@code catchClause} as a branch target for all instructions
355     * in this frame that throw a class assignable to {@code toCatch}. This
356     * includes methods invoked from this frame. Deregister the clause using
357     * {@link #removeCatchClause removeCatchClause()}. It is an error to
358     * register a catch clause without also {@link #mark marking it} in the same
359     * {@code Code} instance.
360     */
361    public void addCatchClause(TypeId<? extends Throwable> toCatch, Label catchClause) {
362        if (catchTypes.contains(toCatch)) {
363            throw new IllegalArgumentException("Already caught: " + toCatch);
364        }
365        adopt(catchClause);
366        catchTypes.add(toCatch);
367        catches = toTypeList(catchTypes);
368        catchLabels.add(catchClause);
369    }
370
371    /**
372     * Deregisters the catch clause label for {@code toCatch} and returns it.
373     */
374    public Label removeCatchClause(TypeId<? extends Throwable> toCatch) {
375        int index = catchTypes.indexOf(toCatch);
376        if (index == -1) {
377            throw new IllegalArgumentException("No catch clause: " + toCatch);
378        }
379        catchTypes.remove(index);
380        catches = toTypeList(catchTypes);
381        return catchLabels.remove(index);
382    }
383
384    /**
385     * Throws the throwable in {@code toThrow}.
386     */
387    public void throwValue(Local<? extends Throwable> toThrow) {
388        addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
389                RegisterSpecList.make(toThrow.spec()), catches));
390    }
391
392    private StdTypeList toTypeList(List<TypeId<?>> types) {
393        StdTypeList result = new StdTypeList(types.size());
394        for (int i = 0; i < types.size(); i++) {
395            result.set(i, types.get(i).ropType);
396        }
397        return result;
398    }
399
400    private void addInstruction(Insn insn) {
401        addInstruction(insn, null);
402    }
403
404    /**
405     * @param branch the branches to follow; interpretation depends on the
406     *     instruction's branchingness.
407     */
408    private void addInstruction(Insn insn, Label branch) {
409        if (currentLabel == null || !currentLabel.marked) {
410            throw new IllegalStateException("no current label");
411        }
412        currentLabel.instructions.add(insn);
413
414        switch (insn.getOpcode().getBranchingness()) {
415        case BRANCH_NONE:
416            if (branch != null) {
417                throw new IllegalArgumentException("unexpected branch: " + branch);
418            }
419            return;
420
421        case BRANCH_RETURN:
422            if (branch != null) {
423                throw new IllegalArgumentException("unexpected branch: " + branch);
424            }
425            currentLabel = null;
426            break;
427
428        case BRANCH_GOTO:
429            if (branch == null) {
430                throw new IllegalArgumentException("branch == null");
431            }
432            currentLabel.primarySuccessor = branch;
433            currentLabel = null;
434            break;
435
436        case Rop.BRANCH_IF:
437            if (branch == null) {
438                throw new IllegalArgumentException("branch == null");
439            }
440            splitCurrentLabel(branch, Collections.<Label>emptyList());
441            break;
442
443        case Rop.BRANCH_THROW:
444            if (branch != null) {
445                throw new IllegalArgumentException("unexpected branch: " + branch);
446            }
447            splitCurrentLabel(null, new ArrayList<Label>(catchLabels));
448            break;
449
450        default:
451            throw new IllegalArgumentException();
452        }
453    }
454
455    /**
456     * Closes the current label and starts a new one.
457     *
458     * @param catchLabels an immutable list of catch labels
459     */
460    private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) {
461        Label newLabel = new Label();
462        adopt(newLabel);
463        currentLabel.primarySuccessor = newLabel;
464        currentLabel.alternateSuccessor = alternateSuccessor;
465        currentLabel.catchLabels = catchLabels;
466        currentLabel = newLabel;
467        currentLabel.marked = true;
468    }
469
470    // instructions: locals
471
472    /**
473     * Copies the constant value {@code value} to {@code target}. The constant
474     * must be a primitive, String, Class, TypeId, or null.
475     */
476    public <T> void loadConstant(Local<T> target, T value) {
477        Rop rop = value == null
478                ? Rops.CONST_OBJECT_NOTHROW
479                : Rops.opConst(target.type.ropType);
480        if (rop.getBranchingness() == BRANCH_NONE) {
481            addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
482                    RegisterSpecList.EMPTY, Constants.getConstant(value)));
483        } else {
484            addInstruction(new ThrowingCstInsn(rop, sourcePosition,
485                    RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
486            moveResult(target, true);
487        }
488    }
489
490    /**
491     * Copies the value in {@code source} to {@code target}.
492     */
493    public <T> void move(Local<T> target, Local<T> source) {
494        addInstruction(new PlainInsn(Rops.opMove(source.type.ropType),
495                sourcePosition, target.spec(), source.spec()));
496    }
497
498    // instructions: unary and binary
499
500    /**
501     * Executes {@code op} and sets {@code target} to the result.
502     */
503    public <T> void op(UnaryOp op, Local<T> target, Local<T> source) {
504        addInstruction(new PlainInsn(op.rop(source.type), sourcePosition,
505                target.spec(), source.spec()));
506    }
507
508    /**
509     * Executes {@code op} and sets {@code target} to the result.
510     */
511    public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) {
512        Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
513        RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec());
514
515        if (rop.getBranchingness() == BRANCH_NONE) {
516            addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources));
517        } else {
518            addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches));
519            moveResult(target, true);
520        }
521    }
522
523    // instructions: branches
524
525    /**
526     * Compare ints or references. If the comparison is true, execution jumps to
527     * {@code trueLabel}. If it is false, execution continues to the next
528     * instruction.
529     */
530    public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) {
531        adopt(trueLabel);
532        // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make()
533        Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
534        addInstruction(new PlainInsn(rop, sourcePosition, null,
535                RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
536    }
537
538    /**
539     * Compare floats or doubles. This stores -1 in {@code target} if {@code
540     * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
541     * a > b}. This stores {@code nanValue} in {@code target} if either value
542     * is {@code NaN}.
543     */
544    public <T extends Number> void compareFloatingPoint(
545            Local<Integer> target, Local<T> a, Local<T> b, int nanValue) {
546        Rop rop;
547        if (nanValue == 1) {
548            rop = Rops.opCmpg(a.type.ropType);
549        } else if (nanValue == -1) {
550            rop = Rops.opCmpl(a.type.ropType);
551        } else {
552            throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
553        }
554        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
555                RegisterSpecList.make(a.spec(), b.spec())));
556    }
557
558    /**
559     * Compare longs. This stores -1 in {@code target} if {@code
560     * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
561     * a > b}.
562     */
563    public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) {
564        addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
565                RegisterSpecList.make(a.spec(), b.spec())));
566    }
567
568    // instructions: fields
569
570    /**
571     * Copies the value in instance field {@code fieldId} of {@code instance} to
572     * {@code target}.
573     */
574    public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) {
575        addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
576                RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
577        moveResult(target, true);
578    }
579
580    /**
581     * Copies the value in {@code source} to the instance field {@code fieldId}
582     * of {@code instance}.
583     */
584   public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
585        addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
586                RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
587    }
588
589    /**
590     * Copies the value in {@code target} to the static field {@code fieldId}.
591     */
592    public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
593        addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
594                RegisterSpecList.EMPTY, catches, fieldId.constant));
595        moveResult(target, true);
596    }
597
598    /**
599     * Copies the value in {@code source} to the static field {@code fieldId}.
600     */
601    public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
602        addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
603                RegisterSpecList.make(source.spec()), catches, fieldId.constant));
604    }
605
606    // instructions: invoke
607
608    /**
609     * Calls the constructor {@code constructor} using {@code args} and assigns
610     * the new instance to {@code target}.
611     */
612    public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
613        if (target == null) {
614            throw new IllegalArgumentException();
615        }
616        addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
617                RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
618        moveResult(target, true);
619        invokeDirect(constructor, null, target, args);
620    }
621
622    /**
623     * Calls the static method {@code method} using {@code args} and assigns the
624     * result to {@code target}.
625     *
626     * @param target the local to receive the method's return value, or {@code
627     *     null} if the return type is {@code void} or if its value not needed.
628     */
629    public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
630        invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
631    }
632
633    /**
634     * Calls the non-private instance method {@code method} of {@code instance}
635     * using {@code args} and assigns the result to {@code target}.
636     *
637     * @param method a non-private, non-static, method declared on a class. May
638     *     not be an interface method or a constructor.
639     * @param target the local to receive the method's return value, or {@code
640     *     null} if the return type is {@code void} or if its value not needed.
641     */
642    public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
643            Local<? extends D> instance, Local<?>... args) {
644        invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, instance, args);
645    }
646
647    /**
648     * Calls {@code method} of {@code instance} using {@code args} and assigns
649     * the result to {@code target}.
650     *
651     * @param method either a private method or the superclass's constructor in
652     *     a constructor's call to {@code super()}.
653     * @param target the local to receive the method's return value, or {@code
654     *     null} if the return type is {@code void} or if its value not needed.
655     */
656    public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
657            Local<? extends D> instance, Local<?>... args) {
658        invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, instance, args);
659    }
660
661    /**
662     * Calls the closest superclass's virtual method {@code method} of {@code
663     * instance} using {@code args} and assigns the result to {@code target}.
664     *
665     * @param target the local to receive the method's return value, or {@code
666     *     null} if the return type is {@code void} or if its value not needed.
667     */
668    public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
669            Local<? extends D> instance, Local<?>... args) {
670        invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, instance, args);
671    }
672
673    /**
674     * Calls the interface method {@code method} of {@code instance} using
675     * {@code args} and assigns the result to {@code target}.
676     *
677     * @param method a method declared on an interface.
678     * @param target the local to receive the method's return value, or {@code
679     *     null} if the return type is {@code void} or if its value not needed.
680     */
681    public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
682            Local<? extends D> instance, Local<?>... args) {
683        invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, instance, args);
684    }
685
686    private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
687            Local<? extends D> object, Local<?>... args) {
688        addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
689                catches, method.constant));
690        if (target != null) {
691            moveResult(target, false);
692        }
693    }
694
695    // instructions: types
696
697    /**
698     * Tests if the value in {@code source} is assignable to {@code type}. If it
699     * is, {@code target} is assigned to 1; otherwise {@code target} is assigned
700     * to 0.
701     */
702    public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) {
703        addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
704                RegisterSpecList.make(source.spec()), catches, type.constant));
705        moveResult(target, true);
706    }
707
708    /**
709     * Performs either a numeric cast or a type cast.
710     *
711     * <h3>Numeric Casts</h3>
712     * Converts a primitive to a different representation. Numeric casts may
713     * be lossy. For example, converting the double {@code 1.8d} to an integer
714     * yields {@code 1}, losing the fractional part. Converting the integer
715     * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high
716     * bytes. The following numeric casts are supported:
717     *
718     * <p><table border="1">
719     * <tr><th>From</th><th>To</th></tr>
720     * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr>
721     * <tr><td>long</td><td>int, float, double</td></tr>
722     * <tr><td>float</td><td>int, long, double</td></tr>
723     * <tr><td>double</td><td>int, long, float</td></tr>
724     * </table>
725     *
726     * <p>For some primitive conversions it will be necessary to chain multiple
727     * cast operations. For example, to go from float to short one would first
728     * cast float to int and then int to short.
729     *
730     * <p>Numeric casts never throw {@link ClassCastException}.
731     *
732     * <h3>Type Casts</h3>
733     * Checks that a reference value is assignable to the target type. If it is
734     * assignable it is copied to the target local. If it is not assignable a
735     * {@link ClassCastException} is thrown.
736     */
737    public void cast(Local<?> target, Local<?> source) {
738        if (source.getType().ropType.isReference()) {
739            addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition,
740                    RegisterSpecList.make(source.spec()), catches, target.type.constant));
741            moveResult(target, true);
742        } else {
743            addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType),
744                    sourcePosition, target.spec(), source.spec()));
745        }
746    }
747
748    private Rop getCastRop(com.android.dx.rop.type.Type sourceType,
749            com.android.dx.rop.type.Type targetType) {
750        if (sourceType.getBasicType() == BT_INT) {
751            switch (targetType.getBasicType()) {
752            case BT_SHORT:
753                return Rops.TO_SHORT;
754            case BT_CHAR:
755                return Rops.TO_CHAR;
756            case BT_BYTE:
757                return Rops.TO_BYTE;
758            }
759        }
760        return Rops.opConv(targetType, sourceType);
761    }
762
763    // instructions: arrays
764
765    /**
766     * Sets {@code target} to the length of the array in {@code array}.
767     */
768    public <T> void arrayLength(Local<Integer> target, Local<T> array) {
769        addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
770                RegisterSpecList.make(array.spec()), catches));
771        moveResult(target, true);
772    }
773
774    /**
775     * Assigns {@code target} to a newly allocated array of length {@code
776     * length}. The array's type is the same as {@code target}'s type.
777     */
778    public <T> void newArray(Local<T> target, Local<Integer> length) {
779        addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
780                RegisterSpecList.make(length.spec()), catches, target.type.constant));
781        moveResult(target, true);
782    }
783
784    /**
785     * Assigns {@code target} to the element of {@code array} at index {@code
786     * index}.
787     */
788    public void aget(Local<?> target, Local<?> array, Local<Integer> index) {
789        addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
790                RegisterSpecList.make(array.spec(), index.spec()), catches));
791        moveResult(target, true);
792    }
793
794    /**
795     * Sets the element at {@code index} in {@code array} the value in {@code
796     * source}.
797     */
798    public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
799        addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
800                RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
801    }
802
803    // instructions: return
804
805    /**
806     * Returns from a {@code void} method. After a return it is an error to
807     * define further instructions after a return without first {@link #mark
808     * marking} an existing unmarked label.
809     */
810    public void returnVoid() {
811        if (!method.returnType.equals(TypeId.VOID)) {
812            throw new IllegalArgumentException("declared " + method.returnType
813                    + " but returned void");
814        }
815        addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null,
816                RegisterSpecList.EMPTY));
817    }
818
819    /**
820     * Returns the value in {@code result} to the calling method. After a return
821     * it is an error to define further instructions after a return without
822     * first {@link #mark marking} an existing unmarked label.
823     */
824    public void returnValue(Local<?> result) {
825        if (!result.type.equals(method.returnType)) {
826            // TODO: this is probably too strict.
827            throw new IllegalArgumentException("declared " + method.returnType
828                    + " but returned " + result.type);
829        }
830        addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
831                null, RegisterSpecList.make(result.spec())));
832    }
833
834    private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
835        Rop rop = afterNonInvokeThrowingInsn
836                ? Rops.opMoveResultPseudo(target.type.ropType)
837                : Rops.opMoveResult(target.type.ropType);
838        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY));
839    }
840
841    // instructions; synchronized
842
843    /**
844     * Awaits the lock on {@code monitor}, and acquires it.
845     */
846    public void monitorEnter(Local<?> monitor) {
847        addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition,
848                RegisterSpecList.make(monitor.spec()), catches));
849    }
850
851    /**
852     * Releases the held lock on {@code monitor}.
853     */
854    public void monitorExit(Local<?> monitor) {
855        addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition,
856                RegisterSpecList.make(monitor.spec()), catches));
857    }
858
859    // produce BasicBlocks for dex
860
861    BasicBlockList toBasicBlocks() {
862        if (!localsInitialized) {
863            initializeLocals();
864        }
865
866        cleanUpLabels();
867
868        BasicBlockList result = new BasicBlockList(labels.size());
869        for (int i = 0; i < labels.size(); i++) {
870            result.set(i, labels.get(i).toBasicBlock());
871        }
872        return result;
873    }
874
875    /**
876     * Removes empty labels and assigns IDs to non-empty labels.
877     */
878    private void cleanUpLabels() {
879        int id = 0;
880        for (Iterator<Label> i = labels.iterator(); i.hasNext();) {
881            Label label = i.next();
882            if (label.isEmpty()) {
883                i.remove();
884            } else {
885                label.compact();
886                label.id = id++;
887            }
888        }
889    }
890
891    private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) {
892        int offset = (first != null) ? 1 : 0;
893        RegisterSpecList result = new RegisterSpecList(offset + rest.length);
894        if (first != null) {
895            result.set(0, first.spec());
896        }
897        for (int i = 0; i < rest.length; i++) {
898            result.set(i + offset, rest[i].spec());
899        }
900        return result;
901    }
902}
903