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 javassist.*; 19 import javassist.bytecode.*; 20 import javassist.compiler.ast.*; 21 22 import java.util.ArrayList; 23 24 /* Code generator methods depending on javassist.* classes. 25 */ 26 public class MemberCodeGen extends CodeGen { 27 protected MemberResolver resolver; 28 protected CtClass thisClass; 29 protected MethodInfo thisMethod; 30 31 protected boolean resultStatic; 32 33 public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { 34 super(b); 35 resolver = new MemberResolver(cp); 36 thisClass = cc; 37 thisMethod = null; 38 } 39 40 /** 41 * Returns the major version of the class file 42 * targeted by this compilation. 43 */ 44 public int getMajorVersion() { 45 ClassFile cf = thisClass.getClassFile2(); 46 if (cf == null) 47 return ClassFile.MAJOR_VERSION; // JDK 1.3 48 else 49 return cf.getMajorVersion(); 50 } 51 52 /** 53 * Records the currently compiled method. 54 */ 55 public void setThisMethod(CtMethod m) { 56 thisMethod = m.getMethodInfo2(); 57 if (typeChecker != null) 58 typeChecker.setThisMethod(thisMethod); 59 } 60 61 public CtClass getThisClass() { return thisClass; } 62 63 /** 64 * Returns the JVM-internal representation of this class name. 65 */ 66 protected String getThisName() { 67 return MemberResolver.javaToJvmName(thisClass.getName()); 68 } 69 70 /** 71 * Returns the JVM-internal representation of this super class name. 72 */ 73 protected String getSuperName() throws CompileError { 74 return MemberResolver.javaToJvmName( 75 MemberResolver.getSuperclass(thisClass).getName()); 76 } 77 78 protected void insertDefaultSuperCall() throws CompileError { 79 bytecode.addAload(0); 80 bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), 81 "<init>", "()V"); 82 } 83 84 static class JsrHook extends ReturnHook { 85 ArrayList jsrList; 86 CodeGen cgen; 87 int var; 88 89 JsrHook(CodeGen gen) { 90 super(gen); 91 jsrList = new ArrayList(); 92 cgen = gen; 93 var = -1; 94 } 95 96 private int getVar(int size) { 97 if (var < 0) { 98 var = cgen.getMaxLocals(); 99 cgen.incMaxLocals(size); 100 } 101 102 return var; 103 } 104 105 private void jsrJmp(Bytecode b) { 106 b.addOpcode(Opcode.GOTO); 107 jsrList.add(new int[] {b.currentPc(), var}); 108 b.addIndex(0); 109 } 110 111 protected boolean doit(Bytecode b, int opcode) { 112 switch (opcode) { 113 case Opcode.RETURN : 114 jsrJmp(b); 115 break; 116 case ARETURN : 117 b.addAstore(getVar(1)); 118 jsrJmp(b); 119 b.addAload(var); 120 break; 121 case IRETURN : 122 b.addIstore(getVar(1)); 123 jsrJmp(b); 124 b.addIload(var); 125 break; 126 case LRETURN : 127 b.addLstore(getVar(2)); 128 jsrJmp(b); 129 b.addLload(var); 130 break; 131 case DRETURN : 132 b.addDstore(getVar(2)); 133 jsrJmp(b); 134 b.addDload(var); 135 break; 136 case FRETURN : 137 b.addFstore(getVar(1)); 138 jsrJmp(b); 139 b.addFload(var); 140 break; 141 default : 142 throw new RuntimeException("fatal"); 143 } 144 145 return false; 146 } 147 } 148 149 static class JsrHook2 extends ReturnHook { 150 int var; 151 int target; 152 153 JsrHook2(CodeGen gen, int[] retTarget) { 154 super(gen); 155 target = retTarget[0]; 156 var = retTarget[1]; 157 } 158 159 protected boolean doit(Bytecode b, int opcode) { 160 switch (opcode) { 161 case Opcode.RETURN : 162 break; 163 case ARETURN : 164 b.addAstore(var); 165 break; 166 case IRETURN : 167 b.addIstore(var); 168 break; 169 case LRETURN : 170 b.addLstore(var); 171 break; 172 case DRETURN : 173 b.addDstore(var); 174 break; 175 case FRETURN : 176 b.addFstore(var); 177 break; 178 default : 179 throw new RuntimeException("fatal"); 180 } 181 182 b.addOpcode(Opcode.GOTO); 183 b.addIndex(target - b.currentPc() + 3); 184 return true; 185 } 186 } 187 188 protected void atTryStmnt(Stmnt st) throws CompileError { 189 Bytecode bc = bytecode; 190 Stmnt body = (Stmnt)st.getLeft(); 191 if (body == null) 192 return; 193 194 ASTList catchList = (ASTList)st.getRight().getLeft(); 195 Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft(); 196 ArrayList gotoList = new ArrayList(); 197 198 JsrHook jsrHook = null; 199 if (finallyBlock != null) 200 jsrHook = new JsrHook(this); 201 202 int start = bc.currentPc(); 203 body.accept(this); 204 int end = bc.currentPc(); 205 if (start == end) 206 throw new CompileError("empty try block"); 207 208 boolean tryNotReturn = !hasReturned; 209 if (tryNotReturn) { 210 bc.addOpcode(Opcode.GOTO); 211 gotoList.add(new Integer(bc.currentPc())); 212 bc.addIndex(0); // correct later 213 } 214 215 int var = getMaxLocals(); 216 incMaxLocals(1); 217 while (catchList != null) { 218 // catch clause 219 Pair p = (Pair)catchList.head(); 220 catchList = catchList.tail(); 221 Declarator decl = (Declarator)p.getLeft(); 222 Stmnt block = (Stmnt)p.getRight(); 223 224 decl.setLocalVar(var); 225 226 CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); 227 decl.setClassName(MemberResolver.javaToJvmName(type.getName())); 228 bc.addExceptionHandler(start, end, bc.currentPc(), type); 229 bc.growStack(1); 230 bc.addAstore(var); 231 hasReturned = false; 232 if (block != null) 233 block.accept(this); 234 235 if (!hasReturned) { 236 bc.addOpcode(Opcode.GOTO); 237 gotoList.add(new Integer(bc.currentPc())); 238 bc.addIndex(0); // correct later 239 tryNotReturn = true; 240 } 241 } 242 243 if (finallyBlock != null) { 244 jsrHook.remove(this); 245 // catch (any) clause 246 int pcAnyCatch = bc.currentPc(); 247 bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); 248 bc.growStack(1); 249 bc.addAstore(var); 250 hasReturned = false; 251 finallyBlock.accept(this); 252 if (!hasReturned) { 253 bc.addAload(var); 254 bc.addOpcode(ATHROW); 255 } 256 257 addFinally(jsrHook.jsrList, finallyBlock); 258 } 259 260 int pcEnd = bc.currentPc(); 261 patchGoto(gotoList, pcEnd); 262 hasReturned = !tryNotReturn; 263 if (finallyBlock != null) { 264 if (tryNotReturn) 265 finallyBlock.accept(this); 266 } 267 } 268 269 /** 270 * Adds a finally clause for earch return statement. 271 */ 272 private void addFinally(ArrayList returnList, Stmnt finallyBlock) 273 throws CompileError 274 { 275 Bytecode bc = bytecode; 276 int n = returnList.size(); 277 for (int i = 0; i < n; ++i) { 278 final int[] ret = (int[])returnList.get(i); 279 int pc = ret[0]; 280 bc.write16bit(pc, bc.currentPc() - pc + 1); 281 ReturnHook hook = new JsrHook2(this, ret); 282 finallyBlock.accept(this); 283 hook.remove(this); 284 if (!hasReturned) { 285 bc.addOpcode(Opcode.GOTO); 286 bc.addIndex(pc + 3 - bc.currentPc()); 287 } 288 } 289 } 290 291 public void atNewExpr(NewExpr expr) throws CompileError { 292 if (expr.isArray()) 293 atNewArrayExpr(expr); 294 else { 295 CtClass clazz = resolver.lookupClassByName(expr.getClassName()); 296 String cname = clazz.getName(); 297 ASTList args = expr.getArguments(); 298 bytecode.addNew(cname); 299 bytecode.addOpcode(DUP); 300 301 atMethodCallCore(clazz, MethodInfo.nameInit, args, 302 false, true, -1, null); 303 304 exprType = CLASS; 305 arrayDim = 0; 306 className = MemberResolver.javaToJvmName(cname); 307 } 308 } 309 310 public void atNewArrayExpr(NewExpr expr) throws CompileError { 311 int type = expr.getArrayType(); 312 ASTList size = expr.getArraySize(); 313 ASTList classname = expr.getClassName(); 314 ArrayInit init = expr.getInitializer(); 315 if (size.length() > 1) { 316 if (init != null) 317 throw new CompileError( 318 "sorry, multi-dimensional array initializer " + 319 "for new is not supported"); 320 321 atMultiNewArray(type, classname, size); 322 return; 323 } 324 325 ASTree sizeExpr = size.head(); 326 atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); 327 } 328 329 private void atNewArrayExpr2(int type, ASTree sizeExpr, 330 String jvmClassname, ArrayInit init) throws CompileError { 331 if (init == null) 332 if (sizeExpr == null) 333 throw new CompileError("no array size"); 334 else 335 sizeExpr.accept(this); 336 else 337 if (sizeExpr == null) { 338 int s = init.length(); 339 bytecode.addIconst(s); 340 } 341 else 342 throw new CompileError("unnecessary array size specified for new"); 343 344 String elementClass; 345 if (type == CLASS) { 346 elementClass = resolveClassName(jvmClassname); 347 bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); 348 } 349 else { 350 elementClass = null; 351 int atype = 0; 352 switch (type) { 353 case BOOLEAN : 354 atype = T_BOOLEAN; 355 break; 356 case CHAR : 357 atype = T_CHAR; 358 break; 359 case FLOAT : 360 atype = T_FLOAT; 361 break; 362 case DOUBLE : 363 atype = T_DOUBLE; 364 break; 365 case BYTE : 366 atype = T_BYTE; 367 break; 368 case SHORT : 369 atype = T_SHORT; 370 break; 371 case INT : 372 atype = T_INT; 373 break; 374 case LONG : 375 atype = T_LONG; 376 break; 377 default : 378 badNewExpr(); 379 break; 380 } 381 382 bytecode.addOpcode(NEWARRAY); 383 bytecode.add(atype); 384 } 385 386 if (init != null) { 387 int s = init.length(); 388 ASTList list = init; 389 for (int i = 0; i < s; i++) { 390 bytecode.addOpcode(DUP); 391 bytecode.addIconst(i); 392 list.head().accept(this); 393 if (!isRefType(type)) 394 atNumCastExpr(exprType, type); 395 396 bytecode.addOpcode(getArrayWriteOp(type, 0)); 397 list = list.tail(); 398 } 399 } 400 401 exprType = type; 402 arrayDim = 1; 403 className = elementClass; 404 } 405 406 private static void badNewExpr() throws CompileError { 407 throw new CompileError("bad new expression"); 408 } 409 410 protected void atArrayVariableAssign(ArrayInit init, int varType, 411 int varArray, String varClass) throws CompileError { 412 atNewArrayExpr2(varType, null, varClass, init); 413 } 414 415 public void atArrayInit(ArrayInit init) throws CompileError { 416 throw new CompileError("array initializer is not supported"); 417 } 418 419 protected void atMultiNewArray(int type, ASTList classname, ASTList size) 420 throws CompileError 421 { 422 int count, dim; 423 dim = size.length(); 424 for (count = 0; size != null; size = size.tail()) { 425 ASTree s = size.head(); 426 if (s == null) 427 break; // int[][][] a = new int[3][4][]; 428 429 ++count; 430 s.accept(this); 431 if (exprType != INT) 432 throw new CompileError("bad type for array size"); 433 } 434 435 String desc; 436 exprType = type; 437 arrayDim = dim; 438 if (type == CLASS) { 439 className = resolveClassName(classname); 440 desc = toJvmArrayName(className, dim); 441 } 442 else 443 desc = toJvmTypeName(type, dim); 444 445 bytecode.addMultiNewarray(desc, count); 446 } 447 448 public void atCallExpr(CallExpr expr) throws CompileError { 449 String mname = null; 450 CtClass targetClass = null; 451 ASTree method = expr.oprand1(); 452 ASTList args = (ASTList)expr.oprand2(); 453 boolean isStatic = false; 454 boolean isSpecial = false; 455 int aload0pos = -1; 456 457 MemberResolver.Method cached = expr.getMethod(); 458 if (method instanceof Member) { 459 mname = ((Member)method).get(); 460 targetClass = thisClass; 461 if (inStaticMethod || (cached != null && cached.isStatic())) 462 isStatic = true; // should be static 463 else { 464 aload0pos = bytecode.currentPc(); 465 bytecode.addAload(0); // this 466 } 467 } 468 else if (method instanceof Keyword) { // constructor 469 isSpecial = true; 470 mname = MethodInfo.nameInit; // <init> 471 targetClass = thisClass; 472 if (inStaticMethod) 473 throw new CompileError("a constructor cannot be static"); 474 else 475 bytecode.addAload(0); // this 476 477 if (((Keyword)method).get() == SUPER) 478 targetClass = MemberResolver.getSuperclass(targetClass); 479 } 480 else if (method instanceof Expr) { 481 Expr e = (Expr)method; 482 mname = ((Symbol)e.oprand2()).get(); 483 int op = e.getOperator(); 484 if (op == MEMBER) { // static method 485 targetClass 486 = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); 487 isStatic = true; 488 } 489 else if (op == '.') { 490 ASTree target = e.oprand1(); 491 if (target instanceof Keyword) 492 if (((Keyword)target).get() == SUPER) 493 isSpecial = true; 494 495 try { 496 target.accept(this); 497 } 498 catch (NoFieldException nfe) { 499 if (nfe.getExpr() != target) 500 throw nfe; 501 502 // it should be a static method. 503 exprType = CLASS; 504 arrayDim = 0; 505 className = nfe.getField(); // JVM-internal 506 resolver.recordPackage(className); 507 isStatic = true; 508 } 509 510 if (arrayDim > 0) 511 targetClass = resolver.lookupClass(javaLangObject, true); 512 else if (exprType == CLASS /* && arrayDim == 0 */) 513 targetClass = resolver.lookupClassByJvmName(className); 514 else 515 badMethod(); 516 } 517 else 518 badMethod(); 519 } 520 else 521 fatal(); 522 523 atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, 524 aload0pos, cached); 525 } 526 527 private static void badMethod() throws CompileError { 528 throw new CompileError("bad method"); 529 } 530 531 /* 532 * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew 533 * 534 * @param targetClass the class at which method lookup starts. 535 * @param found not null if the method look has been already done. 536 */ 537 public void atMethodCallCore(CtClass targetClass, String mname, 538 ASTList args, boolean isStatic, boolean isSpecial, 539 int aload0pos, MemberResolver.Method found) 540 throws CompileError 541 { 542 int nargs = getMethodArgsLength(args); 543 int[] types = new int[nargs]; 544 int[] dims = new int[nargs]; 545 String[] cnames = new String[nargs]; 546 547 if (!isStatic && found != null && found.isStatic()) { 548 bytecode.addOpcode(POP); 549 isStatic = true; 550 } 551 552 int stack = bytecode.getStackDepth(); 553 554 // generate code for evaluating arguments. 555 atMethodArgs(args, types, dims, cnames); 556 557 // used by invokeinterface 558 int count = bytecode.getStackDepth() - stack + 1; 559 560 if (found == null) 561 found = resolver.lookupMethod(targetClass, thisClass, thisMethod, 562 mname, types, dims, cnames); 563 564 if (found == null) { 565 String msg; 566 if (mname.equals(MethodInfo.nameInit)) 567 msg = "constructor not found"; 568 else 569 msg = "Method " + mname + " not found in " 570 + targetClass.getName(); 571 572 throw new CompileError(msg); 573 } 574 575 atMethodCallCore2(targetClass, mname, isStatic, isSpecial, 576 aload0pos, count, found); 577 } 578 579 private void atMethodCallCore2(CtClass targetClass, String mname, 580 boolean isStatic, boolean isSpecial, 581 int aload0pos, int count, 582 MemberResolver.Method found) 583 throws CompileError 584 { 585 CtClass declClass = found.declaring; 586 MethodInfo minfo = found.info; 587 String desc = minfo.getDescriptor(); 588 int acc = minfo.getAccessFlags(); 589 590 if (mname.equals(MethodInfo.nameInit)) { 591 isSpecial = true; 592 if (declClass != targetClass) 593 throw new CompileError("no such constructor"); 594 595 if (declClass != thisClass && AccessFlag.isPrivate(acc)) { 596 desc = getAccessibleConstructor(desc, declClass, minfo); 597 bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter 598 } 599 } 600 else if (AccessFlag.isPrivate(acc)) 601 if (declClass == thisClass) 602 isSpecial = true; 603 else { 604 isSpecial = false; 605 isStatic = true; 606 String origDesc = desc; 607 if ((acc & AccessFlag.STATIC) == 0) 608 desc = Descriptor.insertParameter(declClass.getName(), 609 origDesc); 610 611 acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC; 612 mname = getAccessiblePrivate(mname, origDesc, desc, 613 minfo, declClass); 614 } 615 616 boolean popTarget = false; 617 if ((acc & AccessFlag.STATIC) != 0) { 618 if (!isStatic) { 619 /* this method is static but the target object is 620 on stack. It must be popped out. If aload0pos >= 0, 621 then the target object was pushed by aload_0. It is 622 overwritten by NOP. 623 */ 624 isStatic = true; 625 if (aload0pos >= 0) 626 bytecode.write(aload0pos, NOP); 627 else 628 popTarget = true; 629 } 630 631 bytecode.addInvokestatic(declClass, mname, desc); 632 } 633 else if (isSpecial) // if (isSpecial && notStatic(acc)) 634 bytecode.addInvokespecial(declClass, mname, desc); 635 else { 636 if (!Modifier.isPublic(declClass.getModifiers()) 637 || declClass.isInterface() != targetClass.isInterface()) 638 declClass = targetClass; 639 640 if (declClass.isInterface()) 641 bytecode.addInvokeinterface(declClass, mname, desc, count); 642 else 643 if (isStatic) 644 throw new CompileError(mname + " is not static"); 645 else 646 bytecode.addInvokevirtual(declClass, mname, desc); 647 } 648 649 setReturnType(desc, isStatic, popTarget); 650 } 651 652 /* 653 * Finds (or adds if necessary) a hidden accessor if the method 654 * is in an enclosing class. 655 * 656 * @param desc the descriptor of the method. 657 * @param declClass the class declaring the method. 658 */ 659 protected String getAccessiblePrivate(String methodName, String desc, 660 String newDesc, MethodInfo minfo, 661 CtClass declClass) 662 throws CompileError 663 { 664 if (isEnclosing(declClass, thisClass)) { 665 AccessorMaker maker = declClass.getAccessorMaker(); 666 if (maker != null) 667 return maker.getMethodAccessor(methodName, desc, newDesc, 668 minfo); 669 } 670 671 throw new CompileError("Method " + methodName 672 + " is private"); 673 } 674 675 /* 676 * Finds (or adds if necessary) a hidden constructor if the given 677 * constructor is in an enclosing class. 678 * 679 * @param desc the descriptor of the constructor. 680 * @param declClass the class declaring the constructor. 681 * @param minfo the method info of the constructor. 682 * @return the descriptor of the hidden constructor. 683 */ 684 protected String getAccessibleConstructor(String desc, CtClass declClass, 685 MethodInfo minfo) 686 throws CompileError 687 { 688 if (isEnclosing(declClass, thisClass)) { 689 AccessorMaker maker = declClass.getAccessorMaker(); 690 if (maker != null) 691 return maker.getConstructor(declClass, desc, minfo); 692 } 693 694 throw new CompileError("the called constructor is private in " 695 + declClass.getName()); 696 } 697 698 private boolean isEnclosing(CtClass outer, CtClass inner) { 699 try { 700 while (inner != null) { 701 inner = inner.getDeclaringClass(); 702 if (inner == outer) 703 return true; 704 } 705 } 706 catch (NotFoundException e) {} 707 return false; 708 } 709 710 public int getMethodArgsLength(ASTList args) { 711 return ASTList.length(args); 712 } 713 714 public void atMethodArgs(ASTList args, int[] types, int[] dims, 715 String[] cnames) throws CompileError { 716 int i = 0; 717 while (args != null) { 718 ASTree a = args.head(); 719 a.accept(this); 720 types[i] = exprType; 721 dims[i] = arrayDim; 722 cnames[i] = className; 723 ++i; 724 args = args.tail(); 725 } 726 } 727 728 void setReturnType(String desc, boolean isStatic, boolean popTarget) 729 throws CompileError 730 { 731 int i = desc.indexOf(')'); 732 if (i < 0) 733 badMethod(); 734 735 char c = desc.charAt(++i); 736 int dim = 0; 737 while (c == '[') { 738 ++dim; 739 c = desc.charAt(++i); 740 } 741 742 arrayDim = dim; 743 if (c == 'L') { 744 int j = desc.indexOf(';', i + 1); 745 if (j < 0) 746 badMethod(); 747 748 exprType = CLASS; 749 className = desc.substring(i + 1, j); 750 } 751 else { 752 exprType = MemberResolver.descToType(c); 753 className = null; 754 } 755 756 int etype = exprType; 757 if (isStatic) { 758 if (popTarget) { 759 if (is2word(etype, dim)) { 760 bytecode.addOpcode(DUP2_X1); 761 bytecode.addOpcode(POP2); 762 bytecode.addOpcode(POP); 763 } 764 else if (etype == VOID) 765 bytecode.addOpcode(POP); 766 else { 767 bytecode.addOpcode(SWAP); 768 bytecode.addOpcode(POP); 769 } 770 } 771 } 772 } 773 774 protected void atFieldAssign(Expr expr, int op, ASTree left, 775 ASTree right, boolean doDup) throws CompileError 776 { 777 CtField f = fieldAccess(left, false); 778 boolean is_static = resultStatic; 779 if (op != '=' && !is_static) 780 bytecode.addOpcode(DUP); 781 782 int fi; 783 if (op == '=') { 784 FieldInfo finfo = f.getFieldInfo2(); 785 setFieldType(finfo); 786 AccessorMaker maker = isAccessibleField(f, finfo); 787 if (maker == null) 788 fi = addFieldrefInfo(f, finfo); 789 else 790 fi = 0; 791 } 792 else 793 fi = atFieldRead(f, is_static); 794 795 int fType = exprType; 796 int fDim = arrayDim; 797 String cname = className; 798 799 atAssignCore(expr, op, right, fType, fDim, cname); 800 801 boolean is2w = is2word(fType, fDim); 802 if (doDup) { 803 int dup_code; 804 if (is_static) 805 dup_code = (is2w ? DUP2 : DUP); 806 else 807 dup_code = (is2w ? DUP2_X1 : DUP_X1); 808 809 bytecode.addOpcode(dup_code); 810 } 811 812 atFieldAssignCore(f, is_static, fi, is2w); 813 814 exprType = fType; 815 arrayDim = fDim; 816 className = cname; 817 } 818 819 /* If fi == 0, the field must be a private field in an enclosing class. 820 */ 821 private void atFieldAssignCore(CtField f, boolean is_static, int fi, 822 boolean is2byte) throws CompileError { 823 if (fi != 0) { 824 if (is_static) { 825 bytecode.add(PUTSTATIC); 826 bytecode.growStack(is2byte ? -2 : -1); 827 } 828 else { 829 bytecode.add(PUTFIELD); 830 bytecode.growStack(is2byte ? -3 : -2); 831 } 832 833 bytecode.addIndex(fi); 834 } 835 else { 836 CtClass declClass = f.getDeclaringClass(); 837 AccessorMaker maker = declClass.getAccessorMaker(); 838 // make should be non null. 839 FieldInfo finfo = f.getFieldInfo2(); 840 MethodInfo minfo = maker.getFieldSetter(finfo, is_static); 841 bytecode.addInvokestatic(declClass, minfo.getName(), 842 minfo.getDescriptor()); 843 } 844 } 845 846 /* overwritten in JvstCodeGen. 847 */ 848 public void atMember(Member mem) throws CompileError { 849 atFieldRead(mem); 850 } 851 852 protected void atFieldRead(ASTree expr) throws CompileError 853 { 854 CtField f = fieldAccess(expr, true); 855 if (f == null) { 856 atArrayLength(expr); 857 return; 858 } 859 860 boolean is_static = resultStatic; 861 ASTree cexpr = TypeChecker.getConstantFieldValue(f); 862 if (cexpr == null) 863 atFieldRead(f, is_static); 864 else { 865 cexpr.accept(this); 866 setFieldType(f.getFieldInfo2()); 867 } 868 } 869 870 private void atArrayLength(ASTree expr) throws CompileError { 871 if (arrayDim == 0) 872 throw new CompileError(".length applied to a non array"); 873 874 bytecode.addOpcode(ARRAYLENGTH); 875 exprType = INT; 876 arrayDim = 0; 877 } 878 879 /** 880 * Generates bytecode for reading a field value. 881 * It returns a fieldref_info index or zero if the field is a private 882 * one declared in an enclosing class. 883 */ 884 private int atFieldRead(CtField f, boolean isStatic) throws CompileError { 885 FieldInfo finfo = f.getFieldInfo2(); 886 boolean is2byte = setFieldType(finfo); 887 AccessorMaker maker = isAccessibleField(f, finfo); 888 if (maker != null) { 889 MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); 890 bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), 891 minfo.getDescriptor()); 892 return 0; 893 } 894 else { 895 int fi = addFieldrefInfo(f, finfo); 896 if (isStatic) { 897 bytecode.add(GETSTATIC); 898 bytecode.growStack(is2byte ? 2 : 1); 899 } 900 else { 901 bytecode.add(GETFIELD); 902 bytecode.growStack(is2byte ? 1 : 0); 903 } 904 905 bytecode.addIndex(fi); 906 return fi; 907 } 908 } 909 910 /** 911 * Returns null if the field is accessible. Otherwise, it throws 912 * an exception or it returns AccessorMaker if the field is a private 913 * one declared in an enclosing class. 914 */ 915 private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) 916 throws CompileError 917 { 918 if (AccessFlag.isPrivate(finfo.getAccessFlags()) 919 && f.getDeclaringClass() != thisClass) { 920 CtClass declClass = f.getDeclaringClass(); 921 if (isEnclosing(declClass, thisClass)) { 922 AccessorMaker maker = declClass.getAccessorMaker(); 923 if (maker != null) 924 return maker; 925 else 926 throw new CompileError("fatal error. bug?"); 927 } 928 else 929 throw new CompileError("Field " + f.getName() + " in " 930 + declClass.getName() + " is private."); 931 } 932 933 return null; // accessible field 934 } 935 936 /** 937 * Sets exprType, arrayDim, and className. 938 * 939 * @return true if the field type is long or double. 940 */ 941 private boolean setFieldType(FieldInfo finfo) throws CompileError { 942 String type = finfo.getDescriptor(); 943 944 int i = 0; 945 int dim = 0; 946 char c = type.charAt(i); 947 while (c == '[') { 948 ++dim; 949 c = type.charAt(++i); 950 } 951 952 arrayDim = dim; 953 exprType = MemberResolver.descToType(c); 954 955 if (c == 'L') 956 className = type.substring(i + 1, type.indexOf(';', i + 1)); 957 else 958 className = null; 959 960 boolean is2byte = (c == 'J' || c == 'D'); 961 return is2byte; 962 } 963 964 private int addFieldrefInfo(CtField f, FieldInfo finfo) { 965 ConstPool cp = bytecode.getConstPool(); 966 String cname = f.getDeclaringClass().getName(); 967 int ci = cp.addClassInfo(cname); 968 String name = finfo.getName(); 969 String type = finfo.getDescriptor(); 970 return cp.addFieldrefInfo(ci, name, type); 971 } 972 973 protected void atClassObject2(String cname) throws CompileError { 974 if (getMajorVersion() < ClassFile.JAVA_5) 975 super.atClassObject2(cname); 976 else 977 bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname)); 978 } 979 980 protected void atFieldPlusPlus(int token, boolean isPost, 981 ASTree oprand, Expr expr, boolean doDup) 982 throws CompileError 983 { 984 CtField f = fieldAccess(oprand, false); 985 boolean is_static = resultStatic; 986 if (!is_static) 987 bytecode.addOpcode(DUP); 988 989 int fi = atFieldRead(f, is_static); 990 int t = exprType; 991 boolean is2w = is2word(t, arrayDim); 992 993 int dup_code; 994 if (is_static) 995 dup_code = (is2w ? DUP2 : DUP); 996 else 997 dup_code = (is2w ? DUP2_X1 : DUP_X1); 998 999 atPlusPlusCore(dup_code, doDup, token, isPost, expr); 1000 atFieldAssignCore(f, is_static, fi, is2w); 1001 } 1002 1003 /* This method also returns a value in resultStatic. 1004 * 1005 * @param acceptLength true if array length is acceptable 1006 */ 1007 protected CtField fieldAccess(ASTree expr, boolean acceptLength) 1008 throws CompileError 1009 { 1010 if (expr instanceof Member) { 1011 String name = ((Member)expr).get(); 1012 CtField f = null; 1013 try { 1014 f = thisClass.getField(name); 1015 } 1016 catch (NotFoundException e) { 1017 // EXPR might be part of a static member access? 1018 throw new NoFieldException(name, expr); 1019 } 1020 1021 boolean is_static = Modifier.isStatic(f.getModifiers()); 1022 if (!is_static) 1023 if (inStaticMethod) 1024 throw new CompileError( 1025 "not available in a static method: " + name); 1026 else 1027 bytecode.addAload(0); // this 1028 1029 resultStatic = is_static; 1030 return f; 1031 } 1032 else if (expr instanceof Expr) { 1033 Expr e = (Expr)expr; 1034 int op = e.getOperator(); 1035 if (op == MEMBER) { 1036 /* static member by # (extension by Javassist) 1037 * For example, if int.class is parsed, the resulting tree 1038 * is (# "java.lang.Integer" "TYPE"). 1039 */ 1040 CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), 1041 (Symbol)e.oprand2()); 1042 resultStatic = true; 1043 return f; 1044 } 1045 else if (op == '.') { 1046 CtField f = null; 1047 try { 1048 e.oprand1().accept(this); 1049 /* Don't call lookupFieldByJvmName2(). 1050 * The left operand of . is not a class name but 1051 * a normal expression. 1052 */ 1053 if (exprType == CLASS && arrayDim == 0) 1054 f = resolver.lookupFieldByJvmName(className, 1055 (Symbol)e.oprand2()); 1056 else if (acceptLength && arrayDim > 0 1057 && ((Symbol)e.oprand2()).get().equals("length")) 1058 return null; // expr is an array length. 1059 else 1060 badLvalue(); 1061 1062 boolean is_static = Modifier.isStatic(f.getModifiers()); 1063 if (is_static) 1064 bytecode.addOpcode(POP); 1065 1066 resultStatic = is_static; 1067 return f; 1068 } 1069 catch (NoFieldException nfe) { 1070 if (nfe.getExpr() != e.oprand1()) 1071 throw nfe; 1072 1073 /* EXPR should be a static field. 1074 * If EXPR might be part of a qualified class name, 1075 * lookupFieldByJvmName2() throws NoFieldException. 1076 */ 1077 Symbol fname = (Symbol)e.oprand2(); 1078 String cname = nfe.getField(); 1079 f = resolver.lookupFieldByJvmName2(cname, fname, expr); 1080 resolver.recordPackage(cname); 1081 resultStatic = true; 1082 return f; 1083 } 1084 } 1085 else 1086 badLvalue(); 1087 } 1088 else 1089 badLvalue(); 1090 1091 resultStatic = false; 1092 return null; // never reach 1093 } 1094 1095 private static void badLvalue() throws CompileError { 1096 throw new CompileError("bad l-value"); 1097 } 1098 1099 public CtClass[] makeParamList(MethodDecl md) throws CompileError { 1100 CtClass[] params; 1101 ASTList plist = md.getParams(); 1102 if (plist == null) 1103 params = new CtClass[0]; 1104 else { 1105 int i = 0; 1106 params = new CtClass[plist.length()]; 1107 while (plist != null) { 1108 params[i++] = resolver.lookupClass((Declarator)plist.head()); 1109 plist = plist.tail(); 1110 } 1111 } 1112 1113 return params; 1114 } 1115 1116 public CtClass[] makeThrowsList(MethodDecl md) throws CompileError { 1117 CtClass[] clist; 1118 ASTList list = md.getThrows(); 1119 if (list == null) 1120 return null; 1121 else { 1122 int i = 0; 1123 clist = new CtClass[list.length()]; 1124 while (list != null) { 1125 clist[i++] = resolver.lookupClassByName((ASTList)list.head()); 1126 list = list.tail(); 1127 } 1128 1129 return clist; 1130 } 1131 } 1132 1133 /* Converts a class name into a JVM-internal representation. 1134 * 1135 * It may also expand a simple class name to java.lang.*. 1136 * For example, this converts Object into java/lang/Object. 1137 */ 1138 protected String resolveClassName(ASTList name) throws CompileError { 1139 return resolver.resolveClassName(name); 1140 } 1141 1142 /* Expands a simple class name to java.lang.*. 1143 * For example, this converts Object into java/lang/Object. 1144 */ 1145 protected String resolveClassName(String jvmName) throws CompileError { 1146 return resolver.resolveJvmClassName(jvmName); 1147 } 1148 } 1149