1 /* Proxy.java -- build a proxy class that implements reflected interfaces
2 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
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
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. */
39 package java
.lang
.reflect
;
41 import gnu
.java
.lang
.reflect
.TypeSignature
;
43 import java
.io
.Serializable
;
44 import java
.security
.ProtectionDomain
;
45 import java
.util
.Arrays
;
46 import java
.util
.HashMap
;
47 import java
.util
.HashSet
;
48 import java
.util
.Iterator
;
53 * This class allows you to dynamically create an instance of any (or
54 * even multiple) interfaces by reflection, and decide at runtime
55 * how that instance will behave by giving it an appropriate
56 * {@link InvocationHandler}. Proxy classes serialize specially, so
57 * that the proxy object can be reused between VMs, without requiring
58 * a persistent copy of the generated class code.
61 * To create a proxy for some interface Foo:
64 * InvocationHandler handler = new MyInvocationHandler(...);
65 * Class proxyClass = Proxy.getProxyClass(
66 * Foo.class.getClassLoader(), new Class[] { Foo.class });
67 * Foo f = (Foo) proxyClass
68 * .getConstructor(new Class[] { InvocationHandler.class })
69 * .newInstance(new Object[] { handler });
73 * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
74 * new Class[] { Foo.class },
78 * <h3>Dynamic Proxy Classes</h3>
79 * A dynamic proxy class is created at runtime, and has the following
82 * <li>The class is <code>public</code> and <code>final</code>,
83 * and is neither <code>abstract</code> nor an inner class.</li>
84 * <li>The class has no canonical name (there is no formula you can use
85 * to determine or generate its name), but begins with the
86 * sequence "$Proxy". Abuse this knowledge at your own peril.
87 * (For now, '$' in user identifiers is legal, but it may not
88 * be that way forever. You weren't using '$' in your
89 * identifiers, were you?)</li>
90 * <li>The class extends Proxy, and explicitly implements all the
91 * interfaces specified at creation, in order (this is important
92 * for determining how method invocation is resolved). Note that
93 * a proxy class implements {@link Serializable}, at least
94 * implicitly, since Proxy does, but true serial behavior
95 * depends on using a serializable invocation handler as well.</li>
96 * <li>If at least one interface is non-public, the proxy class
97 * will be in the same package. Otherwise, the package is
98 * unspecified. This will work even if the package is sealed
99 * from user-generated classes, because Proxy classes are
100 * generated by a trusted source. Meanwhile, the proxy class
101 * belongs to the classloader you designated.</li>
102 * <li>Reflection works as expected: {@link Class#getInterfaces()} and
103 * {@link Class#getMethods()} work as they do on normal classes.</li>
104 * <li>The method {@link #isProxyClass(Class)} will distinguish between
105 * true proxy classes and user extensions of this class. It only
106 * returns true for classes created by {@link #getProxyClass}.</li>
107 * <li>The {@link ProtectionDomain} of a proxy class is the same as for
108 * bootstrap classes, such as Object or Proxy, since it is created by
109 * a trusted source. This protection domain will typically be granted
110 * {@link java.security.AllPermission}. But this is not a security
111 * risk, since there are adequate permissions on reflection, which is
112 * the only way to create an instance of the proxy class.</li>
113 * <li>The proxy class contains a single constructor, which takes as
114 * its only argument an {@link InvocationHandler}. The method
115 * {@link #newProxyInstance(ClassLoader, Class[], InvocationHandler)}
116 * is shorthand to do the necessary reflection.</li>
119 * <h3>Proxy Instances</h3>
120 * A proxy instance is an instance of a proxy class. It has the
121 * following properties, many of which follow from the properties of a
122 * proxy class listed above:
124 * <li>For a proxy class with Foo listed as one of its interfaces, the
125 * expression <code>proxy instanceof Foo</code> will return true,
126 * and the expression <code>(Foo) proxy</code> will succeed without
127 * a {@link ClassCastException}.</li>
128 * <li>Each proxy instance has an invocation handler, which can be
129 * accessed by {@link #getInvocationHandler(Object)}. Any call
130 * to an interface method, including {@link Object#hashCode()},
131 * {@link Object#equals(Object)}, or {@link Object#toString()},
132 * but excluding the public final methods of Object, will be
133 * encoded and passed to the {@link InvocationHandler#invoke}
134 * method of this handler.</li>
137 * <h3>Inheritance Issues</h3>
138 * A proxy class may inherit a method from more than one interface.
139 * The order in which interfaces are listed matters, because it determines
140 * which reflected {@link Method} object will be passed to the invocation
141 * handler. This means that the dynamically generated class cannot
142 * determine through which interface a method is being invoked.<p>
144 * In short, if a method is declared in Object (namely, hashCode,
145 * equals, or toString), then Object will be used; otherwise, the
146 * leftmost interface that inherits or declares a method will be used,
147 * even if it has a more permissive throws clause than what the proxy
148 * class is allowed. Thus, in the invocation handler, it is not always
149 * safe to assume that every class listed in the throws clause of the
150 * passed Method object can safely be thrown; fortunately, the Proxy
151 * instance is robust enough to wrap all illegal checked exceptions in
152 * {@link UndeclaredThrowableException}.
154 * @see InvocationHandler
155 * @see UndeclaredThrowableException
157 * @author Eric Blake (ebb9@email.byu.edu)
159 * @status updated to 1.5, except for the use of ProtectionDomain
161 public class Proxy
implements Serializable
164 * Compatible with JDK 1.3+.
166 private static final long serialVersionUID
= -2222568056686623797L;
169 * Map of ProxyType to proxy class.
171 * @XXX This prevents proxy classes from being garbage collected.
172 * java.util.WeakHashSet is not appropriate, because that collects the
173 * keys, but we are interested in collecting the elements.
175 private static final Map proxyClasses
= new HashMap();
178 * The invocation handler for this proxy instance. For Proxy, this
179 * field is unused, but it appears here in order to be serialized in all
182 * <em>NOTE</em>: This implementation is more secure for proxy classes
183 * than what Sun specifies. Sun does not require h to be immutable, but
184 * this means you could change h after the fact by reflection. However,
185 * by making h immutable, we may break non-proxy classes which extend
187 * @serial invocation handler associated with this proxy instance
189 protected InvocationHandler h
;
192 * Constructs a new Proxy from a subclass (usually a proxy class),
193 * with the specified invocation handler.
195 * <em>NOTE</em>: This throws a NullPointerException if you attempt
196 * to create a proxy instance with a null handler using reflection.
197 * This behavior is not yet specified by Sun; see Sun Bug 4487672.
199 * @param handler the invocation handler, may be null if the subclass
200 * is not a proxy class
201 * @throws NullPointerException if handler is null and this is a proxy
204 protected Proxy(InvocationHandler handler
)
206 if (handler
== null && isProxyClass(getClass()))
207 throw new NullPointerException("invalid handler");
212 * Returns the proxy {@link Class} for the given ClassLoader and array
213 * of interfaces, dynamically generating it if necessary.
215 * <p>There are several restrictions on this method, the violation of
216 * which will result in an IllegalArgumentException or
217 * NullPointerException:</p>
220 * <li>All objects in `interfaces' must represent distinct interfaces.
221 * Classes, primitive types, null, and duplicates are forbidden.</li>
222 * <li>The interfaces must be visible in the specified ClassLoader.
223 * In other words, for each interface i:
224 * <code>Class.forName(i.getName(), false, loader) == i</code>
226 * <li>All non-public interfaces (if any) must reside in the same
227 * package, or the proxy class would be non-instantiable. If
228 * there are no non-public interfaces, the package of the proxy
229 * class is unspecified.</li>
230 * <li>All interfaces must be compatible - if two declare a method
231 * with the same name and parameters, the return type must be
232 * the same and the throws clause of the proxy class will be
233 * the maximal subset of subclasses of the throws clauses for
234 * each method that is overridden.</li>
235 * <li>VM constraints limit the number of interfaces a proxy class
236 * may directly implement (however, the indirect inheritance
237 * of {@link Serializable} does not count against this limit).
238 * Even though most VMs can theoretically have 65535
239 * superinterfaces for a class, the actual limit is smaller
240 * because a class's constant pool is limited to 65535 entries,
241 * and not all entries can be interfaces.</li>
244 * <p>Note that different orders of interfaces produce distinct classes.</p>
246 * @param loader the class loader to define the proxy class in; null
247 * implies the bootstrap class loader
248 * @param interfaces the array of interfaces the proxy class implements,
249 * may be empty, but not null
250 * @return the Class object of the proxy class
251 * @throws IllegalArgumentException if the constraints above were
252 * violated, except for problems with null
253 * @throws NullPointerException if `interfaces' is null or contains
256 // synchronized so that we aren't trying to build the same class
257 // simultaneously in two threads
258 public static synchronized Class
<?
> getProxyClass(ClassLoader loader
,
259 Class
<?
>... interfaces
)
261 interfaces
= (Class
[]) interfaces
.clone();
262 ProxyType pt
= new ProxyType(loader
, interfaces
);
263 Class clazz
= (Class
) proxyClasses
.get(pt
);
266 if (VMProxy
.HAVE_NATIVE_GET_PROXY_CLASS
)
267 clazz
= VMProxy
.getProxyClass(loader
, interfaces
);
270 ProxyData data
= (VMProxy
.HAVE_NATIVE_GET_PROXY_DATA
271 ? VMProxy
.getProxyData(loader
, interfaces
)
272 : ProxyData
.getProxyData(pt
));
274 clazz
= (VMProxy
.HAVE_NATIVE_GENERATE_PROXY_CLASS
275 ? VMProxy
.generateProxyClass(loader
, data
)
276 : new ClassFactory(data
).generate(loader
));
279 Object check
= proxyClasses
.put(pt
, clazz
);
280 // assert check == null && clazz != null;
281 if (check
!= null || clazz
== null)
282 throw new InternalError(/*"Fatal flaw in getProxyClass"*/);
288 * Combines several methods into one. This is equivalent to:
290 * Proxy.getProxyClass(loader, interfaces)
291 * .getConstructor(new Class[] {InvocationHandler.class})
292 * .newInstance(new Object[] {handler});
294 * except that it will not fail with the normal problems caused
295 * by reflection. It can still fail for the same reasons documented
296 * in getProxyClass, or if handler is null.
298 * @param loader the class loader to define the proxy class in; null
299 * implies the bootstrap class loader
300 * @param interfaces the array of interfaces the proxy class implements,
301 * may be empty, but not null
302 * @param handler the invocation handler, may not be null
303 * @return a proxy instance implementing the specified interfaces
304 * @throws IllegalArgumentException if the constraints for getProxyClass
305 * were violated, except for problems with null
306 * @throws NullPointerException if `interfaces' is null or contains
307 * a null entry, or if handler is null
308 * @see #getProxyClass(ClassLoader, Class[])
309 * @see Class#getConstructor(Class[])
310 * @see Constructor#newInstance(Object[])
312 public static Object
newProxyInstance(ClassLoader loader
,
313 Class
<?
>[] interfaces
,
314 InvocationHandler handler
)
318 // getProxyClass() and Proxy() throw the necessary exceptions
319 return getProxyClass(loader
, interfaces
)
320 .getConstructor(new Class
[] {InvocationHandler
.class})
321 .newInstance(new Object
[] {handler
});
323 catch (RuntimeException e
)
325 // Let IllegalArgumentException, NullPointerException escape.
326 // assert e instanceof IllegalArgumentException
327 // || e instanceof NullPointerException;
330 catch (InvocationTargetException e
)
332 // Let wrapped NullPointerException escape.
333 // assert e.getTargetException() instanceof NullPointerException
334 throw (NullPointerException
) e
.getCause();
338 // Covers InstantiationException, IllegalAccessException,
339 // NoSuchMethodException, none of which should be generated
340 // if the proxy class was generated correctly.
342 throw (Error
) new InternalError("Unexpected: " + e
).initCause(e
);
347 * Returns true if and only if the Class object is a dynamically created
348 * proxy class (created by <code>getProxyClass</code> or by the
349 * syntactic sugar of <code>newProxyInstance</code>).
351 * <p>This check is secure (in other words, it is not simply
352 * <code>clazz.getSuperclass() == Proxy.class</code>), it will not
353 * be spoofed by non-proxy classes that extend Proxy.
355 * @param clazz the class to check, must not be null
356 * @return true if the class represents a proxy class
357 * @throws NullPointerException if clazz is null
359 // This is synchronized on the off chance that another thread is
360 // trying to add a class to the map at the same time we read it.
361 public static synchronized boolean isProxyClass(Class
<?
> clazz
)
363 if (! Proxy
.class.isAssignableFrom(clazz
))
365 // This is a linear search, even though we could do an O(1) search
366 // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()).
367 return proxyClasses
.containsValue(clazz
);
371 * Returns the invocation handler for the given proxy instance.<p>
373 * <em>NOTE</em>: We guarantee a non-null result if successful,
374 * but Sun allows the creation of a proxy instance with a null
375 * handler. See the comments for {@link #Proxy(InvocationHandler)}.
377 * @param proxy the proxy instance, must not be null
378 * @return the invocation handler, guaranteed non-null.
379 * @throws IllegalArgumentException if
380 * <code>Proxy.isProxyClass(proxy.getClass())</code> returns false.
381 * @throws NullPointerException if proxy is null
383 public static InvocationHandler
getInvocationHandler(Object proxy
)
385 if (! isProxyClass(proxy
.getClass()))
386 throw new IllegalArgumentException("not a proxy instance");
387 return ((Proxy
) proxy
).h
;
391 * Helper class for mapping unique ClassLoader and interface combinations
394 * @author Eric Blake (ebb9@email.byu.edu)
396 private static final class ProxyType
399 * Store the class loader (may be null)
401 final ClassLoader loader
;
404 * Store the interfaces (never null, all elements are interfaces)
406 final Class
[] interfaces
;
409 * Construct the helper object.
411 * @param loader the class loader to define the proxy class in; null
412 * implies the bootstrap class loader
413 * @param interfaces an array of interfaces
415 ProxyType(ClassLoader loader
, Class
[] interfaces
)
417 this.loader
= loader
;
418 this.interfaces
= interfaces
;
422 * Calculates the hash code.
424 * @return a combination of the classloader and interfaces hashcodes.
426 public int hashCode()
428 int hash
= loader
== null ?
0 : loader
.hashCode();
429 for (int i
= 0; i
< interfaces
.length
; i
++)
430 hash
= hash
* 31 + interfaces
[i
].hashCode();
435 * Calculates equality.
437 * @param other object to compare to
438 * @return true if it is a ProxyType with same data
440 public boolean equals(Object other
)
442 ProxyType pt
= (ProxyType
) other
;
443 if (loader
!= pt
.loader
|| interfaces
.length
!= pt
.interfaces
.length
)
445 for (int i
= 0; i
< interfaces
.length
; i
++)
446 if (interfaces
[i
] != pt
.interfaces
[i
])
453 * Helper class which allows hashing of a method name and signature
454 * without worrying about return type, declaring class, or throws clause,
455 * and which reduces the maximally common throws clause between two methods
457 * @author Eric Blake (ebb9@email.byu.edu)
459 private static final class ProxySignature
462 * The core signatures which all Proxy instances handle.
464 static final HashMap coreMethods
= new HashMap();
470 = new ProxySignature(Object
.class
472 new Class
[] {Object
.class}));
473 coreMethods
.put(sig
, sig
);
474 sig
= new ProxySignature(Object
.class.getMethod("hashCode", null));
475 coreMethods
.put(sig
, sig
);
476 sig
= new ProxySignature(Object
.class.getMethod("toString", null));
477 coreMethods
.put(sig
, sig
);
482 throw (Error
) new InternalError("Unexpected: " + e
).initCause(e
);
487 * The underlying Method object, never null
492 * The set of compatible thrown exceptions, may be empty
494 final Set exceptions
= new HashSet();
497 * Construct a signature
499 * @param method the Method this signature is based on, never null
501 ProxySignature(Method method
)
503 this.method
= method
;
504 Class
[] exc
= method
.getExceptionTypes();
508 // discard unchecked exceptions
509 if (Error
.class.isAssignableFrom(exc
[i
])
510 || RuntimeException
.class.isAssignableFrom(exc
[i
]))
512 exceptions
.add(exc
[i
]);
517 * Given a method, make sure it's return type is identical
518 * to this, and adjust this signature's throws clause appropriately
520 * @param other the signature to merge in
521 * @throws IllegalArgumentException if the return types conflict
523 void checkCompatibility(ProxySignature other
)
525 if (method
.getReturnType() != other
.method
.getReturnType())
526 throw new IllegalArgumentException("incompatible return types: "
527 + method
+ ", " + other
.method
);
529 // if you can think of a more efficient way than this O(n^2) search,
531 int size1
= exceptions
.size();
532 int size2
= other
.exceptions
.size();
533 boolean[] valid1
= new boolean[size1
];
534 boolean[] valid2
= new boolean[size2
];
535 Iterator itr
= exceptions
.iterator();
539 Class c1
= (Class
) itr
.next();
540 Iterator itr2
= other
.exceptions
.iterator();
544 Class c2
= (Class
) itr2
.next();
545 if (c2
.isAssignableFrom(c1
))
547 if (c1
.isAssignableFrom(c2
))
552 itr
= exceptions
.iterator();
560 itr
= other
.exceptions
.iterator();
567 exceptions
.addAll(other
.exceptions
);
571 * Calculates the hash code.
573 * @return a combination of name and parameter types
575 public int hashCode()
577 int hash
= method
.getName().hashCode();
578 Class
[] types
= method
.getParameterTypes();
579 for (int i
= 0; i
< types
.length
; i
++)
580 hash
= hash
* 31 + types
[i
].hashCode();
585 * Calculates equality.
587 * @param other object to compare to
588 * @return true if it is a ProxySignature with same data
590 public boolean equals(Object other
)
592 ProxySignature ps
= (ProxySignature
) other
;
593 Class
[] types1
= method
.getParameterTypes();
594 Class
[] types2
= ps
.method
.getParameterTypes();
595 if (! method
.getName().equals(ps
.method
.getName())
596 || types1
.length
!= types2
.length
)
598 int i
= types1
.length
;
600 if (types1
[i
] != types2
[i
])
604 } // class ProxySignature
607 * A flat representation of all data needed to generate bytecode/instantiate
608 * a proxy class. This is basically a struct.
610 * @author Eric Blake (ebb9@email.byu.edu)
612 static final class ProxyData
615 * The package this class is in <b>including the trailing dot</b>
616 * or an empty string for the unnamed (aka default) package.
621 * The interfaces this class implements. Non-null, but possibly empty.
626 * The Method objects this class must pass as the second argument to
627 * invoke (also useful for determining what methods this class has).
628 * Non-null, non-empty (includes at least Object.hashCode, Object.equals,
629 * and Object.toString).
634 * The exceptions that do not need to be wrapped in
635 * UndeclaredThrowableException. exceptions[i] is the same as, or a
636 * subset of subclasses, of methods[i].getExceptionTypes(), depending on
637 * compatible throws clauses with multiple inheritance. It is unspecified
638 * if these lists include or exclude subclasses of Error and
639 * RuntimeException, but excluding them is harmless and generates a
642 Class
[][] exceptions
;
647 private static int count
;
650 * The id of this proxy class
652 final int id
= count
++;
655 * Construct a ProxyData with uninitialized data members.
662 * Return the name of a package (including the trailing dot)
663 * given the name of a class.
664 * Returns an empty string if no package. We use this in preference to
665 * using Class.getPackage() to avoid problems with ClassLoaders
666 * that don't set the package.
668 private static String
getPackage(Class k
)
670 String name
= k
.getName();
671 int idx
= name
.lastIndexOf('.');
672 return name
.substring(0, idx
+ 1);
676 * Verifies that the arguments are legal, and sets up remaining data
677 * This should only be called when a class must be generated, as
680 * @param pt the ProxyType to convert to ProxyData
681 * @return the flattened, verified ProxyData structure for use in
683 * @throws IllegalArgumentException if `interfaces' contains
684 * non-interfaces or incompatible combinations, and verify is true
685 * @throws NullPointerException if interfaces is null or contains null
687 static ProxyData
getProxyData(ProxyType pt
)
689 Map method_set
= (Map
) ProxySignature
.coreMethods
.clone();
690 boolean in_package
= false; // true if we encounter non-public interface
692 ProxyData data
= new ProxyData();
693 data
.interfaces
= pt
.interfaces
;
695 // if interfaces is too large, we croak later on when the constant
697 int i
= data
.interfaces
.length
;
700 Class inter
= data
.interfaces
[i
];
701 if (! inter
.isInterface())
702 throw new IllegalArgumentException("not an interface: " + inter
);
705 if (Class
.forName(inter
.getName(), false, pt
.loader
) != inter
)
706 throw new IllegalArgumentException("not accessible in "
707 + "classloader: " + inter
);
709 catch (ClassNotFoundException e
)
711 throw new IllegalArgumentException("not accessible in "
712 + "classloader: " + inter
);
714 if (! Modifier
.isPublic(inter
.getModifiers()))
717 String p
= getPackage(inter
);
718 if (! data
.pack
.equals(p
))
719 throw new IllegalArgumentException("non-public interfaces "
726 data
.pack
= getPackage(inter
);
728 for (int j
= i
-1; j
>= 0; j
--)
729 if (data
.interfaces
[j
] == inter
)
730 throw new IllegalArgumentException("duplicate interface: "
732 Method
[] methods
= inter
.getMethods();
733 int j
= methods
.length
;
736 if (isCoreObjectMethod(methods
[j
]))
738 // In the case of an attempt to redefine a public non-final
739 // method of Object, we must skip it
742 ProxySignature sig
= new ProxySignature(methods
[j
]);
743 ProxySignature old
= (ProxySignature
) method_set
.put(sig
, sig
);
745 sig
.checkCompatibility(old
);
749 i
= method_set
.size();
750 data
.methods
= new Method
[i
];
751 data
.exceptions
= new Class
[i
][];
752 Iterator itr
= method_set
.values().iterator();
755 ProxySignature sig
= (ProxySignature
) itr
.next();
756 data
.methods
[i
] = sig
.method
;
757 data
.exceptions
[i
] = (Class
[]) sig
.exceptions
758 .toArray(new Class
[sig
.exceptions
.size()]);
764 * Checks whether the method is similar to a public non-final method of
765 * Object or not (i.e. with the same name and parameter types). Note that we
766 * can't rely, directly or indirectly (via Collection.contains) on
767 * Method.equals as it would also check the declaring class, what we do not
768 * want. We only want to check that the given method have the same signature
769 * as a core method (same name and parameter types)
771 * @param method the method to check
772 * @return whether the method has the same name and parameter types as
773 * Object.equals, Object.hashCode or Object.toString
774 * @see java.lang.Object#equals(Object)
775 * @see java.lang.Object#hashCode()
776 * @see java.lang.Object#toString()
778 private static boolean isCoreObjectMethod(Method method
)
780 String methodName
= method
.getName();
781 if (methodName
.equals("equals"))
783 return Arrays
.equals(method
.getParameterTypes(),
784 new Class
[] { Object
.class });
786 if (methodName
.equals("hashCode"))
788 return method
.getParameterTypes().length
== 0;
790 if (methodName
.equals("toString"))
792 return method
.getParameterTypes().length
== 0;
800 * Does all the work of building a class. By making this a nested class,
801 * this code is not loaded in memory if the VM has a native
802 * implementation instead.
804 * @author Eric Blake (ebb9@email.byu.edu)
806 private static final class ClassFactory
808 /** Constants for assisting the compilation */
809 private static final byte FIELD
= 1;
810 private static final byte METHOD
= 2;
811 private static final byte INTERFACE
= 3;
812 private static final String CTOR_SIG
813 = "(Ljava/lang/reflect/InvocationHandler;)V";
814 private static final String INVOKE_SIG
= "(Ljava/lang/Object;"
815 + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
817 /** Bytecodes for insertion in the class definition byte[] */
818 private static final char ACONST_NULL
= 1;
819 private static final char ICONST_0
= 3;
820 private static final char BIPUSH
= 16;
821 private static final char SIPUSH
= 17;
822 private static final char ILOAD
= 21;
823 private static final char ILOAD_0
= 26;
824 private static final char ALOAD_0
= 42;
825 private static final char ALOAD_1
= 43;
826 private static final char AALOAD
= 50;
827 private static final char AASTORE
= 83;
828 private static final char DUP
= 89;
829 private static final char DUP_X1
= 90;
830 private static final char SWAP
= 95;
831 private static final char IRETURN
= 172;
832 private static final char LRETURN
= 173;
833 private static final char FRETURN
= 174;
834 private static final char DRETURN
= 175;
835 private static final char ARETURN
= 176;
836 private static final char RETURN
= 177;
837 private static final char GETSTATIC
= 178;
838 private static final char GETFIELD
= 180;
839 private static final char INVOKEVIRTUAL
= 182;
840 private static final char INVOKESPECIAL
= 183;
841 private static final char INVOKEINTERFACE
= 185;
842 private static final char NEW
= 187;
843 private static final char ANEWARRAY
= 189;
844 private static final char ATHROW
= 191;
845 private static final char CHECKCAST
= 192;
847 // Implementation note: we use StringBuffers to hold the byte data, since
848 // they automatically grow. However, we only use the low 8 bits of
849 // every char in the array, so we are using twice the necessary memory
850 // for the ease StringBuffer provides.
852 /** The constant pool. */
853 private final StringBuffer pool
= new StringBuffer();
854 /** The rest of the class data. */
855 private final StringBuffer stream
= new StringBuffer();
857 /** Map of strings to byte sequences, to minimize size of pool. */
858 private final Map poolEntries
= new HashMap();
860 /** The VM name of this proxy class. */
861 private final String qualName
;
864 * The Method objects the proxy class refers to when calling the
865 * invocation handler.
867 private final Method
[] methods
;
870 * Initializes the buffers with the bytecode contents for a proxy class.
872 * @param data the remainder of the class data
873 * @throws IllegalArgumentException if anything else goes wrong this
874 * late in the game; as far as I can tell, this will only happen
875 * if the constant pool overflows, which is possible even when
876 * the user doesn't exceed the 65535 interface limit
878 ClassFactory(ProxyData data
)
880 methods
= data
.methods
;
882 // magic = 0xcafebabe
884 // major_version = 46
885 // constant_pool_count: place-holder for now
886 pool
.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0");
887 // constant_pool[], filled in as we go
890 putU2(Modifier
.SUPER
| Modifier
.FINAL
| Modifier
.PUBLIC
);
892 qualName
= (data
.pack
+ "$Proxy" + data
.id
);
893 putU2(classInfo(TypeSignature
.getEncodingOfClass(qualName
, false)));
895 putU2(classInfo("java/lang/reflect/Proxy"));
898 putU2(data
.interfaces
.length
);
900 for (int i
= 0; i
< data
.interfaces
.length
; i
++)
901 putU2(classInfo(data
.interfaces
[i
]));
903 // Recall that Proxy classes serialize specially, so we do not need
904 // to worry about a <clinit> method for this field. Instead, we
905 // just assign it by reflection after the class is successfully loaded.
906 // fields_count - private static Method[] m;
910 putU2(Modifier
.PRIVATE
| Modifier
.STATIC
);
912 putU2(utf8Info("m"));
913 // m.descriptor_index
914 putU2(utf8Info("[Ljava/lang/reflect/Method;"));
915 // m.attributes_count
919 // methods_count - # handler methods, plus <init>
920 putU2(methods
.length
+ 1);
922 // <init>.access_flags
923 putU2(Modifier
.PUBLIC
);
925 putU2(utf8Info("<init>"));
926 // <init>.descriptor_index
927 putU2(utf8Info(CTOR_SIG
));
928 // <init>.attributes_count - only Code is needed
930 // <init>.Code.attribute_name_index
931 putU2(utf8Info("Code"));
932 // <init>.Code.attribute_length = 18
934 // $Proxynn(InvocationHandler h) { super(h); }
935 // <init>.Code.max_stack = 2
936 // <init>.Code.max_locals = 2
937 // <init>.Code.code_length = 6
938 // <init>.Code.code[]
939 stream
.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0
+ ALOAD_1
941 putU2(refInfo(METHOD
, "java/lang/reflect/Proxy", "<init>", CTOR_SIG
));
942 // <init>.Code.exception_table_length = 0
943 // <init>.Code.exception_table[]
944 // <init>.Code.attributes_count = 0
945 // <init>.Code.attributes[]
946 stream
.append(RETURN
+ "\0\0\0\0");
948 for (int i
= methods
.length
- 1; i
>= 0; i
--)
949 emitMethod(i
, data
.exceptions
[i
]);
953 // attributes[] - empty; omit SourceFile attribute
954 // XXX should we mark this with a Synthetic attribute?
958 * Produce the bytecode for a single method.
960 * @param i the index of the method we are building
961 * @param e the exceptions possible for the method
963 private void emitMethod(int i
, Class
[] e
)
965 // First, we precalculate the method length and other information.
967 Method m
= methods
[i
];
968 Class
[] paramtypes
= m
.getParameterTypes();
969 int wrap_overhead
= 0; // max words taken by wrapped primitive
970 int param_count
= 1; // 1 for this
971 int code_length
= 16; // aload_0, getfield, aload_0, getstatic, const,
972 // aaload, const/aconst_null, invokeinterface
975 if (i
> Byte
.MAX_VALUE
)
976 code_length
+= 2; // sipush
978 code_length
++; // bipush
980 if (paramtypes
.length
> 0)
982 code_length
+= 3; // anewarray
983 if (paramtypes
.length
> Byte
.MAX_VALUE
)
984 code_length
+= 2; // sipush
985 else if (paramtypes
.length
> 5)
986 code_length
++; // bipush
987 for (int j
= 0; j
< paramtypes
.length
; j
++)
989 code_length
+= 4; // dup, const, load, store
990 Class type
= paramtypes
[j
];
993 if (j
> Byte
.MAX_VALUE
)
994 code_length
+= 2; // sipush
996 code_length
++; // bipush
998 if (param_count
>= 4)
999 code_length
++; // 2-byte load
1001 if (type
.isPrimitive())
1003 code_length
+= 7; // new, dup, invokespecial
1004 if (type
== long.class || type
== double.class)
1009 else if (wrap_overhead
< 2)
1014 int end_pc
= code_length
;
1015 Class ret_type
= m
.getReturnType();
1016 if (ret_type
== void.class)
1017 code_length
++; // return
1018 else if (ret_type
.isPrimitive())
1019 code_length
+= 7; // cast, invokevirtual, return
1021 code_length
+= 4; // cast, return
1022 int exception_count
= 0;
1023 boolean throws_throwable
= false;
1024 for (int j
= 0; j
< e
.length
; j
++)
1025 if (e
[j
] == Throwable
.class)
1027 throws_throwable
= true;
1030 if (! throws_throwable
)
1032 exception_count
= e
.length
+ 3; // Throwable, Error, RuntimeException
1033 code_length
+= 9; // new, dup_x1, swap, invokespecial, athrow
1035 int handler_pc
= code_length
- 1;
1036 StringBuffer signature
= new StringBuffer("(");
1037 for (int j
= 0; j
< paramtypes
.length
; j
++)
1038 signature
.append(TypeSignature
.getEncodingOfClass(paramtypes
[j
]));
1039 signature
.append(")").append(TypeSignature
.getEncodingOfClass(ret_type
));
1041 // Now we have enough information to emit the method.
1043 // handler.access_flags
1044 putU2(Modifier
.PUBLIC
| Modifier
.FINAL
);
1045 // handler.name_index
1046 putU2(utf8Info(m
.getName()));
1047 // handler.descriptor_index
1048 putU2(utf8Info(signature
.toString()));
1049 // handler.attributes_count - Code is necessary, Exceptions possible
1050 putU2(e
.length
> 0 ?
2 : 1);
1052 // handler.Code.info:
1053 // type name(args) {
1055 // return (type) h.invoke(this, methods[i], new Object[] {args});
1056 // } catch (<declared Exceptions> e) {
1058 // } catch (Throwable t) {
1059 // throw new UndeclaredThrowableException(t);
1063 // if arg_n is primitive, wrap it
1064 // if method throws Throwable, try-catch is not needed
1065 // if method returns void, return statement not needed
1066 // if method returns primitive, unwrap it
1067 // save space by sharing code for all the declared handlers
1069 // handler.Code.attribute_name_index
1070 putU2(utf8Info("Code"));
1071 // handler.Code.attribute_length
1072 putU4(12 + code_length
+ 8 * exception_count
);
1073 // handler.Code.max_stack
1074 putU2(param_count
== 1 ?
4 : 7 + wrap_overhead
);
1075 // handler.Code.max_locals
1077 // handler.Code.code_length
1079 // handler.Code.code[]
1082 putU2(refInfo(FIELD
, "java/lang/reflect/Proxy", "h",
1083 "Ljava/lang/reflect/InvocationHandler;"));
1086 putU2(refInfo(FIELD
, TypeSignature
.getEncodingOfClass(qualName
, false),
1087 "m", "[Ljava/lang/reflect/Method;"));
1090 if (paramtypes
.length
> 0)
1092 putConst(paramtypes
.length
);
1094 putU2(classInfo("java/lang/Object"));
1096 for (int j
= 0; j
< paramtypes
.length
; j
++, param_count
++)
1100 if (paramtypes
[j
].isPrimitive())
1103 putU2(classInfo(wrapper(paramtypes
[j
])));
1106 putLoad(param_count
, paramtypes
[j
]);
1107 if (paramtypes
[j
].isPrimitive())
1109 putU1(INVOKESPECIAL
);
1110 putU2(refInfo(METHOD
, wrapper(paramtypes
[j
]), "<init>",
1111 '(' + (TypeSignature
1112 .getEncodingOfClass(paramtypes
[j
])
1114 if (paramtypes
[j
] == long.class
1115 || paramtypes
[j
] == double.class)
1123 putU1(INVOKEINTERFACE
);
1124 putU2(refInfo(INTERFACE
, "java/lang/reflect/InvocationHandler",
1125 "invoke", INVOKE_SIG
));
1126 putU1(4); // InvocationHandler, this, Method, Object[]
1128 if (ret_type
== void.class)
1130 else if (ret_type
.isPrimitive())
1133 putU2(classInfo(wrapper(ret_type
)));
1134 putU1(INVOKEVIRTUAL
);
1135 putU2(refInfo(METHOD
, wrapper(ret_type
),
1136 ret_type
.getName() + "Value",
1137 "()" + TypeSignature
.getEncodingOfClass(ret_type
)));
1138 if (ret_type
== long.class)
1140 else if (ret_type
== float.class)
1142 else if (ret_type
== double.class)
1150 putU2(classInfo(ret_type
));
1153 if (! throws_throwable
)
1156 putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
1159 putU1(INVOKESPECIAL
);
1160 putU2(refInfo(METHOD
,
1161 "java/lang/reflect/UndeclaredThrowableException",
1162 "<init>", "(Ljava/lang/Throwable;)V"));
1166 // handler.Code.exception_table_length
1167 putU2(exception_count
);
1168 // handler.Code.exception_table[]
1169 if (! throws_throwable
)
1171 // handler.Code.exception_table.start_pc
1173 // handler.Code.exception_table.end_pc
1175 // handler.Code.exception_table.handler_pc
1177 // handler.Code.exception_table.catch_type
1178 putU2(classInfo("java/lang/Error"));
1179 // handler.Code.exception_table.start_pc
1181 // handler.Code.exception_table.end_pc
1183 // handler.Code.exception_table.handler_pc
1185 // handler.Code.exception_table.catch_type
1186 putU2(classInfo("java/lang/RuntimeException"));
1187 for (int j
= 0; j
< e
.length
; j
++)
1189 // handler.Code.exception_table.start_pc
1191 // handler.Code.exception_table.end_pc
1193 // handler.Code.exception_table.handler_pc
1195 // handler.Code.exception_table.catch_type
1196 putU2(classInfo(e
[j
]));
1198 // handler.Code.exception_table.start_pc
1200 // handler.Code.exception_table.end_pc
1202 // handler.Code.exception_table.handler_pc -
1203 // -8 for undeclared handler, which falls thru to normal one
1204 putU2(handler_pc
- 8);
1205 // handler.Code.exception_table.catch_type
1208 // handler.Code.attributes_count
1210 // handler.Code.attributes[]
1214 // handler.Exceptions.attribute_name_index
1215 putU2(utf8Info("Exceptions"));
1216 // handler.Exceptions.attribute_length
1217 putU4(2 * e
.length
+ 2);
1218 // handler.Exceptions.number_of_exceptions
1220 // handler.Exceptions.exception_index_table[]
1221 for (int j
= 0; j
< e
.length
; j
++)
1222 putU2(classInfo(e
[j
]));
1227 * Creates the Class object that corresponds to the bytecode buffers
1228 * built when this object was constructed.
1230 * @param loader the class loader to define the proxy class in; null
1231 * implies the bootstrap class loader
1232 * @return the proxy class Class object
1234 Class
generate(ClassLoader loader
)
1236 byte[] bytecode
= new byte[pool
.length() + stream
.length()];
1237 // More efficient to bypass calling charAt() repetitively.
1238 char[] c
= pool
.toString().toCharArray();
1241 bytecode
[i
] = (byte) c
[i
];
1242 c
= stream
.toString().toCharArray();
1244 int j
= bytecode
.length
;
1246 bytecode
[--j
] = (byte) c
[--i
];
1248 // Patch the constant pool size, which we left at 0 earlier.
1249 int count
= poolEntries
.size() + 1;
1250 bytecode
[8] = (byte) (count
>> 8);
1251 bytecode
[9] = (byte) count
;
1255 Class vmClassLoader
= Class
.forName("java.lang.VMClassLoader");
1256 Class
[] types
= {ClassLoader
.class, String
.class,
1257 byte[].class, int.class, int.class,
1258 ProtectionDomain
.class };
1259 Method m
= vmClassLoader
.getDeclaredMethod("defineClass", types
);
1260 // We can bypass the security check of setAccessible(true), since
1261 // we're in the same package.
1264 Object
[] args
= {loader
, qualName
, bytecode
, new Integer(0),
1265 new Integer(bytecode
.length
),
1266 Object
.class.getProtectionDomain() };
1267 Class clazz
= (Class
) m
.invoke(null, args
);
1269 // Finally, initialize the m field of the proxy class, before
1271 Field f
= clazz
.getDeclaredField("m");
1273 // we can share the array, because it is not publicized
1274 f
.set(null, methods
);
1281 throw (Error
) new InternalError("Unexpected: " + e
).initCause(e
);
1286 * Put a single byte on the stream.
1288 * @param i the information to add (only lowest 8 bits are used)
1290 private void putU1(int i
)
1292 stream
.append((char) i
);
1296 * Put two bytes on the stream.
1298 * @param i the information to add (only lowest 16 bits are used)
1300 private void putU2(int i
)
1302 stream
.append((char) (i
>> 8)).append((char) i
);
1306 * Put four bytes on the stream.
1308 * @param i the information to add (treated as unsigned)
1310 private void putU4(int i
)
1312 stream
.append((char) (i
>> 24)).append((char) (i
>> 16));
1313 stream
.append((char) (i
>> 8)).append((char) i
);
1317 * Put bytecode to load a constant integer on the stream. This only
1318 * needs to work for values less than Short.MAX_VALUE.
1320 * @param i the int to add
1322 private void putConst(int i
)
1324 if (i
>= -1 && i
<= 5)
1325 putU1(ICONST_0
+ i
);
1326 else if (i
>= Byte
.MIN_VALUE
&& i
<= Byte
.MAX_VALUE
)
1339 * Put bytecode to load a given local variable on the stream.
1341 * @param i the slot to load
1342 * @param type the base type of the load
1344 private void putLoad(int i
, Class type
)
1347 if (type
== long.class)
1349 else if (type
== float.class)
1351 else if (type
== double.class)
1353 else if (! type
.isPrimitive())
1356 putU1(ILOAD_0
+ 4 * offset
+ i
);
1359 putU1(ILOAD
+ offset
);
1365 * Given a primitive type, return its wrapper class name.
1367 * @param clazz the primitive type (but not void.class)
1368 * @return the internal form of the wrapper class name
1370 private String
wrapper(Class clazz
)
1372 if (clazz
== boolean.class)
1373 return "java/lang/Boolean";
1374 if (clazz
== byte.class)
1375 return "java/lang/Byte";
1376 if (clazz
== short.class)
1377 return "java/lang/Short";
1378 if (clazz
== char.class)
1379 return "java/lang/Character";
1380 if (clazz
== int.class)
1381 return "java/lang/Integer";
1382 if (clazz
== long.class)
1383 return "java/lang/Long";
1384 if (clazz
== float.class)
1385 return "java/lang/Float";
1386 if (clazz
== double.class)
1387 return "java/lang/Double";
1393 * Returns the entry of this String in the Constant pool, adding it
1396 * @param str the String to resolve
1397 * @return the index of the String in the constant pool
1399 private char utf8Info(String str
)
1401 String utf8
= toUtf8(str
);
1402 int len
= utf8
.length();
1403 return poolIndex("\1" + (char) (len
>> 8) + (char) (len
& 0xff) + utf8
);
1407 * Returns the entry of the appropriate class info structure in the
1408 * Constant pool, adding it if necessary.
1410 * @param name the class name, in internal form
1411 * @return the index of the ClassInfo in the constant pool
1413 private char classInfo(String name
)
1415 char index
= utf8Info(name
);
1416 char[] c
= {7, (char) (index
>> 8), (char) (index
& 0xff)};
1417 return poolIndex(new String(c
));
1421 * Returns the entry of the appropriate class info structure in the
1422 * Constant pool, adding it if necessary.
1424 * @param clazz the class type
1425 * @return the index of the ClassInfo in the constant pool
1427 private char classInfo(Class clazz
)
1429 return classInfo(TypeSignature
.getEncodingOfClass(clazz
.getName(),
1434 * Returns the entry of the appropriate fieldref, methodref, or
1435 * interfacemethodref info structure in the Constant pool, adding it
1438 * @param structure FIELD, METHOD, or INTERFACE
1439 * @param clazz the class name, in internal form
1440 * @param name the simple reference name
1441 * @param type the type of the reference
1442 * @return the index of the appropriate Info structure in the constant pool
1444 private char refInfo(byte structure
, String clazz
, String name
,
1447 char cindex
= classInfo(clazz
);
1448 char ntindex
= nameAndTypeInfo(name
, type
);
1449 // relies on FIELD == 1, METHOD == 2, INTERFACE == 3
1450 char[] c
= {(char) (structure
+ 8),
1451 (char) (cindex
>> 8), (char) (cindex
& 0xff),
1452 (char) (ntindex
>> 8), (char) (ntindex
& 0xff)};
1453 return poolIndex(new String(c
));
1457 * Returns the entry of the appropriate nameAndTyperef info structure
1458 * in the Constant pool, adding it if necessary.
1460 * @param name the simple name
1461 * @param type the reference type
1462 * @return the index of the NameAndTypeInfo structure in the constant pool
1464 private char nameAndTypeInfo(String name
, String type
)
1466 char nindex
= utf8Info(name
);
1467 char tindex
= utf8Info(type
);
1468 char[] c
= {12, (char) (nindex
>> 8), (char) (nindex
& 0xff),
1469 (char) (tindex
>> 8), (char) (tindex
& 0xff)};
1470 return poolIndex(new String(c
));
1474 * Converts a regular string to a UTF8 string, where the upper byte
1475 * of every char is 0, and '\\u0000' is not in the string. This is
1476 * basically to use a String as a fancy byte[], and while it is less
1477 * efficient in memory use, it is easier for hashing.
1479 * @param str the original, in straight unicode
1480 * @return a modified string, in UTF8 format in the low bytes
1482 private String
toUtf8(String str
)
1484 final char[] ca
= str
.toCharArray();
1485 final int len
= ca
.length
;
1487 // Avoid object creation, if str is already fits UTF8.
1489 for (i
= 0; i
< len
; i
++)
1490 if (ca
[i
] == 0 || ca
[i
] > '\u007f')
1495 final StringBuffer sb
= new StringBuffer(str
);
1497 for ( ; i
< len
; i
++)
1499 final char c
= ca
[i
];
1500 if (c
> 0 && c
<= '\u007f')
1502 else if (c
<= '\u07ff') // includes '\0'
1504 sb
.append((char) (0xc0 | (c
>> 6)));
1505 sb
.append((char) (0x80 | (c
& 0x6f)));
1509 sb
.append((char) (0xe0 | (c
>> 12)));
1510 sb
.append((char) (0x80 | ((c
>> 6) & 0x6f)));
1511 sb
.append((char) (0x80 | (c
& 0x6f)));
1514 return sb
.toString();
1518 * Returns the location of a byte sequence (conveniently wrapped in
1519 * a String with all characters between \u0001 and \u00ff inclusive)
1520 * in the constant pool, adding it if necessary.
1522 * @param sequence the byte sequence to look for
1523 * @return the index of the sequence
1524 * @throws IllegalArgumentException if this would make the constant
1527 private char poolIndex(String sequence
)
1529 Integer i
= (Integer
) poolEntries
.get(sequence
);
1532 // pool starts at index 1
1533 int size
= poolEntries
.size() + 1;
1535 throw new IllegalArgumentException("exceeds VM limitations");
1536 i
= new Integer(size
);
1537 poolEntries
.put(sequence
, i
);
1538 pool
.append(sequence
);
1540 return (char) i
.intValue();
1542 } // class ClassFactory