Proxy.java 54.7 KB
Newer Older
Tom Tromey committed
1
/* Proxy.java -- build a proxy class that implements reflected interfaces
2
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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;

41 42
import gnu.java.lang.CPStringBuilder;

Tom Tromey committed
43 44 45 46
import gnu.java.lang.reflect.TypeSignature;

import java.io.Serializable;
import java.security.ProtectionDomain;
47
import java.util.Arrays;
Tom Tromey committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 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>
106
 *  <li>The method {@link #isProxyClass(Class)} will distinguish between
Tom Tromey committed
107 108 109 110 111 112 113 114 115 116
 *      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
117 118
 *      {@link #newProxyInstance(ClassLoader, Class[], InvocationHandler)}
 *      is shorthand to do the necessary reflection.</li>
Tom Tromey committed
119 120 121 122 123 124 125 126 127 128 129 130 131
 * </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
132
 *      to an interface method, including {@link Object#hashCode()},
Tom Tromey committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
 *      {@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
161
 * @status updated to 1.5, except for the use of ProtectionDomain
Tom Tromey committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
 */
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 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.
   *
   * <p>There are several restrictions on this method, the violation of
   * which will result in an IllegalArgumentException or
   * NullPointerException:</p>
220
   *
Tom Tromey committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
   * <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.</p>
   *
   * @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
260
  public static synchronized Class<?> getProxyClass(ClassLoader loader,
261
                                                    Class<?>... interfaces)
Tom Tromey committed
262 263 264 265 266 267
  {
    interfaces = (Class[]) interfaces.clone();
    ProxyType pt = new ProxyType(loader, interfaces);
    Class clazz = (Class) proxyClasses.get(pt);
    if (clazz == null)
      {
Tom Tromey committed
268 269
        if (VMProxy.HAVE_NATIVE_GET_PROXY_CLASS)
          clazz = VMProxy.getProxyClass(loader, interfaces);
Tom Tromey committed
270 271
        else
          {
Tom Tromey committed
272 273
            ProxyData data = (VMProxy.HAVE_NATIVE_GET_PROXY_DATA
                              ? VMProxy.getProxyData(loader, interfaces)
Tom Tromey committed
274 275
                              : ProxyData.getProxyData(pt));

Tom Tromey committed
276
            clazz = (VMProxy.HAVE_NATIVE_GENERATE_PROXY_CLASS
277
                     ? VMProxy.generateProxyClass(loader, data)
Tom Tromey committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
                     : new ClassFactory(data).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,
315
                                        Class<?>[] interfaces,
Tom Tromey committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
                                        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.
363
  public static synchronized boolean isProxyClass(Class<?> clazz)
Tom Tromey committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  {
    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;
  }

  /**
   * 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()
    {
430
      int hash = loader == null ? 0 : loader.hashCode();
Tom Tromey committed
431 432 433 434 435 436 437 438
      for (int i = 0; i < interfaces.length; i++)
        hash = hash * 31 + interfaces[i].hashCode();
      return hash;
    }

    /**
     * Calculates equality.
     *
439
     * @param other object to compare to
Tom Tromey committed
440 441 442 443 444 445 446
     * @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;
Tom Tromey committed
447 448 449 450
      for (int i = 0; i < interfaces.length; i++)
        if (interfaces[i] != pt.interfaces[i])
          return false;
      return true;
Tom Tromey committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
    }
  } // 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);
476
          sig = new ProxySignature(Object.class.getMethod("hashCode"));
Tom Tromey committed
477
          coreMethods.put(sig, sig);
478
          sig = new ProxySignature(Object.class.getMethod("toString"));
Tom Tromey committed
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
          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.
     *
589
     * @param other object to compare to
Tom Tromey committed
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
     * @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)
   */
Tom Tromey committed
614
  static final class ProxyData
Tom Tromey committed
615 616 617 618 619
  {
    /**
     * The package this class is in <b>including the trailing dot</b>
     * or an empty string for the unnamed (aka default) package.
     */
620
    String pack = "";
Tom Tromey committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718

    /**
     * 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;

    /**
     * The id of this proxy class
     */
    final int id = count++;

    /**
     * Construct a ProxyData with uninitialized data members.
     */
    ProxyData()
    {
    }

    /**
     * Return the name of a package (including the trailing dot)
     * given the name of a class.
     * Returns an empty string if no package.  We use this in preference to
     * using Class.getPackage() to avoid problems with ClassLoaders
     * that don't set the package.
     */
    private static String getPackage(Class k)
    {
      String name = k.getName();
      int idx = name.lastIndexOf('.');
      return name.substring(0, idx + 1);
    }

    /**
     * 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)
              {
719
                String p = getPackage(inter);
Tom Tromey committed
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
                if (! data.pack.equals(p))
                  throw new IllegalArgumentException("non-public interfaces "
                                                     + "from different "
                                                     + "packages");
              }
            else
              {
                in_package = true;
                data.pack = getPackage(inter);
              }
          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)
            {
738 739 740 741 742 743
              if (isCoreObjectMethod(methods[j]))
                {
                  // In the case of an attempt to redefine a public non-final
                  // method of Object, we must skip it
                  continue;
                }
Tom Tromey committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
              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;
    }
764 765 766 767 768 769 770 771

    /**
     * Checks whether the method is similar to a public non-final method of
     * Object or not (i.e. with the same name and parameter types). Note that we
     * can't rely, directly or indirectly (via Collection.contains) on
     * Method.equals as it would also check the declaring class, what we do not
     * want. We only want to check that the given method have the same signature
     * as a core method (same name and parameter types)
772
     *
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
     * @param method the method to check
     * @return whether the method has the same name and parameter types as
     *         Object.equals, Object.hashCode or Object.toString
     * @see java.lang.Object#equals(Object)
     * @see java.lang.Object#hashCode()
     * @see java.lang.Object#toString()
     */
    private static boolean isCoreObjectMethod(Method method)
    {
      String methodName = method.getName();
      if (methodName.equals("equals"))
        {
          return Arrays.equals(method.getParameterTypes(),
                               new Class[] { Object.class });
        }
      if (methodName.equals("hashCode"))
        {
          return method.getParameterTypes().length == 0;
        }
      if (methodName.equals("toString"))
        {
          return method.getParameterTypes().length == 0;
        }
      return false;
    }
798

Tom Tromey committed
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
  } // 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 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 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 + "$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;
1038
      CPStringBuilder signature = new CPStringBuilder("(");
Tom Tromey committed
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
      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
     */
    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
        {
          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);
          // We can bypass the security check of setAccessible(true), since
1263
          // we're in the same package.
Tom Tromey committed
1264 1265
          m.flag = true;

1266 1267
          Object[] args = {loader, qualName, bytecode, Integer.valueOf(0),
                           Integer.valueOf(bytecode.length),
Tom Tromey committed
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
                           Object.class.getProtectionDomain() };
          Class clazz = (Class) m.invoke(null, args);

          // Finally, initialize the m field of the proxy class, before
          // returning it.
          Field f = clazz.getDeclaredField("m");
          f.flag = true;
          // we can share the array, because it is not publicized
          f.set(null, methods);

          return clazz;
        }
      catch (Exception 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;

1497
      final CPStringBuilder sb = new CPStringBuilder(str);
Tom Tromey committed
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537
      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");
1538
          i = Integer.valueOf(size);
Tom Tromey committed
1539 1540 1541 1542 1543 1544 1545
          poolEntries.put(sequence, i);
          pool.append(sequence);
        }
      return (char) i.intValue();
    }
  } // class ClassFactory
}