1 /* Proxy.java -- build a proxy class that implements reflected interfaces
2 Copyright (C) 2001, 2002, 2003, 2004, 2005 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., 59 Temple Place, Suite 330, 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
.classpath
.Configuration
;
42 import gnu
.java
.lang
.reflect
.TypeSignature
;
44 import java
.io
.Serializable
;
45 import java
.security
.ProtectionDomain
;
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()} 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 #newInstance} is shorthand to do the necessary
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.4, 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
,
261 interfaces
= (Class
[]) interfaces
.clone();
262 ProxyType pt
= new ProxyType(loader
, interfaces
);
263 Class clazz
= (Class
) proxyClasses
.get(pt
);
266 if (Configuration
.HAVE_NATIVE_GET_PROXY_CLASS
)
267 clazz
= getProxyClass0(loader
, interfaces
);
270 ProxyData data
= (Configuration
.HAVE_NATIVE_GET_PROXY_DATA
271 ?
getProxyData0(loader
, interfaces
)
272 : ProxyData
.getProxyData(pt
));
274 clazz
= (Configuration
.HAVE_NATIVE_GENERATE_PROXY_CLASS
275 ?
generateProxyClass0(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
,
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 * Optional native method to replace (and speed up) the pure Java
392 * implementation of getProxyClass. Only needed if
393 * Configuration.HAVE_NATIVE_GET_PROXY_CLASS is true, this does the
394 * work of both getProxyData0 and generateProxyClass0 with no
395 * intermediate form in Java. The native code may safely assume that
396 * this class must be created, and does not already exist.
398 * @param loader the class loader to define the proxy class in; null
399 * implies the bootstrap class loader
400 * @param interfaces the interfaces the class will extend
401 * @return the generated proxy class
402 * @throws IllegalArgumentException if the constraints for getProxyClass
403 * were violated, except for problems with null
404 * @throws NullPointerException if `interfaces' is null or contains
405 * a null entry, or if handler is null
406 * @see Configuration#HAVE_NATIVE_GET_PROXY_CLASS
407 * @see #getProxyClass(ClassLoader, Class[])
408 * @see #getProxyData0(ClassLoader, Class[])
409 * @see #generateProxyClass0(ProxyData)
411 private static native Class
getProxyClass0(ClassLoader loader
,
415 * Optional native method to replace (and speed up) the pure Java
416 * implementation of getProxyData. Only needed if
417 * Configuration.HAVE_NATIVE_GET_PROXY_DATA is true. The native code
418 * may safely assume that a new ProxyData object must be created which
419 * does not duplicate any existing ones.
421 * @param loader the class loader to define the proxy class in; null
422 * implies the bootstrap class loader
423 * @param interfaces the interfaces the class will extend
424 * @return all data that is required to make this proxy class
425 * @throws IllegalArgumentException if the constraints for getProxyClass
426 * were violated, except for problems with null
427 * @throws NullPointerException if `interfaces' is null or contains
428 * a null entry, or if handler is null
429 * @see Configuration.HAVE_NATIVE_GET_PROXY_DATA
430 * @see #getProxyClass(ClassLoader, Class[])
431 * @see #getProxyClass0(ClassLoader, Class[])
432 * @see ProxyType#getProxyData()
434 private static native ProxyData
getProxyData0(ClassLoader loader
,
438 * Optional native method to replace (and speed up) the pure Java
439 * implementation of generateProxyClass. Only needed if
440 * Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS is true. The native
441 * code may safely assume that a new Class must be created, and that
442 * the ProxyData object does not describe any existing class.
444 * @param loader the class loader to define the proxy class in; null
445 * implies the bootstrap class loader
446 * @param data the struct of information to convert to a Class. This
447 * has already been verified for all problems except exceeding
449 * @return the newly generated class
450 * @throws IllegalArgumentException if VM limitations are exceeded
451 * @see #getProxyClass(ClassLoader, Class[])
452 * @see #getProxyClass0(ClassLoader, Class[])
453 * @see ProxyData#generateProxyClass(ClassLoader)
455 private static native Class
generateProxyClass0(ClassLoader loader
,
459 * Helper class for mapping unique ClassLoader and interface combinations
462 * @author Eric Blake (ebb9@email.byu.edu)
464 private static final class ProxyType
467 * Store the class loader (may be null)
469 final ClassLoader loader
;
472 * Store the interfaces (never null, all elements are interfaces)
474 final Class
[] interfaces
;
477 * Construct the helper object.
479 * @param loader the class loader to define the proxy class in; null
480 * implies the bootstrap class loader
481 * @param interfaces an array of interfaces
483 ProxyType(ClassLoader loader
, Class
[] interfaces
)
486 loader
= ClassLoader
.getSystemClassLoader();
487 this.loader
= loader
;
488 this.interfaces
= interfaces
;
492 * Calculates the hash code.
494 * @return a combination of the classloader and interfaces hashcodes.
496 public int hashCode()
498 //loader is always not null
499 int hash
= loader
.hashCode();
500 for (int i
= 0; i
< interfaces
.length
; i
++)
501 hash
= hash
* 31 + interfaces
[i
].hashCode();
505 // A more comprehensive comparison of two arrays,
506 // ignore array element order, and
507 // ignore redundant elements
508 private static boolean sameTypes(Class arr1
[], Class arr2
[]) {
509 if (arr1
.length
== 1 && arr2
.length
== 1) {
510 return arr1
[0] == arr2
[0];
513 // total occurrance of elements of arr1 in arr2
514 int total_occ_of_arr1_in_arr2
= 0;
516 for (int i
= arr1
.length
; --i
>= 0; )
519 for (int j
= i
; --j
>= 0; )
522 { //found duplicate type
527 // count c(a unique element of arr1)'s
528 // occurrences in arr2
530 for (int j
= arr2
.length
; --j
>= 0; )
537 if (occ_in_arr2
== 0)
538 { // t does not occur in arr2
542 total_occ_of_arr1_in_arr2
+= occ_in_arr2
;
544 // now, each element of arr2 must have been visited
545 return total_occ_of_arr1_in_arr2
== arr2
.length
;
549 * Calculates equality.
551 * @param the object to compare to
552 * @return true if it is a ProxyType with same data
554 public boolean equals(Object other
)
556 ProxyType pt
= (ProxyType
) other
;
557 if (loader
!= pt
.loader
|| interfaces
.length
!= pt
.interfaces
.length
)
559 return sameTypes(interfaces
, pt
.interfaces
);
564 * Helper class which allows hashing of a method name and signature
565 * without worrying about return type, declaring class, or throws clause,
566 * and which reduces the maximally common throws clause between two methods
568 * @author Eric Blake (ebb9@email.byu.edu)
570 private static final class ProxySignature
573 * The core signatures which all Proxy instances handle.
575 static final HashMap coreMethods
= new HashMap();
581 = new ProxySignature(Object
.class
583 new Class
[] {Object
.class}));
584 coreMethods
.put(sig
, sig
);
585 sig
= new ProxySignature(Object
.class.getMethod("hashCode", null));
586 coreMethods
.put(sig
, sig
);
587 sig
= new ProxySignature(Object
.class.getMethod("toString", null));
588 coreMethods
.put(sig
, sig
);
593 throw (Error
) new InternalError("Unexpected: " + e
).initCause(e
);
598 * The underlying Method object, never null
603 * The set of compatible thrown exceptions, may be empty
605 final Set exceptions
= new HashSet();
608 * Construct a signature
610 * @param method the Method this signature is based on, never null
612 ProxySignature(Method method
)
614 this.method
= method
;
615 Class
[] exc
= method
.getExceptionTypes();
619 // discard unchecked exceptions
620 if (Error
.class.isAssignableFrom(exc
[i
])
621 || RuntimeException
.class.isAssignableFrom(exc
[i
]))
623 exceptions
.add(exc
[i
]);
628 * Given a method, make sure it's return type is identical
629 * to this, and adjust this signature's throws clause appropriately
631 * @param other the signature to merge in
632 * @throws IllegalArgumentException if the return types conflict
634 void checkCompatibility(ProxySignature other
)
636 if (method
.getReturnType() != other
.method
.getReturnType())
637 throw new IllegalArgumentException("incompatible return types: "
638 + method
+ ", " + other
.method
);
640 // if you can think of a more efficient way than this O(n^2) search,
642 int size1
= exceptions
.size();
643 int size2
= other
.exceptions
.size();
644 boolean[] valid1
= new boolean[size1
];
645 boolean[] valid2
= new boolean[size2
];
646 Iterator itr
= exceptions
.iterator();
650 Class c1
= (Class
) itr
.next();
651 Iterator itr2
= other
.exceptions
.iterator();
655 Class c2
= (Class
) itr2
.next();
656 if (c2
.isAssignableFrom(c1
))
658 if (c1
.isAssignableFrom(c2
))
663 itr
= exceptions
.iterator();
671 itr
= other
.exceptions
.iterator();
678 exceptions
.addAll(other
.exceptions
);
682 * Calculates the hash code.
684 * @return a combination of name and parameter types
686 public int hashCode()
688 int hash
= method
.getName().hashCode();
689 Class
[] types
= method
.getParameterTypes();
690 for (int i
= 0; i
< types
.length
; i
++)
691 hash
= hash
* 31 + types
[i
].hashCode();
696 * Calculates equality.
698 * @param the object to compare to
699 * @return true if it is a ProxySignature with same data
701 public boolean equals(Object other
)
703 ProxySignature ps
= (ProxySignature
) other
;
704 Class
[] types1
= method
.getParameterTypes();
705 Class
[] types2
= ps
.method
.getParameterTypes();
706 if (! method
.getName().equals(ps
.method
.getName())
707 || types1
.length
!= types2
.length
)
709 int i
= types1
.length
;
711 if (types1
[i
] != types2
[i
])
715 } // class ProxySignature
718 * A flat representation of all data needed to generate bytecode/instantiate
719 * a proxy class. This is basically a struct.
721 * @author Eric Blake (ebb9@email.byu.edu)
723 private static final class ProxyData
726 * The package this class is in <b>including the trailing dot</b>
727 * or an empty string for the unnamed (aka default) package.
732 * The interfaces this class implements. Non-null, but possibly empty.
737 * The Method objects this class must pass as the second argument to
738 * invoke (also useful for determining what methods this class has).
739 * Non-null, non-empty (includes at least Object.hashCode, Object.equals,
740 * and Object.toString).
745 * The exceptions that do not need to be wrapped in
746 * UndeclaredThrowableException. exceptions[i] is the same as, or a
747 * subset of subclasses, of methods[i].getExceptionTypes(), depending on
748 * compatible throws clauses with multiple inheritance. It is unspecified
749 * if these lists include or exclude subclasses of Error and
750 * RuntimeException, but excluding them is harmless and generates a
753 Class
[][] exceptions
;
758 private static int count
;
761 * The id of this proxy class
763 final int id
= count
++;
766 * Construct a ProxyData with uninitialized data members.
773 * Return the name of a package (including the trailing dot)
774 * given the name of a class.
775 * Returns an empty string if no package. We use this in preference to
776 * using Class.getPackage() to avoid problems with ClassLoaders
777 * that don't set the package.
779 private static String
getPackage(Class k
)
781 String name
= k
.getName();
782 int idx
= name
.lastIndexOf('.');
783 return name
.substring(0, idx
+ 1);
787 * Verifies that the arguments are legal, and sets up remaining data
788 * This should only be called when a class must be generated, as
791 * @param pt the ProxyType to convert to ProxyData
792 * @return the flattened, verified ProxyData structure for use in
794 * @throws IllegalArgumentException if `interfaces' contains
795 * non-interfaces or incompatible combinations, and verify is true
796 * @throws NullPointerException if interfaces is null or contains null
798 static ProxyData
getProxyData(ProxyType pt
)
800 Map method_set
= (Map
) ProxySignature
.coreMethods
.clone();
801 boolean in_package
= false; // true if we encounter non-public interface
803 ProxyData data
= new ProxyData();
804 data
.interfaces
= pt
.interfaces
;
806 // if interfaces is too large, we croak later on when the constant
808 int i
= data
.interfaces
.length
;
811 Class inter
= data
.interfaces
[i
];
812 if (! inter
.isInterface())
813 throw new IllegalArgumentException("not an interface: " + inter
);
816 if (Class
.forName(inter
.getName(), false, pt
.loader
) != inter
)
817 throw new IllegalArgumentException("not accessible in "
818 + "classloader: " + inter
);
820 catch (ClassNotFoundException e
)
822 throw new IllegalArgumentException("not accessible in "
823 + "classloader: " + inter
);
825 if (! Modifier
.isPublic(inter
.getModifiers()))
828 String p
= getPackage(inter
);
829 if (! data
.pack
.equals(p
))
830 throw new IllegalArgumentException("non-public interfaces "
837 data
.pack
= getPackage(inter
);
839 for (int j
= i
-1; j
>= 0; j
--)
840 if (data
.interfaces
[j
] == inter
)
841 throw new IllegalArgumentException("duplicate interface: "
843 Method
[] methods
= inter
.getMethods();
844 int j
= methods
.length
;
847 ProxySignature sig
= new ProxySignature(methods
[j
]);
848 ProxySignature old
= (ProxySignature
) method_set
.put(sig
, sig
);
850 sig
.checkCompatibility(old
);
854 i
= method_set
.size();
855 data
.methods
= new Method
[i
];
856 data
.exceptions
= new Class
[i
][];
857 Iterator itr
= method_set
.values().iterator();
860 ProxySignature sig
= (ProxySignature
) itr
.next();
861 data
.methods
[i
] = sig
.method
;
862 data
.exceptions
[i
] = (Class
[]) sig
.exceptions
863 .toArray(new Class
[sig
.exceptions
.size()]);
870 * Does all the work of building a class. By making this a nested class,
871 * this code is not loaded in memory if the VM has a native
872 * implementation instead.
874 * @author Eric Blake (ebb9@email.byu.edu)
876 private static final class ClassFactory
878 /** Constants for assisting the compilation */
879 private static final byte POOL
= 0;
880 private static final byte FIELD
= 1;
881 private static final byte METHOD
= 2;
882 private static final byte INTERFACE
= 3;
883 private static final String CTOR_SIG
884 = "(Ljava/lang/reflect/InvocationHandler;)V";
885 private static final String INVOKE_SIG
= "(Ljava/lang/Object;"
886 + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
888 /** Bytecodes for insertion in the class definition byte[] */
889 private static final char ACONST_NULL
= 1;
890 private static final char ICONST_0
= 3;
891 private static final char BIPUSH
= 16;
892 private static final char SIPUSH
= 17;
893 private static final char ILOAD
= 21;
894 private static final char ILOAD_0
= 26;
895 private static final char ALOAD_0
= 42;
896 private static final char ALOAD_1
= 43;
897 private static final char AALOAD
= 50;
898 private static final char AASTORE
= 83;
899 private static final char DUP
= 89;
900 private static final char DUP_X1
= 90;
901 private static final char SWAP
= 95;
902 private static final char IRETURN
= 172;
903 private static final char LRETURN
= 173;
904 private static final char FRETURN
= 174;
905 private static final char DRETURN
= 175;
906 private static final char ARETURN
= 176;
907 private static final char RETURN
= 177;
908 private static final char GETSTATIC
= 178;
909 private static final char GETFIELD
= 180;
910 private static final char INVOKEVIRTUAL
= 182;
911 private static final char INVOKESPECIAL
= 183;
912 private static final char INVOKESTATIC
= 184;
913 private static final char INVOKEINTERFACE
= 185;
914 private static final char NEW
= 187;
915 private static final char ANEWARRAY
= 189;
916 private static final char ATHROW
= 191;
917 private static final char CHECKCAST
= 192;
919 // Implementation note: we use StringBuffers to hold the byte data, since
920 // they automatically grow. However, we only use the low 8 bits of
921 // every char in the array, so we are using twice the necessary memory
922 // for the ease StringBuffer provides.
924 /** The constant pool. */
925 private final StringBuffer pool
= new StringBuffer();
926 /** The rest of the class data. */
927 private final StringBuffer stream
= new StringBuffer();
929 /** Map of strings to byte sequences, to minimize size of pool. */
930 private final Map poolEntries
= new HashMap();
932 /** The VM name of this proxy class. */
933 private final String qualName
;
936 * The Method objects the proxy class refers to when calling the
937 * invocation handler.
939 private final Method
[] methods
;
942 * Initializes the buffers with the bytecode contents for a proxy class.
944 * @param data the remainder of the class data
945 * @throws IllegalArgumentException if anything else goes wrong this
946 * late in the game; as far as I can tell, this will only happen
947 * if the constant pool overflows, which is possible even when
948 * the user doesn't exceed the 65535 interface limit
950 ClassFactory(ProxyData data
)
952 methods
= data
.methods
;
954 // magic = 0xcafebabe
956 // major_version = 46
957 // constant_pool_count: place-holder for now
958 pool
.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0");
959 // constant_pool[], filled in as we go
962 putU2(Modifier
.SUPER
| Modifier
.FINAL
| Modifier
.PUBLIC
);
964 qualName
= (data
.pack
+ "$Proxy" + data
.id
);
965 putU2(classInfo(TypeSignature
.getEncodingOfClass(qualName
, false)));
967 putU2(classInfo("java/lang/reflect/Proxy"));
970 putU2(data
.interfaces
.length
);
972 for (int i
= 0; i
< data
.interfaces
.length
; i
++)
973 putU2(classInfo(data
.interfaces
[i
]));
975 // Recall that Proxy classes serialize specially, so we do not need
976 // to worry about a <clinit> method for this field. Instead, we
977 // just assign it by reflection after the class is successfully loaded.
978 // fields_count - private static Method[] m;
982 putU2(Modifier
.PRIVATE
| Modifier
.STATIC
);
984 putU2(utf8Info("m"));
985 // m.descriptor_index
986 putU2(utf8Info("[Ljava/lang/reflect/Method;"));
987 // m.attributes_count
991 // methods_count - # handler methods, plus <init>
992 putU2(methods
.length
+ 1);
994 // <init>.access_flags
995 putU2(Modifier
.PUBLIC
);
997 putU2(utf8Info("<init>"));
998 // <init>.descriptor_index
999 putU2(utf8Info(CTOR_SIG
));
1000 // <init>.attributes_count - only Code is needed
1002 // <init>.Code.attribute_name_index
1003 putU2(utf8Info("Code"));
1004 // <init>.Code.attribute_length = 18
1005 // <init>.Code.info:
1006 // $Proxynn(InvocationHandler h) { super(h); }
1007 // <init>.Code.max_stack = 2
1008 // <init>.Code.max_locals = 2
1009 // <init>.Code.code_length = 6
1010 // <init>.Code.code[]
1011 stream
.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0
+ ALOAD_1
1013 putU2(refInfo(METHOD
, "java/lang/reflect/Proxy", "<init>", CTOR_SIG
));
1014 // <init>.Code.exception_table_length = 0
1015 // <init>.Code.exception_table[]
1016 // <init>.Code.attributes_count = 0
1017 // <init>.Code.attributes[]
1018 stream
.append(RETURN
+ "\0\0\0\0");
1020 for (int i
= methods
.length
- 1; i
>= 0; i
--)
1021 emitMethod(i
, data
.exceptions
[i
]);
1025 // attributes[] - empty; omit SourceFile attribute
1026 // XXX should we mark this with a Synthetic attribute?
1030 * Produce the bytecode for a single method.
1032 * @param i the index of the method we are building
1033 * @param e the exceptions possible for the method
1035 private void emitMethod(int i
, Class
[] e
)
1037 // First, we precalculate the method length and other information.
1039 Method m
= methods
[i
];
1040 Class
[] paramtypes
= m
.getParameterTypes();
1041 int wrap_overhead
= 0; // max words taken by wrapped primitive
1042 int param_count
= 1; // 1 for this
1043 int code_length
= 16; // aload_0, getfield, aload_0, getstatic, const,
1044 // aaload, const/aconst_null, invokeinterface
1047 if (i
> Byte
.MAX_VALUE
)
1048 code_length
+= 2; // sipush
1050 code_length
++; // bipush
1052 if (paramtypes
.length
> 0)
1054 code_length
+= 3; // anewarray
1055 if (paramtypes
.length
> Byte
.MAX_VALUE
)
1056 code_length
+= 2; // sipush
1057 else if (paramtypes
.length
> 5)
1058 code_length
++; // bipush
1059 for (int j
= 0; j
< paramtypes
.length
; j
++)
1061 code_length
+= 4; // dup, const, load, store
1062 Class type
= paramtypes
[j
];
1065 if (j
> Byte
.MAX_VALUE
)
1066 code_length
+= 2; // sipush
1068 code_length
++; // bipush
1070 if (param_count
>= 4)
1071 code_length
++; // 2-byte load
1073 if (type
.isPrimitive())
1075 code_length
+= 7; // new, dup, invokespecial
1076 if (type
== long.class || type
== double.class)
1081 else if (wrap_overhead
< 2)
1086 int end_pc
= code_length
;
1087 Class ret_type
= m
.getReturnType();
1088 if (ret_type
== void.class)
1089 code_length
++; // return
1090 else if (ret_type
.isPrimitive())
1091 code_length
+= 7; // cast, invokevirtual, return
1093 code_length
+= 4; // cast, return
1094 int exception_count
= 0;
1095 boolean throws_throwable
= false;
1096 for (int j
= 0; j
< e
.length
; j
++)
1097 if (e
[j
] == Throwable
.class)
1099 throws_throwable
= true;
1102 if (! throws_throwable
)
1104 exception_count
= e
.length
+ 3; // Throwable, Error, RuntimeException
1105 code_length
+= 9; // new, dup_x1, swap, invokespecial, athrow
1107 int handler_pc
= code_length
- 1;
1108 StringBuffer signature
= new StringBuffer("(");
1109 for (int j
= 0; j
< paramtypes
.length
; j
++)
1110 signature
.append(TypeSignature
.getEncodingOfClass(paramtypes
[j
]));
1111 signature
.append(")").append(TypeSignature
.getEncodingOfClass(ret_type
));
1113 // Now we have enough information to emit the method.
1115 // handler.access_flags
1116 putU2(Modifier
.PUBLIC
| Modifier
.FINAL
);
1117 // handler.name_index
1118 putU2(utf8Info(m
.getName()));
1119 // handler.descriptor_index
1120 putU2(utf8Info(signature
.toString()));
1121 // handler.attributes_count - Code is necessary, Exceptions possible
1122 putU2(e
.length
> 0 ?
2 : 1);
1124 // handler.Code.info:
1125 // type name(args) {
1127 // return (type) h.invoke(this, methods[i], new Object[] {args});
1128 // } catch (<declared Exceptions> e) {
1130 // } catch (Throwable t) {
1131 // throw new UndeclaredThrowableException(t);
1135 // if arg_n is primitive, wrap it
1136 // if method throws Throwable, try-catch is not needed
1137 // if method returns void, return statement not needed
1138 // if method returns primitive, unwrap it
1139 // save space by sharing code for all the declared handlers
1141 // handler.Code.attribute_name_index
1142 putU2(utf8Info("Code"));
1143 // handler.Code.attribute_length
1144 putU4(12 + code_length
+ 8 * exception_count
);
1145 // handler.Code.max_stack
1146 putU2(param_count
== 1 ?
4 : 7 + wrap_overhead
);
1147 // handler.Code.max_locals
1149 // handler.Code.code_length
1151 // handler.Code.code[]
1154 putU2(refInfo(FIELD
, "java/lang/reflect/Proxy", "h",
1155 "Ljava/lang/reflect/InvocationHandler;"));
1158 putU2(refInfo(FIELD
, TypeSignature
.getEncodingOfClass(qualName
, false),
1159 "m", "[Ljava/lang/reflect/Method;"));
1162 if (paramtypes
.length
> 0)
1164 putConst(paramtypes
.length
);
1166 putU2(classInfo("java/lang/Object"));
1168 for (int j
= 0; j
< paramtypes
.length
; j
++, param_count
++)
1172 if (paramtypes
[j
].isPrimitive())
1175 putU2(classInfo(wrapper(paramtypes
[j
])));
1178 putLoad(param_count
, paramtypes
[j
]);
1179 if (paramtypes
[j
].isPrimitive())
1181 putU1(INVOKESPECIAL
);
1182 putU2(refInfo(METHOD
, wrapper(paramtypes
[j
]), "<init>",
1183 '(' + (TypeSignature
1184 .getEncodingOfClass(paramtypes
[j
])
1186 if (paramtypes
[j
] == long.class
1187 || paramtypes
[j
] == double.class)
1195 putU1(INVOKEINTERFACE
);
1196 putU2(refInfo(INTERFACE
, "java/lang/reflect/InvocationHandler",
1197 "invoke", INVOKE_SIG
));
1198 putU1(4); // InvocationHandler, this, Method, Object[]
1200 if (ret_type
== void.class)
1202 else if (ret_type
.isPrimitive())
1205 putU2(classInfo(wrapper(ret_type
)));
1206 putU1(INVOKEVIRTUAL
);
1207 putU2(refInfo(METHOD
, wrapper(ret_type
),
1208 ret_type
.getName() + "Value",
1209 "()" + TypeSignature
.getEncodingOfClass(ret_type
)));
1210 if (ret_type
== long.class)
1212 else if (ret_type
== float.class)
1214 else if (ret_type
== double.class)
1222 putU2(classInfo(ret_type
));
1225 if (! throws_throwable
)
1228 putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
1231 putU1(INVOKESPECIAL
);
1232 putU2(refInfo(METHOD
,
1233 "java/lang/reflect/UndeclaredThrowableException",
1234 "<init>", "(Ljava/lang/Throwable;)V"));
1238 // handler.Code.exception_table_length
1239 putU2(exception_count
);
1240 // handler.Code.exception_table[]
1241 if (! throws_throwable
)
1243 // handler.Code.exception_table.start_pc
1245 // handler.Code.exception_table.end_pc
1247 // handler.Code.exception_table.handler_pc
1249 // handler.Code.exception_table.catch_type
1250 putU2(classInfo("java/lang/Error"));
1251 // handler.Code.exception_table.start_pc
1253 // handler.Code.exception_table.end_pc
1255 // handler.Code.exception_table.handler_pc
1257 // handler.Code.exception_table.catch_type
1258 putU2(classInfo("java/lang/RuntimeException"));
1259 for (int j
= 0; j
< e
.length
; j
++)
1261 // handler.Code.exception_table.start_pc
1263 // handler.Code.exception_table.end_pc
1265 // handler.Code.exception_table.handler_pc
1267 // handler.Code.exception_table.catch_type
1268 putU2(classInfo(e
[j
]));
1270 // handler.Code.exception_table.start_pc
1272 // handler.Code.exception_table.end_pc
1274 // handler.Code.exception_table.handler_pc -
1275 // -8 for undeclared handler, which falls thru to normal one
1276 putU2(handler_pc
- 8);
1277 // handler.Code.exception_table.catch_type
1280 // handler.Code.attributes_count
1282 // handler.Code.attributes[]
1286 // handler.Exceptions.attribute_name_index
1287 putU2(utf8Info("Exceptions"));
1288 // handler.Exceptions.attribute_length
1289 putU4(2 * e
.length
+ 2);
1290 // handler.Exceptions.number_of_exceptions
1292 // handler.Exceptions.exception_index_table[]
1293 for (int j
= 0; j
< e
.length
; j
++)
1294 putU2(classInfo(e
[j
]));
1299 * Creates the Class object that corresponds to the bytecode buffers
1300 * built when this object was constructed.
1302 * @param loader the class loader to define the proxy class in; null
1303 * implies the bootstrap class loader
1304 * @return the proxy class Class object
1306 Class
generate(ClassLoader loader
)
1308 byte[] bytecode
= new byte[pool
.length() + stream
.length()];
1309 // More efficient to bypass calling charAt() repetitively.
1310 char[] c
= pool
.toString().toCharArray();
1313 bytecode
[i
] = (byte) c
[i
];
1314 c
= stream
.toString().toCharArray();
1316 int j
= bytecode
.length
;
1318 bytecode
[--j
] = (byte) c
[--i
];
1320 // Patch the constant pool size, which we left at 0 earlier.
1321 int count
= poolEntries
.size() + 1;
1322 bytecode
[8] = (byte) (count
>> 8);
1323 bytecode
[9] = (byte) count
;
1327 Class vmClassLoader
= Class
.forName("java.lang.VMClassLoader");
1328 Class
[] types
= {ClassLoader
.class, String
.class,
1329 byte[].class, int.class, int.class,
1330 ProtectionDomain
.class };
1331 Method m
= vmClassLoader
.getDeclaredMethod("defineClass", types
);
1332 // We can bypass the security check of setAccessible(true), since
1333 // we're in the same package.
1336 Object
[] args
= {loader
, qualName
, bytecode
, new Integer(0),
1337 new Integer(bytecode
.length
),
1338 Object
.class.getProtectionDomain() };
1339 Class clazz
= (Class
) m
.invoke(null, args
);
1341 // Finally, initialize the m field of the proxy class, before
1343 Field f
= clazz
.getDeclaredField("m");
1345 // we can share the array, because it is not publicized
1346 f
.set(null, methods
);
1353 throw (Error
) new InternalError("Unexpected: " + e
).initCause(e
);
1358 * Put a single byte on the stream.
1360 * @param i the information to add (only lowest 8 bits are used)
1362 private void putU1(int i
)
1364 stream
.append((char) i
);
1368 * Put two bytes on the stream.
1370 * @param i the information to add (only lowest 16 bits are used)
1372 private void putU2(int i
)
1374 stream
.append((char) (i
>> 8)).append((char) i
);
1378 * Put four bytes on the stream.
1380 * @param i the information to add (treated as unsigned)
1382 private void putU4(int i
)
1384 stream
.append((char) (i
>> 24)).append((char) (i
>> 16));
1385 stream
.append((char) (i
>> 8)).append((char) i
);
1389 * Put bytecode to load a constant integer on the stream. This only
1390 * needs to work for values less than Short.MAX_VALUE.
1392 * @param i the int to add
1394 private void putConst(int i
)
1396 if (i
>= -1 && i
<= 5)
1397 putU1(ICONST_0
+ i
);
1398 else if (i
>= Byte
.MIN_VALUE
&& i
<= Byte
.MAX_VALUE
)
1411 * Put bytecode to load a given local variable on the stream.
1413 * @param i the slot to load
1414 * @param type the base type of the load
1416 private void putLoad(int i
, Class type
)
1419 if (type
== long.class)
1421 else if (type
== float.class)
1423 else if (type
== double.class)
1425 else if (! type
.isPrimitive())
1428 putU1(ILOAD_0
+ 4 * offset
+ i
);
1431 putU1(ILOAD
+ offset
);
1437 * Given a primitive type, return its wrapper class name.
1439 * @param clazz the primitive type (but not void.class)
1440 * @return the internal form of the wrapper class name
1442 private String
wrapper(Class clazz
)
1444 if (clazz
== boolean.class)
1445 return "java/lang/Boolean";
1446 if (clazz
== byte.class)
1447 return "java/lang/Byte";
1448 if (clazz
== short.class)
1449 return "java/lang/Short";
1450 if (clazz
== char.class)
1451 return "java/lang/Character";
1452 if (clazz
== int.class)
1453 return "java/lang/Integer";
1454 if (clazz
== long.class)
1455 return "java/lang/Long";
1456 if (clazz
== float.class)
1457 return "java/lang/Float";
1458 if (clazz
== double.class)
1459 return "java/lang/Double";
1465 * Returns the entry of this String in the Constant pool, adding it
1468 * @param str the String to resolve
1469 * @return the index of the String in the constant pool
1471 private char utf8Info(String str
)
1473 String utf8
= toUtf8(str
);
1474 int len
= utf8
.length();
1475 return poolIndex("\1" + (char) (len
>> 8) + (char) (len
& 0xff) + utf8
);
1479 * Returns the entry of the appropriate class info structure in the
1480 * Constant pool, adding it if necessary.
1482 * @param name the class name, in internal form
1483 * @return the index of the ClassInfo in the constant pool
1485 private char classInfo(String name
)
1487 char index
= utf8Info(name
);
1488 char[] c
= {7, (char) (index
>> 8), (char) (index
& 0xff)};
1489 return poolIndex(new String(c
));
1493 * Returns the entry of the appropriate class info structure in the
1494 * Constant pool, adding it if necessary.
1496 * @param clazz the class type
1497 * @return the index of the ClassInfo in the constant pool
1499 private char classInfo(Class clazz
)
1501 return classInfo(TypeSignature
.getEncodingOfClass(clazz
.getName(),
1506 * Returns the entry of the appropriate fieldref, methodref, or
1507 * interfacemethodref info structure in the Constant pool, adding it
1510 * @param structure FIELD, METHOD, or INTERFACE
1511 * @param clazz the class name, in internal form
1512 * @param name the simple reference name
1513 * @param type the type of the reference
1514 * @return the index of the appropriate Info structure in the constant pool
1516 private char refInfo(byte structure
, String clazz
, String name
,
1519 char cindex
= classInfo(clazz
);
1520 char ntindex
= nameAndTypeInfo(name
, type
);
1521 // relies on FIELD == 1, METHOD == 2, INTERFACE == 3
1522 char[] c
= {(char) (structure
+ 8),
1523 (char) (cindex
>> 8), (char) (cindex
& 0xff),
1524 (char) (ntindex
>> 8), (char) (ntindex
& 0xff)};
1525 return poolIndex(new String(c
));
1529 * Returns the entry of the appropriate nameAndTyperef info structure
1530 * in the Constant pool, adding it if necessary.
1532 * @param name the simple name
1533 * @param type the reference type
1534 * @return the index of the NameAndTypeInfo structure in the constant pool
1536 private char nameAndTypeInfo(String name
, String type
)
1538 char nindex
= utf8Info(name
);
1539 char tindex
= utf8Info(type
);
1540 char[] c
= {12, (char) (nindex
>> 8), (char) (nindex
& 0xff),
1541 (char) (tindex
>> 8), (char) (tindex
& 0xff)};
1542 return poolIndex(new String(c
));
1546 * Converts a regular string to a UTF8 string, where the upper byte
1547 * of every char is 0, and '\\u0000' is not in the string. This is
1548 * basically to use a String as a fancy byte[], and while it is less
1549 * efficient in memory use, it is easier for hashing.
1551 * @param str the original, in straight unicode
1552 * @return a modified string, in UTF8 format in the low bytes
1554 private String
toUtf8(String str
)
1556 final char[] ca
= str
.toCharArray();
1557 final int len
= ca
.length
;
1559 // Avoid object creation, if str is already fits UTF8.
1561 for (i
= 0; i
< len
; i
++)
1562 if (ca
[i
] == 0 || ca
[i
] > '\u007f')
1567 final StringBuffer sb
= new StringBuffer(str
);
1569 for ( ; i
< len
; i
++)
1571 final char c
= ca
[i
];
1572 if (c
> 0 && c
<= '\u007f')
1574 else if (c
<= '\u07ff') // includes '\0'
1576 sb
.append((char) (0xc0 | (c
>> 6)));
1577 sb
.append((char) (0x80 | (c
& 0x6f)));
1581 sb
.append((char) (0xe0 | (c
>> 12)));
1582 sb
.append((char) (0x80 | ((c
>> 6) & 0x6f)));
1583 sb
.append((char) (0x80 | (c
& 0x6f)));
1586 return sb
.toString();
1590 * Returns the location of a byte sequence (conveniently wrapped in
1591 * a String with all characters between \u0001 and \u00ff inclusive)
1592 * in the constant pool, adding it if necessary.
1594 * @param sequence the byte sequence to look for
1595 * @return the index of the sequence
1596 * @throws IllegalArgumentException if this would make the constant
1599 private char poolIndex(String sequence
)
1601 Integer i
= (Integer
) poolEntries
.get(sequence
);
1604 // pool starts at index 1
1605 int size
= poolEntries
.size() + 1;
1607 throw new IllegalArgumentException("exceeds VM limitations");
1608 i
= new Integer(size
);
1609 poolEntries
.put(sequence
, i
);
1610 pool
.append(sequence
);
1612 return (char) i
.intValue();
1614 } // class ClassFactory