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

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