You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MemberResolver.java 20KB

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