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.

Reflection.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999-2005 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. package javassist.tools.reflect;
  16. import javassist.*;
  17. import javassist.CtMethod.ConstParameter;
  18. /**
  19. * The class implementing the behavioral reflection mechanism.
  20. *
  21. * <p>If a class is reflective,
  22. * then all the method invocations on every
  23. * instance of that class are intercepted by the runtime
  24. * metaobject controlling that instance. The methods inherited from the
  25. * super classes are also intercepted except final methods. To intercept
  26. * a final method in a super class, that super class must be also reflective.
  27. *
  28. * <p>To do this, the original class file representing a reflective class:
  29. *
  30. * <ul><pre>
  31. * class Person {
  32. * public int f(int i) { return i + 1; }
  33. * public int value;
  34. * }
  35. * </pre></ul>
  36. *
  37. * <p>is modified so that it represents a class:
  38. *
  39. * <ul><pre>
  40. * class Person implements Metalevel {
  41. * public int _original_f(int i) { return i + 1; }
  42. * public int f(int i) { <i>delegate to the metaobject</i> }
  43. *
  44. * public int value;
  45. * public int _r_value() { <i>read "value"</i> }
  46. * public void _w_value(int v) { <i>write "value"</i> }
  47. *
  48. * public ClassMetaobject _getClass() { <i>return a class metaobject</i> }
  49. * public Metaobject _getMetaobject() { <i>return a metaobject</i> }
  50. * public void _setMetaobject(Metaobject m) { <i>change a metaobject</i> }
  51. * }
  52. * </pre></ul>
  53. *
  54. * @see javassist.tools.reflect.ClassMetaobject
  55. * @see javassist.tools.reflect.Metaobject
  56. * @see javassist.tools.reflect.Loader
  57. * @see javassist.tools.reflect.Compiler
  58. */
  59. public class Reflection implements Translator {
  60. static final String classobjectField = "_classobject";
  61. static final String classobjectAccessor = "_getClass";
  62. static final String metaobjectField = "_metaobject";
  63. static final String metaobjectGetter = "_getMetaobject";
  64. static final String metaobjectSetter = "_setMetaobject";
  65. static final String readPrefix = "_r_";
  66. static final String writePrefix = "_w_";
  67. static final String metaobjectClassName = "javassist.tools.reflect.Metaobject";
  68. static final String classMetaobjectClassName
  69. = "javassist.tools.reflect.ClassMetaobject";
  70. protected CtMethod trapMethod, trapStaticMethod;
  71. protected CtMethod trapRead, trapWrite;
  72. protected CtClass[] readParam;
  73. protected ClassPool classPool;
  74. protected CodeConverter converter;
  75. private boolean isExcluded(String name) {
  76. return name.startsWith(ClassMetaobject.methodPrefix)
  77. || name.equals(classobjectAccessor)
  78. || name.equals(metaobjectSetter)
  79. || name.equals(metaobjectGetter)
  80. || name.startsWith(readPrefix)
  81. || name.startsWith(writePrefix);
  82. }
  83. /**
  84. * Constructs a new <code>Reflection</code> object.
  85. */
  86. public Reflection() {
  87. classPool = null;
  88. converter = new CodeConverter();
  89. }
  90. /**
  91. * Initializes the object.
  92. */
  93. public void start(ClassPool pool) throws NotFoundException {
  94. classPool = pool;
  95. final String msg
  96. = "javassist.tools.reflect.Sample is not found or broken.";
  97. try {
  98. CtClass c = classPool.get("javassist.tools.reflect.Sample");
  99. trapMethod = c.getDeclaredMethod("trap");
  100. trapStaticMethod = c.getDeclaredMethod("trapStatic");
  101. trapRead = c.getDeclaredMethod("trapRead");
  102. trapWrite = c.getDeclaredMethod("trapWrite");
  103. readParam
  104. = new CtClass[] { classPool.get("java.lang.Object") };
  105. }
  106. catch (NotFoundException e) {
  107. throw new RuntimeException(msg);
  108. }
  109. }
  110. /**
  111. * Inserts hooks for intercepting accesses to the fields declared
  112. * in reflective classes.
  113. */
  114. public void onLoad(ClassPool pool, String classname)
  115. throws CannotCompileException, NotFoundException
  116. {
  117. CtClass clazz = pool.get(classname);
  118. clazz.instrument(converter);
  119. }
  120. /**
  121. * Produces a reflective class.
  122. * If the super class is also made reflective, it must be done
  123. * before the sub class.
  124. *
  125. * @param classname the name of the reflective class
  126. * @param metaobject the class name of metaobjects.
  127. * @param metaclass the class name of the class metaobject.
  128. * @return <code>false</code> if the class is already reflective.
  129. *
  130. * @see javassist.tools.reflect.Metaobject
  131. * @see javassist.tools.reflect.ClassMetaobject
  132. */
  133. public boolean makeReflective(String classname,
  134. String metaobject, String metaclass)
  135. throws CannotCompileException, NotFoundException
  136. {
  137. return makeReflective(classPool.get(classname),
  138. classPool.get(metaobject),
  139. classPool.get(metaclass));
  140. }
  141. /**
  142. * Produces a reflective class.
  143. * If the super class is also made reflective, it must be done
  144. * before the sub class.
  145. *
  146. * @param clazz the reflective class.
  147. * @param metaobject the class of metaobjects.
  148. * It must be a subclass of
  149. * <code>Metaobject</code>.
  150. * @param metaclass the class of the class metaobject.
  151. * It must be a subclass of
  152. * <code>ClassMetaobject</code>.
  153. * @return <code>false</code> if the class is already reflective.
  154. *
  155. * @see javassist.tools.reflect.Metaobject
  156. * @see javassist.tools.reflect.ClassMetaobject
  157. */
  158. public boolean makeReflective(Class clazz,
  159. Class metaobject, Class metaclass)
  160. throws CannotCompileException, NotFoundException
  161. {
  162. return makeReflective(clazz.getName(), metaobject.getName(),
  163. metaclass.getName());
  164. }
  165. /**
  166. * Produces a reflective class. It modifies the given
  167. * <code>CtClass</code> object and makes it reflective.
  168. * If the super class is also made reflective, it must be done
  169. * before the sub class.
  170. *
  171. * @param clazz the reflective class.
  172. * @param metaobject the class of metaobjects.
  173. * It must be a subclass of
  174. * <code>Metaobject</code>.
  175. * @param metaclass the class of the class metaobject.
  176. * It must be a subclass of
  177. * <code>ClassMetaobject</code>.
  178. * @return <code>false</code> if the class is already reflective.
  179. *
  180. * @see javassist.tools.reflect.Metaobject
  181. * @see javassist.tools.reflect.ClassMetaobject
  182. */
  183. public boolean makeReflective(CtClass clazz,
  184. CtClass metaobject, CtClass metaclass)
  185. throws CannotCompileException, CannotReflectException,
  186. NotFoundException
  187. {
  188. if (clazz.isInterface())
  189. throw new CannotReflectException(
  190. "Cannot reflect an interface: " + clazz.getName());
  191. if (clazz.subclassOf(classPool.get(classMetaobjectClassName)))
  192. throw new CannotReflectException(
  193. "Cannot reflect a subclass of ClassMetaobject: "
  194. + clazz.getName());
  195. if (clazz.subclassOf(classPool.get(metaobjectClassName)))
  196. throw new CannotReflectException(
  197. "Cannot reflect a subclass of Metaobject: "
  198. + clazz.getName());
  199. registerReflectiveClass(clazz);
  200. return modifyClassfile(clazz, metaobject, metaclass);
  201. }
  202. /**
  203. * Registers a reflective class. The field accesses to the instances
  204. * of this class are instrumented.
  205. */
  206. private void registerReflectiveClass(CtClass clazz) {
  207. CtField[] fs = clazz.getDeclaredFields();
  208. for (int i = 0; i < fs.length; ++i) {
  209. CtField f = fs[i];
  210. int mod = f.getModifiers();
  211. if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
  212. String name = f.getName();
  213. converter.replaceFieldRead(f, clazz, readPrefix + name);
  214. converter.replaceFieldWrite(f, clazz, writePrefix + name);
  215. }
  216. }
  217. }
  218. private boolean modifyClassfile(CtClass clazz, CtClass metaobject,
  219. CtClass metaclass)
  220. throws CannotCompileException, NotFoundException
  221. {
  222. if (clazz.getAttribute("Reflective") != null)
  223. return false; // this is already reflective.
  224. else
  225. clazz.setAttribute("Reflective", new byte[0]);
  226. CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel");
  227. boolean addMeta = !clazz.subtypeOf(mlevel);
  228. if (addMeta)
  229. clazz.addInterface(mlevel);
  230. processMethods(clazz, addMeta);
  231. processFields(clazz);
  232. CtField f;
  233. if (addMeta) {
  234. f = new CtField(classPool.get("javassist.tools.reflect.Metaobject"),
  235. metaobjectField, clazz);
  236. f.setModifiers(Modifier.PROTECTED);
  237. clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject));
  238. clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f));
  239. clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f));
  240. }
  241. f = new CtField(classPool.get("javassist.tools.reflect.ClassMetaobject"),
  242. classobjectField, clazz);
  243. f.setModifiers(Modifier.PRIVATE | Modifier.STATIC);
  244. clazz.addField(f, CtField.Initializer.byNew(metaclass,
  245. new String[] { clazz.getName() }));
  246. clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f));
  247. return true;
  248. }
  249. private void processMethods(CtClass clazz, boolean dontSearch)
  250. throws CannotCompileException, NotFoundException
  251. {
  252. CtMethod[] ms = clazz.getMethods();
  253. for (int i = 0; i < ms.length; ++i) {
  254. CtMethod m = ms[i];
  255. int mod = m.getModifiers();
  256. if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod))
  257. processMethods0(mod, clazz, m, i, dontSearch);
  258. }
  259. }
  260. private void processMethods0(int mod, CtClass clazz,
  261. CtMethod m, int identifier, boolean dontSearch)
  262. throws CannotCompileException, NotFoundException
  263. {
  264. CtMethod body;
  265. String name = m.getName();
  266. if (isExcluded(name)) // internally-used method inherited
  267. return; // from a reflective class.
  268. CtMethod m2;
  269. if (m.getDeclaringClass() == clazz) {
  270. if (Modifier.isNative(mod))
  271. return;
  272. m2 = m;
  273. if (Modifier.isFinal(mod)) {
  274. mod &= ~Modifier.FINAL;
  275. m2.setModifiers(mod);
  276. }
  277. }
  278. else {
  279. if (Modifier.isFinal(mod))
  280. return;
  281. mod &= ~Modifier.NATIVE;
  282. m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz);
  283. m2.setModifiers(mod);
  284. clazz.addMethod(m2);
  285. }
  286. m2.setName(ClassMetaobject.methodPrefix + identifier
  287. + "_" + name);
  288. if (Modifier.isStatic(mod))
  289. body = trapStaticMethod;
  290. else
  291. body = trapMethod;
  292. CtMethod wmethod
  293. = CtNewMethod.wrapped(m.getReturnType(), name,
  294. m.getParameterTypes(), m.getExceptionTypes(),
  295. body, ConstParameter.integer(identifier),
  296. clazz);
  297. wmethod.setModifiers(mod);
  298. clazz.addMethod(wmethod);
  299. }
  300. private CtMethod findOriginal(CtMethod m, boolean dontSearch)
  301. throws NotFoundException
  302. {
  303. if (dontSearch)
  304. return m;
  305. String name = m.getName();
  306. CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods();
  307. for (int i = 0; i < ms.length; ++i) {
  308. String orgName = ms[i].getName();
  309. if (orgName.endsWith(name)
  310. && orgName.startsWith(ClassMetaobject.methodPrefix)
  311. && ms[i].getSignature().equals(m.getSignature()))
  312. return ms[i];
  313. }
  314. return m;
  315. }
  316. private void processFields(CtClass clazz)
  317. throws CannotCompileException, NotFoundException
  318. {
  319. CtField[] fs = clazz.getDeclaredFields();
  320. for (int i = 0; i < fs.length; ++i) {
  321. CtField f = fs[i];
  322. int mod = f.getModifiers();
  323. if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
  324. mod |= Modifier.STATIC;
  325. String name = f.getName();
  326. CtClass ftype = f.getType();
  327. CtMethod wmethod
  328. = CtNewMethod.wrapped(ftype, readPrefix + name,
  329. readParam, null, trapRead,
  330. ConstParameter.string(name),
  331. clazz);
  332. wmethod.setModifiers(mod);
  333. clazz.addMethod(wmethod);
  334. CtClass[] writeParam = new CtClass[2];
  335. writeParam[0] = classPool.get("java.lang.Object");
  336. writeParam[1] = ftype;
  337. wmethod = CtNewMethod.wrapped(CtClass.voidType,
  338. writePrefix + name,
  339. writeParam, null, trapWrite,
  340. ConstParameter.string(name), clazz);
  341. wmethod.setModifiers(mod);
  342. clazz.addMethod(wmethod);
  343. }
  344. }
  345. }
  346. }