private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        Class<?> proxyClass = null;

        /* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];

        // for detecting duplicates
        Set<Class<?>> interfaceSet = new HashSet<>();

        for (int i = 0; i < interfaces.length; i++) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " is not visible from class loader");
            }

            /*
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }

            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        /*
         * Using string representations of the proxy interfaces as
         * keys in the proxy class cache (instead of their Class
         * objects) is sufficient because we require the proxy
         * interfaces to be resolvable by name through the supplied
         * class loader, and it has the advantage that using a string
         * representation of a class makes for an implicit weak
         * reference to the class.
         */
        List<String> key = Arrays.asList(interfaceNames);

        /*
         * Find or create the proxy class cache for the class loader.
         */
        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
            /*
             * This mapping will remain valid for the duration of this
             * method, without further synchronization, because the mapping
             * will only be removed if the class loader becomes unreachable.
             */
        }

        /*
         * Look up the list of interfaces in the proxy class cache using
         * the key.  This lookup will result in one of three possible
         * kinds of values:
         *     null, if there is currently no proxy class for the list of
         *         interfaces in the class loader,
         *     the pendingGenerationMarker object, if a proxy class for the
         *         list of interfaces is currently being generated,
         *     or a weak reference to a Class object, if a proxy class for
         *         the list of interfaces has already been generated.
         */
        synchronized (cache) {
            /*
             * Note that we need not worry about reaping the cache for
             * entries with cleared weak references because if a proxy class
             * has been garbage collected, its class loader will have been
             * garbage collected as well, so the entire cache will be reaped
             * from the loaderToCache map.
             */
            do {
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference<?>) value).get();
                }
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        /*
                         * The class generation that we are waiting for should
                         * take a small, bounded time, so we can safely ignore
                         * thread interrupts here.
                         */
                    }
                    continue;
                } else {
                    /*
                     * No proxy class for this list of interfaces has been
                     * generated or is being generated, so we will go and
                     * generate it now.  Mark it as pending generation.
                     */
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null;     // package to define proxy class in

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use the default package.
                proxyPkg = "";
            }

            {
                // Android-changed: Generate the proxy directly instead of calling
                // through to ProxyGenerator.
                /* J2ObjC removed.
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
                */

                /*
                 * Choose a name for the proxy class to generate.
                 */
                final long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                proxyClass = generateProxy(proxyName, interfaces, loader);
            }
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {
            /*
             * We must clean up the "pending generation" state of the proxy
             * class cache entry somehow.  If a proxy class was successfully
             * generated, store it in the cache (with a weak reference);
             * otherwise, remove the reserved entry.  In all cases, notify
             * all waiters on reserved entries in this cache.
             */
            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }
        }
        return proxyClass;
    }
private static Class < ? > getProxyClass0 ( ClassLoader loader , Class < ? > ... interfaces ) { if ( interfaces . length > 65535 ) { throw new IllegalArgumentException ( "interface limit exceeded" ) ; } Class < ? > proxyClass = null ; String [ ] interfaceNames = new String [ interfaces . length ] ; Set < Class < ? > > interfaceSet = new HashSet < > ( ) ; for ( int i = 0 ; i < interfaces . length ; i ++ ) { String interfaceName = interfaces [ i ] . getName ( ) ; Class < ? > interfaceClass = null ; try { interfaceClass = Class . forName ( interfaceName , false , loader ) ; } catch ( ClassNotFoundException e ) { } if ( interfaceClass != interfaces [ i ] ) { throw new IllegalArgumentException ( interfaces [ i ] + " is not visible from class loader" ) ; } if ( ! interfaceClass . isInterface ( ) ) { throw new IllegalArgumentException ( interfaceClass . getName ( ) + " is not an interface" ) ; } if ( interfaceSet . contains ( interfaceClass ) ) { throw new IllegalArgumentException ( "repeated interface: " + interfaceClass . getName ( ) ) ; } interfaceSet . add ( interfaceClass ) ; interfaceNames [ i ] = interfaceName ; } List < String > key = Arrays . asList ( interfaceNames ) ; Map < List < String > , Object > cache ; synchronized ( loaderToCache ) { cache = loaderToCache . get ( loader ) ; if ( cache == null ) { cache = new HashMap < > ( ) ; loaderToCache . put ( loader , cache ) ; } } synchronized ( cache ) { do { Object value = cache . get ( key ) ; if ( value instanceof Reference ) { proxyClass = ( Class < ? > ) ( ( Reference < ? > ) value ) . get ( ) ; } if ( proxyClass != null ) { return proxyClass ; } else if ( value == pendingGenerationMarker ) { try { cache . wait ( ) ; } catch ( InterruptedException e ) { } continue ; } else { cache . put ( key , pendingGenerationMarker ) ; break ; } } while ( true ) ; } try { String proxyPkg = null ; for ( int i = 0 ; i < interfaces . length ; i ++ ) { int flags = interfaces [ i ] . getModifiers ( ) ; if ( ! Modifier . isPublic ( flags ) ) { String name = interfaces [ i ] . getName ( ) ; int n = name . lastIndexOf ( '.' ) ; String pkg = ( ( n == - 1 ) ? "" : name . substring ( 0 , n + 1 ) ) ; if ( proxyPkg == null ) { proxyPkg = pkg ; } else if ( ! pkg . equals ( proxyPkg ) ) { throw new IllegalArgumentException ( "non-public interfaces from different packages" ) ; } } } if ( proxyPkg == null ) { proxyPkg = "" ; } { final long num ; synchronized ( nextUniqueNumberLock ) { num = nextUniqueNumber ++ ; } String proxyName = proxyPkg + proxyClassNamePrefix + num ; proxyClass = generateProxy ( proxyName , interfaces , loader ) ; } proxyClasses . put ( proxyClass , null ) ; } finally { synchronized ( cache ) { if ( proxyClass != null ) { cache . put ( key , new WeakReference < Class < ? > > ( proxyClass ) ) ; } else { cache . remove ( key ) ; } cache . notifyAll ( ) ; } } return proxyClass ; }
Generate a proxy class.  Must call the checkProxyAccess method
to perform permission checks before calling this.