src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java

Fri, 09 Aug 2013 20:48:44 +0530

author
sundar
date
Fri, 09 Aug 2013 20:48:44 +0530
changeset 492
47e2b609fe31
parent 211
3a209cbd1d8f
child 952
6d5471a497fb
child 962
ac62e33a99b0
permissions
-rw-r--r--

8022707: Revisit all doPrivileged blocks
Reviewed-by: jlaskey, hannesw

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.runtime.linker;
    28 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    30 import java.security.AccessControlContext;
    31 import java.security.AccessController;
    32 import java.security.Permissions;
    33 import java.security.PrivilegedAction;
    34 import java.security.ProtectionDomain;
    35 import java.util.Collection;
    36 import java.util.Iterator;
    37 import java.util.LinkedHashMap;
    38 import java.util.LinkedList;
    39 import java.util.List;
    40 import java.util.Map;
    42 /**
    43  * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
    44  * equals/hashCode is defined in terms of the identity of the class loader. The rationale for this class is that it
    45  * couples a class loader with a random representative class coming from that loader - this representative class is then
    46  * used to determine if one loader can see the other loader's classes.
    47  */
    48 final class ClassAndLoader {
    49     static AccessControlContext createPermAccCtxt(final String... permNames) {
    50         final Permissions perms = new Permissions();
    51         for (final String permName : permNames) {
    52             perms.add(new RuntimePermission(permName));
    53         }
    54         return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
    55     }
    57     private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader");
    59     private final Class<?> representativeClass;
    60     // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
    61     // getLoader().
    62     private ClassLoader loader;
    63     // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
    64     // the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
    65     private boolean loaderRetrieved;
    67     ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) {
    68         this.representativeClass = representativeClass;
    69         if(retrieveLoader) {
    70             retrieveLoader();
    71         }
    72     }
    74     Class<?> getRepresentativeClass() {
    75         return representativeClass;
    76     }
    78     boolean canSee(ClassAndLoader other) {
    79         try {
    80             final Class<?> otherClass = other.getRepresentativeClass();
    81             return Class.forName(otherClass.getName(), false, getLoader()) == otherClass;
    82         } catch (final ClassNotFoundException e) {
    83             return false;
    84         }
    85     }
    87     ClassLoader getLoader() {
    88         if(!loaderRetrieved) {
    89             retrieveLoader();
    90         }
    91         return getRetrievedLoader();
    92     }
    94     ClassLoader getRetrievedLoader() {
    95         assert loaderRetrieved;
    96         return loader;
    97     }
    99     private void retrieveLoader() {
   100         loader = representativeClass.getClassLoader();
   101         loaderRetrieved = true;
   102     }
   104     @Override
   105     public boolean equals(final Object obj) {
   106         return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
   107     }
   109     @Override
   110     public int hashCode() {
   111         return System.identityHashCode(getRetrievedLoader());
   112     }
   114     /**
   115      * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
   116      * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
   117      * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
   118      * @param types the input types
   119      * @return the first type from the array that is defined in a class loader that can also see all other types.
   120      */
   121     static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
   122         // Short circuit the cheap case
   123         if(types.length == 1) {
   124             return new ClassAndLoader(types[0], false);
   125         }
   127         return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
   128             @Override
   129             public ClassAndLoader run() {
   130                 return getDefiningClassAndLoaderPrivileged(types);
   131             }
   132         }, GET_LOADER_ACC_CTXT);
   133     }
   135     static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
   136         final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
   138         final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
   139         if(maximumVisibilityLoaders.size() == 1) {
   140             // Fortunate case - single maximally specific class loader; return its representative class.
   141             return it.next();
   142         }
   144         // Ambiguity; throw an error.
   145         assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
   146         final StringBuilder b = new StringBuilder();
   147         b.append(it.next().getRepresentativeClass().getCanonicalName());
   148         while(it.hasNext()) {
   149             b.append(", ").append(it.next().getRepresentativeClass().getCanonicalName());
   150         }
   151         throw typeError("extend.ambiguous.defining.class", b.toString());
   152     }
   154     /**
   155      * Given an array of types, return a subset of their class loaders that are maximal according to the
   156      * "can see other loaders' classes" relation, which is presumed to be a partial ordering.
   157      * @param types types
   158      * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
   159      */
   160     private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
   161         final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
   162         outer:  for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
   163             final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
   164             while(it.hasNext()) {
   165                 final ClassAndLoader existingMax = it.next();
   166                 final boolean candidateSeesExisting = maxCandidate.canSee(existingMax);
   167                 final boolean exitingSeesCandidate = existingMax.canSee(maxCandidate);
   168                 if(candidateSeesExisting) {
   169                     if(!exitingSeesCandidate) {
   170                         // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
   171                         it.remove();
   172                     }
   173                     // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
   174                     // about that one, as two distinct class loaders both seeing each other's classes is weird and
   175                     // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
   176                     // just not do anything, and treat them as incomparable; hopefully some later class loader that
   177                     // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
   178                     // throw an error at the end.
   179                 } else if(exitingSeesCandidate) {
   180                     // Existing sees the candidate, so drop the candidate.
   181                     continue outer;
   182                 }
   183             }
   184             // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
   185             // maximum.
   186             maximumVisibilityLoaders.add(maxCandidate);
   187         }
   188         return maximumVisibilityLoaders;
   189     }
   191     private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
   192         final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
   193         for(final Class<?> c: types) {
   194             final ClassAndLoader cl = new ClassAndLoader(c, true);
   195             if(!classesAndLoaders.containsKey(cl)) {
   196                 classesAndLoaders.put(cl, cl);
   197             }
   198         }
   199         return classesAndLoaders.keySet();
   200     }
   201 }

mercurial