rt/emul/compact/src/main/java/org/apidesign/bck2brwsr/emul/reflect/ProxyImpl.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 05 May 2014 12:58:10 +0200
branchclosure
changeset 1529 9afa6856382c
parent 1378 9ee9b36adb53
child 1563 31bd01cfe50d
permissions -rw-r--r--
Adopting proxy implementation to extension mode
jaroslav@601
     1
/*
jaroslav@601
     2
 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
jaroslav@601
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@601
     4
 *
jaroslav@601
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@601
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@601
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@601
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@601
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@601
    10
 *
jaroslav@601
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@601
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@601
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@601
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@601
    15
 * accompanied this code).
jaroslav@601
    16
 *
jaroslav@601
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@601
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@601
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@601
    20
 *
jaroslav@601
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@601
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@601
    23
 * questions.
jaroslav@601
    24
 */
jaroslav@601
    25
jaroslav@1378
    26
package org.apidesign.bck2brwsr.emul.reflect;
jaroslav@601
    27
jaroslav@1378
    28
import java.io.ByteArrayOutputStream;
jaroslav@1378
    29
import java.io.DataOutputStream;
jaroslav@1378
    30
import java.io.IOException;
jaroslav@1378
    31
import java.io.OutputStream;
jaroslav@1378
    32
import java.lang.ref.Reference;
jaroslav@1378
    33
import java.lang.ref.WeakReference;
jaroslav@1378
    34
import java.lang.reflect.Array;
jaroslav@1378
    35
import java.lang.reflect.Constructor;
jaroslav@1378
    36
import java.lang.reflect.InvocationHandler;
jaroslav@1378
    37
import java.lang.reflect.InvocationTargetException;
jaroslav@1378
    38
import java.lang.reflect.Method;
jaroslav@1378
    39
import java.lang.reflect.Modifier;
jaroslav@1378
    40
import java.util.ArrayList;
jaroslav@1378
    41
import java.util.Arrays;
jaroslav@1378
    42
import java.util.Collections;
jaroslav@1378
    43
import java.util.HashMap;
jaroslav@1378
    44
import java.util.HashSet;
jaroslav@1378
    45
import java.util.LinkedList;
jaroslav@1378
    46
import java.util.Map;
jaroslav@1378
    47
import java.util.Set;
jaroslav@1378
    48
import java.util.List;
jaroslav@1378
    49
import java.util.ListIterator;
jaroslav@1378
    50
import java.util.WeakHashMap;
jaroslav@1378
    51
import org.apidesign.bck2brwsr.core.JavaScriptBody;
jaroslav@1378
    52
import org.apidesign.bck2brwsr.emul.reflect.MethodImpl;
jaroslav@1529
    53
import org.apidesign.vm4brwsr.api.VM;
jaroslav@601
    54
jaroslav@601
    55
/**
jaroslav@601
    56
 * {@code Proxy} provides static methods for creating dynamic proxy
jaroslav@601
    57
 * classes and instances, and it is also the superclass of all
jaroslav@601
    58
 * dynamic proxy classes created by those methods.
jaroslav@601
    59
 *
jaroslav@601
    60
 * <p>To create a proxy for some interface {@code Foo}:
jaroslav@601
    61
 * <pre>
jaroslav@601
    62
 *     InvocationHandler handler = new MyInvocationHandler(...);
jaroslav@601
    63
 *     Class proxyClass = Proxy.getProxyClass(
jaroslav@601
    64
 *         Foo.class.getClassLoader(), new Class[] { Foo.class });
jaroslav@601
    65
 *     Foo f = (Foo) proxyClass.
jaroslav@601
    66
 *         getConstructor(new Class[] { InvocationHandler.class }).
jaroslav@601
    67
 *         newInstance(new Object[] { handler });
jaroslav@601
    68
 * </pre>
jaroslav@601
    69
 * or more simply:
jaroslav@601
    70
 * <pre>
jaroslav@601
    71
 *     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
jaroslav@601
    72
 *                                          new Class[] { Foo.class },
jaroslav@601
    73
 *                                          handler);
jaroslav@601
    74
 * </pre>
jaroslav@601
    75
 *
jaroslav@601
    76
 * <p>A <i>dynamic proxy class</i> (simply referred to as a <i>proxy
jaroslav@601
    77
 * class</i> below) is a class that implements a list of interfaces
jaroslav@601
    78
 * specified at runtime when the class is created, with behavior as
jaroslav@601
    79
 * described below.
jaroslav@601
    80
 *
jaroslav@601
    81
 * A <i>proxy interface</i> is such an interface that is implemented
jaroslav@601
    82
 * by a proxy class.
jaroslav@601
    83
 *
jaroslav@601
    84
 * A <i>proxy instance</i> is an instance of a proxy class.
jaroslav@601
    85
 *
jaroslav@601
    86
 * Each proxy instance has an associated <i>invocation handler</i>
jaroslav@601
    87
 * object, which implements the interface {@link InvocationHandler}.
jaroslav@601
    88
 * A method invocation on a proxy instance through one of its proxy
jaroslav@601
    89
 * interfaces will be dispatched to the {@link InvocationHandler#invoke
jaroslav@601
    90
 * invoke} method of the instance's invocation handler, passing the proxy
jaroslav@601
    91
 * instance, a {@code java.lang.reflect.Method} object identifying
jaroslav@601
    92
 * the method that was invoked, and an array of type {@code Object}
jaroslav@601
    93
 * containing the arguments.  The invocation handler processes the
jaroslav@601
    94
 * encoded method invocation as appropriate and the result that it
jaroslav@601
    95
 * returns will be returned as the result of the method invocation on
jaroslav@601
    96
 * the proxy instance.
jaroslav@601
    97
 *
jaroslav@601
    98
 * <p>A proxy class has the following properties:
jaroslav@601
    99
 *
jaroslav@601
   100
 * <ul>
jaroslav@601
   101
 * <li>Proxy classes are public, final, and not abstract.
jaroslav@601
   102
 *
jaroslav@601
   103
 * <li>The unqualified name of a proxy class is unspecified.  The space
jaroslav@601
   104
 * of class names that begin with the string {@code "$Proxy"}
jaroslav@601
   105
 * should be, however, reserved for proxy classes.
jaroslav@601
   106
 *
jaroslav@601
   107
 * <li>A proxy class extends {@code java.lang.reflect.Proxy}.
jaroslav@601
   108
 *
jaroslav@601
   109
 * <li>A proxy class implements exactly the interfaces specified at its
jaroslav@601
   110
 * creation, in the same order.
jaroslav@601
   111
 *
jaroslav@601
   112
 * <li>If a proxy class implements a non-public interface, then it will
jaroslav@601
   113
 * be defined in the same package as that interface.  Otherwise, the
jaroslav@601
   114
 * package of a proxy class is also unspecified.  Note that package
jaroslav@601
   115
 * sealing will not prevent a proxy class from being successfully defined
jaroslav@601
   116
 * in a particular package at runtime, and neither will classes already
jaroslav@601
   117
 * defined by the same class loader and the same package with particular
jaroslav@601
   118
 * signers.
jaroslav@601
   119
 *
jaroslav@601
   120
 * <li>Since a proxy class implements all of the interfaces specified at
jaroslav@601
   121
 * its creation, invoking {@code getInterfaces} on its
jaroslav@601
   122
 * {@code Class} object will return an array containing the same
jaroslav@601
   123
 * list of interfaces (in the order specified at its creation), invoking
jaroslav@601
   124
 * {@code getMethods} on its {@code Class} object will return
jaroslav@601
   125
 * an array of {@code Method} objects that include all of the
jaroslav@601
   126
 * methods in those interfaces, and invoking {@code getMethod} will
jaroslav@601
   127
 * find methods in the proxy interfaces as would be expected.
jaroslav@601
   128
 *
jaroslav@601
   129
 * <li>The {@link Proxy#isProxyClass Proxy.isProxyClass} method will
jaroslav@601
   130
 * return true if it is passed a proxy class-- a class returned by
jaroslav@601
   131
 * {@code Proxy.getProxyClass} or the class of an object returned by
jaroslav@601
   132
 * {@code Proxy.newProxyInstance}-- and false otherwise.
jaroslav@601
   133
 *
jaroslav@601
   134
 * <li>The {@code java.security.ProtectionDomain} of a proxy class
jaroslav@601
   135
 * is the same as that of system classes loaded by the bootstrap class
jaroslav@601
   136
 * loader, such as {@code java.lang.Object}, because the code for a
jaroslav@601
   137
 * proxy class is generated by trusted system code.  This protection
jaroslav@601
   138
 * domain will typically be granted
jaroslav@601
   139
 * {@code java.security.AllPermission}.
jaroslav@601
   140
 *
jaroslav@601
   141
 * <li>Each proxy class has one public constructor that takes one argument,
jaroslav@601
   142
 * an implementation of the interface {@link InvocationHandler}, to set
jaroslav@601
   143
 * the invocation handler for a proxy instance.  Rather than having to use
jaroslav@601
   144
 * the reflection API to access the public constructor, a proxy instance
jaroslav@601
   145
 * can be also be created by calling the {@link Proxy#newProxyInstance
jaroslav@601
   146
 * Proxy.newProxyInstance} method, which combines the actions of calling
jaroslav@601
   147
 * {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the
jaroslav@601
   148
 * constructor with an invocation handler.
jaroslav@601
   149
 * </ul>
jaroslav@601
   150
 *
jaroslav@601
   151
 * <p>A proxy instance has the following properties:
jaroslav@601
   152
 *
jaroslav@601
   153
 * <ul>
jaroslav@601
   154
 * <li>Given a proxy instance {@code proxy} and one of the
jaroslav@601
   155
 * interfaces implemented by its proxy class {@code Foo}, the
jaroslav@601
   156
 * following expression will return true:
jaroslav@601
   157
 * <pre>
jaroslav@601
   158
 *     {@code proxy instanceof Foo}
jaroslav@601
   159
 * </pre>
jaroslav@601
   160
 * and the following cast operation will succeed (rather than throwing
jaroslav@601
   161
 * a {@code ClassCastException}):
jaroslav@601
   162
 * <pre>
jaroslav@601
   163
 *     {@code (Foo) proxy}
jaroslav@601
   164
 * </pre>
jaroslav@601
   165
 *
jaroslav@601
   166
 * <li>Each proxy instance has an associated invocation handler, the one
jaroslav@601
   167
 * that was passed to its constructor.  The static
jaroslav@601
   168
 * {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method
jaroslav@601
   169
 * will return the invocation handler associated with the proxy instance
jaroslav@601
   170
 * passed as its argument.
jaroslav@601
   171
 *
jaroslav@601
   172
 * <li>An interface method invocation on a proxy instance will be
jaroslav@601
   173
 * encoded and dispatched to the invocation handler's {@link
jaroslav@601
   174
 * InvocationHandler#invoke invoke} method as described in the
jaroslav@601
   175
 * documentation for that method.
jaroslav@601
   176
 *
jaroslav@601
   177
 * <li>An invocation of the {@code hashCode},
jaroslav@601
   178
 * {@code equals}, or {@code toString} methods declared in
jaroslav@601
   179
 * {@code java.lang.Object} on a proxy instance will be encoded and
jaroslav@601
   180
 * dispatched to the invocation handler's {@code invoke} method in
jaroslav@601
   181
 * the same manner as interface method invocations are encoded and
jaroslav@601
   182
 * dispatched, as described above.  The declaring class of the
jaroslav@601
   183
 * {@code Method} object passed to {@code invoke} will be
jaroslav@601
   184
 * {@code java.lang.Object}.  Other public methods of a proxy
jaroslav@601
   185
 * instance inherited from {@code java.lang.Object} are not
jaroslav@601
   186
 * overridden by a proxy class, so invocations of those methods behave
jaroslav@601
   187
 * like they do for instances of {@code java.lang.Object}.
jaroslav@601
   188
 * </ul>
jaroslav@601
   189
 *
jaroslav@601
   190
 * <h3>Methods Duplicated in Multiple Proxy Interfaces</h3>
jaroslav@601
   191
 *
jaroslav@601
   192
 * <p>When two or more interfaces of a proxy class contain a method with
jaroslav@601
   193
 * the same name and parameter signature, the order of the proxy class's
jaroslav@601
   194
 * interfaces becomes significant.  When such a <i>duplicate method</i>
jaroslav@601
   195
 * is invoked on a proxy instance, the {@code Method} object passed
jaroslav@601
   196
 * to the invocation handler will not necessarily be the one whose
jaroslav@601
   197
 * declaring class is assignable from the reference type of the interface
jaroslav@601
   198
 * that the proxy's method was invoked through.  This limitation exists
jaroslav@601
   199
 * because the corresponding method implementation in the generated proxy
jaroslav@601
   200
 * class cannot determine which interface it was invoked through.
jaroslav@601
   201
 * Therefore, when a duplicate method is invoked on a proxy instance,
jaroslav@601
   202
 * the {@code Method} object for the method in the foremost interface
jaroslav@601
   203
 * that contains the method (either directly or inherited through a
jaroslav@601
   204
 * superinterface) in the proxy class's list of interfaces is passed to
jaroslav@601
   205
 * the invocation handler's {@code invoke} method, regardless of the
jaroslav@601
   206
 * reference type through which the method invocation occurred.
jaroslav@601
   207
 *
jaroslav@601
   208
 * <p>If a proxy interface contains a method with the same name and
jaroslav@601
   209
 * parameter signature as the {@code hashCode}, {@code equals},
jaroslav@601
   210
 * or {@code toString} methods of {@code java.lang.Object},
jaroslav@601
   211
 * when such a method is invoked on a proxy instance, the
jaroslav@601
   212
 * {@code Method} object passed to the invocation handler will have
jaroslav@601
   213
 * {@code java.lang.Object} as its declaring class.  In other words,
jaroslav@601
   214
 * the public, non-final methods of {@code java.lang.Object}
jaroslav@601
   215
 * logically precede all of the proxy interfaces for the determination of
jaroslav@601
   216
 * which {@code Method} object to pass to the invocation handler.
jaroslav@601
   217
 *
jaroslav@601
   218
 * <p>Note also that when a duplicate method is dispatched to an
jaroslav@601
   219
 * invocation handler, the {@code invoke} method may only throw
jaroslav@601
   220
 * checked exception types that are assignable to one of the exception
jaroslav@601
   221
 * types in the {@code throws} clause of the method in <i>all</i> of
jaroslav@601
   222
 * the proxy interfaces that it can be invoked through.  If the
jaroslav@601
   223
 * {@code invoke} method throws a checked exception that is not
jaroslav@601
   224
 * assignable to any of the exception types declared by the method in one
jaroslav@601
   225
 * of the proxy interfaces that it can be invoked through, then an
jaroslav@601
   226
 * unchecked {@code UndeclaredThrowableException} will be thrown by
jaroslav@601
   227
 * the invocation on the proxy instance.  This restriction means that not
jaroslav@601
   228
 * all of the exception types returned by invoking
jaroslav@601
   229
 * {@code getExceptionTypes} on the {@code Method} object
jaroslav@601
   230
 * passed to the {@code invoke} method can necessarily be thrown
jaroslav@601
   231
 * successfully by the {@code invoke} method.
jaroslav@601
   232
 *
jaroslav@601
   233
 * @author      Peter Jones
jaroslav@601
   234
 * @see         InvocationHandler
jaroslav@601
   235
 * @since       1.3
jaroslav@601
   236
 */
