Code.java revision 3e7a2230ec75b59ae9b4aad292f51df2542ced7d
1b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher/* 2b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Copyright (C) 2011 The Android Open Source Project 3b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * 4b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Licensed under the Apache License, Version 2.0 (the "License"); 5b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * you may not use this file except in compliance with the License. 6b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * You may obtain a copy of the License at 7b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * 8b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * http://www.apache.org/licenses/LICENSE-2.0 9b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * 10b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Unless required by applicable law or agreed to in writing, software 11b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * distributed under the License is distributed on an "AS IS" BASIS, 12b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * See the License for the specific language governing permissions and 14b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * limitations under the License. 15674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak */ 16674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak 17b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherpackage com.google.dexmaker; 180b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth 19b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.BasicBlockList; 20b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.Insn; 21b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.PlainCstInsn; 223574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport com.android.dx.rop.code.PlainInsn; 239d434dbff3eb0501efc3457acec2401afdffef2fEli Friedmanimport com.android.dx.rop.code.RegisterSpecList; 24dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hinesimport com.android.dx.rop.code.Rop; 25b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_GOTO; 26b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_NONE; 27b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport static com.android.dx.rop.code.Rop.BRANCH_RETURN; 28b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.Rops; 29b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.SourcePosition; 30b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.code.ThrowingCstInsn; 313574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport com.android.dx.rop.code.ThrowingInsn; 3251004dff923259c90591621e7151408ad94e0eb8Nuno Lopesimport com.android.dx.rop.cst.CstInteger; 33b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport com.android.dx.rop.type.StdTypeList; 34a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_BYTE; 35a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_CHAR; 36a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_INT; 37a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport static com.android.dx.rop.type.Type.BT_SHORT; 383574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmowimport java.util.ArrayList; 39a536835230afcd334068bc6f5781a244938feaf9Nuno Lopesimport java.util.Collections; 40b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport java.util.Iterator; 41b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopherimport java.util.List; 42b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher 433574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow/** 4451004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * Builds a sequence of instructions. 45b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * 46386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * <h3>Locals</h3> 47386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * All data manipulation takes place in local variables. Each parameter gets its 483574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * own local by default; access these using {@link #getParameter 49386e918438b72620e7c00ee1197bf6940bfe6cf8Benjamin Kramer * getParameter()}. Non-static methods and constructors also have a {@code this} 50b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * parameter; it's available as {@link #getThis getThis()}. Allocate a new local 51b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * variable using {@link #newLocal newLocal()}, and assign a default value to it 52b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * with {@link #loadConstant loadConstant()}. Copy a value from one local to 533574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * another with {@link #move move()}. 5451004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * 557fa30b8e5d2774867787a4e4db17bf27358e5aacBenjamin Kramer * <p>Every local variable has a fixed type. This is either a primitive type (of 56bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * any size) or a reference type. This class emits instructions appropriate to 57bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * the types they operate on. Not all operations are local on all types; 58bd973762003329c217eec2439071e9f93af3eea5Eric Christopher * attempting to emit such an operation will fail with an unchecked exception. 593574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * 6051004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * <h3>Math and Bit Operations</h3> 61b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Transform a single value into another related value using {@link 62a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * #op(UnaryOp,Local,Local) op(UnaryOp, Local, Local)}. Transform two values 63a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * into a third value using {@link #op(BinaryOp,Local,Local,Local) op(BinaryOp, 64a56daf8a3906e478838c0d502f6a03cc2f22ef4cEvan Cheng * Local, Local, Local)}. In either overload the first {@code Local} parameter 65d826e65ef2a1af0851be0901b1afd227362a912cEvan Cheng * is where the result will be sent; the other {@code Local} parameters are the 663574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * inputs. 6751004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * 68d826e65ef2a1af0851be0901b1afd227362a912cEvan Cheng * <h3>Comparisons</h3> 69b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * There are three different comparison operations each with different 70b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * constraints: 71b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <ul> 723574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * <li>{@link #compareLongs compareLongs()} compares two locals each 73b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * containing a {@code long} primitive. This is the only operation that 74b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * can compare longs. The result of the comparison is written to another 75b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * {@code int} local.</li> 763574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * <li>{@link #compareFloatingPoint compareFloatingPoint()} compares two 77b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * locals; both {@code float} primitives or both {@code double} 78b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * primitives. This is the only operation that can compare floating 79b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * point values. This comparison takes an extra parameter that sets 80b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * the desired result if either parameter is {@code NaN}. The result of 81b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * the comparison is wrtten to another {@code int} local. 82b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <li>{@link #compare compare()} compares two locals. The {@link 83b5ccb25bc229f096e8bc88d2233cd96af1552eeaBenjamin Kramer * Comparison#EQ} and {@link Comparison#NE} options compare either 8499faa3b4ec6d03ac7808fe4ff3fbf3d04e375502Bill Wendling * {@code int} primitives or references. The other options compare only 85b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * {@code int} primitives. This comparison takes a {@link Label} that 8636b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * will be jumped to if the comparison is true. If the comparison is 8736b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * false the next instruction in sequence will be executed. 8836b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * </ul> 8936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * There's no single operation to compare longs and jump, or to compare ints and 9036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * store the result in a local. Accomplish these goals by chaining multiple 9136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * operations together. 9236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * 9336b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines * <h3>Branches, Labels and Returns</h3> 94b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * Basic control flow is expressed using jumps and labels. Each label must be 95b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * marked exactly once and may be jumped to any number of times. Create a label 963574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * using its constructor: {@code new Label()}, and mark it using {@link #mark 9751004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * mark(Label)}. All jumps to a label will execute instructions starting from 98b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * that label. You can jump to a label that hasn't yet been marked (jumping 99b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * forward) or to a label that has already been marked (jumping backward). Jump 100b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * unconditionally with {@link #jump jump(Label)} or conditionally based on a 1013574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * comparison using {@link #compare compare()}. 10251004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * 103b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <p>Most methods should contain either a return instruction. Void methods 104b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * should use {@link #returnVoid()}; non-void methods should use {@link 105b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * #returnValue returnValue()} with a local whose return type matches the 10651004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * method's return type. Constructors are considered void methods and should 1073574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * call {@link #returnVoid()}. Methods may make multiple returns. Methods 108b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * containing no return statements must either loop infinitely or throw 109b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * unconditionally; it is not legal to end a sequence of instructions without a 110b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * jump, return or throw. 1113574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * 11251004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * <h3>Throwing and Catching</h3> 113b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * This API uses labels to handle thrown exceptions, errors and throwables. Call 114b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * {@link #addCatchClause addCatchClause()} to register the target label and 115b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * throwable class. All statements that follow will jump to that catch clause if 11651004dff923259c90591621e7151408ad94e0eb8Nuno Lopes * they throw a {@link Throwable} assignable to that type. Use {@link 1173574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * #removeCatchClause removeCatchClause()} to unregister the throwable class. 1180b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * 1190b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * <p>Throw an throwable by first assigning it to a local and then calling 1200b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * {@link #throwValue throwValue()}. Control flow will jump to the nearest label 1210b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * assigned to a type assignable to the thrown type. In this context, "nearest" 1220b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * means the label requiring the fewest stack frames to be popped. 1230b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * 1240b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * <h3>Calling methods</h3> 1250b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * A method's caller must know its return type, name, parameters, and invoke 1260b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * kind. Lookup a method on a type using {@link TypeId#getMethod 127dce4a407a24b04eebc6a376f8e62b41aaa7b071fStephen Hines * TypeId.getMethod()}. This is more onerous than Java language invokes, which 1280b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * can infer the target method using the target object and parameters. There are 129a30b181c20710f0b3c511b7119c2d37859e0158dBenjamin Kramer * four invoke kinds: 1303574eca1b02600bac4e625297f4ecf745f4c4f32Micah Villmow * <ul> 1310b6cb507385c8bd10b6a51b5e45a9b99d8d94798Benjamin Kramer * <li>{@link #invokeStatic invokeStatic()} is used for static methods.</li> 132b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * <li>{@link #invokeDirect invokeDirect()} is used for private instance 133b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * methods and for constructors to call their superclass's 134b6174e3605a184df0e403d6502df1ed57cf34352Eric Christopher * 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 */ 178public 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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 387 public void throwValue(Local<? extends Throwable> toThrow) { 388 addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition, 389 RegisterSpecList.make(toThrow.spec()), catches)); 390 } 391 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 {@code target} to the static field {@code fieldId}. 591 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 {@code target} to the element of {@code array} at index {@code 786 * index}. 787 */ 788 public void aget(Local<?> target, Local<?> array, Local<Integer> index) { 789 addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition, 790 RegisterSpecList.make(array.spec(), index.spec()), catches)); 791 moveResult(target, true); 792 } 793 794 /** 795 * Sets the element at {@code index} in {@code array} the value in {@code 796 * source}. 797 */ 798 public void aput(Local<?> array, Local<Integer> index, Local<?> source) { 799 addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition, 800 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches)); 801 } 802 803 // instructions: return 804 805 /** 806 * Returns from a {@code void} method. After a return it is an error to 807 * define further instructions after a return without first {@link #mark 808 * marking} an existing unmarked label. 809 */ 810 public void returnVoid() { 811 if (!method.returnType.equals(TypeId.VOID)) { 812 throw new IllegalArgumentException("declared " + method.returnType 813 + " but returned void"); 814 } 815 addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null, 816 RegisterSpecList.EMPTY)); 817 } 818 819 /** 820 * Returns the value in {@code result} to the calling method. After a return 821 * it is an error to define further instructions after a return without 822 * first {@link #mark marking} an existing unmarked label. 823 */ 824 public void returnValue(Local<?> result) { 825 if (!result.type.equals(method.returnType)) { 826 // TODO: this is probably too strict. 827 throw new IllegalArgumentException("declared " + method.returnType 828 + " but returned " + result.type); 829 } 830 addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition, 831 null, RegisterSpecList.make(result.spec()))); 832 } 833 834 private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) { 835 Rop rop = afterNonInvokeThrowingInsn 836 ? Rops.opMoveResultPseudo(target.type.ropType) 837 : Rops.opMoveResult(target.type.ropType); 838 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY)); 839 } 840 841 // instructions; synchronized 842 843 /** 844 * Awaits the lock on {@code monitor}, and acquires it. 845 */ 846 public void monitorEnter(Local<?> monitor) { 847 addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition, 848 RegisterSpecList.make(monitor.spec()), catches)); 849 } 850 851 /** 852 * Releases the held lock on {@code monitor}. 853 */ 854 public void monitorExit(Local<?> monitor) { 855 addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition, 856 RegisterSpecList.make(monitor.spec()), catches)); 857 } 858 859 // produce BasicBlocks for dex 860 861 BasicBlockList toBasicBlocks() { 862 if (!localsInitialized) { 863 initializeLocals(); 864 } 865 866 cleanUpLabels(); 867 868 BasicBlockList result = new BasicBlockList(labels.size()); 869 for (int i = 0; i < labels.size(); i++) { 870 result.set(i, labels.get(i).toBasicBlock()); 871 } 872 return result; 873 } 874 875 /** 876 * Removes empty labels and assigns IDs to non-empty labels. 877 */ 878 private void cleanUpLabels() { 879 int id = 0; 880 for (Iterator<Label> i = labels.iterator(); i.hasNext();) { 881 Label label = i.next(); 882 if (label.isEmpty()) { 883 i.remove(); 884 } else { 885 label.compact(); 886 label.id = id++; 887 } 888 } 889 } 890 891 private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { 892 int offset = (first != null) ? 1 : 0; 893 RegisterSpecList result = new RegisterSpecList(offset + rest.length); 894 if (first != null) { 895 result.set(0, first.spec()); 896 } 897 for (int i = 0; i < rest.length; i++) { 898 result.set(i + offset, rest[i].spec()); 899 } 900 return result; 901 } 902} 903