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 MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp)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 */ getMajorVersion()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 */ setThisMethod(CtMethod m)55 public void setThisMethod(CtMethod m) { 56 thisMethod = m.getMethodInfo2(); 57 if (typeChecker != null) 58 typeChecker.setThisMethod(thisMethod); 59 } 60 getThisClass()61 public CtClass getThisClass() { return thisClass; } 62 63 /** 64 * Returns the JVM-internal representation of this class name. 65 */ getThisName()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 */ getSuperName()73 protected String getSuperName() throws CompileError { 74 return MemberResolver.javaToJvmName( 75 MemberResolver.getSuperclass(thisClass).getName()); 76 } 77 insertDefaultSuperCall()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 JsrHook(CodeGen gen)89 JsrHook(CodeGen gen) { 90 super(gen); 91 jsrList = new ArrayList(); 92 cgen = gen; 93 var = -1; 94 } 95 getVar(int size)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 jsrJmp(Bytecode b)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 doit(Bytecode b, int opcode)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 JsrHook2(CodeGen gen, int[] retTarget)153 JsrHook2(CodeGen gen, int[] retTarget) { 154 super(gen); 155 target = retTarget[0]; 156 var = retTarget[1]; 157 } 158 doit(Bytecode b, int opcode)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 atTryStmnt(Stmnt st)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 */ addFinally(ArrayList returnList, Stmnt finallyBlock)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 atNewExpr(NewExpr expr)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 atNewArrayExpr(NewExpr expr)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 atNewArrayExpr2(int type, ASTree sizeExpr, String jvmClassname, ArrayInit init)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 badNewExpr()406 private static void badNewExpr() throws CompileError { 407 throw new CompileError("bad new expression"); 408 } 409 atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass)410 protected void atArrayVariableAssign(ArrayInit init, int varType, 411 int varArray, String varClass) throws CompileError { 412 atNewArrayExpr2(varType, null, varClass, init); 413 } 414 atArrayInit(ArrayInit init)415 public void atArrayInit(ArrayInit init) throws CompileError { 416 throw new CompileError("array initializer is not supported"); 417 } 418 atMultiNewArray(int type, ASTList classname, ASTList size)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 atCallExpr(CallExpr expr)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 badMethod()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 */ atMethodCallCore(CtClass targetClass, String mname, ASTList args, boolean isStatic, boolean isSpecial, int aload0pos, MemberResolver.Method found)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 atMethodCallCore2(CtClass targetClass, String mname, boolean isStatic, boolean isSpecial, int aload0pos, int count, MemberResolver.Method found)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 */ getAccessiblePrivate(String methodName, String desc, String newDesc, MethodInfo minfo, CtClass declClass)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 */ getAccessibleConstructor(String desc, CtClass declClass, MethodInfo minfo)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 isEnclosing(CtClass outer, CtClass inner)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 getMethodArgsLength(ASTList args)710 public int getMethodArgsLength(ASTList args) { 711 return ASTList.length(args); 712 } 713 atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames)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 setReturnType(String desc, boolean isStatic, boolean popTarget)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 atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup)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 */ atFieldAssignCore(CtField f, boolean is_static, int fi, boolean is2byte)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 */ atMember(Member mem)848 public void atMember(Member mem) throws CompileError { 849 atFieldRead(mem); 850 } 851 atFieldRead(ASTree expr)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 atArrayLength(ASTree expr)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 */ atFieldRead(CtField f, boolean isStatic)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 */ isAccessibleField(CtField f, FieldInfo finfo)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 */ setFieldType(FieldInfo finfo)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 addFieldrefInfo(CtField f, FieldInfo finfo)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 atClassObject2(String cname)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 atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup)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 */ fieldAccess(ASTree expr, boolean acceptLength)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 badLvalue()1095 private static void badLvalue() throws CompileError { 1096 throw new CompileError("bad l-value"); 1097 } 1098 makeParamList(MethodDecl md)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 makeThrowsList(MethodDecl md)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 */ resolveClassName(ASTList name)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 */ resolveClassName(String jvmName)1145 protected String resolveClassName(String jvmName) throws CompileError { 1146 return resolver.resolveJvmClassName(jvmName); 1147 } 1148 } 1149