jaroslav@1378
   237
public final class ProxyImpl implements java.io.Serializable {
jaroslav@601
   238
jaroslav@601
   239
    private static final long serialVersionUID = -2222568056686623797L;
jaroslav@601
   240
jaroslav@1378
   241
    /** prefix for all proxy class names */
jaroslav@1378
   242
    private final static String proxyClassNamePrefix = "$Proxy";
jaroslav@601
   243
jaroslav@1378
   244
    /** parameter types of a proxy class constructor */
jaroslav@1378
   245
    private final static Class[] constructorParams =
jaroslav@1378
   246
        { InvocationHandler.class };
jaroslav@1378
   247
jaroslav@1378
   248
    /** maps a class loader to the proxy class cache for that loader */
jaroslav@1378
   249
    private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
jaroslav@1378
   250
        = new WeakHashMap<>();
jaroslav@1378
   251
jaroslav@1378
   252
    /** marks that a particular proxy class is currently being generated */
jaroslav@1378
   253
    private static Object pendingGenerationMarker = new Object();
jaroslav@1378
   254
jaroslav@1378
   255
    /** next number to use for generation of unique proxy class names */
jaroslav@1378
   256
    private static long nextUniqueNumber = 0;
jaroslav@1378
   257
    private static Object nextUniqueNumberLock = new Object();
jaroslav@1378
   258
jaroslav@1378
   259
    /** set of all generated proxy classes, for isProxyClass implementation */
jaroslav@1378
   260
    private static Map<Class<?>, Void> proxyClasses =
jaroslav@1378
   261
        Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
jaroslav@601
   262
jaroslav@601
   263
    /**
jaroslav@601
   264
     * the invocation handler for this proxy instance.
jaroslav@601
   265
     * @serial
jaroslav@601
   266
     */
jaroslav@601
   267
    protected InvocationHandler h;
jaroslav@601
   268
jaroslav@601
   269
    /**
jaroslav@601
   270
     * Prohibits instantiation.
jaroslav@601
   271
     */
jaroslav@1378
   272
    private ProxyImpl() {
jaroslav@601
   273
    }
jaroslav@601
   274
jaroslav@601
   275
    /**
jaroslav@601
   276
     * Constructs a new {@code Proxy} instance from a subclass
jaroslav@601
   277
     * (typically, a dynamic proxy class) with the specified value
jaroslav@601
   278
     * for its invocation handler.
jaroslav@601
   279
     *
jaroslav@601
   280
     * @param   h the invocation handler for this proxy instance
jaroslav@601
   281
     */
jaroslav@1378
   282
    protected ProxyImpl(InvocationHandler h) {
jaroslav@601
   283
        this.h = h;
jaroslav@601
   284
    }
jaroslav@601
   285
jaroslav@601
   286
    /**
jaroslav@601
   287
     * Returns the {@code java.lang.Class} object for a proxy class
jaroslav@601
   288
     * given a class loader and an array of interfaces.  The proxy class
jaroslav@601
   289
     * will be defined by the specified class loader and will implement
jaroslav@601
   290
     * all of the supplied interfaces.  If a proxy class for the same
jaroslav@601
   291
     * permutation of interfaces has already been defined by the class
jaroslav@601
   292
     * loader, then the existing proxy class will be returned; otherwise,
jaroslav@601
   293
     * a proxy class for those interfaces will be generated dynamically
jaroslav@601
   294
     * and defined by the class loader.
jaroslav@601
   295
     *
jaroslav@601
   296
     * <p>There are several restrictions on the parameters that may be
jaroslav@601
   297
     * passed to {@code Proxy.getProxyClass}:
jaroslav@601
   298
     *
jaroslav@601
   299
     * <ul>
jaroslav@601
   300
     * <li>All of the {@code Class} objects in the
jaroslav@601
   301
     * {@code interfaces} array must represent interfaces, not
jaroslav@601
   302
     * classes or primitive types.
jaroslav@601
   303
     *
jaroslav@601
   304
     * <li>No two elements in the {@code interfaces} array may
jaroslav@601
   305
     * refer to identical {@code Class} objects.
jaroslav@601
   306
     *
jaroslav@601
   307
     * <li>All of the interface types must be visible by name through the
jaroslav@601
   308
     * specified class loader.  In other words, for class loader
jaroslav@601
   309
     * {@code cl} and every interface {@code i}, the following
jaroslav@601
   310
     * expression must be true:
jaroslav@601
   311
     * <pre>
jaroslav@601
   312
     *     Class.forName(i.getName(), false, cl) == i
jaroslav@601
   313
     * </pre>
jaroslav@601
   314
     *
jaroslav@601
   315
     * <li>All non-public interfaces must be in the same package;
jaroslav@601
   316
     * otherwise, it would not be possible for the proxy class to
jaroslav@601
   317
     * implement all of the interfaces, regardless of what package it is
jaroslav@601
   318
     * defined in.
jaroslav@601
   319
     *
jaroslav@601
   320
     * <li>For any set of member methods of the specified interfaces
jaroslav@601
   321
     * that have the same signature:
jaroslav@601
   322
     * <ul>
jaroslav@601
   323
     * <li>If the return type of any of the methods is a primitive
jaroslav@601
   324
     * type or void, then all of the methods must have that same
jaroslav@601
   325
     * return type.
jaroslav@601
   326
     * <li>Otherwise, one of the methods must have a return type that
jaroslav@601
   327
     * is assignable to all of the return types of the rest of the
jaroslav@601
   328
     * methods.
jaroslav@601
   329
     * </ul>
jaroslav@601
   330
     *
jaroslav@601
   331
     * <li>The resulting proxy class must not exceed any limits imposed
jaroslav@601
   332
     * on classes by the virtual machine.  For example, the VM may limit
jaroslav@601
   333
     * the number of interfaces that a class may implement to 65535; in
jaroslav@601
   334
     * that case, the size of the {@code interfaces} array must not
jaroslav@601
   335
     * exceed 65535.
jaroslav@601
   336
     * </ul>
jaroslav@601
   337
     *
jaroslav@601
   338
     * <p>If any of these restrictions are violated,
jaroslav@601
   339
     * {@code Proxy.getProxyClass} will throw an
jaroslav@601
   340
     * {@code IllegalArgumentException}.  If the {@code interfaces}
jaroslav@601
   341
     * array argument or any of its elements are {@code null}, a
jaroslav@601
   342
     * {@code NullPointerException} will be thrown.
jaroslav@601
   343
     *
jaroslav@601
   344
     * <p>Note that the order of the specified proxy interfaces is
jaroslav@601
   345
     * significant: two requests for a proxy class with the same combination
jaroslav@601
   346
     * of interfaces but in a different order will result in two distinct
jaroslav@601
   347
     * proxy classes.
jaroslav@601
   348
     *
jaroslav@601
   349
     * @param   loader the class loader to define the proxy class
jaroslav@601
   350
     * @param   interfaces the list of interfaces for the proxy class
jaroslav@601
   351
     *          to implement
jaroslav@601
   352
     * @return  a proxy class that is defined in the specified class loader
jaroslav@601
   353
     *          and that implements the specified interfaces
jaroslav@601
   354
     * @throws  IllegalArgumentException if any of the restrictions on the
jaroslav@601
   355
     *          parameters that may be passed to {@code getProxyClass}
jaroslav@601
   356
     *          are violated
jaroslav@601
   357
     * @throws  NullPointerException if the {@code interfaces} array
jaroslav@601
   358
     *          argument or any of its elements are {@code null}
jaroslav@601
   359
     */
jaroslav@601
   360
    public static Class<?> getProxyClass(ClassLoader loader,
jaroslav@601
   361
                                         Class<?>... interfaces)
jaroslav@601
   362
        throws IllegalArgumentException
jaroslav@601
   363
    {
jaroslav@1378
   364
        if (interfaces.length > 65535) {
jaroslav@1378
   365
            throw new IllegalArgumentException("interface limit exceeded");
jaroslav@1378
   366
        }
jaroslav@1378
   367
jaroslav@1378
   368
        Class<?> proxyClass = null;
jaroslav@1378
   369
jaroslav@1378
   370
        /* collect interface names to use as key for proxy class cache */
jaroslav@1378
   371
        String[] interfaceNames = new String[interfaces.length];
jaroslav@1378
   372
jaroslav@1378
   373
        // for detecting duplicates
jaroslav@1378
   374
        Set<Class<?>> interfaceSet = new HashSet<>();
jaroslav@1378
   375
jaroslav@1378
   376
        for (int i = 0; i < interfaces.length; i++) {
jaroslav@1378
   377
            /*
jaroslav@1378
   378
             * Verify that the class loader resolves the name of this
jaroslav@1378
   379
             * interface to the same Class object.
jaroslav@1378
   380
             */
jaroslav@1378
   381
            String interfaceName = interfaces[i].getName();
jaroslav@1378
   382
            Class<?> interfaceClass = null;
jaroslav@1378
   383
            try {
jaroslav@1378
   384
                interfaceClass = Class.forName(interfaceName, false, loader);
jaroslav@1378
   385
            } catch (ClassNotFoundException e) {
jaroslav@1378
   386
            }
jaroslav@1378
   387
            if (interfaceClass != interfaces[i]) {
jaroslav@1378
   388
                throw new IllegalArgumentException(
jaroslav@1378
   389
                    interfaces[i] + " is not visible from class loader");
jaroslav@1378
   390
            }
jaroslav@1378
   391
jaroslav@1378
   392
            /*
jaroslav@1378
   393
             * Verify that the Class object actually represents an
jaroslav@1378
   394
             * interface.
jaroslav@1378
   395
             */
jaroslav@1378
   396
            if (!interfaceClass.isInterface()) {
jaroslav@1378
   397
                throw new IllegalArgumentException(
jaroslav@1378
   398
                    interfaceClass.getName() + " is not an interface");
jaroslav@1378
   399
            }
jaroslav@1378
   400
jaroslav@1378
   401
            /*
jaroslav@1378
   402
             * Verify that this interface is not a duplicate.
jaroslav@1378
   403
             */
jaroslav@1378
   404
            if (interfaceSet.contains(interfaceClass)) {
jaroslav@1378
   405
                throw new IllegalArgumentException(
jaroslav@1378
   406
                    "repeated interface: " + interfaceClass.getName());
jaroslav@1378
   407
            }
jaroslav@1378
   408
            interfaceSet.add(interfaceClass);
jaroslav@1378
   409
jaroslav@1378
   410
            interfaceNames[i] = interfaceName;
jaroslav@1378
   411
        }
jaroslav@1378
   412
jaroslav@1378
   413
        /*
jaroslav@1378
   414
         * Using string representations of the proxy interfaces as
jaroslav@1378
   415
         * keys in the proxy class cache (instead of their Class
jaroslav@1378
   416
         * objects) is sufficient because we require the proxy
jaroslav@1378
   417
         * interfaces to be resolvable by name through the supplied
jaroslav@1378
   418
         * class loader, and it has the advantage that using a string
jaroslav@1378
   419
         * representation of a class makes for an implicit weak
jaroslav@1378
   420
         * reference to the class.
jaroslav@1378
   421
         */
jaroslav@1378
   422
        List<String> key = Arrays.asList(interfaceNames);
jaroslav@1378
   423
jaroslav@1378
   424
        /*
jaroslav@1378
   425
         * Find or create the proxy class cache for the class loader.
jaroslav@1378
   426
         */
jaroslav@1378
   427
        Map<List<String>, Object> cache;
jaroslav@1378
   428
        synchronized (loaderToCache) {
jaroslav@1378
   429
            cache = loaderToCache.get(loader);
jaroslav@1378
   430
            if (cache == null) {
jaroslav@1378
   431
                cache = new HashMap<>();
jaroslav@1378
   432
                loaderToCache.put(loader, cache);
jaroslav@1378
   433
            }
jaroslav@1378
   434
            /*
jaroslav@1378
   435
             * This mapping will remain valid for the duration of this
jaroslav@1378
   436
             * method, without further synchronization, because the mapping
jaroslav@1378
   437
             * will only be removed if the class loader becomes unreachable.
jaroslav@1378
   438
             */
jaroslav@1378
   439
        }
jaroslav@1378
   440
jaroslav@1378
   441
        /*
jaroslav@1378
   442
         * Look up the list of interfaces in the proxy class cache using
jaroslav@1378
   443
         * the key.  This lookup will result in one of three possible
jaroslav@1378
   444
         * kinds of values:
jaroslav@1378
   445
         *     null, if there is currently no proxy class for the list of
jaroslav@1378
   446
         *         interfaces in the class loader,
jaroslav@1378
   447
         *     the pendingGenerationMarker object, if a proxy class for the
jaroslav@1378
   448
         *         list of interfaces is currently being generated,
jaroslav@1378
   449
         *     or a weak reference to a Class object, if a proxy class for
jaroslav@1378
   450
         *         the list of interfaces has already been generated.
jaroslav@1378
   451
         */
jaroslav@1378
   452
        synchronized (cache) {
jaroslav@1378
   453
            /*
jaroslav@1378
   454
             * Note that we need not worry about reaping the cache for
jaroslav@1378
   455
             * entries with cleared weak references because if a proxy class
jaroslav@1378
   456
             * has been garbage collected, its class loader will have been
jaroslav@1378
   457
             * garbage collected as well, so the entire cache will be reaped
jaroslav@1378
   458
             * from the loaderToCache map.
jaroslav@1378
   459
             */
jaroslav@1378
   460
            do {
jaroslav@1378
   461
                Object value = cache.get(key);
jaroslav@1378
   462
                if (value instanceof Reference) {
jaroslav@1378
   463
                    proxyClass = (Class<?>) ((Reference) value).get();
jaroslav@1378
   464
                }
jaroslav@1378
   465
                if (proxyClass != null) {
jaroslav@1378
   466
                    // proxy class already generated: return it
jaroslav@1378
   467
                    return proxyClass;
jaroslav@1378
   468
                } else if (value == pendingGenerationMarker) {
jaroslav@1378
   469
                    // proxy class being generated: wait for it
jaroslav@1378
   470
                    try {
jaroslav@1378
   471
                        cache.wait();
jaroslav@1378
   472
                    } catch (InterruptedException e) {
jaroslav@1378
   473
                        /*
jaroslav@1378
   474
                         * The class generation that we are waiting for should
jaroslav@1378
   475
                         * take a small, bounded time, so we can safely ignore
jaroslav@1378
   476
                         * thread interrupts here.
jaroslav@1378
   477
                         */
jaroslav@1378
   478
                    }
jaroslav@1378
   479
                    continue;
jaroslav@1378
   480
                } else {
jaroslav@1378
   481
                    /*
jaroslav@1378
   482
                     * No proxy class for this list of interfaces has been
jaroslav@1378
   483
                     * generated or is being generated, so we will go and
jaroslav@1378
   484
                     * generate it now.  Mark it as pending generation.
jaroslav@1378
   485
                     */
jaroslav@1378
   486
                    cache.put(key, pendingGenerationMarker);
jaroslav@1378
   487
                    break;
jaroslav@1378
   488
                }
jaroslav@1378
   489
            } while (true);
jaroslav@1378
   490
        }
jaroslav@1378
   491
jaroslav@1378
   492
        try {
jaroslav@1378
   493
            String proxyPkg = null;     // package to define proxy class in
jaroslav@1378
   494
jaroslav@1378
   495
            /*
jaroslav@1378
   496
             * Record the package of a non-public proxy interface so that the
jaroslav@1378
   497
             * proxy class will be defined in the same package.  Verify that
jaroslav@1378
   498
             * all non-public proxy interfaces are in the same package.
jaroslav@1378
   499
             */
jaroslav@1378
   500
            for (int i = 0; i < interfaces.length; i++) {
jaroslav@1378
   501
                int flags = interfaces[i].getModifiers();
jaroslav@1378
   502
                if (!Modifier.isPublic(flags)) {
jaroslav@1378
   503
                    String name = interfaces[i].getName();
jaroslav@1378
   504
                    int n = name.lastIndexOf('.');
jaroslav@1378
   505
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
jaroslav@1378
   506
                    if (proxyPkg == null) {
jaroslav@1378
   507
                        proxyPkg = pkg;
jaroslav@1378
   508
                    } else if (!pkg.equals(proxyPkg)) {
jaroslav@1378
   509
                        throw new IllegalArgumentException(
jaroslav@1378
   510
                            "non-public interfaces from different packages");
jaroslav@1378
   511
                    }
jaroslav@1378
   512
                }
jaroslav@1378
   513
            }
jaroslav@1378
   514
jaroslav@1378
   515
            if (proxyPkg == null) {     // if no non-public proxy interfaces,
jaroslav@1378
   516
                proxyPkg = "";          // use the unnamed package
jaroslav@1378
   517
            }
jaroslav@1378
   518
jaroslav@1378
   519
            {
jaroslav@1378
   520
                /*
jaroslav@1378
   521
                 * Choose a name for the proxy class to generate.
jaroslav@1378
   522
                 */
jaroslav@1378
   523
                long num;
jaroslav@1378
   524
                synchronized (nextUniqueNumberLock) {
jaroslav@1378
   525
                    num = nextUniqueNumber++;
jaroslav@1378
   526
                }
jaroslav@1378
   527
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
jaroslav@1378
   528
                /*
jaroslav@1378
   529
                 * Verify that the class loader hasn't already
jaroslav@1378
   530
                 * defined a class with the chosen name.
jaroslav@1378
   531
                 */
jaroslav@1378
   532
jaroslav@1378
   533
                /*
jaroslav@1378
   534
                 * Generate the specified proxy class.
jaroslav@1378
   535
                 */
jaroslav@1378
   536
                Generator gen = new Generator(proxyName, interfaces);
jaroslav@1378
   537
                final byte[] proxyClassFile = gen.generateClassFile();
jaroslav@1378
   538
                try {
jaroslav@1378
   539
                    proxyClass = defineClass0(loader, proxyName,
jaroslav@1378
   540
                        proxyClassFile);
jaroslav@1378
   541
                } catch (ClassFormatError e) {
jaroslav@1378
   542
                    /*
jaroslav@1378
   543
                     * A ClassFormatError here means that (barring bugs in the
jaroslav@1378
   544
                     * proxy class generation code) there was some other
jaroslav@1378
   545
                     * invalid aspect of the arguments supplied to the proxy
jaroslav@1378
   546
                     * class creation (such as virtual machine limitations
jaroslav@1378
   547
                     * exceeded).
jaroslav@1378
   548
                     */
jaroslav@1378
   549
                    throw new IllegalArgumentException(e.toString());
jaroslav@1378
   550
                }
jaroslav@1378
   551
                gen.fillInMethods(proxyClass);
jaroslav@1378
   552
            }
jaroslav@1378
   553
            // add to set of all generated proxy classes, for isProxyClass
jaroslav@1378
   554
            proxyClasses.put(proxyClass, null);
jaroslav@1378
   555
jaroslav@1378
   556
        } finally {
jaroslav@1378
   557
            /*
jaroslav@1378
   558
             * We must clean up the "pending generation" state of the proxy
jaroslav@1378
   559
             * class cache entry somehow.  If a proxy class was successfully
jaroslav@1378
   560
             * generated, store it in the cache (with a weak reference);
jaroslav@1378
   561
             * otherwise, remove the reserved entry.  In all cases, notify
jaroslav@1378
   562
             * all waiters on reserved entries in this cache.
jaroslav@1378
   563
             */
jaroslav@1378
   564
            synchronized (cache) {
jaroslav@1378
   565
                if (proxyClass != null) {
jaroslav@1378
   566
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
jaroslav@1378
   567
                } else {
jaroslav@1378
   568
                    cache.remove(key);
jaroslav@1378
   569
                }
jaroslav@1378
   570
                cache.notifyAll();
jaroslav@1378
   571
            }
jaroslav@1378
   572
        }
jaroslav@1378
   573
        return proxyClass;
jaroslav@601
   574
    }
jaroslav@601
   575
jaroslav@601
   576
    /**
jaroslav@601
   577
     * Returns an instance of a proxy class for the specified interfaces
jaroslav@601
   578
     * that dispatches method invocations to the specified invocation
jaroslav@601
   579
     * handler.  This method is equivalent to:
jaroslav@601
   580
     * <pre>
jaroslav@601
   581
     *     Proxy.getProxyClass(loader, interfaces).
jaroslav@601
   582
     *         getConstructor(new Class[] { InvocationHandler.class }).
jaroslav@601
   583
     *         newInstance(new Object[] { handler });
jaroslav@601
   584
     * </pre>
jaroslav@601
   585
     *
jaroslav@601
   586
     * <p>{@code Proxy.newProxyInstance} throws
jaroslav@601
   587
     * {@code IllegalArgumentException} for the same reasons that
jaroslav@601
   588
     * {@code Proxy.getProxyClass} does.
jaroslav@601
   589
     *
jaroslav@601
   590
     * @param   loader the class loader to define the proxy class
jaroslav@601
   591
     * @param   interfaces the list of interfaces for the proxy class
jaroslav@601
   592
     *          to implement
jaroslav@601
   593
     * @param   h the invocation handler to dispatch method invocations to
jaroslav@601
   594
     * @return  a proxy instance with the specified invocation handler of a
jaroslav@601
   595
     *          proxy class that is defined by the specified class loader
jaroslav@601
   596
     *          and that implements the specified interfaces
jaroslav@601
   597
     * @throws  IllegalArgumentException if any of the restrictions on the
jaroslav@601
   598
     *          parameters that may be passed to {@code getProxyClass}
jaroslav@601
   599
     *          are violated
jaroslav@601
   600
     * @throws  NullPointerException if the {@code interfaces} array
jaroslav@601
   601
     *          argument or any of its elements are {@code null}, or
jaroslav@601
   602
     *          if the invocation handler, {@code h}, is
jaroslav@601
   603
     *          {@code null}
jaroslav@601
   604
     */
jaroslav@601
   605
    public static Object newProxyInstance(ClassLoader loader,
jaroslav@601
   606
                                          Class<?>[] interfaces,
jaroslav@601
   607
                                          InvocationHandler h)
jaroslav@601
   608
        throws IllegalArgumentException
jaroslav@601
   609
    {
jaroslav@601
   610
        if (h == null) {
jaroslav@601
   611
            throw new NullPointerException();
jaroslav@601
   612
        }
jaroslav@1378
   613
jaroslav@1378
   614
        /*
jaroslav@1378
   615
         * Look up or generate the designated proxy class.
jaroslav@1378
   616
         */
jaroslav@1378
   617
        Class<?> cl = getProxyClass(loader, interfaces);
jaroslav@1378
   618
jaroslav@1378
   619
        /*
jaroslav@1378
   620
         * Invoke its constructor with the designated invocation handler.
jaroslav@1378
   621
         */
jaroslav@1378
   622
        try {
jaroslav@1378
   623
            Constructor cons = cl.getConstructor(constructorParams);
jaroslav@1378
   624
            return cons.newInstance(new Object[] { h });
jaroslav@1378
   625
        } catch (NoSuchMethodException e) {
jaroslav@1378
   626
            throw new InternalError(e.toString());
jaroslav@1378
   627
        } catch (IllegalAccessException e) {
jaroslav@1378
   628
            throw new InternalError(e.toString());
jaroslav@1378
   629
        } catch (InstantiationException e) {
jaroslav@1378
   630
            throw new InternalError(e.toString());
jaroslav@1378
   631
        } catch (InvocationTargetException e) {
jaroslav@1378
   632
            throw new InternalError(e.toString());
jaroslav@1378
   633
        }
jaroslav@601
   634
    }
jaroslav@601
   635
jaroslav@601
   636
    /**
jaroslav@601
   637
     * Returns true if and only if the specified class was dynamically
jaroslav@601
   638
     * generated to be a proxy class using the {@code getProxyClass}
jaroslav@601
   639
     * method or the {@code newProxyInstance} method.
jaroslav@601
   640
     *
jaroslav@601
   641
     * <p>The reliability of this method is important for the ability
jaroslav@601
   642
     * to use it to make security decisions, so its implementation should
jaroslav@601
   643
     * not just test if the class in question extends {@code Proxy}.
jaroslav@601
   644
     *
jaroslav@601
   645
     * @param   cl the class to test
jaroslav@601
   646
     * @return  {@code true} if the class is a proxy class and
jaroslav@601
   647
     *          {@code false} otherwise
jaroslav@601
   648
     * @throws  NullPointerException if {@code cl} is {@code null}
jaroslav@601
   649
     */
jaroslav@601
   650
    public static boolean isProxyClass(Class<?> cl) {
jaroslav@601
   651
        if (cl == null) {
jaroslav@601
   652
            throw new NullPointerException();
jaroslav@601
   653
        }
jaroslav@601
   654
jaroslav@1378
   655
        return proxyClasses.containsKey(cl);
jaroslav@601
   656
    }
jaroslav@601
   657
jaroslav@601
   658
    /**
jaroslav@601
   659
     * Returns the invocation handler for the specified proxy instance.
jaroslav@601
   660
     *
jaroslav@601
   661
     * @param   proxy the proxy instance to return the invocation handler for
jaroslav@601
   662
     * @return  the invocation handler for the proxy instance
jaroslav@601
   663
     * @throws  IllegalArgumentException if the argument is not a
jaroslav@601
   664
     *          proxy instance
jaroslav@601
   665
     */
jaroslav@601
   666
    public static InvocationHandler getInvocationHandler(Object proxy)
jaroslav@601
   667
        throws IllegalArgumentException
jaroslav@601
   668
    {
jaroslav@601
   669
        /*
jaroslav@601
   670
         * Verify that the object is actually a proxy instance.
jaroslav@601
   671
         */
jaroslav@601
   672
        if (!isProxyClass(proxy.getClass())) {
jaroslav@601
   673
            throw new IllegalArgumentException("not a proxy instance");
jaroslav@601
   674
        }
jaroslav@601
   675
jaroslav@1378
   676
        ProxyImpl p = (ProxyImpl) proxy;
jaroslav@601
   677
        return p.h;
jaroslav@601
   678
    }
jaroslav@601
   679
jaroslav@1378
   680
    @JavaScriptBody(args = { "ignore", "name", "byteCode" }, 
jaroslav@1529
   681
        body = 
jaroslav@1529
   682
            "var r = vm._reload;"
jaroslav@1529
   683
          + "if (!r) r = exports._reload;"
jaroslav@1529
   684
          + "return r(name, byteCode).constructor.$class;"
jaroslav@1378
   685
    )
jaroslav@1378
   686
    private static native Class defineClass0(
jaroslav@1378
   687
        ClassLoader loader, String name, byte[] b
jaroslav@1378
   688
    );
jaroslav@1378
   689
    
jaroslav@1378
   690
    private static class Generator {
jaroslav@1378
   691
        /*
jaroslav@1378
   692
         * In the comments below, "JVMS" refers to The Java Virtual Machine
jaroslav@1378
   693
         * Specification Second Edition and "JLS" refers to the original
jaroslav@1378
   694
         * version of The Java Language Specification, unless otherwise
jaroslav@1378
   695
         * specified.
jaroslav@1378
   696
         */
jaroslav@1378
   697
jaroslav@1378
   698
        /* need 1.6 bytecode */
jaroslav@1378
   699
        private static final int CLASSFILE_MAJOR_VERSION = 50;
jaroslav@1378
   700
        private static final int CLASSFILE_MINOR_VERSION = 0;
jaroslav@1378
   701
jaroslav@1378
   702
        /*
jaroslav@1378
   703
         * beginning of constants copied from
jaroslav@1378
   704
         * sun.tools.java.RuntimeConstants (which no longer exists):
jaroslav@1378
   705
         */
jaroslav@1378
   706
jaroslav@1378
   707
        /* constant pool tags */
jaroslav@1378
   708
        private static final int CONSTANT_UTF8 = 1;
jaroslav@1378
   709
        private static final int CONSTANT_UNICODE = 2;
jaroslav@1378
   710
        private static final int CONSTANT_INTEGER = 3;
jaroslav@1378
   711
        private static final int CONSTANT_FLOAT = 4;
jaroslav@1378
   712
        private static final int CONSTANT_LONG = 5;
jaroslav@1378
   713
        private static final int CONSTANT_DOUBLE = 6;
jaroslav@1378
   714
        private static final int CONSTANT_CLASS = 7;
jaroslav@1378
   715
        private static final int CONSTANT_STRING = 8;
jaroslav@1378
   716
        private static final int CONSTANT_FIELD = 9;
jaroslav@1378
   717
        private static final int CONSTANT_METHOD = 10;
jaroslav@1378
   718
        private static final int CONSTANT_INTERFACEMETHOD = 11;
jaroslav@1378
   719
        private static final int CONSTANT_NAMEANDTYPE = 12;
jaroslav@1378
   720
jaroslav@1378
   721
        /* access and modifier flags */
jaroslav@1378
   722
        private static final int ACC_PUBLIC = 0x00000001;
jaroslav@1378
   723
        private static final int ACC_FINAL = 0x00000010;
jaroslav@1378
   724
        private static final int ACC_SUPER = 0x00000020;
jaroslav@1378
   725
jaroslav@1378
   726
    // end of constants copied from sun.tools.java.RuntimeConstants
jaroslav@1378
   727
        /**
jaroslav@1378
   728
         * name of the superclass of proxy classes
jaroslav@1378
   729
         */
jaroslav@1378
   730
        private final static String superclassName = "java/lang/reflect/Proxy";
jaroslav@1378
   731
jaroslav@1378
   732
        /**
jaroslav@1378
   733
         * name of field for storing a proxy instance's invocation handler
jaroslav@1378
   734
         */
jaroslav@1378
   735
        private final static String handlerFieldName = "h";
jaroslav@1378
   736
jaroslav@1378
   737
        /* preloaded Method objects for methods in java.lang.Object */
jaroslav@1378
   738
        private static Method hashCodeMethod;
jaroslav@1378
   739
        private static Method equalsMethod;
jaroslav@1378
   740
        private static Method toStringMethod;
jaroslav@1378
   741
jaroslav@1378
   742
        static {
jaroslav@1378
   743
            try {
jaroslav@1378
   744
                hashCodeMethod = Object.class.getMethod("hashCode");
jaroslav@1378
   745
                equalsMethod
jaroslav@1378
   746
                    = Object.class.getMethod("equals", new Class[]{Object.class});
jaroslav@1378
   747
                toStringMethod = Object.class.getMethod("toString");
jaroslav@1378
   748
            } catch (NoSuchMethodException e) {
jaroslav@1378
   749
                throw new IllegalStateException(e.getMessage());
jaroslav@1378
   750
            }
jaroslav@1378
   751
        }
jaroslav@1378
   752
jaroslav@1378
   753
        /**
jaroslav@1378
   754
         * name of proxy class
jaroslav@1378
   755
         */
jaroslav@1378
   756
        private String className;
jaroslav@1378
   757
jaroslav@1378
   758
        /**
jaroslav@1378
   759
         * proxy interfaces
jaroslav@1378
   760
         */
jaroslav@1378
   761
        private Class[] interfaces;
jaroslav@1378
   762
jaroslav@1378
   763
        /**
jaroslav@1378
   764
         * constant pool of class being generated
jaroslav@1378
   765
         */
jaroslav@1378
   766
        private ConstantPool cp = new ConstantPool();
jaroslav@1378
   767
jaroslav@1378
   768
        /**
jaroslav@1378
   769
         * maps method signature string to list of ProxyMethod objects for proxy
jaroslav@1378
   770
         * methods with that signature
jaroslav@1378
   771
         */
jaroslav@1378
   772
        private Map<String, List<ProxyMethod>> proxyMethods
jaroslav@1378
   773
            = new HashMap<String, List<ProxyMethod>>();
jaroslav@1378
   774
jaroslav@1378
   775
        /**
jaroslav@1378
   776
         * count of ProxyMethod objects added to proxyMethods
jaroslav@1378
   777
         */
jaroslav@1378
   778
        private int proxyMethodCount = 0;
jaroslav@1378
   779
jaroslav@1378
   780
        /**
jaroslav@1378
   781
         * Construct a ProxyGenerator to generate a proxy class with the
jaroslav@1378
   782
         * specified name and for the given interfaces.
jaroslav@1378
   783
         *
jaroslav@1378
   784
         * A ProxyGenerator object contains the state for the ongoing generation
jaroslav@1378
   785
         * of a particular proxy class.
jaroslav@1378
   786
         */
jaroslav@1378
   787
        private Generator(String className, Class[] interfaces) {
jaroslav@1378
   788
            this.className = className;
jaroslav@1378
   789
            this.interfaces = interfaces;
jaroslav@1378
   790
        }
jaroslav@1378
   791
jaroslav@1378
   792
        /**
jaroslav@1378
   793
         * Generate a class file for the proxy class. This method drives the
jaroslav@1378
   794
         * class file generation process.
jaroslav@1378
   795
         */
jaroslav@1378
   796
        private byte[] generateClassFile() {
jaroslav@1378
   797
jaroslav@1378
   798
            /* ============================================================
jaroslav@1378
   799
             * Step 1: Assemble ProxyMethod objects for all methods to
jaroslav@1378
   800
             * generate proxy dispatching code for.
jaroslav@1378
   801
             */
jaroslav@1378
   802
jaroslav@1378
   803
            /*
jaroslav@1378
   804
             * Record that proxy methods are needed for the hashCode, equals,
jaroslav@1378
   805
             * and toString methods of java.lang.Object.  This is done before
jaroslav@1378
   806
             * the methods from the proxy interfaces so that the methods from
jaroslav@1378
   807
             * java.lang.Object take precedence over duplicate methods in the
jaroslav@1378
   808
             * proxy interfaces.
jaroslav@1378
   809
             */
jaroslav@1378
   810
            addProxyMethod(hashCodeMethod, Object.class);
jaroslav@1378
   811
            addProxyMethod(equalsMethod, Object.class);
jaroslav@1378
   812
            addProxyMethod(toStringMethod, Object.class);
jaroslav@1378
   813
jaroslav@1378
   814
            /*
jaroslav@1378
   815
             * Now record all of the methods from the proxy interfaces, giving
jaroslav@1378
   816
             * earlier interfaces precedence over later ones with duplicate
jaroslav@1378
   817
             * methods.
jaroslav@1378
   818
             */
jaroslav@1378
   819
            for (int i = 0; i < interfaces.length; i++) {
jaroslav@1378
   820
                Method[] methods = interfaces[i].getMethods();
jaroslav@1378
   821
                for (int j = 0; j < methods.length; j++) {
jaroslav@1378
   822
                    addProxyMethod(methods[j], interfaces[i]);
jaroslav@1378
   823
                }
jaroslav@1378
   824
            }
jaroslav@1378
   825
jaroslav@1378
   826
            /*
jaroslav@1378
   827
             * For each set of proxy methods with the same signature,
jaroslav@1378
   828
             * verify that the methods' return types are compatible.
jaroslav@1378
   829
             */
jaroslav@1378
   830
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
jaroslav@1378
   831
                checkReturnTypes(sigmethods);
jaroslav@1378
   832
            }
jaroslav@1378
   833
jaroslav@1378
   834
            /* ============================================================
jaroslav@1378
   835
             * Step 2: Assemble FieldInfo and MethodInfo structs for all of
jaroslav@1378
   836
             * fields and methods in the class we are generating.
jaroslav@1378
   837
             */
jaroslav@1378
   838
            
jaroslav@1378
   839
            // will be done in fillInMethods
jaroslav@1378
   840
jaroslav@1378
   841
            /* ============================================================
jaroslav@1378
   842
             * Step 3: Write the final class file.
jaroslav@1378
   843
             */
jaroslav@1378
   844
jaroslav@1378
   845
            /*
jaroslav@1378
   846
             * Make sure that constant pool indexes are reserved for the
jaroslav@1378
   847
             * following items before starting to write the final class file.
jaroslav@1378
   848
             */
jaroslav@1378
   849
            cp.getClass(dotToSlash(className));
jaroslav@1378
   850
            cp.getClass(superclassName);
jaroslav@1378
   851
            for (int i = 0; i < interfaces.length; i++) {
jaroslav@1378
   852
                cp.getClass(dotToSlash(interfaces[i].getName()));
jaroslav@1378
   853
            }
jaroslav@1378
   854
jaroslav@1378
   855
            /*
jaroslav@1378
   856
             * Disallow new constant pool additions beyond this point, since
jaroslav@1378
   857
             * we are about to write the final constant pool table.
jaroslav@1378
   858
             */
jaroslav@1378
   859
            cp.setReadOnly();
jaroslav@1378
   860
jaroslav@1378
   861
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
jaroslav@1378
   862
            DataOutputStream dout = new DataOutputStream(bout);
jaroslav@1378
   863
jaroslav@1378
   864
            try {
jaroslav@1378
   865
                /*
jaroslav@1378
   866
                 * Write all the items of the "ClassFile" structure.
jaroslav@1378
   867
                 * See JVMS section 4.1.
jaroslav@1378
   868
                 */
jaroslav@1378
   869
                // u4 magic;
jaroslav@1378
   870
                dout.writeInt(0xCAFEBABE);
jaroslav@1378
   871
                // u2 minor_version;
jaroslav@1378
   872
                dout.writeShort(CLASSFILE_MINOR_VERSION);
jaroslav@1378
   873
                // u2 major_version;
jaroslav@1378
   874
                dout.writeShort(CLASSFILE_MAJOR_VERSION);
jaroslav@1378
   875
jaroslav@1378
   876
                cp.write(dout);             // (write constant pool)
jaroslav@1378
   877
jaroslav@1378
   878
                // u2 access_flags;
jaroslav@1378
   879
                dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
jaroslav@1378
   880
                // u2 this_class;
jaroslav@1378
   881
                dout.writeShort(cp.getClass(dotToSlash(className)));
jaroslav@1378
   882
                // u2 super_class;
jaroslav@1378
   883
                dout.writeShort(cp.getClass(superclassName));
jaroslav@1378
   884
jaroslav@1378
   885
                // u2 interfaces_count;
jaroslav@1378
   886
                dout.writeShort(interfaces.length);
jaroslav@1378
   887
                // u2 interfaces[interfaces_count];
jaroslav@1378
   888
                for (int i = 0; i < interfaces.length; i++) {
jaroslav@1378
   889
                    dout.writeShort(cp.getClass(
jaroslav@1378
   890
                        dotToSlash(interfaces[i].getName())));
jaroslav@1378
   891
                }
jaroslav@1378
   892
jaroslav@1378
   893
                // u2 fields_count;
jaroslav@1378
   894
                dout.writeShort(0);
jaroslav@1378
   895
jaroslav@1378
   896
                // u2 methods_count;
jaroslav@1378
   897
                dout.writeShort(0);
jaroslav@1378
   898
jaroslav@1378
   899
                // u2 attributes_count;
jaroslav@1378
   900
                dout.writeShort(0); // (no ClassFile attributes for proxy classes)
jaroslav@1378
   901
jaroslav@1378
   902
            } catch (IOException e) {
jaroslav@1378
   903
                throw new InternalError("unexpected I/O Exception");
jaroslav@1378
   904
            }
jaroslav@1378
   905
jaroslav@1378
   906
            return bout.toByteArray();
jaroslav@1378
   907
        }
jaroslav@1378
   908
jaroslav@1378
   909
        @JavaScriptBody(args = { "c", "sig", "method", "primitive" }, body = 
jaroslav@1378
   910
            "var p = c.cnstr.prototype;\n" +
jaroslav@1378
   911
            "p[sig] = function() {\n" +
jaroslav@1378
   912
            "  var h = this._h();\n" +
jaroslav@1378
   913
            "  var res = h.invoke__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_reflect_Method_2_3Ljava_lang_Object_2(this, method, arguments);\n" +
jaroslav@1378
   914
            "  \n" +
jaroslav@1378
   915
            "  \n" +
jaroslav@1378
   916
            "  return res;\n" +
jaroslav@1378
   917
            "};"
jaroslav@1378
   918
        )
jaroslav@1378
   919
        private static native void defineMethod(Class<?> proxyClass, String sig, Method method, boolean primitive);
jaroslav@1378
   920
jaroslav@1378
   921
        @JavaScriptBody(args = "c", body = 
jaroslav@1378
   922
              "var h = c.cnstr.cons__VLjava_lang_reflect_InvocationHandler_2 = function(h) {\n"
jaroslav@1378
   923
            + "  c.superclass.cnstr.cons__VLjava_lang_reflect_InvocationHandler_2.call(this, h);\n"
jaroslav@1378
   924
            + "}\n"
jaroslav@1378
   925
            + "h.cls = c.cnstr;\n"
jaroslav@1378
   926
        )
jaroslav@1378
   927
        private static native void defineConstructor(Class<?> proxyClass);
jaroslav@1378
   928
        
jaroslav@1378
   929
        final void fillInMethods(Class<?> proxyClass) {
jaroslav@1378
   930
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
jaroslav@1378
   931
                for (ProxyMethod pm : sigmethods) {
jaroslav@1378
   932
                    String sig = MethodImpl.toSignature(pm.method);
jaroslav@1378
   933
                    defineMethod(proxyClass, sig, pm.method, pm.method.getReturnType().isPrimitive());
jaroslav@1378
   934
                }
jaroslav@1378
   935
            }
jaroslav@1378
   936
            defineConstructor(proxyClass);
jaroslav@1378
   937
        }
