| /* |
| * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.nashorn.internal.runtime.linker; |
| |
| import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
| |
| import java.security.AccessControlContext; |
| import java.security.AccessController; |
| import java.security.Permissions; |
| import java.security.PrivilegedAction; |
| import java.security.ProtectionDomain; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its |
| * equals/hashCode is defined in terms of the identity of the class loader. The rationale for this class is that it |
| * couples a class loader with a random representative class coming from that loader - this representative class is then |
| * used to determine if one loader can see the other loader's classes. |
| */ |
| final class ClassAndLoader { |
| static AccessControlContext createPermAccCtxt(final String... permNames) { |
| final Permissions perms = new Permissions(); |
| for (final String permName : permNames) { |
| perms.add(new RuntimePermission(permName)); |
| } |
| return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); |
| } |
| |
| private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); |
| |
| private final Class<?> representativeClass; |
| // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing, |
| // getLoader(). |
| private ClassLoader loader; |
| // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For |
| // the most basic case of looking up an already-generated adapter info for a single type, we avoid it. |
| private boolean loaderRetrieved; |
| |
| ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) { |
| this.representativeClass = representativeClass; |
| if(retrieveLoader) { |
| retrieveLoader(); |
| } |
| } |
| |
| Class<?> getRepresentativeClass() { |
| return representativeClass; |
| } |
| |
| boolean canSee(final ClassAndLoader other) { |
| try { |
| final Class<?> otherClass = other.getRepresentativeClass(); |
| return Class.forName(otherClass.getName(), false, getLoader()) == otherClass; |
| } catch (final ClassNotFoundException e) { |
| return false; |
| } |
| } |
| |
| ClassLoader getLoader() { |
| if(!loaderRetrieved) { |
| retrieveLoader(); |
| } |
| return getRetrievedLoader(); |
| } |
| |
| ClassLoader getRetrievedLoader() { |
| assert loaderRetrieved; |
| return loader; |
| } |
| |
| private void retrieveLoader() { |
| loader = representativeClass.getClassLoader(); |
| loaderRetrieved = true; |
| } |
| |
| @Override |
| public boolean equals(final Object obj) { |
| return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader(); |
| } |
| |
| @Override |
| public int hashCode() { |
| return System.identityHashCode(getRetrievedLoader()); |
| } |
| |
| /** |
| * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the |
| * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a |
| * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown. |
| * @param types the input types |
| * @return the first type from the array that is defined in a class loader that can also see all other types. |
| */ |
| static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) { |
| // Short circuit the cheap case |
| if(types.length == 1) { |
| return new ClassAndLoader(types[0], false); |
| } |
| |
| return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() { |
| @Override |
| public ClassAndLoader run() { |
| return getDefiningClassAndLoaderPrivileged(types); |
| } |
| }, GET_LOADER_ACC_CTXT); |
| } |
| |
| static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) { |
| final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types); |
| |
| final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator(); |
| if(maximumVisibilityLoaders.size() == 1) { |
| // Fortunate case - single maximally specific class loader; return its representative class. |
| return it.next(); |
| } |
| |
| // Ambiguity; throw an error. |
| assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero |
| final StringBuilder b = new StringBuilder(); |
| b.append(it.next().getRepresentativeClass().getCanonicalName()); |
| while(it.hasNext()) { |
| b.append(", ").append(it.next().getRepresentativeClass().getCanonicalName()); |
| } |
| throw typeError("extend.ambiguous.defining.class", b.toString()); |
| } |
| |
| /** |
| * Given an array of types, return a subset of their class loaders that are maximal according to the |
| * "can see other loaders' classes" relation, which is presumed to be a partial ordering. |
| * @param types types |
| * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element. |
| */ |
| private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) { |
| final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>(); |
| outer: for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) { |
| final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator(); |
| while(it.hasNext()) { |
| final ClassAndLoader existingMax = it.next(); |
| final boolean candidateSeesExisting = maxCandidate.canSee(existingMax); |
| final boolean exitingSeesCandidate = existingMax.canSee(maxCandidate); |
| if(candidateSeesExisting) { |
| if(!exitingSeesCandidate) { |
| // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal. |
| it.remove(); |
| } |
| // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do |
| // about that one, as two distinct class loaders both seeing each other's classes is weird and |
| // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll |
| // just not do anything, and treat them as incomparable; hopefully some later class loader that |
| // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and |
| // throw an error at the end. |
| } else if(exitingSeesCandidate) { |
| // Existing sees the candidate, so drop the candidate. |
| continue outer; |
| } |
| } |
| // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new |
| // maximum. |
| maximumVisibilityLoaders.add(maxCandidate); |
| } |
| return maximumVisibilityLoaders; |
| } |
| |
| private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) { |
| final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>(); |
| for(final Class<?> c: types) { |
| final ClassAndLoader cl = new ClassAndLoader(c, true); |
| if(!classesAndLoaders.containsKey(cl)) { |
| classesAndLoaders.put(cl, cl); |
| } |
| } |
| return classesAndLoaders.keySet(); |
| } |
| } |