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