jaroslav@1378
   938
jaroslav@1378
   939
        /**
jaroslav@1378
   940
         * Add another method to be proxied, either by creating a new
jaroslav@1378
   941
         * ProxyMethod object or augmenting an old one for a duplicate method.
jaroslav@1378
   942
         *
jaroslav@1378
   943
         * "fromClass" indicates the proxy interface that the method was found
jaroslav@1378
   944
         * through, which may be different from (a subinterface of) the method's
jaroslav@1378
   945
         * "declaring class". Note that the first Method object passed for a
jaroslav@1378
   946
         * given name and descriptor identifies the Method object (and thus the
jaroslav@1378
   947
         * declaring class) that will be passed to the invocation handler's
jaroslav@1378
   948
         * "invoke" method for a given set of duplicate methods.
jaroslav@1378
   949
         */
jaroslav@1378
   950
        private void addProxyMethod(Method m, Class fromClass) {
jaroslav@1378
   951
            String name = m.getName();
jaroslav@1378
   952
            Class[] parameterTypes = m.getParameterTypes();
jaroslav@1378
   953
            Class returnType = m.getReturnType();
jaroslav@1378
   954
            Class[] exceptionTypes = m.getExceptionTypes();
jaroslav@1378
   955
jaroslav@1378
   956
            String sig = MethodImpl.toSignature(m);
jaroslav@1378
   957
            List<ProxyMethod> sigmethods = proxyMethods.get(sig);
jaroslav@1378
   958
            if (sigmethods != null) {
jaroslav@1378
   959
                for (ProxyMethod pm : sigmethods) {
jaroslav@1378
   960
                    if (returnType == pm.returnType) {
jaroslav@1378
   961
                        /*
jaroslav@1378
   962
                         * Found a match: reduce exception types to the
jaroslav@1378
   963
                         * greatest set of exceptions that can thrown
jaroslav@1378
   964
                         * compatibly with the throws clauses of both
jaroslav@1378
   965
                         * overridden methods.
jaroslav@1378
   966
                         */
jaroslav@1378
   967
                        List<Class<?>> legalExceptions = new ArrayList<Class<?>>();
jaroslav@1378
   968
                        collectCompatibleTypes(
jaroslav@1378
   969
                            exceptionTypes, pm.exceptionTypes, legalExceptions);
jaroslav@1378
   970
                        collectCompatibleTypes(
jaroslav@1378
   971
                            pm.exceptionTypes, exceptionTypes, legalExceptions);
jaroslav@1378
   972
                        pm.exceptionTypes = new Class[legalExceptions.size()];
jaroslav@1378
   973
                        pm.exceptionTypes
jaroslav@1378
   974
                            = legalExceptions.toArray(pm.exceptionTypes);
jaroslav@1378
   975
                        return;
jaroslav@1378
   976
                    }
jaroslav@1378
   977
                }
jaroslav@1378
   978
            } else {
jaroslav@1378
   979
                sigmethods = new ArrayList<ProxyMethod>(3);
jaroslav@1378
   980
                proxyMethods.put(sig, sigmethods);
jaroslav@1378
   981
            }
jaroslav@1378
   982
            sigmethods.add(new ProxyMethod(m, name, parameterTypes, returnType,
jaroslav@1378
   983
                exceptionTypes, fromClass));
jaroslav@1378
   984
        }
