Code.java revision ff561a27def418f8c19a36df5fec727dfc8bb17a
10e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org/*
20e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Copyright (C) 2011 The Android Open Source Project
30e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
40e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Licensed under the Apache License, Version 2.0 (the "License");
50e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * you may not use this file except in compliance with the License.
60e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * You may obtain a copy of the License at
70e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
80e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *      http://www.apache.org/licenses/LICENSE-2.0
90e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Unless required by applicable law or agreed to in writing, software
110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * distributed under the License is distributed on an "AS IS" BASIS,
120e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * See the License for the specific language governing permissions and
140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * limitations under the License.
150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org */
160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgpackage com.google.dexmaker;
180e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
190e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.BasicBlockList;
200e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.Insn;
210e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.PlainCstInsn;
220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.PlainInsn;
230e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.RegisterSpecList;
240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.Rop;
250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.code.Rop.BRANCH_GOTO;
260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.code.Rop.BRANCH_NONE;
270e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.code.Rop.BRANCH_RETURN;
280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.Rops;
290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.SourcePosition;
300e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.ThrowingCstInsn;
310e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.code.ThrowingInsn;
320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.cst.CstInteger;
330e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport com.android.dx.rop.type.StdTypeList;
340e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.type.Type.BT_BYTE;
350e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.type.Type.BT_CHAR;
360e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.type.Type.BT_INT;
370e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport static com.android.dx.rop.type.Type.BT_SHORT;
380e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport java.util.ArrayList;
390e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport java.util.Collections;
400e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgimport java.util.Iterator;
41cf81adffe15fa8ea0f333432e41f6d504148f18abuildbot@webrtc.orgimport java.util.List;
420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org/**
440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Builds a sequence of instructions.
450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Locals</h3>
470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * All data manipulation takes place in local variables. Each parameter gets its
480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * own local by default; access these using {@link #getParameter}. Non-static
490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * methods and constructors also have a {@code this} parameter; it's available
500e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * as {@link #getThis}. Allocate a new local variable using {@link #newLocal},
510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * and assign a default value to it with {@link #loadConstant}. Every local
520e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * variable has a fixed type. This is either a primitive type (of any size) or a
530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * reference type. This class emits instructions appropriate to the types they
540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * operate on. Not all operations are local on all types; attempting to emit
550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * such an operation will fail with an unchecked exception.
560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Math and Bit Operations</h3>
580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Transform a single value into another related value using {@link
590e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #op(UnaryOp,Local,Local)}. Transform two values into a third value using
600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * {@link #op(BinaryOp,Local,Local,Local)}. In either overload the first {@code
610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Local} parameter is where the result will be sent; the other {@code Local}
620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * parameters are the inputs.
630e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
640e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Comparisons</h3>
650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * There are three different comparison operations each with different
660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * constraints:
670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <ul>
680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #compareLongs(Local,Local,Local)} compares two locals each
690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         containing a {@code long} primitive. This is the only operation that
700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         can compare longs. The result of the comparison is written to another
710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         {@code int} local.</li>
720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #compareFloatingPoint(Local,Local,Local,int)} compares two
730e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         locals; both {@code float} primitives or both {@code double}
740e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         primitives. This is the only operation that can compare floating
750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         point values. This comparison takes an extra parameter that sets
760e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         the desired result if either parameter is {@code NaN}. The result of
770e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         the comparison is wrtten to another {@code int} local.
780e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #compare(Comparison,Label,Local,Local)} compares two locals.
790e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         The {@link Comparison#EQ} and {@link Comparison#NE} options compare
800e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         either {@code int} primitives or references. The other options
810e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         compare only {@code int} primitives. This comparison takes a {@link
820e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         Label} that will be jumped to if the comparison is true. If the
830e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         comparison is false the next instruction in sequence will be
840e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         executed.
850e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * </ul>
860e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * There's no single operation to compare longs and jump, or to compare ints and
870e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * store the result in a local. Accomplish these goals by chaining multiple
880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * operations together.
890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Branches, Labels and Returns</h3>
910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Basic control flow is expressed using jumps and labels. Each label must be
920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * marked exactly once and may be jumped to any number of times. Create a label
930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * using its constructor: {@code new Label()}, and mark it using {@link #mark}.
940e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * All jumps to a label will execute instructions starting from that label. You
950e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * can jump to a label that hasn't yet been marked (jumping forward) or to a
96249b3894e5fe5cd977313f0df6eb222d5010b561kjellander@webrtc.org * label that has already been marked (jumping backward). Jump unconditionally
970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * with {@link #jump} or conditionally based on a comparison using {@link
980e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #compare(Comparison,Label,Local,Local)}.
990e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1000e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <p>Most methods should contain either a return instruction. Void methods
1010e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * should use {@link #returnVoid}; non-void methods should use {@link
1020e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #returnValue} with a local whose return type matches the method's return
1030e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * type. Constructors are considered void methods and should call {@link
1040e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #returnVoid()}. Methods may make multiple returns. Methods containing no
1050e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * return statements must either loop infinitely or throw unconditionally; it is
1060e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * not legal to end a sequence of instructions without a jump, return or throw.
1070e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1080e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Throwing and Catching</h3>
1090e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * This API uses labels to handle thrown exceptions, errors and throwables. Call
1100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * {@link #addCatchClause} to register the target label and throwable class. All
1110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * statements that follow will jump to that catch clause if they throw a {@link
1120e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Throwable} assignable to that type. Use {@link #removeCatchClause} to
1130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * unregister the throwable class.
1140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <p>Throw an throwable by first assigning it to a local and then calling
1160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * {@link #throwValue}. Control flow will jump to the nearest label assigned to
1170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * a type assignable to the thrown type. In this context, "nearest" means the
1180e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * label requiring the fewest stack frames to be popped.
1190e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1200e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Calling methods</h3>
1210e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * A method's caller must know its return type, name, parameters, and invoke
1220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * kind. Lookup a method on a type using {@link TypeId#getMethod}. This is more
1230e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * onerous than Java language invokes, which can infer the target method using
1240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * the target object and parameters. There are four invoke kinds:
1250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <ul>
1260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #invokeStatic} is used for static methods.</li>
1270e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #invokeDirect} is used for private instance methods and
1280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         constructors</li>
1290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #invokeInterface} is used to invoke a method whose declaring
1300e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         type is an interface.</li>
1310e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #invokeVirtual} is used to invoke any other method. The target
1320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         must not be static, private, a constructor method, or an interface
1330e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         method.</li>
1340e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #invokeSuper} is used to invoke the closest superclass's
1350e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         virtual method. The target must not be static, private, a constructor
1360e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *         method, or an interface method.</li>
1370e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     <li>{@link #newInstance} is used to invoke a constructor.</li>
1380e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1390e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * </ul>
1400e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * All invoke methods take a local for the return value. For void methods this
1410e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * local is unused and may be null.
1420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Field Access</h3>
1440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Read static fields using {@link #sget}; write them using {@link #sput}. For
1450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * instance values you'll need to specify the declaring instance; use {@link
1460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #getThis} in an instance method to use {@code this}. Read instance values
1470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * using {@link #iget} and write them with {@link #iput}.
1480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Array Access</h3>
1502a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org * Allocate an array using {@link #newArray}. Read an array's length with {@link
1510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * #arrayLength} and its elements with {@link #aget}. Write an array's elements
1522a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org * with {@link #aput}.
1530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
1540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <h3>Types</h3>
1550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Use {@link #cast} to perform either a <strong>numeric cast</strong> or a
1560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * <strong>type cast</strong>. Interrogate the type of a value in a local using
1570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * {@link #instanceOfType}.
1580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org */
1590e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgpublic final class Code {
1600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final MethodId<?, ?> method;
1610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    /**
1620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * All allocated labels. Although the order of the labels in this list
1630e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * shouldn't impact behavior, it is used to determine basic block indices.
1640e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     */
1650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final List<Label> labels = new ArrayList<Label>();
1660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    /**
1680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * The label currently receiving instructions. This is null if the most
1690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * recent instruction was a return or goto.
1700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     */
1710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private Label currentLabel;
1720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1730e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    /** true once we've fixed the positions of the parameter registers */
1740e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private boolean localsInitialized;
1750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1760e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final Local<?> thisLocal;
1770e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1780e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    /**
1790e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * The parameters on this method. If this is non-static, the first parameter
1800e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     * is 'thisLocal' and we have to offset the user's indices by one.
1810e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org     */
1820e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final List<Local<?>> parameters = new ArrayList<Local<?>>();
1830e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final List<Local<?>> locals = new ArrayList<Local<?>>();
1840e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private SourcePosition sourcePosition = SourcePosition.NO_INFO;
1850e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final List<TypeId<?>> catchTypes = new ArrayList<TypeId<?>>();
1860e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private final List<Label> catchLabels = new ArrayList<Label>();
1870e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    private StdTypeList catches = StdTypeList.EMPTY;
1880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    Code(DexMaker.MethodDeclaration methodDeclaration) {
1900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        this.method = methodDeclaration.method;
1910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        if (methodDeclaration.isStatic()) {
1920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org            thisLocal = null;
1930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        } else {
1940e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org            thisLocal = Local.get(this, method.declaringType);
1952a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org            parameters.add(thisLocal);
1962a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org        }
1970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        for (TypeId<?> parameter : method.parameters.types) {
1980e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org            parameters.add(Local.get(this, parameter));
1990e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        }
2000e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        this.currentLabel = new Label();
2010e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        adopt(this.currentLabel);
2020e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        this.currentLabel.marked = true;
2030e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    }
2040e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2050e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    public <T> Local<T> newLocal(TypeId<T> type) {
2060e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        if (localsInitialized) {
2070e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org            throw new IllegalStateException("Cannot allocate locals after adding instructions");
2080e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        }
2090e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        Local<T> result = Local.get(this, type);
210249b3894e5fe5cd977313f0df6eb222d5010b561kjellander@webrtc.org        locals.add(result);
211249b3894e5fe5cd977313f0df6eb222d5010b561kjellander@webrtc.org        return result;
212249b3894e5fe5cd977313f0df6eb222d5010b561kjellander@webrtc.org    }
213249b3894e5fe5cd977313f0df6eb222d5010b561kjellander@webrtc.org
2140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    public <T> Local<T> getParameter(int index, TypeId<T> type) {
2150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        if (thisLocal != null) {
2160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org            index++; // adjust for the hidden 'this' parameter
2170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        }
218        return coerce(parameters.get(index), type);
219    }
220
221    public <T> Local<T> getThis(TypeId<T> type) {
222        if (thisLocal == null) {
223            throw new IllegalStateException("static methods cannot access 'this'");
224        }
225        return coerce(thisLocal, type);
226    }
227
228    @SuppressWarnings("unchecked") // guarded by an equals check
229    private <T> Local<T> coerce(Local<?> local, TypeId<T> expectedType) {
230        if (!local.type.equals(expectedType)) {
231            throw new IllegalArgumentException(
232                    "requested " + expectedType + " but was " + local.type);
233        }
234        return (Local<T>) local;
235    }
236
237    /**
238     * Assigns registers to locals. From the spec:
239     *  "the N arguments to a method land in the last N registers of the
240     *   method's invocation frame, in order. Wide arguments consume two
241     *   registers. Instance methods are passed a this reference as their
242     *   first argument."
243     *
244     * In addition to assigning registers to each of the locals, this creates
245     * instructions to move parameters into their initial registers. These
246     * instructions are inserted before the code's first real instruction.
247     */
248    void initializeLocals() {
249        if (localsInitialized) {
250            throw new AssertionError();
251        }
252        localsInitialized = true;
253
254        int reg = 0;
255        for (Local<?> local : locals) {
256            reg += local.initialize(reg);
257        }
258        int firstParamReg = reg;
259        List<Insn> moveParameterInstructions = new ArrayList<Insn>();
260        for (Local<?> local : parameters) {
261            CstInteger paramConstant = CstInteger.make(reg - firstParamReg);
262            reg += local.initialize(reg);
263            moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType),
264                    sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant));
265        }
266        labels.get(0).instructions.addAll(0, moveParameterInstructions);
267    }
268
269    /**
270     * Returns the number of registers to hold the parameters. This includes the
271     * 'this' parameter if it exists.
272     */
273    int paramSize() {
274        int result = 0;
275        for (Local<?> local : parameters) {
276            result += local.size();
277        }
278        return result;
279    }
280
281    // labels
282
283    /**
284     * Assigns {@code target} to this code.
285     */
286    private void adopt(Label target) {
287        if (target.code == this) {
288            return; // already adopted
289        }
290        if (target.code != null) {
291            throw new IllegalArgumentException("Cannot adopt label; it belongs to another Code");
292        }
293        target.code = this;
294        labels.add(target);
295    }
296
297    /**
298     * Start defining instructions for the named label.
299     */
300    public void mark(Label label) {
301        adopt(label);
302        if (label.marked) {
303            throw new IllegalStateException("already marked");
304        }
305        label.marked = true;
306        if (currentLabel != null) {
307            jump(label); // blocks must end with a branch, return or throw
308        }
309        currentLabel = label;
310    }
311
312    public void jump(Label target) {
313        adopt(target);
314        addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
315                target);
316    }
317
318    public void addCatchClause(TypeId<?> throwable, Label catchClause) {
319        if (catchTypes.contains(throwable)) {
320            throw new IllegalArgumentException("Already caught: " + throwable);
321        }
322        adopt(catchClause);
323        catchTypes.add(throwable);
324        catches = toTypeList(catchTypes);
325        catchLabels.add(catchClause);
326    }
327
328    public Label removeCatchClause(TypeId<?> throwable) {
329        int index = catchTypes.indexOf(throwable);
330        if (index == -1) {
331            throw new IllegalArgumentException("No catch clause: " + throwable);
332        }
333        catchTypes.remove(index);
334        catches = toTypeList(catchTypes);
335        return catchLabels.remove(index);
336    }
337
338    public void throwValue(Local<?> throwable) {
339        addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
340                RegisterSpecList.make(throwable.spec()), catches));
341    }
342
343    private StdTypeList toTypeList(List<TypeId<?>> types) {
344        StdTypeList result = new StdTypeList(types.size());
345        for (int i = 0; i < types.size(); i++) {
346            result.set(i, types.get(i).ropType);
347        }
348        return result;
349    }
350
351    private void addInstruction(Insn insn) {
352        addInstruction(insn, null);
353    }
354
355    /**
356     * @param branch the branches to follow; interpretation depends on the
357     *     instruction's branchingness.
358     */
359    private void addInstruction(Insn insn, Label branch) {
360        if (currentLabel == null || !currentLabel.marked) {
361            throw new IllegalStateException("no current label");
362        }
363        currentLabel.instructions.add(insn);
364
365        switch (insn.getOpcode().getBranchingness()) {
366        case BRANCH_NONE:
367            if (branch != null) {
368                throw new IllegalArgumentException("unexpected branch: " + branch);
369            }
370            return;
371
372        case BRANCH_RETURN:
373            if (branch != null) {
374                throw new IllegalArgumentException("unexpected branch: " + branch);
375            }
376            currentLabel = null;
377            break;
378
379        case BRANCH_GOTO:
380            if (branch == null) {
381                throw new IllegalArgumentException("branch == null");
382            }
383            currentLabel.primarySuccessor = branch;
384            currentLabel = null;
385            break;
386
387        case Rop.BRANCH_IF:
388            if (branch == null) {
389                throw new IllegalArgumentException("branch == null");
390            }
391            splitCurrentLabel(branch, Collections.<Label>emptyList());
392            break;
393
394        case Rop.BRANCH_THROW:
395            if (branch != null) {
396                throw new IllegalArgumentException("unexpected branch: " + branch);
397            }
398            splitCurrentLabel(null, new ArrayList<Label>(catchLabels));
399            break;
400
401        default:
402            throw new IllegalArgumentException();
403        }
404    }
405
406    /**
407     * Closes the current label and starts a new one.
408     *
409     * @param catchLabels an immutable list of catch labels
410     */
411    private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) {
412        Label newLabel = new Label();
413        adopt(newLabel);
414        currentLabel.primarySuccessor = newLabel;
415        currentLabel.alternateSuccessor = alternateSuccessor;
416        currentLabel.catchLabels = catchLabels;
417        currentLabel = newLabel;
418        currentLabel.marked = true;
419    }
420
421    // instructions: constants
422
423    public <T> void loadConstant(Local<T> target, T value) {
424        Rop rop = value == null
425                ? Rops.CONST_OBJECT_NOTHROW
426                : Rops.opConst(target.type.ropType);
427        if (rop.getBranchingness() == BRANCH_NONE) {
428            addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
429                    RegisterSpecList.EMPTY, Constants.getConstant(value)));
430        } else {
431            addInstruction(new ThrowingCstInsn(rop, sourcePosition,
432                    RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
433            moveResult(target, true);
434        }
435    }
436
437    // instructions: unary and binary
438
439    public <T> void op(UnaryOp op, Local<T> target, Local<T> source) {
440        addInstruction(new PlainInsn(op.rop(source.type), sourcePosition,
441                target.spec(), source.spec()));
442    }
443
444    public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) {
445        Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
446        RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec());
447
448        if (rop.getBranchingness() == BRANCH_NONE) {
449            addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources));
450        } else {
451            addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches));
452            moveResult(target, true);
453        }
454    }
455
456    // instructions: branches
457
458    /**
459     * Compare ints or references. If the comparison is true, execution jumps to
460     * {@code trueLabel}. If it is false, execution continues to the next
461     * instruction.
462     */
463    public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) {
464        adopt(trueLabel);
465        // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make()
466        Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
467        addInstruction(new PlainInsn(rop, sourcePosition, null,
468                RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
469    }
470
471    /**
472     * Compare floats or doubles.
473     */
474    public <T extends Number> void compareFloatingPoint(
475            Local<Integer> target, Local<T> a, Local<T> b, int nanValue) {
476        Rop rop;
477        if (nanValue == 1) {
478            rop = Rops.opCmpg(a.type.ropType);
479        } else if (nanValue == -1) {
480            rop = Rops.opCmpl(a.type.ropType);
481        } else {
482            throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
483        }
484        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
485                RegisterSpecList.make(a.spec(), b.spec())));
486    }
487
488    /**
489     * Compare longs.
490     */
491    public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) {
492        addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
493                RegisterSpecList.make(a.spec(), b.spec())));
494    }
495
496    // instructions: fields
497
498    public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) {
499        addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
500                RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
501        moveResult(target, true);
502    }
503
504    public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
505        addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
506                RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
507    }
508
509    public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
510        addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
511                RegisterSpecList.EMPTY, catches, fieldId.constant));
512        moveResult(target, true);
513    }
514
515    public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
516        addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
517                RegisterSpecList.make(source.spec()), catches, fieldId.constant));
518    }
519
520    // instructions: invoke
521
522    public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
523        if (target == null) {
524            throw new IllegalArgumentException();
525        }
526        addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
527                RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
528        moveResult(target, true);
529        invokeDirect(constructor, null, target, args);
530    }
531
532    public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
533        invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
534    }
535
536    public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
537            Local<? extends D> object, Local<?>... args) {
538        invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args);
539    }
540
541    public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
542            Local<? extends D> object, Local<?>... args) {
543        invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args);
544    }
545
546    public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
547            Local<? extends D> object, Local<?>... args) {
548        invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args);
549    }
550
551    public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
552            Local<? extends D> object, Local<?>... args) {
553        invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args);
554    }
555
556    private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
557            Local<? extends D> object, Local<?>... args) {
558        addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
559                catches, method.constant));
560        if (target != null) {
561            moveResult(target, false);
562        }
563    }
564
565    // instructions: types
566
567    public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) {
568        addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
569                RegisterSpecList.make(source.spec()), catches, type.constant));
570        moveResult(target, true);
571    }
572
573    /**
574     * Performs either a numeric cast or a type cast.
575     *
576     * <h3>Numeric Casts</h3>
577     * Converts a primitive to a different representation. Numeric casts may
578     * be lossy. For example, converting the double {@code 1.8d} to an integer
579     * yields {@code 1}, losing the fractional part. Converting the integer
580     * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high
581     * bytes. The following numeric casts are supported:
582     *
583     * <p><table border="1">
584     * <tr><th>From</th><th>To</th></tr>
585     * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr>
586     * <tr><td>long</td><td>int, float, double</td></tr>
587     * <tr><td>float</td><td>int, long, double</td></tr>
588     * <tr><td>double</td><td>int, long, float</td></tr>
589     * </table>
590     *
591     * <p>For some primitive conversions it will be necessary to chain multiple
592     * cast operations. For example, to go from float to short one would first
593     * cast float to int and then int to short.
594     *
595     * <p>Numeric casts never throw {@link ClassCastException}.
596     *
597     * <h3>Type Casts</h3>
598     * Checks that a reference value is assignable to the target type. If it is
599     * assignable it is copied to the target local. If it is not assignable a
600     * {@link ClassCastException} is thrown.
601     */
602    public void cast(Local<?> target, Local<?> source) {
603        if (source.getType().ropType.isReference()) {
604            addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition,
605                    RegisterSpecList.make(source.spec()), catches, target.type.constant));
606            moveResult(target, true);
607        } else {
608            addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType),
609                    sourcePosition, target.spec(), source.spec()));
610        }
611    }
612
613    private Rop getCastRop(com.android.dx.rop.type.Type sourceType,
614            com.android.dx.rop.type.Type targetType) {
615        if (sourceType.getBasicType() == BT_INT) {
616            switch (targetType.getBasicType()) {
617            case BT_SHORT:
618                return Rops.TO_SHORT;
619            case BT_CHAR:
620                return Rops.TO_CHAR;
621            case BT_BYTE:
622                return Rops.TO_BYTE;
623            }
624        }
625        return Rops.opConv(targetType, sourceType);
626    }
627
628    // instructions: arrays
629
630    public <T> void arrayLength(Local<Integer> target, Local<T> array) {
631        addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
632                RegisterSpecList.make(array.spec()), catches));
633        moveResult(target, true);
634    }
635
636    public <T> void newArray(Local<T> target, Local<Integer> length) {
637        addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
638                RegisterSpecList.make(length.spec()), catches, target.type.constant));
639        moveResult(target, true);
640    }
641
642    public void aget(Local<?> target, Local<?> array, Local<Integer> index) {
643        addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
644                RegisterSpecList.make(array.spec(), index.spec()), catches));
645        moveResult(target, true);
646    }
647
648    public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
649        addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
650                RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
651    }
652
653    // instructions: return
654
655    public void returnVoid() {
656        if (!method.returnType.equals(TypeId.VOID)) {
657            throw new IllegalArgumentException("declared " + method.returnType
658                    + " but returned void");
659        }
660        addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null,
661                RegisterSpecList.EMPTY));
662    }
663
664    public void returnValue(Local<?> result) {
665        if (!result.type.equals(method.returnType)) {
666            // TODO: this is probably too strict.
667            throw new IllegalArgumentException("declared " + method.returnType
668                    + " but returned " + result.type);
669        }
670        addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
671                null, RegisterSpecList.make(result.spec())));
672    }
673
674    private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
675        Rop rop = afterNonInvokeThrowingInsn
676                ? Rops.opMoveResultPseudo(target.type.ropType)
677                : Rops.opMoveResult(target.type.ropType);
678        addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY));
679    }
680
681    // produce BasicBlocks for dex
682
683    BasicBlockList toBasicBlocks() {
684        if (!localsInitialized) {
685            initializeLocals();
686        }
687
688        cleanUpLabels();
689
690        BasicBlockList result = new BasicBlockList(labels.size());
691        for (int i = 0; i < labels.size(); i++) {
692            result.set(i, labels.get(i).toBasicBlock());
693        }
694        return result;
695    }
696
697    /**
698     * Removes empty labels and assigns IDs to non-empty labels.
699     */
700    private void cleanUpLabels() {
701        int id = 0;
702        for (Iterator<Label> i = labels.iterator(); i.hasNext();) {
703            Label label = i.next();
704            if (label.isEmpty()) {
705                i.remove();
706            } else {
707                label.compact();
708                label.id = id++;
709            }
710        }
711    }
712
713    private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) {
714        int offset = (first != null) ? 1 : 0;
715        RegisterSpecList result = new RegisterSpecList(offset + rest.length);
716        if (first != null) {
717            result.set(0, first.spec());
718        }
719        for (int i = 0; i < rest.length; i++) {
720            result.set(i + offset, rest[i].spec());
721        }
722        return result;
723    }
724}
725