• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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