jaroslav@1378
   985
jaroslav@1378
   986
        /**
jaroslav@1378
   987
         * For a given set of proxy methods with the same signature, check that
jaroslav@1378
   988
         * their return types are compatible according to the Proxy
jaroslav@1378
   989
         * specification.
jaroslav@1378
   990
         *
jaroslav@1378
   991
         * Specifically, if there is more than one such method, then all of the
jaroslav@1378
   992
         * return types must be reference types, and there must be one return
jaroslav@1378
   993
         * type that is assignable to each of the rest of them.
jaroslav@1378
   994
         */
jaroslav@1378
   995
        private static void checkReturnTypes(List<ProxyMethod> methods) {
jaroslav@1378
   996
            /*
jaroslav@1378
   997
             * If there is only one method with a given signature, there
jaroslav@1378
   998
             * cannot be a conflict.  This is the only case in which a
jaroslav@1378
   999
             * primitive (or void) return type is allowed.
jaroslav@1378
  1000
             */
jaroslav@1378
  1001
            if (methods.size() < 2) {
jaroslav@1378
  1002
                return;
jaroslav@1378
  1003
            }
jaroslav@1378
  1004
jaroslav@1378
  1005
            /*
jaroslav@1378
  1006
             * List of return types that are not yet known to be
jaroslav@1378
  1007
             * assignable from ("covered" by) any of the others.
jaroslav@1378
  1008
             */
jaroslav@1378
  1009
            LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<Class<?>>();
jaroslav@1378
  1010
jaroslav@1378
  1011
            nextNewReturnType:
jaroslav@1378
  1012
            for (ProxyMethod pm : methods) {
jaroslav@1378
  1013
                Class<?> newReturnType = pm.returnType;
jaroslav@1378
  1014
                if (newReturnType.isPrimitive()) {
jaroslav@1378
  1015
                    throw new IllegalArgumentException(
jaroslav@1378
  1016
                        "methods with same signature "
jaroslav@1378
  1017
                        + getFriendlyMethodSignature(pm.methodName,
jaroslav@1378
  1018
                            pm.parameterTypes)
jaroslav@1378
  1019
                        + " but incompatible return types: "
jaroslav@1378
  1020
                        + newReturnType.getName() + " and others");
jaroslav@1378
  1021
                }
jaroslav@1378
  1022
                boolean added = false;
jaroslav@1378
  1023
jaroslav@1378
  1024
                /*
jaroslav@1378
  1025
                 * Compare the new return type to the existing uncovered
jaroslav@1378
  1026
                 * return types.
jaroslav@1378
  1027
                 */
jaroslav@1378
  1028
                ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
jaroslav@1378
  1029
                while (liter.hasNext()) {
jaroslav@1378
  1030
                    Class<?> uncoveredReturnType = liter.next();
jaroslav@1378
  1031
jaroslav@1378
  1032
                    /*
jaroslav@1378
  1033
                     * If an existing uncovered return type is assignable
jaroslav@1378
  1034
                     * to this new one, then we can forget the new one.
jaroslav@1378
  1035
                     */
jaroslav@1378
  1036
                    if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
jaroslav@1378
  1037
                        assert !added;
jaroslav@1378
  1038
                        continue nextNewReturnType;
jaroslav@1378
  1039
                    }
jaroslav@1378
  1040
jaroslav@1378
  1041
                    /*
jaroslav@1378
  1042
                     * If the new return type is assignable to an existing
jaroslav@1378
  1043
                     * uncovered one, then should replace the existing one
jaroslav@1378
  1044
                     * with the new one (or just forget the existing one,
jaroslav@1378
  1045
                     * if the new one has already be put in the list).
jaroslav@1378
  1046
                     */
jaroslav@1378
  1047
                    if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
jaroslav@1378
  1048
                        // (we can assume that each return type is unique)
jaroslav@1378
  1049
                        if (!added) {
jaroslav@1378
  1050
                            liter.set(newReturnType);
jaroslav@1378
  1051
                            added = true;
jaroslav@1378
  1052
                        } else {
jaroslav@1378
  1053
                            liter.remove();
jaroslav@1378
  1054
                        }
jaroslav@1378
  1055
                    }
jaroslav@1378
  1056
                }
