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