Fri, 09 Aug 2013 20:48:44 +0530
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 }