jaroslav@1378
  1057
jaroslav@1378
  1058
                /*
jaroslav@1378
  1059
                 * If we got through the list of existing uncovered return
jaroslav@1378
  1060
                 * types without an assignability relationship, then add
jaroslav@1378
  1061
                 * the new return type to the list of uncovered ones.
jaroslav@1378
  1062
                 */
jaroslav@1378
  1063
                if (!added) {
jaroslav@1378
  1064
                    uncoveredReturnTypes.add(newReturnType);
jaroslav@1378
  1065
                }
jaroslav@1378
  1066
            }
jaroslav@1378
  1067
jaroslav@1378
  1068
            /*
jaroslav@1378
  1069
             * We shouldn't end up with more than one return type that is
jaroslav@1378
  1070
             * not assignable from any of the others.
jaroslav@1378
  1071
             */
jaroslav@1378
  1072
            if (uncoveredReturnTypes.size() > 1) {
jaroslav@1378
  1073
                ProxyMethod pm = methods.get(0);
jaroslav@1378
  1074
                throw new IllegalArgumentException(
jaroslav@1378
  1075
                    "methods with same signature "
jaroslav@1378
  1076
                    + getFriendlyMethodSignature(pm.methodName, pm.parameterTypes)
jaroslav@1378
  1077
                    + " but incompatible return types: " + uncoveredReturnTypes);
jaroslav@1378
  1078
            }
