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 java.util.List; 19 import java.util.Iterator; 20 import javassist.*; 21 import javassist.bytecode.*; 22 import javassist.compiler.ast.*; 23 24 /* Code generator methods depending on javassist.* classes. 25 */ 26 public class MemberResolver implements TokenId { 27 private ClassPool classPool; 28 MemberResolver(ClassPool cp)29 public MemberResolver(ClassPool cp) { 30 classPool = cp; 31 } 32 getClassPool()33 public ClassPool getClassPool() { return classPool; } 34 fatal()35 private static void fatal() throws CompileError { 36 throw new CompileError("fatal"); 37 } 38 39 /** 40 * @param jvmClassName a class name. Not a package name. 41 */ recordPackage(String jvmClassName)42 public void recordPackage(String jvmClassName) { 43 String classname = jvmToJavaName(jvmClassName); 44 for (;;) { 45 int i = classname.lastIndexOf('.'); 46 if (i > 0) { 47 classname = classname.substring(0, i); 48 classPool.recordInvalidClassName(classname); 49 } 50 else 51 break; 52 } 53 } 54 55 public static class Method { 56 public CtClass declaring; 57 public MethodInfo info; 58 public int notmatch; 59 Method(CtClass c, MethodInfo i, int n)60 public Method(CtClass c, MethodInfo i, int n) { 61 declaring = c; 62 info = i; 63 notmatch = n; 64 } 65 66 /** 67 * Returns true if the invoked method is static. 68 */ isStatic()69 public boolean isStatic() { 70 int acc = info.getAccessFlags(); 71 return (acc & AccessFlag.STATIC) != 0; 72 } 73 } 74 lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, String methodName, int[] argTypes, int[] argDims, String[] argClassNames)75 public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, 76 String methodName, 77 int[] argTypes, int[] argDims, 78 String[] argClassNames) 79 throws CompileError 80 { 81 Method maybe = null; 82 // to enable the creation of a recursively called method 83 if (current != null && clazz == currentClass) 84 if (current.getName().equals(methodName)) { 85 int res = compareSignature(current.getDescriptor(), 86 argTypes, argDims, argClassNames); 87 if (res != NO) { 88 Method r = new Method(clazz, current, res); 89 if (res == YES) 90 return r; 91 else 92 maybe = r; 93 } 94 } 95 96 Method m = lookupMethod(clazz, methodName, argTypes, argDims, 97 argClassNames, maybe != null); 98 if (m != null) 99 return m; 100 else 101 return maybe; 102 } 103 lookupMethod(CtClass clazz, String methodName, int[] argTypes, int[] argDims, String[] argClassNames, boolean onlyExact)104 private Method lookupMethod(CtClass clazz, String methodName, 105 int[] argTypes, int[] argDims, 106 String[] argClassNames, boolean onlyExact) 107 throws CompileError 108 { 109 Method maybe = null; 110 ClassFile cf = clazz.getClassFile2(); 111 // If the class is an array type, the class file is null. 112 // If so, search the super class java.lang.Object for clone() etc. 113 if (cf != null) { 114 List list = cf.getMethods(); 115 int n = list.size(); 116 for (int i = 0; i < n; ++i) { 117 MethodInfo minfo = (MethodInfo)list.get(i); 118 if (minfo.getName().equals(methodName)) { 119 int res = compareSignature(minfo.getDescriptor(), 120 argTypes, argDims, argClassNames); 121 if (res != NO) { 122 Method r = new Method(clazz, minfo, res); 123 if (res == YES) 124 return r; 125 else if (maybe == null || maybe.notmatch > res) 126 maybe = r; 127 } 128 } 129 } 130 } 131 132 if (onlyExact) 133 maybe = null; 134 else 135 onlyExact = maybe != null; 136 137 int mod = clazz.getModifiers(); 138 boolean isIntf = Modifier.isInterface(mod); 139 try { 140 // skip searching java.lang.Object if clazz is an interface type. 141 if (!isIntf) { 142 CtClass pclazz = clazz.getSuperclass(); 143 if (pclazz != null) { 144 Method r = lookupMethod(pclazz, methodName, argTypes, 145 argDims, argClassNames, onlyExact); 146 if (r != null) 147 return r; 148 } 149 } 150 } 151 catch (NotFoundException e) {} 152 153 if (isIntf || Modifier.isAbstract(mod)) 154 try { 155 CtClass[] ifs = clazz.getInterfaces(); 156 int size = ifs.length; 157 for (int i = 0; i < size; ++i) { 158 Method r = lookupMethod(ifs[i], methodName, 159 argTypes, argDims, argClassNames, 160 onlyExact); 161 if (r != null) 162 return r; 163 } 164 165 if (isIntf) { 166 // finally search java.lang.Object. 167 CtClass pclazz = clazz.getSuperclass(); 168 if (pclazz != null) { 169 Method r = lookupMethod(pclazz, methodName, argTypes, 170 argDims, argClassNames, onlyExact); 171 if (r != null) 172 return r; 173 } 174 } 175 } 176 catch (NotFoundException e) {} 177 178 return maybe; 179 } 180 181 private static final int YES = 0; 182 private static final int NO = -1; 183 184 /* 185 * Returns YES if actual parameter types matches the given signature. 186 * 187 * argTypes, argDims, and argClassNames represent actual parameters. 188 * 189 * This method does not correctly implement the Java method dispatch 190 * algorithm. 191 * 192 * If some of the parameter types exactly match but others are subtypes of 193 * the corresponding type in the signature, this method returns the number 194 * of parameter types that do not exactly match. 195 */ compareSignature(String desc, int[] argTypes, int[] argDims, String[] argClassNames)196 private int compareSignature(String desc, int[] argTypes, 197 int[] argDims, String[] argClassNames) 198 throws CompileError 199 { 200 int result = YES; 201 int i = 1; 202 int nArgs = argTypes.length; 203 if (nArgs != Descriptor.numOfParameters(desc)) 204 return NO; 205 206 int len = desc.length(); 207 for (int n = 0; i < len; ++n) { 208 char c = desc.charAt(i++); 209 if (c == ')') 210 return (n == nArgs ? result : NO); 211 else if (n >= nArgs) 212 return NO; 213 214 int dim = 0; 215 while (c == '[') { 216 ++dim; 217 c = desc.charAt(i++); 218 } 219 220 if (argTypes[n] == NULL) { 221 if (dim == 0 && c != 'L') 222 return NO; 223 224 if (c == 'L') 225 i = desc.indexOf(';', i) + 1; 226 } 227 else if (argDims[n] != dim) { 228 if (!(dim == 0 && c == 'L' 229 && desc.startsWith("java/lang/Object;", i))) 230 return NO; 231 232 // if the thread reaches here, c must be 'L'. 233 i = desc.indexOf(';', i) + 1; 234 result++; 235 if (i <= 0) 236 return NO; // invalid descriptor? 237 } 238 else if (c == 'L') { // not compare 239 int j = desc.indexOf(';', i); 240 if (j < 0 || argTypes[n] != CLASS) 241 return NO; 242 243 String cname = desc.substring(i, j); 244 if (!cname.equals(argClassNames[n])) { 245 CtClass clazz = lookupClassByJvmName(argClassNames[n]); 246 try { 247 if (clazz.subtypeOf(lookupClassByJvmName(cname))) 248 result++; 249 else 250 return NO; 251 } 252 catch (NotFoundException e) { 253 result++; // should be NO? 254 } 255 } 256 257 i = j + 1; 258 } 259 else { 260 int t = descToType(c); 261 int at = argTypes[n]; 262 if (t != at) 263 if (t == INT 264 && (at == SHORT || at == BYTE || at == CHAR)) 265 result++; 266 else 267 return NO; 268 } 269 } 270 271 return NO; 272 } 273 274 /** 275 * Only used by fieldAccess() in MemberCodeGen and TypeChecker. 276 * 277 * @param jvmClassName a JVM class name. e.g. java/lang/String 278 */ lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, ASTree expr)279 public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, 280 ASTree expr) throws NoFieldException 281 { 282 String field = fieldSym.get(); 283 CtClass cc = null; 284 try { 285 cc = lookupClass(jvmToJavaName(jvmClassName), true); 286 } 287 catch (CompileError e) { 288 // EXPR might be part of a qualified class name. 289 throw new NoFieldException(jvmClassName + "/" + field, expr); 290 } 291 292 try { 293 return cc.getField(field); 294 } 295 catch (NotFoundException e) { 296 // maybe an inner class. 297 jvmClassName = javaToJvmName(cc.getName()); 298 throw new NoFieldException(jvmClassName + "$" + field, expr); 299 } 300 } 301 302 /** 303 * @param jvmClassName a JVM class name. e.g. java/lang/String 304 */ lookupFieldByJvmName(String jvmClassName, Symbol fieldName)305 public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName) 306 throws CompileError 307 { 308 return lookupField(jvmToJavaName(jvmClassName), fieldName); 309 } 310 311 /** 312 * @param name a qualified class name. e.g. java.lang.String 313 */ lookupField(String className, Symbol fieldName)314 public CtField lookupField(String className, Symbol fieldName) 315 throws CompileError 316 { 317 CtClass cc = lookupClass(className, false); 318 try { 319 return cc.getField(fieldName.get()); 320 } 321 catch (NotFoundException e) {} 322 throw new CompileError("no such field: " + fieldName.get()); 323 } 324 lookupClassByName(ASTList name)325 public CtClass lookupClassByName(ASTList name) throws CompileError { 326 return lookupClass(Declarator.astToClassName(name, '.'), false); 327 } 328 lookupClassByJvmName(String jvmName)329 public CtClass lookupClassByJvmName(String jvmName) throws CompileError { 330 return lookupClass(jvmToJavaName(jvmName), false); 331 } 332 lookupClass(Declarator decl)333 public CtClass lookupClass(Declarator decl) throws CompileError { 334 return lookupClass(decl.getType(), decl.getArrayDim(), 335 decl.getClassName()); 336 } 337 338 /** 339 * @parma classname jvm class name. 340 */ lookupClass(int type, int dim, String classname)341 public CtClass lookupClass(int type, int dim, String classname) 342 throws CompileError 343 { 344 String cname = ""; 345 CtClass clazz; 346 if (type == CLASS) { 347 clazz = lookupClassByJvmName(classname); 348 if (dim > 0) 349 cname = clazz.getName(); 350 else 351 return clazz; 352 } 353 else 354 cname = getTypeName(type); 355 356 while (dim-- > 0) 357 cname += "[]"; 358 359 return lookupClass(cname, false); 360 } 361 362 /* 363 * type cannot be CLASS 364 */ getTypeName(int type)365 static String getTypeName(int type) throws CompileError { 366 String cname = ""; 367 switch (type) { 368 case BOOLEAN : 369 cname = "boolean"; 370 break; 371 case CHAR : 372 cname = "char"; 373 break; 374 case BYTE : 375 cname = "byte"; 376 break; 377 case SHORT : 378 cname = "short"; 379 break; 380 case INT : 381 cname = "int"; 382 break; 383 case LONG : 384 cname = "long"; 385 break; 386 case FLOAT : 387 cname = "float"; 388 break; 389 case DOUBLE : 390 cname = "double"; 391 break; 392 case VOID : 393 cname = "void"; 394 break; 395 default : 396 fatal(); 397 } 398 399 return cname; 400 } 401 402 /** 403 * @param name a qualified class name. e.g. java.lang.String 404 */ lookupClass(String name, boolean notCheckInner)405 public CtClass lookupClass(String name, boolean notCheckInner) 406 throws CompileError 407 { 408 try { 409 return lookupClass0(name, notCheckInner); 410 } 411 catch (NotFoundException e) { 412 return searchImports(name); 413 } 414 } 415 searchImports(String orgName)416 private CtClass searchImports(String orgName) 417 throws CompileError 418 { 419 if (orgName.indexOf('.') < 0) { 420 Iterator it = classPool.getImportedPackages(); 421 while (it.hasNext()) { 422 String pac = (String)it.next(); 423 String fqName = pac + '.' + orgName; 424 try { 425 CtClass cc = classPool.get(fqName); 426 // if the class is found, 427 classPool.recordInvalidClassName(orgName); 428 return cc; 429 } 430 catch (NotFoundException e) { 431 classPool.recordInvalidClassName(fqName); 432 try { 433 if (pac.endsWith("." + orgName)) { 434 CtClass cc = classPool.get(pac); 435 // if the class is found, 436 classPool.recordInvalidClassName(orgName); 437 return cc; 438 } 439 } 440 catch (NotFoundException e2) { 441 classPool.recordInvalidClassName(pac); 442 } 443 } 444 } 445 } 446 447 throw new CompileError("no such class: " + orgName); 448 } 449 lookupClass0(String classname, boolean notCheckInner)450 private CtClass lookupClass0(String classname, boolean notCheckInner) 451 throws NotFoundException 452 { 453 CtClass cc = null; 454 do { 455 try { 456 cc = classPool.get(classname); 457 } 458 catch (NotFoundException e) { 459 int i = classname.lastIndexOf('.'); 460 if (notCheckInner || i < 0) 461 throw e; 462 else { 463 StringBuffer sbuf = new StringBuffer(classname); 464 sbuf.setCharAt(i, '$'); 465 classname = sbuf.toString(); 466 } 467 } 468 } while (cc == null); 469 return cc; 470 } 471 472 /* Converts a class name into a JVM-internal representation. 473 * 474 * It may also expand a simple class name to java.lang.*. 475 * For example, this converts Object into java/lang/Object. 476 */ resolveClassName(ASTList name)477 public String resolveClassName(ASTList name) throws CompileError { 478 if (name == null) 479 return null; 480 else 481 return javaToJvmName(lookupClassByName(name).getName()); 482 } 483 484 /* Expands a simple class name to java.lang.*. 485 * For example, this converts Object into java/lang/Object. 486 */ resolveJvmClassName(String jvmName)487 public String resolveJvmClassName(String jvmName) throws CompileError { 488 if (jvmName == null) 489 return null; 490 else 491 return javaToJvmName(lookupClassByJvmName(jvmName).getName()); 492 } 493 getSuperclass(CtClass c)494 public static CtClass getSuperclass(CtClass c) throws CompileError { 495 try { 496 CtClass sc = c.getSuperclass(); 497 if (sc != null) 498 return sc; 499 } 500 catch (NotFoundException e) {} 501 throw new CompileError("cannot find the super class of " 502 + c.getName()); 503 } 504 javaToJvmName(String classname)505 public static String javaToJvmName(String classname) { 506 return classname.replace('.', '/'); 507 } 508 jvmToJavaName(String classname)509 public static String jvmToJavaName(String classname) { 510 return classname.replace('/', '.'); 511 } 512 descToType(char c)513 public static int descToType(char c) throws CompileError { 514 switch (c) { 515 case 'Z' : 516 return BOOLEAN; 517 case 'C' : 518 return CHAR; 519 case 'B' : 520 return BYTE; 521 case 'S' : 522 return SHORT; 523 case 'I' : 524 return INT; 525 case 'J' : 526 return LONG; 527 case 'F' : 528 return FLOAT; 529 case 'D' : 530 return DOUBLE; 531 case 'V' : 532 return VOID; 533 case 'L' : 534 case '[' : 535 return CLASS; 536 default : 537 fatal(); 538 return VOID; // never reach here 539 } 540 } 541 getModifiers(ASTList mods)542 public static int getModifiers(ASTList mods) { 543 int m = 0; 544 while (mods != null) { 545 Keyword k = (Keyword)mods.head(); 546 mods = mods.tail(); 547 switch (k.get()) { 548 case STATIC : 549 m |= Modifier.STATIC; 550 break; 551 case FINAL : 552 m |= Modifier.FINAL; 553 break; 554 case SYNCHRONIZED : 555 m |= Modifier.SYNCHRONIZED; 556 break; 557 case ABSTRACT : 558 m |= Modifier.ABSTRACT; 559 break; 560 case PUBLIC : 561 m |= Modifier.PUBLIC; 562 break; 563 case PROTECTED : 564 m |= Modifier.PROTECTED; 565 break; 566 case PRIVATE : 567 m |= Modifier.PRIVATE; 568 break; 569 case VOLATILE : 570 m |= Modifier.VOLATILE; 571 break; 572 case TRANSIENT : 573 m |= Modifier.TRANSIENT; 574 break; 575 case STRICT : 576 m |= Modifier.STRICT; 577 break; 578 } 579 } 580 581 return m; 582 } 583 } 584