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