• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Code(DexMaker.MethodDeclaration methodDeclaration)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      */
newLocal(TypeId<T> type)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      */
getParameter(int index, TypeId<T> type)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      */
getThis(TypeId<T> type)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
coerce(Local<?> local, TypeId<T> expectedType)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      */
initializeLocals()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      */
paramSize()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      */
adopt(Label target)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      */
mark(Label label)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      */
jump(Label target)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      */
addCatchClause(TypeId<? extends Throwable> toCatch, Label catchClause)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      */
removeCatchClause(TypeId<? extends Throwable> toCatch)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      */
throwValue(Local<? extends Throwable> toThrow)389     public void throwValue(Local<? extends Throwable> toThrow) {
390         addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
391                 RegisterSpecList.make(toThrow.spec()), catches));
392     }
393 
toTypeList(List<TypeId<?>> types)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 
addInstruction(Insn insn)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      */
addInstruction(Insn insn, Label branch)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      */
splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels)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      */
loadConstant(Local<T> target, T value)478     public <T> void loadConstant(Local<T> target, T value) {
479         loadConstantInternal(target, value);
480     }
481 
482     /**
483      * Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)}
484      * is that the {@code value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types.
485      */
loadDeferredClassConstant(Local<Class> target, TypeId value)486     public void loadDeferredClassConstant(Local<Class> target, TypeId value) {
487         loadConstantInternal(target, value);
488     }
489 
loadConstantInternal(Local target, Object value)490     private void loadConstantInternal(Local target, Object value) {
491         Rop rop = value == null
492                   ? Rops.CONST_OBJECT_NOTHROW
493                   : Rops.opConst(target.type.ropType);
494         if (rop.getBranchingness() == BRANCH_NONE) {
495             addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
496                                             RegisterSpecList.EMPTY, Constants.getConstant(value)));
497         } else {
498             addInstruction(new ThrowingCstInsn(rop, sourcePosition,
499                                                RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
500             moveResult(target, true);
501         }
502     }
503 
504     /**
505      * Copies the value in {@code source} to {@code target}.
506      */
move(Local<T> target, Local<T> source)507     public <T> void move(Local<T> target, Local<T> source) {
508         addInstruction(new PlainInsn(Rops.opMove(source.type.ropType),
509                 sourcePosition, target.spec(), source.spec()));
510     }
511 
512     // instructions: unary and binary
513 
514     /**
515      * Executes {@code op} and sets {@code target} to the result.
516      */
op(UnaryOp op, Local<T> target, Local<T> source)517     public <T> void op(UnaryOp op, Local<T> target, Local<T> source) {
518         addInstruction(new PlainInsn(op.rop(source.type), sourcePosition,
519                 target.spec(), source.spec()));
520     }
521 
522     /**
523      * Executes {@code op} and sets {@code target} to the result. For most
524      * binary operations, the types of {@code a} and {@code b} must be the same.
525      * Shift operations (like {@link BinaryOp#SHIFT_LEFT}) require {@code b} to
526      * be an {@code int}, even when {@code a} is a {@code long}.
527      */
op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b)528     public <T1, T2> void op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b) {
529         Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
530         RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec());
531 
532         if (rop.getBranchingness() == BRANCH_NONE) {
533             addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources));
534         } else {
535             addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches));
536             moveResult(target, true);
537         }
538     }
539 
540     // instructions: branches
541 
542     /**
543      * Compare ints or references. If the comparison is true, execution jumps to
544      * {@code trueLabel}. If it is false, execution continues to the next
545      * instruction.
546      */
compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b)547     public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) {
548         adopt(trueLabel);
549         Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
550         addInstruction(new PlainInsn(rop, sourcePosition, null,
551                 RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
552     }
553 
554     /**
555      * Check if an int or reference equals to zero. If the comparison is true,
556      * execution jumps to {@code trueLabel}. If it is false, execution continues to
557      * the next instruction.
558      */
compareZ(Comparison comparison, Label trueLabel, Local<?> a)559     public <T> void compareZ(Comparison comparison, Label trueLabel, Local<?> a) {
560         adopt(trueLabel);
561         Rop rop = comparison.rop(StdTypeList.make(a.type.ropType));
562         addInstruction(new PlainInsn(rop, sourcePosition, null,
563                 RegisterSpecList.make(a.spec())), trueLabel);
564     }
565 
566     /**
567      * Compare floats or doubles. This stores -1 in {@code target} if {@code
568      * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
569      * a > b}. This stores {@code nanValue} in {@code target} if either value
570      * is {@code NaN}.
571      */
compareFloatingPoint( Local<Integer> target, Local<T> a, Local<T> b, int nanValue)572     public <T extends Number> void compareFloatingPoint(
573             Local<Integer> target, Local<T> a, Local<T> b, int nanValue) {
574         Rop rop;
575         if (nanValue == 1) {
576             rop = Rops.opCmpg(a.type.ropType);
577         } else if (nanValue == -1) {
578             rop = Rops.opCmpl(a.type.ropType);
579         } else {
580             throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
581         }
582         addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
583                 RegisterSpecList.make(a.spec(), b.spec())));
584     }
585 
586     /**
587      * Compare longs. This stores -1 in {@code target} if {@code
588      * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code
589      * a > b}.
590      */
compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b)591     public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) {
592         addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
593                 RegisterSpecList.make(a.spec(), b.spec())));
594     }
595 
596     // instructions: fields
597 
598     /**
599      * Copies the value in instance field {@code fieldId} of {@code instance} to
600      * {@code target}.
601      */
iget(FieldId<D, ? extends V> fieldId, Local<V> target, Local<D> instance)602     public <D, V> void iget(FieldId<D, ? extends V> fieldId, Local<V> target, Local<D> instance) {
603         addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
604                 RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
605         moveResult(target, true);
606     }
607 
608     /**
609      * Copies the value in {@code source} to the instance field {@code fieldId}
610      * of {@code instance}.
611      */
iput(FieldId<D, V> fieldId, Local<? extends D> instance, Local<? extends V> source)612    public <D, V> void iput(FieldId<D, V> fieldId, Local<? extends D> instance, Local<? extends V> source) {
613         addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
614                 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
615     }
616 
617     /**
618      * Copies the value in the static field {@code fieldId} to {@code target}.
619      */
sget(FieldId<?, ? extends V> fieldId, Local<V> target)620     public <V> void sget(FieldId<?, ? extends V> fieldId, Local<V> target) {
621         addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
622                 RegisterSpecList.EMPTY, catches, fieldId.constant));
623         moveResult(target, true);
624     }
625 
626     /**
627      * Copies the value in {@code source} to the static field {@code fieldId}.
628      */
sput(FieldId<?, V> fieldId, Local<? extends V> source)629     public <V> void sput(FieldId<?, V> fieldId, Local<? extends V> source) {
630         addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
631                 RegisterSpecList.make(source.spec()), catches, fieldId.constant));
632     }
633 
634     // instructions: invoke
635 
636     /**
637      * Calls the constructor {@code constructor} using {@code args} and assigns
638      * the new instance to {@code target}.
639      */
newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args)640     public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
641         if (target == null) {
642             throw new IllegalArgumentException();
643         }
644         addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
645                 RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
646         moveResult(target, true);
647         invokeDirect(constructor, null, target, args);
648     }
649 
650     /**
651      * Calls the static method {@code method} using {@code args} and assigns the
652      * result to {@code target}.
653      *
654      * @param target the local to receive the method's return value, or {@code
655      *     null} if the return type is {@code void} or if its value not needed.
656      */
invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args)657     public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
658         invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
659     }
660 
661     /**
662      * Calls the non-private instance method {@code method} of {@code instance}
663      * using {@code args} and assigns the result to {@code target}.
664      *
665      * @param method a non-private, non-static, method declared on a class. May
666      *     not be an interface method or a constructor.
667      * @param target the local to receive the method's return value, or {@code
668      *     null} if the return type is {@code void} or if its value not needed.
669      */
invokeVirtual(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)670     public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
671             Local<? extends D> instance, Local<?>... args) {
672         invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, instance, args);
673     }
674 
675     /**
676      * Calls {@code method} of {@code instance} using {@code args} and assigns
677      * the result to {@code target}.
678      *
679      * @param method either a private method or the superclass's constructor in
680      *     a constructor's call to {@code super()}.
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      */
invokeDirect(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)684     public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
685             Local<? extends D> instance, Local<?>... args) {
686         invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, instance, args);
687     }
688 
689     /**
690      * Calls the closest superclass's virtual method {@code method} of {@code
691      * instance} using {@code args} and assigns the result to {@code target}.
692      *
693      * @param target the local to receive the method's return value, or {@code
694      *     null} if the return type is {@code void} or if its value not needed.
695      */
invokeSuper(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)696     public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
697             Local<? extends D> instance, Local<?>... args) {
698         invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, instance, args);
699     }
700 
701     /**
702      * Calls the interface method {@code method} of {@code instance} using
703      * {@code args} and assigns the result to {@code target}.
704      *
705      * @param method a method declared on an interface.
706      * @param target the local to receive the method's return value, or {@code
707      *     null} if the return type is {@code void} or if its value not needed.
708      */
invokeInterface(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)709     public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
710             Local<? extends D> instance, Local<?>... args) {
711         invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, instance, args);
712     }
713 
invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, Local<? extends D> object, Local<?>... args)714     private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
715             Local<? extends D> object, Local<?>... args) {
716         addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
717                 catches, method.constant));
718         if (target != null) {
719             moveResult(target, false);
720         }
721     }
722 
723     // instructions: types
724 
725     /**
726      * Tests if the value in {@code source} is assignable to {@code type}. If it
727      * is, {@code target} is assigned to 1; otherwise {@code target} is assigned
728      * to 0.
729      */
instanceOfType(Local<?> target, Local<?> source, TypeId<?> type)730     public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) {
731         addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
732                 RegisterSpecList.make(source.spec()), catches, type.constant));
733         moveResult(target, true);
734     }
735 
736     /**
737      * Performs either a numeric cast or a type cast.
738      *
739      * <h3>Numeric Casts</h3>
740      * Converts a primitive to a different representation. Numeric casts may
741      * be lossy. For example, converting the double {@code 1.8d} to an integer
742      * yields {@code 1}, losing the fractional part. Converting the integer
743      * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high
744      * bytes. The following numeric casts are supported:
745      *
746      * <p><table border="1" summary="Supported Numeric Casts">
747      * <tr><th>From</th><th>To</th></tr>
748      * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr>
749      * <tr><td>long</td><td>int, float, double</td></tr>
750      * <tr><td>float</td><td>int, long, double</td></tr>
751      * <tr><td>double</td><td>int, long, float</td></tr>
752      * </table>
753      *
754      * <p>For some primitive conversions it will be necessary to chain multiple
755      * cast operations. For example, to go from float to short one would first
756      * cast float to int and then int to short.
757      *
758      * <p>Numeric casts never throw {@link ClassCastException}.
759      *
760      * <h3>Type Casts</h3>
761      * Checks that a reference value is assignable to the target type. If it is
762      * assignable it is copied to the target local. If it is not assignable a
763      * {@link ClassCastException} is thrown.
764      */
cast(Local<?> target, Local<?> source)765     public void cast(Local<?> target, Local<?> source) {
766         if (source.getType().ropType.isReference()) {
767             addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition,
768                     RegisterSpecList.make(source.spec()), catches, target.type.constant));
769             moveResult(target, true);
770         } else {
771             addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType),
772                     sourcePosition, target.spec(), source.spec()));
773         }
774     }
775 
getCastRop(com.android.dx.rop.type.Type sourceType, com.android.dx.rop.type.Type targetType)776     private Rop getCastRop(com.android.dx.rop.type.Type sourceType,
777             com.android.dx.rop.type.Type targetType) {
778         if (sourceType.getBasicType() == BT_INT) {
779             switch (targetType.getBasicType()) {
780             case BT_SHORT:
781                 return Rops.TO_SHORT;
782             case BT_CHAR:
783                 return Rops.TO_CHAR;
784             case BT_BYTE:
785                 return Rops.TO_BYTE;
786             }
787         }
788         return Rops.opConv(targetType, sourceType);
789     }
790 
791     // instructions: arrays
792 
793     /**
794      * Sets {@code target} to the length of the array in {@code array}.
795      */
arrayLength(Local<Integer> target, Local<T> array)796     public <T> void arrayLength(Local<Integer> target, Local<T> array) {
797         addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
798                 RegisterSpecList.make(array.spec()), catches));
799         moveResult(target, true);
800     }
801 
802     /**
803      * Assigns {@code target} to a newly allocated array of length {@code
804      * length}. The array's type is the same as {@code target}'s type.
805      */
newArray(Local<T> target, Local<Integer> length)806     public <T> void newArray(Local<T> target, Local<Integer> length) {
807         addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
808                 RegisterSpecList.make(length.spec()), catches, target.type.constant));
809         moveResult(target, true);
810     }
811 
812     /**
813      * Assigns the element at {@code index} in {@code array} to {@code target}.
814      */
aget(Local<?> target, Local<?> array, Local<Integer> index)815     public void aget(Local<?> target, Local<?> array, Local<Integer> index) {
816         addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
817                 RegisterSpecList.make(array.spec(), index.spec()), catches));
818         moveResult(target, true);
819     }
820 
821     /**
822      * Assigns {@code source} to the element at {@code index} in {@code array}.
823      */
aput(Local<?> array, Local<Integer> index, Local<?> source)824     public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
825         addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
826                 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
827     }
828 
829     // instructions: return
830 
831     /**
832      * Returns from a {@code void} method. After a return it is an error to
833      * define further instructions after a return without first {@link #mark
834      * marking} an existing unmarked label.
835      */
returnVoid()836     public void returnVoid() {
837         if (!method.returnType.equals(TypeId.VOID)) {
838             throw new IllegalArgumentException("declared " + method.returnType
839                     + " but returned void");
840         }
841         addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null,
842                 RegisterSpecList.EMPTY));
843     }
844 
845     /**
846      * Returns the value in {@code result} to the calling method. After a return
847      * it is an error to define further instructions after a return without
848      * first {@link #mark marking} an existing unmarked label.
849      */
returnValue(Local<?> result)850     public void returnValue(Local<?> result) {
851         if (!result.type.equals(method.returnType)) {
852             // TODO: this is probably too strict.
853             throw new IllegalArgumentException("declared " + method.returnType
854                     + " but returned " + result.type);
855         }
856         addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
857                 null, RegisterSpecList.make(result.spec())));
858     }
859 
moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn)860     private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
861         Rop rop = afterNonInvokeThrowingInsn
862                 ? Rops.opMoveResultPseudo(target.type.ropType)
863                 : Rops.opMoveResult(target.type.ropType);
864         addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY));
865     }
866 
867     // instructions; synchronized
868 
869     /**
870      * Awaits the lock on {@code monitor}, and acquires it.
871      */
monitorEnter(Local<?> monitor)872     public void monitorEnter(Local<?> monitor) {
873         addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition,
874                 RegisterSpecList.make(monitor.spec()), catches));
875     }
876 
877     /**
878      * Releases the held lock on {@code monitor}.
879      */
monitorExit(Local<?> monitor)880     public void monitorExit(Local<?> monitor) {
881         addInstruction(new ThrowingInsn(Rops.MONITOR_EXIT, sourcePosition,
882                 RegisterSpecList.make(monitor.spec()), catches));
883     }
884 
885     // produce BasicBlocks for dex
886 
toBasicBlocks()887     BasicBlockList toBasicBlocks() {
888         if (!localsInitialized) {
889             initializeLocals();
890         }
891 
892         cleanUpLabels();
893 
894         BasicBlockList result = new BasicBlockList(labels.size());
895         for (int i = 0; i < labels.size(); i++) {
896             result.set(i, labels.get(i).toBasicBlock());
897         }
898         return result;
899     }
900 
901     /**
902      * Removes empty labels and assigns IDs to non-empty labels.
903      */
cleanUpLabels()904     private void cleanUpLabels() {
905         int id = 0;
906         for (Iterator<Label> i = labels.iterator(); i.hasNext();) {
907             Label label = i.next();
908             if (label.isEmpty()) {
909                 i.remove();
910             } else {
911                 label.compact();
912                 label.id = id++;
913             }
914         }
915     }
916 
concatenate(Local<?> first, Local<?>[] rest)917     private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) {
918         int offset = (first != null) ? 1 : 0;
919         RegisterSpecList result = new RegisterSpecList(offset + rest.length);
920         if (first != null) {
921             result.set(0, first.spec());
922         }
923         for (int i = 0; i < rest.length; i++) {
924             result.set(i + offset, rest[i].spec());
925         }
926         return result;
927     }
928 }
929