Home | History | Annotate | Download | only in dx
      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 
     17 package com.android.dx;
     18 
     19 import com.android.dx.rop.code.BasicBlockList;
     20 import com.android.dx.rop.code.Insn;
     21 import com.android.dx.rop.code.PlainCstInsn;
     22 import com.android.dx.rop.code.PlainInsn;
     23 import com.android.dx.rop.code.RegisterSpecList;
     24 import com.android.dx.rop.code.Rop;
     25 import com.android.dx.rop.code.Rops;
     26 import com.android.dx.rop.code.SourcePosition;
     27 import com.android.dx.rop.code.ThrowingCstInsn;
     28 import com.android.dx.rop.code.ThrowingInsn;
     29 import com.android.dx.rop.cst.CstInteger;
     30 import com.android.dx.rop.type.StdTypeList;
     31 
     32 import java.util.ArrayList;
     33 import java.util.Collections;
     34 import java.util.Iterator;
     35 import java.util.List;
     36 
     37 import static com.android.dx.rop.code.Rop.BRANCH_GOTO;
     38 import static com.android.dx.rop.code.Rop.BRANCH_NONE;
     39 import static com.android.dx.rop.code.Rop.BRANCH_RETURN;
     40 import static com.android.dx.rop.type.Type.BT_BYTE;
     41 import static com.android.dx.rop.type.Type.BT_CHAR;
     42 import static com.android.dx.rop.type.Type.BT_INT;
     43 import 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  */
    180 public 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         // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make()
    538         Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
    539         addInstruction(new PlainInsn(rop, sourcePosition, null,
    540                 RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
    541     }
    542 
    543     /**
    544      * Compare floats or doubles. This stores -1 in {@code target} if {@code
    545      * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
    546      * a > b}. This stores {@code nanValue} in {@code target} if either value
    547      * is {@code NaN}.
    548      */
    549     public <T extends Number> void compareFloatingPoint(
    550             Local<Integer> target, Local<T> a, Local<T> b, int nanValue) {
    551         Rop rop;
    552         if (nanValue == 1) {
    553             rop = Rops.opCmpg(a.type.ropType);
    554         } else if (nanValue == -1) {
    555             rop = Rops.opCmpl(a.type.ropType);
    556         } else {
    557             throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
    558         }
    559         addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
    560                 RegisterSpecList.make(a.spec(), b.spec())));
    561     }
    562 
    563     /**
    564      * Compare longs. This stores -1 in {@code target} if {@code
    565      * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
    566      * a > b}.
    567      */
    568     public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) {
    569         addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
    570                 RegisterSpecList.make(a.spec(), b.spec())));
    571     }
    572 
    573     // instructions: fields
    574 
    575     /**
    576      * Copies the value in instance field {@code fieldId} of {@code instance} to
    577      * {@code target}.
    578      */
    579     public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) {
    580         addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
    581                 RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
    582         moveResult(target, true);
    583     }
    584 
    585     /**
    586      * Copies the value in {@code source} to the instance field {@code fieldId}
    587      * of {@code instance}.
    588      */
    589    public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
    590         addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
    591                 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
    592     }
    593 
    594     /**
    595      * Copies the value in the static field {@code fieldId} to {@code target}.
    596      */
    597     public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
    598         addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
    599                 RegisterSpecList.EMPTY, catches, fieldId.constant));
    600         moveResult(target, true);
    601     }
    602 
    603     /**
    604      * Copies the value in {@code source} to the static field {@code fieldId}.
    605      */
    606     public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
    607         addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
    608                 RegisterSpecList.make(source.spec()), catches, fieldId.constant));
    609     }
    610 
    611     // instructions: invoke
    612 
    613     /**
    614      * Calls the constructor {@code constructor} using {@code args} and assigns
    615      * the new instance to {@code target}.
    616      */
    617     public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
    618         if (target == null) {
    619             throw new IllegalArgumentException();
    620         }
    621         addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
    622                 RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
    623         moveResult(target, true);
    624         invokeDirect(constructor, null, target, args);
    625     }
    626 
    627     /**
    628      * Calls the static method {@code method} using {@code args} and assigns the
    629      * result to {@code target}.
    630      *
    631      * @param target the local to receive the method's return value, or {@code
    632      *     null} if the return type is {@code void} or if its value not needed.
    633      */
    634     public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
    635         invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
    636     }
    637 
    638     /**
    639      * Calls the non-private instance method {@code method} of {@code instance}
    640      * using {@code args} and assigns the result to {@code target}.
    641      *
    642      * @param method a non-private, non-static, method declared on a class. May
    643      *     not be an interface method or a constructor.
    644      * @param target the local to receive the method's return value, or {@code
    645      *     null} if the return type is {@code void} or if its value not needed.
    646      */
    647     public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
    648             Local<? extends D> instance, Local<?>... args) {
    649         invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, instance, args);
    650     }
    651 
    652     /**
    653      * Calls {@code method} of {@code instance} using {@code args} and assigns
    654      * the result to {@code target}.
    655      *
    656      * @param method either a private method or the superclass's constructor in
    657      *     a constructor's call to {@code super()}.
    658      * @param target the local to receive the method's return value, or {@code
    659      *     null} if the return type is {@code void} or if its value not needed.
    660      */
    661     public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
    662             Local<? extends D> instance, Local<?>... args) {
    663         invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, instance, args);
    664     }
    665 
    666     /**
    667      * Calls the closest superclass's virtual method {@code method} of {@code
    668      * instance} using {@code args} and assigns the result to {@code target}.
    669      *
    670      * @param target the local to receive the method's return value, or {@code
    671      *     null} if the return type is {@code void} or if its value not needed.
    672      */
    673     public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
    674             Local<? extends D> instance, Local<?>... args) {
    675         invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, instance, args);
    676     }
    677 
    678     /**
    679      * Calls the interface method {@code method} of {@code instance} using
    680      * {@code args} and assigns the result to {@code target}.
    681      *
    682      * @param method a method declared on an interface.
    683      * @param target the local to receive the method's return value, or {@code
    684      *     null} if the return type is {@code void} or if its value not needed.
    685      */
    686     public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
    687             Local<? extends D> instance, Local<?>... args) {
    688         invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, instance, args);
    689     }
    690 
    691     private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
    692             Local<? extends D> object, Local<?>... args) {
    693         addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
    694                 catches, method.constant));
    695         if (target != null) {
    696             moveResult(target, false);
    697         }
    698     }
    699 
    700     // instructions: types
    701 
    702     /**
    703      * Tests if the value in {@code source} is assignable to {@code type}. If it
    704      * is, {@code target} is assigned to 1; otherwise {@code target} is assigned
    705      * to 0.
    706      */
    707     public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) {
    708         addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
    709                 RegisterSpecList.make(source.spec()), catches, type.constant));
    710         moveResult(target, true);
    711     }
    712 
    713     /**
    714      * Performs either a numeric cast or a type cast.
    715      *
    716      * <h3>Numeric Casts</h3>
    717      * Converts a primitive to a different representation. Numeric casts may
    718      * be lossy. For example, converting the double {@code 1.8d} to an integer
    719      * yields {@code 1}, losing the fractional part. Converting the integer
    720      * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high
    721      * bytes. The following numeric casts are supported:
    722      *
    723      * <p><table border="1" summary="Supported Numeric Casts">
    724      * <tr><th>From</th><th>To</th></tr>
    725      * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr>
    726      * <tr><td>long</td><td>int, float, double</td></tr>
    727      * <tr><td>float</td><td>int, long, double</td></tr>
    728      * <tr><td>double</td><td>int, long, float</td></tr>
    729      * </table>
    730      *
    731      * <p>For some primitive conversions it will be necessary to chain multiple
    732      * cast operations. For example, to go from float to short one would first
    733      * cast float to int and then int to short.
    734      *
    735      * <p>Numeric casts never throw {@link ClassCastException}.
    736      *
    737      * <h3>Type Casts</h3>
    738      * Checks that a reference value is assignable to the target type. If it is
    739      * assignable it is copied to the target local. If it is not assignable a
    740      * {@link ClassCastException} is thrown.
    741      */
    742     public void cast(Local<?> target, Local<?> source) {
    743         if (source.getType().ropType.isReference()) {
    744             addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition,
    745                     RegisterSpecList.make(source.spec()), catches, target.type.constant));
    746             moveResult(target, true);
    747         } else {
    748             addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType),
    749                     sourcePosition, target.spec(), source.spec()));
    750         }
    751     }
    752 
    753     private Rop getCastRop(com.android.dx.rop.type.Type sourceType,
    754             com.android.dx.rop.type.Type targetType) {
    755         if (sourceType.getBasicType() == BT_INT) {
    756             switch (targetType.getBasicType()) {
    757             case BT_SHORT:
    758                 return Rops.TO_SHORT;
    759             case BT_CHAR:
    760                 return Rops.TO_CHAR;
    761             case BT_BYTE:
    762                 return Rops.TO_BYTE;
    763             }
    764         }
    765         return Rops.opConv(targetType, sourceType);
    766     }
    767 
    768     // instructions: arrays
    769 
    770     /**
    771      * Sets {@code target} to the length of the array in {@code array}.
    772      */
    773     public <T> void arrayLength(Local<Integer> target, Local<T> array) {
    774         addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
    775                 RegisterSpecList.make(array.spec()), catches));
    776         moveResult(target, true);
    777     }
    778 
    779     /**
    780      * Assigns {@code target} to a newly allocated array of length {@code
    781      * length}. The array's type is the same as {@code target}'s type.
    782      */
    783     public <T> void newArray(Local<T> target, Local<Integer> length) {
    784         addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
    785                 RegisterSpecList.make(length.spec()), catches, target.type.constant));
    786         moveResult(target, true);
    787     }
    788 
    789     /**
    790      * Assigns the element at {@code index} in {@code array} to {@code target}.
    791      */
    792     public void aget(Local<?> target, Local<?> array, Local<Integer> index) {
    793         addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
    794                 RegisterSpecList.make(array.spec(), index.spec()), catches));
    795         moveResult(target, true);
    796     }
    797 
    798     /**
    799      * Assigns {@code source} to the element at {@code index} in {@code array}.
    800      */
    801     public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
    802         addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
    803                 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
    804     }
    805 
    806     // instructions: return
    807 
    808     /**
    809      * Returns from a {@code void} method. After a return it is an error to
    810      * define further instructions after a return without first {@link #mark
    811      * marking} an existing unmarked label.
    812      */
    813     public void returnVoid() {
    814         if (!method.returnType.equals(TypeId.VOID)) {
    815             throw new IllegalArgumentException("declared " + method.returnType
    816                     + " but returned void");
    817         }
    818         addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null,
    819                 RegisterSpecList.EMPTY));
    820     }
    821 
    822     /**
    823      * Returns the value in {@code result} to the calling method. After a return
    824      * it is an error to define further instructions after a return without
    825      * first {@link #mark marking} an existing unmarked label.
    826      */
    827     public void returnValue(Local<?> result) {
    828         if (!result.type.equals(method.returnType)) {
    829             // TODO: this is probably too strict.
    830             throw new IllegalArgumentException("declared " + method.returnType
    831                     + " but returned " + result.type);
    832         }
    833         addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
    834                 null, RegisterSpecList.make(result.spec())));
    835     }
    836 
    837     private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
    838         Rop rop = afterNonInvokeThrowingInsn
    839                 ? Rops.opMoveResultPseudo(target.type.ropType)
    840                 : Rops.opMoveResult(target.type.ropType);
    841         addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY));
    842     }
    843 
    844     // instructions; synchronized
    845 
    846     /**
    847      * Awaits the lock on {@code monitor}, and acquires it.
    848      */
    849     public void monitorEnter(Local<?> monitor) {
    850         addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition,
    851                 RegisterSpecList.make(monitor.spec()), catches));
    852     }
    853 
    854     /**
    855      * Releases the held lock on {@code monitor}.
    856      */
    857     public void monitorExit(Local<?> monitor) {
    858         addInstruction(new ThrowingInsn(Rops.MONITOR_EXIT, sourcePosition,
    859                 RegisterSpecList.make(monitor.spec()), catches));
    860     }
    861 
    862     // produce BasicBlocks for dex
    863 
    864     BasicBlockList toBasicBlocks() {
    865         if (!localsInitialized) {
    866             initializeLocals();
    867         }
    868 
    869         cleanUpLabels();
    870 
    871         BasicBlockList result = new BasicBlockList(labels.size());
    872         for (int i = 0; i < labels.size(); i++) {
    873             result.set(i, labels.get(i).toBasicBlock());
    874         }
    875         return result;
    876     }
    877 
    878     /**
    879      * Removes empty labels and assigns IDs to non-empty labels.
    880      */
    881     private void cleanUpLabels() {
    882         int id = 0;
    883         for (Iterator<Label> i = labels.iterator(); i.hasNext();) {
    884             Label label = i.next();
    885             if (label.isEmpty()) {
    886                 i.remove();
    887             } else {
    888                 label.compact();
    889                 label.id = id++;
    890             }
    891         }
    892     }
    893 
    894     private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) {
    895         int offset = (first != null) ? 1 : 0;
    896         RegisterSpecList result = new RegisterSpecList(offset + rest.length);
    897         if (first != null) {
    898             result.set(0, first.spec());
    899         }
    900         for (int i = 0; i < rest.length; i++) {
    901             result.set(i + offset, rest[i].spec());
    902         }
    903         return result;
    904     }
    905 }
    906