1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.compiler; 17 18 import java.util.ArrayList; 19 import java.util.Arrays; 20 import javassist.compiler.ast.*; 21 import javassist.bytecode.*; 22 23 /* The code generator is implemeted by three files: 24 * CodeGen.java, MemberCodeGen.java, and JvstCodeGen. 25 * I just wanted to split a big file into three smaller ones. 26 */ 27 28 public abstract class CodeGen extends Visitor implements Opcode, TokenId { 29 static final String javaLangObject = "java.lang.Object"; 30 static final String jvmJavaLangObject = "java/lang/Object"; 31 32 static final String javaLangString = "java.lang.String"; 33 static final String jvmJavaLangString = "java/lang/String"; 34 35 protected Bytecode bytecode; 36 private int tempVar; 37 TypeChecker typeChecker; 38 39 /** 40 * true if the last visited node is a return statement. 41 */ 42 protected boolean hasReturned; 43 44 /** 45 * Must be true if compilation is for a static method. 46 */ 47 public boolean inStaticMethod; 48 49 protected ArrayList breakList, continueList; 50 51 /** 52 * doit() in ReturnHook is called from atReturn(). 53 */ 54 protected static abstract class ReturnHook { 55 ReturnHook next; 56 57 /** 58 * Returns true if the generated code ends with return, 59 * throw, or goto. 60 */ 61 protected abstract boolean doit(Bytecode b, int opcode); 62 63 protected ReturnHook(CodeGen gen) { 64 next = gen.returnHooks; 65 gen.returnHooks = this; 66 } 67 68 protected void remove(CodeGen gen) { 69 gen.returnHooks = next; 70 } 71 } 72 73 protected ReturnHook returnHooks; 74 75 /* The following fields are used by atXXX() methods 76 * for returning the type of the compiled expression. 77 */ 78 protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... 79 protected int arrayDim; 80 protected String className; // JVM-internal representation 81 82 public CodeGen(Bytecode b) { 83 bytecode = b; 84 tempVar = -1; 85 typeChecker = null; 86 hasReturned = false; 87 inStaticMethod = false; 88 breakList = null; 89 continueList = null; 90 returnHooks = null; 91 } 92 93 public void setTypeChecker(TypeChecker checker) { 94 typeChecker = checker; 95 } 96 97 protected static void fatal() throws CompileError { 98 throw new CompileError("fatal"); 99 } 100 101 public static boolean is2word(int type, int dim) { 102 return dim == 0 && (type == DOUBLE || type == LONG); 103 } 104 105 public int getMaxLocals() { return bytecode.getMaxLocals(); } 106 107 public void setMaxLocals(int n) { 108 bytecode.setMaxLocals(n); 109 } 110 111 protected void incMaxLocals(int size) { 112 bytecode.incMaxLocals(size); 113 } 114 115 /** 116 * Returns a local variable that single or double words can be 117 * stored in. 118 */ 119 protected int getTempVar() { 120 if (tempVar < 0) { 121 tempVar = getMaxLocals(); 122 incMaxLocals(2); 123 } 124 125 return tempVar; 126 } 127 128 protected int getLocalVar(Declarator d) { 129 int v = d.getLocalVar(); 130 if (v < 0) { 131 v = getMaxLocals(); // delayed variable allocation. 132 d.setLocalVar(v); 133 incMaxLocals(1); 134 } 135 136 return v; 137 } 138 139 /** 140 * Returns the JVM-internal representation of this class name. 141 */ 142 protected abstract String getThisName(); 143 144 /** 145 * Returns the JVM-internal representation of this super class name. 146 */ 147 protected abstract String getSuperName() throws CompileError; 148 149 /* Converts a class name into a JVM-internal representation. 150 * 151 * It may also expand a simple class name to java.lang.*. 152 * For example, this converts Object into java/lang/Object. 153 */ 154 protected abstract String resolveClassName(ASTList name) 155 throws CompileError; 156 157 /* Expands a simple class name to java.lang.*. 158 * For example, this converts Object into java/lang/Object. 159 */ 160 protected abstract String resolveClassName(String jvmClassName) 161 throws CompileError; 162 163 /** 164 * @param name the JVM-internal representation. 165 * name is not exapnded to java.lang.*. 166 */ 167 protected static String toJvmArrayName(String name, int dim) { 168 if (name == null) 169 return null; 170 171 if (dim == 0) 172 return name; 173 else { 174 StringBuffer sbuf = new StringBuffer(); 175 int d = dim; 176 while (d-- > 0) 177 sbuf.append('['); 178 179 sbuf.append('L'); 180 sbuf.append(name); 181 sbuf.append(';'); 182 183 return sbuf.toString(); 184 } 185 } 186 187 protected static String toJvmTypeName(int type, int dim) { 188 char c = 'I'; 189 switch(type) { 190 case BOOLEAN : 191 c = 'Z'; 192 break; 193 case BYTE : 194 c = 'B'; 195 break; 196 case CHAR : 197 c = 'C'; 198 break; 199 case SHORT : 200 c = 'S'; 201 break; 202 case INT : 203 c = 'I'; 204 break; 205 case LONG : 206 c = 'J'; 207 break; 208 case FLOAT : 209 c = 'F'; 210 break; 211 case DOUBLE : 212 c = 'D'; 213 break; 214 case VOID : 215 c = 'V'; 216 break; 217 } 218 219 StringBuffer sbuf = new StringBuffer(); 220 while (dim-- > 0) 221 sbuf.append('['); 222 223 sbuf.append(c); 224 return sbuf.toString(); 225 } 226 227 public void compileExpr(ASTree expr) throws CompileError { 228 doTypeCheck(expr); 229 expr.accept(this); 230 } 231 232 public boolean compileBooleanExpr(boolean branchIf, ASTree expr) 233 throws CompileError 234 { 235 doTypeCheck(expr); 236 return booleanExpr(branchIf, expr); 237 } 238 239 public void doTypeCheck(ASTree expr) throws CompileError { 240 if (typeChecker != null) 241 expr.accept(typeChecker); 242 } 243 244 public void atASTList(ASTList n) throws CompileError { fatal(); } 245 246 public void atPair(Pair n) throws CompileError { fatal(); } 247 248 public void atSymbol(Symbol n) throws CompileError { fatal(); } 249 250 public void atFieldDecl(FieldDecl field) throws CompileError { 251 field.getInit().accept(this); 252 } 253 254 public void atMethodDecl(MethodDecl method) throws CompileError { 255 ASTList mods = method.getModifiers(); 256 setMaxLocals(1); 257 while (mods != null) { 258 Keyword k = (Keyword)mods.head(); 259 mods = mods.tail(); 260 if (k.get() == STATIC) { 261 setMaxLocals(0); 262 inStaticMethod = true; 263 } 264 } 265 266 ASTList params = method.getParams(); 267 while (params != null) { 268 atDeclarator((Declarator)params.head()); 269 params = params.tail(); 270 } 271 272 Stmnt s = method.getBody(); 273 atMethodBody(s, method.isConstructor(), 274 method.getReturn().getType() == VOID); 275 } 276 277 /** 278 * @param isCons true if super() must be called. 279 * false if the method is a class initializer. 280 */ 281 public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) 282 throws CompileError 283 { 284 if (s == null) 285 return; 286 287 if (isCons && needsSuperCall(s)) 288 insertDefaultSuperCall(); 289 290 hasReturned = false; 291 s.accept(this); 292 if (!hasReturned) 293 if (isVoid) { 294 bytecode.addOpcode(Opcode.RETURN); 295 hasReturned = true; 296 } 297 else 298 throw new CompileError("no return statement"); 299 } 300 301 private boolean needsSuperCall(Stmnt body) throws CompileError { 302 if (body.getOperator() == BLOCK) 303 body = (Stmnt)body.head(); 304 305 if (body != null && body.getOperator() == EXPR) { 306 ASTree expr = body.head(); 307 if (expr != null && expr instanceof Expr 308 && ((Expr)expr).getOperator() == CALL) { 309 ASTree target = ((Expr)expr).head(); 310 if (target instanceof Keyword) { 311 int token = ((Keyword)target).get(); 312 return token != THIS && token != SUPER; 313 } 314 } 315 } 316 317 return true; 318 } 319 320 protected abstract void insertDefaultSuperCall() throws CompileError; 321 322 public void atStmnt(Stmnt st) throws CompileError { 323 if (st == null) 324 return; // empty 325 326 int op = st.getOperator(); 327 if (op == EXPR) { 328 ASTree expr = st.getLeft(); 329 doTypeCheck(expr); 330 if (expr instanceof AssignExpr) 331 atAssignExpr((AssignExpr)expr, false); 332 else if (isPlusPlusExpr(expr)) { 333 Expr e = (Expr)expr; 334 atPlusPlus(e.getOperator(), e.oprand1(), e, false); 335 } 336 else { 337 expr.accept(this); 338 if (is2word(exprType, arrayDim)) 339 bytecode.addOpcode(POP2); 340 else if (exprType != VOID) 341 bytecode.addOpcode(POP); 342 } 343 } 344 else if (op == DECL || op == BLOCK) { 345 ASTList list = st; 346 while (list != null) { 347 ASTree h = list.head(); 348 list = list.tail(); 349 if (h != null) 350 h.accept(this); 351 } 352 } 353 else if (op == IF) 354 atIfStmnt(st); 355 else if (op == WHILE || op == DO) 356 atWhileStmnt(st, op == WHILE); 357 else if (op == FOR) 358 atForStmnt(st); 359 else if (op == BREAK || op == CONTINUE) 360 atBreakStmnt(st, op == BREAK); 361 else if (op == TokenId.RETURN) 362 atReturnStmnt(st); 363 else if (op == THROW) 364 atThrowStmnt(st); 365 else if (op == TRY) 366 atTryStmnt(st); 367 else if (op == SWITCH) 368 atSwitchStmnt(st); 369 else if (op == SYNCHRONIZED) 370 atSyncStmnt(st); 371 else { 372 // LABEL, SWITCH label stament might be null?. 373 hasReturned = false; 374 throw new CompileError( 375 "sorry, not supported statement: TokenId " + op); 376 } 377 } 378 379 private void atIfStmnt(Stmnt st) throws CompileError { 380 ASTree expr = st.head(); 381 Stmnt thenp = (Stmnt)st.tail().head(); 382 Stmnt elsep = (Stmnt)st.tail().tail().head(); 383 compileBooleanExpr(false, expr); 384 int pc = bytecode.currentPc(); 385 int pc2 = 0; 386 bytecode.addIndex(0); // correct later 387 388 hasReturned = false; 389 if (thenp != null) 390 thenp.accept(this); 391 392 boolean thenHasReturned = hasReturned; 393 hasReturned = false; 394 395 if (elsep != null && !thenHasReturned) { 396 bytecode.addOpcode(Opcode.GOTO); 397 pc2 = bytecode.currentPc(); 398 bytecode.addIndex(0); 399 } 400 401 bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); 402 403 if (elsep != null) { 404 elsep.accept(this); 405 if (!thenHasReturned) 406 bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); 407 408 hasReturned = thenHasReturned && hasReturned; 409 } 410 } 411 412 private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError { 413 ArrayList prevBreakList = breakList; 414 ArrayList prevContList = continueList; 415 breakList = new ArrayList(); 416 continueList = new ArrayList(); 417 418 ASTree expr = st.head(); 419 Stmnt body = (Stmnt)st.tail(); 420 421 int pc = 0; 422 if (notDo) { 423 bytecode.addOpcode(Opcode.GOTO); 424 pc = bytecode.currentPc(); 425 bytecode.addIndex(0); 426 } 427 428 int pc2 = bytecode.currentPc(); 429 if (body != null) 430 body.accept(this); 431 432 int pc3 = bytecode.currentPc(); 433 if (notDo) 434 bytecode.write16bit(pc, pc3 - pc + 1); 435 436 boolean alwaysBranch = compileBooleanExpr(true, expr); 437 bytecode.addIndex(pc2 - bytecode.currentPc() + 1); 438 439 patchGoto(breakList, bytecode.currentPc()); 440 patchGoto(continueList, pc3); 441 continueList = prevContList; 442 breakList = prevBreakList; 443 hasReturned = alwaysBranch; 444 } 445 446 protected void patchGoto(ArrayList list, int targetPc) { 447 int n = list.size(); 448 for (int i = 0; i < n; ++i) { 449 int pc = ((Integer)list.get(i)).intValue(); 450 bytecode.write16bit(pc, targetPc - pc + 1); 451 } 452 } 453 454 private void atForStmnt(Stmnt st) throws CompileError { 455 ArrayList prevBreakList = breakList; 456 ArrayList prevContList = continueList; 457 breakList = new ArrayList(); 458 continueList = new ArrayList(); 459 460 Stmnt init = (Stmnt)st.head(); 461 ASTList p = st.tail(); 462 ASTree expr = p.head(); 463 p = p.tail(); 464 Stmnt update = (Stmnt)p.head(); 465 Stmnt body = (Stmnt)p.tail(); 466 467 if (init != null) 468 init.accept(this); 469 470 int pc = bytecode.currentPc(); 471 int pc2 = 0; 472 if (expr != null) { 473 compileBooleanExpr(false, expr); 474 pc2 = bytecode.currentPc(); 475 bytecode.addIndex(0); 476 } 477 478 if (body != null) 479 body.accept(this); 480 481 int pc3 = bytecode.currentPc(); 482 if (update != null) 483 update.accept(this); 484 485 bytecode.addOpcode(Opcode.GOTO); 486 bytecode.addIndex(pc - bytecode.currentPc() + 1); 487 488 int pc4 = bytecode.currentPc(); 489 if (expr != null) 490 bytecode.write16bit(pc2, pc4 - pc2 + 1); 491 492 patchGoto(breakList, pc4); 493 patchGoto(continueList, pc3); 494 continueList = prevContList; 495 breakList = prevBreakList; 496 hasReturned = false; 497 } 498 499 private void atSwitchStmnt(Stmnt st) throws CompileError { 500 compileExpr(st.head()); 501 502 ArrayList prevBreakList = breakList; 503 breakList = new ArrayList(); 504 int opcodePc = bytecode.currentPc(); 505 bytecode.addOpcode(LOOKUPSWITCH); 506 int npads = 3 - (opcodePc & 3); 507 while (npads-- > 0) 508 bytecode.add(0); 509 510 Stmnt body = (Stmnt)st.tail(); 511 int npairs = 0; 512 for (ASTList list = body; list != null; list = list.tail()) 513 if (((Stmnt)list.head()).getOperator() == CASE) 514 ++npairs; 515 516 // opcodePc2 is the position at which the default jump offset is. 517 int opcodePc2 = bytecode.currentPc(); 518 bytecode.addGap(4); 519 bytecode.add32bit(npairs); 520 bytecode.addGap(npairs * 8); 521 522 long[] pairs = new long[npairs]; 523 int ipairs = 0; 524 int defaultPc = -1; 525 for (ASTList list = body; list != null; list = list.tail()) { 526 Stmnt label = (Stmnt)list.head(); 527 int op = label.getOperator(); 528 if (op == DEFAULT) 529 defaultPc = bytecode.currentPc(); 530 else if (op != CASE) 531 fatal(); 532 else { 533 pairs[ipairs++] 534 = ((long)computeLabel(label.head()) << 32) + 535 ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff); 536 } 537 538 hasReturned = false; 539 ((Stmnt)label.tail()).accept(this); 540 } 541 542 Arrays.sort(pairs); 543 int pc = opcodePc2 + 8; 544 for (int i = 0; i < npairs; ++i) { 545 bytecode.write32bit(pc, (int)(pairs[i] >>> 32)); 546 bytecode.write32bit(pc + 4, (int)pairs[i]); 547 pc += 8; 548 } 549 550 if (defaultPc < 0 || breakList.size() > 0) 551 hasReturned = false; 552 553 int endPc = bytecode.currentPc(); 554 if (defaultPc < 0) 555 defaultPc = endPc; 556 557 bytecode.write32bit(opcodePc2, defaultPc - opcodePc); 558 559 patchGoto(breakList, endPc); 560 breakList = prevBreakList; 561 } 562 563 private int computeLabel(ASTree expr) throws CompileError { 564 doTypeCheck(expr); 565 expr = TypeChecker.stripPlusExpr(expr); 566 if (expr instanceof IntConst) 567 return (int)((IntConst)expr).get(); 568 else 569 throw new CompileError("bad case label"); 570 } 571 572 private void atBreakStmnt(Stmnt st, boolean notCont) 573 throws CompileError 574 { 575 if (st.head() != null) 576 throw new CompileError( 577 "sorry, not support labeled break or continue"); 578 579 bytecode.addOpcode(Opcode.GOTO); 580 Integer pc = new Integer(bytecode.currentPc()); 581 bytecode.addIndex(0); 582 if (notCont) 583 breakList.add(pc); 584 else 585 continueList.add(pc); 586 } 587 588 protected void atReturnStmnt(Stmnt st) throws CompileError { 589 atReturnStmnt2(st.getLeft()); 590 } 591 592 protected final void atReturnStmnt2(ASTree result) throws CompileError { 593 int op; 594 if (result == null) 595 op = Opcode.RETURN; 596 else { 597 compileExpr(result); 598 if (arrayDim > 0) 599 op = ARETURN; 600 else { 601 int type = exprType; 602 if (type == DOUBLE) 603 op = DRETURN; 604 else if (type == FLOAT) 605 op = FRETURN; 606 else if (type == LONG) 607 op = LRETURN; 608 else if (isRefType(type)) 609 op = ARETURN; 610 else 611 op = IRETURN; 612 } 613 } 614 615 for (ReturnHook har = returnHooks; har != null; har = har.next) 616 if (har.doit(bytecode, op)) { 617 hasReturned = true; 618 return; 619 } 620 621 bytecode.addOpcode(op); 622 hasReturned = true; 623 } 624 625 private void atThrowStmnt(Stmnt st) throws CompileError { 626 ASTree e = st.getLeft(); 627 compileExpr(e); 628 if (exprType != CLASS || arrayDim > 0) 629 throw new CompileError("bad throw statement"); 630 631 bytecode.addOpcode(ATHROW); 632 hasReturned = true; 633 } 634 635 /* overridden in MemberCodeGen 636 */ 637 protected void atTryStmnt(Stmnt st) throws CompileError { 638 hasReturned = false; 639 } 640 641 private void atSyncStmnt(Stmnt st) throws CompileError { 642 int nbreaks = getListSize(breakList); 643 int ncontinues = getListSize(continueList); 644 645 compileExpr(st.head()); 646 if (exprType != CLASS && arrayDim == 0) 647 throw new CompileError("bad type expr for synchronized block"); 648 649 Bytecode bc = bytecode; 650 final int var = bc.getMaxLocals(); 651 bc.incMaxLocals(1); 652 bc.addOpcode(DUP); 653 bc.addAstore(var); 654 bc.addOpcode(MONITORENTER); 655 656 ReturnHook rh = new ReturnHook(this) { 657 protected boolean doit(Bytecode b, int opcode) { 658 b.addAload(var); 659 b.addOpcode(MONITOREXIT); 660 return false; 661 } 662 }; 663 664 int pc = bc.currentPc(); 665 Stmnt body = (Stmnt)st.tail(); 666 if (body != null) 667 body.accept(this); 668 669 int pc2 = bc.currentPc(); 670 int pc3 = 0; 671 if (!hasReturned) { 672 rh.doit(bc, 0); // the 2nd arg is ignored. 673 bc.addOpcode(Opcode.GOTO); 674 pc3 = bc.currentPc(); 675 bc.addIndex(0); 676 } 677 678 if (pc < pc2) { // if the body is not empty 679 int pc4 = bc.currentPc(); 680 rh.doit(bc, 0); // the 2nd arg is ignored. 681 bc.addOpcode(ATHROW); 682 bc.addExceptionHandler(pc, pc2, pc4, 0); 683 } 684 685 if (!hasReturned) 686 bc.write16bit(pc3, bc.currentPc() - pc3 + 1); 687 688 rh.remove(this); 689 690 if (getListSize(breakList) != nbreaks 691 || getListSize(continueList) != ncontinues) 692 throw new CompileError( 693 "sorry, cannot break/continue in synchronized block"); 694 } 695 696 private static int getListSize(ArrayList list) { 697 return list == null ? 0 : list.size(); 698 } 699 700 private static boolean isPlusPlusExpr(ASTree expr) { 701 if (expr instanceof Expr) { 702 int op = ((Expr)expr).getOperator(); 703 return op == PLUSPLUS || op == MINUSMINUS; 704 } 705 706 return false; 707 } 708 709 public void atDeclarator(Declarator d) throws CompileError { 710 d.setLocalVar(getMaxLocals()); 711 d.setClassName(resolveClassName(d.getClassName())); 712 713 int size; 714 if (is2word(d.getType(), d.getArrayDim())) 715 size = 2; 716 else 717 size = 1; 718 719 incMaxLocals(size); 720 721 /* NOTE: Array initializers has not been supported. 722 */ 723 ASTree init = d.getInitializer(); 724 if (init != null) { 725 doTypeCheck(init); 726 atVariableAssign(null, '=', null, d, init, false); 727 } 728 } 729 730 public abstract void atNewExpr(NewExpr n) throws CompileError; 731 732 public abstract void atArrayInit(ArrayInit init) throws CompileError; 733 734 public void atAssignExpr(AssignExpr expr) throws CompileError { 735 atAssignExpr(expr, true); 736 } 737 738 protected void atAssignExpr(AssignExpr expr, boolean doDup) 739 throws CompileError 740 { 741 // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= 742 int op = expr.getOperator(); 743 ASTree left = expr.oprand1(); 744 ASTree right = expr.oprand2(); 745 if (left instanceof Variable) 746 atVariableAssign(expr, op, (Variable)left, 747 ((Variable)left).getDeclarator(), 748 right, doDup); 749 else { 750 if (left instanceof Expr) { 751 Expr e = (Expr)left; 752 if (e.getOperator() == ARRAY) { 753 atArrayAssign(expr, op, (Expr)left, right, doDup); 754 return; 755 } 756 } 757 758 atFieldAssign(expr, op, left, right, doDup); 759 } 760 } 761 762 protected static void badAssign(Expr expr) throws CompileError { 763 String msg; 764 if (expr == null) 765 msg = "incompatible type for assignment"; 766 else 767 msg = "incompatible type for " + expr.getName(); 768 769 throw new CompileError(msg); 770 } 771 772 /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. 773 * 774 * expr and var can be null. 775 */ 776 private void atVariableAssign(Expr expr, int op, Variable var, 777 Declarator d, ASTree right, 778 boolean doDup) throws CompileError 779 { 780 int varType = d.getType(); 781 int varArray = d.getArrayDim(); 782 String varClass = d.getClassName(); 783 int varNo = getLocalVar(d); 784 785 if (op != '=') 786 atVariable(var); 787 788 // expr is null if the caller is atDeclarator(). 789 if (expr == null && right instanceof ArrayInit) 790 atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass); 791 else 792 atAssignCore(expr, op, right, varType, varArray, varClass); 793 794 if (doDup) 795 if (is2word(varType, varArray)) 796 bytecode.addOpcode(DUP2); 797 else 798 bytecode.addOpcode(DUP); 799 800 if (varArray > 0) 801 bytecode.addAstore(varNo); 802 else if (varType == DOUBLE) 803 bytecode.addDstore(varNo); 804 else if (varType == FLOAT) 805 bytecode.addFstore(varNo); 806 else if (varType == LONG) 807 bytecode.addLstore(varNo); 808 else if (isRefType(varType)) 809 bytecode.addAstore(varNo); 810 else 811 bytecode.addIstore(varNo); 812 813 exprType = varType; 814 arrayDim = varArray; 815 className = varClass; 816 } 817 818 protected abstract void atArrayVariableAssign(ArrayInit init, 819 int varType, int varArray, String varClass) throws CompileError; 820 821 private void atArrayAssign(Expr expr, int op, Expr array, 822 ASTree right, boolean doDup) throws CompileError 823 { 824 arrayAccess(array.oprand1(), array.oprand2()); 825 826 if (op != '=') { 827 bytecode.addOpcode(DUP2); 828 bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); 829 } 830 831 int aType = exprType; 832 int aDim = arrayDim; 833 String cname = className; 834 835 atAssignCore(expr, op, right, aType, aDim, cname); 836 837 if (doDup) 838 if (is2word(aType, aDim)) 839 bytecode.addOpcode(DUP2_X2); 840 else 841 bytecode.addOpcode(DUP_X2); 842 843 bytecode.addOpcode(getArrayWriteOp(aType, aDim)); 844 exprType = aType; 845 arrayDim = aDim; 846 className = cname; 847 } 848 849 protected abstract void atFieldAssign(Expr expr, int op, ASTree left, 850 ASTree right, boolean doDup) throws CompileError; 851 852 protected void atAssignCore(Expr expr, int op, ASTree right, 853 int type, int dim, String cname) 854 throws CompileError 855 { 856 if (op == PLUS_E && dim == 0 && type == CLASS) 857 atStringPlusEq(expr, type, dim, cname, right); 858 else { 859 right.accept(this); 860 if (invalidDim(exprType, arrayDim, className, type, dim, cname, 861 false) || (op != '=' && dim > 0)) 862 badAssign(expr); 863 864 if (op != '=') { 865 int token = assignOps[op - MOD_E]; 866 int k = lookupBinOp(token); 867 if (k < 0) 868 fatal(); 869 870 atArithBinExpr(expr, token, k, type); 871 } 872 } 873 874 if (op != '=' || (dim == 0 && !isRefType(type))) 875 atNumCastExpr(exprType, type); 876 877 // type check should be done here. 878 } 879 880 private void atStringPlusEq(Expr expr, int type, int dim, String cname, 881 ASTree right) 882 throws CompileError 883 { 884 if (!jvmJavaLangString.equals(cname)) 885 badAssign(expr); 886 887 convToString(type, dim); // the value might be null. 888 right.accept(this); 889 convToString(exprType, arrayDim); 890 bytecode.addInvokevirtual(javaLangString, "concat", 891 "(Ljava/lang/String;)Ljava/lang/String;"); 892 exprType = CLASS; 893 arrayDim = 0; 894 className = jvmJavaLangString; 895 } 896 897 private boolean invalidDim(int srcType, int srcDim, String srcClass, 898 int destType, int destDim, String destClass, 899 boolean isCast) 900 { 901 if (srcDim != destDim) 902 if (srcType == NULL) 903 return false; 904 else if (destDim == 0 && destType == CLASS 905 && jvmJavaLangObject.equals(destClass)) 906 return false; 907 else if (isCast && srcDim == 0 && srcType == CLASS 908 && jvmJavaLangObject.equals(srcClass)) 909 return false; 910 else 911 return true; 912 913 return false; 914 } 915 916 public void atCondExpr(CondExpr expr) throws CompileError { 917 booleanExpr(false, expr.condExpr()); 918 int pc = bytecode.currentPc(); 919 bytecode.addIndex(0); // correct later 920 expr.thenExpr().accept(this); 921 int dim1 = arrayDim; 922 bytecode.addOpcode(Opcode.GOTO); 923 int pc2 = bytecode.currentPc(); 924 bytecode.addIndex(0); 925 bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); 926 expr.elseExpr().accept(this); 927 if (dim1 != arrayDim) 928 throw new CompileError("type mismatch in ?:"); 929 930 bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); 931 } 932 933 static final int[] binOp = { 934 '+', DADD, FADD, LADD, IADD, 935 '-', DSUB, FSUB, LSUB, ISUB, 936 '*', DMUL, FMUL, LMUL, IMUL, 937 '/', DDIV, FDIV, LDIV, IDIV, 938 '%', DREM, FREM, LREM, IREM, 939 '|', NOP, NOP, LOR, IOR, 940 '^', NOP, NOP, LXOR, IXOR, 941 '&', NOP, NOP, LAND, IAND, 942 LSHIFT, NOP, NOP, LSHL, ISHL, 943 RSHIFT, NOP, NOP, LSHR, ISHR, 944 ARSHIFT, NOP, NOP, LUSHR, IUSHR }; 945 946 static int lookupBinOp(int token) { 947 int[] code = binOp; 948 int s = code.length; 949 for (int k = 0; k < s; k = k + 5) 950 if (code[k] == token) 951 return k; 952 953 return -1; 954 } 955 956 public void atBinExpr(BinExpr expr) throws CompileError { 957 int token = expr.getOperator(); 958 959 /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> 960 */ 961 int k = lookupBinOp(token); 962 if (k >= 0) { 963 expr.oprand1().accept(this); 964 ASTree right = expr.oprand2(); 965 if (right == null) 966 return; // see TypeChecker.atBinExpr(). 967 968 int type1 = exprType; 969 int dim1 = arrayDim; 970 String cname1 = className; 971 right.accept(this); 972 if (dim1 != arrayDim) 973 throw new CompileError("incompatible array types"); 974 975 if (token == '+' && dim1 == 0 976 && (type1 == CLASS || exprType == CLASS)) 977 atStringConcatExpr(expr, type1, dim1, cname1); 978 else 979 atArithBinExpr(expr, token, k, type1); 980 } 981 else { 982 /* equation: &&, ||, ==, !=, <=, >=, <, > 983 */ 984 booleanExpr(true, expr); 985 bytecode.addIndex(7); 986 bytecode.addIconst(0); // false 987 bytecode.addOpcode(Opcode.GOTO); 988 bytecode.addIndex(4); 989 bytecode.addIconst(1); // true 990 } 991 } 992 993 /* arrayDim values of the two oprands must be equal. 994 * If an oprand type is not a numeric type, this method 995 * throws an exception. 996 */ 997 private void atArithBinExpr(Expr expr, int token, 998 int index, int type1) throws CompileError 999 { 1000 if (arrayDim != 0) 1001 badTypes(expr); 1002 1003 int type2 = exprType; 1004 if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) 1005 if (type2 == INT || type2 == SHORT 1006 || type2 == CHAR || type2 == BYTE) 1007 exprType = type1; 1008 else 1009 badTypes(expr); 1010 else 1011 convertOprandTypes(type1, type2, expr); 1012 1013 int p = typePrecedence(exprType); 1014 if (p >= 0) { 1015 int op = binOp[index + p + 1]; 1016 if (op != NOP) { 1017 if (p == P_INT && exprType != BOOLEAN) 1018 exprType = INT; // type1 may be BYTE, ... 1019 1020 bytecode.addOpcode(op); 1021 return; 1022 } 1023 } 1024 1025 badTypes(expr); 1026 } 1027 1028 private void atStringConcatExpr(Expr expr, int type1, int dim1, 1029 String cname1) throws CompileError 1030 { 1031 int type2 = exprType; 1032 int dim2 = arrayDim; 1033 boolean type2Is2 = is2word(type2, dim2); 1034 boolean type2IsString 1035 = (type2 == CLASS && jvmJavaLangString.equals(className)); 1036 1037 if (type2Is2) 1038 convToString(type2, dim2); 1039 1040 if (is2word(type1, dim1)) { 1041 bytecode.addOpcode(DUP_X2); 1042 bytecode.addOpcode(POP); 1043 } 1044 else 1045 bytecode.addOpcode(SWAP); 1046 1047 // even if type1 is String, the left operand might be null. 1048 convToString(type1, dim1); 1049 bytecode.addOpcode(SWAP); 1050 1051 if (!type2Is2 && !type2IsString) 1052 convToString(type2, dim2); 1053 1054 bytecode.addInvokevirtual(javaLangString, "concat", 1055 "(Ljava/lang/String;)Ljava/lang/String;"); 1056 exprType = CLASS; 1057 arrayDim = 0; 1058 className = jvmJavaLangString; 1059 } 1060 1061 private void convToString(int type, int dim) throws CompileError { 1062 final String method = "valueOf"; 1063 1064 if (isRefType(type) || dim > 0) 1065 bytecode.addInvokestatic(javaLangString, method, 1066 "(Ljava/lang/Object;)Ljava/lang/String;"); 1067 else if (type == DOUBLE) 1068 bytecode.addInvokestatic(javaLangString, method, 1069 "(D)Ljava/lang/String;"); 1070 else if (type == FLOAT) 1071 bytecode.addInvokestatic(javaLangString, method, 1072 "(F)Ljava/lang/String;"); 1073 else if (type == LONG) 1074 bytecode.addInvokestatic(javaLangString, method, 1075 "(J)Ljava/lang/String;"); 1076 else if (type == BOOLEAN) 1077 bytecode.addInvokestatic(javaLangString, method, 1078 "(Z)Ljava/lang/String;"); 1079 else if (type == CHAR) 1080 bytecode.addInvokestatic(javaLangString, method, 1081 "(C)Ljava/lang/String;"); 1082 else if (type == VOID) 1083 throw new CompileError("void type expression"); 1084 else /* INT, BYTE, SHORT */ 1085 bytecode.addInvokestatic(javaLangString, method, 1086 "(I)Ljava/lang/String;"); 1087 } 1088 1089 /* Produces the opcode to branch if the condition is true. 1090 * The oprand is not produced. 1091 * 1092 * @return true if the compiled code is GOTO (always branch). 1093 */ 1094 private boolean booleanExpr(boolean branchIf, ASTree expr) 1095 throws CompileError 1096 { 1097 boolean isAndAnd; 1098 int op = getCompOperator(expr); 1099 if (op == EQ) { // ==, !=, ... 1100 BinExpr bexpr = (BinExpr)expr; 1101 int type1 = compileOprands(bexpr); 1102 // here, arrayDim might represent the array dim. of the left oprand 1103 // if the right oprand is NULL. 1104 compareExpr(branchIf, bexpr.getOperator(), type1, bexpr); 1105 } 1106 else if (op == '!') 1107 booleanExpr(!branchIf, ((Expr)expr).oprand1()); 1108 else if ((isAndAnd = (op == ANDAND)) || op == OROR) { 1109 BinExpr bexpr = (BinExpr)expr; 1110 booleanExpr(!isAndAnd, bexpr.oprand1()); 1111 int pc = bytecode.currentPc(); 1112 bytecode.addIndex(0); // correct later 1113 1114 booleanExpr(isAndAnd, bexpr.oprand2()); 1115 bytecode.write16bit(pc, bytecode.currentPc() - pc + 3); 1116 if (branchIf != isAndAnd) { 1117 bytecode.addIndex(6); // skip GOTO instruction 1118 bytecode.addOpcode(Opcode.GOTO); 1119 } 1120 } 1121 else if (isAlwaysBranch(expr, branchIf)) { 1122 bytecode.addOpcode(Opcode.GOTO); 1123 return true; // always branch 1124 } 1125 else { // others 1126 expr.accept(this); 1127 if (exprType != BOOLEAN || arrayDim != 0) 1128 throw new CompileError("boolean expr is required"); 1129 1130 bytecode.addOpcode(branchIf ? IFNE : IFEQ); 1131 } 1132 1133 exprType = BOOLEAN; 1134 arrayDim = 0; 1135 return false; 1136 } 1137 1138 1139 private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) { 1140 if (expr instanceof Keyword) { 1141 int t = ((Keyword)expr).get(); 1142 return branchIf ? t == TRUE : t == FALSE; 1143 } 1144 1145 return false; 1146 } 1147 1148 static int getCompOperator(ASTree expr) throws CompileError { 1149 if (expr instanceof Expr) { 1150 Expr bexpr = (Expr)expr; 1151 int token = bexpr.getOperator(); 1152 if (token == '!') 1153 return '!'; 1154 else if ((bexpr instanceof BinExpr) 1155 && token != OROR && token != ANDAND 1156 && token != '&' && token != '|') 1157 return EQ; // ==, !=, ... 1158 else 1159 return token; 1160 } 1161 1162 return ' '; // others 1163 } 1164 1165 private int compileOprands(BinExpr expr) throws CompileError { 1166 expr.oprand1().accept(this); 1167 int type1 = exprType; 1168 int dim1 = arrayDim; 1169 expr.oprand2().accept(this); 1170 if (dim1 != arrayDim) 1171 if (type1 != NULL && exprType != NULL) 1172 throw new CompileError("incompatible array types"); 1173 else if (exprType == NULL) 1174 arrayDim = dim1; 1175 1176 if (type1 == NULL) 1177 return exprType; 1178 else 1179 return type1; 1180 } 1181 1182 private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, 1183 NEQ, IF_ICMPNE, IF_ICMPEQ, 1184 LE, IF_ICMPLE, IF_ICMPGT, 1185 GE, IF_ICMPGE, IF_ICMPLT, 1186 '<', IF_ICMPLT, IF_ICMPGE, 1187 '>', IF_ICMPGT, IF_ICMPLE }; 1188 1189 private static final int ifOp2[] = { EQ, IFEQ, IFNE, 1190 NEQ, IFNE, IFEQ, 1191 LE, IFLE, IFGT, 1192 GE, IFGE, IFLT, 1193 '<', IFLT, IFGE, 1194 '>', IFGT, IFLE }; 1195 1196 /* Produces the opcode to branch if the condition is true. 1197 * The oprands are not produced. 1198 * 1199 * Parameter expr - compare expression ==, !=, <=, >=, <, > 1200 */ 1201 private void compareExpr(boolean branchIf, 1202 int token, int type1, BinExpr expr) 1203 throws CompileError 1204 { 1205 if (arrayDim == 0) 1206 convertOprandTypes(type1, exprType, expr); 1207 1208 int p = typePrecedence(exprType); 1209 if (p == P_OTHER || arrayDim > 0) 1210 if (token == EQ) 1211 bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE); 1212 else if (token == NEQ) 1213 bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ); 1214 else 1215 badTypes(expr); 1216 else 1217 if (p == P_INT) { 1218 int op[] = ifOp; 1219 for (int i = 0; i < op.length; i += 3) 1220 if (op[i] == token) { 1221 bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); 1222 return; 1223 } 1224 1225 badTypes(expr); 1226 } 1227 else { 1228 if (p == P_DOUBLE) 1229 if (token == '<' || token == LE) 1230 bytecode.addOpcode(DCMPG); 1231 else 1232 bytecode.addOpcode(DCMPL); 1233 else if (p == P_FLOAT) 1234 if (token == '<' || token == LE) 1235 bytecode.addOpcode(FCMPG); 1236 else 1237 bytecode.addOpcode(FCMPL); 1238 else if (p == P_LONG) 1239 bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: < 1240 else 1241 fatal(); 1242 1243 int[] op = ifOp2; 1244 for (int i = 0; i < op.length; i += 3) 1245 if (op[i] == token) { 1246 bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]); 1247 return; 1248 } 1249 1250 badTypes(expr); 1251 } 1252 } 1253 1254 protected static void badTypes(Expr expr) throws CompileError { 1255 throw new CompileError("invalid types for " + expr.getName()); 1256 } 1257 1258 private static final int P_DOUBLE = 0; 1259 private static final int P_FLOAT = 1; 1260 private static final int P_LONG = 2; 1261 private static final int P_INT = 3; 1262 private static final int P_OTHER = -1; 1263 1264 protected static boolean isRefType(int type) { 1265 return type == CLASS || type == NULL; 1266 } 1267 1268 private static int typePrecedence(int type) { 1269 if (type == DOUBLE) 1270 return P_DOUBLE; 1271 else if (type == FLOAT) 1272 return P_FLOAT; 1273 else if (type == LONG) 1274 return P_LONG; 1275 else if (isRefType(type)) 1276 return P_OTHER; 1277 else if (type == VOID) 1278 return P_OTHER; // this is wrong, but ... 1279 else 1280 return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT 1281 } 1282 1283 // used in TypeChecker. 1284 static boolean isP_INT(int type) { 1285 return typePrecedence(type) == P_INT; 1286 } 1287 1288 // used in TypeChecker. 1289 static boolean rightIsStrong(int type1, int type2) { 1290 int type1_p = typePrecedence(type1); 1291 int type2_p = typePrecedence(type2); 1292 return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p; 1293 } 1294 1295 private static final int[] castOp = { 1296 /* D F L I */ 1297 /* double */ NOP, D2F, D2L, D2I, 1298 /* float */ F2D, NOP, F2L, F2I, 1299 /* long */ L2D, L2F, NOP, L2I, 1300 /* other */ I2D, I2F, I2L, NOP }; 1301 1302 /* do implicit type conversion. 1303 * arrayDim values of the two oprands must be zero. 1304 */ 1305 private void convertOprandTypes(int type1, int type2, Expr expr) 1306 throws CompileError 1307 { 1308 boolean rightStrong; 1309 int type1_p = typePrecedence(type1); 1310 int type2_p = typePrecedence(type2); 1311 1312 if (type2_p < 0 && type1_p < 0) // not primitive types 1313 return; 1314 1315 if (type2_p < 0 || type1_p < 0) // either is not a primitive type 1316 badTypes(expr); 1317 1318 int op, result_type; 1319 if (type1_p <= type2_p) { 1320 rightStrong = false; 1321 exprType = type1; 1322 op = castOp[type2_p * 4 + type1_p]; 1323 result_type = type1_p; 1324 } 1325 else { 1326 rightStrong = true; 1327 op = castOp[type1_p * 4 + type2_p]; 1328 result_type = type2_p; 1329 } 1330 1331 if (rightStrong) { 1332 if (result_type == P_DOUBLE || result_type == P_LONG) { 1333 if (type1_p == P_DOUBLE || type1_p == P_LONG) 1334 bytecode.addOpcode(DUP2_X2); 1335 else 1336 bytecode.addOpcode(DUP2_X1); 1337 1338 bytecode.addOpcode(POP2); 1339 bytecode.addOpcode(op); 1340 bytecode.addOpcode(DUP2_X2); 1341 bytecode.addOpcode(POP2); 1342 } 1343 else if (result_type == P_FLOAT) { 1344 if (type1_p == P_LONG) { 1345 bytecode.addOpcode(DUP_X2); 1346 bytecode.addOpcode(POP); 1347 } 1348 else 1349 bytecode.addOpcode(SWAP); 1350 1351 bytecode.addOpcode(op); 1352 bytecode.addOpcode(SWAP); 1353 } 1354 else 1355 fatal(); 1356 } 1357 else if (op != NOP) 1358 bytecode.addOpcode(op); 1359 } 1360 1361 public void atCastExpr(CastExpr expr) throws CompileError { 1362 String cname = resolveClassName(expr.getClassName()); 1363 String toClass = checkCastExpr(expr, cname); 1364 int srcType = exprType; 1365 exprType = expr.getType(); 1366 arrayDim = expr.getArrayDim(); 1367 className = cname; 1368 if (toClass == null) 1369 atNumCastExpr(srcType, exprType); // built-in type 1370 else 1371 bytecode.addCheckcast(toClass); 1372 } 1373 1374 public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { 1375 String cname = resolveClassName(expr.getClassName()); 1376 String toClass = checkCastExpr(expr, cname); 1377 bytecode.addInstanceof(toClass); 1378 exprType = BOOLEAN; 1379 arrayDim = 0; 1380 } 1381 1382 private String checkCastExpr(CastExpr expr, String name) 1383 throws CompileError 1384 { 1385 final String msg = "invalid cast"; 1386 ASTree oprand = expr.getOprand(); 1387 int dim = expr.getArrayDim(); 1388 int type = expr.getType(); 1389 oprand.accept(this); 1390 int srcType = exprType; 1391 if (invalidDim(srcType, arrayDim, className, type, dim, name, true) 1392 || srcType == VOID || type == VOID) 1393 throw new CompileError(msg); 1394 1395 if (type == CLASS) { 1396 if (!isRefType(srcType)) 1397 throw new CompileError(msg); 1398 1399 return toJvmArrayName(name, dim); 1400 } 1401 else 1402 if (dim > 0) 1403 return toJvmTypeName(type, dim); 1404 else 1405 return null; // built-in type 1406 } 1407 1408 void atNumCastExpr(int srcType, int destType) 1409 throws CompileError 1410 { 1411 if (srcType == destType) 1412 return; 1413 1414 int op, op2; 1415 int stype = typePrecedence(srcType); 1416 int dtype = typePrecedence(destType); 1417 if (0 <= stype && stype < 3) 1418 op = castOp[stype * 4 + dtype]; 1419 else 1420 op = NOP; 1421 1422 if (destType == DOUBLE) 1423 op2 = I2D; 1424 else if (destType == FLOAT) 1425 op2 = I2F; 1426 else if (destType == LONG) 1427 op2 = I2L; 1428 else if (destType == SHORT) 1429 op2 = I2S; 1430 else if (destType == CHAR) 1431 op2 = I2C; 1432 else if (destType == BYTE) 1433 op2 = I2B; 1434 else 1435 op2 = NOP; 1436 1437 if (op != NOP) 1438 bytecode.addOpcode(op); 1439 1440 if (op == NOP || op == L2I || op == F2I || op == D2I) 1441 if (op2 != NOP) 1442 bytecode.addOpcode(op2); 1443 } 1444 1445 public void atExpr(Expr expr) throws CompileError { 1446 // array access, member access, 1447 // (unary) +, (unary) -, ++, --, !, ~ 1448 1449 int token = expr.getOperator(); 1450 ASTree oprand = expr.oprand1(); 1451 if (token == '.') { 1452 String member = ((Symbol)expr.oprand2()).get(); 1453 if (member.equals("class")) 1454 atClassObject(expr); // .class 1455 else 1456 atFieldRead(expr); 1457 } 1458 else if (token == MEMBER) { // field read 1459 /* MEMBER ('#') is an extension by Javassist. 1460 * The compiler internally uses # for compiling .class 1461 * expressions such as "int.class". 1462 */ 1463 atFieldRead(expr); 1464 } 1465 else if (token == ARRAY) 1466 atArrayRead(oprand, expr.oprand2()); 1467 else if (token == PLUSPLUS || token == MINUSMINUS) 1468 atPlusPlus(token, oprand, expr, true); 1469 else if (token == '!') { 1470 booleanExpr(false, expr); 1471 bytecode.addIndex(7); 1472 bytecode.addIconst(1); 1473 bytecode.addOpcode(Opcode.GOTO); 1474 bytecode.addIndex(4); 1475 bytecode.addIconst(0); 1476 } 1477 else if (token == CALL) // method call 1478 fatal(); 1479 else { 1480 expr.oprand1().accept(this); 1481 int type = typePrecedence(exprType); 1482 if (arrayDim > 0) 1483 badType(expr); 1484 1485 if (token == '-') { 1486 if (type == P_DOUBLE) 1487 bytecode.addOpcode(DNEG); 1488 else if (type == P_FLOAT) 1489 bytecode.addOpcode(FNEG); 1490 else if (type == P_LONG) 1491 bytecode.addOpcode(LNEG); 1492 else if (type == P_INT) { 1493 bytecode.addOpcode(INEG); 1494 exprType = INT; // type may be BYTE, ... 1495 } 1496 else 1497 badType(expr); 1498 } 1499 else if (token == '~') { 1500 if (type == P_INT) { 1501 bytecode.addIconst(-1); 1502 bytecode.addOpcode(IXOR); 1503 exprType = INT; // type may be BYTE. ... 1504 } 1505 else if (type == P_LONG) { 1506 bytecode.addLconst(-1); 1507 bytecode.addOpcode(LXOR); 1508 } 1509 else 1510 badType(expr); 1511 1512 } 1513 else if (token == '+') { 1514 if (type == P_OTHER) 1515 badType(expr); 1516 1517 // do nothing. ignore. 1518 } 1519 else 1520 fatal(); 1521 } 1522 } 1523 1524 protected static void badType(Expr expr) throws CompileError { 1525 throw new CompileError("invalid type for " + expr.getName()); 1526 } 1527 1528 public abstract void atCallExpr(CallExpr expr) throws CompileError; 1529 1530 protected abstract void atFieldRead(ASTree expr) throws CompileError; 1531 1532 public void atClassObject(Expr expr) throws CompileError { 1533 ASTree op1 = expr.oprand1(); 1534 if (!(op1 instanceof Symbol)) 1535 throw new CompileError("fatal error: badly parsed .class expr"); 1536 1537 String cname = ((Symbol)op1).get(); 1538 if (cname.startsWith("[")) { 1539 int i = cname.indexOf("[L"); 1540 if (i >= 0) { 1541 String name = cname.substring(i + 2, cname.length() - 1); 1542 String name2 = resolveClassName(name); 1543 if (!name.equals(name2)) { 1544 /* For example, to obtain String[].class, 1545 * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) 1546 * must be passed to Class.forName(). 1547 */ 1548 name2 = MemberResolver.jvmToJavaName(name2); 1549 StringBuffer sbuf = new StringBuffer(); 1550 while (i-- >= 0) 1551 sbuf.append('['); 1552 1553 sbuf.append('L').append(name2).append(';'); 1554 cname = sbuf.toString(); 1555 } 1556 } 1557 } 1558 else { 1559 cname = resolveClassName(MemberResolver.javaToJvmName(cname)); 1560 cname = MemberResolver.jvmToJavaName(cname); 1561 } 1562 1563 atClassObject2(cname); 1564 exprType = CLASS; 1565 arrayDim = 0; 1566 className = "java/lang/Class"; 1567 } 1568 1569 /* MemberCodeGen overrides this method. 1570 */ 1571 protected void atClassObject2(String cname) throws CompileError { 1572 int start = bytecode.currentPc(); 1573 bytecode.addLdc(cname); 1574 bytecode.addInvokestatic("java.lang.Class", "forName", 1575 "(Ljava/lang/String;)Ljava/lang/Class;"); 1576 int end = bytecode.currentPc(); 1577 bytecode.addOpcode(Opcode.GOTO); 1578 int pc = bytecode.currentPc(); 1579 bytecode.addIndex(0); // correct later 1580 1581 bytecode.addExceptionHandler(start, end, bytecode.currentPc(), 1582 "java.lang.ClassNotFoundException"); 1583 1584 /* -- the following code is for inlining a call to DotClass.fail(). 1585 1586 int var = getMaxLocals(); 1587 incMaxLocals(1); 1588 bytecode.growStack(1); 1589 bytecode.addAstore(var); 1590 1591 bytecode.addNew("java.lang.NoClassDefFoundError"); 1592 bytecode.addOpcode(DUP); 1593 bytecode.addAload(var); 1594 bytecode.addInvokevirtual("java.lang.ClassNotFoundException", 1595 "getMessage", "()Ljava/lang/String;"); 1596 bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "<init>", 1597 "(Ljava/lang/String;)V"); 1598 */ 1599 1600 bytecode.growStack(1); 1601 bytecode.addInvokestatic("javassist.runtime.DotClass", "fail", 1602 "(Ljava/lang/ClassNotFoundException;)" 1603 + "Ljava/lang/NoClassDefFoundError;"); 1604 bytecode.addOpcode(ATHROW); 1605 bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); 1606 } 1607 1608 public void atArrayRead(ASTree array, ASTree index) 1609 throws CompileError 1610 { 1611 arrayAccess(array, index); 1612 bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); 1613 } 1614 1615 protected void arrayAccess(ASTree array, ASTree index) 1616 throws CompileError 1617 { 1618 array.accept(this); 1619 int type = exprType; 1620 int dim = arrayDim; 1621 if (dim == 0) 1622 throw new CompileError("bad array access"); 1623 1624 String cname = className; 1625 1626 index.accept(this); 1627 if (typePrecedence(exprType) != P_INT || arrayDim > 0) 1628 throw new CompileError("bad array index"); 1629 1630 exprType = type; 1631 arrayDim = dim - 1; 1632 className = cname; 1633 } 1634 1635 protected static int getArrayReadOp(int type, int dim) { 1636 if (dim > 0) 1637 return AALOAD; 1638 1639 switch (type) { 1640 case DOUBLE : 1641 return DALOAD; 1642 case FLOAT : 1643 return FALOAD; 1644 case LONG : 1645 return LALOAD; 1646 case INT : 1647 return IALOAD; 1648 case SHORT : 1649 return SALOAD; 1650 case CHAR : 1651 return CALOAD; 1652 case BYTE : 1653 case BOOLEAN : 1654 return BALOAD; 1655 default : 1656 return AALOAD; 1657 } 1658 } 1659 1660 protected static int getArrayWriteOp(int type, int dim) { 1661 if (dim > 0) 1662 return AASTORE; 1663 1664 switch (type) { 1665 case DOUBLE : 1666 return DASTORE; 1667 case FLOAT : 1668 return FASTORE; 1669 case LONG : 1670 return LASTORE; 1671 case INT : 1672 return IASTORE; 1673 case SHORT : 1674 return SASTORE; 1675 case CHAR : 1676 return CASTORE; 1677 case BYTE : 1678 case BOOLEAN : 1679 return BASTORE; 1680 default : 1681 return AASTORE; 1682 } 1683 } 1684 1685 private void atPlusPlus(int token, ASTree oprand, Expr expr, 1686 boolean doDup) throws CompileError 1687 { 1688 boolean isPost = oprand == null; // ++i or i++? 1689 if (isPost) 1690 oprand = expr.oprand2(); 1691 1692 if (oprand instanceof Variable) { 1693 Declarator d = ((Variable)oprand).getDeclarator(); 1694 int t = exprType = d.getType(); 1695 arrayDim = d.getArrayDim(); 1696 int var = getLocalVar(d); 1697 if (arrayDim > 0) 1698 badType(expr); 1699 1700 if (t == DOUBLE) { 1701 bytecode.addDload(var); 1702 if (doDup && isPost) 1703 bytecode.addOpcode(DUP2); 1704 1705 bytecode.addDconst(1.0); 1706 bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); 1707 if (doDup && !isPost) 1708 bytecode.addOpcode(DUP2); 1709 1710 bytecode.addDstore(var); 1711 } 1712 else if (t == LONG) { 1713 bytecode.addLload(var); 1714 if (doDup && isPost) 1715 bytecode.addOpcode(DUP2); 1716 1717 bytecode.addLconst((long)1); 1718 bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); 1719 if (doDup && !isPost) 1720 bytecode.addOpcode(DUP2); 1721 1722 bytecode.addLstore(var); 1723 } 1724 else if (t == FLOAT) { 1725 bytecode.addFload(var); 1726 if (doDup && isPost) 1727 bytecode.addOpcode(DUP); 1728 1729 bytecode.addFconst(1.0f); 1730 bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); 1731 if (doDup && !isPost) 1732 bytecode.addOpcode(DUP); 1733 1734 bytecode.addFstore(var); 1735 } 1736 else if (t == BYTE || t == CHAR || t == SHORT || t == INT) { 1737 if (doDup && isPost) 1738 bytecode.addIload(var); 1739 1740 int delta = token == PLUSPLUS ? 1 : -1; 1741 if (var > 0xff) { 1742 bytecode.addOpcode(WIDE); 1743 bytecode.addOpcode(IINC); 1744 bytecode.addIndex(var); 1745 bytecode.addIndex(delta); 1746 } 1747 else { 1748 bytecode.addOpcode(IINC); 1749 bytecode.add(var); 1750 bytecode.add(delta); 1751 } 1752 1753 if (doDup && !isPost) 1754 bytecode.addIload(var); 1755 } 1756 else 1757 badType(expr); 1758 } 1759 else { 1760 if (oprand instanceof Expr) { 1761 Expr e = (Expr)oprand; 1762 if (e.getOperator() == ARRAY) { 1763 atArrayPlusPlus(token, isPost, e, doDup); 1764 return; 1765 } 1766 } 1767 1768 atFieldPlusPlus(token, isPost, oprand, expr, doDup); 1769 } 1770 } 1771 1772 public void atArrayPlusPlus(int token, boolean isPost, 1773 Expr expr, boolean doDup) throws CompileError 1774 { 1775 arrayAccess(expr.oprand1(), expr.oprand2()); 1776 int t = exprType; 1777 int dim = arrayDim; 1778 if (dim > 0) 1779 badType(expr); 1780 1781 bytecode.addOpcode(DUP2); 1782 bytecode.addOpcode(getArrayReadOp(t, arrayDim)); 1783 int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2; 1784 atPlusPlusCore(dup_code, doDup, token, isPost, expr); 1785 bytecode.addOpcode(getArrayWriteOp(t, dim)); 1786 } 1787 1788 protected void atPlusPlusCore(int dup_code, boolean doDup, 1789 int token, boolean isPost, 1790 Expr expr) throws CompileError 1791 { 1792 int t = exprType; 1793 1794 if (doDup && isPost) 1795 bytecode.addOpcode(dup_code); 1796 1797 if (t == INT || t == BYTE || t == CHAR || t == SHORT) { 1798 bytecode.addIconst(1); 1799 bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB); 1800 exprType = INT; 1801 } 1802 else if (t == LONG) { 1803 bytecode.addLconst((long)1); 1804 bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB); 1805 } 1806 else if (t == FLOAT) { 1807 bytecode.addFconst(1.0f); 1808 bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB); 1809 } 1810 else if (t == DOUBLE) { 1811 bytecode.addDconst(1.0); 1812 bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB); 1813 } 1814 else 1815 badType(expr); 1816 1817 if (doDup && !isPost) 1818 bytecode.addOpcode(dup_code); 1819 } 1820 1821 protected abstract void atFieldPlusPlus(int token, boolean isPost, 1822 ASTree oprand, Expr expr, boolean doDup) throws CompileError; 1823 1824 public abstract void atMember(Member n) throws CompileError; 1825 1826 public void atVariable(Variable v) throws CompileError { 1827 Declarator d = v.getDeclarator(); 1828 exprType = d.getType(); 1829 arrayDim = d.getArrayDim(); 1830 className = d.getClassName(); 1831 int var = getLocalVar(d); 1832 1833 if (arrayDim > 0) 1834 bytecode.addAload(var); 1835 else 1836 switch (exprType) { 1837 case CLASS : 1838 bytecode.addAload(var); 1839 break; 1840 case LONG : 1841 bytecode.addLload(var); 1842 break; 1843 case FLOAT : 1844 bytecode.addFload(var); 1845 break; 1846 case DOUBLE : 1847 bytecode.addDload(var); 1848 break; 1849 default : // BOOLEAN, BYTE, CHAR, SHORT, INT 1850 bytecode.addIload(var); 1851 break; 1852 } 1853 } 1854 1855 public void atKeyword(Keyword k) throws CompileError { 1856 arrayDim = 0; 1857 int token = k.get(); 1858 switch (token) { 1859 case TRUE : 1860 bytecode.addIconst(1); 1861 exprType = BOOLEAN; 1862 break; 1863 case FALSE : 1864 bytecode.addIconst(0); 1865 exprType = BOOLEAN; 1866 break; 1867 case NULL : 1868 bytecode.addOpcode(ACONST_NULL); 1869 exprType = NULL; 1870 break; 1871 case THIS : 1872 case SUPER : 1873 if (inStaticMethod) 1874 throw new CompileError("not-available: " 1875 + (token == THIS ? "this" : "super")); 1876 1877 bytecode.addAload(0); 1878 exprType = CLASS; 1879 if (token == THIS) 1880 className = getThisName(); 1881 else 1882 className = getSuperName(); 1883 break; 1884 default : 1885 fatal(); 1886 } 1887 } 1888 1889 public void atStringL(StringL s) throws CompileError { 1890 exprType = CLASS; 1891 arrayDim = 0; 1892 className = jvmJavaLangString; 1893 bytecode.addLdc(s.get()); 1894 } 1895 1896 public void atIntConst(IntConst i) throws CompileError { 1897 arrayDim = 0; 1898 long value = i.get(); 1899 int type = i.getType(); 1900 if (type == IntConstant || type == CharConstant) { 1901 exprType = (type == IntConstant ? INT : CHAR); 1902 bytecode.addIconst((int)value); 1903 } 1904 else { 1905 exprType = LONG; 1906 bytecode.addLconst(value); 1907 } 1908 } 1909 1910 public void atDoubleConst(DoubleConst d) throws CompileError { 1911 arrayDim = 0; 1912 if (d.getType() == DoubleConstant) { 1913 exprType = DOUBLE; 1914 bytecode.addDconst(d.get()); 1915 } 1916 else { 1917 exprType = FLOAT; 1918 bytecode.addFconst((float)d.get()); 1919 } 1920 } 1921 } 1922