jaroslav@1378
  1079
        }
jaroslav@1378
  1080
jaroslav@1378
  1081
jaroslav@1378
  1082
        /**
jaroslav@1378
  1083
         * A ProxyMethod object represents a proxy method in the proxy class
jaroslav@1378
  1084
         * being generated: a method whose implementation will encode and
jaroslav@1378
  1085
         * dispatch invocations to the proxy instance's invocation handler.
jaroslav@1378
  1086
         */
jaroslav@1378
  1087
        private class ProxyMethod {
jaroslav@1378
  1088
jaroslav@1378
  1089
            private final Method method;
jaroslav@1378
  1090
            public String methodName;
jaroslav@1378
  1091
            public Class[] parameterTypes;
jaroslav@1378
  1092
            public Class returnType;
jaroslav@1378
  1093
            public Class[] exceptionTypes;
jaroslav@1378
  1094
            public Class fromClass;
jaroslav@1378
  1095
            public String methodFieldName;
jaroslav@1378
  1096
jaroslav@1378
  1097
            private ProxyMethod(Method m, 
jaroslav@1378
  1098
                String methodName, Class[] parameterTypes, 
jaroslav@1378
  1099
                Class returnType, Class[] exceptionTypes, 
jaroslav@1378
  1100
                Class fromClass
jaroslav@1378
  1101
            ) {
jaroslav@1378
  1102
                this.method = m;
jaroslav@1378
  1103
                this.methodName = methodName;
jaroslav@1378
  1104
                this.parameterTypes = parameterTypes;
jaroslav@1378
  1105
                this.returnType = returnType;
jaroslav@1378
  1106
                this.exceptionTypes = exceptionTypes;
jaroslav@1378
  1107
                this.fromClass = fromClass;
jaroslav@1378
  1108
                this.methodFieldName = "m" + proxyMethodCount++;
jaroslav@1378
  1109
            }
jaroslav@1378
  1110
jaroslav@1378
  1111
        }
jaroslav@1378
  1112
jaroslav@1378
  1113
        /*
jaroslav@1378
  1114
         * ==================== General Utility Methods ====================
jaroslav@1378
  1115
         */
jaroslav@1378
  1116
        /**
jaroslav@1378
  1117
         * Convert a fully qualified class name that uses '.' as the package
jaroslav@1378
  1118
         * separator, the external representation used by the Java language and
jaroslav@1378
  1119
         * APIs, to a fully qualified class name that uses '/' as the package
jaroslav@1378
  1120
         * separator, the representation used in the class file format (see JVMS
jaroslav@1378
  1121
         * section 4.2).
jaroslav@1378
  1122
         */
jaroslav@1378
  1123
        private static String dotToSlash(String name) {
jaroslav@1378
  1124
            return name.replace('.', '/');
jaroslav@1378
  1125
        }
jaroslav@1378
  1126
jaroslav@1378
  1127
        /**
jaroslav@1378
  1128
         * Return the list of "parameter descriptor" strings enclosed in
jaroslav@1378
  1129
         * parentheses corresponding to the given parameter types (in other
jaroslav@1378
  1130
         * words, a method descriptor without a return descriptor). This string
jaroslav@1378
  1131
         * is useful for constructing string keys for methods without regard to
jaroslav@1378
  1132
         * their return type.
jaroslav@1378
  1133
         */
jaroslav@1378
  1134
        private static String getParameterDescriptors(Class[] parameterTypes) {
jaroslav@1378
  1135
            StringBuilder desc = new StringBuilder("(");
jaroslav@1378
  1136
            for (int i = 0; i < parameterTypes.length; i++) {
jaroslav@1378
  1137
                desc.append(getFieldType(parameterTypes[i]));
jaroslav@1378
  1138
            }
jaroslav@1378
  1139
            desc.append(')');
jaroslav@1378
  1140
            return desc.toString();
jaroslav@1378
  1141
        }
jaroslav@1378
  1142
jaroslav@1378
  1143
        /**
jaroslav@1378
  1144
         * Return the "field type" string for the given type, appropriate for a
jaroslav@1378
  1145
         * field descriptor, a parameter descriptor, or a return descriptor
jaroslav@1378
  1146
         * other than "void". See JVMS section 4.3.2.
jaroslav@1378
  1147
         */
jaroslav@1378
  1148
        private static String getFieldType(Class type) {
jaroslav@1378
  1149
            if (type.isPrimitive()) {
jaroslav@1378
  1150
                return PrimitiveTypeInfo.get(type).baseTypeString;
jaroslav@1378
  1151
            } else if (type.isArray()) {
jaroslav@1378
  1152
                /*
jaroslav@1378
  1153
                 * According to JLS 20.3.2, the getName() method on Class does
jaroslav@1378
  1154
                 * return the VM type descriptor format for array classes (only);
jaroslav@1378
  1155
                 * using that should be quicker than the otherwise obvious code:
jaroslav@1378
  1156
                 *
jaroslav@1378
  1157
                 *     return "[" + getTypeDescriptor(type.getComponentType());
jaroslav@1378
  1158
                 */
jaroslav@1378
  1159
                return type.getName().replace('.', '/');
jaroslav@1378
  1160
            } else {
jaroslav@1378
  1161
                return "L" + dotToSlash(type.getName()) + ";";
jaroslav@1378
  1162
            }
jaroslav@1378
  1163
        }
jaroslav@1378
  1164
jaroslav@1378
  1165
        /**
jaroslav@1378
  1166
         * Returns a human-readable string representing the signature of a
jaroslav@1378
  1167
         * method with the given name and parameter types.
jaroslav@1378
  1168
         */
jaroslav@1378
  1169
        private static String getFriendlyMethodSignature(String name,
jaroslav@1378
  1170
            Class[] parameterTypes) {
jaroslav@1378
  1171
            StringBuilder sig = new StringBuilder(name);
jaroslav@1378
  1172
            sig.append('(');
jaroslav@1378
  1173
            for (int i = 0; i < parameterTypes.length; i++) {
jaroslav@1378
  1174
                if (i > 0) {
jaroslav@1378
  1175
                    sig.append(',');
jaroslav@1378
  1176
                }
jaroslav@1378
  1177
                Class parameterType = parameterTypes[i];
jaroslav@1378
  1178
                int dimensions = 0;
jaroslav@1378
  1179
                while (parameterType.isArray()) {
jaroslav@1378
  1180
                    parameterType = parameterType.getComponentType();
jaroslav@1378
  1181
                    dimensions++;
jaroslav@1378
  1182
                }
jaroslav@1378
  1183
                sig.append(parameterType.getName());
jaroslav@1378
  1184
                while (dimensions-- > 0) {
jaroslav@1378
  1185
                    sig.append("[]");
jaroslav@1378
  1186
                }
jaroslav@1378
  1187
            }
jaroslav@1378
  1188
            sig.append(')');
jaroslav@1378
  1189
            return sig.toString();
jaroslav@1378
  1190
        }
jaroslav@1378
  1191
jaroslav@1378
  1192
        /**
jaroslav@1378
  1193
         * Add to the given list all of the types in the "from" array that are
jaroslav@1378
  1194
         * not already contained in the list and are assignable to at least one
jaroslav@1378
  1195
         * of the types in the "with" array.
jaroslav@1378
  1196
         *
jaroslav@1378
  1197
         * This method is useful for computing the greatest common set of
jaroslav@1378
  1198
         * declared exceptions from duplicate methods inherited from different
jaroslav@1378
  1199
         * interfaces.
jaroslav@1378
  1200
         */
