Code.java revision ab220f004db90fa94ef9349ca1adde5f89012e8d
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 */ 46public final class Code { 47 private final MethodId<?, ?> method; 48 /** 49 * All allocated labels. Although the order of the labels in this list 50 * shouldn't impact behavior, it is used to determine basic block indices. 51 */ 52 private final List<Label> labels = new ArrayList<Label>(); 53 54 /** 55 * The label currently receiving instructions. This is null if the most 56 * recent instruction was a return or goto. 57 */ 58 private Label currentLabel; 59 60 /** true once we've fixed the positions of the parameter registers */ 61 private boolean localsInitialized; 62 63 private final Local<?> thisLocal; 64 65 /** 66 * The parameters on this method. If this is non-static, the first parameter 67 * is 'thisLocal' and we have to offset the user's indices by one. 68 */ 69 private final List<Local<?>> parameters = new ArrayList<Local<?>>(); 70 private final List<Local<?>> locals = new ArrayList<Local<?>>(); 71 private SourcePosition sourcePosition = SourcePosition.NO_INFO; 72 private final List<Type<?>> catchTypes = new ArrayList<Type<?>>(); 73 private final List<Label> catchLabels = new ArrayList<Label>(); 74 private StdTypeList catches = StdTypeList.EMPTY; 75 76 Code(DexMaker.MethodDeclaration methodDeclaration) { 77 this.method = methodDeclaration.method; 78 if (methodDeclaration.isStatic()) { 79 thisLocal = null; 80 } else { 81 thisLocal = Local.get(this, method.declaringType); 82 parameters.add(thisLocal); 83 } 84 for (Type<?> parameter : method.parameters.types) { 85 parameters.add(Local.get(this, parameter)); 86 } 87 this.currentLabel = newLabel(); 88 this.currentLabel.marked = true; 89 } 90 91 public <T> Local<T> newLocal(Type<T> type) { 92 if (localsInitialized) { 93 throw new IllegalStateException("Cannot allocate locals after adding instructions"); 94 } 95 Local<T> result = Local.get(this, type); 96 locals.add(result); 97 return result; 98 } 99 100 public <T> Local<T> getParameter(int index, Type<T> type) { 101 if (thisLocal != null) { 102 index++; // adjust for the hidden 'this' parameter 103 } 104 return coerce(parameters.get(index), type); 105 } 106 107 public <T> Local<T> getThis(Type<T> type) { 108 if (thisLocal == null) { 109 throw new IllegalStateException("static methods cannot access 'this'"); 110 } 111 return coerce(thisLocal, type); 112 } 113 114 @SuppressWarnings("unchecked") // guarded by an equals check 115 private <T> Local<T> coerce(Local<?> local, Type<T> expectedType) { 116 if (!local.type.equals(expectedType)) { 117 throw new IllegalArgumentException( 118 "requested " + expectedType + " but was " + local.type); 119 } 120 return (Local<T>) local; 121 } 122 123 /** 124 * Assigns registers to locals. From the spec: 125 * "the N arguments to a method land in the last N registers of the 126 * method's invocation frame, in order. Wide arguments consume two 127 * registers. Instance methods are passed a this reference as their 128 * first argument." 129 * 130 * In addition to assigning registers to each of the locals, this creates 131 * instructions to move parameters into their initial registers. These 132 * instructions are inserted before the code's first real instruction. 133 */ 134 void initializeLocals() { 135 if (localsInitialized) { 136 throw new AssertionError(); 137 } 138 localsInitialized = true; 139 140 int reg = 0; 141 for (Local<?> local : locals) { 142 reg += local.initialize(reg); 143 } 144 int firstParamReg = reg; 145 List<Insn> moveParameterInstructions = new ArrayList<Insn>(); 146 for (Local<?> local : parameters) { 147 CstInteger paramConstant = CstInteger.make(reg - firstParamReg); 148 reg += local.initialize(reg); 149 moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType), 150 sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant)); 151 } 152 labels.get(0).instructions.addAll(0, moveParameterInstructions); 153 } 154 155 /** 156 * Returns the number of registers to hold the parameters. This includes the 157 * 'this' parameter if it exists. 158 */ 159 int paramSize() { 160 int result = 0; 161 for (Local<?> local : parameters) { 162 result += local.size(); 163 } 164 return result; 165 } 166 167 // labels 168 169 /** 170 * Creates a new label for use as a branch target. The new label must have 171 * code attached to it later by calling {@link #mark(Label)}. 172 */ 173 public Label newLabel() { 174 Label result = new Label(); 175 labels.add(result); 176 return result; 177 } 178 179 /** 180 * Start defining instructions for the named label. 181 */ 182 public void mark(Label label) { 183 if (label.marked) { 184 throw new IllegalStateException("already marked"); 185 } 186 label.marked = true; 187 if (currentLabel != null) { 188 jump(label); // blocks must end with a branch, return or throw 189 } 190 currentLabel = label; 191 } 192 193 public void jump(Label target) { 194 addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY), 195 target); 196 } 197 198 public void addCatchClause(Type<?> throwable, Label catchClause) { 199 if (catchTypes.contains(throwable)) { 200 throw new IllegalArgumentException("Already caught: " + throwable); 201 } 202 catchTypes.add(throwable); 203 catches = toTypeList(catchTypes); 204 catchLabels.add(catchClause); 205 } 206 207 public Label removeCatchClause(Type<?> throwable) { 208 int index = catchTypes.indexOf(throwable); 209 if (index == -1) { 210 throw new IllegalArgumentException("No catch clause: " + throwable); 211 } 212 catchTypes.remove(index); 213 catches = toTypeList(catchTypes); 214 return catchLabels.remove(index); 215 } 216 217 public void throwValue(Local<?> throwable) { 218 addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition, 219 RegisterSpecList.make(throwable.spec()), catches)); 220 } 221 222 private StdTypeList toTypeList(List<Type<?>> types) { 223 StdTypeList result = new StdTypeList(types.size()); 224 for (int i = 0; i < types.size(); i++) { 225 result.set(i, types.get(i).ropType); 226 } 227 return result; 228 } 229 230 private void addInstruction(Insn insn) { 231 addInstruction(insn, null); 232 } 233 234 /** 235 * @param branch the branches to follow; interpretation depends on the 236 * instruction's branchingness. 237 */ 238 private void addInstruction(Insn insn, Label branch) { 239 if (currentLabel == null || !currentLabel.marked) { 240 throw new IllegalStateException("no current label"); 241 } 242 currentLabel.instructions.add(insn); 243 244 switch (insn.getOpcode().getBranchingness()) { 245 case BRANCH_NONE: 246 if (branch != null) { 247 throw new IllegalArgumentException("unexpected branch: " + branch); 248 } 249 return; 250 251 case BRANCH_RETURN: 252 if (branch != null) { 253 throw new IllegalArgumentException("unexpected branch: " + branch); 254 } 255 currentLabel = null; 256 break; 257 258 case BRANCH_GOTO: 259 if (branch == null) { 260 throw new IllegalArgumentException("branch == null"); 261 } 262 currentLabel.primarySuccessor = branch; 263 currentLabel = null; 264 break; 265 266 case Rop.BRANCH_IF: 267 if (branch == null) { 268 throw new IllegalArgumentException("branch == null"); 269 } 270 splitCurrentLabel(branch, Collections.<Label>emptyList()); 271 break; 272 273 case Rop.BRANCH_THROW: 274 if (branch != null) { 275 throw new IllegalArgumentException("unexpected branch: " + branch); 276 } 277 splitCurrentLabel(null, new ArrayList<Label>(catchLabels)); 278 break; 279 280 default: 281 throw new IllegalArgumentException(); 282 } 283 } 284 285 /** 286 * Closes the current label and starts a new one. 287 * 288 * @param catchLabels an immutable list of catch labels 289 */ 290 private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) { 291 Label newLabel = newLabel(); 292 currentLabel.primarySuccessor = newLabel; 293 currentLabel.alternateSuccessor = alternateSuccessor; 294 currentLabel.catchLabels = catchLabels; 295 currentLabel = newLabel; 296 currentLabel.marked = true; 297 } 298 299 // instructions: constants 300 301 public <T> void loadConstant(Local<T> target, T value) { 302 Rop rop = value == null 303 ? Rops.CONST_OBJECT_NOTHROW 304 : Rops.opConst(target.type.ropType); 305 if (rop.getBranchingness() == BRANCH_NONE) { 306 addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(), 307 RegisterSpecList.EMPTY, Constants.getConstant(value))); 308 } else { 309 addInstruction(new ThrowingCstInsn(rop, sourcePosition, 310 RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); 311 moveResult(target, true); 312 } 313 } 314 315 // instructions: unary 316 317 public <T> void negate(Local<T> source, Local<T> target) { 318 unary(Rops.opNeg(source.type.ropType), source, target); 319 } 320 321 public <T> void not(Local<T> source, Local<T> target) { 322 unary(Rops.opNot(source.type.ropType), source, target); 323 } 324 325 public void numericCast(Local<?> source, Local<?> target) { 326 unary(getCastRop(source.type.ropType, target.type.ropType), source, target); 327 } 328 329 private Rop getCastRop(com.android.dx.rop.type.Type sourceType, 330 com.android.dx.rop.type.Type targetType) { 331 if (sourceType.getBasicType() == BT_INT) { 332 switch (targetType.getBasicType()) { 333 case BT_SHORT: 334 return Rops.TO_SHORT; 335 case BT_CHAR: 336 return Rops.TO_CHAR; 337 case BT_BYTE: 338 return Rops.TO_BYTE; 339 } 340 } 341 return Rops.opConv(targetType, sourceType); 342 } 343 344 private void unary(Rop rop, Local<?> source, Local<?> target) { 345 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), source.spec())); 346 } 347 348 // instructions: binary 349 350 public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) { 351 Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 352 RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec()); 353 354 if (rop.getBranchingness() == BRANCH_NONE) { 355 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources)); 356 } else { 357 addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches)); 358 moveResult(target, true); 359 } 360 } 361 362 // instructions: branches 363 364 /** 365 * Compare ints. If the comparison is true, execution jumps to {@code 366 * trueLabel}. If it is false, execution continues to the next instruction. 367 */ 368 public <T> void compare(Comparison comparison, Local<T> a, Local<T> b, Label trueLabel) { 369 if (trueLabel == null) { 370 throw new IllegalArgumentException(); 371 } 372 Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); 373 addInstruction(new PlainInsn(rop, sourcePosition, null, 374 RegisterSpecList.make(a.spec(), b.spec())), trueLabel); 375 } 376 377 /** 378 * Compare floats or doubles. 379 */ 380 public <T extends Number> void compare(Local<T> a, Local<T> b, Local<Integer> target, 381 int nanValue) { 382 Rop rop; 383 if (nanValue == 1) { 384 rop = Rops.opCmpg(a.type.ropType); 385 } else if (nanValue == -1) { 386 rop = Rops.opCmpl(a.type.ropType); 387 } else { 388 throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue); 389 } 390 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), 391 RegisterSpecList.make(a.spec(), b.spec()))); 392 } 393 394 /** 395 * Compare longs. 396 */ 397 public <T> void compare(Local<T> a, Local<T> b, Local<?> target) { 398 addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(), 399 RegisterSpecList.make(a.spec(), b.spec()))); 400 } 401 402 // instructions: fields 403 404 public <D, V> void iget(FieldId<D, V> fieldId, Local<D> instance, Local<V> target) { 405 addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition, 406 RegisterSpecList.make(instance.spec()), catches, fieldId.constant)); 407 moveResult(target, true); 408 } 409 410 public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) { 411 addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition, 412 RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant)); 413 } 414 415 public <V> void sget(FieldId<?, V> fieldId, Local<V> target) { 416 addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition, 417 RegisterSpecList.EMPTY, catches, fieldId.constant)); 418 moveResult(target, true); 419 } 420 421 public <V> void sput(FieldId<?, V> fieldId, Local<V> source) { 422 addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition, 423 RegisterSpecList.make(source.spec()), catches, fieldId.constant)); 424 } 425 426 // instructions: invoke 427 428 public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) { 429 if (target == null) { 430 throw new IllegalArgumentException(); 431 } 432 addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, 433 RegisterSpecList.EMPTY, catches, constructor.declaringType.constant)); 434 moveResult(target, true); 435 invokeDirect(constructor, null, target, args); 436 } 437 438 public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) { 439 invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args); 440 } 441 442 public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target, 443 Local<? extends D> object, Local<?>... args) { 444 invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args); 445 } 446 447 public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target, 448 Local<? extends D> object, Local<?>... args) { 449 invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args); 450 } 451 452 public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target, 453 Local<? extends D> object, Local<?>... args) { 454 invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args); 455 } 456 457 public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target, 458 Local<? extends D> object, Local<?>... args) { 459 invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args); 460 } 461 462 private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, 463 Local<? extends D> object, Local<?>... args) { 464 addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args), 465 catches, method.constant)); 466 if (target != null) { 467 moveResult(target, false); 468 } 469 } 470 471 // instructions: types 472 473 public void instanceOfType(Local<?> target, Local<?> source, Type<?> type) { 474 addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition, 475 RegisterSpecList.make(source.spec()), catches, type.constant)); 476 moveResult(target, true); 477 } 478 479 public void typeCast(Local<?> source, Local<?> target) { 480 addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, 481 RegisterSpecList.make(source.spec()), catches, target.type.constant)); 482 moveResult(target, true); 483 } 484 485 // instructions: arrays 486 487 public <T> void arrayLength(Local<T> array, Local<Integer> target) { 488 addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition, 489 RegisterSpecList.make(array.spec()), catches)); 490 moveResult(target, true); 491 } 492 493 public <T> void newArray(Local<Integer> length, Local<T> target) { 494 addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition, 495 RegisterSpecList.make(length.spec()), catches, target.type.constant)); 496 moveResult(target, true); 497 } 498 499 public void aget(Local<?> array, Local<Integer> index, Local<?> target) { 500 addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition, 501 RegisterSpecList.make(array.spec(), index.spec()), catches)); 502 moveResult(target, true); 503 } 504 505 public void aput(Local<?> array, Local<Integer> index, Local<?> source) { 506 addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition, 507 RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches)); 508 } 509 510 // instructions: return 511 512 public void returnVoid() { 513 if (!method.returnType.equals(Type.VOID)) { 514 throw new IllegalArgumentException("declared " + method.returnType 515 + " but returned void"); 516 } 517 addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null, 518 RegisterSpecList.EMPTY)); 519 } 520 521 public void returnValue(Local<?> result) { 522 if (!result.type.equals(method.returnType)) { 523 // TODO: this is probably too strict. 524 throw new IllegalArgumentException("declared " + method.returnType 525 + " but returned " + result.type); 526 } 527 addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition, 528 null, RegisterSpecList.make(result.spec()))); 529 } 530 531 private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) { 532 Rop rop = afterNonInvokeThrowingInsn 533 ? Rops.opMoveResultPseudo(target.type.ropType) 534 : Rops.opMoveResult(target.type.ropType); 535 addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY)); 536 } 537 538 // produce BasicBlocks for dex 539 540 BasicBlockList toBasicBlocks() { 541 if (!localsInitialized) { 542 initializeLocals(); 543 } 544 545 cleanUpLabels(); 546 547 BasicBlockList result = new BasicBlockList(labels.size()); 548 for (int i = 0; i < labels.size(); i++) { 549 result.set(i, labels.get(i).toBasicBlock()); 550 } 551 return result; 552 } 553 554 /** 555 * Removes empty labels and assigns IDs to non-empty labels. 556 */ 557 private void cleanUpLabels() { 558 int id = 0; 559 for (Iterator<Label> i = labels.iterator(); i.hasNext();) { 560 Label label = i.next(); 561 if (label.isEmpty()) { 562 i.remove(); 563 } else { 564 label.compact(); 565 label.id = id++; 566 } 567 } 568 } 569 570 private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { 571 int offset = (first != null) ? 1 : 0; 572 RegisterSpecList result = new RegisterSpecList(offset + rest.length); 573 if (first != null) { 574 result.set(0, first.spec()); 575 } 576 for (int i = 0; i < rest.length; i++) { 577 result.set(i + offset, rest[i].spec()); 578 } 579 return result; 580 } 581} 582