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