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