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

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