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