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.CtClass; 19 import javassist.CtField; 20 import javassist.ClassPool; 21 import javassist.Modifier; 22 import javassist.NotFoundException; 23 import javassist.compiler.ast.*; 24 import javassist.bytecode.*; 25 26 public class TypeChecker extends Visitor implements Opcode, TokenId { 27 static final String javaLangObject = "java.lang.Object"; 28 static final String jvmJavaLangObject = "java/lang/Object"; 29 static final String jvmJavaLangString = "java/lang/String"; 30 static final String jvmJavaLangClass = "java/lang/Class"; 31 32 /* The following fields are used by atXXX() methods 33 * for returning the type of the compiled expression. 34 */ 35 protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... 36 protected int arrayDim; 37 protected String className; // JVM-internal representation 38 39 protected MemberResolver resolver; 40 protected CtClass thisClass; 41 protected MethodInfo thisMethod; 42 TypeChecker(CtClass cc, ClassPool cp)43 public TypeChecker(CtClass cc, ClassPool cp) { 44 resolver = new MemberResolver(cp); 45 thisClass = cc; 46 thisMethod = null; 47 } 48 49 /* 50 * Converts an array of tuples of exprType, arrayDim, and className 51 * into a String object. 52 */ argTypesToString(int[] types, int[] dims, String[] cnames)53 protected static String argTypesToString(int[] types, int[] dims, 54 String[] cnames) { 55 StringBuffer sbuf = new StringBuffer(); 56 sbuf.append('('); 57 int n = types.length; 58 if (n > 0) { 59 int i = 0; 60 while (true) { 61 typeToString(sbuf, types[i], dims[i], cnames[i]); 62 if (++i < n) 63 sbuf.append(','); 64 else 65 break; 66 } 67 } 68 69 sbuf.append(')'); 70 return sbuf.toString(); 71 } 72 73 /* 74 * Converts a tuple of exprType, arrayDim, and className 75 * into a String object. 76 */ typeToString(StringBuffer sbuf, int type, int dim, String cname)77 protected static StringBuffer typeToString(StringBuffer sbuf, 78 int type, int dim, String cname) { 79 String s; 80 if (type == CLASS) 81 s = MemberResolver.jvmToJavaName(cname); 82 else if (type == NULL) 83 s = "Object"; 84 else 85 try { 86 s = MemberResolver.getTypeName(type); 87 } 88 catch (CompileError e) { 89 s = "?"; 90 } 91 92 sbuf.append(s); 93 while (dim-- > 0) 94 sbuf.append("[]"); 95 96 return sbuf; 97 } 98 99 /** 100 * Records the currently compiled method. 101 */ setThisMethod(MethodInfo m)102 public void setThisMethod(MethodInfo m) { 103 thisMethod = m; 104 } 105 fatal()106 protected static void fatal() throws CompileError { 107 throw new CompileError("fatal"); 108 } 109 110 /** 111 * Returns the JVM-internal representation of this class name. 112 */ getThisName()113 protected String getThisName() { 114 return MemberResolver.javaToJvmName(thisClass.getName()); 115 } 116 117 /** 118 * Returns the JVM-internal representation of this super class name. 119 */ getSuperName()120 protected String getSuperName() throws CompileError { 121 return MemberResolver.javaToJvmName( 122 MemberResolver.getSuperclass(thisClass).getName()); 123 } 124 125 /* Converts a class name into a JVM-internal representation. 126 * 127 * It may also expand a simple class name to java.lang.*. 128 * For example, this converts Object into java/lang/Object. 129 */ resolveClassName(ASTList name)130 protected String resolveClassName(ASTList name) throws CompileError { 131 return resolver.resolveClassName(name); 132 } 133 134 /* Expands a simple class name to java.lang.*. 135 * For example, this converts Object into java/lang/Object. 136 */ resolveClassName(String jvmName)137 protected String resolveClassName(String jvmName) throws CompileError { 138 return resolver.resolveJvmClassName(jvmName); 139 } 140 atNewExpr(NewExpr expr)141 public void atNewExpr(NewExpr expr) throws CompileError { 142 if (expr.isArray()) 143 atNewArrayExpr(expr); 144 else { 145 CtClass clazz = resolver.lookupClassByName(expr.getClassName()); 146 String cname = clazz.getName(); 147 ASTList args = expr.getArguments(); 148 atMethodCallCore(clazz, MethodInfo.nameInit, args); 149 exprType = CLASS; 150 arrayDim = 0; 151 className = MemberResolver.javaToJvmName(cname); 152 } 153 } 154 atNewArrayExpr(NewExpr expr)155 public void atNewArrayExpr(NewExpr expr) throws CompileError { 156 int type = expr.getArrayType(); 157 ASTList size = expr.getArraySize(); 158 ASTList classname = expr.getClassName(); 159 ASTree init = expr.getInitializer(); 160 if (init != null) 161 init.accept(this); 162 163 if (size.length() > 1) 164 atMultiNewArray(type, classname, size); 165 else { 166 ASTree sizeExpr = size.head(); 167 if (sizeExpr != null) 168 sizeExpr.accept(this); 169 170 exprType = type; 171 arrayDim = 1; 172 if (type == CLASS) 173 className = resolveClassName(classname); 174 else 175 className = null; 176 } 177 } 178 atArrayInit(ArrayInit init)179 public void atArrayInit(ArrayInit init) throws CompileError { 180 ASTList list = init; 181 while (list != null) { 182 ASTree h = list.head(); 183 list = list.tail(); 184 if (h != null) 185 h.accept(this); 186 } 187 } 188 atMultiNewArray(int type, ASTList classname, ASTList size)189 protected void atMultiNewArray(int type, ASTList classname, ASTList size) 190 throws CompileError 191 { 192 int count, dim; 193 dim = size.length(); 194 for (count = 0; size != null; size = size.tail()) { 195 ASTree s = size.head(); 196 if (s == null) 197 break; // int[][][] a = new int[3][4][]; 198 199 ++count; 200 s.accept(this); 201 } 202 203 exprType = type; 204 arrayDim = dim; 205 if (type == CLASS) 206 className = resolveClassName(classname); 207 else 208 className = null; 209 } 210 atAssignExpr(AssignExpr expr)211 public void atAssignExpr(AssignExpr expr) throws CompileError { 212 // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= 213 int op = expr.getOperator(); 214 ASTree left = expr.oprand1(); 215 ASTree right = expr.oprand2(); 216 if (left instanceof Variable) 217 atVariableAssign(expr, op, (Variable)left, 218 ((Variable)left).getDeclarator(), 219 right); 220 else { 221 if (left instanceof Expr) { 222 Expr e = (Expr)left; 223 if (e.getOperator() == ARRAY) { 224 atArrayAssign(expr, op, (Expr)left, right); 225 return; 226 } 227 } 228 229 atFieldAssign(expr, op, left, right); 230 } 231 } 232 233 /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. 234 * 235 * expr and var can be null. 236 */ atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right)237 private void atVariableAssign(Expr expr, int op, Variable var, 238 Declarator d, ASTree right) 239 throws CompileError 240 { 241 int varType = d.getType(); 242 int varArray = d.getArrayDim(); 243 String varClass = d.getClassName(); 244 245 if (op != '=') 246 atVariable(var); 247 248 right.accept(this); 249 exprType = varType; 250 arrayDim = varArray; 251 className = varClass; 252 } 253 atArrayAssign(Expr expr, int op, Expr array, ASTree right)254 private void atArrayAssign(Expr expr, int op, Expr array, 255 ASTree right) throws CompileError 256 { 257 atArrayRead(array.oprand1(), array.oprand2()); 258 int aType = exprType; 259 int aDim = arrayDim; 260 String cname = className; 261 right.accept(this); 262 exprType = aType; 263 arrayDim = aDim; 264 className = cname; 265 } 266 atFieldAssign(Expr expr, int op, ASTree left, ASTree right)267 protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) 268 throws CompileError 269 { 270 CtField f = fieldAccess(left); 271 atFieldRead(f); 272 int fType = exprType; 273 int fDim = arrayDim; 274 String cname = className; 275 right.accept(this); 276 exprType = fType; 277 arrayDim = fDim; 278 className = cname; 279 } 280 atCondExpr(CondExpr expr)281 public void atCondExpr(CondExpr expr) throws CompileError { 282 booleanExpr(expr.condExpr()); 283 expr.thenExpr().accept(this); 284 int type1 = exprType; 285 int dim1 = arrayDim; 286 String cname1 = className; 287 expr.elseExpr().accept(this); 288 289 if (dim1 == 0 && dim1 == arrayDim) 290 if (CodeGen.rightIsStrong(type1, exprType)) 291 expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); 292 else if (CodeGen.rightIsStrong(exprType, type1)) { 293 expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); 294 exprType = type1; 295 } 296 } 297 298 /* 299 * If atBinExpr() substitutes a new expression for the original 300 * binary-operator expression, it changes the operator name to '+' 301 * (if the original is not '+') and sets the new expression to the 302 * left-hand-side expression and null to the right-hand-side expression. 303 */ atBinExpr(BinExpr expr)304 public void atBinExpr(BinExpr expr) throws CompileError { 305 int token = expr.getOperator(); 306 int k = CodeGen.lookupBinOp(token); 307 if (k >= 0) { 308 /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> 309 */ 310 if (token == '+') { 311 Expr e = atPlusExpr(expr); 312 if (e != null) { 313 /* String concatenation has been translated into 314 * an expression using StringBuffer. 315 */ 316 e = CallExpr.makeCall(Expr.make('.', e, 317 new Member("toString")), null); 318 expr.setOprand1(e); 319 expr.setOprand2(null); // <---- look at this! 320 className = jvmJavaLangString; 321 } 322 } 323 else { 324 ASTree left = expr.oprand1(); 325 ASTree right = expr.oprand2(); 326 left.accept(this); 327 int type1 = exprType; 328 right.accept(this); 329 if (!isConstant(expr, token, left, right)) 330 computeBinExprType(expr, token, type1); 331 } 332 } 333 else { 334 /* equation: &&, ||, ==, !=, <=, >=, <, > 335 */ 336 booleanExpr(expr); 337 } 338 } 339 340 /* EXPR must be a + expression. 341 * atPlusExpr() returns non-null if the given expression is string 342 * concatenation. The returned value is "new StringBuffer().append..". 343 */ atPlusExpr(BinExpr expr)344 private Expr atPlusExpr(BinExpr expr) throws CompileError { 345 ASTree left = expr.oprand1(); 346 ASTree right = expr.oprand2(); 347 if (right == null) { 348 // this expression has been already type-checked. 349 // see atBinExpr() above. 350 left.accept(this); 351 return null; 352 } 353 354 if (isPlusExpr(left)) { 355 Expr newExpr = atPlusExpr((BinExpr)left); 356 if (newExpr != null) { 357 right.accept(this); 358 exprType = CLASS; 359 arrayDim = 0; 360 className = "java/lang/StringBuffer"; 361 return makeAppendCall(newExpr, right); 362 } 363 } 364 else 365 left.accept(this); 366 367 int type1 = exprType; 368 int dim1 = arrayDim; 369 String cname = className; 370 right.accept(this); 371 372 if (isConstant(expr, '+', left, right)) 373 return null; 374 375 if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) 376 || (exprType == CLASS && arrayDim == 0 377 && jvmJavaLangString.equals(className))) { 378 ASTList sbufClass = ASTList.make(new Symbol("java"), 379 new Symbol("lang"), new Symbol("StringBuffer")); 380 ASTree e = new NewExpr(sbufClass, null); 381 exprType = CLASS; 382 arrayDim = 0; 383 className = "java/lang/StringBuffer"; 384 return makeAppendCall(makeAppendCall(e, left), right); 385 } 386 else { 387 computeBinExprType(expr, '+', type1); 388 return null; 389 } 390 } 391 isConstant(BinExpr expr, int op, ASTree left, ASTree right)392 private boolean isConstant(BinExpr expr, int op, ASTree left, 393 ASTree right) throws CompileError 394 { 395 left = stripPlusExpr(left); 396 right = stripPlusExpr(right); 397 ASTree newExpr = null; 398 if (left instanceof StringL && right instanceof StringL && op == '+') 399 newExpr = new StringL(((StringL)left).get() 400 + ((StringL)right).get()); 401 else if (left instanceof IntConst) 402 newExpr = ((IntConst)left).compute(op, right); 403 else if (left instanceof DoubleConst) 404 newExpr = ((DoubleConst)left).compute(op, right); 405 406 if (newExpr == null) 407 return false; // not a constant expression 408 else { 409 expr.setOperator('+'); 410 expr.setOprand1(newExpr); 411 expr.setOprand2(null); 412 newExpr.accept(this); // for setting exprType, arrayDim, ... 413 return true; 414 } 415 } 416 417 /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). 418 */ stripPlusExpr(ASTree expr)419 static ASTree stripPlusExpr(ASTree expr) { 420 if (expr instanceof BinExpr) { 421 BinExpr e = (BinExpr)expr; 422 if (e.getOperator() == '+' && e.oprand2() == null) 423 return e.getLeft(); 424 } 425 else if (expr instanceof Expr) { // note: BinExpr extends Expr. 426 Expr e = (Expr)expr; 427 int op = e.getOperator(); 428 if (op == MEMBER) { 429 ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); 430 if (cexpr != null) 431 return cexpr; 432 } 433 else if (op == '+' && e.getRight() == null) 434 return e.getLeft(); 435 } 436 else if (expr instanceof Member) { 437 ASTree cexpr = getConstantFieldValue((Member)expr); 438 if (cexpr != null) 439 return cexpr; 440 } 441 442 return expr; 443 } 444 445 /** 446 * If MEM is a static final field, this method returns a constant 447 * expression representing the value of that field. 448 */ getConstantFieldValue(Member mem)449 private static ASTree getConstantFieldValue(Member mem) { 450 return getConstantFieldValue(mem.getField()); 451 } 452 getConstantFieldValue(CtField f)453 public static ASTree getConstantFieldValue(CtField f) { 454 if (f == null) 455 return null; 456 457 Object value = f.getConstantValue(); 458 if (value == null) 459 return null; 460 461 if (value instanceof String) 462 return new StringL((String)value); 463 else if (value instanceof Double || value instanceof Float) { 464 int token = (value instanceof Double) 465 ? DoubleConstant : FloatConstant; 466 return new DoubleConst(((Number)value).doubleValue(), token); 467 } 468 else if (value instanceof Number) { 469 int token = (value instanceof Long) ? LongConstant : IntConstant; 470 return new IntConst(((Number)value).longValue(), token); 471 } 472 else if (value instanceof Boolean) 473 return new Keyword(((Boolean)value).booleanValue() 474 ? TokenId.TRUE : TokenId.FALSE); 475 else 476 return null; 477 } 478 isPlusExpr(ASTree expr)479 private static boolean isPlusExpr(ASTree expr) { 480 if (expr instanceof BinExpr) { 481 BinExpr bexpr = (BinExpr)expr; 482 int token = bexpr.getOperator(); 483 return token == '+'; 484 } 485 486 return false; 487 } 488 makeAppendCall(ASTree target, ASTree arg)489 private static Expr makeAppendCall(ASTree target, ASTree arg) { 490 return CallExpr.makeCall(Expr.make('.', target, new Member("append")), 491 new ASTList(arg)); 492 } 493 computeBinExprType(BinExpr expr, int token, int type1)494 private void computeBinExprType(BinExpr expr, int token, int type1) 495 throws CompileError 496 { 497 // arrayDim should be 0. 498 int type2 = exprType; 499 if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) 500 exprType = type1; 501 else 502 insertCast(expr, type1, type2); 503 504 if (CodeGen.isP_INT(exprType)) 505 exprType = INT; // type1 may be BYTE, ... 506 } 507 booleanExpr(ASTree expr)508 private void booleanExpr(ASTree expr) 509 throws CompileError 510 { 511 int op = CodeGen.getCompOperator(expr); 512 if (op == EQ) { // ==, !=, ... 513 BinExpr bexpr = (BinExpr)expr; 514 bexpr.oprand1().accept(this); 515 int type1 = exprType; 516 int dim1 = arrayDim; 517 bexpr.oprand2().accept(this); 518 if (dim1 == 0 && arrayDim == 0) 519 insertCast(bexpr, type1, exprType); 520 } 521 else if (op == '!') 522 ((Expr)expr).oprand1().accept(this); 523 else if (op == ANDAND || op == OROR) { 524 BinExpr bexpr = (BinExpr)expr; 525 bexpr.oprand1().accept(this); 526 bexpr.oprand2().accept(this); 527 } 528 else // others 529 expr.accept(this); 530 531 exprType = BOOLEAN; 532 arrayDim = 0; 533 } 534 insertCast(BinExpr expr, int type1, int type2)535 private void insertCast(BinExpr expr, int type1, int type2) 536 throws CompileError 537 { 538 if (CodeGen.rightIsStrong(type1, type2)) 539 expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); 540 else 541 exprType = type1; 542 } 543 atCastExpr(CastExpr expr)544 public void atCastExpr(CastExpr expr) throws CompileError { 545 String cname = resolveClassName(expr.getClassName()); 546 expr.getOprand().accept(this); 547 exprType = expr.getType(); 548 arrayDim = expr.getArrayDim(); 549 className = cname; 550 } 551 atInstanceOfExpr(InstanceOfExpr expr)552 public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { 553 expr.getOprand().accept(this); 554 exprType = BOOLEAN; 555 arrayDim = 0; 556 } 557 atExpr(Expr expr)558 public void atExpr(Expr expr) throws CompileError { 559 // array access, member access, 560 // (unary) +, (unary) -, ++, --, !, ~ 561 562 int token = expr.getOperator(); 563 ASTree oprand = expr.oprand1(); 564 if (token == '.') { 565 String member = ((Symbol)expr.oprand2()).get(); 566 if (member.equals("length")) 567 atArrayLength(expr); 568 else if (member.equals("class")) 569 atClassObject(expr); // .class 570 else 571 atFieldRead(expr); 572 } 573 else if (token == MEMBER) { // field read 574 String member = ((Symbol)expr.oprand2()).get(); 575 if (member.equals("class")) 576 atClassObject(expr); // .class 577 else 578 atFieldRead(expr); 579 } 580 else if (token == ARRAY) 581 atArrayRead(oprand, expr.oprand2()); 582 else if (token == PLUSPLUS || token == MINUSMINUS) 583 atPlusPlus(token, oprand, expr); 584 else if (token == '!') 585 booleanExpr(expr); 586 else if (token == CALL) // method call 587 fatal(); 588 else { 589 oprand.accept(this); 590 if (!isConstant(expr, token, oprand)) 591 if (token == '-' || token == '~') 592 if (CodeGen.isP_INT(exprType)) 593 exprType = INT; // type may be BYTE, ... 594 } 595 } 596 isConstant(Expr expr, int op, ASTree oprand)597 private boolean isConstant(Expr expr, int op, ASTree oprand) { 598 oprand = stripPlusExpr(oprand); 599 if (oprand instanceof IntConst) { 600 IntConst c = (IntConst)oprand; 601 long v = c.get(); 602 if (op == '-') 603 v = -v; 604 else if (op == '~') 605 v = ~v; 606 else 607 return false; 608 609 c.set(v); 610 } 611 else if (oprand instanceof DoubleConst) { 612 DoubleConst c = (DoubleConst)oprand; 613 if (op == '-') 614 c.set(-c.get()); 615 else 616 return false; 617 } 618 else 619 return false; 620 621 expr.setOperator('+'); 622 return true; 623 } 624 atCallExpr(CallExpr expr)625 public void atCallExpr(CallExpr expr) throws CompileError { 626 String mname = null; 627 CtClass targetClass = null; 628 ASTree method = expr.oprand1(); 629 ASTList args = (ASTList)expr.oprand2(); 630 631 if (method instanceof Member) { 632 mname = ((Member)method).get(); 633 targetClass = thisClass; 634 } 635 else if (method instanceof Keyword) { // constructor 636 mname = MethodInfo.nameInit; // <init> 637 if (((Keyword)method).get() == SUPER) 638 targetClass = MemberResolver.getSuperclass(thisClass); 639 else 640 targetClass = thisClass; 641 } 642 else if (method instanceof Expr) { 643 Expr e = (Expr)method; 644 mname = ((Symbol)e.oprand2()).get(); 645 int op = e.getOperator(); 646 if (op == MEMBER) // static method 647 targetClass 648 = resolver.lookupClass(((Symbol)e.oprand1()).get(), 649 false); 650 else if (op == '.') { 651 ASTree target = e.oprand1(); 652 try { 653 target.accept(this); 654 } 655 catch (NoFieldException nfe) { 656 if (nfe.getExpr() != target) 657 throw nfe; 658 659 // it should be a static method. 660 exprType = CLASS; 661 arrayDim = 0; 662 className = nfe.getField(); // JVM-internal 663 e.setOperator(MEMBER); 664 e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( 665 className))); 666 } 667 668 if (arrayDim > 0) 669 targetClass = resolver.lookupClass(javaLangObject, true); 670 else if (exprType == CLASS /* && arrayDim == 0 */) 671 targetClass = resolver.lookupClassByJvmName(className); 672 else 673 badMethod(); 674 } 675 else 676 badMethod(); 677 } 678 else 679 fatal(); 680 681 MemberResolver.Method minfo 682 = atMethodCallCore(targetClass, mname, args); 683 expr.setMethod(minfo); 684 } 685 badMethod()686 private static void badMethod() throws CompileError { 687 throw new CompileError("bad method"); 688 } 689 690 /** 691 * @return a pair of the class declaring the invoked method 692 * and the MethodInfo of that method. Never null. 693 */ atMethodCallCore(CtClass targetClass, String mname, ASTList args)694 public MemberResolver.Method atMethodCallCore(CtClass targetClass, 695 String mname, ASTList args) 696 throws CompileError 697 { 698 int nargs = getMethodArgsLength(args); 699 int[] types = new int[nargs]; 700 int[] dims = new int[nargs]; 701 String[] cnames = new String[nargs]; 702 atMethodArgs(args, types, dims, cnames); 703 704 MemberResolver.Method found 705 = resolver.lookupMethod(targetClass, thisClass, thisMethod, 706 mname, types, dims, cnames); 707 if (found == null) { 708 String clazz = targetClass.getName(); 709 String signature = argTypesToString(types, dims, cnames); 710 String msg; 711 if (mname.equals(MethodInfo.nameInit)) 712 msg = "cannot find constructor " + clazz + signature; 713 else 714 msg = mname + signature + " not found in " + clazz; 715 716 throw new CompileError(msg); 717 } 718 719 String desc = found.info.getDescriptor(); 720 setReturnType(desc); 721 return found; 722 } 723 getMethodArgsLength(ASTList args)724 public int getMethodArgsLength(ASTList args) { 725 return ASTList.length(args); 726 } 727 atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames)728 public void atMethodArgs(ASTList args, int[] types, int[] dims, 729 String[] cnames) throws CompileError { 730 int i = 0; 731 while (args != null) { 732 ASTree a = args.head(); 733 a.accept(this); 734 types[i] = exprType; 735 dims[i] = arrayDim; 736 cnames[i] = className; 737 ++i; 738 args = args.tail(); 739 } 740 } 741 setReturnType(String desc)742 void setReturnType(String desc) throws CompileError { 743 int i = desc.indexOf(')'); 744 if (i < 0) 745 badMethod(); 746 747 char c = desc.charAt(++i); 748 int dim = 0; 749 while (c == '[') { 750 ++dim; 751 c = desc.charAt(++i); 752 } 753 754 arrayDim = dim; 755 if (c == 'L') { 756 int j = desc.indexOf(';', i + 1); 757 if (j < 0) 758 badMethod(); 759 760 exprType = CLASS; 761 className = desc.substring(i + 1, j); 762 } 763 else { 764 exprType = MemberResolver.descToType(c); 765 className = null; 766 } 767 } 768 atFieldRead(ASTree expr)769 private void atFieldRead(ASTree expr) throws CompileError { 770 atFieldRead(fieldAccess(expr)); 771 } 772 atFieldRead(CtField f)773 private void atFieldRead(CtField f) throws CompileError { 774 FieldInfo finfo = f.getFieldInfo2(); 775 String type = finfo.getDescriptor(); 776 777 int i = 0; 778 int dim = 0; 779 char c = type.charAt(i); 780 while (c == '[') { 781 ++dim; 782 c = type.charAt(++i); 783 } 784 785 arrayDim = dim; 786 exprType = MemberResolver.descToType(c); 787 788 if (c == 'L') 789 className = type.substring(i + 1, type.indexOf(';', i + 1)); 790 else 791 className = null; 792 } 793 794 /* if EXPR is to access a static field, fieldAccess() translates EXPR 795 * into an expression using '#' (MEMBER). For example, it translates 796 * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation 797 * speeds up type resolution by MemberCodeGen. 798 */ fieldAccess(ASTree expr)799 protected CtField fieldAccess(ASTree expr) throws CompileError { 800 if (expr instanceof Member) { 801 Member mem = (Member)expr; 802 String name = mem.get(); 803 try { 804 CtField f = thisClass.getField(name); 805 if (Modifier.isStatic(f.getModifiers())) 806 mem.setField(f); 807 808 return f; 809 } 810 catch (NotFoundException e) { 811 // EXPR might be part of a static member access? 812 throw new NoFieldException(name, expr); 813 } 814 } 815 else if (expr instanceof Expr) { 816 Expr e = (Expr)expr; 817 int op = e.getOperator(); 818 if (op == MEMBER) { 819 Member mem = (Member)e.oprand2(); 820 CtField f 821 = resolver.lookupField(((Symbol)e.oprand1()).get(), mem); 822 mem.setField(f); 823 return f; 824 } 825 else if (op == '.') { 826 try { 827 e.oprand1().accept(this); 828 } 829 catch (NoFieldException nfe) { 830 if (nfe.getExpr() != e.oprand1()) 831 throw nfe; 832 833 /* EXPR should be a static field. 834 * If EXPR might be part of a qualified class name, 835 * lookupFieldByJvmName2() throws NoFieldException. 836 */ 837 return fieldAccess2(e, nfe.getField()); 838 } 839 840 CompileError err = null; 841 try { 842 if (exprType == CLASS && arrayDim == 0) 843 return resolver.lookupFieldByJvmName(className, 844 (Symbol)e.oprand2()); 845 } 846 catch (CompileError ce) { 847 err = ce; 848 } 849 850 /* If a filed name is the same name as a package's, 851 * a static member of a class in that package is not 852 * visible. For example, 853 * 854 * class Foo { 855 * int javassist; 856 * } 857 * 858 * It is impossible to add the following method: 859 * 860 * String m() { return javassist.CtClass.intType.toString(); } 861 * 862 * because javassist is a field name. However, this is 863 * often inconvenient, this compiler allows it. The following 864 * code is for that. 865 */ 866 ASTree oprnd1 = e.oprand1(); 867 if (oprnd1 instanceof Symbol) 868 return fieldAccess2(e, ((Symbol)oprnd1).get()); 869 870 if (err != null) 871 throw err; 872 } 873 } 874 875 throw new CompileError("bad filed access"); 876 } 877 fieldAccess2(Expr e, String jvmClassName)878 private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { 879 Member fname = (Member)e.oprand2(); 880 CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); 881 e.setOperator(MEMBER); 882 e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); 883 fname.setField(f); 884 return f; 885 } 886 atClassObject(Expr expr)887 public void atClassObject(Expr expr) throws CompileError { 888 exprType = CLASS; 889 arrayDim = 0; 890 className =jvmJavaLangClass; 891 } 892 atArrayLength(Expr expr)893 public void atArrayLength(Expr expr) throws CompileError { 894 expr.oprand1().accept(this); 895 exprType = INT; 896 arrayDim = 0; 897 } 898 atArrayRead(ASTree array, ASTree index)899 public void atArrayRead(ASTree array, ASTree index) 900 throws CompileError 901 { 902 array.accept(this); 903 int type = exprType; 904 int dim = arrayDim; 905 String cname = className; 906 index.accept(this); 907 exprType = type; 908 arrayDim = dim - 1; 909 className = cname; 910 } 911 atPlusPlus(int token, ASTree oprand, Expr expr)912 private void atPlusPlus(int token, ASTree oprand, Expr expr) 913 throws CompileError 914 { 915 boolean isPost = oprand == null; // ++i or i++? 916 if (isPost) 917 oprand = expr.oprand2(); 918 919 if (oprand instanceof Variable) { 920 Declarator d = ((Variable)oprand).getDeclarator(); 921 exprType = d.getType(); 922 arrayDim = d.getArrayDim(); 923 } 924 else { 925 if (oprand instanceof Expr) { 926 Expr e = (Expr)oprand; 927 if (e.getOperator() == ARRAY) { 928 atArrayRead(e.oprand1(), e.oprand2()); 929 // arrayDim should be 0. 930 int t = exprType; 931 if (t == INT || t == BYTE || t == CHAR || t == SHORT) 932 exprType = INT; 933 934 return; 935 } 936 } 937 938 atFieldPlusPlus(oprand); 939 } 940 } 941 atFieldPlusPlus(ASTree oprand)942 protected void atFieldPlusPlus(ASTree oprand) throws CompileError 943 { 944 CtField f = fieldAccess(oprand); 945 atFieldRead(f); 946 int t = exprType; 947 if (t == INT || t == BYTE || t == CHAR || t == SHORT) 948 exprType = INT; 949 } 950 atMember(Member mem)951 public void atMember(Member mem) throws CompileError { 952 atFieldRead(mem); 953 } 954 atVariable(Variable v)955 public void atVariable(Variable v) throws CompileError { 956 Declarator d = v.getDeclarator(); 957 exprType = d.getType(); 958 arrayDim = d.getArrayDim(); 959 className = d.getClassName(); 960 } 961 atKeyword(Keyword k)962 public void atKeyword(Keyword k) throws CompileError { 963 arrayDim = 0; 964 int token = k.get(); 965 switch (token) { 966 case TRUE : 967 case FALSE : 968 exprType = BOOLEAN; 969 break; 970 case NULL : 971 exprType = NULL; 972 break; 973 case THIS : 974 case SUPER : 975 exprType = CLASS; 976 if (token == THIS) 977 className = getThisName(); 978 else 979 className = getSuperName(); 980 break; 981 default : 982 fatal(); 983 } 984 } 985 atStringL(StringL s)986 public void atStringL(StringL s) throws CompileError { 987 exprType = CLASS; 988 arrayDim = 0; 989 className = jvmJavaLangString; 990 } 991 atIntConst(IntConst i)992 public void atIntConst(IntConst i) throws CompileError { 993 arrayDim = 0; 994 int type = i.getType(); 995 if (type == IntConstant || type == CharConstant) 996 exprType = (type == IntConstant ? INT : CHAR); 997 else 998 exprType = LONG; 999 } 1000 atDoubleConst(DoubleConst d)1001 public void atDoubleConst(DoubleConst d) throws CompileError { 1002 arrayDim = 0; 1003 if (d.getType() == DoubleConstant) 1004 exprType = DOUBLE; 1005 else 1006 exprType = FLOAT; 1007 } 1008 } 1009