/* * 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(); } }