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 18KB

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