Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / lang / reflect / Proxy.java
blob3f13254fdc3c7a4b97b782d5d008edddfac1b84e
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)
9 any later version.
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
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
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;
49 import java.util.Map;
50 import java.util.Set;
52 /**
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.
60 * <h3>Creation</h3>
61 * To create a proxy for some interface Foo:
63 * <pre>
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 });
70 * </pre>
71 * or more simply:
72 * <pre>
73 * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
74 * new Class[] { Foo.class },
75 * handler);
76 * </pre>
78 * <h3>Dynamic Proxy Classes</h3>
79 * A dynamic proxy class is created at runtime, and has the following
80 * properties:
81 * <ul>
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
116 * reflection.</li>
117 * </ul>
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:
123 * <ul>
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>
135 * </ul>
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
156 * @see Class
157 * @author Eric Blake (ebb9@email.byu.edu)
158 * @since 1.3
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
180 * proxy classes.
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
186 * Proxy.
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
202 * instance
204 protected Proxy(InvocationHandler handler)
206 if (handler == null && isProxyClass(getClass()))
207 throw new NullPointerException("invalid handler");
208 h = 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>
219 * <ul>
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>
225 * must be true.</li>
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>
242 * </ul>
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
254 * a null entry
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);
264 if (clazz == null)
266 if (Configuration.HAVE_NATIVE_GET_PROXY_CLASS)
267 clazz = getProxyClass0(loader, interfaces);
268 else
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"*/);
284 return clazz;
288 * Combines several methods into one. This is equivalent to:
289 * <pre>
290 * Proxy.getProxyClass(loader, interfaces)
291 * .getConstructor(new Class[] {InvocationHandler.class})
292 * .newInstance(new Object[] {handler});
293 * </pre>
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;
328 throw e;
330 catch (InvocationTargetException e)
332 // Let wrapped NullPointerException escape.
333 // assert e.getTargetException() instanceof NullPointerException
334 throw (NullPointerException) e.getCause();
336 catch (Exception e)
338 // Covers InstantiationException, IllegalAccessException,
339 // NoSuchMethodException, none of which should be generated
340 // if the proxy class was generated correctly.
341 // assert false;
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))
364 return false;
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,
412 Class[] interfaces);
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,
435 Class[] interfaces);
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
448 * VM limitations
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,
456 ProxyData data);
459 * Helper class for mapping unique ClassLoader and interface combinations
460 * to proxy classes.
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)
485 if (loader == null)
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();
502 return hash;
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;
515 each_type:
516 for (int i = arr1.length; --i >= 0; )
518 Class t = arr1[i];
519 for (int j = i; --j >= 0; )
521 if (t == arr1[j])
522 { //found duplicate type
523 continue each_type;
527 // count c(a unique element of arr1)'s
528 // occurrences in arr2
529 int occ_in_arr2 = 0;
530 for (int j = arr2.length; --j >= 0; )
532 if (t == arr2[j])
534 ++occ_in_arr2;
537 if (occ_in_arr2 == 0)
538 { // t does not occur in arr2
539 return false;
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)
558 return false;
559 return sameTypes(interfaces, pt.interfaces);
561 } // class ProxyType
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();
576 static
580 ProxySignature sig
581 = new ProxySignature(Object.class
582 .getMethod("equals",
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);
590 catch (Exception e)
592 // assert false;
593 throw (Error) new InternalError("Unexpected: " + e).initCause(e);
598 * The underlying Method object, never null
600 final Method method;
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();
616 int i = exc.length;
617 while (--i >= 0)
619 // discard unchecked exceptions
620 if (Error.class.isAssignableFrom(exc[i])
621 || RuntimeException.class.isAssignableFrom(exc[i]))
622 continue;
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,
641 // implement it!
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();
647 int pos = size1;
648 while (--pos >= 0)
650 Class c1 = (Class) itr.next();
651 Iterator itr2 = other.exceptions.iterator();
652 int pos2 = size2;
653 while (--pos2 >= 0)
655 Class c2 = (Class) itr2.next();
656 if (c2.isAssignableFrom(c1))
657 valid1[pos] = true;
658 if (c1.isAssignableFrom(c2))
659 valid2[pos2] = true;
662 pos = size1;
663 itr = exceptions.iterator();
664 while (--pos >= 0)
666 itr.next();
667 if (! valid1[pos])
668 itr.remove();
670 pos = size2;
671 itr = other.exceptions.iterator();
672 while (--pos >= 0)
674 itr.next();
675 if (! valid2[pos])
676 itr.remove();
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();
692 return hash;
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)
708 return false;
709 int i = types1.length;
710 while (--i >= 0)
711 if (types1[i] != types2[i])
712 return false;
713 return true;
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.
729 String pack;
732 * The interfaces this class implements. Non-null, but possibly empty.
734 Class[] interfaces;
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).
742 Method[] methods;
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
751 * smaller class.
753 Class[][] exceptions;
756 * For unique id's
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.
768 ProxyData()
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
789 * it is expensive.
791 * @param pt the ProxyType to convert to ProxyData
792 * @return the flattened, verified ProxyData structure for use in
793 * class generation
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
807 // pool overflows
808 int i = data.interfaces.length;
809 while (--i >= 0)
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()))
826 if (in_package)
828 String p = getPackage(inter);
829 if (! data.pack.equals(p))
830 throw new IllegalArgumentException("non-public interfaces "
831 + "from different "
832 + "packages");
834 else
836 in_package = true;
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: "
842 + inter);
843 Method[] methods = inter.getMethods();
844 int j = methods.length;
845 while (--j >= 0)
847 ProxySignature sig = new ProxySignature(methods[j]);
848 ProxySignature old = (ProxySignature) method_set.put(sig, sig);
849 if (old != null)
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();
858 while (--i >= 0)
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()]);
865 return data;
867 } // class ProxyData
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
955 // minor_version = 0
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
961 // access_flags
962 putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC);
963 // this_class
964 qualName = (data.pack + "$Proxy" + data.id);
965 putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false)));
966 // super_class
967 putU2(classInfo("java/lang/reflect/Proxy"));
969 // interfaces_count
970 putU2(data.interfaces.length);
971 // interfaces[]
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;
979 putU2(1);
980 // fields[]
981 // m.access_flags
982 putU2(Modifier.PRIVATE | Modifier.STATIC);
983 // m.name_index
984 putU2(utf8Info("m"));
985 // m.descriptor_index
986 putU2(utf8Info("[Ljava/lang/reflect/Method;"));
987 // m.attributes_count
988 putU2(0);
989 // m.attributes[]
991 // methods_count - # handler methods, plus <init>
992 putU2(methods.length + 1);
993 // methods[]
994 // <init>.access_flags
995 putU2(Modifier.PUBLIC);
996 // <init>.name_index
997 putU2(utf8Info("<init>"));
998 // <init>.descriptor_index
999 putU2(utf8Info(CTOR_SIG));
1000 // <init>.attributes_count - only Code is needed
1001 putU2(1);
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
1012 + INVOKESPECIAL);
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]);
1023 // attributes_count
1024 putU2(0);
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
1045 if (i > 5)
1047 if (i > Byte.MAX_VALUE)
1048 code_length += 2; // sipush
1049 else
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];
1063 if (j > 5)
1065 if (j > Byte.MAX_VALUE)
1066 code_length += 2; // sipush
1067 else
1068 code_length++; // bipush
1070 if (param_count >= 4)
1071 code_length++; // 2-byte load
1072 param_count++;
1073 if (type.isPrimitive())
1075 code_length += 7; // new, dup, invokespecial
1076 if (type == long.class || type == double.class)
1078 wrap_overhead = 3;
1079 param_count++;
1081 else if (wrap_overhead < 2)
1082 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
1092 else
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;
1100 break;
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) {
1126 // try {
1127 // return (type) h.invoke(this, methods[i], new Object[] {args});
1128 // } catch (<declared Exceptions> e) {
1129 // throw e;
1130 // } catch (Throwable t) {
1131 // throw new UndeclaredThrowableException(t);
1132 // }
1133 // }
1134 // Special cases:
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
1148 putU2(param_count);
1149 // handler.Code.code_length
1150 putU4(code_length);
1151 // handler.Code.code[]
1152 putU1(ALOAD_0);
1153 putU1(GETFIELD);
1154 putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h",
1155 "Ljava/lang/reflect/InvocationHandler;"));
1156 putU1(ALOAD_0);
1157 putU1(GETSTATIC);
1158 putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false),
1159 "m", "[Ljava/lang/reflect/Method;"));
1160 putConst(i);
1161 putU1(AALOAD);
1162 if (paramtypes.length > 0)
1164 putConst(paramtypes.length);
1165 putU1(ANEWARRAY);
1166 putU2(classInfo("java/lang/Object"));
1167 param_count = 1;
1168 for (int j = 0; j < paramtypes.length; j++, param_count++)
1170 putU1(DUP);
1171 putConst(j);
1172 if (paramtypes[j].isPrimitive())
1174 putU1(NEW);
1175 putU2(classInfo(wrapper(paramtypes[j])));
1176 putU1(DUP);
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])
1185 + ")V")));
1186 if (paramtypes[j] == long.class
1187 || paramtypes[j] == double.class)
1188 param_count++;
1190 putU1(AASTORE);
1193 else
1194 putU1(ACONST_NULL);
1195 putU1(INVOKEINTERFACE);
1196 putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler",
1197 "invoke", INVOKE_SIG));
1198 putU1(4); // InvocationHandler, this, Method, Object[]
1199 putU1(0);
1200 if (ret_type == void.class)
1201 putU1(RETURN);
1202 else if (ret_type.isPrimitive())
1204 putU1(CHECKCAST);
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)
1211 putU1(LRETURN);
1212 else if (ret_type == float.class)
1213 putU1(FRETURN);
1214 else if (ret_type == double.class)
1215 putU1(DRETURN);
1216 else
1217 putU1(IRETURN);
1219 else
1221 putU1(CHECKCAST);
1222 putU2(classInfo(ret_type));
1223 putU1(ARETURN);
1225 if (! throws_throwable)
1227 putU1(NEW);
1228 putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
1229 putU1(DUP_X1);
1230 putU1(SWAP);
1231 putU1(INVOKESPECIAL);
1232 putU2(refInfo(METHOD,
1233 "java/lang/reflect/UndeclaredThrowableException",
1234 "<init>", "(Ljava/lang/Throwable;)V"));
1235 putU1(ATHROW);
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
1244 putU2(0);
1245 // handler.Code.exception_table.end_pc
1246 putU2(end_pc);
1247 // handler.Code.exception_table.handler_pc
1248 putU2(handler_pc);
1249 // handler.Code.exception_table.catch_type
1250 putU2(classInfo("java/lang/Error"));
1251 // handler.Code.exception_table.start_pc
1252 putU2(0);
1253 // handler.Code.exception_table.end_pc
1254 putU2(end_pc);
1255 // handler.Code.exception_table.handler_pc
1256 putU2(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
1262 putU2(0);
1263 // handler.Code.exception_table.end_pc
1264 putU2(end_pc);
1265 // handler.Code.exception_table.handler_pc
1266 putU2(handler_pc);
1267 // handler.Code.exception_table.catch_type
1268 putU2(classInfo(e[j]));
1270 // handler.Code.exception_table.start_pc
1271 putU2(0);
1272 // handler.Code.exception_table.end_pc
1273 putU2(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
1278 putU2(0);
1280 // handler.Code.attributes_count
1281 putU2(0);
1282 // handler.Code.attributes[]
1284 if (e.length > 0)
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
1291 putU2(e.length);
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();
1311 int i = c.length;
1312 while (--i >= 0)
1313 bytecode[i] = (byte) c[i];
1314 c = stream.toString().toCharArray();
1315 i = c.length;
1316 int j = bytecode.length;
1317 while (i > 0)
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.
1334 m.flag = true;
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
1342 // returning it.
1343 Field f = clazz.getDeclaredField("m");
1344 f.flag = true;
1345 // we can share the array, because it is not publicized
1346 f.set(null, methods);
1348 return clazz;
1350 catch (Exception e)
1352 // assert false;
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)
1400 putU1(BIPUSH);
1401 putU1(i);
1403 else
1405 putU1(SIPUSH);
1406 putU2(i);
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)
1418 int offset = 0;
1419 if (type == long.class)
1420 offset = 1;
1421 else if (type == float.class)
1422 offset = 2;
1423 else if (type == double.class)
1424 offset = 3;
1425 else if (! type.isPrimitive())
1426 offset = 4;
1427 if (i < 4)
1428 putU1(ILOAD_0 + 4 * offset + i);
1429 else
1431 putU1(ILOAD + offset);
1432 putU1(i);
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";
1460 // assert false;
1461 return null;
1465 * Returns the entry of this String in the Constant pool, adding it
1466 * if necessary.
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(),
1502 false));
1506 * Returns the entry of the appropriate fieldref, methodref, or
1507 * interfacemethodref info structure in the Constant pool, adding it
1508 * if necessary.
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,
1517 String type)
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.
1560 int i;
1561 for (i = 0; i < len; i++)
1562 if (ca[i] == 0 || ca[i] > '\u007f')
1563 break;
1564 if (i == len)
1565 return str;
1567 final StringBuffer sb = new StringBuffer(str);
1568 sb.setLength(i);
1569 for ( ; i < len; i++)
1571 final char c = ca[i];
1572 if (c > 0 && c <= '\u007f')
1573 sb.append(c);
1574 else if (c <= '\u07ff') // includes '\0'
1576 sb.append((char) (0xc0 | (c >> 6)));
1577 sb.append((char) (0x80 | (c & 0x6f)));
1579 else
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
1597 * pool overflow
1599 private char poolIndex(String sequence)
1601 Integer i = (Integer) poolEntries.get(sequence);
1602 if (i == null)
1604 // pool starts at index 1
1605 int size = poolEntries.size() + 1;
1606 if (size >= 65535)
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