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