Code.java revision d6c77efc0b187577dd7956070adfc7c335f65698
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>Compare</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 and Labels</h3> 91 * All control flow is created with branches and labels. 92 */ 93/* TDODO: document these below 94 * 95 * new Label() 96 * jump() 97 * mark() 98 * return() 99 * 100 * <h3>Try/Catch blocks</h3> 101 * addCatchClause() 102 * removeCatchClause() 103 * 104 * <h3>Invoke</h3> 105 * invokeXxx 106 * newInstance() 107 * 108 * <h3>Fields</h3> 109 * iget() 110 * iput() 111 * sget() 112 * sput() 113 * 114 * <h3>Arrays</h3> 115 * aget() 116 * aput() 117 * arrayLength() 118 * newArray() 119 * 120 * <h3>Types and Casts</h3> 121 * cast() 122 * instanceOfType() 123 */ 124public final class Code { 125 private final MethodId<?, ?> method; 126 /** 127 * All allocated labels. Although the order of the labels in this list 128 * shouldn't impact behavior, it is used to determine basic block indices. 129 */ 130 private final List<Label> labels = new ArrayList<Label>(); 131 132 /** 133 * The label currently receiving instructions. This is null if the most 134 * recent instruction was a return or goto. 135 */ 136 private Label currentLabel; 137 138 /** true once we've fixed the positions of the parameter registers */ 139 private boolean localsInitialized; 140 141 private final Local<?> thisLocal; 142 143 /** 144 * The parameters on this method. If this is non-static, the first parameter 145 * is 'thisLocal' and we have to offset the user's indices by one. 146 */ 147 private final List<Local<?>> parameters = new ArrayList<Local<?>>(); 148 private final List<Local<?>> locals = new ArrayList<Local<?>>(); 149 private SourcePosition sourcePosition = SourcePosition.NO_INFO; 150 private final List<TypeId<?>> catchTypes = new ArrayList<TypeId<?>>(); 151 private final List<Label> catchLabels = new ArrayList<Label>(); 152 private StdTypeList catches = StdTypeList.EMPTY; 153 154 Code(DexMaker.MethodDeclaration methodDeclaration) { 155 this.method = methodDeclaration.method; 156 if (methodDeclaration.isStatic()) { 157 thisLocal = null; 158 } else { 159 thisLocal = Local.get(this, method.declaringType); 160 parameters.add(thisLocal); 161 } 162 for (TypeId<?> parameter : method.parameters.types) { 163 parameters.add(Local.get(this, parameter)); 164 } 165 this.currentLabel = new Label(); 166 adopt(this.currentLabel); 167 this.currentLabel.marked = true; 168 } 169 170 public <T> Local<T> newLocal(TypeId<T> type) { 171 if (localsInitialized) { 172 throw new IllegalStateException("Cannot allocate locals after adding instructions"); 173 } 174 Local<T> result = Local.get(this, type); 175 locals.add(result); 176 return result; 177 } 178 179 public <T> Local<T> getParameter(int index, TypeId<T> type) { 180 if (thisLocal != null) { 181 index++; // adjust for the hidden 'this' parameter 182 } 183 return coerce(parameters.get(index), type); 184 } 185 186 public <T> Local<T> getThis(TypeId<T> type) { 187 if (thisLocal == null) { 188 throw new IllegalStateException("static methods cannot access 'this'"); 189 } 190 return coerce(thisLocal, type); 191 } 192 193 @SuppressWarnings("unchecked") // guarded by an equals check 194 private <T> Local<T> coerce(Local<?> local, TypeId<T> expectedType) { 195 if (!local.type.equals(expectedType)) { 196 throw new IllegalArgumentException( 197 "requested " + expectedType + " but was " + local.type); 198 } 199 return (Local<T>) local; 200 } 201 202 /** 203 * Assigns registers to locals. From the spec: 204 * "the N arguments to a method land in the last N registers of the 205 * method's invocation frame, in order. Wide arguments consume two 206 * registers. Instance methods are passed a this reference as their 207 * first argument." 208 * 209 * In addition to assigning registers to each of the locals, this creates 210 * instructions to move parameters into their initial registers. These 211 * instructions are inserted before the code's first real instruction. 212 */ 213 void initializeLocals() { 214 if (localsInitialized) { 215 throw new AssertionError(); 216 } 217 localsInitialized = true; 218 219 int reg = 0; 220 for (Local<?> local : locals) { 221 reg += local.initialize(reg); 222 } 223 int firstParamReg = reg; 224 List<Insn> moveParameterInstructions = new ArrayList<Insn>(); 225 for (Local<?> local : parameters) { 226 CstInteger paramConstant = CstInteger.make(reg - firstParamReg); 227 reg += local.initialize(reg); 228 moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType), 229 sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant)); 230 } 231 labels.get(0).instructions.addAll(0, moveParameterInstructions); 232 } 233 234 /** 235 * Returns the number of registers to hold the parameters. This includes the 236 * 'this' parameter if it exists. 237 */ 238 int paramSize() { 239 int result = 0; 240 for (Local<?> local : parameters) { 241 result += local.size(); 242 } 243 return result; 244 } 245 246 // labels 247 248 /** 249 * Assigns {@code target} to this code. 250 */ 251 private void adopt(Label target) { 252 if (target.code == this) { 253 return; // already adopted 254 } 255 if (target.code != null) { 256 throw new IllegalArgumentException("Cannot adopt label; it belongs to another Code"); 257 } 258 target.code = this; 259 labels.add(target); 260 } 261 262 /** 263 * Start defining instructions for the named label. 264 */ 265 public void mark(Label label) { 266 adopt(label); 267 if (label.marked) { 268 throw new IllegalStateException("already marked"); 269 } 270 label.marked = true; 271 if (currentLabel != null) { 272 jump(label); // blocks must end with a branch, return or throw 273 } 274 currentLabel = label; 275 } 276 277 public void jump(Label target) { 278 adopt(target); 279 addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY), 280 target); 281 } 282 283 public void addCatchClause(TypeId<?> throwable, Label catchClause) { 284 if (catchTypes.contains(throwable)) { 285 throw new IllegalArgumentException("Already caught: " + throwable); 286 } 287 adopt(catchClause); 288 catchTypes.add(throwable); 289 catches = toTypeList(catchTypes); 290 catchLabels.add(catchClause); 291 } 292 293 public Label removeCatchClause(TypeId<?> throwable) { 294 int index = catchTypes.indexOf(throwable); 295 if (index == -1) { 296 throw new IllegalArgumentException("No catch clause: " + throwable); 297 } 298 catchTypes.remove(index); 299 catches = toTypeList(catchTypes); 300 return catchLabels.remove(index); 301 } 302 303 public void throwValue(Local<?> throwable) { 304 addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition, 305 RegisterSpecList.make(throwable.spec()), catches)); 306 } 307 308 private StdTypeList toTypeList(List<TypeId<?>> types) { 309 StdTypeList result = new StdTypeList(types.size()); 310 for (int i = 0; i < types.size(); i++) { 311 result.set(i, types.get(i).ropType); 312 } 313 return result; 314 } 315 316 private void addInstruction(Insn insn) { 317 addInstruction(insn, null); 318 } 319 320 /** 321 * @param branch the branches to follow; interpretation depends on the 322 * instruction's branchingness. 323 */ 324 private void addInstruction(Insn insn, Label branch) { 325 if (currentLabel == null || !currentLabel.marked) { 326 throw new IllegalStateException("no current label"); 327 } 328 currentLabel.instructions.add(insn); 329 330 switch (insn.getOpcode().getBranchingness()) { 331 case BRANCH_NONE: 332 if (branch != null) { 333 throw new IllegalArgumentException("unexpected branch: " + branch); 334 } 335 return; 336 337 case BRANCH_RETURN: 338 if (branch != null) { 339 throw new IllegalArgumentException("unexpected branch: " + branch); 340 } 341 currentLabel = null; 342 break; 343 344 case BRANCH_GOTO: 345 if (branch == null) { 346 throw new IllegalArgumentException("branch == null"); 347 } 348 currentLabel.primarySuccessor = branch; 349 currentLabel = null; 350 break; 351 352 case Rop.BRANCH_IF: 353 if (branch == null) { 354 throw new IllegalArgumentException("branch == null"); 355 } 356 splitCurrentLabel(branch, Collections.<Label>emptyList()); 357 break; 358 359 case Rop.BRANCH_THROW: 360 if (branch != null) { 361 throw new IllegalArgumentException("unexpected branch: " + branch); 362 } 363 splitCurrentLabel(null, new ArrayList<Label>(catchLabels)); 364 break; 365 366 default: 367 throw new IllegalArgumentException(); 368 } 369 } 370 371 /** 372 * Closes the current label and starts a new one. 373 * 374 * @param catchLabels an immutable list of catch labels 375 */ 376 private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) { 377 Label newLabel = new Label(); 378 adopt(newLabel); 379 currentLabel.primarySuccessor = newLabel; 380 currentLabel.alternateSuccessor = alternateSuccessor; 381 currentLabel.catchLabels = catchLabels; 382 currentLabel = newLabel; 383 currentLabel.marked = true; 384 } 385 386 // instructions: constants 387 388 public <T> void loadConstant(Local<T> target, T value) { 389 Rop rop = value == null 390 ? Rops.CONST_OBJECT_NOTHROW 391 : Rops.opConst(target.type.ropType); 392 if (rop.getBranchingness() == BRANCH_NONE) { 393 addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(), 394 RegisterSpecList.EMPTY, Constants.getConstant(value))); 395 } else { 396 addInstruction(new ThrowingCstInsn(rop, sourcePosition, 397 RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); 398 moveResult(target, true); 399 } 400 } 401 402 // instructions: unary and binary 403 404 public <T> void op(UnaryOp op, Local<T> target, Local<T> source) { 405 addInstruction(new PlainInsn(op.rop(source.type), sourcePosition, 406 target.spec(), source.spec())); 407 } 408 409 public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) { 410 Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 411 RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec()); 412 413 if (rop.getBranchingness() == BRANCH_NONE) { 414 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources)); 415 } else { 416 addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches)); 417 moveResult(target, true); 418 } 419 } 420 421 // instructions: branches 422 423 /** 424 * Compare ints or references. If the comparison is true, execution jumps to 425 * {@code trueLabel}. If it is false, execution continues to the next 426 * instruction. 427 */ 428 public <T> void compare(Comparison comparison, Label trueLabel, Local<T> a, Local<T> b) { 429 adopt(trueLabel); 430 // TODO: ops to compare with zero/null: just omit the 2nd local in StdTypeList.make() 431 Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 432 addInstruction(new PlainInsn(rop, sourcePosition, null, 433 RegisterSpecList.make(a.spec(), b.spec())), trueLabel); 434 } 435 436 /** 437 * Compare floats or doubles. 438 */ 439 public <T extends Number> void compareFloatingPoint( 440 Local<Integer> target, Local<T> a, Local<T> b, int nanValue) { 441 Rop rop; 442 if (nanValue == 1) { 443 rop = Rops.opCmpg(a.type.ropType); 444 } else if (nanValue == -1) { 445 rop = Rops.opCmpl(a.type.ropType); 446 } else { 447 throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue); 448 } 449 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), 450 RegisterSpecList.make(a.spec(), b.spec()))); 451 } 452 453 /** 454 * Compare longs. 455 */ 456 public void compareLongs(Local<Integer> target, Local<Long> a, Local<Long> b) { 457 addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(), 458 RegisterSpecList.make(a.spec(), b.spec()))); 459 } 460 461 // instructions: fields 462 463 public <D, V> void iget(FieldId<D, V> fieldId, Local<V> target, Local<D> instance) { 464 addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition, 465 RegisterSpecList.make(instance.spec()), catches, fieldId.constant)); 466 moveResult(target, true); 467 } 468 469 public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) { 470 addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition, 471 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant)); 472 } 473 474 public <V> void sget(FieldId<?, V> fieldId, Local<V> target) { 475 addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition, 476 RegisterSpecList.EMPTY, catches, fieldId.constant)); 477 moveResult(target, true); 478 } 479 480 public <V> void sput(FieldId<?, V> fieldId, Local<V> source) { 481 addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition, 482 RegisterSpecList.make(source.spec()), catches, fieldId.constant)); 483 } 484 485 // instructions: invoke 486 487 public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) { 488 if (target == null) { 489 throw new IllegalArgumentException(); 490 } 491 addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, 492 RegisterSpecList.EMPTY, catches, constructor.declaringType.constant)); 493 moveResult(target, true); 494 invokeDirect(constructor, null, target, args); 495 } 496 497 public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) { 498 invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args); 499 } 500 501 public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target, 502 Local<? extends D> object, Local<?>... args) { 503 invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args); 504 } 505 506 public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target, 507 Local<? extends D> object, Local<?>... args) { 508 invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args); 509 } 510 511 public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target, 512 Local<? extends D> object, Local<?>... args) { 513 invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args); 514 } 515 516 public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target, 517 Local<? extends D> object, Local<?>... args) { 518 invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args); 519 } 520 521 private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, 522 Local<? extends D> object, Local<?>... args) { 523 addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args), 524 catches, method.constant)); 525 if (target != null) { 526 moveResult(target, false); 527 } 528 } 529 530 // instructions: types 531 532 public void instanceOfType(Local<?> target, Local<?> source, TypeId<?> type) { 533 addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition, 534 RegisterSpecList.make(source.spec()), catches, type.constant)); 535 moveResult(target, true); 536 } 537 538 /** 539 * Performs either a numeric cast or a type cast. 540 * 541 * <h3>Numeric Casts</h3> 542 * Converts a primitive to a different representation. Numeric casts may 543 * be lossy. For example, converting the double {@code 1.8d} to an integer 544 * yields {@code 1}, losing the fractional part. Converting the integer 545 * {@code 0x12345678} to a short yields {@code 0x5678}, losing the high 546 * bytes. The following numeric casts are supported: 547 * 548 * <p><table border="1"> 549 * <tr><th>From</th><th>To</th></tr> 550 * <tr><td>int</td><td>byte, char, short, long, float, double</td></tr> 551 * <tr><td>long</td><td>int, float, double</td></tr> 552 * <tr><td>float</td><td>int, long, double</td></tr> 553 * <tr><td>double</td><td>int, long, float</td></tr> 554 * </table> 555 * 556 * <p>For some primitive conversions it will be necessary to chain multiple 557 * cast operations. For example, to go from float to short one would first 558 * cast float to int and then int to short. 559 * 560 * <p>Numeric casts never throw {@link ClassCastException}. 561 * 562 * <h3>Type Casts</h3> 563 * Checks that a reference value is assignable to the target type. If it is 564 * assignable it is copied to the target local. If it is not assignable a 565 * {@link ClassCastException} is thrown. 566 */ 567 public void cast(Local<?> target, Local<?> source) { 568 if (source.getType().ropType.isReference()) { 569 addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, 570 RegisterSpecList.make(source.spec()), catches, target.type.constant)); 571 moveResult(target, true); 572 } else { 573 addInstruction(new PlainInsn(getCastRop(source.type.ropType, target.type.ropType), 574 sourcePosition, target.spec(), source.spec())); 575 } 576 } 577 578 private Rop getCastRop(com.android.dx.rop.type.Type sourceType, 579 com.android.dx.rop.type.Type targetType) { 580 if (sourceType.getBasicType() == BT_INT) { 581 switch (targetType.getBasicType()) { 582 case BT_SHORT: 583 return Rops.TO_SHORT; 584 case BT_CHAR: 585 return Rops.TO_CHAR; 586 case BT_BYTE: 587 return Rops.TO_BYTE; 588 } 589 } 590 return Rops.opConv(targetType, sourceType); 591 } 592 593 // instructions: arrays 594 595 public <T> void arrayLength(Local<Integer> target, Local<T> array) { 596 addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition, 597 RegisterSpecList.make(array.spec()), catches)); 598 moveResult(target, true); 599 } 600 601 public <T> void newArray(Local<T> target, Local<Integer> length) { 602 addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition, 603 RegisterSpecList.make(length.spec()), catches, target.type.constant)); 604 moveResult(target, true); 605 } 606 607 public void aget(Local<?> target, Local<?> array, Local<Integer> index) { 608 addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition, 609 RegisterSpecList.make(array.spec(), index.spec()), catches)); 610 moveResult(target, true); 611 } 612 613 public void aput(Local<?> array, Local<Integer> index, Local<?> source) { 614 addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition, 615 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches)); 616 } 617 618 // instructions: return 619 620 public void returnVoid() { 621 if (!method.returnType.equals(TypeId.VOID)) { 622 throw new IllegalArgumentException("declared " + method.returnType 623 + " but returned void"); 624 } 625 addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null, 626 RegisterSpecList.EMPTY)); 627 } 628 629 public void returnValue(Local<?> result) { 630 if (!result.type.equals(method.returnType)) { 631 // TODO: this is probably too strict. 632 throw new IllegalArgumentException("declared " + method.returnType 633 + " but returned " + result.type); 634 } 635 addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition, 636 null, RegisterSpecList.make(result.spec()))); 637 } 638 639 private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) { 640 Rop rop = afterNonInvokeThrowingInsn 641 ? Rops.opMoveResultPseudo(target.type.ropType) 642 : Rops.opMoveResult(target.type.ropType); 643 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY)); 644 } 645 646 // produce BasicBlocks for dex 647 648 BasicBlockList toBasicBlocks() { 649 if (!localsInitialized) { 650 initializeLocals(); 651 } 652 653 cleanUpLabels(); 654 655 BasicBlockList result = new BasicBlockList(labels.size()); 656 for (int i = 0; i < labels.size(); i++) { 657 result.set(i, labels.get(i).toBasicBlock()); 658 } 659 return result; 660 } 661 662 /** 663 * Removes empty labels and assigns IDs to non-empty labels. 664 */ 665 private void cleanUpLabels() { 666 int id = 0; 667 for (Iterator<Label> i = labels.iterator(); i.hasNext();) { 668 Label label = i.next(); 669 if (label.isEmpty()) { 670 i.remove(); 671 } else { 672 label.compact(); 673 label.id = id++; 674 } 675 } 676 } 677 678 private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { 679 int offset = (first != null) ? 1 : 0; 680 RegisterSpecList result = new RegisterSpecList(offset + rest.length); 681 if (first != null) { 682 result.set(0, first.spec()); 683 } 684 for (int i = 0; i < rest.length; i++) { 685 result.set(i + offset, rest[i].spec()); 686 } 687 return result; 688 } 689} 690