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