Code.java revision d6c77efc0b187577dd7956070adfc7c335f65698
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>Compare</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 and Labels</h3>
91 * All control flow is created with branches and labels.
92 */
93/* TDODO: document these below
94 *
95 * new Label()
96 * jump()
97 * mark()
98 * return()
99 *
100 * <h3>Try/Catch blocks</h3>
101 * addCatchClause()
102 * removeCatchClause()
103 *
104 * <h3>Invoke</h3>
105 * invokeXxx
106 * newInstance()
107 *
108 * <h3>Fields</h3>
109 * iget()
110 * iput()
111 * sget()
112 * sput()
113 *
114 * <h3>Arrays</h3>
115 * aget()
116 * aput()
117 * arrayLength()
118 * newArray()
119 *
120 * <h3>Types and Casts</h3>
121 * cast()
122 * instanceOfType()
123 */
124public final class Code {
125    private final MethodId<?, ?> method;
126    /**
127     * All allocated labels. Although the order of the labels in this list
128     * shouldn't impact behavior, it is used to determine basic block indices.
129     */
130    private final List<Label> labels = new ArrayList<Label>();
131
132    /**
133     * The label currently receiving instructions. This is null if the most
134     * recent instruction was a return or goto.
135     */
136    private Label currentLabel;
137
138    /** true once we've fixed the positions of the parameter registers */
139    private boolean localsInitialized;
140
141    private final Local<?> thisLocal;
142
143    /**
144     * The parameters on this method. If this is non-static, the first parameter
145     * is 'thisLocal' and we have to offset the user's indices by one.
146     */
147    private final List<Local<?>> parameters = new ArrayList<Local<?>>();
148    private final List<Local<?>> locals = new ArrayList<Local<?>>();
149    private SourcePosition sourcePosition = SourcePosition.NO_INFO;
150    private final List<TypeId<?>> catchTypes = new ArrayList<TypeId<?>>();
151    private final List<Label> catchLabels = new ArrayList<Label>();
152    private StdTypeList catches = StdTypeList.EMPTY;
153
154    Code(DexMaker.MethodDeclaration methodDeclaration) {
155        this.method = methodDeclaration.method;
156        if (methodDeclaration.isStatic()) {
157            thisLocal = null;
158        } else {
159            thisLocal = Local.get(this, method.declaringType);
160            parameters.add(thisLocal);
161        }
162        for (TypeId<?> parameter : method.parameters.types) {
163            parameters.add(Local.get(this, parameter));
164        }
165        this.currentLabel = new Label();
166        adopt(this.currentLabel);
167        this.currentLabel.marked = true;
168    }
169
170    public <T> Local<T> newLocal(TypeId<T> type) {
171        if (localsInitialized) {
172            throw new IllegalStateException("Cannot allocate locals after adding instructions");
173        }
174        Local<T> result = Local.get(this, type);
175        locals.add(result);
176        return result;
177    }
178
179    public <T> Local<T> getParameter(int index, TypeId<T> type) {
180        if (thisLocal != null) {
181            index++; // adjust for the hidden 'this' parameter
182        }
183        return coerce(parameters.get(index), type);
184    }
185
186    public <T> Local<T> getThis(TypeId<T> type) {
187        if (thisLocal == null) {
188            throw new IllegalStateException("static methods cannot access 'this'");
189        }
190        return coerce(thisLocal, type);
191    }
192
193    @SuppressWarnings("unchecked") // guarded by an equals check
194    private <T> Local<T> coerce(Local<?> local, TypeId<T> expectedType) {
195        if (!local.type.equals(expectedType)) {
196            throw new IllegalArgumentException(
197                    "requested " + expectedType + " but was " + local.type);
198        }
199        return (Local<T>) local;
200    }
201
202    /**
203     * Assigns registers to locals. From the spec:
204     *  "the N arguments to a method land in the last N registers of the
205     *   method's invocation frame, in order. Wide arguments consume two
206     *   registers. Instance methods are passed a this reference as their
207     *   first argument."
208     *
209     * In addition to assigning registers to each of the locals, this creates
210     * instructions to move parameters into their initial registers. These
211     * instructions are inserted before the code's first real instruction.
212     */
213    void initializeLocals() {
214        if (localsInitialized) {
215            throw new AssertionError();
216        }
217        localsInitialized = true;
218
219        int reg = 0;
220        for (Local<?> local : locals) {
221            reg += local.initialize(reg);
222        }
223        int firstParamReg = reg;
224        List<Insn> moveParameterInstructions = new ArrayList<Insn>();
225        for (Local<?> local : parameters) {
226            CstInteger paramConstant = CstInteger.make(reg - firstParamReg);
227            reg += local.initialize(reg);
228            moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType),
229                    sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant));
230        }
231        labels.get(0).instructions.addAll(0, moveParameterInstructions);
232    }
233
234    /**
235     * Returns the number of registers to hold the parameters. This includes the
236     * 'this' parameter if it exists.
237     */
238    int paramSize() {
239        int result = 0;
240        for (Local<?> local : parameters) {
241            result += local.size();
242        }
243        return result;
244    }
245
246    // labels
247
248    /**
249     * Assigns {@code target} to this code.
250     */
251    private void adopt(Label target) {
252        if (target.code == this) {
253            return; // already adopted
254        }
255        if (target.code != null) {
256            throw new IllegalArgumentException("Cannot adopt label; it belongs to another Code");
257        }
258        target.code = this;
259        labels.add(target);
260    }
261
262    /**
263     * Start defining instructions for the named label.
264     */
265    public void mark(Label label) {
266        adopt(label);
267        if (label.marked) {
268            throw new IllegalStateException("already marked");
269        }
270        label.marked = true;
271        if (currentLabel != null) {
272            jump(label); // blocks must end with a branch, return or throw
273        }
274        currentLabel = label;
275    }
276
277    public void jump(Label target) {
278        adopt(target);
279        addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
280                target);
281    }
282
283    public void addCatchClause(TypeId<?> throwable, Label catchClause) {
284        if (catchTypes.contains(throwable)) {
285            throw new IllegalArgumentException("Already caught: " + throwable);
286        }
287        adopt(catchClause);
288        catchTypes.add(throwable);
289        catches = toTypeList(catchTypes);
290        catchLabels.add(catchClause);
291    }
292
293    public Label removeCatchClause(TypeId<?> throwable) {
294        int index = catchTypes.indexOf(throwable);
295        if (index == -1) {
296            throw new IllegalArgumentException("No catch clause: " + throwable);
297        }
298        catchTypes.remove(index);
299        catches = toTypeList(catchTypes);
300        return catchLabels.remove(index);
301    }
302
303    public void throwValue(Local<?> throwable) {
304        addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
305                RegisterSpecList.make(throwable.spec()), catches));
306    }
307
308    private StdTypeList toTypeList(List<TypeId<?>> types) {
309        StdTypeList result = new StdTypeList(types.size());
310        for (int i = 0; i < types.size(); i++) {
311            result.set(i, types.get(i).ropType);
312        }
313        return result;
314    }
315
316    private void addInstruction(Insn insn) {
317        addInstruction(insn, null);
318    }
319
320    /**
321     * @param branch the branches to follow; interpretation depends on the
322     *     instruction's branchingness.
323     */
324    private void addInstruction(Insn insn, Label branch) {
325        if (currentLabel == null || !currentLabel.marked) {
326            throw new IllegalStateException("no current label");
327        }
328        currentLabel.instructions.add(insn);
329
330        switch (insn.getOpcode().getBranchingness()) {
331        case BRANCH_NONE:
332            if (branch != null) {
333                throw new IllegalArgumentException("unexpected branch: " + branch);
334            }
335            return;
336
337        case BRANCH_RETURN:
338            if (branch != null) {
339                throw new IllegalArgumentException("unexpected branch: " + branch);
340            }
341            currentLabel = null;
342            break;
343
344        case BRANCH_GOTO:
345            if (branch == null) {
346                throw new IllegalArgumentException("branch == null");
347            }
348            currentLabel.primarySuccessor = branch;
349            currentLabel = null;
350            break;
351
352        case Rop.BRANCH_IF:
353            if (branch == null) {
354                throw new IllegalArgumentException("branch == null");
355            }
356            splitCurrentLabel(branch, Collections.<Label>emptyList());
357            break;
358
359        case Rop.BRANCH_THROW:
360            if (branch != null) {
361                throw new IllegalArgumentException("unexpected branch: " + branch);
362            }
363            splitCurrentLabel(null, new ArrayList<Label>(catchLabels));
364            break;
365
366        default:
367            throw new IllegalArgumentException();
368        }
369    }
370
371    /**
372     * Closes the current label and starts a new one.
373     *
374     * @param catchLabels an immutable list of catch labels
375     */
376    private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) {
377        Label newLabel = new Label();
378        adopt(newLabel);
379        currentLabel.primarySuccessor = newLabel;
380        currentLabel.alternateSuccessor = alternateSuccessor;
381        currentLabel.catchLabels = catchLabels;
382        currentLabel = newLabel;
383        currentLabel.marked = true;
384    }
385
386    // instructions: constants
387
388    public <T> void loadConstant(Local<T> target, T value) {
389        Rop rop = value == null
390                ? Rops.CONST_OBJECT_NOTHROW
391                : Rops.opConst(target.type.ropType);
392        if (rop.getBranchingness() == BRANCH_NONE) {
393            addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
394                    RegisterSpecList.EMPTY, Constants.getConstant(value)));
395        } else {
396            addInstruction(new ThrowingCstInsn(rop, sourcePosition,
397                    RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
398            moveResult(target, true);
399        }
400    }
401
402    // instructions: unary and binary
403
404    public <T> void op(UnaryOp op, Local<T> target, Local<T> source) {
405        addInstruction(new PlainInsn(op.rop(source.type), sourcePosition,
406                target.spec(), source.spec()));
407    }
408
409    public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) {
410        Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
411        RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec());
412
413        if (rop.getBranchingness() == BRANCH_NONE) {
414            addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources));
415        } else {
416            addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches));
417            moveResult(target, true);
418        }
419    }
420
421    // instructions: branches
422
423    /**
424     * Compare ints or references. If the comparison is true, execution jumps to
425     * {@code trueLabel}. If it is false, execution continues to the next
426     * instruction.
427     */
428    public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) {
429        adopt(trueLabel);
430        // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make()
431        Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
432        addInstruction(new PlainInsn(rop, sourcePosition, null,
433                RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
434    }
435
436    /**
437     * Compare floats or doubles.
438     */
439    public <T extends Number> void compareFloatingPoint(
440            Local<Integer> target, Local<T> a, Local<T> b, int nanValue) {
441        Rop rop;
442        if (nanValue == 1) {
443            rop = Rops.opCmpg(a.type.ropType);
444        } else if (nanValue == -1) {
445            rop = Rops.opCmpl(a.type.ropType);
446        } else {
447            throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
448        }
449        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
450                RegisterSpecList.make(a.spec(), b.spec())));
451    }
452
453    /**
454     * Compare longs.
455     */
456    public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) {
457        addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
458                RegisterSpecList.make(a.spec(), b.spec())));
459    }
460
461    // instructions: fields
462
463    public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) {
464        addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
465                RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
466        moveResult(target, true);
467    }
468
469    public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
470        addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
471                RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
472    }
473
474    public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
475        addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
476                RegisterSpecList.EMPTY, catches, fieldId.constant));
477        moveResult(target, true);
478    }
479
480    public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
481        addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
482                RegisterSpecList.make(source.spec()), catches, fieldId.constant));
483    }
484
485    // instructions: invoke
486
487    public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
488        if (target == null) {
489            throw new IllegalArgumentException();
490        }
491        addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
492                RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
493        moveResult(target, true);
494        invokeDirect(constructor, null, target, args);
495    }
496
497    public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
498        invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
499    }
500
501    public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
502            Local<? extends D> object, Local<?>... args) {
503        invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args);
504    }
505
506    public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
507            Local<? extends D> object, Local<?>... args) {
508        invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args);
509    }
510
511    public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
512            Local<? extends D> object, Local<?>... args) {
513        invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args);
514    }
515
516    public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
517            Local<? extends D> object, Local<?>... args) {
518        invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args);
519    }
520
521    private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
522            Local<? extends D> object, Local<?>... args) {
523        addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
524                catches, method.constant));
525        if (target != null) {
526            moveResult(target, false);
527        }
528    }
529
530    // instructions: types
531
532    public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) {
533        addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
534                RegisterSpecList.make(source.spec()), catches, type.constant));
535        moveResult(target, true);
536    }
537
538    /**
539     * Performs either a numeric cast or a type cast.
540     *
541     * <h3>Numeric Casts</h3>
542     * Converts a primitive to a different representation. Numeric casts may
543     * be lossy. For example, converting the double {@code 1.8d} to an integer
544     * yields {@code 1}, losing the fractional part. Converting the integer
545     * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high
546     * bytes. The following numeric casts are supported:
547     *
548     * <p><table border="1">
549     * <tr><th>From</th><th>To</th></tr>
550     * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr>
551     * <tr><td>long</td><td>int, float, double</td></tr>
552     * <tr><td>float</td><td>int, long, double</td></tr>
553     * <tr><td>double</td><td>int, long, float</td></tr>
554     * </table>
555     *
556     * <p>For some primitive conversions it will be necessary to chain multiple
557     * cast operations. For example, to go from float to short one would first
558     * cast float to int and then int to short.
559     *
560     * <p>Numeric casts never throw {@link ClassCastException}.
561     *
562     * <h3>Type Casts</h3>
563     * Checks that a reference value is assignable to the target type. If it is
564     * assignable it is copied to the target local. If it is not assignable a
565     * {@link ClassCastException} is thrown.
566     */
567    public void cast(Local<?> target, Local<?> source) {
568        if (source.getType().ropType.isReference()) {
569            addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition,
570                    RegisterSpecList.make(source.spec()), catches, target.type.constant));
571            moveResult(target, true);
572        } else {
573            addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType),
574                    sourcePosition, target.spec(), source.spec()));
575        }
576    }
577
578    private Rop getCastRop(com.android.dx.rop.type.Type sourceType,
579            com.android.dx.rop.type.Type targetType) {
580        if (sourceType.getBasicType() == BT_INT) {
581            switch (targetType.getBasicType()) {
582            case BT_SHORT:
583                return Rops.TO_SHORT;
584            case BT_CHAR:
585                return Rops.TO_CHAR;
586            case BT_BYTE:
587                return Rops.TO_BYTE;
588            }
589        }
590        return Rops.opConv(targetType, sourceType);
591    }
592
593    // instructions: arrays
594
595    public <T> void arrayLength(Local<Integer> target, Local<T> array) {
596        addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
597                RegisterSpecList.make(array.spec()), catches));
598        moveResult(target, true);
599    }
600
601    public <T> void newArray(Local<T> target, Local<Integer> length) {
602        addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
603                RegisterSpecList.make(length.spec()), catches, target.type.constant));
604        moveResult(target, true);
605    }
606
607    public void aget(Local<?> target, Local<?> array, Local<Integer> index) {
608        addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
609                RegisterSpecList.make(array.spec(), index.spec()), catches));
610        moveResult(target, true);
611    }
612
613    public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
614        addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
615                RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
616    }
617
618    // instructions: return
619
620    public void returnVoid() {
621        if (!method.returnType.equals(TypeId.VOID)) {
622            throw new IllegalArgumentException("declared " + method.returnType
623                    + " but returned void");
624        }
625        addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null,
626                RegisterSpecList.EMPTY));
627    }
628
629    public void returnValue(Local<?> result) {
630        if (!result.type.equals(method.returnType)) {
631            // TODO: this is probably too strict.
632            throw new IllegalArgumentException("declared " + method.returnType
633                    + " but returned " + result.type);
634        }
635        addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
636                null, RegisterSpecList.make(result.spec())));
637    }
638
639    private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
640        Rop rop = afterNonInvokeThrowingInsn
641                ? Rops.opMoveResultPseudo(target.type.ropType)
642                : Rops.opMoveResult(target.type.ropType);
643        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY));
644    }
645
646    // produce BasicBlocks for dex
647
648    BasicBlockList toBasicBlocks() {
649        if (!localsInitialized) {
650            initializeLocals();
651        }
652
653        cleanUpLabels();
654
655        BasicBlockList result = new BasicBlockList(labels.size());
656        for (int i = 0; i < labels.size(); i++) {
657            result.set(i, labels.get(i).toBasicBlock());
658        }
659        return result;
660    }
661
662    /**
663     * Removes empty labels and assigns IDs to non-empty labels.
664     */
665    private void cleanUpLabels() {
666        int id = 0;
667        for (Iterator<Label> i = labels.iterator(); i.hasNext();) {
668            Label label = i.next();
669            if (label.isEmpty()) {
670                i.remove();
671            } else {
672                label.compact();
673                label.id = id++;
674            }
675        }
676    }
677
678    private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) {
679        int offset = (first != null) ? 1 : 0;
680        RegisterSpecList result = new RegisterSpecList(offset + rest.length);
681        if (first != null) {
682            result.set(0, first.spec());
683        }
684        for (int i = 0; i < rest.length; i++) {
685            result.set(i + offset, rest[i].spec());
686        }
687        return result;
688    }
689}
690