1 | /* Proxy.java -- build a proxy class that implements reflected interfaces
|
---|
2 | Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
---|
3 |
|
---|
4 | This file is part of GNU Classpath.
|
---|
5 |
|
---|
6 | GNU Classpath is free software; you can redistribute it and/or modify
|
---|
7 | it under the terms of the GNU General Public License as published by
|
---|
8 | the Free Software Foundation; either version 2, or (at your option)
|
---|
9 | any later version.
|
---|
10 |
|
---|
11 | GNU Classpath is distributed in the hope that it will be useful, but
|
---|
12 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
14 | General Public License for more details.
|
---|
15 |
|
---|
16 | You should have received a copy of the GNU General Public License
|
---|
17 | along with GNU Classpath; see the file COPYING. If not, write to the
|
---|
18 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
---|
19 | 02111-1307 USA.
|
---|
20 |
|
---|
21 | Linking this library statically or dynamically with other modules is
|
---|
22 | making a combined work based on this library. Thus, the terms and
|
---|
23 | conditions of the GNU General Public License cover the whole
|
---|
24 | combination.
|
---|
25 |
|
---|
26 | As a special exception, the copyright holders of this library give you
|
---|
27 | permission to link this library with independent modules to produce an
|
---|
28 | executable, regardless of the license terms of these independent
|
---|
29 | modules, and to copy and distribute the resulting executable under
|
---|
30 | terms of your choice, provided that you also meet, for each linked
|
---|
31 | independent module, the terms and conditions of the license of that
|
---|
32 | module. An independent module is a module which is not derived from
|
---|
33 | or based on this library. If you modify this library, you may extend
|
---|
34 | this exception to your version of the library, but you are not
|
---|
35 | obligated to do so. If you do not wish to do so, delete this
|
---|
36 | exception statement from your version. */
|
---|
37 |
|
---|
38 |
|
---|
39 | package java.lang.reflect;
|
---|
40 |
|
---|
41 | import java.io.Serializable;
|
---|
42 | import java.security.ProtectionDomain;
|
---|
43 | import java.util.Map;
|
---|
44 | import java.util.HashMap;
|
---|
45 | import java.util.Set;
|
---|
46 | import java.util.HashSet;
|
---|
47 | import java.util.Iterator;
|
---|
48 | import gnu.classpath.Configuration;
|
---|
49 | import gnu.java.lang.reflect.TypeSignature;
|
---|
50 |
|
---|
51 | /**
|
---|
52 | * This class allows you to dynamically create an instance of any (or
|
---|
53 | * even multiple) interfaces by reflection, and decide at runtime
|
---|
54 | * how that instance will behave by giving it an appropriate
|
---|
55 | * {@link InvocationHandler}. Proxy classes serialize specially, so
|
---|
56 | * that the proxy object can be reused between VMs, without requiring
|
---|
57 | * a persistent copy of the generated class code.
|
---|
58 | *
|
---|
59 | * <h3>Creation</h3>
|
---|
60 | * To create a proxy for some interface Foo:
|
---|
61 | *
|
---|
62 | * <pre>
|
---|
63 | * InvocationHandler handler = new MyInvocationHandler(...);
|
---|
64 | * Class proxyClass = Proxy.getProxyClass(
|
---|
65 | * Foo.class.getClassLoader(), new Class[] { Foo.class });
|
---|
66 | * Foo f = (Foo) proxyClass
|
---|
67 | * .getConstructor(new Class[] { InvocationHandler.class })
|
---|
68 | * .newInstance(new Object[] { handler });
|
---|
69 | * </pre>
|
---|
70 | * or more simply:
|
---|
71 | * <pre>
|
---|
72 | * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
|
---|
73 | * new Class[] { Foo.class },
|
---|
74 | * handler);
|
---|
75 | * </pre>
|
---|
76 | *
|
---|
77 | * <h3>Dynamic Proxy Classes</h3>
|
---|
78 | * A dynamic proxy class is created at runtime, and has the following
|
---|
79 | * properties:
|
---|
80 | * <ul>
|
---|
81 | * <li>The class is <code>public</code> and <code>final</code>,
|
---|
82 | * and is neither <code>abstract</code> nor an inner class.</li>
|
---|
83 | * <li>The class has no canonical name (there is no formula you can use
|
---|
84 | * to determine or generate its name), but begins with the
|
---|
85 | * sequence "$Proxy". Abuse this knowledge at your own peril.
|
---|
86 | * (For now, '$' in user identifiers is legal, but it may not
|
---|
87 | * be that way forever. You weren't using '$' in your
|
---|
88 | * identifiers, were you?)</li>
|
---|
89 | * <li>The class extends Proxy, and explicitly implements all the
|
---|
90 | * interfaces specified at creation, in order (this is important
|
---|
91 | * for determining how method invocation is resolved). Note that
|
---|
92 | * a proxy class implements {@link Serializable}, at least
|
---|
93 | * implicitly, since Proxy does, but true serial behavior
|
---|
94 | * depends on using a serializable invocation handler as well.</li>
|
---|
95 | * <li>If at least one interface is non-public, the proxy class
|
---|
96 | * will be in the same package. Otherwise, the package is
|
---|
97 | * unspecified. This will work even if the package is sealed
|
---|
98 | * from user-generated classes, because Proxy classes are
|
---|
99 | * generated by a trusted source. Meanwhile, the proxy class
|
---|
100 | * belongs to the classloader you designated.</li>
|
---|
101 | * <li>Reflection works as expected: {@link Class#getInterfaces()} and
|
---|
102 | * {@link Class#getMethods()} work as they do on normal classes.</li>
|
---|
103 | * <li>The method {@link #isProxyClass()} will distinguish between
|
---|
104 | * true proxy classes and user extensions of this class. It only
|
---|
105 | * returns true for classes created by {@link #getProxyClass}.</li>
|
---|
106 | * <li>The {@link ProtectionDomain} of a proxy class is the same as for
|
---|
107 | * bootstrap classes, such as Object or Proxy, since it is created by
|
---|
108 | * a trusted source. This protection domain will typically be granted
|
---|
109 | * {@link java.security.AllPermission}. But this is not a security
|
---|
110 | * risk, since there are adequate permissions on reflection, which is
|
---|
111 | * the only way to create an instance of the proxy class.</li>
|
---|
112 | * <li>The proxy class contains a single constructor, which takes as
|
---|
113 | * its only argument an {@link InvocationHandler}. The method
|
---|
114 | * {@link #newInstance} is shorthand to do the necessary
|
---|
115 | * reflection.</li>
|
---|
116 | * </ul>
|
---|
117 | *
|
---|
118 | * <h3>Proxy Instances</h3>
|
---|
119 | * A proxy instance is an instance of a proxy class. It has the
|
---|
120 | * following properties, many of which follow from the properties of a
|
---|
121 | * proxy class listed above:
|
---|
122 | * <ul>
|
---|
123 | * <li>For a proxy class with Foo listed as one of its interfaces, the
|
---|
124 | * expression <code>proxy instanceof Foo</code> will return true,
|
---|
125 | * and the expression <code>(Foo) proxy</code> will succeed without
|
---|
126 | * a {@link ClassCastException}.</li>
|
---|
127 | * <li>Each proxy instance has an invocation handler, which can be
|
---|
128 | * accessed by {@link #getInvocationHandler(Object)}. Any call
|
---|
129 | * to an interface method, including {@link Object#hashcode()},
|
---|
130 | * {@link Object#equals(Object)}, or {@link Object#toString()},
|
---|
131 | * but excluding the public final methods of Object, will be
|
---|
132 | * encoded and passed to the {@link InvocationHandler#invoke}
|
---|
133 | * method of this handler.</li>
|
---|
134 | * </ul>
|
---|
135 | *
|
---|
136 | * <h3>Inheritance Issues</h3>
|
---|
137 | * A proxy class may inherit a method from more than one interface.
|
---|
138 | * The order in which interfaces are listed matters, because it determines
|
---|
139 | * which reflected {@link Method} object will be passed to the invocation
|
---|
140 | * handler. This means that the dynamically generated class cannot
|
---|
141 | * determine through which interface a method is being invoked.<p>
|
---|
142 | *
|
---|
143 | * In short, if a method is declared in Object (namely, hashCode,
|
---|
144 | * equals, or toString), then Object will be used; otherwise, the
|
---|
145 | * leftmost interface that inherits or declares a method will be used,
|
---|
146 | * even if it has a more permissive throws clause than what the proxy
|
---|
147 | * class is allowed. Thus, in the invocation handler, it is not always
|
---|
148 | * safe to assume that every class listed in the throws clause of the
|
---|
149 | * passed Method object can safely be thrown; fortunately, the Proxy
|
---|
150 | * instance is robust enough to wrap all illegal checked exceptions in
|
---|
151 | * {@link UndeclaredThrowableException}.
|
---|
152 | *
|
---|
153 | * @see InvocationHandler
|
---|
154 | * @see UndeclaredThrowableException
|
---|
155 | * @see Class
|
---|
156 | * @author Eric Blake <ebb9@email.byu.edu>
|
---|
157 | * @since 1.3
|
---|
158 | * @status updated to 1.4, except for the use of ProtectionDomain
|
---|
159 | */
|
---|
160 | public class Proxy implements Serializable
|
---|
161 | {
|
---|
162 | /**
|
---|
163 | * Compatible with JDK 1.3+.
|
---|
164 | */
|
---|
165 | private static final long serialVersionUID = -2222568056686623797L;
|
---|
166 |
|
---|
167 | /**
|
---|
168 | * Map of ProxyType to proxy class.
|
---|
169 | *
|
---|
170 | * @XXX This prevents proxy classes from being garbage collected.
|
---|
171 | * java.util.WeakHashSet is not appropriate, because that collects the
|
---|
172 | * keys, but we are interested in collecting the elements.
|
---|
173 | */
|
---|
174 | private static final Map proxyClasses = new HashMap();
|
---|
175 |
|
---|
176 | /**
|
---|
177 | * The invocation handler for this proxy instance. For Proxy, this
|
---|
178 | * field is unused, but it appears here in order to be serialized in all
|
---|
179 | * proxy classes.
|
---|
180 | *
|
---|
181 | * <em>NOTE</em>: This implementation is more secure for proxy classes
|
---|
182 | * than what Sun specifies. Sun does not require h to be immutable, but
|
---|
183 | * this means you could change h after the fact by reflection. However,
|
---|
184 | * by making h immutable, we may break non-proxy classes which extend
|
---|
185 | * Proxy.
|
---|
186 | * @serial invocation handler associated with this proxy instance
|
---|
187 | */
|
---|
188 | protected final InvocationHandler h;
|
---|
189 |
|
---|
190 | /**
|
---|
191 | * Constructs a new Proxy from a subclass (usually a proxy class),
|
---|
192 | * with the specified invocation handler.
|
---|
193 | *
|
---|
194 | * <em>NOTE</em>: This throws a NullPointerException if you attempt
|
---|
195 | * to create a proxy instance with a null handler using reflection.
|
---|
196 | * This behavior is not yet specified by Sun; see Sun Bug 4487672.
|
---|
197 | *
|
---|
198 | * @param handler the invocation handler, may be null if the subclass
|
---|
199 | * is not a proxy class
|
---|
200 | * @throws NullPointerException if handler is null and this is a proxy
|
---|
201 | * instance
|
---|
202 | */
|
---|
203 | protected Proxy(InvocationHandler handler)
|
---|
204 | {
|
---|
205 | if (handler == null && isProxyClass(getClass()))
|
---|
206 | throw new NullPointerException("invalid handler");
|
---|
207 | h = handler;
|
---|
208 | }
|
---|
209 |
|
---|
210 | /**
|
---|
211 | * Returns the proxy {@link Class} for the given ClassLoader and array
|
---|
212 | * of interfaces, dynamically generating it if necessary.
|
---|
213 | *
|
---|
214 | * There are several restrictions on this method, the violation of
|
---|
215 | * which will result in an IllegalArgumentException or
|
---|
216 | * NullPointerException:
|
---|
217 | * <ul>
|
---|
218 | * <li>All objects in `interfaces' must represent distinct interfaces.
|
---|
219 | * Classes, primitive types, null, and duplicates are forbidden.</li>
|
---|
220 | * <li>The interfaces must be visible in the specified ClassLoader.
|
---|
221 | * In other words, for each interface i:
|
---|
222 | * <code>Class.forName(i.getName(), false, loader) == i</code>
|
---|
223 | * must be true.</li>
|
---|
224 | * <li>All non-public interfaces (if any) must reside in the same
|
---|
225 | * package, or the proxy class would be non-instantiable. If
|
---|
226 | * there are no non-public interfaces, the package of the proxy
|
---|
227 | * class is unspecified.</li>
|
---|
228 | * <li>All interfaces must be compatible - if two declare a method
|
---|
229 | * with the same name and parameters, the return type must be
|
---|
230 | * the same and the throws clause of the proxy class will be
|
---|
231 | * the maximal subset of subclasses of the throws clauses for
|
---|
232 | * each method that is overridden.</li>
|
---|
233 | * <li>VM constraints limit the number of interfaces a proxy class
|
---|
234 | * may directly implement (however, the indirect inheritance
|
---|
235 | * of {@link Serializable} does not count against this limit).
|
---|
236 | * Even though most VMs can theoretically have 65535
|
---|
237 | * superinterfaces for a class, the actual limit is smaller
|
---|
238 | * because a class's constant pool is limited to 65535 entries,
|
---|
239 | * and not all entries can be interfaces.</li>
|
---|
240 | * </ul><p>
|
---|
241 | *
|
---|
242 | * Note that different orders of interfaces produce distinct classes.
|
---|
243 | *
|
---|
244 | * @param loader the class loader to define the proxy class in; null
|
---|
245 | * implies the bootstrap class loader
|
---|
246 | * @param interfaces the array of interfaces the proxy class implements,
|
---|
247 | * may be empty, but not null
|
---|
248 | * @return the Class object of the proxy class
|
---|
249 | * @throws IllegalArgumentException if the constraints above were
|
---|
250 | * violated, except for problems with null
|
---|
251 | * @throws NullPointerException if `interfaces' is null or contains
|
---|
252 | * a null entry
|
---|
253 | */
|
---|
254 | // synchronized so that we aren't trying to build the same class
|
---|
255 | // simultaneously in two threads
|
---|
256 | public static synchronized Class getProxyClass(ClassLoader loader,
|
---|
257 | Class[] interfaces)
|
---|
258 | {
|
---|
259 | interfaces = (Class[]) interfaces.clone();
|
---|
260 | ProxyType pt = new ProxyType(loader, interfaces);
|
---|
261 | Class clazz = (Class) proxyClasses.get(pt);
|
---|
262 | if (clazz == null)
|
---|
263 | {
|
---|
264 | if (Configuration.HAVE_NATIVE_GET_PROXY_CLASS)
|
---|
265 | clazz = getProxyClass0(loader, interfaces);
|
---|
266 | else
|
---|
267 | {
|
---|
268 | ProxyData data = (Configuration.HAVE_NATIVE_GET_PROXY_DATA
|
---|
269 | ? getProxyData0(loader, interfaces)
|
---|
270 | : ProxyData.getProxyData(pt));
|
---|
271 |
|
---|
272 | // FIXME workaround for bug in gcj 3.0.x
|
---|
273 | // Not needed with the latest gcj from cvs
|
---|
274 | //clazz = (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS
|
---|
275 | // ? generateProxyClass0(loader, data)
|
---|
276 | // : new ClassFactory(data).generate(loader));
|
---|
277 | if (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS)
|
---|
278 | clazz = generateProxyClass0(loader, data);
|
---|
279 | else
|
---|
280 | {
|
---|
281 | ClassFactory cf = new ClassFactory(data);
|
---|
282 | clazz = cf.generate(loader);
|
---|
283 | }
|
---|
284 | }
|
---|
285 |
|
---|
286 | Object check = proxyClasses.put(pt, clazz);
|
---|
287 | // assert check == null && clazz != null;
|
---|
288 | if (check != null || clazz == null)
|
---|
289 | throw new InternalError(/*"Fatal flaw in getProxyClass"*/);
|
---|
290 | }
|
---|
291 | return clazz;
|
---|
292 | }
|
---|
293 |
|
---|
294 | /**
|
---|
295 | * Combines several methods into one. This is equivalent to:
|
---|
296 | * <pre>
|
---|
297 | * Proxy.getProxyClass(loader, interfaces)
|
---|
298 | * .getConstructor(new Class[] {InvocationHandler.class})
|
---|
299 | * .newInstance(new Object[] {handler});
|
---|
300 | * </pre>
|
---|
301 | * except that it will not fail with the normal problems caused
|
---|
302 | * by reflection. It can still fail for the same reasons documented
|
---|
303 | * in getProxyClass, or if handler is null.
|
---|
304 | *
|
---|
305 | * @param loader the class loader to define the proxy class in; null
|
---|
306 | * implies the bootstrap class loader
|
---|
307 | * @param interfaces the array of interfaces the proxy class implements,
|
---|
308 | * may be empty, but not null
|
---|
309 | * @param handler the invocation handler, may not be null
|
---|
310 | * @return a proxy instance implementing the specified interfaces
|
---|
311 | * @throws IllegalArgumentException if the constraints for getProxyClass
|
---|
312 | * were violated, except for problems with null
|
---|
313 | * @throws NullPointerException if `interfaces' is null or contains
|
---|
314 | * a null entry, or if handler is null
|
---|
315 | * @see #getProxyClass(ClassLoader, Class[])
|
---|
316 | * @see Class#getConstructor(Class[])
|
---|
317 | * @see Constructor#newInstance(Object[])
|
---|
318 | */
|
---|
319 | public static Object newProxyInstance(ClassLoader loader,
|
---|
320 | Class[] interfaces,
|
---|
321 | InvocationHandler handler)
|
---|
322 | {
|
---|
323 | try
|
---|
324 | {
|
---|
325 | // getProxyClass() and Proxy() throw the necessary exceptions
|
---|
326 | return getProxyClass(loader, interfaces)
|
---|
327 | .getConstructor(new Class[] {InvocationHandler.class})
|
---|
328 | .newInstance(new Object[] {handler});
|
---|
329 | }
|
---|
330 | catch (RuntimeException e)
|
---|
331 | {
|
---|
332 | // Let IllegalArgumentException, NullPointerException escape.
|
---|
333 | // assert e instanceof IllegalArgumentException
|
---|
334 | // || e instanceof NullPointerException;
|
---|
335 | throw e;
|
---|
336 | }
|
---|
337 | catch (InvocationTargetException e)
|
---|
338 | {
|
---|
339 | // Let wrapped NullPointerException escape.
|
---|
340 | // assert e.getTargetException() instanceof NullPointerException
|
---|
341 | throw (NullPointerException) e.getCause();
|
---|
342 | }
|
---|
343 | catch (Exception e)
|
---|
344 | {
|
---|
345 | // Covers InstantiationException, IllegalAccessException,
|
---|
346 | // NoSuchMethodException, none of which should be generated
|
---|
347 | // if the proxy class was generated correctly.
|
---|
348 | // assert false;
|
---|
349 | throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
---|
350 | }
|
---|
351 | }
|
---|
352 |
|
---|
353 | /**
|
---|
354 | * Returns true if and only if the Class object is a dynamically created
|
---|
355 | * proxy class (created by <code>getProxyClass</code> or by the
|
---|
356 | * syntactic sugar of <code>newProxyInstance</code>).
|
---|
357 | *
|
---|
358 | * <p>This check is secure (in other words, it is not simply
|
---|
359 | * <code>clazz.getSuperclass() == Proxy.class</code>), it will not
|
---|
360 | * be spoofed by non-proxy classes that extend Proxy.
|
---|
361 | *
|
---|
362 | * @param clazz the class to check, must not be null
|
---|
363 | * @return true if the class represents a proxy class
|
---|
364 | * @throws NullPointerException if clazz is null
|
---|
365 | */
|
---|
366 | // This is synchronized on the off chance that another thread is
|
---|
367 | // trying to add a class to the map at the same time we read it.
|
---|
368 | public static synchronized boolean isProxyClass(Class clazz)
|
---|
369 | {
|
---|
370 | if (! Proxy.class.isAssignableFrom(clazz))
|
---|
371 | return false;
|
---|
372 | // This is a linear search, even though we could do an O(1) search
|
---|
373 | // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()).
|
---|
374 | return proxyClasses.containsValue(clazz);
|
---|
375 | }
|
---|
376 |
|
---|
377 | /**
|
---|
378 | * Returns the invocation handler for the given proxy instance.<p>
|
---|
379 | *
|
---|
380 | * <em>NOTE</em>: We guarantee a non-null result if successful,
|
---|
381 | * but Sun allows the creation of a proxy instance with a null
|
---|
382 | * handler. See the comments for {@link #Proxy(InvocationHandler)}.
|
---|
383 | *
|
---|
384 | * @param proxy the proxy instance, must not be null
|
---|
385 | * @return the invocation handler, guaranteed non-null.
|
---|
386 | * @throws IllegalArgumentException if
|
---|
387 | * <code>Proxy.isProxyClass(proxy.getClass())</code> returns false.
|
---|
388 | * @throws NullPointerException if proxy is null
|
---|
389 | */
|
---|
390 | public static InvocationHandler getInvocationHandler(Object proxy)
|
---|
391 | {
|
---|
392 | if (! isProxyClass(proxy.getClass()))
|
---|
393 | throw new IllegalArgumentException("not a proxy instance");
|
---|
394 | return ((Proxy) proxy).h;
|
---|
395 | }
|
---|
396 |
|
---|
397 | /**
|
---|
398 | * Optional native method to replace (and speed up) the pure Java
|
---|
399 | * implementation of getProxyClass. Only needed if
|
---|
400 | * Configuration.HAVE_NATIVE_GET_PROXY_CLASS is true, this does the
|
---|
401 | * work of both getProxyData0 and generateProxyClass0 with no
|
---|
402 | * intermediate form in Java. The native code may safely assume that
|
---|
403 | * this class must be created, and does not already exist.
|
---|
404 | *
|
---|
405 | * @param loader the class loader to define the proxy class in; null
|
---|
406 | * implies the bootstrap class loader
|
---|
407 | * @param interfaces the interfaces the class will extend
|
---|
408 | * @return the generated proxy class
|
---|
409 | * @throws IllegalArgumentException if the constraints for getProxyClass
|
---|
410 | * were violated, except for problems with null
|
---|
411 | * @throws NullPointerException if `interfaces' is null or contains
|
---|
412 | * a null entry, or if handler is null
|
---|
413 | * @see Configuration#HAVE_NATIVE_GET_PROXY_CLASS
|
---|
414 | * @see #getProxyClass(ClassLoader, Class[])
|
---|
415 | * @see #getProxyData0(ClassLoader, Class[])
|
---|
416 | * @see #generateProxyClass0(ProxyData)
|
---|
417 | */
|
---|
418 | private static native Class getProxyClass0(ClassLoader loader,
|
---|
419 | Class[] interfaces);
|
---|
420 |
|
---|
421 | /**
|
---|
422 | * Optional native method to replace (and speed up) the pure Java
|
---|
423 | * implementation of getProxyData. Only needed if
|
---|
424 | * Configuration.HAVE_NATIVE_GET_PROXY_DATA is true. The native code
|
---|
425 | * may safely assume that a new ProxyData object must be created which
|
---|
426 | * does not duplicate any existing ones.
|
---|
427 | *
|
---|
428 | * @param loader the class loader to define the proxy class in; null
|
---|
429 | * implies the bootstrap class loader
|
---|
430 | * @param interfaces the interfaces the class will extend
|
---|
431 | * @return all data that is required to make this proxy class
|
---|
432 | * @throws IllegalArgumentException if the constraints for getProxyClass
|
---|
433 | * were violated, except for problems with null
|
---|
434 | * @throws NullPointerException if `interfaces' is null or contains
|
---|
435 | * a null entry, or if handler is null
|
---|
436 | * @see Configuration.HAVE_NATIVE_GET_PROXY_DATA
|
---|
437 | * @see #getProxyClass(ClassLoader, Class[])
|
---|
438 | * @see #getProxyClass0(ClassLoader, Class[])
|
---|
439 | * @see ProxyType#getProxyData()
|
---|
440 | */
|
---|
441 | private static native ProxyData getProxyData0(ClassLoader loader,
|
---|
442 | Class[] interfaces);
|
---|
443 |
|
---|
444 | /**
|
---|
445 | * Optional native method to replace (and speed up) the pure Java
|
---|
446 | * implementation of generateProxyClass. Only needed if
|
---|
447 | * Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS is true. The native
|
---|
448 | * code may safely assume that a new Class must be created, and that
|
---|
449 | * the ProxyData object does not describe any existing class.
|
---|
450 | *
|
---|
451 | * @param loader the class loader to define the proxy class in; null
|
---|
452 | * implies the bootstrap class loader
|
---|
453 | * @param data the struct of information to convert to a Class. This
|
---|
454 | * has already been verified for all problems except exceeding
|
---|
455 | * VM limitations
|
---|
456 | * @return the newly generated class
|
---|
457 | * @throws IllegalArgumentException if VM limitations are exceeded
|
---|
458 | * @see #getProxyClass(ClassLoader, Class[])
|
---|
459 | * @see #getProxyClass0(ClassLoader, Class[])
|
---|
460 | * @see ProxyData#generateProxyClass(ClassLoader)
|
---|
461 | */
|
---|
462 | private static native Class generateProxyClass0(ClassLoader loader,
|
---|
463 | ProxyData data);
|
---|
464 |
|
---|
465 | /**
|
---|
466 | * Helper class for mapping unique ClassLoader and interface combinations
|
---|
467 | * to proxy classes.
|
---|
468 | *
|
---|
469 | * @author Eric Blake <ebb9@email.byu.edu>
|
---|
470 | */
|
---|
471 | private static final class ProxyType
|
---|
472 | {
|
---|
473 | /**
|
---|
474 | * Store the class loader (may be null)
|
---|
475 | */
|
---|
476 | final ClassLoader loader;
|
---|
477 |
|
---|
478 | /**
|
---|
479 | * Store the interfaces (never null, all elements are interfaces)
|
---|
480 | */
|
---|
481 | final Class[] interfaces;
|
---|
482 |
|
---|
483 | /**
|
---|
484 | * Construct the helper object.
|
---|
485 | *
|
---|
486 | * @param loader the class loader to define the proxy class in; null
|
---|
487 | * implies the bootstrap class loader
|
---|
488 | * @param interfaces an array of interfaces
|
---|
489 | */
|
---|
490 | ProxyType(ClassLoader loader, Class[] interfaces)
|
---|
491 | {
|
---|
492 | if (loader == null)
|
---|
493 | loader = ClassLoader.getSystemClassLoader();
|
---|
494 | this.loader = loader;
|
---|
495 | this.interfaces = interfaces;
|
---|
496 | }
|
---|
497 |
|
---|
498 | /**
|
---|
499 | * Calculates the hash code.
|
---|
500 | *
|
---|
501 | * @return a combination of the classloader and interfaces hashcodes.
|
---|
502 | */
|
---|
503 | public int hashCode()
|
---|
504 | {
|
---|
505 | //loader is always not null
|
---|
506 | int hash = loader.hashCode();
|
---|
507 | for (int i = 0; i < interfaces.length; i++)
|
---|
508 | hash = hash * 31 + interfaces[i].hashCode();
|
---|
509 | return hash;
|
---|
510 | }
|
---|
511 |
|
---|
512 | // A more comprehensive comparison of two arrays,
|
---|
513 | // ignore array element order, and
|
---|
514 | // ignore redundant elements
|
---|
515 | private static boolean sameTypes(Class arr1[], Class arr2[]) {
|
---|
516 | if (arr1.length == 1 && arr2.length == 1) {
|
---|
517 | return arr1[0] == arr2[0];
|
---|
518 | }
|
---|
519 |
|
---|
520 | // total occurrance of elements of arr1 in arr2
|
---|
521 | int total_occ_of_arr1_in_arr2 = 0;
|
---|
522 | each_type:
|
---|
523 | for (int i = arr1.length; --i >= 0; )
|
---|
524 | {
|
---|
525 | Class t = arr1[i];
|
---|
526 | for (int j = i; --j >= 0; )
|
---|
527 | {
|
---|
528 | if (t == arr1[j])
|
---|
529 | { //found duplicate type
|
---|
530 | continue each_type;
|
---|
531 | }
|
---|
532 | }
|
---|
533 |
|
---|
534 | // count c(a unique element of arr1)'s
|
---|
535 | // occurrences in arr2
|
---|
536 | int occ_in_arr2 = 0;
|
---|
537 | for (int j = arr2.length; --j >= 0; )
|
---|
538 | {
|
---|
539 | if (t == arr2[j])
|
---|
540 | {
|
---|
541 | ++occ_in_arr2;
|
---|
542 | }
|
---|
543 | }
|
---|
544 | if (occ_in_arr2 == 0)
|
---|
545 | { // t does not occur in arr2
|
---|
546 | return false;
|
---|
547 | }
|
---|
548 |
|
---|
549 | total_occ_of_arr1_in_arr2 += occ_in_arr2;
|
---|
550 | }
|
---|
551 | // now, each element of arr2 must have been visited
|
---|
552 | return total_occ_of_arr1_in_arr2 == arr2.length;
|
---|
553 | }
|
---|
554 |
|
---|
555 | /**
|
---|
556 | * Calculates equality.
|
---|
557 | *
|
---|
558 | * @param the object to compare to
|
---|
559 | * @return true if it is a ProxyType with same data
|
---|
560 | */
|
---|
561 | public boolean equals(Object other)
|
---|
562 | {
|
---|
563 | ProxyType pt = (ProxyType) other;
|
---|
564 | if (loader != pt.loader || interfaces.length != pt.interfaces.length)
|
---|
565 | return false;
|
---|
566 | return sameTypes(interfaces, pt.interfaces);
|
---|
567 | }
|
---|
568 | } // class ProxyType
|
---|
569 |
|
---|
570 | /**
|
---|
571 | * Helper class which allows hashing of a method name and signature
|
---|
572 | * without worrying about return type, declaring class, or throws clause,
|
---|
573 | * and which reduces the maximally common throws clause between two methods
|
---|
574 | *
|
---|
575 | * @author Eric Blake <ebb9@email.byu.edu>
|
---|
576 | */
|
---|
577 | private static final class ProxySignature
|
---|
578 | {
|
---|
579 | /**
|
---|
580 | * The core signatures which all Proxy instances handle.
|
---|
581 | */
|
---|
582 | static final HashMap coreMethods = new HashMap();
|
---|
583 | static
|
---|
584 | {
|
---|
585 | try
|
---|
586 | {
|
---|
587 | ProxySignature sig
|
---|
588 | = new ProxySignature(Object.class
|
---|
589 | .getMethod("equals",
|
---|
590 | new Class[] {Object.class}));
|
---|
591 | coreMethods.put(sig, sig);
|
---|
592 | sig = new ProxySignature(Object.class.getMethod("hashCode", null));
|
---|
593 | coreMethods.put(sig, sig);
|
---|
594 | sig = new ProxySignature(Object.class.getMethod("toString", null));
|
---|
595 | coreMethods.put(sig, sig);
|
---|
596 | }
|
---|
597 | catch (Exception e)
|
---|
598 | {
|
---|
599 | // assert false;
|
---|
600 | throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
---|
601 | }
|
---|
602 | }
|
---|
603 |
|
---|
604 | /**
|
---|
605 | * The underlying Method object, never null
|
---|
606 | */
|
---|
607 | final Method method;
|
---|
608 |
|
---|
609 | /**
|
---|
610 | * The set of compatible thrown exceptions, may be empty
|
---|
611 | */
|
---|
612 | final Set exceptions = new HashSet();
|
---|
613 |
|
---|
614 | /**
|
---|
615 | * Construct a signature
|
---|
616 | *
|
---|
617 | * @param method the Method this signature is based on, never null
|
---|
618 | */
|
---|
619 | ProxySignature(Method method)
|
---|
620 | {
|
---|
621 | this.method = method;
|
---|
622 | Class[] exc = method.getExceptionTypes();
|
---|
623 | int i = exc.length;
|
---|
624 | while (--i >= 0)
|
---|
625 | {
|
---|
626 | // discard unchecked exceptions
|
---|
627 | if (Error.class.isAssignableFrom(exc[i])
|
---|
628 | || RuntimeException.class.isAssignableFrom(exc[i]))
|
---|
629 | continue;
|
---|
630 | exceptions.add(exc[i]);
|
---|
631 | }
|
---|
632 | }
|
---|
633 |
|
---|
634 | /**
|
---|
635 | * Given a method, make sure it's return type is identical
|
---|
636 | * to this, and adjust this signature's throws clause appropriately
|
---|
637 | *
|
---|
638 | * @param other the signature to merge in
|
---|
639 | * @throws IllegalArgumentException if the return types conflict
|
---|
640 | */
|
---|
641 | void checkCompatibility(ProxySignature other)
|
---|
642 | {
|
---|
643 | if (method.getReturnType() != other.method.getReturnType())
|
---|
644 | throw new IllegalArgumentException("incompatible return types: "
|
---|
645 | + method + ", " + other.method);
|
---|
646 |
|
---|
647 | // if you can think of a more efficient way than this O(n^2) search,
|
---|
648 | // implement it!
|
---|
649 | int size1 = exceptions.size();
|
---|
650 | int size2 = other.exceptions.size();
|
---|
651 | boolean[] valid1 = new boolean[size1];
|
---|
652 | boolean[] valid2 = new boolean[size2];
|
---|
653 | Iterator itr = exceptions.iterator();
|
---|
654 | int pos = size1;
|
---|
655 | while (--pos >= 0)
|
---|
656 | {
|
---|
657 | Class c1 = (Class) itr.next();
|
---|
658 | Iterator itr2 = other.exceptions.iterator();
|
---|
659 | int pos2 = size2;
|
---|
660 | while (--pos2 >= 0)
|
---|
661 | {
|
---|
662 | Class c2 = (Class) itr2.next();
|
---|
663 | if (c2.isAssignableFrom(c1))
|
---|
664 | valid1[pos] = true;
|
---|
665 | if (c1.isAssignableFrom(c2))
|
---|
666 | valid2[pos2] = true;
|
---|
667 | }
|
---|
668 | }
|
---|
669 | pos = size1;
|
---|
670 | itr = exceptions.iterator();
|
---|
671 | while (--pos >= 0)
|
---|
672 | {
|
---|
673 | itr.next();
|
---|
674 | if (! valid1[pos])
|
---|
675 | itr.remove();
|
---|
676 | }
|
---|
677 | pos = size2;
|
---|
678 | itr = other.exceptions.iterator();
|
---|
679 | while (--pos >= 0)
|
---|
680 | {
|
---|
681 | itr.next();
|
---|
682 | if (! valid2[pos])
|
---|
683 | itr.remove();
|
---|
684 | }
|
---|
685 | exceptions.addAll(other.exceptions);
|
---|
686 | }
|
---|
687 |
|
---|
688 | /**
|
---|
689 | * Calculates the hash code.
|
---|
690 | *
|
---|
691 | * @return a combination of name and parameter types
|
---|
692 | */
|
---|
693 | public int hashCode()
|
---|
694 | {
|
---|
695 | int hash = method.getName().hashCode();
|
---|
696 | Class[] types = method.getParameterTypes();
|
---|
697 | for (int i = 0; i < types.length; i++)
|
---|
698 | hash = hash * 31 + types[i].hashCode();
|
---|
699 | return hash;
|
---|
700 | }
|
---|
701 |
|
---|
702 | /**
|
---|
703 | * Calculates equality.
|
---|
704 | *
|
---|
705 | * @param the object to compare to
|
---|
706 | * @return true if it is a ProxySignature with same data
|
---|
707 | */
|
---|
708 | public boolean equals(Object other)
|
---|
709 | {
|
---|
710 | ProxySignature ps = (ProxySignature) other;
|
---|
711 | Class[] types1 = method.getParameterTypes();
|
---|
712 | Class[] types2 = ps.method.getParameterTypes();
|
---|
713 | if (! method.getName().equals(ps.method.getName())
|
---|
714 | || types1.length != types2.length)
|
---|
715 | return false;
|
---|
716 | int i = types1.length;
|
---|
717 | while (--i >= 0)
|
---|
718 | if (types1[i] != types2[i])
|
---|
719 | return false;
|
---|
720 | return true;
|
---|
721 | }
|
---|
722 | } // class ProxySignature
|
---|
723 |
|
---|
724 | /**
|
---|
725 | * A flat representation of all data needed to generate bytecode/instantiate
|
---|
726 | * a proxy class. This is basically a struct.
|
---|
727 | *
|
---|
728 | * @author Eric Blake <ebb9@email.byu.edu>
|
---|
729 | */
|
---|
730 | private static final class ProxyData
|
---|
731 | {
|
---|
732 | /**
|
---|
733 | * The package this class is in. Possibly null, meaning the unnamed
|
---|
734 | * package.
|
---|
735 | */
|
---|
736 | Package pack;
|
---|
737 |
|
---|
738 | /**
|
---|
739 | * The interfaces this class implements. Non-null, but possibly empty.
|
---|
740 | */
|
---|
741 | Class[] interfaces;
|
---|
742 |
|
---|
743 | /**
|
---|
744 | * The Method objects this class must pass as the second argument to
|
---|
745 | * invoke (also useful for determining what methods this class has).
|
---|
746 | * Non-null, non-empty (includes at least Object.hashCode, Object.equals,
|
---|
747 | * and Object.toString).
|
---|
748 | */
|
---|
749 | Method[] methods;
|
---|
750 |
|
---|
751 | /**
|
---|
752 | * The exceptions that do not need to be wrapped in
|
---|
753 | * UndeclaredThrowableException. exceptions[i] is the same as, or a
|
---|
754 | * subset of subclasses, of methods[i].getExceptionTypes(), depending on
|
---|
755 | * compatible throws clauses with multiple inheritance. It is unspecified
|
---|
756 | * if these lists include or exclude subclasses of Error and
|
---|
757 | * RuntimeException, but excluding them is harmless and generates a
|
---|
758 | * smaller class.
|
---|
759 | */
|
---|
760 | Class[][] exceptions;
|
---|
761 |
|
---|
762 | /**
|
---|
763 | * For unique id's
|
---|
764 | */
|
---|
765 | private static int count = 0;
|
---|
766 |
|
---|
767 | /**
|
---|
768 | * The id of this proxy class
|
---|
769 | */
|
---|
770 | final int id = count++;
|
---|
771 |
|
---|
772 | /**
|
---|
773 | * Construct a ProxyData with uninitialized data members.
|
---|
774 | */
|
---|
775 | ProxyData()
|
---|
776 | {
|
---|
777 | }
|
---|
778 |
|
---|
779 | /**
|
---|
780 | * Verifies that the arguments are legal, and sets up remaining data
|
---|
781 | * This should only be called when a class must be generated, as
|
---|
782 | * it is expensive.
|
---|
783 | *
|
---|
784 | * @param pt the ProxyType to convert to ProxyData
|
---|
785 | * @return the flattened, verified ProxyData structure for use in
|
---|
786 | * class generation
|
---|
787 | * @throws IllegalArgumentException if `interfaces' contains
|
---|
788 | * non-interfaces or incompatible combinations, and verify is true
|
---|
789 | * @throws NullPointerException if interfaces is null or contains null
|
---|
790 | */
|
---|
791 | static ProxyData getProxyData(ProxyType pt)
|
---|
792 | {
|
---|
793 | Map method_set = (Map) ProxySignature.coreMethods.clone();
|
---|
794 | boolean in_package = false; // true if we encounter non-public interface
|
---|
795 |
|
---|
796 | ProxyData data = new ProxyData();
|
---|
797 | data.interfaces = pt.interfaces;
|
---|
798 |
|
---|
799 | // if interfaces is too large, we croak later on when the constant
|
---|
800 | // pool overflows
|
---|
801 | int i = data.interfaces.length;
|
---|
802 | while (--i >= 0)
|
---|
803 | {
|
---|
804 | Class inter = data.interfaces[i];
|
---|
805 | if (! inter.isInterface())
|
---|
806 | throw new IllegalArgumentException("not an interface: " + inter);
|
---|
807 | try
|
---|
808 | {
|
---|
809 | if (Class.forName(inter.getName(), false, pt.loader) != inter)
|
---|
810 | throw new IllegalArgumentException("not accessible in "
|
---|
811 | + "classloader: " + inter);
|
---|
812 | }
|
---|
813 | catch (ClassNotFoundException e)
|
---|
814 | {
|
---|
815 | throw new IllegalArgumentException("not accessible in "
|
---|
816 | + "classloader: " + inter);
|
---|
817 | }
|
---|
818 | if (! Modifier.isPublic(inter.getModifiers()))
|
---|
819 | if (in_package)
|
---|
820 | {
|
---|
821 | Package p = inter.getPackage();
|
---|
822 | if (data.pack != inter.getPackage())
|
---|
823 | throw new IllegalArgumentException("non-public interfaces "
|
---|
824 | + "from different "
|
---|
825 | + "packages");
|
---|
826 | }
|
---|
827 | else
|
---|
828 | {
|
---|
829 | in_package = true;
|
---|
830 | data.pack = inter.getPackage();
|
---|
831 | }
|
---|
832 | for (int j = i-1; j >= 0; j--)
|
---|
833 | if (data.interfaces[j] == inter)
|
---|
834 | throw new IllegalArgumentException("duplicate interface: "
|
---|
835 | + inter);
|
---|
836 | Method[] methods = inter.getMethods();
|
---|
837 | int j = methods.length;
|
---|
838 | while (--j >= 0)
|
---|
839 | {
|
---|
840 | ProxySignature sig = new ProxySignature(methods[j]);
|
---|
841 | ProxySignature old = (ProxySignature) method_set.put(sig, sig);
|
---|
842 | if (old != null)
|
---|
843 | sig.checkCompatibility(old);
|
---|
844 | }
|
---|
845 | }
|
---|
846 |
|
---|
847 | i = method_set.size();
|
---|
848 | data.methods = new Method[i];
|
---|
849 | data.exceptions = new Class[i][];
|
---|
850 | Iterator itr = method_set.values().iterator();
|
---|
851 | while (--i >= 0)
|
---|
852 | {
|
---|
853 | ProxySignature sig = (ProxySignature) itr.next();
|
---|
854 | data.methods[i] = sig.method;
|
---|
855 | data.exceptions[i] = (Class[]) sig.exceptions
|
---|
856 | .toArray(new Class[sig.exceptions.size()]);
|
---|
857 | }
|
---|
858 | return data;
|
---|
859 | }
|
---|
860 | } // class ProxyData
|
---|
861 |
|
---|
862 | /**
|
---|
863 | * Does all the work of building a class. By making this a nested class,
|
---|
864 | * this code is not loaded in memory if the VM has a native
|
---|
865 | * implementation instead.
|
---|
866 | *
|
---|
867 | * @author Eric Blake <ebb9@email.byu.edu>
|
---|
868 | */
|
---|
869 | private static final class ClassFactory
|
---|
870 | {
|
---|
871 | /** Constants for assisting the compilation */
|
---|
872 | private static final byte POOL = 0;
|
---|
873 | private static final byte FIELD = 1;
|
---|
874 | private static final byte METHOD = 2;
|
---|
875 | private static final byte INTERFACE = 3;
|
---|
876 | private static final String CTOR_SIG
|
---|
877 | = "(Ljava/lang/reflect/InvocationHandler;)V";
|
---|
878 | private static final String INVOKE_SIG = "(Ljava/lang/Object;"
|
---|
879 | + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
|
---|
880 |
|
---|
881 | /** Bytecodes for insertion in the class definition byte[] */
|
---|
882 | private static final char ACONST_NULL = 1;
|
---|
883 | private static final char ICONST_0 = 3;
|
---|
884 | private static final char BIPUSH = 16;
|
---|
885 | private static final char SIPUSH = 17;
|
---|
886 | private static final char ILOAD = 21;
|
---|
887 | private static final char ILOAD_0 = 26;
|
---|
888 | private static final char ALOAD_0 = 42;
|
---|
889 | private static final char ALOAD_1 = 43;
|
---|
890 | private static final char AALOAD = 50;
|
---|
891 | private static final char AASTORE = 83;
|
---|
892 | private static final char DUP = 89;
|
---|
893 | private static final char DUP_X1 = 90;
|
---|
894 | private static final char SWAP = 95;
|
---|
895 | private static final char IRETURN = 172;
|
---|
896 | private static final char LRETURN = 173;
|
---|
897 | private static final char FRETURN = 174;
|
---|
898 | private static final char DRETURN = 175;
|
---|
899 | private static final char ARETURN = 176;
|
---|
900 | private static final char RETURN = 177;
|
---|
901 | private static final char GETSTATIC = 178;
|
---|
902 | private static final char GETFIELD = 180;
|
---|
903 | private static final char INVOKEVIRTUAL = 182;
|
---|
904 | private static final char INVOKESPECIAL = 183;
|
---|
905 | private static final char INVOKESTATIC = 184;
|
---|
906 | private static final char INVOKEINTERFACE = 185;
|
---|
907 | private static final char NEW = 187;
|
---|
908 | private static final char ANEWARRAY = 189;
|
---|
909 | private static final char ATHROW = 191;
|
---|
910 | private static final char CHECKCAST = 192;
|
---|
911 |
|
---|
912 | // Implementation note: we use StringBuffers to hold the byte data, since
|
---|
913 | // they automatically grow. However, we only use the low 8 bits of
|
---|
914 | // every char in the array, so we are using twice the necessary memory
|
---|
915 | // for the ease StringBuffer provides.
|
---|
916 |
|
---|
917 | /** The constant pool. */
|
---|
918 | private final StringBuffer pool = new StringBuffer();
|
---|
919 | /** The rest of the class data. */
|
---|
920 | private final StringBuffer stream = new StringBuffer();
|
---|
921 |
|
---|
922 | /** Map of strings to byte sequences, to minimize size of pool. */
|
---|
923 | private final Map poolEntries = new HashMap();
|
---|
924 |
|
---|
925 | /** The VM name of this proxy class. */
|
---|
926 | private final String qualName;
|
---|
927 |
|
---|
928 | /**
|
---|
929 | * The Method objects the proxy class refers to when calling the
|
---|
930 | * invocation handler.
|
---|
931 | */
|
---|
932 | private final Method[] methods;
|
---|
933 |
|
---|
934 | /**
|
---|
935 | * Initializes the buffers with the bytecode contents for a proxy class.
|
---|
936 | *
|
---|
937 | * @param data the remainder of the class data
|
---|
938 | * @throws IllegalArgumentException if anything else goes wrong this
|
---|
939 | * late in the game; as far as I can tell, this will only happen
|
---|
940 | * if the constant pool overflows, which is possible even when
|
---|
941 | * the user doesn't exceed the 65535 interface limit
|
---|
942 | */
|
---|
943 | ClassFactory(ProxyData data)
|
---|
944 | {
|
---|
945 | methods = data.methods;
|
---|
946 |
|
---|
947 | // magic = 0xcafebabe
|
---|
948 | // minor_version = 0
|
---|
949 | // major_version = 46
|
---|
950 | // constant_pool_count: place-holder for now
|
---|
951 | pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0");
|
---|
952 | // constant_pool[], filled in as we go
|
---|
953 |
|
---|
954 | // access_flags
|
---|
955 | putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC);
|
---|
956 | // this_class
|
---|
957 | qualName = ((data.pack == null ? "" : data.pack.getName() + '.')
|
---|
958 | + "$Proxy" + data.id);
|
---|
959 | putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false)));
|
---|
960 | // super_class
|
---|
961 | putU2(classInfo("java/lang/reflect/Proxy"));
|
---|
962 |
|
---|
963 | // interfaces_count
|
---|
964 | putU2(data.interfaces.length);
|
---|
965 | // interfaces[]
|
---|
966 | for (int i = 0; i < data.interfaces.length; i++)
|
---|
967 | putU2(classInfo(data.interfaces[i]));
|
---|
968 |
|
---|
969 | // Recall that Proxy classes serialize specially, so we do not need
|
---|
970 | // to worry about a <clinit> method for this field. Instead, we
|
---|
971 | // just assign it by reflection after the class is successfully loaded.
|
---|
972 | // fields_count - private static Method[] m;
|
---|
973 | putU2(1);
|
---|
974 | // fields[]
|
---|
975 | // m.access_flags
|
---|
976 | putU2(Modifier.PRIVATE | Modifier.STATIC);
|
---|
977 | // m.name_index
|
---|
978 | putU2(utf8Info("m"));
|
---|
979 | // m.descriptor_index
|
---|
980 | putU2(utf8Info("[Ljava/lang/reflect/Method;"));
|
---|
981 | // m.attributes_count
|
---|
982 | putU2(0);
|
---|
983 | // m.attributes[]
|
---|
984 |
|
---|
985 | // methods_count - # handler methods, plus <init>
|
---|
986 | putU2(methods.length + 1);
|
---|
987 | // methods[]
|
---|
988 | // <init>.access_flags
|
---|
989 | putU2(Modifier.PUBLIC);
|
---|
990 | // <init>.name_index
|
---|
991 | putU2(utf8Info("<init>"));
|
---|
992 | // <init>.descriptor_index
|
---|
993 | putU2(utf8Info(CTOR_SIG));
|
---|
994 | // <init>.attributes_count - only Code is needed
|
---|
995 | putU2(1);
|
---|
996 | // <init>.Code.attribute_name_index
|
---|
997 | putU2(utf8Info("Code"));
|
---|
998 | // <init>.Code.attribute_length = 18
|
---|
999 | // <init>.Code.info:
|
---|
1000 | // $Proxynn(InvocationHandler h) { super(h); }
|
---|
1001 | // <init>.Code.max_stack = 2
|
---|
1002 | // <init>.Code.max_locals = 2
|
---|
1003 | // <init>.Code.code_length = 6
|
---|
1004 | // <init>.Code.code[]
|
---|
1005 | stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1
|
---|
1006 | + INVOKESPECIAL);
|
---|
1007 | putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "<init>", CTOR_SIG));
|
---|
1008 | // <init>.Code.exception_table_length = 0
|
---|
1009 | // <init>.Code.exception_table[]
|
---|
1010 | // <init>.Code.attributes_count = 0
|
---|
1011 | // <init>.Code.attributes[]
|
---|
1012 | stream.append(RETURN + "\0\0\0\0");
|
---|
1013 |
|
---|
1014 | for (int i = methods.length - 1; i >= 0; i--)
|
---|
1015 | emitMethod(i, data.exceptions[i]);
|
---|
1016 |
|
---|
1017 | // attributes_count
|
---|
1018 | putU2(0);
|
---|
1019 | // attributes[] - empty; omit SourceFile attribute
|
---|
1020 | // XXX should we mark this with a Synthetic attribute?
|
---|
1021 | }
|
---|
1022 |
|
---|
1023 | /**
|
---|
1024 | * Produce the bytecode for a single method.
|
---|
1025 | *
|
---|
1026 | * @param i the index of the method we are building
|
---|
1027 | * @param e the exceptions possible for the method
|
---|
1028 | */
|
---|
1029 | private void emitMethod(int i, Class[] e)
|
---|
1030 | {
|
---|
1031 | // First, we precalculate the method length and other information.
|
---|
1032 |
|
---|
1033 | Method m = methods[i];
|
---|
1034 | Class[] paramtypes = m.getParameterTypes();
|
---|
1035 | int wrap_overhead = 0; // max words taken by wrapped primitive
|
---|
1036 | int param_count = 1; // 1 for this
|
---|
1037 | int code_length = 16; // aload_0, getfield, aload_0, getstatic, const,
|
---|
1038 | // aaload, const/aconst_null, invokeinterface
|
---|
1039 | if (i > 5)
|
---|
1040 | {
|
---|
1041 | if (i > Byte.MAX_VALUE)
|
---|
1042 | code_length += 2; // sipush
|
---|
1043 | else
|
---|
1044 | code_length++; // bipush
|
---|
1045 | }
|
---|
1046 | if (paramtypes.length > 0)
|
---|
1047 | {
|
---|
1048 | code_length += 3; // anewarray
|
---|
1049 | if (paramtypes.length > Byte.MAX_VALUE)
|
---|
1050 | code_length += 2; // sipush
|
---|
1051 | else if (paramtypes.length > 5)
|
---|
1052 | code_length++; // bipush
|
---|
1053 | for (int j = 0; j < paramtypes.length; j++)
|
---|
1054 | {
|
---|
1055 | code_length += 4; // dup, const, load, store
|
---|
1056 | Class type = paramtypes[j];
|
---|
1057 | if (j > 5)
|
---|
1058 | {
|
---|
1059 | if (j > Byte.MAX_VALUE)
|
---|
1060 | code_length += 2; // sipush
|
---|
1061 | else
|
---|
1062 | code_length++; // bipush
|
---|
1063 | }
|
---|
1064 | if (param_count >= 4)
|
---|
1065 | code_length++; // 2-byte load
|
---|
1066 | param_count++;
|
---|
1067 | if (type.isPrimitive())
|
---|
1068 | {
|
---|
1069 | code_length += 7; // new, dup, invokespecial
|
---|
1070 | if (type == long.class || type == double.class)
|
---|
1071 | {
|
---|
1072 | wrap_overhead = 3;
|
---|
1073 | param_count++;
|
---|
1074 | }
|
---|
1075 | else if (wrap_overhead < 2)
|
---|
1076 | wrap_overhead = 2;
|
---|
1077 | }
|
---|
1078 | }
|
---|
1079 | }
|
---|
1080 | int end_pc = code_length;
|
---|
1081 | Class ret_type = m.getReturnType();
|
---|
1082 | if (ret_type == void.class)
|
---|
1083 | code_length++; // return
|
---|
1084 | else if (ret_type.isPrimitive())
|
---|
1085 | code_length += 7; // cast, invokevirtual, return
|
---|
1086 | else
|
---|
1087 | code_length += 4; // cast, return
|
---|
1088 | int exception_count = 0;
|
---|
1089 | boolean throws_throwable = false;
|
---|
1090 | for (int j = 0; j < e.length; j++)
|
---|
1091 | if (e[j] == Throwable.class)
|
---|
1092 | {
|
---|
1093 | throws_throwable = true;
|
---|
1094 | break;
|
---|
1095 | }
|
---|
1096 | if (! throws_throwable)
|
---|
1097 | {
|
---|
1098 | exception_count = e.length + 3; // Throwable, Error, RuntimeException
|
---|
1099 | code_length += 9; // new, dup_x1, swap, invokespecial, athrow
|
---|
1100 | }
|
---|
1101 | int handler_pc = code_length - 1;
|
---|
1102 | StringBuffer signature = new StringBuffer("(");
|
---|
1103 | for (int j = 0; j < paramtypes.length; j++)
|
---|
1104 | signature.append(TypeSignature.getEncodingOfClass(paramtypes[j]));
|
---|
1105 | signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type));
|
---|
1106 |
|
---|
1107 | // Now we have enough information to emit the method.
|
---|
1108 |
|
---|
1109 | // handler.access_flags
|
---|
1110 | putU2(Modifier.PUBLIC | Modifier.FINAL);
|
---|
1111 | // handler.name_index
|
---|
1112 | putU2(utf8Info(m.getName()));
|
---|
1113 | // handler.descriptor_index
|
---|
1114 | putU2(utf8Info(signature.toString()));
|
---|
1115 | // handler.attributes_count - Code is necessary, Exceptions possible
|
---|
1116 | putU2(e.length > 0 ? 2 : 1);
|
---|
1117 |
|
---|
1118 | // handler.Code.info:
|
---|
1119 | // type name(args) {
|
---|
1120 | // try {
|
---|
1121 | // return (type) h.invoke(this, methods[i], new Object[] {args});
|
---|
1122 | // } catch (<declared Exceptions> e) {
|
---|
1123 | // throw e;
|
---|
1124 | // } catch (Throwable t) {
|
---|
1125 | // throw new UndeclaredThrowableException(t);
|
---|
1126 | // }
|
---|
1127 | // }
|
---|
1128 | // Special cases:
|
---|
1129 | // if arg_n is primitive, wrap it
|
---|
1130 | // if method throws Throwable, try-catch is not needed
|
---|
1131 | // if method returns void, return statement not needed
|
---|
1132 | // if method returns primitive, unwrap it
|
---|
1133 | // save space by sharing code for all the declared handlers
|
---|
1134 |
|
---|
1135 | // handler.Code.attribute_name_index
|
---|
1136 | putU2(utf8Info("Code"));
|
---|
1137 | // handler.Code.attribute_length
|
---|
1138 | putU4(12 + code_length + 8 * exception_count);
|
---|
1139 | // handler.Code.max_stack
|
---|
1140 | putU2(param_count == 1 ? 4 : 7 + wrap_overhead);
|
---|
1141 | // handler.Code.max_locals
|
---|
1142 | putU2(param_count);
|
---|
1143 | // handler.Code.code_length
|
---|
1144 | putU4(code_length);
|
---|
1145 | // handler.Code.code[]
|
---|
1146 | putU1(ALOAD_0);
|
---|
1147 | putU1(GETFIELD);
|
---|
1148 | putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h",
|
---|
1149 | "Ljava/lang/reflect/InvocationHandler;"));
|
---|
1150 | putU1(ALOAD_0);
|
---|
1151 | putU1(GETSTATIC);
|
---|
1152 | putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false),
|
---|
1153 | "m", "[Ljava/lang/reflect/Method;"));
|
---|
1154 | putConst(i);
|
---|
1155 | putU1(AALOAD);
|
---|
1156 | if (paramtypes.length > 0)
|
---|
1157 | {
|
---|
1158 | putConst(paramtypes.length);
|
---|
1159 | putU1(ANEWARRAY);
|
---|
1160 | putU2(classInfo("java/lang/Object"));
|
---|
1161 | param_count = 1;
|
---|
1162 | for (int j = 0; j < paramtypes.length; j++, param_count++)
|
---|
1163 | {
|
---|
1164 | putU1(DUP);
|
---|
1165 | putConst(j);
|
---|
1166 | if (paramtypes[j].isPrimitive())
|
---|
1167 | {
|
---|
1168 | putU1(NEW);
|
---|
1169 | putU2(classInfo(wrapper(paramtypes[j])));
|
---|
1170 | putU1(DUP);
|
---|
1171 | }
|
---|
1172 | putLoad(param_count, paramtypes[j]);
|
---|
1173 | if (paramtypes[j].isPrimitive())
|
---|
1174 | {
|
---|
1175 | putU1(INVOKESPECIAL);
|
---|
1176 | putU2(refInfo(METHOD, wrapper(paramtypes[j]), "<init>",
|
---|
1177 | '(' + (TypeSignature
|
---|
1178 | .getEncodingOfClass(paramtypes[j])
|
---|
1179 | + ")V")));
|
---|
1180 | if (paramtypes[j] == long.class
|
---|
1181 | || paramtypes[j] == double.class)
|
---|
1182 | param_count++;
|
---|
1183 | }
|
---|
1184 | putU1(AASTORE);
|
---|
1185 | }
|
---|
1186 | }
|
---|
1187 | else
|
---|
1188 | putU1(ACONST_NULL);
|
---|
1189 | putU1(INVOKEINTERFACE);
|
---|
1190 | putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler",
|
---|
1191 | "invoke", INVOKE_SIG));
|
---|
1192 | putU1(4); // InvocationHandler, this, Method, Object[]
|
---|
1193 | putU1(0);
|
---|
1194 | if (ret_type == void.class)
|
---|
1195 | putU1(RETURN);
|
---|
1196 | else if (ret_type.isPrimitive())
|
---|
1197 | {
|
---|
1198 | putU1(CHECKCAST);
|
---|
1199 | putU2(classInfo(wrapper(ret_type)));
|
---|
1200 | putU1(INVOKEVIRTUAL);
|
---|
1201 | putU2(refInfo(METHOD, wrapper(ret_type),
|
---|
1202 | ret_type.getName() + "Value",
|
---|
1203 | "()" + TypeSignature.getEncodingOfClass(ret_type)));
|
---|
1204 | if (ret_type == long.class)
|
---|
1205 | putU1(LRETURN);
|
---|
1206 | else if (ret_type == float.class)
|
---|
1207 | putU1(FRETURN);
|
---|
1208 | else if (ret_type == double.class)
|
---|
1209 | putU1(DRETURN);
|
---|
1210 | else
|
---|
1211 | putU1(IRETURN);
|
---|
1212 | }
|
---|
1213 | else
|
---|
1214 | {
|
---|
1215 | putU1(CHECKCAST);
|
---|
1216 | putU2(classInfo(ret_type));
|
---|
1217 | putU1(ARETURN);
|
---|
1218 | }
|
---|
1219 | if (! throws_throwable)
|
---|
1220 | {
|
---|
1221 | putU1(NEW);
|
---|
1222 | putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
|
---|
1223 | putU1(DUP_X1);
|
---|
1224 | putU1(SWAP);
|
---|
1225 | putU1(INVOKESPECIAL);
|
---|
1226 | putU2(refInfo(METHOD,
|
---|
1227 | "java/lang/reflect/UndeclaredThrowableException",
|
---|
1228 | "<init>", "(Ljava/lang/Throwable;)V"));
|
---|
1229 | putU1(ATHROW);
|
---|
1230 | }
|
---|
1231 |
|
---|
1232 | // handler.Code.exception_table_length
|
---|
1233 | putU2(exception_count);
|
---|
1234 | // handler.Code.exception_table[]
|
---|
1235 | if (! throws_throwable)
|
---|
1236 | {
|
---|
1237 | // handler.Code.exception_table.start_pc
|
---|
1238 | putU2(0);
|
---|
1239 | // handler.Code.exception_table.end_pc
|
---|
1240 | putU2(end_pc);
|
---|
1241 | // handler.Code.exception_table.handler_pc
|
---|
1242 | putU2(handler_pc);
|
---|
1243 | // handler.Code.exception_table.catch_type
|
---|
1244 | putU2(classInfo("java/lang/Error"));
|
---|
1245 | // handler.Code.exception_table.start_pc
|
---|
1246 | putU2(0);
|
---|
1247 | // handler.Code.exception_table.end_pc
|
---|
1248 | putU2(end_pc);
|
---|
1249 | // handler.Code.exception_table.handler_pc
|
---|
1250 | putU2(handler_pc);
|
---|
1251 | // handler.Code.exception_table.catch_type
|
---|
1252 | putU2(classInfo("java/lang/RuntimeException"));
|
---|
1253 | for (int j = 0; j < e.length; j++)
|
---|
1254 | {
|
---|
1255 | // handler.Code.exception_table.start_pc
|
---|
1256 | putU2(0);
|
---|
1257 | // handler.Code.exception_table.end_pc
|
---|
1258 | putU2(end_pc);
|
---|
1259 | // handler.Code.exception_table.handler_pc
|
---|
1260 | putU2(handler_pc);
|
---|
1261 | // handler.Code.exception_table.catch_type
|
---|
1262 | putU2(classInfo(e[j]));
|
---|
1263 | }
|
---|
1264 | // handler.Code.exception_table.start_pc
|
---|
1265 | putU2(0);
|
---|
1266 | // handler.Code.exception_table.end_pc
|
---|
1267 | putU2(end_pc);
|
---|
1268 | // handler.Code.exception_table.handler_pc -
|
---|
1269 | // -8 for undeclared handler, which falls thru to normal one
|
---|
1270 | putU2(handler_pc - 8);
|
---|
1271 | // handler.Code.exception_table.catch_type
|
---|
1272 | putU2(0);
|
---|
1273 | }
|
---|
1274 | // handler.Code.attributes_count
|
---|
1275 | putU2(0);
|
---|
1276 | // handler.Code.attributes[]
|
---|
1277 |
|
---|
1278 | if (e.length > 0)
|
---|
1279 | {
|
---|
1280 | // handler.Exceptions.attribute_name_index
|
---|
1281 | putU2(utf8Info("Exceptions"));
|
---|
1282 | // handler.Exceptions.attribute_length
|
---|
1283 | putU4(2 * e.length + 2);
|
---|
1284 | // handler.Exceptions.number_of_exceptions
|
---|
1285 | putU2(e.length);
|
---|
1286 | // handler.Exceptions.exception_index_table[]
|
---|
1287 | for (int j = 0; j < e.length; j++)
|
---|
1288 | putU2(classInfo(e[j]));
|
---|
1289 | }
|
---|
1290 | }
|
---|
1291 |
|
---|
1292 | /**
|
---|
1293 | * Creates the Class object that corresponds to the bytecode buffers
|
---|
1294 | * built when this object was constructed.
|
---|
1295 | *
|
---|
1296 | * @param loader the class loader to define the proxy class in; null
|
---|
1297 | * implies the bootstrap class loader
|
---|
1298 | * @return the proxy class Class object
|
---|
1299 | */
|
---|
1300 | final Class generate(ClassLoader loader)
|
---|
1301 | {
|
---|
1302 | byte[] bytecode = new byte[pool.length() + stream.length()];
|
---|
1303 | // More efficient to bypass calling charAt() repetitively.
|
---|
1304 | char[] c = pool.toString().toCharArray();
|
---|
1305 | int i = c.length;
|
---|
1306 | while (--i >= 0)
|
---|
1307 | bytecode[i] = (byte) c[i];
|
---|
1308 | c = stream.toString().toCharArray();
|
---|
1309 | i = c.length;
|
---|
1310 | int j = bytecode.length;
|
---|
1311 | while (i > 0)
|
---|
1312 | bytecode[--j] = (byte) c[--i];
|
---|
1313 |
|
---|
1314 | // Patch the constant pool size, which we left at 0 earlier.
|
---|
1315 | int count = poolEntries.size() + 1;
|
---|
1316 | bytecode[8] = (byte) (count >> 8);
|
---|
1317 | bytecode[9] = (byte) count;
|
---|
1318 |
|
---|
1319 | try
|
---|
1320 | {
|
---|
1321 | // XXX Do we require more native support here?
|
---|
1322 |
|
---|
1323 | // XXX Security hole - it is possible for another thread to grab the
|
---|
1324 | // VMClassLoader.defineClass Method object, and abuse it while we
|
---|
1325 | // have temporarily made it accessible. Do we need to add some
|
---|
1326 | // synchronization lock to prevent user reflection while we use it?
|
---|
1327 |
|
---|
1328 | // XXX This is waiting on VM support for protection domains.
|
---|
1329 |
|
---|
1330 | Class vmClassLoader = Class.forName("java.lang.VMClassLoader");
|
---|
1331 | Class[] types = {ClassLoader.class, String.class,
|
---|
1332 | byte[].class, int.class, int.class,
|
---|
1333 | /* ProtectionDomain.class */ };
|
---|
1334 | Method m = vmClassLoader.getDeclaredMethod("defineClass", types);
|
---|
1335 |
|
---|
1336 | // Bypass the security check of setAccessible(true), since this
|
---|
1337 | // is trusted code. But note the comment above about the security
|
---|
1338 | // risk of doing this outside a synchronized block.
|
---|
1339 | m.flag = true;
|
---|
1340 | Object[] args = {loader, qualName, bytecode, new Integer(0),
|
---|
1341 | new Integer(bytecode.length),
|
---|
1342 | /* Object.class.getProtectionDomain() */ };
|
---|
1343 | Class clazz = (Class) m.invoke(null, args);
|
---|
1344 | m.flag = false;
|
---|
1345 |
|
---|
1346 | // Finally, initialize the m field of the proxy class, before
|
---|
1347 | // returning it.
|
---|
1348 |
|
---|
1349 | // No security risk here, since clazz has not been exposed yet,
|
---|
1350 | // so user code cannot grab the same reflection object.
|
---|
1351 | Field f = clazz.getDeclaredField("m");
|
---|
1352 | f.flag = true;
|
---|
1353 | // we can share the array, because it is not publicized
|
---|
1354 | f.set(null, methods);
|
---|
1355 | f.flag = false;
|
---|
1356 |
|
---|
1357 | return clazz;
|
---|
1358 | }
|
---|
1359 | catch (Throwable e)
|
---|
1360 | {
|
---|
1361 | // assert false;
|
---|
1362 | throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
---|
1363 | }
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | /**
|
---|
1367 | * Put a single byte on the stream.
|
---|
1368 | *
|
---|
1369 | * @param i the information to add (only lowest 8 bits are used)
|
---|
1370 | */
|
---|
1371 | private void putU1(int i)
|
---|
1372 | {
|
---|
1373 | stream.append((char) i);
|
---|
1374 | }
|
---|
1375 |
|
---|
1376 | /**
|
---|
1377 | * Put two bytes on the stream.
|
---|
1378 | *
|
---|
1379 | * @param i the information to add (only lowest 16 bits are used)
|
---|
1380 | */
|
---|
1381 | private void putU2(int i)
|
---|
1382 | {
|
---|
1383 | stream.append((char) (i >> 8)).append((char) i);
|
---|
1384 | }
|
---|
1385 |
|
---|
1386 | /**
|
---|
1387 | * Put four bytes on the stream.
|
---|
1388 | *
|
---|
1389 | * @param i the information to add (treated as unsigned)
|
---|
1390 | */
|
---|
1391 | private void putU4(int i)
|
---|
1392 | {
|
---|
1393 | stream.append((char) (i >> 24)).append((char) (i >> 16));
|
---|
1394 | stream.append((char) (i >> 8)).append((char) i);
|
---|
1395 | }
|
---|
1396 |
|
---|
1397 | /**
|
---|
1398 | * Put bytecode to load a constant integer on the stream. This only
|
---|
1399 | * needs to work for values less than Short.MAX_VALUE.
|
---|
1400 | *
|
---|
1401 | * @param i the int to add
|
---|
1402 | */
|
---|
1403 | private void putConst(int i)
|
---|
1404 | {
|
---|
1405 | if (i >= -1 && i <= 5)
|
---|
1406 | putU1(ICONST_0 + i);
|
---|
1407 | else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE)
|
---|
1408 | {
|
---|
1409 | putU1(BIPUSH);
|
---|
1410 | putU1(i);
|
---|
1411 | }
|
---|
1412 | else
|
---|
1413 | {
|
---|
1414 | putU1(SIPUSH);
|
---|
1415 | putU2(i);
|
---|
1416 | }
|
---|
1417 | }
|
---|
1418 |
|
---|
1419 | /**
|
---|
1420 | * Put bytecode to load a given local variable on the stream.
|
---|
1421 | *
|
---|
1422 | * @param i the slot to load
|
---|
1423 | * @param type the base type of the load
|
---|
1424 | */
|
---|
1425 | private void putLoad(int i, Class type)
|
---|
1426 | {
|
---|
1427 | int offset = 0;
|
---|
1428 | if (type == long.class)
|
---|
1429 | offset = 1;
|
---|
1430 | else if (type == float.class)
|
---|
1431 | offset = 2;
|
---|
1432 | else if (type == double.class)
|
---|
1433 | offset = 3;
|
---|
1434 | else if (! type.isPrimitive())
|
---|
1435 | offset = 4;
|
---|
1436 | if (i < 4)
|
---|
1437 | putU1(ILOAD_0 + 4 * offset + i);
|
---|
1438 | else
|
---|
1439 | {
|
---|
1440 | putU1(ILOAD + offset);
|
---|
1441 | putU1(i);
|
---|
1442 | }
|
---|
1443 | }
|
---|
1444 |
|
---|
1445 | /**
|
---|
1446 | * Given a primitive type, return its wrapper class name.
|
---|
1447 | *
|
---|
1448 | * @param clazz the primitive type (but not void.class)
|
---|
1449 | * @return the internal form of the wrapper class name
|
---|
1450 | */
|
---|
1451 | private String wrapper(Class clazz)
|
---|
1452 | {
|
---|
1453 | if (clazz == boolean.class)
|
---|
1454 | return "java/lang/Boolean";
|
---|
1455 | if (clazz == byte.class)
|
---|
1456 | return "java/lang/Byte";
|
---|
1457 | if (clazz == short.class)
|
---|
1458 | return "java/lang/Short";
|
---|
1459 | if (clazz == char.class)
|
---|
1460 | return "java/lang/Character";
|
---|
1461 | if (clazz == int.class)
|
---|
1462 | return "java/lang/Integer";
|
---|
1463 | if (clazz == long.class)
|
---|
1464 | return "java/lang/Long";
|
---|
1465 | if (clazz == float.class)
|
---|
1466 | return "java/lang/Float";
|
---|
1467 | if (clazz == double.class)
|
---|
1468 | return "java/lang/Double";
|
---|
1469 | // assert false;
|
---|
1470 | return null;
|
---|
1471 | }
|
---|
1472 |
|
---|
1473 | /**
|
---|
1474 | * Returns the entry of this String in the Constant pool, adding it
|
---|
1475 | * if necessary.
|
---|
1476 | *
|
---|
1477 | * @param str the String to resolve
|
---|
1478 | * @return the index of the String in the constant pool
|
---|
1479 | */
|
---|
1480 | private char utf8Info(String str)
|
---|
1481 | {
|
---|
1482 | String utf8 = toUtf8(str);
|
---|
1483 | int len = utf8.length();
|
---|
1484 | return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8);
|
---|
1485 | }
|
---|
1486 |
|
---|
1487 | /**
|
---|
1488 | * Returns the entry of the appropriate class info structure in the
|
---|
1489 | * Constant pool, adding it if necessary.
|
---|
1490 | *
|
---|
1491 | * @param name the class name, in internal form
|
---|
1492 | * @return the index of the ClassInfo in the constant pool
|
---|
1493 | */
|
---|
1494 | private char classInfo(String name)
|
---|
1495 | {
|
---|
1496 | char index = utf8Info(name);
|
---|
1497 | char[] c = {7, (char) (index >> 8), (char) (index & 0xff)};
|
---|
1498 | return poolIndex(new String(c));
|
---|
1499 | }
|
---|
1500 |
|
---|
1501 | /**
|
---|
1502 | * Returns the entry of the appropriate class info structure in the
|
---|
1503 | * Constant pool, adding it if necessary.
|
---|
1504 | *
|
---|
1505 | * @param clazz the class type
|
---|
1506 | * @return the index of the ClassInfo in the constant pool
|
---|
1507 | */
|
---|
1508 | private char classInfo(Class clazz)
|
---|
1509 | {
|
---|
1510 | return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(),
|
---|
1511 | false));
|
---|
1512 | }
|
---|
1513 |
|
---|
1514 | /**
|
---|
1515 | * Returns the entry of the appropriate fieldref, methodref, or
|
---|
1516 | * interfacemethodref info structure in the Constant pool, adding it
|
---|
1517 | * if necessary.
|
---|
1518 | *
|
---|
1519 | * @param structure FIELD, METHOD, or INTERFACE
|
---|
1520 | * @param clazz the class name, in internal form
|
---|
1521 | * @param name the simple reference name
|
---|
1522 | * @param type the type of the reference
|
---|
1523 | * @return the index of the appropriate Info structure in the constant pool
|
---|
1524 | */
|
---|
1525 | private char refInfo(byte structure, String clazz, String name,
|
---|
1526 | String type)
|
---|
1527 | {
|
---|
1528 | char cindex = classInfo(clazz);
|
---|
1529 | char ntindex = nameAndTypeInfo(name, type);
|
---|
1530 | // relies on FIELD == 1, METHOD == 2, INTERFACE == 3
|
---|
1531 | char[] c = {(char) (structure + 8),
|
---|
1532 | (char) (cindex >> 8), (char) (cindex & 0xff),
|
---|
1533 | (char) (ntindex >> 8), (char) (ntindex & 0xff)};
|
---|
1534 | return poolIndex(new String(c));
|
---|
1535 | }
|
---|
1536 |
|
---|
1537 | /**
|
---|
1538 | * Returns the entry of the appropriate nameAndTyperef info structure
|
---|
1539 | * in the Constant pool, adding it if necessary.
|
---|
1540 | *
|
---|
1541 | * @param name the simple name
|
---|
1542 | * @param type the reference type
|
---|
1543 | * @return the index of the NameAndTypeInfo structure in the constant pool
|
---|
1544 | */
|
---|
1545 | private char nameAndTypeInfo(String name, String type)
|
---|
1546 | {
|
---|
1547 | char nindex = utf8Info(name);
|
---|
1548 | char tindex = utf8Info(type);
|
---|
1549 | char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff),
|
---|
1550 | (char) (tindex >> 8), (char) (tindex & 0xff)};
|
---|
1551 | return poolIndex(new String(c));
|
---|
1552 | }
|
---|
1553 |
|
---|
1554 | /**
|
---|
1555 | * Converts a regular string to a UTF8 string, where the upper byte
|
---|
1556 | * of every char is 0, and '\\u0000' is not in the string. This is
|
---|
1557 | * basically to use a String as a fancy byte[], and while it is less
|
---|
1558 | * efficient in memory use, it is easier for hashing.
|
---|
1559 | *
|
---|
1560 | * @param str the original, in straight unicode
|
---|
1561 | * @return a modified string, in UTF8 format in the low bytes
|
---|
1562 | */
|
---|
1563 | private String toUtf8(String str)
|
---|
1564 | {
|
---|
1565 | final char[] ca = str.toCharArray();
|
---|
1566 | final int len = ca.length;
|
---|
1567 |
|
---|
1568 | // Avoid object creation, if str is already fits UTF8.
|
---|
1569 | int i;
|
---|
1570 | for (i = 0; i < len; i++)
|
---|
1571 | if (ca[i] == 0 || ca[i] > '\u007f')
|
---|
1572 | break;
|
---|
1573 | if (i == len)
|
---|
1574 | return str;
|
---|
1575 |
|
---|
1576 | final StringBuffer sb = new StringBuffer(str);
|
---|
1577 | sb.setLength(i);
|
---|
1578 | for ( ; i < len; i++)
|
---|
1579 | {
|
---|
1580 | final char c = ca[i];
|
---|
1581 | if (c > 0 && c <= '\u007f')
|
---|
1582 | sb.append(c);
|
---|
1583 | else if (c <= '\u07ff') // includes '\0'
|
---|
1584 | {
|
---|
1585 | sb.append((char) (0xc0 | (c >> 6)));
|
---|
1586 | sb.append((char) (0x80 | (c & 0x6f)));
|
---|
1587 | }
|
---|
1588 | else
|
---|
1589 | {
|
---|
1590 | sb.append((char) (0xe0 | (c >> 12)));
|
---|
1591 | sb.append((char) (0x80 | ((c >> 6) & 0x6f)));
|
---|
1592 | sb.append((char) (0x80 | (c & 0x6f)));
|
---|
1593 | }
|
---|
1594 | }
|
---|
1595 | return sb.toString();
|
---|
1596 | }
|
---|
1597 |
|
---|
1598 | /**
|
---|
1599 | * Returns the location of a byte sequence (conveniently wrapped in
|
---|
1600 | * a String with all characters between \u0001 and \u00ff inclusive)
|
---|
1601 | * in the constant pool, adding it if necessary.
|
---|
1602 | *
|
---|
1603 | * @param sequence the byte sequence to look for
|
---|
1604 | * @return the index of the sequence
|
---|
1605 | * @throws IllegalArgumentException if this would make the constant
|
---|
1606 | * pool overflow
|
---|
1607 | */
|
---|
1608 | private char poolIndex(String sequence)
|
---|
1609 | {
|
---|
1610 | Integer i = (Integer) poolEntries.get(sequence);
|
---|
1611 | if (i == null)
|
---|
1612 | {
|
---|
1613 | // pool starts at index 1
|
---|
1614 | int size = poolEntries.size() + 1;
|
---|
1615 | if (size >= 65535)
|
---|
1616 | throw new IllegalArgumentException("exceeds VM limitations");
|
---|
1617 | i = new Integer(size);
|
---|
1618 | poolEntries.put(sequence, i);
|
---|
1619 | pool.append(sequence);
|
---|
1620 | }
|
---|
1621 | return (char) i.intValue();
|
---|
1622 | }
|
---|
1623 | } // class ClassFactory
|
---|
1624 | }
|
---|