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