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 
Code(DexMaker.MethodDeclaration methodDeclaration)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      */
newLocal(TypeId<T> type)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      */
getParameter(int index, TypeId<T> type)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      */
getThis(TypeId<T> type)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
coerce(Local<?> local, TypeId<T> expectedType)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      */
initializeLocals()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      */
paramSize()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      */
adopt(Label target)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      */
mark(Label label)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      */
jump(Label target)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      */
addCatchClause(TypeId<? extends Throwable> toCatch, Label catchClause)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      */
removeCatchClause(TypeId<? extends Throwable> toCatch)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      */
throwValue(Local<? extends Throwable> toThrow)387     public void throwValue(Local<? extends Throwable> toThrow) {
388         addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
389                 RegisterSpecList.make(toThrow.spec()), catches));
390     }
391 
toTypeList(List<TypeId<?>> types)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 
addInstruction(Insn insn)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      */
addInstruction(Insn insn, Label branch)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      */
splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels)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      */
loadConstant(Local<T> target, T value)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      */
move(Local<T> target, Local<T> source)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      */
op(UnaryOp op, Local<T> target, Local<T> source)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      */
op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b)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      */
compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b)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      */
compareFloatingPoint( Local<Integer> target, Local<T> a, Local<T> b, int nanValue)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      */
compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b)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      */
iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance)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      */
iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source)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      */
sget(FieldId<?, V> fieldId, Local<V> target)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      */
sput(FieldId<?, V> fieldId, Local<V> source)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      */
newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args)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      */
invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args)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      */
invokeVirtual(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)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      */
invokeDirect(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)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      */
invokeSuper(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)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      */
invokeInterface(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)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 
invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, Local<? extends D> object, Local<?>... args)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      */
instanceOfType(Local<?> target, Local<?> source, TypeId<?> type)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      */
cast(Local<?> target, Local<?> source)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 
getCastRop(com.android.dx.rop.type.Type sourceType, com.android.dx.rop.type.Type targetType)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      */
arrayLength(Local<Integer> target, Local<T> array)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      */
newArray(Local<T> target, Local<Integer> length)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      */
aget(Local<?> target, Local<?> array, Local<Integer> index)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      */
aput(Local<?> array, Local<Integer> index, Local<?> source)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      */
returnVoid()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      */
returnValue(Local<?> result)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 
moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn)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      */
monitorEnter(Local<?> monitor)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      */
monitorExit(Local<?> monitor)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 
toBasicBlocks()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      */
cleanUpLabels()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 
concatenate(Local<?> first, Local<?>[] rest)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