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 */ doit(Bytecode b, int opcode)61 protected abstract boolean doit(Bytecode b, int opcode); 62 ReturnHook(CodeGen gen)63 protected ReturnHook(CodeGen gen) { 64 next = gen.returnHooks; 65 gen.returnHooks = this; 66 } 67 remove(CodeGen gen)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 CodeGen(Bytecode b)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 setTypeChecker(TypeChecker checker)93 public void setTypeChecker(TypeChecker checker) { 94 typeChecker = checker; 95 } 96 fatal()97 protected static void fatal() throws CompileError { 98 throw new CompileError("fatal"); 99 } 100 is2word(int type, int dim)101 public static boolean is2word(int type, int dim) { 102 return dim == 0 && (type == DOUBLE || type == LONG); 103 } 104 getMaxLocals()105 public int getMaxLocals() { return bytecode.getMaxLocals(); } 106 setMaxLocals(int n)107 public void setMaxLocals(int n) { 108 bytecode.setMaxLocals(n); 109 } 110 incMaxLocals(int size)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 */ getTempVar()119 protected int getTempVar() { 120 if (tempVar < 0) { 121 tempVar = getMaxLocals(); 122 incMaxLocals(2); 123 } 124 125 return tempVar; 126 } 127 getLocalVar(Declarator d)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 */ getThisName()142 protected abstract String getThisName(); 143 144 /** 145 * Returns the JVM-internal representation of this super class name. 146 */ getSuperName()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 */ resolveClassName(ASTList name)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 */ resolveClassName(String jvmClassName)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 */ toJvmArrayName(String name, int dim)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 toJvmTypeName(int type, int dim)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 compileExpr(ASTree expr)227 public void compileExpr(ASTree expr) throws CompileError { 228 doTypeCheck(expr); 229 expr.accept(this); 230 } 231 compileBooleanExpr(boolean branchIf, ASTree expr)232 public boolean compileBooleanExpr(boolean branchIf, ASTree expr) 233 throws CompileError 234 { 235 doTypeCheck(expr); 236 return booleanExpr(branchIf, expr); 237 } 238 doTypeCheck(ASTree expr)239 public void doTypeCheck(ASTree expr) throws CompileError { 240 if (typeChecker != null) 241 expr.accept(typeChecker); 242 } 243 atASTList(ASTList n)244 public void atASTList(ASTList n) throws CompileError { fatal(); } 245 atPair(Pair n)246 public void atPair(Pair n) throws CompileError { fatal(); } 247 atSymbol(Symbol n)248 public void atSymbol(Symbol n) throws CompileError { fatal(); } 249 atFieldDecl(FieldDecl field)250 public void atFieldDecl(FieldDecl field) throws CompileError { 251 field.getInit().accept(this); 252 } 253 atMethodDecl(MethodDecl method)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 */ atMethodBody(Stmnt s, boolean isCons, boolean isVoid)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 needsSuperCall(Stmnt body)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 insertDefaultSuperCall()320 protected abstract void insertDefaultSuperCall() throws CompileError; 321 atStmnt(Stmnt st)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 atIfStmnt(Stmnt st)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 atWhileStmnt(Stmnt st, boolean notDo)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 patchGoto(ArrayList list, int targetPc)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 atForStmnt(Stmnt st)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 atSwitchStmnt(Stmnt st)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 computeLabel(ASTree expr)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 atBreakStmnt(Stmnt st, boolean notCont)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 atReturnStmnt(Stmnt st)588 protected void atReturnStmnt(Stmnt st) throws CompileError { 589 atReturnStmnt2(st.getLeft()); 590 } 591 atReturnStmnt2(ASTree result)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 atThrowStmnt(Stmnt st)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 */ atTryStmnt(Stmnt st)637 protected void atTryStmnt(Stmnt st) throws CompileError { 638 hasReturned = false; 639 } 640 atSyncStmnt(Stmnt st)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 getListSize(ArrayList list)696 private static int getListSize(ArrayList list) { 697 return list == null ? 0 : list.size(); 698 } 699 isPlusPlusExpr(ASTree expr)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 atDeclarator(Declarator d)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 atNewExpr(NewExpr n)730 public abstract void atNewExpr(NewExpr n) throws CompileError; 731 atArrayInit(ArrayInit init)732 public abstract void atArrayInit(ArrayInit init) throws CompileError; 733 atAssignExpr(AssignExpr expr)734 public void atAssignExpr(AssignExpr expr) throws CompileError { 735 atAssignExpr(expr, true); 736 } 737 atAssignExpr(AssignExpr expr, boolean doDup)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 badAssign(Expr expr)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 */ atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right, boolean doDup)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 atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass)818 protected abstract void atArrayVariableAssign(ArrayInit init, 819 int varType, int varArray, String varClass) throws CompileError; 820 atArrayAssign(Expr expr, int op, Expr array, ASTree right, boolean doDup)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 atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup)849 protected abstract void atFieldAssign(Expr expr, int op, ASTree left, 850 ASTree right, boolean doDup) throws CompileError; 851 atAssignCore(Expr expr, int op, ASTree right, int type, int dim, String cname)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 atStringPlusEq(Expr expr, int type, int dim, String cname, ASTree right)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 invalidDim(int srcType, int srcDim, String srcClass, int destType, int destDim, String destClass, boolean isCast)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 atCondExpr(CondExpr expr)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 lookupBinOp(int token)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 atBinExpr(BinExpr expr)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 */ atArithBinExpr(Expr expr, int token, int index, int type1)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 atStringConcatExpr(Expr expr, int type1, int dim1, String cname1)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 convToString(int type, int dim)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 */ booleanExpr(boolean branchIf, ASTree expr)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 isAlwaysBranch(ASTree expr, boolean branchIf)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 getCompOperator(ASTree expr)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 compileOprands(BinExpr expr)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 */ compareExpr(boolean branchIf, int token, int type1, BinExpr expr)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 badTypes(Expr expr)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 isRefType(int type)1264 protected static boolean isRefType(int type) { 1265 return type == CLASS || type == NULL; 1266 } 1267 typePrecedence(int type)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. isP_INT(int type)1284 static boolean isP_INT(int type) { 1285 return typePrecedence(type) == P_INT; 1286 } 1287 1288 // used in TypeChecker. rightIsStrong(int type1, int type2)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 */ convertOprandTypes(int type1, int type2, Expr expr)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 atCastExpr(CastExpr expr)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 atInstanceOfExpr(InstanceOfExpr expr)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 checkCastExpr(CastExpr expr, String name)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 atNumCastExpr(int srcType, int destType)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 atExpr(Expr expr)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 badType(Expr expr)1524 protected static void badType(Expr expr) throws CompileError { 1525 throw new CompileError("invalid type for " + expr.getName()); 1526 } 1527 atCallExpr(CallExpr expr)1528 public abstract void atCallExpr(CallExpr expr) throws CompileError; 1529 atFieldRead(ASTree expr)1530 protected abstract void atFieldRead(ASTree expr) throws CompileError; 1531 atClassObject(Expr expr)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 */ atClassObject2(String cname)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 atArrayRead(ASTree array, ASTree index)1608 public void atArrayRead(ASTree array, ASTree index) 1609 throws CompileError 1610 { 1611 arrayAccess(array, index); 1612 bytecode.addOpcode(getArrayReadOp(exprType, arrayDim)); 1613 } 1614 arrayAccess(ASTree array, ASTree index)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 getArrayReadOp(int type, int dim)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 getArrayWriteOp(int type, int dim)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 atPlusPlus(int token, ASTree oprand, Expr expr, boolean doDup)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 atArrayPlusPlus(int token, boolean isPost, Expr expr, boolean doDup)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 atPlusPlusCore(int dup_code, boolean doDup, int token, boolean isPost, Expr expr)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 atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup)1821 protected abstract void atFieldPlusPlus(int token, boolean isPost, 1822 ASTree oprand, Expr expr, boolean doDup) throws CompileError; 1823 atMember(Member n)1824 public abstract void atMember(Member n) throws CompileError; 1825 atVariable(Variable v)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 atKeyword(Keyword k)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 atStringL(StringL s)1889 public void atStringL(StringL s) throws CompileError { 1890 exprType = CLASS; 1891 arrayDim = 0; 1892 className = jvmJavaLangString; 1893 bytecode.addLdc(s.get()); 1894 } 1895 atIntConst(IntConst i)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 atDoubleConst(DoubleConst d)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