jaroslav@1378
  1201
        private static void collectCompatibleTypes(Class<?>[] from,
jaroslav@1378
  1202
            Class<?>[] with,
jaroslav@1378
  1203
            List<Class<?>> list) {
jaroslav@1378
  1204
            for (int i = 0; i < from.length; i++) {
jaroslav@1378
  1205
                if (!list.contains(from[i])) {
jaroslav@1378
  1206
                    for (int j = 0; j < with.length; j++) {
jaroslav@1378
  1207
                        if (with[j].isAssignableFrom(from[i])) {
jaroslav@1378
  1208
                            list.add(from[i]);
jaroslav@1378
  1209
                            break;
jaroslav@1378
  1210
                        }
jaroslav@1378
  1211
                    }
jaroslav@1378
  1212
                }
jaroslav@1378
  1213
            }
jaroslav@1378
  1214
        }
jaroslav@1378
  1215
jaroslav@1378
  1216
jaroslav@1378
  1217
        /**
jaroslav@1378
  1218
         * A PrimitiveTypeInfo object contains assorted information about a
jaroslav@1378
  1219
         * primitive type in its public fields. The struct for a particular
jaroslav@1378
  1220
         * primitive type can be obtained using the static "get" method.
jaroslav@1378
  1221
         */
jaroslav@1378
  1222
        private static class PrimitiveTypeInfo {
jaroslav@1378
  1223
jaroslav@1378
  1224
            /**
jaroslav@1378
  1225
             * "base type" used in various descriptors (see JVMS section 4.3.2)
jaroslav@1378
  1226
             */
jaroslav@1378
  1227
            public String baseTypeString;
jaroslav@1378
  1228
jaroslav@1378
  1229
            /**
jaroslav@1378
  1230
             * name of corresponding wrapper class
jaroslav@1378
  1231
             */
jaroslav@1378
  1232
            public String wrapperClassName;
jaroslav@1378
  1233
jaroslav@1378
  1234
            /**
jaroslav@1378
  1235
             * method descriptor for wrapper class "valueOf" factory method
jaroslav@1378
  1236
             */
jaroslav@1378
  1237
            public String wrapperValueOfDesc;
jaroslav@1378
  1238
jaroslav@1378
  1239
            /**
jaroslav@1378
  1240
             * name of wrapper class method for retrieving primitive value
jaroslav@1378
  1241
             */
jaroslav@1378
  1242
            public String unwrapMethodName;
jaroslav@1378
  1243
jaroslav@1378
  1244
            /**
jaroslav@1378
  1245
             * descriptor of same method
jaroslav@1378
  1246
             */
jaroslav@1378
  1247
            public String unwrapMethodDesc;
jaroslav@1378
  1248
jaroslav@1378
  1249
            private static Map<Class, PrimitiveTypeInfo> table
jaroslav@1378
  1250
                = new HashMap<Class, PrimitiveTypeInfo>();
jaroslav@1378
  1251
jaroslav@1378
  1252
            static {
jaroslav@1378
  1253
                add(byte.class, Byte.class);
jaroslav@1378
  1254
                add(char.class, Character.class);
jaroslav@1378
  1255
                add(double.class, Double.class);
jaroslav@1378
  1256
                add(float.class, Float.class);
jaroslav@1378
  1257
                add(int.class, Integer.class);
jaroslav@1378
  1258
                add(long.class, Long.class);
jaroslav@1378
  1259
                add(short.class, Short.class);
jaroslav@1378
  1260
                add(boolean.class, Boolean.class);
jaroslav@1378
  1261
            }
jaroslav@1378
  1262
jaroslav@1378
  1263
            private static void add(Class primitiveClass, Class wrapperClass) {
jaroslav@1378
  1264
                table.put(primitiveClass,
jaroslav@1378
  1265
                    new PrimitiveTypeInfo(primitiveClass, wrapperClass));
jaroslav@1378
  1266
            }
jaroslav@1378
  1267
jaroslav@1378
  1268
            private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) {
jaroslav@1378
  1269
                assert primitiveClass.isPrimitive();
jaroslav@1378
  1270
jaroslav@1378
  1271
                baseTypeString
jaroslav@1378
  1272
                    = Array.newInstance(primitiveClass, 0)
jaroslav@1378
  1273
                    .getClass().getName().substring(1);
jaroslav@1378
  1274
                wrapperClassName = dotToSlash(wrapperClass.getName());
jaroslav@1378
  1275
                wrapperValueOfDesc
jaroslav@1378
  1276
                    = "(" + baseTypeString + ")L" + wrapperClassName + ";";
jaroslav@1378
  1277
                unwrapMethodName = primitiveClass.getName() + "Value";
jaroslav@1378
  1278
                unwrapMethodDesc = "()" + baseTypeString;
jaroslav@1378
  1279
            }
jaroslav@1378
  1280
jaroslav@1378
  1281
            public static PrimitiveTypeInfo get(Class cl) {
jaroslav@1378
  1282
                return table.get(cl);
jaroslav@1378
  1283
            }
jaroslav@1378
  1284
        }
jaroslav@1378
  1285
jaroslav@1378
  1286
        /**
jaroslav@1378
  1287
         * A ConstantPool object represents the constant pool of a class file
jaroslav@1378
  1288
         * being generated. This representation of a constant pool is designed
jaroslav@1378
  1289
         * specifically for use by ProxyGenerator; in particular, it assumes
jaroslav@1378
  1290
         * that constant pool entries will not need to be resorted (for example,
jaroslav@1378
  1291
         * by their type, as the Java compiler does), so that the final index
jaroslav@1378
  1292
         * value can be assigned and used when an entry is first created.
jaroslav@1378
  1293
         *
jaroslav@1378
  1294
         * Note that new entries cannot be created after the constant pool has
jaroslav@1378
  1295
         * been written to a class file. To prevent such logic errors, a
jaroslav@1378
  1296
         * ConstantPool instance can be marked "read only", so that further
jaroslav@1378
  1297
         * attempts to add new entries will fail with a runtime exception.
jaroslav@1378
  1298
         *
jaroslav@1378
  1299
         * See JVMS section 4.4 for more information about the constant pool of
jaroslav@1378
  1300
         * a class file.
jaroslav@1378
  1301
         */
jaroslav@1378
  1302
        private static class ConstantPool {
jaroslav@1378
  1303
jaroslav@1378
  1304
            /**
jaroslav@1378
  1305
             * list of constant pool entries, in constant pool index order.
jaroslav@1378
  1306
             *
jaroslav@1378
  1307
             * This list is used when writing the constant pool to a stream and
jaroslav@1378
  1308
             * for assigning the next index value. Note that element 0 of this
jaroslav@1378
  1309
             * list corresponds to constant pool index 1.
jaroslav@1378
  1310
             */
jaroslav@1378
  1311
            private List<Entry> pool = new ArrayList<Entry>(32);
jaroslav@1378
  1312
jaroslav@1378
  1313
            /**
jaroslav@1378
  1314
             * maps constant pool data of all types to constant pool indexes.
jaroslav@1378
  1315
             *
jaroslav@1378
  1316
             * This map is used to look up the index of an existing entry for
jaroslav@1378
  1317
             * values of all types.
jaroslav@1378
  1318
             */
jaroslav@1378
  1319
            private Map<Object, Short> map = new HashMap<Object, Short>(16);
jaroslav@1378
  1320
jaroslav@1378
  1321
            /**
jaroslav@1378
  1322
             * true if no new constant pool entries may be added
jaroslav@1378
  1323
             */
jaroslav@1378
  1324
            private boolean readOnly = false;
jaroslav@1378
  1325
jaroslav@1378
  1326
            /**
jaroslav@1378
  1327
             * Get or assign the index for a CONSTANT_Utf8 entry.
jaroslav@1378
  1328
             */
jaroslav@1378
  1329
            public short getUtf8(String s) {
jaroslav@1378
  1330
                if (s == null) {
jaroslav@1378
  1331
                    throw new NullPointerException();
jaroslav@1378
  1332
                }
jaroslav@1378
  1333
                return getValue(s);
jaroslav@1378
  1334
            }
jaroslav@1378
  1335
jaroslav@1378
  1336
            /**
jaroslav@1378
  1337
             * Get or assign the index for a CONSTANT_Integer entry.
jaroslav@1378
  1338
             */
jaroslav@1378
  1339
            public short getInteger(int i) {
jaroslav@1378
  1340
                return getValue(new Integer(i));
jaroslav@1378
  1341
            }
jaroslav@1378
  1342
jaroslav@1378
  1343
            /**
jaroslav@1378
  1344
             * Get or assign the index for a CONSTANT_Float entry.
jaroslav@1378
  1345
             */
jaroslav@1378
  1346
            public short getFloat(float f) {
jaroslav@1378
  1347
                return getValue(new Float(f));
jaroslav@1378
  1348
            }
jaroslav@1378
  1349
jaroslav@1378
  1350
            /**
jaroslav@1378
  1351
             * Get or assign the index for a CONSTANT_Class entry.
jaroslav@1378
  1352
             */
jaroslav@1378
  1353
            public short getClass(String name) {
jaroslav@1378
  1354
                short utf8Index = getUtf8(name);
jaroslav@1378
  1355
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1356
                    CONSTANT_CLASS, utf8Index));
jaroslav@1378
  1357
            }
jaroslav@1378
  1358
jaroslav@1378
  1359
            /**
jaroslav@1378
  1360
             * Get or assign the index for a CONSTANT_String entry.
jaroslav@1378
  1361
             */
jaroslav@1378
  1362
            public short getString(String s) {
jaroslav@1378
  1363
                short utf8Index = getUtf8(s);
jaroslav@1378
  1364
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1365
                    CONSTANT_STRING, utf8Index));
jaroslav@1378
  1366
            }
jaroslav@1378
  1367
jaroslav@1378
  1368
            /**
jaroslav@1378
  1369
             * Get or assign the index for a CONSTANT_FieldRef entry.
jaroslav@1378
  1370
             */
jaroslav@1378
  1371
            public short getFieldRef(String className,
jaroslav@1378
  1372
                String name, String descriptor) {
jaroslav@1378
  1373
                short classIndex = getClass(className);
jaroslav@1378
  1374
                short nameAndTypeIndex = getNameAndType(name, descriptor);
jaroslav@1378
  1375
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1376
                    CONSTANT_FIELD, classIndex, nameAndTypeIndex));
jaroslav@1378
  1377
            }
jaroslav@1378
  1378
jaroslav@1378
  1379
            /**
jaroslav@1378
  1380
             * Get or assign the index for a CONSTANT_MethodRef entry.
jaroslav@1378
  1381
             */
jaroslav@1378
  1382
            public short getMethodRef(String className,
jaroslav@1378
  1383
                String name, String descriptor) {
jaroslav@1378
  1384
                short classIndex = getClass(className);
jaroslav@1378
  1385
                short nameAndTypeIndex = getNameAndType(name, descriptor);
jaroslav@1378
  1386
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1387
                    CONSTANT_METHOD, classIndex, nameAndTypeIndex));
jaroslav@1378
  1388
            }
jaroslav@1378
  1389
jaroslav@1378
  1390
            /**
jaroslav@1378
  1391
             * Get or assign the index for a CONSTANT_InterfaceMethodRef entry.
jaroslav@1378
  1392
             */
jaroslav@1378
  1393
            public short getInterfaceMethodRef(String className, String name,
jaroslav@1378
  1394
                String descriptor) {
jaroslav@1378
  1395
                short classIndex = getClass(className);
jaroslav@1378
  1396
                short nameAndTypeIndex = getNameAndType(name, descriptor);
jaroslav@1378
  1397
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1398
                    CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex));
jaroslav@1378
  1399
            }
jaroslav@1378
  1400
jaroslav@1378
  1401
            /**
jaroslav@1378
  1402
             * Get or assign the index for a CONSTANT_NameAndType entry.
jaroslav@1378
  1403
             */
jaroslav@1378
  1404
            public short getNameAndType(String name, String descriptor) {
jaroslav@1378
  1405
                short nameIndex = getUtf8(name);
jaroslav@1378
  1406
                short descriptorIndex = getUtf8(descriptor);
jaroslav@1378
  1407
                return getIndirect(new IndirectEntry(
jaroslav@1378
  1408
                    CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex));
jaroslav@1378
  1409
            }
jaroslav@1378
  1410
jaroslav@1378
  1411
            /**
jaroslav@1378
  1412
             * Set this ConstantPool instance to be "read only".
jaroslav@1378
  1413
             *
jaroslav@1378
  1414
             * After this method has been called, further requests to get an
jaroslav@1378
  1415
             * index for a non-existent entry will cause an InternalError to be
jaroslav@1378
  1416
             * thrown instead of creating of the entry.
jaroslav@1378
  1417
             */
