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 */ 25 26 package jdk.nashorn.internal.runtime.linker; 27 28 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 29 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; 41 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 } 56 57 private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); 58 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; 66 67 ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) { 68 this.representativeClass = representativeClass; 69 if(retrieveLoader) { 70 retrieveLoader(); 71 } 72 } 73 74 Class<?> getRepresentativeClass() { 75 return representativeClass; 76 } 77 78 boolean canSee(final 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 } 86 87 ClassLoader getLoader() { 88 if(!loaderRetrieved) { 89 retrieveLoader(); 90 } 91 return getRetrievedLoader(); 92 } 93 94 ClassLoader getRetrievedLoader() { 95 assert loaderRetrieved; 96 return loader; 97 } 98 99 private void retrieveLoader() { 100 loader = representativeClass.getClassLoader(); 101 loaderRetrieved = true; 102 } 103 104 @Override 105 public boolean equals(final Object obj) { 106 return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader(); 107 } 108 109 @Override 110 public int hashCode() { 111 return System.identityHashCode(getRetrievedLoader()); 112 } 113 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 } 126 127 return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() { 128 @Override 129 public ClassAndLoader run() { 130 return getDefiningClassAndLoaderPrivileged(types); 131 } 132 }, GET_LOADER_ACC_CTXT); 133 } 134 135 static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) { 136 final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types); 137 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 } 143 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 } 153 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 } 190 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 }