1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx; 18 19 import com.android.dx.rop.code.BasicBlockList; 20 import com.android.dx.rop.code.Insn; 21 import com.android.dx.rop.code.PlainCstInsn; 22 import com.android.dx.rop.code.PlainInsn; 23 import com.android.dx.rop.code.RegisterSpecList; 24 import com.android.dx.rop.code.Rop; 25 import com.android.dx.rop.code.Rops; 26 import com.android.dx.rop.code.SourcePosition; 27 import com.android.dx.rop.code.ThrowingCstInsn; 28 import com.android.dx.rop.code.ThrowingInsn; 29 import com.android.dx.rop.cst.CstInteger; 30 import com.android.dx.rop.type.StdTypeList; 31 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.Iterator; 35 import java.util.List; 36 37 import static com.android.dx.rop.code.Rop.BRANCH_GOTO; 38 import static com.android.dx.rop.code.Rop.BRANCH_NONE; 39 import static com.android.dx.rop.code.Rop.BRANCH_RETURN; 40 import static com.android.dx.rop.type.Type.BT_BYTE; 41 import static com.android.dx.rop.type.Type.BT_CHAR; 42 import static com.android.dx.rop.type.Type.BT_INT; 43 import static com.android.dx.rop.type.Type.BT_SHORT; 44 45 /** 46 * Builds a sequence of instructions. 47 * 48 * <h3>Locals</h3> 49 * All data manipulation takes place in local variables. Each parameter gets its 50 * own local by default; access these using {@link #getParameter 51 * getParameter()}. Non-static methods and constructors also have a {@code this} 52 * parameter; it's available as {@link #getThis getThis()}. Allocate a new local 53 * variable using {@link #newLocal newLocal()}, and assign a default value to it 54 * with {@link #loadConstant loadConstant()}. Copy a value from one local to 55 * another with {@link #move move()}. 56 * 57 * <p>Every local variable has a fixed type. This is either a primitive type (of 58 * any size) or a reference type. This class emits instructions appropriate to 59 * the types they operate on. Not all operations are local on all types; 60 * attempting to emit such an operation will fail with an unchecked exception. 61 * 62 * <h3>Math and Bit Operations</h3> 63 * Transform a single value into another related value using {@link 64 * #op(UnaryOp,Local,Local) op(UnaryOp, Local, Local)}. Transform two values 65 * into a third value using {@link #op(BinaryOp,Local,Local,Local) op(BinaryOp, 66 * Local, Local, Local)}. In either overload the first {@code Local} parameter 67 * is where the result will be sent; the other {@code Local} parameters are the 68 * inputs. 69 * 70 * <h3>Comparisons</h3> 71 * There are three different comparison operations each with different 72 * constraints: 73 * <ul> 74 * <li>{@link #compareLongs compareLongs()} compares two locals each 75 * containing a {@code long} primitive. This is the only operation that 76 * can compare longs. The result of the comparison is written to another 77 * {@code int} local.</li> 78 * <li>{@link #compareFloatingPoint compareFloatingPoint()} compares two 79 * locals; both {@code float} primitives or both {@code double} 80 * primitives. This is the only operation that can compare floating 81 * point values. This comparison takes an extra parameter that sets 82 * the desired result if either parameter is {@code NaN}. The result of 83 * the comparison is wrtten to another {@code int} local. 84 * <li>{@link #compare compare()} compares two locals. The {@link 85 * Comparison#EQ} and {@link Comparison#NE} options compare either 86 * {@code int} primitives or references. The other options compare only 87 * {@code int} primitives. This comparison takes a {@link Label} that 88 * will be jumped to if the comparison is true. If the comparison is 89 * false the next instruction in sequence will be executed. 90 * </ul> 91 * There's no single operation to compare longs and jump, or to compare ints and 92 * store the result in a local. Accomplish these goals by chaining multiple 93 * operations together. 94 * 95 * <h3>Branches, Labels and Returns</h3> 96 * Basic control flow is expressed using jumps and labels. Each label must be 97 * marked exactly once and may be jumped to any number of times. Create a label 98 * using its constructor: {@code new Label()}, and mark it using {@link #mark 99 * mark(Label)}. All jumps to a label will execute instructions starting from 100 * that label. You can jump to a label that hasn't yet been marked (jumping 101 * forward) or to a label that has already been marked (jumping backward). Jump 102 * unconditionally with {@link #jump jump(Label)} or conditionally based on a 103 * comparison using {@link #compare compare()}. 104 * 105 * <p>Most methods should contain a return instruction. Void methods 106 * should use {@link #returnVoid()}; non-void methods should use {@link 107 * #returnValue returnValue()} with a local whose return type matches the 108 * method's return type. Constructors are considered void methods and should 109 * call {@link #returnVoid()}. Methods may make multiple returns. Methods 110 * containing no return statements must either loop infinitely or throw 111 * unconditionally; it is not legal to end a sequence of instructions without a 112 * jump, return or throw. 113 * 114 * <h3>Throwing and Catching</h3> 115 * This API uses labels to handle thrown exceptions, errors and throwables. Call 116 * {@link #addCatchClause addCatchClause()} to register the target label and 117 * throwable class. All statements that follow will jump to that catch clause if 118 * they throw a {@link Throwable} assignable to that type. Use {@link 119 * #removeCatchClause removeCatchClause()} to unregister the throwable class. 120 * 121 * <p>Throw an throwable by first assigning it to a local and then calling 122 * {@link #throwValue throwValue()}. Control flow will jump to the nearest label 123 * assigned to a type assignable to the thrown type. In this context, "nearest" 124 * means the label requiring the fewest stack frames to be popped. 125 * 126 * <h3>Calling methods</h3> 127 * A method's caller must know its return type, name, parameters, and invoke 128 * kind. Lookup a method on a type using {@link TypeId#getMethod 129 * TypeId.getMethod()}. This is more onerous than Java language invokes, which 130 * can infer the target method using the target object and parameters. There are 131 * four invoke kinds: 132 * <ul> 133 * <li>{@link #invokeStatic invokeStatic()} is used for static methods.</li> 134 * <li>{@link #invokeDirect invokeDirect()} is used for private instance 135 * methods and for constructors to call their superclass's 136 * constructor.</li> 137 * <li>{@link #invokeInterface invokeInterface()} is used to invoke a method 138 * whose declaring type is an interface.</li> 139 * <li>{@link #invokeVirtual invokeVirtual()} is used to invoke any other 140 * method. The target must not be static, private, a constructor, or an 141 * interface method.</li> 142 * <li>{@link #invokeSuper invokeSuper()} is used to invoke the closest 143 * superclass's virtual method. The target must not be static, private, 144 * a constructor method, or an interface method.</li> 145 * <li>{@link #newInstance newInstance()} is used to invoke a 146 * constructor.</li> 147 * </ul> 148 * All invoke methods take a local for the return value. For void methods this 149 * local is unused and may be null. 150 * 151 * <h3>Field Access</h3> 152 * Read static fields using {@link #sget sget()}; write them using {@link 153 * #sput sput()}. For instance values you'll need to specify the declaring 154 * instance; use {@link #getThis getThis()} in an instance method to use {@code 155 * this}. Read instance values using {@link #iget iget()} and write them with 156 * {@link #iput iput()}. 157 * 158 * <h3>Array Access</h3> 159 * Allocate an array using {@link #newArray newArray()}. Read an array's length 160 * with {@link #arrayLength arrayLength()} and its elements with {@link #aget 161 * aget()}. Write an array's elements with {@link #aput aput()}. 162 * 163 * <h3>Types</h3> 164 * Use {@link #cast cast()} to perform either a <strong>numeric cast</strong> or 165 * a <strong>type cast</strong>. Interrogate the type of a value in a local 166 * using {@link #instanceOfType instanceOfType()}. 167 * 168 * <h3>Synchronization</h3> 169 * Acquire a monitor using {@link #monitorEnter monitorEnter()}; release it with 170 * {@link #monitorExit monitorExit()}. It is the caller's responsibility to 171 * guarantee that enter and exit calls are balanced, even in the presence of 172 * exceptions thrown. 173 * 174 * <strong>Warning:</strong> Even if a method has the {@code synchronized} flag, 175 * dex requires instructions to acquire and release monitors manually. A method 176 * declared with {@link java.lang.reflect.Modifier#SYNCHRONIZED SYNCHRONIZED} 177 * but without manual calls to {@code monitorEnter()} and {@code monitorExit()} 178 * will not be synchronized when executed. 179 */ 180 public final class Code { 181 private final MethodId<?, ?> method; 182 /** 183 * All allocated labels. Although the order of the labels in this list 184 * shouldn't impact behavior, it is used to determine basic block indices. 185 */ 186 private final List<Label> labels = new ArrayList<Label>(); 187 188 /** 189 * The label currently receiving instructions. This is null if the most 190 * recent instruction was a return or goto. 191 */ 192 private Label currentLabel; 193 194 /** true once we've fixed the positions of the parameter registers */ 195 private boolean localsInitialized; 196 197 private final Local<?> thisLocal; 198 199 /** 200 * The parameters on this method. If this is non-static, the first parameter 201 * is 'thisLocal' and we have to offset the user's indices by one. 202 */ 203 private final List<Local<?>> parameters = new ArrayList<Local<?>>(); 204 private final List<Local<?>> locals = new ArrayList<Local<?>>(); 205 private SourcePosition sourcePosition = SourcePosition.NO_INFO; 206 private final List<TypeId<?>> catchTypes = new ArrayList<TypeId<?>>(); 207 private final List<Label> catchLabels = new ArrayList<Label>(); 208 private StdTypeList catches = StdTypeList.EMPTY; 209 Code(DexMaker.MethodDeclaration methodDeclaration)210 Code(DexMaker.MethodDeclaration methodDeclaration) { 211 this.method = methodDeclaration.method; 212 if (methodDeclaration.isStatic()) { 213 thisLocal = null; 214 } else { 215 thisLocal = Local.get(this, method.declaringType); 216 parameters.add(thisLocal); 217 } 218 for (TypeId<?> parameter : method.parameters.types) { 219 parameters.add(Local.get(this, parameter)); 220 } 221 this.currentLabel = new Label(); 222 adopt(this.currentLabel); 223 this.currentLabel.marked = true; 224 } 225 226 /** 227 * Allocates a new local variable of type {@code type}. It is an error to 228 * allocate a local after instructions have been emitted. 229 */ newLocal(TypeId<T> type)230 public <T> Local<T> newLocal(TypeId<T> type) { 231 if (localsInitialized) { 232 throw new IllegalStateException("Cannot allocate locals after adding instructions"); 233 } 234 Local<T> result = Local.get(this, type); 235 locals.add(result); 236 return result; 237 } 238 239 /** 240 * Returns the local for the parameter at index {@code index} and of type 241 * {@code type}. 242 */ getParameter(int index, TypeId<T> type)243 public <T> Local<T> getParameter(int index, TypeId<T> type) { 244 if (thisLocal != null) { 245 index++; // adjust for the hidden 'this' parameter 246 } 247 return coerce(parameters.get(index), type); 248 } 249 250 /** 251 * Returns the local for {@code this} of type {@code type}. It is an error 252 * to call {@code getThis()} if this is a static method. 253 */ getThis(TypeId<T> type)254 public <T> Local<T> getThis(TypeId<T> type) { 255 if (thisLocal == null) { 256 throw new IllegalStateException("static methods cannot access 'this'"); 257 } 258 return coerce(thisLocal, type); 259 } 260 261 @SuppressWarnings("unchecked") // guarded by an equals check coerce(Local<?> local, TypeId<T> expectedType)262 private <T> Local<T> coerce(Local<?> local, TypeId<T> expectedType) { 263 if (!local.type.equals(expectedType)) { 264 throw new IllegalArgumentException( 265 "requested " + expectedType + " but was " + local.type); 266 } 267 return (Local<T>) local; 268 } 269 270 /** 271 * Assigns registers to locals. From the spec: 272 * "the N arguments to a method land in the last N registers of the 273 * method's invocation frame, in order. Wide arguments consume two 274 * registers. Instance methods are passed a this reference as their 275 * first argument." 276 * 277 * In addition to assigning registers to each of the locals, this creates 278 * instructions to move parameters into their initial registers. These 279 * instructions are inserted before the code's first real instruction. 280 */ initializeLocals()281 void initializeLocals() { 282 if (localsInitialized) { 283 throw new AssertionError(); 284 } 285 localsInitialized = true; 286 287 int reg = 0; 288 for (Local<?> local : locals) { 289 reg += local.initialize(reg); 290 } 291 int firstParamReg = reg; 292 List<Insn> moveParameterInstructions = new ArrayList<Insn>(); 293 for (Local<?> local : parameters) { 294 CstInteger paramConstant = CstInteger.make(reg - firstParamReg); 295 reg += local.initialize(reg); 296 moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType), 297 sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant)); 298 } 299 labels.get(0).instructions.addAll(0, moveParameterInstructions); 300 } 301 302 /** 303 * Returns the number of registers to hold the parameters. This includes the 304 * 'this' parameter if it exists. 305 */ paramSize()306 int paramSize() { 307 int result = 0; 308 for (Local<?> local : parameters) { 309 result += local.size(); 310 } 311 return result; 312 } 313 314 // labels 315 316 /** 317 * Assigns {@code target} to this code. 318 */ adopt(Label target)319 private void adopt(Label target) { 320 if (target.code == this) { 321 return; // already adopted 322 } 323 if (target.code != null) { 324 throw new IllegalArgumentException("Cannot adopt label; it belongs to another Code"); 325 } 326 target.code = this; 327 labels.add(target); 328 } 329 330 /** 331 * Start defining instructions for the named label. 332 */ mark(Label label)333 public void mark(Label label) { 334 adopt(label); 335 if (label.marked) { 336 throw new IllegalStateException("already marked"); 337 } 338 label.marked = true; 339 if (currentLabel != null) { 340 jump(label); // blocks must end with a branch, return or throw 341 } 342 currentLabel = label; 343 } 344 345 /** 346 * Transfers flow control to the instructions at {@code target}. It is an 347 * error to jump to a label not marked on this {@code Code}. 348 */ jump(Label target)349 public void jump(Label target) { 350 adopt(target); 351 addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY), 352 target); 353 } 354 355 /** 356 * Registers {@code catchClause} as a branch target for all instructions 357 * in this frame that throw a class assignable to {@code toCatch}. This 358 * includes methods invoked from this frame. Deregister the clause using 359 * {@link #removeCatchClause removeCatchClause()}. It is an error to 360 * register a catch clause without also {@link #mark marking it} in the same 361 * {@code Code} instance. 362 */ addCatchClause(TypeId<? extends Throwable> toCatch, Label catchClause)363 public void addCatchClause(TypeId<? extends Throwable> toCatch, Label catchClause) { 364 if (catchTypes.contains(toCatch)) { 365 throw new IllegalArgumentException("Already caught: " + toCatch); 366 } 367 adopt(catchClause); 368 catchTypes.add(toCatch); 369 catches = toTypeList(catchTypes); 370 catchLabels.add(catchClause); 371 } 372 373 /** 374 * Deregisters the catch clause label for {@code toCatch} and returns it. 375 */ removeCatchClause(TypeId<? extends Throwable> toCatch)376 public Label removeCatchClause(TypeId<? extends Throwable> toCatch) { 377 int index = catchTypes.indexOf(toCatch); 378 if (index == -1) { 379 throw new IllegalArgumentException("No catch clause: " + toCatch); 380 } 381 catchTypes.remove(index); 382 catches = toTypeList(catchTypes); 383 return catchLabels.remove(index); 384 } 385 386 /** 387 * Throws the throwable in {@code toThrow}. 388 */ throwValue(Local<? extends Throwable> toThrow)389 public void throwValue(Local<? extends Throwable> toThrow) { 390 addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition, 391 RegisterSpecList.make(toThrow.spec()), catches)); 392 } 393 toTypeList(List<TypeId<?>> types)394 private StdTypeList toTypeList(List<TypeId<?>> types) { 395 StdTypeList result = new StdTypeList(types.size()); 396 for (int i = 0; i < types.size(); i++) { 397 result.set(i, types.get(i).ropType); 398 } 399 return result; 400 } 401 addInstruction(Insn insn)402 private void addInstruction(Insn insn) { 403 addInstruction(insn, null); 404 } 405 406 /** 407 * @param branch the branches to follow; interpretation depends on the 408 * instruction's branchingness. 409 */ addInstruction(Insn insn, Label branch)410 private void addInstruction(Insn insn, Label branch) { 411 if (currentLabel == null || !currentLabel.marked) { 412 throw new IllegalStateException("no current label"); 413 } 414 currentLabel.instructions.add(insn); 415 416 switch (insn.getOpcode().getBranchingness()) { 417 case BRANCH_NONE: 418 if (branch != null) { 419 throw new IllegalArgumentException("unexpected branch: " + branch); 420 } 421 return; 422 423 case BRANCH_RETURN: 424 if (branch != null) { 425 throw new IllegalArgumentException("unexpected branch: " + branch); 426 } 427 currentLabel = null; 428 break; 429 430 case BRANCH_GOTO: 431 if (branch == null) { 432 throw new IllegalArgumentException("branch == null"); 433 } 434 currentLabel.primarySuccessor = branch; 435 currentLabel = null; 436 break; 437 438 case Rop.BRANCH_IF: 439 if (branch == null) { 440 throw new IllegalArgumentException("branch == null"); 441 } 442 splitCurrentLabel(branch, Collections.<Label>emptyList()); 443 break; 444 445 case Rop.BRANCH_THROW: 446 if (branch != null) { 447 throw new IllegalArgumentException("unexpected branch: " + branch); 448 } 449 splitCurrentLabel(null, new ArrayList<Label>(catchLabels)); 450 break; 451 452 default: 453 throw new IllegalArgumentException(); 454 } 455 } 456 457 /** 458 * Closes the current label and starts a new one. 459 * 460 * @param catchLabels an immutable list of catch labels 461 */ splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels)462 private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) { 463 Label newLabel = new Label(); 464 adopt(newLabel); 465 currentLabel.primarySuccessor = newLabel; 466 currentLabel.alternateSuccessor = alternateSuccessor; 467 currentLabel.catchLabels = catchLabels; 468 currentLabel = newLabel; 469 currentLabel.marked = true; 470 } 471 472 // instructions: locals 473 474 /** 475 * Copies the constant value {@code value} to {@code target}. The constant 476 * must be a primitive, String, Class, TypeId, or null. 477 */ loadConstant(Local<T> target, T value)478 public <T> void loadConstant(Local<T> target, T value) { 479 loadConstantInternal(target, value); 480 } 481 482 /** 483 * Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)} 484 * is that the {@code value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types. 485 */ loadDeferredClassConstant(Local<Class> target, TypeId value)486 public void loadDeferredClassConstant(Local<Class> target, TypeId value) { 487 loadConstantInternal(target, value); 488 } 489 loadConstantInternal(Local target, Object value)490 private void loadConstantInternal(Local target, Object value) { 491 Rop rop = value == null 492 ? Rops.CONST_OBJECT_NOTHROW 493 : Rops.opConst(target.type.ropType); 494 if (rop.getBranchingness() == BRANCH_NONE) { 495 addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(), 496 RegisterSpecList.EMPTY, Constants.getConstant(value))); 497 } else { 498 addInstruction(new ThrowingCstInsn(rop, sourcePosition, 499 RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); 500 moveResult(target, true); 501 } 502 } 503 504 /** 505 * Copies the value in {@code source} to {@code target}. 506 */ move(Local<T> target, Local<T> source)507 public <T> void move(Local<T> target, Local<T> source) { 508 addInstruction(new PlainInsn(Rops.opMove(source.type.ropType), 509 sourcePosition, target.spec(), source.spec())); 510 } 511 512 // instructions: unary and binary 513 514 /** 515 * Executes {@code op} and sets {@code target} to the result. 516 */ op(UnaryOp op, Local<T> target, Local<T> source)517 public <T> void op(UnaryOp op, Local<T> target, Local<T> source) { 518 addInstruction(new PlainInsn(op.rop(source.type), sourcePosition, 519 target.spec(), source.spec())); 520 } 521 522 /** 523 * Executes {@code op} and sets {@code target} to the result. For most 524 * binary operations, the types of {@code a} and {@code b} must be the same. 525 * Shift operations (like {@link BinaryOp#SHIFT_LEFT}) require {@code b} to 526 * be an {@code int}, even when {@code a} is a {@code long}. 527 */ op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b)528 public <T1, T2> void op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b) { 529 Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 530 RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec()); 531 532 if (rop.getBranchingness() == BRANCH_NONE) { 533 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources)); 534 } else { 535 addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches)); 536 moveResult(target, true); 537 } 538 } 539 540 // instructions: branches 541 542 /** 543 * Compare ints or references. If the comparison is true, execution jumps to 544 * {@code trueLabel}. If it is false, execution continues to the next 545 * instruction. 546 */ compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b)547 public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) { 548 adopt(trueLabel); 549 Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 550 addInstruction(new PlainInsn(rop, sourcePosition, null, 551 RegisterSpecList.make(a.spec(), b.spec())), trueLabel); 552 } 553 554 /** 555 * Check if an int or reference equals to zero. If the comparison is true, 556 * execution jumps to {@code trueLabel}. If it is false, execution continues to 557 * the next instruction. 558 */ compareZ(Comparison comparison, Label trueLabel, Local<?> a)559 public <T> void compareZ(Comparison comparison, Label trueLabel, Local<?> a) { 560 adopt(trueLabel); 561 Rop rop = comparison.rop(StdTypeList.make(a.type.ropType)); 562 addInstruction(new PlainInsn(rop, sourcePosition, null, 563 RegisterSpecList.make(a.spec())), trueLabel); 564 } 565 566 /** 567 * Compare floats or doubles. This stores -1 in {@code target} if {@code 568 * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code 569 * a > b}. This stores {@code nanValue} in {@code target} if either value 570 * is {@code NaN}. 571 */ compareFloatingPoint( Local<Integer> target, Local<T> a, Local<T> b, int nanValue)572 public <T extends Number> void compareFloatingPoint( 573 Local<Integer> target, Local<T> a, Local<T> b, int nanValue) { 574 Rop rop; 575 if (nanValue == 1) { 576 rop = Rops.opCmpg(a.type.ropType); 577 } else if (nanValue == -1) { 578 rop = Rops.opCmpl(a.type.ropType); 579 } else { 580 throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue); 581 } 582 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), 583 RegisterSpecList.make(a.spec(), b.spec()))); 584 } 585 586 /** 587 * Compare longs. This stores -1 in {@code target} if {@code 588 * a < b}, 0 in {@code target} if {@code a == b} and 1 in target if {@code 589 * a > b}. 590 */ compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b)591 public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) { 592 addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(), 593 RegisterSpecList.make(a.spec(), b.spec()))); 594 } 595 596 // instructions: fields 597 598 /** 599 * Copies the value in instance field {@code fieldId} of {@code instance} to 600 * {@code target}. 601 */ iget(FieldId<D, ? extends V> fieldId, Local<V> target, Local<D> instance)602 public <D, V> void iget(FieldId<D, ? extends V> fieldId, Local<V> target, Local<D> instance) { 603 addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition, 604 RegisterSpecList.make(instance.spec()), catches, fieldId.constant)); 605 moveResult(target, true); 606 } 607 608 /** 609 * Copies the value in {@code source} to the instance field {@code fieldId} 610 * of {@code instance}. 611 */ iput(FieldId<D, V> fieldId, Local<? extends D> instance, Local<? extends V> source)612 public <D, V> void iput(FieldId<D, V> fieldId, Local<? extends D> instance, Local<? extends V> source) { 613 addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition, 614 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant)); 615 } 616 617 /** 618 * Copies the value in the static field {@code fieldId} to {@code target}. 619 */ sget(FieldId<?, ? extends V> fieldId, Local<V> target)620 public <V> void sget(FieldId<?, ? extends V> fieldId, Local<V> target) { 621 addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition, 622 RegisterSpecList.EMPTY, catches, fieldId.constant)); 623 moveResult(target, true); 624 } 625 626 /** 627 * Copies the value in {@code source} to the static field {@code fieldId}. 628 */ sput(FieldId<?, V> fieldId, Local<? extends V> source)629 public <V> void sput(FieldId<?, V> fieldId, Local<? extends V> source) { 630 addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition, 631 RegisterSpecList.make(source.spec()), catches, fieldId.constant)); 632 } 633 634 // instructions: invoke 635 636 /** 637 * Calls the constructor {@code constructor} using {@code args} and assigns 638 * the new instance to {@code target}. 639 */ newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args)640 public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) { 641 if (target == null) { 642 throw new IllegalArgumentException(); 643 } 644 addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, 645 RegisterSpecList.EMPTY, catches, constructor.declaringType.constant)); 646 moveResult(target, true); 647 invokeDirect(constructor, null, target, args); 648 } 649 650 /** 651 * Calls the static method {@code method} using {@code args} and assigns the 652 * result to {@code target}. 653 * 654 * @param target the local to receive the method's return value, or {@code 655 * null} if the return type is {@code void} or if its value not needed. 656 */ invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args)657 public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) { 658 invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args); 659 } 660 661 /** 662 * Calls the non-private instance method {@code method} of {@code instance} 663 * using {@code args} and assigns the result to {@code target}. 664 * 665 * @param method a non-private, non-static, method declared on a class. May 666 * not be an interface method or a constructor. 667 * @param target the local to receive the method's return value, or {@code 668 * null} if the return type is {@code void} or if its value not needed. 669 */ invokeVirtual(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)670 public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target, 671 Local<? extends D> instance, Local<?>... args) { 672 invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, instance, args); 673 } 674 675 /** 676 * Calls {@code method} of {@code instance} using {@code args} and assigns 677 * the result to {@code target}. 678 * 679 * @param method either a private method or the superclass's constructor in 680 * a constructor's call to {@code super()}. 681 * @param target the local to receive the method's return value, or {@code 682 * null} if the return type is {@code void} or if its value not needed. 683 */ invokeDirect(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)684 public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target, 685 Local<? extends D> instance, Local<?>... args) { 686 invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, instance, args); 687 } 688 689 /** 690 * Calls the closest superclass's virtual method {@code method} of {@code 691 * instance} using {@code args} and assigns the result to {@code target}. 692 * 693 * @param target the local to receive the method's return value, or {@code 694 * null} if the return type is {@code void} or if its value not needed. 695 */ invokeSuper(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)696 public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target, 697 Local<? extends D> instance, Local<?>... args) { 698 invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, instance, args); 699 } 700 701 /** 702 * Calls the interface method {@code method} of {@code instance} using 703 * {@code args} and assigns the result to {@code target}. 704 * 705 * @param method a method declared on an interface. 706 * @param target the local to receive the method's return value, or {@code 707 * null} if the return type is {@code void} or if its value not needed. 708 */ invokeInterface(MethodId<D, R> method, Local<? super R> target, Local<? extends D> instance, Local<?>... args)709 public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target, 710 Local<? extends D> instance, Local<?>... args) { 711 invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, instance, args); 712 } 713 invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, Local<? extends D> object, Local<?>... args)714 private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, 715 Local<? extends D> object, Local<?>... args) { 716 addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args), 717 catches, method.constant)); 718 if (target != null) { 719 moveResult(target, false); 720 } 721 } 722 723 // instructions: types 724 725 /** 726 * Tests if the value in {@code source} is assignable to {@code type}. If it 727 * is, {@code target} is assigned to 1; otherwise {@code target} is assigned 728 * to 0. 729 */ instanceOfType(Local<?> target, Local<?> source, TypeId<?> type)730 public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) { 731 addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition, 732 RegisterSpecList.make(source.spec()), catches, type.constant)); 733 moveResult(target, true); 734 } 735 736 /** 737 * Performs either a numeric cast or a type cast. 738 * 739 * <h3>Numeric Casts</h3> 740 * Converts a primitive to a different representation. Numeric casts may 741 * be lossy. For example, converting the double {@code 1.8d} to an integer 742 * yields {@code 1}, losing the fractional part. Converting the integer 743 * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high 744 * bytes. The following numeric casts are supported: 745 * 746 * <p><table border="1" summary="Supported Numeric Casts"> 747 * <tr><th>From</th><th>To</th></tr> 748 * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr> 749 * <tr><td>long</td><td>int, float, double</td></tr> 750 * <tr><td>float</td><td>int, long, double</td></tr> 751 * <tr><td>double</td><td>int, long, float</td></tr> 752 * </table> 753 * 754 * <p>For some primitive conversions it will be necessary to chain multiple 755 * cast operations. For example, to go from float to short one would first 756 * cast float to int and then int to short. 757 * 758 * <p>Numeric casts never throw {@link ClassCastException}. 759 * 760 * <h3>Type Casts</h3> 761 * Checks that a reference value is assignable to the target type. If it is 762 * assignable it is copied to the target local. If it is not assignable a 763 * {@link ClassCastException} is thrown. 764 */ cast(Local<?> target, Local<?> source)765 public void cast(Local<?> target, Local<?> source) { 766 if (source.getType().ropType.isReference()) { 767 addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, 768 RegisterSpecList.make(source.spec()), catches, target.type.constant)); 769 moveResult(target, true); 770 } else { 771 addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType), 772 sourcePosition, target.spec(), source.spec())); 773 } 774 } 775 getCastRop(com.android.dx.rop.type.Type sourceType, com.android.dx.rop.type.Type targetType)776 private Rop getCastRop(com.android.dx.rop.type.Type sourceType, 777 com.android.dx.rop.type.Type targetType) { 778 if (sourceType.getBasicType() == BT_INT) { 779 switch (targetType.getBasicType()) { 780 case BT_SHORT: 781 return Rops.TO_SHORT; 782 case BT_CHAR: 783 return Rops.TO_CHAR; 784 case BT_BYTE: 785 return Rops.TO_BYTE; 786 } 787 } 788 return Rops.opConv(targetType, sourceType); 789 } 790 791 // instructions: arrays 792 793 /** 794 * Sets {@code target} to the length of the array in {@code array}. 795 */ arrayLength(Local<Integer> target, Local<T> array)796 public <T> void arrayLength(Local<Integer> target, Local<T> array) { 797 addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition, 798 RegisterSpecList.make(array.spec()), catches)); 799 moveResult(target, true); 800 } 801 802 /** 803 * Assigns {@code target} to a newly allocated array of length {@code 804 * length}. The array's type is the same as {@code target}'s type. 805 */ newArray(Local<T> target, Local<Integer> length)806 public <T> void newArray(Local<T> target, Local<Integer> length) { 807 addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition, 808 RegisterSpecList.make(length.spec()), catches, target.type.constant)); 809 moveResult(target, true); 810 } 811 812 /** 813 * Assigns the element at {@code index} in {@code array} to {@code target}. 814 */ aget(Local<?> target, Local<?> array, Local<Integer> index)815 public void aget(Local<?> target, Local<?> array, Local<Integer> index) { 816 addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition, 817 RegisterSpecList.make(array.spec(), index.spec()), catches)); 818 moveResult(target, true); 819 } 820 821 /** 822 * Assigns {@code source} to the element at {@code index} in {@code array}. 823 */ aput(Local<?> array, Local<Integer> index, Local<?> source)824 public void aput(Local<?> array, Local<Integer> index, Local<?> source) { 825 addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition, 826 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches)); 827 } 828 829 // instructions: return 830 831 /** 832 * Returns from a {@code void} method. After a return it is an error to 833 * define further instructions after a return without first {@link #mark 834 * marking} an existing unmarked label. 835 */ returnVoid()836 public void returnVoid() { 837 if (!method.returnType.equals(TypeId.VOID)) { 838 throw new IllegalArgumentException("declared " + method.returnType 839 + " but returned void"); 840 } 841 addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null, 842 RegisterSpecList.EMPTY)); 843 } 844 845 /** 846 * Returns the value in {@code result} to the calling method. After a return 847 * it is an error to define further instructions after a return without 848 * first {@link #mark marking} an existing unmarked label. 849 */ returnValue(Local<?> result)850 public void returnValue(Local<?> result) { 851 if (!result.type.equals(method.returnType)) { 852 // TODO: this is probably too strict. 853 throw new IllegalArgumentException("declared " + method.returnType 854 + " but returned " + result.type); 855 } 856 addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition, 857 null, RegisterSpecList.make(result.spec()))); 858 } 859 moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn)860 private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) { 861 Rop rop = afterNonInvokeThrowingInsn 862 ? Rops.opMoveResultPseudo(target.type.ropType) 863 : Rops.opMoveResult(target.type.ropType); 864 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY)); 865 } 866 867 // instructions; synchronized 868 869 /** 870 * Awaits the lock on {@code monitor}, and acquires it. 871 */ monitorEnter(Local<?> monitor)872 public void monitorEnter(Local<?> monitor) { 873 addInstruction(new ThrowingInsn(Rops.MONITOR_ENTER, sourcePosition, 874 RegisterSpecList.make(monitor.spec()), catches)); 875 } 876 877 /** 878 * Releases the held lock on {@code monitor}. 879 */ monitorExit(Local<?> monitor)880 public void monitorExit(Local<?> monitor) { 881 addInstruction(new ThrowingInsn(Rops.MONITOR_EXIT, sourcePosition, 882 RegisterSpecList.make(monitor.spec()), catches)); 883 } 884 885 // produce BasicBlocks for dex 886 toBasicBlocks()887 BasicBlockList toBasicBlocks() { 888 if (!localsInitialized) { 889 initializeLocals(); 890 } 891 892 cleanUpLabels(); 893 894 BasicBlockList result = new BasicBlockList(labels.size()); 895 for (int i = 0; i < labels.size(); i++) { 896 result.set(i, labels.get(i).toBasicBlock()); 897 } 898 return result; 899 } 900 901 /** 902 * Removes empty labels and assigns IDs to non-empty labels. 903 */ cleanUpLabels()904 private void cleanUpLabels() { 905 int id = 0; 906 for (Iterator<Label> i = labels.iterator(); i.hasNext();) { 907 Label label = i.next(); 908 if (label.isEmpty()) { 909 i.remove(); 910 } else { 911 label.compact(); 912 label.id = id++; 913 } 914 } 915 } 916 concatenate(Local<?> first, Local<?>[] rest)917 private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { 918 int offset = (first != null) ? 1 : 0; 919 RegisterSpecList result = new RegisterSpecList(offset + rest.length); 920 if (first != null) { 921 result.set(0, first.spec()); 922 } 923 for (int i = 0; i < rest.length; i++) { 924 result.set(i + offset, rest[i].spec()); 925 } 926 return result; 927 } 928 } 929