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