From d3cc3f10dac7b93c7b62d7e30b15f171fec3e8a5 Mon Sep 17 00:00:00 2001
From: Anthony Green <green@redhat.com>
Date: Mon, 30 Sep 2002 05:19:09 +0000
Subject: Add Proxy support.

From-SVN: r57635
---
 libjava/java/lang/VMClassLoader.java               |    8 +-
 libjava/java/lang/natClassLoader.cc                |   11 +
 libjava/java/lang/reflect/InvocationHandler.java   |  136 ++
 libjava/java/lang/reflect/Proxy.java               | 1586 ++++++++++++++++++++
 .../lang/reflect/UndeclaredThrowableException.java |  128 ++
 libjava/java/lang/reflect/natProxy.cc              |   38 +
 6 files changed, 1903 insertions(+), 4 deletions(-)
 create mode 100644 libjava/java/lang/reflect/InvocationHandler.java
 create mode 100644 libjava/java/lang/reflect/Proxy.java
 create mode 100644 libjava/java/lang/reflect/UndeclaredThrowableException.java
 create mode 100644 libjava/java/lang/reflect/natProxy.cc

(limited to 'libjava/java/lang')

diff --git a/libjava/java/lang/VMClassLoader.java b/libjava/java/lang/VMClassLoader.java
index 445272b..11b7554 100644
--- a/libjava/java/lang/VMClassLoader.java
+++ b/libjava/java/lang/VMClassLoader.java
@@ -43,10 +43,10 @@ class VMClassLoader {
      * @return the class that was defined.
      * @exception ClassFormatError if the byte array is not in proper classfile format.
      */
-  // Not yet needed for libgcj.
-//      final static native Class defineClass(ClassLoader cl, String name, 
-//  	     byte[] data, int offset, int len) throws ClassFormatError;
-    
+    final static native Class defineClass(ClassLoader cl, String name, 
+					  byte[] data, int offset, int len) 
+	throws ClassFormatError;
+
     /** 
      * Helper to resolve all references to other classes from this class.
      * @param c the class to resolve.
diff --git a/libjava/java/lang/natClassLoader.cc b/libjava/java/lang/natClassLoader.cc
index d33614d..c229943 100644
--- a/libjava/java/lang/natClassLoader.cc
+++ b/libjava/java/lang/natClassLoader.cc
@@ -171,6 +171,16 @@ java::lang::ClassLoader::markClassErrorState0 (java::lang::Class *klass)
 }
 
 jclass
+java::lang::VMClassLoader::defineClass (java::lang::ClassLoader *cl, 
+					jstring name,
+					jbyteArray data, 
+					jint offset,
+					jint length)
+{
+  return cl->defineClass (name, data, offset, length);
+}
+
+jclass
 java::lang::VMClassLoader::getPrimitiveClass (jchar type)
 {
   char sig[2];
@@ -269,6 +279,7 @@ _Jv_PrepareCompiledClass (jclass klass)
       else if (pool->tags[index] == JV_CONSTANT_String)
 	{
 	  jstring str;
+
 	  str = _Jv_NewStringUtf8Const (pool->data[index].utf8);
 	  pool->data[index].o = str;
 	  pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
diff --git a/libjava/java/lang/reflect/InvocationHandler.java b/libjava/java/lang/reflect/InvocationHandler.java
new file mode 100644
index 0000000..91907e2
--- /dev/null
+++ b/libjava/java/lang/reflect/InvocationHandler.java
@@ -0,0 +1,136 @@
+/* java.lang.reflect.InvocationHandler - dynamically executes methods in
+   proxy instances
+   Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang.reflect;
+
+/**
+ * This interface defines an invocation handler.  Suppose you are using
+ * reflection, and found a method that requires that its parameter
+ * be an object of a given interface.  You want to call this method,
+ * but have no idea what classes implement that interface.  So, you can
+ * create a {@link Proxy} instance, a convenient way to dynamically
+ * generate a class that meets all the necessary properties of that
+ * interface.  But in order for the proxy instance to do any good, it
+ * needs to know what to do when interface methods are invoked!  So,
+ * this interface is basically a cool wrapper that provides runtime
+ * code generation needed by proxy instances.<p>
+ *
+ * While this interface was designed for use by Proxy, it will also
+ * work on any object in general.<p>
+ *
+ * Hints for implementing this class:<br>
+ * <ul>
+ *  <li>Don't forget that Object.equals, Object.hashCode, and
+ *      Object.toString will call this handler.  In particular,
+ *      a naive call to proxy.equals, proxy.hashCode, or proxy.toString
+ *      will put you in an infinite loop.  And remember that string
+ *      concatenation also invokes toString.</li>
+ *  <li>Obey the contract of the Method object you are handling, or
+ *      the proxy instance will be forced to throw a
+ *      {@link NullPointerException}, {@link ClassCastException},
+ *      or {@link UndeclaredThrowableException}.</li>
+ *  <li>Be prepared to wrap/unwrap primitives as necessary.</li>
+ *  <li>The Method object may be owned by a different interface than
+ *      what was actually used as the qualifying type of the method
+ *      invocation in the Java source code. This means that it might
+ *      not always be safe to throw an exception listed as belonging
+ *      to the method's throws clause.</li>
+ * </ul>
+ *
+ * <p><small>For a fun time, create an InvocationHandler that handles the
+ * methods of a proxy instance of the InvocationHandler interface!</small>
+ *
+ * @see Proxy
+ * @see UndeclaredThrowableException
+ *
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @since 1.3
+ * @status updated to 1.4
+ */
+public interface InvocationHandler
+{
+  /**
+   * When a method is invoked on a proxy instance, it is wrapped and
+   * this method is called instead, so that you may decide at runtime
+   * how the original method should behave.
+   *
+   * @param proxy the instance that the wrapped method should be
+   *        invoked on.  When this method is called by a Proxy object,
+   *        `proxy' will be an instance of {@link Proxy}, and oddly enough,
+   *        <code>Proxy.getInvocationHandler(proxy)</code> will return
+   *        <code>this</code>!
+   * @param method the reflected method to invoke on the proxy.
+   *        When this method is called by a Proxy object, 'method'
+   *        will be the reflection object owned by the declaring
+   *        class or interface, which may be a supertype of the
+   *        interfaces the proxy directly implements.
+   * @param args the arguments passed to the original method, or
+   *        <code>null</code> if the method takes no arguments.
+   *        (But also be prepared to handle a 0-length array).
+   *        Arguments of primitive type, such as <code>boolean</code>
+   *        or <code>int</code>, are wrapped in the appropriate
+   *        class such as {@link Boolean} or {@link Integer}.
+   * @return whatever is necessary to return from the wrapped method.
+   *         If the wrapped method is <code>void</code>, the proxy
+   *         instance will ignore it.  If the wrapped method returns
+   *         a primitive, this must be the correct wrapper type whose value
+   *         is exactly assignable to the appropriate type (no widening
+   *         will be performed); a null object in this case causes a
+   *         {@link NullPointerException}.  In all remaining cases, if
+   *         the returned object is not assignment compatible to the
+   *         declared type of the original method, the proxy instance
+   *         will generate a {@link ClassCastException}.
+   * @throws Throwable this interface is listed as throwing anything,
+   *         but the implementation should only throw unchecked
+   *         exceptions and exceptions listed in the throws clause of
+   *         all methods being overridden by the proxy instance.  If
+   *         something is thrown that is not compatible with the throws
+   *         clause of all overridden methods, the proxy instance will
+   *         wrap the exception in an UndeclaredThrowableException.
+   *         Note that an exception listed in the throws clause of the
+   *         `method' parameter might not be declared in additional
+   *         interfaces also implemented by the proxy object.
+   *
+   * @see Proxy
+   * @see UndeclaredThrowableException
+   */
+  Object invoke(Object proxy, Method method, Object[] args)
+    throws Throwable;
+
+}
diff --git a/libjava/java/lang/reflect/Proxy.java b/libjava/java/lang/reflect/Proxy.java
new file mode 100644
index 0000000..972ac19
--- /dev/null
+++ b/libjava/java/lang/reflect/Proxy.java
@@ -0,0 +1,1586 @@
+/* Proxy.java -- build a proxy class that implements reflected interfaces
+   Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang.reflect;
+
+import java.io.Serializable;
+import java.security.ProtectionDomain;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import gnu.classpath.Configuration;
+import gnu.java.lang.reflect.TypeSignature;
+
+/**
+ * This class allows you to dynamically create an instance of any (or
+ * even multiple) interfaces by reflection, and decide at runtime
+ * how that instance will behave by giving it an appropriate
+ * {@link InvocationHandler}.  Proxy classes serialize specially, so
+ * that the proxy object can be reused between VMs, without requiring
+ * a persistent copy of the generated class code.
+ *
+ * <h3>Creation</h3>
+ * To create a proxy for some interface Foo:
+ *
+ * <pre>
+ *   InvocationHandler handler = new MyInvocationHandler(...);
+ *   Class proxyClass = Proxy.getProxyClass(
+ *       Foo.class.getClassLoader(), new Class[] { Foo.class });
+ *   Foo f = (Foo) proxyClass
+ *       .getConstructor(new Class[] { InvocationHandler.class })
+ *       .newInstance(new Object[] { handler });
+ * </pre>
+ * or more simply:
+ * <pre>
+ *   Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
+ *                                        new Class[] { Foo.class },
+ *                                        handler);
+ * </pre>
+ *
+ * <h3>Dynamic Proxy Classes</h3>
+ * A dynamic proxy class is created at runtime, and has the following
+ * properties:
+ * <ul>
+ *  <li>The class is <code>public</code> and <code>final</code>,
+ *      and is neither <code>abstract</code> nor an inner class.</li>
+ *  <li>The class has no canonical name (there is no formula you can use
+ *      to determine or generate its name), but begins with the
+ *      sequence "$Proxy".  Abuse this knowledge at your own peril.
+ *      (For now, '$' in user identifiers is legal, but it may not
+ *      be that way forever. You weren't using '$' in your
+ *      identifiers, were you?)</li>
+ *  <li>The class extends Proxy, and explicitly implements all the
+ *      interfaces specified at creation, in order (this is important
+ *      for determining how method invocation is resolved).  Note that
+ *      a proxy class implements {@link Serializable}, at least
+ *      implicitly, since Proxy does, but true serial behavior
+ *      depends on using a serializable invocation handler as well.</li>
+ *  <li>If at least one interface is non-public, the proxy class
+ *      will be in the same package.  Otherwise, the package is
+ *      unspecified.  This will work even if the package is sealed
+ *      from user-generated classes, because Proxy classes are
+ *      generated by a trusted source.  Meanwhile, the proxy class
+ *      belongs to the classloader you designated.</li>
+ *  <li>Reflection works as expected: {@link Class#getInterfaces()} and
+ *      {@link Class#getMethods()} work as they do on normal classes.</li>
+ *  <li>The method {@link #isProxyClass()} will distinguish between
+ *      true proxy classes and user extensions of this class.  It only
+ *      returns true for classes created by {@link #getProxyClass}.</li>
+ *  <li>The {@link ProtectionDomain} of a proxy class is the same as for
+ *      bootstrap classes, such as Object or Proxy, since it is created by
+ *      a trusted source.  This protection domain will typically be granted
+ *      {@link java.security.AllPermission}. But this is not a security
+ *      risk, since there are adequate permissions on reflection, which is
+ *      the only way to create an instance of the proxy class.</li>
+ *  <li>The proxy class contains a single constructor, which takes as
+ *      its only argument an {@link InvocationHandler}.  The method
+ *      {@link #newInstance} is shorthand to do the necessary
+ *      reflection.</li>
+ * </ul>
+ *
+ * <h3>Proxy Instances</h3>
+ * A proxy instance is an instance of a proxy class.  It has the
+ * following properties, many of which follow from the properties of a
+ * proxy class listed above:
+ * <ul>
+ *  <li>For a proxy class with Foo listed as one of its interfaces, the
+ *      expression <code>proxy instanceof Foo</code> will return true,
+ *      and the expression <code>(Foo) proxy</code> will succeed without
+ *      a {@link ClassCastException}.</li>
+ *  <li>Each proxy instance has an invocation handler, which can be
+ *      accessed by {@link #getInvocationHandler(Object)}.  Any call
+ *      to an interface method, including {@link Object#hashcode()},
+ *      {@link Object#equals(Object)}, or {@link Object#toString()},
+ *      but excluding the public final methods of Object, will be
+ *      encoded and passed to the {@link InvocationHandler#invoke}
+ *      method of this handler.</li>
+ * </ul>
+ *
+ * <h3>Inheritance Issues</h3>
+ * A proxy class may inherit a method from more than one interface.
+ * The order in which interfaces are listed matters, because it determines
+ * which reflected {@link Method} object will be passed to the invocation
+ * handler.  This means that the dynamically generated class cannot
+ * determine through which interface a method is being invoked.<p>
+ *
+ * In short, if a method is declared in Object (namely, hashCode,
+ * equals, or toString), then Object will be used; otherwise, the
+ * leftmost interface that inherits or declares a method will be used,
+ * even if it has a more permissive throws clause than what the proxy
+ * class is allowed. Thus, in the invocation handler, it is not always
+ * safe to assume that every class listed in the throws clause of the
+ * passed Method object can safely be thrown; fortunately, the Proxy
+ * instance is robust enough to wrap all illegal checked exceptions in
+ * {@link UndeclaredThrowableException}.
+ *
+ * @see InvocationHandler
+ * @see UndeclaredThrowableException
+ * @see Class
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @since 1.3
+ * @status updated to 1.4, except for the use of ProtectionDomain
+ */
+public class Proxy implements Serializable
+{
+  /**
+   * Compatible with JDK 1.3+.
+   */
+  private static final long serialVersionUID = -2222568056686623797L;
+
+  /**
+   * Map of ProxyType to proxy class.
+   *
+   * @XXX This prevents proxy classes from being garbage collected.
+   * java.util.WeakHashSet is not appropriate, because that collects the
+   * keys, but we are interested in collecting the elements.
+   */
+  private static final Map proxyClasses = new HashMap();
+
+  /**
+   * The invocation handler for this proxy instance.  For Proxy, this
+   * field is unused, but it appears here in order to be serialized in all
+   * proxy classes.
+   *
+   * <em>NOTE</em>: This implementation is more secure for proxy classes
+   * than what Sun specifies. Sun does not require h to be immutable, but
+   * this means you could change h after the fact by reflection.  However,
+   * by making h immutable, we may break non-proxy classes which extend
+   * Proxy.
+   * @serial invocation handler associated with this proxy instance
+   */
+  protected final InvocationHandler h;
+
+  /**
+   * Constructs a new Proxy from a subclass (usually a proxy class),
+   * with the specified invocation handler.
+   *
+   * <em>NOTE</em>: This throws a NullPointerException if you attempt
+   * to create a proxy instance with a null handler using reflection.
+   * This behavior is not yet specified by Sun; see Sun Bug 4487672.
+   *
+   * @param handler the invocation handler, may be null if the subclass
+   *        is not a proxy class
+   * @throws NullPointerException if handler is null and this is a proxy
+   *         instance
+   */
+  protected Proxy(InvocationHandler handler)
+  {
+    if (handler == null && isProxyClass(getClass()))
+      throw new NullPointerException("invalid handler");
+    h = handler;
+  }
+
+  /**
+   * Returns the proxy {@link Class} for the given ClassLoader and array
+   * of interfaces, dynamically generating it if necessary.
+   *
+   * There are several restrictions on this method, the violation of
+   * which will result in an IllegalArgumentException or
+   * NullPointerException:
+   * <ul>
+   *  <li>All objects in `interfaces' must represent distinct interfaces.
+   *      Classes, primitive types, null, and duplicates are forbidden.</li>
+   *  <li>The interfaces must be visible in the specified ClassLoader.
+   *      In other words, for each interface i:
+   *      <code>Class.forName(i.getName(), false, loader) == i</code>
+   *      must be true.</li>
+   *  <li>All non-public interfaces (if any) must reside in the same
+   *      package, or the proxy class would be non-instantiable.  If
+   *      there are no non-public interfaces, the package of the proxy
+   *      class is unspecified.</li>
+   *  <li>All interfaces must be compatible - if two declare a method
+   *      with the same name and parameters, the return type must be
+   *      the same and the throws clause of the proxy class will be
+   *      the maximal subset of subclasses of the throws clauses for
+   *      each method that is overridden.</li>
+   *  <li>VM constraints limit the number of interfaces a proxy class
+   *      may directly implement (however, the indirect inheritance
+   *      of {@link Serializable} does not count against this limit).
+   *      Even though most VMs can theoretically have 65535
+   *      superinterfaces for a class, the actual limit is smaller
+   *      because a class's constant pool is limited to 65535 entries,
+   *      and not all entries can be interfaces.</li>
+   * </ul><p>
+   *
+   * Note that different orders of interfaces produce distinct classes.
+   *
+   * @param loader the class loader to define the proxy class in; null
+   *        implies the bootstrap class loader
+   * @param interfaces the array of interfaces the proxy class implements,
+   *        may be empty, but not null
+   * @return the Class object of the proxy class
+   * @throws IllegalArgumentException if the constraints above were
+   *         violated, except for problems with null
+   * @throws NullPointerException if `interfaces' is null or contains
+   *         a null entry
+   */
+  // synchronized so that we aren't trying to build the same class
+  // simultaneously in two threads
+  public static synchronized Class getProxyClass(ClassLoader loader,
+                                                 Class[] interfaces)
+  {
+    interfaces = (Class[]) interfaces.clone();
+    ProxyType pt = new ProxyType(loader, interfaces);
+    Class clazz = (Class) proxyClasses.get(pt);
+    if (clazz == null)
+      {
+        if (Configuration.HAVE_NATIVE_GET_PROXY_CLASS)
+          clazz = getProxyClass0(loader, interfaces);
+        else
+          {
+            ProxyData data = (Configuration.HAVE_NATIVE_GET_PROXY_DATA
+                              ? getProxyData0(loader, interfaces)
+                              : ProxyData.getProxyData(pt));
+
+            // FIXME workaround for bug in gcj 3.0.x
+            // Not needed with the latest gcj from cvs
+            //clazz = (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS
+            //	       ? generateProxyClass0(loader, data)
+            //         : new ClassFactory(data).generate(loader));
+            if (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS)
+              clazz = generateProxyClass0(loader, data);
+            else
+              {
+                ClassFactory cf = new ClassFactory(data);
+                clazz = cf.generate(loader);
+              }
+          }
+
+        Object check = proxyClasses.put(pt, clazz);
+        // assert check == null && clazz != null;
+        if (check != null || clazz == null)
+          throw new InternalError(/*"Fatal flaw in getProxyClass"*/);
+      }
+    return clazz;
+  }
+
+  /**
+   * Combines several methods into one.  This is equivalent to:
+   * <pre>
+   *   Proxy.getProxyClass(loader, interfaces)
+   *       .getConstructor(new Class[] {InvocationHandler.class})
+   *       .newInstance(new Object[] {handler});
+   * </pre>
+   * except that it will not fail with the normal problems caused
+   * by reflection.  It can still fail for the same reasons documented
+   * in getProxyClass, or if handler is null.
+   *
+   * @param loader the class loader to define the proxy class in; null
+   *        implies the bootstrap class loader
+   * @param interfaces the array of interfaces the proxy class implements,
+   *        may be empty, but not null
+   * @param handler the invocation handler, may not be null
+   * @return a proxy instance implementing the specified interfaces
+   * @throws IllegalArgumentException if the constraints for getProxyClass
+   *         were violated, except for problems with null
+   * @throws NullPointerException if `interfaces' is null or contains
+   *         a null entry, or if handler is null
+   * @see #getProxyClass(ClassLoader, Class[])
+   * @see Class#getConstructor(Class[])
+   * @see Constructor#newInstance(Object[])
+   */
+  public static Object newProxyInstance(ClassLoader loader,
+                                        Class[] interfaces,
+                                        InvocationHandler handler)
+  {
+    try
+      {
+        // getProxyClass() and Proxy() throw the necessary exceptions
+        return getProxyClass(loader, interfaces)
+          .getConstructor(new Class[] {InvocationHandler.class})
+          .newInstance(new Object[] {handler});
+      }
+    catch (RuntimeException e)
+      {
+        // Let IllegalArgumentException, NullPointerException escape.
+        // assert e instanceof IllegalArgumentException
+        //   || e instanceof NullPointerException;
+        throw e;
+      }
+    catch (InvocationTargetException e)
+      {
+        // Let wrapped NullPointerException escape.
+        // assert e.getTargetException() instanceof NullPointerException
+        throw (NullPointerException) e.getCause();
+      }
+    catch (Exception e)
+      {
+        // Covers InstantiationException, IllegalAccessException,
+        // NoSuchMethodException, none of which should be generated
+        // if the proxy class was generated correctly.
+        // assert false;
+        throw (Error) new InternalError("Unexpected: " + e).initCause(e);
+      }
+  }
+
+  /**
+   * Returns true if and only if the Class object is a dynamically created
+   * proxy class (created by <code>getProxyClass</code> or by the
+   * syntactic sugar of <code>newProxyInstance</code>).
+   *
+   * <p>This check is secure (in other words, it is not simply
+   * <code>clazz.getSuperclass() == Proxy.class</code>), it will not
+   * be spoofed by non-proxy classes that extend Proxy.
+   *
+   * @param clazz the class to check, must not be null
+   * @return true if the class represents a proxy class
+   * @throws NullPointerException if clazz is null
+   */
+  // This is synchronized on the off chance that another thread is
+  // trying to add a class to the map at the same time we read it.
+  public static synchronized boolean isProxyClass(Class clazz)
+  {
+    if (! Proxy.class.isAssignableFrom(clazz))
+      return false;
+    // This is a linear search, even though we could do an O(1) search
+    // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()).
+    return proxyClasses.containsValue(clazz);
+  }
+
+  /**
+   * Returns the invocation handler for the given proxy instance.<p>
+   *
+   * <em>NOTE</em>: We guarantee a non-null result if successful,
+   * but Sun allows the creation of a proxy instance with a null
+   * handler.  See the comments for {@link #Proxy(InvocationHandler)}.
+   *
+   * @param proxy the proxy instance, must not be null
+   * @return the invocation handler, guaranteed non-null.
+   * @throws IllegalArgumentException if
+   *         <code>Proxy.isProxyClass(proxy.getClass())</code> returns false.
+   * @throws NullPointerException if proxy is null
+   */
+  public static InvocationHandler getInvocationHandler(Object proxy)
+  {
+    if (! isProxyClass(proxy.getClass()))
+      throw new IllegalArgumentException("not a proxy instance");
+    return ((Proxy) proxy).h;
+  }
+
+  /**
+   * Optional native method to replace (and speed up) the pure Java
+   * implementation of getProxyClass.  Only needed if
+   * Configuration.HAVE_NATIVE_GET_PROXY_CLASS is true, this does the
+   * work of both getProxyData0 and generateProxyClass0 with no
+   * intermediate form in Java. The native code may safely assume that
+   * this class must be created, and does not already exist.
+   *
+   * @param loader the class loader to define the proxy class in; null
+   *        implies the bootstrap class loader
+   * @param interfaces the interfaces the class will extend
+   * @return the generated proxy class
+   * @throws IllegalArgumentException if the constraints for getProxyClass
+   *         were violated, except for problems with null
+   * @throws NullPointerException if `interfaces' is null or contains
+   *         a null entry, or if handler is null
+   * @see Configuration#HAVE_NATIVE_GET_PROXY_CLASS
+   * @see #getProxyClass(ClassLoader, Class[])
+   * @see #getProxyData0(ClassLoader, Class[])
+   * @see #generateProxyClass0(ProxyData)
+   */
+  private static native Class getProxyClass0(ClassLoader loader,
+                                             Class[] interfaces);
+
+  /**
+   * Optional native method to replace (and speed up) the pure Java
+   * implementation of getProxyData.  Only needed if
+   * Configuration.HAVE_NATIVE_GET_PROXY_DATA is true. The native code
+   * may safely assume that a new ProxyData object must be created which
+   * does not duplicate any existing ones.
+   *
+   * @param loader the class loader to define the proxy class in; null
+   *        implies the bootstrap class loader
+   * @param interfaces the interfaces the class will extend
+   * @return all data that is required to make this proxy class
+   * @throws IllegalArgumentException if the constraints for getProxyClass
+   *         were violated, except for problems with null
+   * @throws NullPointerException if `interfaces' is null or contains
+   *         a null entry, or if handler is null
+   * @see Configuration.HAVE_NATIVE_GET_PROXY_DATA
+   * @see #getProxyClass(ClassLoader, Class[])
+   * @see #getProxyClass0(ClassLoader, Class[])
+   * @see ProxyType#getProxyData()
+   */
+  private static native ProxyData getProxyData0(ClassLoader loader,
+                                                Class[] interfaces);
+
+  /**
+   * Optional native method to replace (and speed up) the pure Java
+   * implementation of generateProxyClass.  Only needed if
+   * Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS is true. The native
+   * code may safely assume that a new Class must be created, and that
+   * the ProxyData object does not describe any existing class.
+   *
+   * @param loader the class loader to define the proxy class in; null
+   *        implies the bootstrap class loader
+   * @param data the struct of information to convert to a Class. This
+   *        has already been verified for all problems except exceeding
+   *        VM limitations
+   * @return the newly generated class
+   * @throws IllegalArgumentException if VM limitations are exceeded
+   * @see #getProxyClass(ClassLoader, Class[])
+   * @see #getProxyClass0(ClassLoader, Class[])
+   * @see ProxyData#generateProxyClass(ClassLoader)
+   */
+  private static native Class generateProxyClass0(ClassLoader loader,
+                                                  ProxyData data);
+
+
+  /**
+   * Helper class for mapping unique ClassLoader and interface combinations
+   * to proxy classes.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class ProxyType
+  {
+    /**
+     * Store the class loader (may be null)
+     */
+    final ClassLoader loader;
+
+    /**
+     * Store the interfaces (never null, all elements are interfaces)
+     */
+    final Class[] interfaces;
+
+    /**
+     * Construct the helper object.
+     *
+     * @param loader the class loader to define the proxy class in; null
+     *        implies the bootstrap class loader
+     * @param interfaces an array of interfaces
+     */
+    ProxyType(ClassLoader loader, Class[] interfaces)
+    {
+      this.loader = loader;
+      this.interfaces = interfaces;
+    }
+
+    /**
+     * Calculates the hash code.
+     *
+     * @return a combination of the classloader and interfaces hashcodes.
+     */
+    public int hashCode()
+    {
+      int hash = (loader == null) ? 0 : loader.hashCode();
+      for (int i = 0; i < interfaces.length; i++)
+        hash = hash * 31 + interfaces[i].hashCode();
+      return hash;
+    }
+
+    /**
+     * Calculates equality.
+     *
+     * @param the object to compare to
+     * @return true if it is a ProxyType with same data
+     */
+    public boolean equals(Object other)
+    {
+      ProxyType pt = (ProxyType) other;
+      if (loader != pt.loader || interfaces.length != pt.interfaces.length)
+        return false;
+      int i = interfaces.length;
+      while (--i >= 0)
+        if (interfaces[i] != pt.interfaces[i])
+          return false;
+      return true;
+    }
+  } // class ProxyType
+
+
+  /**
+   * Helper class which allows hashing of a method name and signature
+   * without worrying about return type, declaring class, or throws clause,
+   * and which reduces the maximally common throws clause between two methods
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class ProxySignature
+  {
+    /**
+     * The core signatures which all Proxy instances handle.
+     */
+    static final HashMap coreMethods = new HashMap();
+    static
+    {
+      try
+        {
+          ProxySignature sig
+            = new ProxySignature(Object.class
+                                 .getMethod("equals",
+                                            new Class[] {Object.class}));
+          coreMethods.put(sig, sig);
+          sig = new ProxySignature(Object.class.getMethod("hashCode", null));
+          coreMethods.put(sig, sig);
+          sig = new ProxySignature(Object.class.getMethod("toString", null));
+          coreMethods.put(sig, sig);
+        }
+      catch (Exception e)
+        {
+          // assert false;
+          throw (Error) new InternalError("Unexpected: " + e).initCause(e);
+        }
+    }
+
+    /**
+     * The underlying Method object, never null
+     */
+    final Method method;
+
+    /**
+     * The set of compatible thrown exceptions, may be empty
+     */
+    final Set exceptions = new HashSet();
+
+    /**
+     * Construct a signature
+     *
+     * @param method the Method this signature is based on, never null
+     */
+    ProxySignature(Method method)
+    {
+      this.method = method;
+      Class[] exc = method.getExceptionTypes();
+      int i = exc.length;
+      while (--i >= 0)
+        {
+          // discard unchecked exceptions
+          if (Error.class.isAssignableFrom(exc[i])
+              || RuntimeException.class.isAssignableFrom(exc[i]))
+            continue;
+          exceptions.add(exc[i]);
+        }
+    }
+
+    /**
+     * Given a method, make sure it's return type is identical
+     * to this, and adjust this signature's throws clause appropriately
+     *
+     * @param other the signature to merge in
+     * @throws IllegalArgumentException if the return types conflict
+     */
+    void checkCompatibility(ProxySignature other)
+    {
+      if (method.getReturnType() != other.method.getReturnType())
+        throw new IllegalArgumentException("incompatible return types: "
+                                           + method + ", " + other.method);
+
+      // if you can think of a more efficient way than this O(n^2) search,
+      // implement it!
+      int size1 = exceptions.size();
+      int size2 = other.exceptions.size();
+      boolean[] valid1 = new boolean[size1];
+      boolean[] valid2 = new boolean[size2];
+      Iterator itr = exceptions.iterator();
+      int pos = size1;
+      while (--pos >= 0)
+        {
+          Class c1 = (Class) itr.next();
+          Iterator itr2 = other.exceptions.iterator();
+          int pos2 = size2;
+          while (--pos2 >= 0)
+            {
+              Class c2 = (Class) itr2.next();
+              if (c2.isAssignableFrom(c1))
+                valid1[pos] = true;
+              if (c1.isAssignableFrom(c2))
+                valid2[pos2] = true;
+            }
+        }
+      pos = size1;
+      itr = exceptions.iterator();
+      while (--pos >= 0)
+        {
+          itr.next();
+          if (! valid1[pos])
+            itr.remove();
+        }
+      pos = size2;
+      itr = other.exceptions.iterator();
+      while (--pos >= 0)
+        {
+          itr.next();
+          if (! valid2[pos])
+            itr.remove();
+        }
+      exceptions.addAll(other.exceptions);
+    }
+
+    /**
+     * Calculates the hash code.
+     *
+     * @return a combination of name and parameter types
+     */
+    public int hashCode()
+    {
+      int hash = method.getName().hashCode();
+      Class[] types = method.getParameterTypes();
+      for (int i = 0; i < types.length; i++)
+        hash = hash * 31 + types[i].hashCode();
+      return hash;
+    }
+
+    /**
+     * Calculates equality.
+     *
+     * @param the object to compare to
+     * @return true if it is a ProxySignature with same data
+     */
+    public boolean equals(Object other)
+    {
+      ProxySignature ps = (ProxySignature) other;
+      Class[] types1 = method.getParameterTypes();
+      Class[] types2 = ps.method.getParameterTypes();
+      if (! method.getName().equals(ps.method.getName())
+          || types1.length != types2.length)
+        return false;
+      int i = types1.length;
+      while (--i >= 0)
+        if (types1[i] != types2[i])
+          return false;
+      return true;
+    }
+  } // class ProxySignature
+
+
+  /**
+   * A flat representation of all data needed to generate bytecode/instantiate
+   * a proxy class.  This is basically a struct.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class ProxyData
+  {
+    /**
+     * The package this class is in.  Possibly null, meaning the unnamed
+     * package.
+     */
+    Package pack;
+
+    /**
+     * The interfaces this class implements.  Non-null, but possibly empty.
+     */
+    Class[] interfaces;
+
+    /**
+     * The Method objects this class must pass as the second argument to
+     * invoke (also useful for determining what methods this class has).
+     * Non-null, non-empty (includes at least Object.hashCode, Object.equals,
+     * and Object.toString).
+     */
+    Method[] methods;
+
+    /**
+     * The exceptions that do not need to be wrapped in
+     * UndeclaredThrowableException. exceptions[i] is the same as, or a
+     * subset of subclasses, of methods[i].getExceptionTypes(), depending on
+     * compatible throws clauses with multiple inheritance. It is unspecified
+     * if these lists include or exclude subclasses of Error and
+     * RuntimeException, but excluding them is harmless and generates a
+     * smaller class.
+     */
+    Class[][] exceptions;
+
+    /**
+     * For unique id's
+     */
+    private static int count = 0;
+
+    /**
+     * The id of this proxy class
+     */
+    final int id = count++;
+
+    /**
+     * Construct a ProxyData with uninitialized data members.
+     */
+    ProxyData()
+    {
+    }
+
+    /**
+     * Verifies that the arguments are legal, and sets up remaining data
+     * This should only be called when a class must be generated, as
+     * it is expensive.
+     *
+     * @param pt the ProxyType to convert to ProxyData
+     * @return the flattened, verified ProxyData structure for use in
+     *         class generation
+     * @throws IllegalArgumentException if `interfaces' contains
+     *         non-interfaces or incompatible combinations, and verify is true
+     * @throws NullPointerException if interfaces is null or contains null
+     */
+    static ProxyData getProxyData(ProxyType pt)
+    {
+      Map method_set = (Map) ProxySignature.coreMethods.clone();
+      boolean in_package = false; // true if we encounter non-public interface
+
+      ProxyData data = new ProxyData();
+      data.interfaces = pt.interfaces;
+
+      // if interfaces is too large, we croak later on when the constant
+      // pool overflows
+      int i = data.interfaces.length;
+      while (--i >= 0)
+        {
+          Class inter = data.interfaces[i];
+          if (! inter.isInterface())
+            throw new IllegalArgumentException("not an interface: " + inter);
+          try
+            {
+              if (Class.forName(inter.getName(), false, pt.loader) != inter)
+                throw new IllegalArgumentException("not accessible in "
+                                                   + "classloader: " + inter);
+            }
+          catch (ClassNotFoundException e)
+            {
+              throw new IllegalArgumentException("not accessible in "
+                                                 + "classloader: " + inter);
+            }
+          if (! Modifier.isPublic(inter.getModifiers()))
+            if (in_package)
+              {
+                Package p = inter.getPackage();
+                if (data.pack != inter.getPackage())
+                  throw new IllegalArgumentException("non-public interfaces "
+                                                     + "from different "
+                                                     + "packages");
+              }
+            else
+              {
+                in_package = true;
+                data.pack = inter.getPackage();
+              }
+          for (int j = i-1; j >= 0; j--)
+            if (data.interfaces[j] == inter)
+              throw new IllegalArgumentException("duplicate interface: "
+                                                 + inter);
+          Method[] methods = inter.getMethods();
+          int j = methods.length;
+          while (--j >= 0)
+            {
+              ProxySignature sig = new ProxySignature(methods[j]);
+              ProxySignature old = (ProxySignature) method_set.put(sig, sig);
+              if (old != null)
+                sig.checkCompatibility(old);
+            }
+        }
+
+      i = method_set.size();
+      data.methods = new Method[i];
+      data.exceptions = new Class[i][];
+      Iterator itr = method_set.values().iterator();
+      while (--i >= 0)
+        {
+          ProxySignature sig = (ProxySignature) itr.next();
+          data.methods[i] = sig.method;
+          data.exceptions[i] = (Class[]) sig.exceptions
+            .toArray(new Class[sig.exceptions.size()]);
+        }
+      return data;
+    }
+  } // class ProxyData
+
+
+  /**
+   * Does all the work of building a class. By making this a nested class,
+   * this code is not loaded in memory if the VM has a native
+   * implementation instead.
+   *
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  private static final class ClassFactory
+  {
+    /** Constants for assisting the compilation */
+    private static final byte POOL = 0;
+    private static final byte FIELD = 1;
+    private static final byte METHOD = 2;
+    private static final byte INTERFACE = 3;
+    private static final String CTOR_SIG
+      = "(Ljava/lang/reflect/InvocationHandler;)V";
+    private static final String INVOKE_SIG = "(Ljava/lang/Object;"
+      + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
+
+    /** Bytecodes for insertion in the class definition byte[] */
+    private static final char ACONST_NULL = 1;
+    private static final char ICONST_0 = 3;
+    private static final char BIPUSH = 16;
+    private static final char SIPUSH = 17;
+    private static final char ILOAD = 21;
+    private static final char ILOAD_0 = 26;
+    private static final char ALOAD_0 = 42;
+    private static final char ALOAD_1 = 43;
+    private static final char AALOAD = 50;
+    private static final char AASTORE = 83;
+    private static final char DUP = 89;
+    private static final char DUP_X1 = 90;
+    private static final char SWAP = 95;
+    private static final char IRETURN = 172;
+    private static final char LRETURN = 173;
+    private static final char FRETURN = 174;
+    private static final char DRETURN = 175;
+    private static final char ARETURN = 176;
+    private static final char RETURN = 177;
+    private static final char GETSTATIC = 178;
+    private static final char GETFIELD = 180;
+    private static final char INVOKEVIRTUAL = 182;
+    private static final char INVOKESPECIAL = 183;
+    private static final char INVOKESTATIC = 184;
+    private static final char INVOKEINTERFACE = 185;
+    private static final char NEW = 187;
+    private static final char ANEWARRAY = 189;
+    private static final char ATHROW = 191;
+    private static final char CHECKCAST = 192;
+
+    // Implementation note: we use StringBuffers to hold the byte data, since
+    // they automatically grow.  However, we only use the low 8 bits of
+    // every char in the array, so we are using twice the necessary memory
+    // for the ease StringBuffer provides.
+
+    /** The constant pool. */
+    private final StringBuffer pool = new StringBuffer();
+    /** The rest of the class data. */
+    private final StringBuffer stream = new StringBuffer();
+
+    /** Map of strings to byte sequences, to minimize size of pool. */
+    private final Map poolEntries = new HashMap();
+
+    /** The VM name of this proxy class. */
+    private final String qualName;
+
+    /**
+     * The Method objects the proxy class refers to when calling the
+     * invocation handler.
+     */
+    private final Method[] methods;
+
+    /**
+     * Initializes the buffers with the bytecode contents for a proxy class.
+     *
+     * @param data the remainder of the class data
+     * @throws IllegalArgumentException if anything else goes wrong this
+     *         late in the game; as far as I can tell, this will only happen
+     *         if the constant pool overflows, which is possible even when
+     *         the user doesn't exceed the 65535 interface limit
+     */
+    ClassFactory(ProxyData data)
+    {
+      methods = data.methods;
+
+      // magic = 0xcafebabe
+      // minor_version = 0
+      // major_version = 46
+      // constant_pool_count: place-holder for now
+      pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0");
+      // constant_pool[], filled in as we go
+
+      // access_flags
+      putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC);
+      // this_class
+      qualName = ((data.pack == null ? "" : data.pack.getName() + '.')
+                  + "$Proxy" + data.id);
+      putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false)));
+      // super_class
+      putU2(classInfo("java/lang/reflect/Proxy"));
+
+      // interfaces_count
+      putU2(data.interfaces.length);
+      // interfaces[]
+      for (int i = 0; i < data.interfaces.length; i++)
+        putU2(classInfo(data.interfaces[i]));
+
+      // Recall that Proxy classes serialize specially, so we do not need
+      // to worry about a <clinit> method for this field.  Instead, we
+      // just assign it by reflection after the class is successfully loaded.
+      // fields_count - private static Method[] m;
+      putU2(1);
+      // fields[]
+      // m.access_flags
+      putU2(Modifier.PRIVATE | Modifier.STATIC);
+      // m.name_index
+      putU2(utf8Info("m"));
+      // m.descriptor_index
+      putU2(utf8Info("[Ljava/lang/reflect/Method;"));
+      // m.attributes_count
+      putU2(0);
+      // m.attributes[]
+
+      // methods_count - # handler methods, plus <init>
+      putU2(methods.length + 1);
+      // methods[]
+      // <init>.access_flags
+      putU2(Modifier.PUBLIC);
+      // <init>.name_index
+      putU2(utf8Info("<init>"));
+      // <init>.descriptor_index
+      putU2(utf8Info(CTOR_SIG));
+      // <init>.attributes_count - only Code is needed
+      putU2(1);
+      // <init>.Code.attribute_name_index
+      putU2(utf8Info("Code"));
+      // <init>.Code.attribute_length = 18
+      // <init>.Code.info:
+      //   $Proxynn(InvocationHandler h) { super(h); }
+      // <init>.Code.max_stack = 2
+      // <init>.Code.max_locals = 2
+      // <init>.Code.code_length = 6
+      // <init>.Code.code[]
+      stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1
+                    + INVOKESPECIAL);
+      putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "<init>", CTOR_SIG));
+      // <init>.Code.exception_table_length = 0
+      // <init>.Code.exception_table[]
+      // <init>.Code.attributes_count = 0
+      // <init>.Code.attributes[]
+      stream.append(RETURN + "\0\0\0\0");
+
+      for (int i = methods.length - 1; i >= 0; i--)
+        emitMethod(i, data.exceptions[i]);
+
+      // attributes_count
+      putU2(0);
+      // attributes[] - empty; omit SourceFile attribute
+      // XXX should we mark this with a Synthetic attribute?
+    }
+
+    /**
+     * Produce the bytecode for a single method.
+     *
+     * @param i the index of the method we are building
+     * @param e the exceptions possible for the method
+     */
+    private void emitMethod(int i, Class[] e)
+    {
+      // First, we precalculate the method length and other information.
+
+      Method m = methods[i];
+      Class[] paramtypes = m.getParameterTypes();
+      int wrap_overhead = 0; // max words taken by wrapped primitive
+      int param_count = 1; // 1 for this
+      int code_length = 16; // aload_0, getfield, aload_0, getstatic, const,
+      // aaload, const/aconst_null, invokeinterface
+      if (i > 5)
+        {
+          if (i > Byte.MAX_VALUE)
+            code_length += 2; // sipush
+          else
+            code_length++; // bipush
+        }
+      if (paramtypes.length > 0)
+        {
+          code_length += 3; // anewarray
+          if (paramtypes.length > Byte.MAX_VALUE)
+            code_length += 2; // sipush
+          else if (paramtypes.length > 5)
+            code_length++; // bipush
+          for (int j = 0; j < paramtypes.length; j++)
+            {
+              code_length += 4; // dup, const, load, store
+              Class type = paramtypes[j];
+              if (j > 5)
+                {
+                  if (j > Byte.MAX_VALUE)
+                    code_length += 2; // sipush
+                  else
+                    code_length++; // bipush
+                }
+              if (param_count >= 4)
+                code_length++; // 2-byte load
+              param_count++;
+              if (type.isPrimitive())
+                {
+                  code_length += 7; // new, dup, invokespecial
+                  if (type == long.class || type == double.class)
+                    {
+                      wrap_overhead = 3;
+                      param_count++;
+                    }
+                  else if (wrap_overhead < 2)
+                    wrap_overhead = 2;
+                }
+            }
+        }
+      int end_pc = code_length;
+      Class ret_type = m.getReturnType();
+      if (ret_type == void.class)
+        code_length++; // return
+      else if (ret_type.isPrimitive())
+        code_length += 7; // cast, invokevirtual, return
+      else
+        code_length += 4; // cast, return
+      int exception_count = 0;
+      boolean throws_throwable = false;
+      for (int j = 0; j < e.length; j++)
+        if (e[j] == Throwable.class)
+          {
+            throws_throwable = true;
+            break;
+          }
+      if (! throws_throwable)
+        {
+          exception_count = e.length + 3; // Throwable, Error, RuntimeException
+          code_length += 9; // new, dup_x1, swap, invokespecial, athrow
+        }
+      int handler_pc = code_length - 1;
+      StringBuffer signature = new StringBuffer("(");
+      for (int j = 0; j < paramtypes.length; j++)
+        signature.append(TypeSignature.getEncodingOfClass(paramtypes[j]));
+      signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type));
+
+      // Now we have enough information to emit the method.
+
+      // handler.access_flags
+      putU2(Modifier.PUBLIC | Modifier.FINAL);
+      // handler.name_index
+      putU2(utf8Info(m.getName()));
+      // handler.descriptor_index
+      putU2(utf8Info(signature.toString()));
+      // handler.attributes_count - Code is necessary, Exceptions possible
+      putU2(e.length > 0 ? 2 : 1);
+
+      // handler.Code.info:
+      //   type name(args) {
+      //     try {
+      //       return (type) h.invoke(this, methods[i], new Object[] {args});
+      //     } catch (<declared Exceptions> e) {
+      //       throw e;
+      //     } catch (Throwable t) {
+      //       throw new UndeclaredThrowableException(t);
+      //     }
+      //   }
+      // Special cases:
+      //  if arg_n is primitive, wrap it
+      //  if method throws Throwable, try-catch is not needed
+      //  if method returns void, return statement not needed
+      //  if method returns primitive, unwrap it
+      //  save space by sharing code for all the declared handlers
+
+      // handler.Code.attribute_name_index
+      putU2(utf8Info("Code"));
+      // handler.Code.attribute_length
+      putU4(12 + code_length + 8 * exception_count);
+      // handler.Code.max_stack
+      putU2(param_count == 1 ? 4 : 7 + wrap_overhead);
+      // handler.Code.max_locals
+      putU2(param_count);
+      // handler.Code.code_length
+      putU4(code_length);
+      // handler.Code.code[]
+      putU1(ALOAD_0);
+      putU1(GETFIELD);
+      putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h",
+                    "Ljava/lang/reflect/InvocationHandler;"));
+      putU1(ALOAD_0);
+      putU1(GETSTATIC);
+      putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false),
+                    "m", "[Ljava/lang/reflect/Method;"));
+      putConst(i);
+      putU1(AALOAD);
+      if (paramtypes.length > 0)
+        {
+          putConst(paramtypes.length);
+          putU1(ANEWARRAY);
+          putU2(classInfo("java/lang/Object"));
+          param_count = 1;
+          for (int j = 0; j < paramtypes.length; j++, param_count++)
+            {
+              putU1(DUP);
+              putConst(j);
+              if (paramtypes[j].isPrimitive())
+                {
+                  putU1(NEW);
+                  putU2(classInfo(wrapper(paramtypes[j])));
+                  putU1(DUP);
+                }
+              putLoad(param_count, paramtypes[j]);
+              if (paramtypes[j].isPrimitive())
+                {
+                  putU1(INVOKESPECIAL);
+                  putU2(refInfo(METHOD, wrapper(paramtypes[j]), "<init>",
+                                '(' + (TypeSignature
+                                       .getEncodingOfClass(paramtypes[j])
+                                       + ")V")));
+                  if (paramtypes[j] == long.class
+                      || paramtypes[j] == double.class)
+                    param_count++;
+                }
+              putU1(AASTORE);
+            }
+        }
+      else
+        putU1(ACONST_NULL);
+      putU1(INVOKEINTERFACE);
+      putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler",
+                    "invoke", INVOKE_SIG));
+      putU1(4); // InvocationHandler, this, Method, Object[]
+      putU1(0);
+      if (ret_type == void.class)
+        putU1(RETURN);
+      else if (ret_type.isPrimitive())
+        {
+          putU1(CHECKCAST);
+          putU2(classInfo(wrapper(ret_type)));
+          putU1(INVOKEVIRTUAL);
+          putU2(refInfo(METHOD, wrapper(ret_type),
+                        ret_type.getName() + "Value",
+                        "()" + TypeSignature.getEncodingOfClass(ret_type)));
+          if (ret_type == long.class)
+            putU1(LRETURN);
+          else if (ret_type == float.class)
+            putU1(FRETURN);
+          else if (ret_type == double.class)
+            putU1(DRETURN);
+          else
+            putU1(IRETURN);
+        }
+      else
+        {
+          putU1(CHECKCAST);
+          putU2(classInfo(ret_type));
+          putU1(ARETURN);
+        }
+      if (! throws_throwable)
+        {
+          putU1(NEW);
+          putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
+          putU1(DUP_X1);
+          putU1(SWAP);
+          putU1(INVOKESPECIAL);
+          putU2(refInfo(METHOD,
+                        "java/lang/reflect/UndeclaredThrowableException",
+                        "<init>", "(Ljava/lang/Throwable;)V"));
+          putU1(ATHROW);
+        }
+
+      // handler.Code.exception_table_length
+      putU2(exception_count);
+      // handler.Code.exception_table[]
+      if (! throws_throwable)
+        {
+          // handler.Code.exception_table.start_pc
+          putU2(0);
+          // handler.Code.exception_table.end_pc
+          putU2(end_pc);
+          // handler.Code.exception_table.handler_pc
+          putU2(handler_pc);
+          // handler.Code.exception_table.catch_type
+          putU2(classInfo("java/lang/Error"));
+          // handler.Code.exception_table.start_pc
+          putU2(0);
+          // handler.Code.exception_table.end_pc
+          putU2(end_pc);
+          // handler.Code.exception_table.handler_pc
+          putU2(handler_pc);
+          // handler.Code.exception_table.catch_type
+          putU2(classInfo("java/lang/RuntimeException"));
+          for (int j = 0; j < e.length; j++)
+            {
+              // handler.Code.exception_table.start_pc
+              putU2(0);
+              // handler.Code.exception_table.end_pc
+              putU2(end_pc);
+              // handler.Code.exception_table.handler_pc
+              putU2(handler_pc);
+              // handler.Code.exception_table.catch_type
+              putU2(classInfo(e[j]));
+            }
+          // handler.Code.exception_table.start_pc
+          putU2(0);
+          // handler.Code.exception_table.end_pc
+          putU2(end_pc);
+          // handler.Code.exception_table.handler_pc -
+          //   -8 for undeclared handler, which falls thru to normal one
+          putU2(handler_pc - 8);
+          // handler.Code.exception_table.catch_type
+          putU2(0);
+        }
+      // handler.Code.attributes_count
+      putU2(0);
+      // handler.Code.attributes[]
+
+      if (e.length > 0)
+        {
+          // handler.Exceptions.attribute_name_index
+          putU2(utf8Info("Exceptions"));
+          // handler.Exceptions.attribute_length
+          putU4(2 * e.length + 2);
+          // handler.Exceptions.number_of_exceptions
+          putU2(e.length);
+          // handler.Exceptions.exception_index_table[]
+          for (int j = 0; j < e.length; j++)
+            putU2(classInfo(e[j]));
+        }
+    }
+
+    /**
+     * Creates the Class object that corresponds to the bytecode buffers
+     * built when this object was constructed.
+     *
+     * @param loader the class loader to define the proxy class in; null
+     *        implies the bootstrap class loader
+     * @return the proxy class Class object
+     */
+    final Class generate(ClassLoader loader)
+    {
+      byte[] bytecode = new byte[pool.length() + stream.length()];
+      // More efficient to bypass calling charAt() repetitively.
+      char[] c = pool.toString().toCharArray();
+      int i = c.length;
+      while (--i >= 0)
+        bytecode[i] = (byte) c[i];
+      c = stream.toString().toCharArray();
+      i = c.length;
+      int j = bytecode.length;
+      while (i > 0)
+        bytecode[--j] = (byte) c[--i];
+
+      // Patch the constant pool size, which we left at 0 earlier.
+      int count = poolEntries.size() + 1;
+      bytecode[8] = (byte) (count >> 8);
+      bytecode[9] = (byte) count;
+
+      try
+        {
+          // XXX Do we require more native support here?
+
+          // XXX Security hole - it is possible for another thread to grab the
+          // VMClassLoader.defineClass Method object, and abuse it while we
+          // have temporarily made it accessible. Do we need to add some
+          // synchronization lock to prevent user reflection while we use it?
+
+          // XXX This is waiting on VM support for protection domains.
+
+          Class vmClassLoader = Class.forName("java.lang.VMClassLoader");
+          Class[] types = {ClassLoader.class, String.class,
+                           byte[].class, int.class, int.class,
+                           /* ProtectionDomain.class */ };
+          Method m = vmClassLoader.getDeclaredMethod("defineClass", types);
+
+          // Bypass the security check of setAccessible(true), since this
+          // is trusted code. But note the comment above about the security
+          // risk of doing this outside a synchronized block.
+          m.flag = true;
+          Object[] args = {loader, qualName, bytecode, new Integer(0),
+                           new Integer(bytecode.length),
+                           /* Object.class.getProtectionDomain() */ };
+          Class clazz = (Class) m.invoke(null, args);
+          m.flag = false;
+
+          // Finally, initialize the m field of the proxy class, before
+          // returning it.
+
+          // No security risk here, since clazz has not been exposed yet,
+          // so user code cannot grab the same reflection object.
+          Field f = clazz.getDeclaredField("m");
+          f.flag = true;
+          // we can share the array, because it is not publicized
+          f.set(null, methods);
+          f.flag = false;
+
+          return clazz;
+        }
+      catch (Throwable e)
+        {
+          // assert false;
+          throw (Error) new InternalError("Unexpected: " + e).initCause(e);
+        }
+    }
+
+    /**
+     * Put a single byte on the stream.
+     *
+     * @param i the information to add (only lowest 8 bits are used)
+     */
+    private void putU1(int i)
+    {
+      stream.append((char) i);
+    }
+
+    /**
+     * Put two bytes on the stream.
+     *
+     * @param i the information to add (only lowest 16 bits are used)
+     */
+    private void putU2(int i)
+    {
+      stream.append((char) (i >> 8)).append((char) i);
+    }
+
+    /**
+     * Put four bytes on the stream.
+     *
+     * @param i the information to add (treated as unsigned)
+     */
+    private void putU4(int i)
+    {
+      stream.append((char) (i >> 24)).append((char) (i >> 16));
+      stream.append((char) (i >> 8)).append((char) i);
+    }
+
+    /**
+     * Put bytecode to load a constant integer on the stream. This only
+     * needs to work for values less than Short.MAX_VALUE.
+     *
+     * @param i the int to add
+     */
+    private void putConst(int i)
+    {
+      if (i >= -1 && i <= 5)
+        putU1(ICONST_0 + i);
+      else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE)
+        {
+          putU1(BIPUSH);
+          putU1(i);
+        }
+      else
+        {
+          putU1(SIPUSH);
+          putU2(i);
+        }
+    }
+
+    /**
+     * Put bytecode to load a given local variable on the stream.
+     *
+     * @param i the slot to load
+     * @param type the base type of the load
+     */
+    private void putLoad(int i, Class type)
+    {
+      int offset = 0;
+      if (type == long.class)
+        offset = 1;
+      else if (type == float.class)
+        offset = 2;
+      else if (type == double.class)
+        offset = 3;
+      else if (! type.isPrimitive())
+        offset = 4;
+      if (i < 4)
+        putU1(ILOAD_0 + 4 * offset + i);
+      else
+        {
+          putU1(ILOAD + offset);
+          putU1(i);
+        }
+    }
+
+    /**
+     * Given a primitive type, return its wrapper class name.
+     *
+     * @param clazz the primitive type (but not void.class)
+     * @return the internal form of the wrapper class name
+     */
+    private String wrapper(Class clazz)
+    {
+      if (clazz == boolean.class)
+        return "java/lang/Boolean";
+      if (clazz == byte.class)
+        return "java/lang/Byte";
+      if (clazz == short.class)
+        return "java/lang/Short";
+      if (clazz == char.class)
+        return "java/lang/Character";
+      if (clazz == int.class)
+        return "java/lang/Integer";
+      if (clazz == long.class)
+        return "java/lang/Long";
+      if (clazz == float.class)
+        return "java/lang/Float";
+      if (clazz == double.class)
+        return "java/lang/Double";
+      // assert false;
+      return null;
+    }
+
+    /**
+     * Returns the entry of this String in the Constant pool, adding it
+     * if necessary.
+     *
+     * @param str the String to resolve
+     * @return the index of the String in the constant pool
+     */
+    private char utf8Info(String str)
+    {
+      String utf8 = toUtf8(str);
+      int len = utf8.length();
+      return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8);
+    }
+
+    /**
+     * Returns the entry of the appropriate class info structure in the
+     * Constant pool, adding it if necessary.
+     *
+     * @param name the class name, in internal form
+     * @return the index of the ClassInfo in the constant pool
+     */
+    private char classInfo(String name)
+    {
+      char index = utf8Info(name);
+      char[] c = {7, (char) (index >> 8), (char) (index & 0xff)};
+      return poolIndex(new String(c));
+    }
+
+    /**
+     * Returns the entry of the appropriate class info structure in the
+     * Constant pool, adding it if necessary.
+     *
+     * @param clazz the class type
+     * @return the index of the ClassInfo in the constant pool
+     */
+    private char classInfo(Class clazz)
+    {
+      return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(),
+                                                        false));
+    }
+
+    /**
+     * Returns the entry of the appropriate fieldref, methodref, or
+     * interfacemethodref info structure in the Constant pool, adding it
+     * if necessary.
+     *
+     * @param structure FIELD, METHOD, or INTERFACE
+     * @param clazz the class name, in internal form
+     * @param name the simple reference name
+     * @param type the type of the reference
+     * @return the index of the appropriate Info structure in the constant pool
+     */
+    private char refInfo(byte structure, String clazz, String name,
+                         String type)
+    {
+      char cindex = classInfo(clazz);
+      char ntindex = nameAndTypeInfo(name, type);
+      // relies on FIELD == 1, METHOD == 2, INTERFACE == 3
+      char[] c = {(char) (structure + 8),
+                  (char) (cindex >> 8), (char) (cindex & 0xff),
+                  (char) (ntindex >> 8), (char) (ntindex & 0xff)};
+      return poolIndex(new String(c));
+    }
+
+    /**
+     * Returns the entry of the appropriate nameAndTyperef info structure
+     * in the Constant pool, adding it if necessary.
+     *
+     * @param name the simple name
+     * @param type the reference type
+     * @return the index of the NameAndTypeInfo structure in the constant pool
+     */
+    private char nameAndTypeInfo(String name, String type)
+    {
+      char nindex = utf8Info(name);
+      char tindex = utf8Info(type);
+      char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff),
+                  (char) (tindex >> 8), (char) (tindex & 0xff)};
+      return poolIndex(new String(c));
+    }
+
+    /**
+     * Converts a regular string to a UTF8 string, where the upper byte
+     * of every char is 0, and '\\u0000' is not in the string.  This is
+     * basically to use a String as a fancy byte[], and while it is less
+     * efficient in memory use, it is easier for hashing.
+     *
+     * @param str the original, in straight unicode
+     * @return a modified string, in UTF8 format in the low bytes
+     */
+    private String toUtf8(String str)
+    {
+      final char[] ca = str.toCharArray();
+      final int len = ca.length;
+
+      // Avoid object creation, if str is already fits UTF8.
+      int i;
+      for (i = 0; i < len; i++)
+        if (ca[i] == 0 || ca[i] > '\u007f')
+          break;
+      if (i == len)
+        return str;
+
+      final StringBuffer sb = new StringBuffer(str);
+      sb.setLength(i);
+      for ( ; i < len; i++)
+        {
+          final char c = ca[i];
+          if (c > 0 && c <= '\u007f')
+            sb.append(c);
+          else if (c <= '\u07ff') // includes '\0'
+            {
+              sb.append((char) (0xc0 | (c >> 6)));
+              sb.append((char) (0x80 | (c & 0x6f)));
+            }
+          else
+            {
+              sb.append((char) (0xe0 | (c >> 12)));
+              sb.append((char) (0x80 | ((c >> 6) & 0x6f)));
+              sb.append((char) (0x80 | (c & 0x6f)));
+            }
+        }
+      return sb.toString();
+    }
+
+    /**
+     * Returns the location of a byte sequence (conveniently wrapped in
+     * a String with all characters between \u0001 and \u00ff inclusive)
+     * in the constant pool, adding it if necessary.
+     *
+     * @param sequence the byte sequence to look for
+     * @return the index of the sequence
+     * @throws IllegalArgumentException if this would make the constant
+     *         pool overflow
+     */
+    private char poolIndex(String sequence)
+    {
+      Integer i = (Integer) poolEntries.get(sequence);
+      if (i == null)
+        {
+          // pool starts at index 1
+          int size = poolEntries.size() + 1;
+          if (size >= 65535)
+            throw new IllegalArgumentException("exceeds VM limitations");
+          i = new Integer(size);
+          poolEntries.put(sequence, i);
+          pool.append(sequence);
+        }
+      return (char) i.intValue();
+    }
+  } // class ClassFactory
+}
diff --git a/libjava/java/lang/reflect/UndeclaredThrowableException.java b/libjava/java/lang/reflect/UndeclaredThrowableException.java
new file mode 100644
index 0000000..d959692
--- /dev/null
+++ b/libjava/java/lang/reflect/UndeclaredThrowableException.java
@@ -0,0 +1,128 @@
+/* UndeclaredThrowableException.java -- wraps an undeclared checked exception
+   thrown by a Proxy invocation handler
+   Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang.reflect;
+
+/**
+ * This exception class is thrown by a {@link Proxy} instance if
+ * the {@link InvocationHandler#invoke(Object, Method, Object[]) invoke}
+ * method of that instance's InvocationHandler attempts to throw an
+ * exception that not declared by the throws clauses of all of the
+ * interface methods that the proxy instance is implementing.
+ *
+ * <p>When thrown by Proxy, this class will always wrap a checked
+ * exception, never {@link Error} or {@link RuntimeException},
+ * which are unchecked.
+ *
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Proxy
+ * @see InvocationHandler
+ * @since 1.3
+ * @status updated to 1.4
+ */
+public class UndeclaredThrowableException extends RuntimeException
+{
+  /**
+   * Compatible with JDK 1.3+.
+   */
+  private static final long serialVersionUID = 330127114055056639L;
+
+  /**
+   * The immutable exception that this wraps. This field is redundant
+   * with {@link Throwable#cause}, but is necessary for serial compatibility.
+   *
+   * @serial the chained exception
+   */
+  private final Throwable undeclaredThrowable;
+
+  /**
+   * Wraps the given checked exception into a RuntimeException, with no
+   * detail message.  {@link Throwable#initCause(Throwable)} will fail
+   * on this instance.
+   *
+   * @param cause the undeclared throwable that caused this exception,
+   *        may be null
+   */
+  public UndeclaredThrowableException(Throwable cause)
+  {
+    this(cause, null);
+  }
+
+  /**
+   * Wraps the given checked exception into a RuntimeException, with the
+   * specified detail message.  {@link Throwable#initCause(Throwable)} will
+   * fail on this instance.
+   *
+   * @param cause the undeclared throwable that caused this exception,
+   *        may be null
+   * @param message the message, may be null
+   */
+  public UndeclaredThrowableException(Throwable cause, String message)
+  {
+    super(message, cause);
+    undeclaredThrowable = cause;
+  }
+
+  /**
+   * Returns the cause of this exception.  If this exception was created
+   * by a {@link Proxy} instance, it will be a non-null checked
+   * exception.  This method pre-dates exception chaining, and is now
+   * simply a longer way to call <code>getCause()</code>.
+   *
+   * @return the cause of this exception, may be null
+   * @see #getCause()
+   */
+  public Throwable getUndeclaredThrowable()
+  {
+    return undeclaredThrowable;
+  }
+
+  /**
+   * Returns the cause of this exception.  If this exception was created
+   * by a {@link Proxy} instance, it will be a non-null checked
+   * exception.
+   *
+   * @return the cause of this exception, may be null
+   * @since 1.4
+   */
+  public Throwable getCause()
+  {
+    return undeclaredThrowable;
+  }
+}
diff --git a/libjava/java/lang/reflect/natProxy.cc b/libjava/java/lang/reflect/natProxy.cc
new file mode 100644
index 0000000..1a24858
--- /dev/null
+++ b/libjava/java/lang/reflect/natProxy.cc
@@ -0,0 +1,38 @@
+// natProxy.cc - Native code for Proxy class.
+
+/* Copyright (C) 2002  Free Software Foundation
+
+   This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
+details.  */
+
+#include <config.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+
+#include <java/lang/Class.h>
+#include <java/lang/ClassLoader.h>
+#include <java/lang/reflect/Proxy.h>
+#include <java/lang/reflect/Proxy$ProxyData.h>
+
+::java::lang::Class *
+java::lang::reflect::Proxy::getProxyClass0 (::java::lang::ClassLoader *, JArray< ::java::lang::Class *> *)
+{
+  return 0;
+}
+
+::java::lang::reflect::Proxy$ProxyData *
+java::lang::reflect::Proxy::getProxyData0 (::java::lang::ClassLoader *, JArray< ::java::lang::Class *> *)
+{
+  return 0;
+}
+
+::java::lang::Class *
+java::lang::reflect::Proxy::generateProxyClass0 (::java::lang::ClassLoader *, 
+						 ::java::lang::reflect::Proxy$ProxyData *)
+{
+  return 0;
+}
-- 
cgit v1.1