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