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