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 java.io.IOException; 20 import java.io.ObjectInputStream; 21 import java.io.ObjectOutputStream; 22 import java.io.Serializable; 23 import java.lang.reflect.Constructor; 24 import java.lang.reflect.InvocationTargetException; 25 import java.lang.reflect.Method; 26 import java.util.Arrays; 27 28 /** 29 * A runtime class metaobject. 30 * 31 * <p>A <code>ClassMetaobject</code> is created for every 32 * class of reflective objects. It can be used to hold values 33 * shared among the reflective objects of the same class. 34 * 35 * <p>To obtain a class metaobject, calls <code>_getClass()</code> 36 * on a reflective object. For example, 37 * 38 * <pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass(); 39 * </pre> 40 * 41 * @see javassist.tools.reflect.Metaobject 42 * @see javassist.tools.reflect.Metalevel 43 */ 44 public class ClassMetaobject implements Serializable { 45 /** default serialVersionUID */ 46 private static final long serialVersionUID = 1L; 47 /** 48 * The base-level methods controlled by a metaobject 49 * are renamed so that they begin with 50 * <code>methodPrefix "_m_"</code>. 51 */ 52 static final String methodPrefix = "_m_"; 53 static final int methodPrefixLen = 3; 54 55 private Class<?> javaClass; 56 private Constructor<?>[] constructors; 57 private Method[] methods; 58 59 /** 60 * Specifies how a <code>java.lang.Class</code> object is loaded. 61 * 62 * <p>If true, it is loaded by: 63 * <pre>Thread.currentThread().getContextClassLoader().loadClass()</pre> 64 * <p>If false, it is loaded by <code>Class.forName()</code>. 65 * The default value is false. 66 */ 67 public static boolean useContextClassLoader = false; 68 69 /** 70 * Constructs a <code>ClassMetaobject</code>. 71 * 72 * @param params <code>params[0]</code> is the name of the class 73 * of the reflective objects. 74 */ ClassMetaobject(String[] params)75 public ClassMetaobject(String[] params) 76 { 77 try { 78 javaClass = getClassObject(params[0]); 79 } 80 catch (ClassNotFoundException e) { 81 throw new RuntimeException("not found: " + params[0] 82 + ", useContextClassLoader: " 83 + Boolean.toString(useContextClassLoader), e); 84 } 85 86 constructors = javaClass.getConstructors(); 87 methods = null; 88 } 89 writeObject(ObjectOutputStream out)90 private void writeObject(ObjectOutputStream out) throws IOException { 91 out.writeUTF(javaClass.getName()); 92 } 93 readObject(ObjectInputStream in)94 private void readObject(ObjectInputStream in) 95 throws IOException, ClassNotFoundException 96 { 97 javaClass = getClassObject(in.readUTF()); 98 constructors = javaClass.getConstructors(); 99 methods = null; 100 } 101 getClassObject(String name)102 private Class<?> getClassObject(String name) throws ClassNotFoundException { 103 if (useContextClassLoader) 104 return Thread.currentThread().getContextClassLoader() 105 .loadClass(name); 106 return Class.forName(name); 107 } 108 109 /** 110 * Obtains the <code>java.lang.Class</code> representing this class. 111 */ getJavaClass()112 public final Class<?> getJavaClass() { 113 return javaClass; 114 } 115 116 /** 117 * Obtains the name of this class. 118 */ getName()119 public final String getName() { 120 return javaClass.getName(); 121 } 122 123 /** 124 * Returns true if <code>obj</code> is an instance of this class. 125 */ isInstance(Object obj)126 public final boolean isInstance(Object obj) { 127 return javaClass.isInstance(obj); 128 } 129 130 /** 131 * Creates a new instance of the class. 132 * 133 * @param args the arguments passed to the constructor. 134 */ newInstance(Object[] args)135 public final Object newInstance(Object[] args) 136 throws CannotCreateException 137 { 138 int n = constructors.length; 139 for (int i = 0; i < n; ++i) { 140 try { 141 return constructors[i].newInstance(args); 142 } 143 catch (IllegalArgumentException e) { 144 // try again 145 } 146 catch (InstantiationException e) { 147 throw new CannotCreateException(e); 148 } 149 catch (IllegalAccessException e) { 150 throw new CannotCreateException(e); 151 } 152 catch (InvocationTargetException e) { 153 throw new CannotCreateException(e); 154 } 155 } 156 157 throw new CannotCreateException("no constructor matches"); 158 } 159 160 /** 161 * Is invoked when <code>static</code> fields of the base-level 162 * class are read and the runtime system intercepts it. 163 * This method simply returns the value of the field. 164 * 165 * <p>Every subclass of this class should redefine this method. 166 */ trapFieldRead(String name)167 public Object trapFieldRead(String name) { 168 Class<?> jc = getJavaClass(); 169 try { 170 return jc.getField(name).get(null); 171 } 172 catch (NoSuchFieldException e) { 173 throw new RuntimeException(e.toString()); 174 } 175 catch (IllegalAccessException e) { 176 throw new RuntimeException(e.toString()); 177 } 178 } 179 180 /** 181 * Is invoked when <code>static</code> fields of the base-level 182 * class are modified and the runtime system intercepts it. 183 * This method simply sets the field to the given value. 184 * 185 * <p>Every subclass of this class should redefine this method. 186 */ trapFieldWrite(String name, Object value)187 public void trapFieldWrite(String name, Object value) { 188 Class<?> jc = getJavaClass(); 189 try { 190 jc.getField(name).set(null, value); 191 } 192 catch (NoSuchFieldException e) { 193 throw new RuntimeException(e.toString()); 194 } 195 catch (IllegalAccessException e) { 196 throw new RuntimeException(e.toString()); 197 } 198 } 199 200 /** 201 * Invokes a method whose name begins with 202 * <code>methodPrefix "_m_"</code> and the identifier. 203 * 204 * @exception CannotInvokeException if the invocation fails. 205 */ invoke(Object target, int identifier, Object[] args)206 static public Object invoke(Object target, int identifier, Object[] args) 207 throws Throwable 208 { 209 Method[] allmethods = target.getClass().getMethods(); 210 int n = allmethods.length; 211 String head = methodPrefix + identifier; 212 for (int i = 0; i < n; ++i) 213 if (allmethods[i].getName().startsWith(head)) { 214 try { 215 return allmethods[i].invoke(target, args); 216 } catch (java.lang.reflect.InvocationTargetException e) { 217 throw e.getTargetException(); 218 } catch (java.lang.IllegalAccessException e) { 219 throw new CannotInvokeException(e); 220 } 221 } 222 223 throw new CannotInvokeException("cannot find a method"); 224 } 225 226 /** 227 * Is invoked when <code>static</code> methods of the base-level 228 * class are called and the runtime system intercepts it. 229 * This method simply executes the intercepted method invocation 230 * with the original parameters and returns the resulting value. 231 * 232 * <p>Every subclass of this class should redefine this method. 233 */ trapMethodcall(int identifier, Object[] args)234 public Object trapMethodcall(int identifier, Object[] args) 235 throws Throwable 236 { 237 try { 238 Method[] m = getReflectiveMethods(); 239 return m[identifier].invoke(null, args); 240 } 241 catch (java.lang.reflect.InvocationTargetException e) { 242 throw e.getTargetException(); 243 } 244 catch (java.lang.IllegalAccessException e) { 245 throw new CannotInvokeException(e); 246 } 247 } 248 249 /** 250 * Returns an array of the methods defined on the given reflective 251 * object. This method is for the internal use only. 252 */ getReflectiveMethods()253 public final Method[] getReflectiveMethods() { 254 if (methods != null) 255 return methods; 256 257 Class<?> baseclass = getJavaClass(); 258 Method[] allmethods = baseclass.getDeclaredMethods(); 259 int n = allmethods.length; 260 int[] index = new int[n]; 261 int max = 0; 262 for (int i = 0; i < n; ++i) { 263 Method m = allmethods[i]; 264 String mname = m.getName(); 265 if (mname.startsWith(methodPrefix)) { 266 int k = 0; 267 for (int j = methodPrefixLen;; ++j) { 268 char c = mname.charAt(j); 269 if ('0' <= c && c <= '9') 270 k = k * 10 + c - '0'; 271 else 272 break; 273 } 274 275 index[i] = ++k; 276 if (k > max) 277 max = k; 278 } 279 } 280 281 methods = new Method[max]; 282 for (int i = 0; i < n; ++i) 283 if (index[i] > 0) 284 methods[index[i] - 1] = allmethods[i]; 285 286 return methods; 287 } 288 289 /** 290 * Returns the <code>java.lang.reflect.Method</code> object representing 291 * the method specified by <code>identifier</code>. 292 * 293 * <p>Note that the actual method returned will be have an altered, 294 * reflective name i.e. <code>_m_2_..</code>. 295 * 296 * @param identifier the identifier index 297 * given to <code>trapMethodcall()</code> etc. 298 * @see Metaobject#trapMethodcall(int,Object[]) 299 * @see #trapMethodcall(int,Object[]) 300 */ getMethod(int identifier)301 public final Method getMethod(int identifier) { 302 return getReflectiveMethods()[identifier]; 303 } 304 305 /** 306 * Returns the name of the method specified 307 * by <code>identifier</code>. 308 */ getMethodName(int identifier)309 public final String getMethodName(int identifier) { 310 String mname = getReflectiveMethods()[identifier].getName(); 311 int j = ClassMetaobject.methodPrefixLen; 312 for (;;) { 313 char c = mname.charAt(j++); 314 if (c < '0' || '9' < c) 315 break; 316 } 317 318 return mname.substring(j); 319 } 320 321 /** 322 * Returns an array of <code>Class</code> objects representing the 323 * formal parameter types of the method specified 324 * by <code>identifier</code>. 325 */ getParameterTypes(int identifier)326 public final Class<?>[] getParameterTypes(int identifier) { 327 return getReflectiveMethods()[identifier].getParameterTypes(); 328 } 329 330 /** 331 * Returns a <code>Class</code> objects representing the 332 * return type of the method specified by <code>identifier</code>. 333 */ getReturnType(int identifier)334 public final Class<?> getReturnType(int identifier) { 335 return getReflectiveMethods()[identifier].getReturnType(); 336 } 337 338 /** 339 * Returns the identifier index of the method, as identified by its 340 * original name. 341 * 342 * <p>This method is useful, in conjuction with 343 * {@link ClassMetaobject#getMethod(int)}, to obtain a quick reference 344 * to the original method in the reflected class (i.e. not the proxy 345 * method), using the original name of the method. 346 * 347 * <p>Written by Brett Randall and Shigeru Chiba. 348 * 349 * @param originalName The original name of the reflected method 350 * @param argTypes array of Class specifying the method signature 351 * @return the identifier index of the original method 352 * @throws NoSuchMethodException if the method does not exist 353 * 354 * @see ClassMetaobject#getMethod(int) 355 */ getMethodIndex(String originalName, Class<?>[] argTypes)356 public final int getMethodIndex(String originalName, Class<?>[] argTypes) 357 throws NoSuchMethodException 358 { 359 Method[] mthds = getReflectiveMethods(); 360 for (int i = 0; i < mthds.length; i++) { 361 if (mthds[i] == null) 362 continue; 363 364 // check name and parameter types match 365 if (getMethodName(i).equals(originalName) 366 && Arrays.equals(argTypes, mthds[i].getParameterTypes())) 367 return i; 368 } 369 370 throw new NoSuchMethodException("Method " + originalName 371 + " not found"); 372 } 373 } 374