jaroslav@1378
  1418
            public void setReadOnly() {
jaroslav@1378
  1419
                readOnly = true;
jaroslav@1378
  1420
            }
jaroslav@1378
  1421
jaroslav@1378
  1422
            /**
jaroslav@1378
  1423
             * Write this constant pool to a stream as part of the class file
jaroslav@1378
  1424
             * format.
jaroslav@1378
  1425
             *
jaroslav@1378
  1426
             * This consists of writing the "constant_pool_count" and
jaroslav@1378
  1427
             * "constant_pool[]" items of the "ClassFile" structure, as
jaroslav@1378
  1428
             * described in JVMS section 4.1.
jaroslav@1378
  1429
             */
jaroslav@1378
  1430
            public void write(OutputStream out) throws IOException {
jaroslav@1378
  1431
                DataOutputStream dataOut = new DataOutputStream(out);
jaroslav@1378
  1432
jaroslav@1378
  1433
                // constant_pool_count: number of entries plus one
jaroslav@1378
  1434
                dataOut.writeShort(pool.size() + 1);
jaroslav@1378
  1435
jaroslav@1378
  1436
                for (Entry e : pool) {
jaroslav@1378
  1437
                    e.write(dataOut);
jaroslav@1378
  1438
                }
jaroslav@1378
  1439
            }
jaroslav@1378
  1440
jaroslav@1378
  1441
            /**
jaroslav@1378
  1442
             * Add a new constant pool entry and return its index.
jaroslav@1378
  1443
             */
jaroslav@1378
  1444
            private short addEntry(Entry entry) {
jaroslav@1378
  1445
                pool.add(entry);
jaroslav@1378
  1446
                /*
jaroslav@1378
  1447
                 * Note that this way of determining the index of the
jaroslav@1378
  1448
                 * added entry is wrong if this pool supports
jaroslav@1378
  1449
                 * CONSTANT_Long or CONSTANT_Double entries.
jaroslav@1378
  1450
                 */
jaroslav@1378
  1451
                if (pool.size() >= 65535) {
jaroslav@1378
  1452
                    throw new IllegalArgumentException(
jaroslav@1378
  1453
                        "constant pool size limit exceeded");
jaroslav@1378
  1454
                }
jaroslav@1378
  1455
                return (short) pool.size();
jaroslav@1378
  1456
            }
jaroslav@1378
  1457
jaroslav@1378
  1458
            /**
jaroslav@1378
  1459
             * Get or assign the index for an entry of a type that contains a
jaroslav@1378
  1460
             * direct value. The type of the given object determines the type of
jaroslav@1378
  1461
             * the desired entry as follows:
jaroslav@1378
  1462
             *
jaroslav@1378
  1463
             * java.lang.String CONSTANT_Utf8 java.lang.Integer CONSTANT_Integer
jaroslav@1378
  1464
             * java.lang.Float CONSTANT_Float java.lang.Long CONSTANT_Long
jaroslav@1378
  1465
             * java.lang.Double CONSTANT_DOUBLE
jaroslav@1378
  1466
             */
jaroslav@1378
  1467
            private short getValue(Object key) {
jaroslav@1378
  1468
                Short index = map.get(key);
jaroslav@1378
  1469
                if (index != null) {
jaroslav@1378
  1470
                    return index.shortValue();
jaroslav@1378
  1471
                } else {
jaroslav@1378
  1472
                    if (readOnly) {
jaroslav@1378
  1473
                        throw new InternalError(
jaroslav@1378
  1474
                            "late constant pool addition: " + key);
jaroslav@1378
  1475
                    }
jaroslav@1378
  1476
                    short i = addEntry(new ValueEntry(key));
jaroslav@1378
  1477
                    map.put(key, new Short(i));
jaroslav@1378
  1478
                    return i;
jaroslav@1378
  1479
                }
jaroslav@1378
  1480
            }
jaroslav@1378
  1481
jaroslav@1378
  1482
            /**
jaroslav@1378
  1483
             * Get or assign the index for an entry of a type that contains
jaroslav@1378
  1484
             * references to other constant pool entries.
jaroslav@1378
  1485
             */
jaroslav@1378
  1486
            private short getIndirect(IndirectEntry e) {
jaroslav@1378
  1487
                Short index = map.get(e);
jaroslav@1378
  1488
                if (index != null) {
jaroslav@1378
  1489
                    return index.shortValue();
jaroslav@1378
  1490
                } else {
jaroslav@1378
  1491
                    if (readOnly) {
jaroslav@1378
  1492
                        throw new InternalError("late constant pool addition");
jaroslav@1378
  1493
                    }
jaroslav@1378
  1494
                    short i = addEntry(e);
jaroslav@1378
  1495
                    map.put(e, new Short(i));
jaroslav@1378
  1496
                    return i;
jaroslav@1378
  1497
                }
jaroslav@1378
  1498
            }
jaroslav@1378
  1499
jaroslav@1378
  1500
            /**
jaroslav@1378
  1501
             * Entry is the abstact superclass of all constant pool entry types
jaroslav@1378
  1502
             * that can be stored in the "pool" list; its purpose is to define a
jaroslav@1378
  1503
             * common method for writing constant pool entries to a class file.
jaroslav@1378
  1504
             */
jaroslav@1378
  1505
            private static abstract class Entry {
jaroslav@1378
  1506
jaroslav@1378
  1507
                public abstract void write(DataOutputStream out)
jaroslav@1378
  1508
                    throws IOException;
jaroslav@1378
  1509
            }
jaroslav@1378
  1510
jaroslav@1378
  1511
            /**
jaroslav@1378
  1512
             * ValueEntry represents a constant pool entry of a type that
jaroslav@1378
  1513
             * contains a direct value (see the comments for the "getValue"
jaroslav@1378
  1514
             * method for a list of such types).
jaroslav@1378
  1515
             *
jaroslav@1378
  1516
             * ValueEntry objects are not used as keys for their entries in the
jaroslav@1378
  1517
             * Map "map", so no useful hashCode or equals methods are defined.
jaroslav@1378
  1518
             */
jaroslav@1378
  1519
            private static class ValueEntry extends Entry {
jaroslav@1378
  1520
jaroslav@1378
  1521
                private Object value;
jaroslav@1378
  1522
jaroslav@1378
  1523
                public ValueEntry(Object value) {
jaroslav@1378
  1524
                    this.value = value;
jaroslav@1378
  1525
                }
jaroslav@1378
  1526
jaroslav@1378
  1527
                public void write(DataOutputStream out) throws IOException {
jaroslav@1378
  1528
                    if (value instanceof String) {
jaroslav@1378
  1529
                        out.writeByte(CONSTANT_UTF8);
jaroslav@1378
  1530
                        out.writeUTF((String) value);
jaroslav@1378
  1531
                    } else if (value instanceof Integer) {
jaroslav@1378
  1532
                        out.writeByte(CONSTANT_INTEGER);
jaroslav@1378
  1533
                        out.writeInt(((Integer) value).intValue());
jaroslav@1378
  1534
                    } else if (value instanceof Float) {
jaroslav@1378
  1535
                        out.writeByte(CONSTANT_FLOAT);
jaroslav@1378
  1536
                        out.writeFloat(((Float) value).floatValue());
jaroslav@1378
  1537
                    } else if (value instanceof Long) {
jaroslav@1378
  1538
                        out.writeByte(CONSTANT_LONG);
jaroslav@1378
  1539
                        out.writeLong(((Long) value).longValue());
jaroslav@1378
  1540
                    } else if (value instanceof Double) {
jaroslav@1378
  1541
                        out.writeDouble(CONSTANT_DOUBLE);
jaroslav@1378
  1542
                        out.writeDouble(((Double) value).doubleValue());
jaroslav@1378
  1543
                    } else {
jaroslav@1378
  1544
                        throw new InternalError("bogus value entry: " + value);
jaroslav@1378
  1545
                    }
jaroslav@1378
  1546
                }
jaroslav@1378
  1547
            }
jaroslav@1378
  1548
jaroslav@1378
  1549
            /**
jaroslav@1378
  1550
             * IndirectEntry represents a constant pool entry of a type that
jaroslav@1378
  1551
             * references other constant pool entries, i.e., the following
jaroslav@1378
  1552
             * types:
jaroslav@1378
  1553
             *
jaroslav@1378
  1554
             * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref,
jaroslav@1378
  1555
             * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and
jaroslav@1378
  1556
             * CONSTANT_NameAndType.
jaroslav@1378
  1557
             *
jaroslav@1378
  1558
             * Each of these entry types contains either one or two indexes of
jaroslav@1378
  1559
             * other constant pool entries.
jaroslav@1378
  1560
             *
jaroslav@1378
  1561
             * IndirectEntry objects are used as the keys for their entries in
jaroslav@1378
  1562
             * the Map "map", so the hashCode and equals methods are overridden
jaroslav@1378
  1563
             * to allow matching.
jaroslav@1378
  1564
             */
jaroslav@1378
  1565
            private static class IndirectEntry extends Entry {
jaroslav@1378
  1566
jaroslav@1378
  1567
                private int tag;
jaroslav@1378
  1568
                private short index0;
jaroslav@1378
  1569
                private short index1;
jaroslav@1378
  1570
jaroslav@1378
  1571
                /**
jaroslav@1378
  1572
                 * Construct an IndirectEntry for a constant pool entry type
jaroslav@1378
  1573
                 * that contains one index of another entry.
jaroslav@1378
  1574
                 */
jaroslav@1378
  1575
                public IndirectEntry(int tag, short index) {
jaroslav@1378
  1576
                    this.tag = tag;
jaroslav@1378
  1577
                    this.index0 = index;
jaroslav@1378
  1578
                    this.index1 = 0;
jaroslav@1378
  1579
                }
jaroslav@1378
  1580
jaroslav@1378
  1581
                /**
jaroslav@1378
  1582
                 * Construct an IndirectEntry for a constant pool entry type
jaroslav@1378
  1583
                 * that contains two indexes for other entries.
jaroslav@1378
  1584
                 */
jaroslav@1378
  1585
                public IndirectEntry(int tag, short index0, short index1) {
jaroslav@1378
  1586
                    this.tag = tag;
jaroslav@1378
  1587
                    this.index0 = index0;
jaroslav@1378
  1588
                    this.index1 = index1;
jaroslav@1378
  1589
                }
jaroslav@1378
  1590
jaroslav@1378
  1591
                public void write(DataOutputStream out) throws IOException {
jaroslav@1378
  1592
                    out.writeByte(tag);
jaroslav@1378
  1593
                    out.writeShort(index0);
jaroslav@1378
  1594
                    /*
jaroslav@1378
  1595
                     * If this entry type contains two indexes, write
jaroslav@1378
  1596
                     * out the second, too.
jaroslav@1378
  1597
                     */
jaroslav@1378
  1598
                    if (tag == CONSTANT_FIELD
jaroslav@1378
  1599
                        || tag == CONSTANT_METHOD
jaroslav@1378
  1600
                        || tag == CONSTANT_INTERFACEMETHOD
jaroslav@1378
  1601
                        || tag == CONSTANT_NAMEANDTYPE) {
jaroslav@1378
  1602
                        out.writeShort(index1);
jaroslav@1378
  1603
                    }
jaroslav@1378
  1604
                }
jaroslav@1378
  1605
jaroslav@1378
  1606
                public int hashCode() {
jaroslav@1378
  1607
                    return tag + index0 + index1;
jaroslav@1378
  1608
                }
jaroslav@1378
  1609
jaroslav@1378
  1610
                public boolean equals(Object obj) {
jaroslav@1378
  1611
                    if (obj instanceof IndirectEntry) {
jaroslav@1378
  1612
                        IndirectEntry other = (IndirectEntry) obj;
jaroslav@1378
  1613
                        if (tag == other.tag
jaroslav@1378
  1614
                            && index0 == other.index0 && index1 == other.index1) {
jaroslav@1378
  1615
                            return true;
jaroslav@1378
  1616
                        }
jaroslav@1378
  1617
                    }
jaroslav@1378
  1618
                    return false;
jaroslav@1378
  1619
                }
jaroslav@1378
  1620
            }
jaroslav@1378
  1621
        }
jaroslav@1378
  1622
    }
jaroslav@1378
  1623
    
jaroslav@601
  1624
}