1 /*
2  * Copyright (c) 2015, 2018, 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.internal.loader;
27 
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.lang.module.ModuleDescriptor;
31 import java.lang.module.ModuleReference;
32 import java.lang.module.ModuleReader;
33 import java.lang.ref.SoftReference;
34 import java.net.MalformedURLException;
35 import java.net.URI;
36 import java.net.URL;
37 import java.nio.ByteBuffer;
38 import java.security.AccessController;
39 import java.security.CodeSigner;
40 import java.security.CodeSource;
41 import java.security.PermissionCollection;
42 import java.security.PrivilegedAction;
43 import java.security.PrivilegedActionException;
44 import java.security.PrivilegedExceptionAction;
45 import java.security.SecureClassLoader;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.Enumeration;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.NoSuchElementException;
53 import java.util.Optional;
54 import java.util.concurrent.ConcurrentHashMap;
55 import java.util.function.Function;
56 import java.util.jar.Attributes;
57 import java.util.jar.Manifest;
58 import java.util.stream.Stream;
59 
60 import jdk.internal.misc.SharedSecrets;
61 import jdk.internal.misc.VM;
62 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
63 import jdk.internal.module.Resources;
64 import sun.security.util.LazyCodeSourcePermissionCollection;
65 
66 
67 /**
68  * The platform or application class loader. Resources loaded from modules
69  * defined to the boot class loader are also loaded via an instance of this
70  * ClassLoader type.
71  *
72  * <p> This ClassLoader supports loading of classes and resources from modules.
73  * Modules are defined to the ClassLoader by invoking the {@link #loadModule}
74  * method. Defining a module to this ClassLoader has the effect of making the
75  * types in the module visible. </p>
76  *
77  * <p> This ClassLoader also supports loading of classes and resources from a
78  * class path of URLs that are specified to the ClassLoader at construction
79  * time. The class path may expand at runtime (the Class-Path attribute in JAR
80  * files or via instrumentation agents). </p>
81  *
82  * <p> The delegation model used by this ClassLoader differs to the regular
83  * delegation model. When requested to load a class then this ClassLoader first
84  * maps the class name to its package name. If there is a module defined to a
85  * BuiltinClassLoader containing this package then the class loader delegates
86  * directly to that class loader. If there isn't a module containing the
87  * package then it delegates the search to the parent class loader and if not
88  * found in the parent then it searches the class path. The main difference
89  * between this and the usual delegation model is that it allows the platform
90  * class loader to delegate to the application class loader, important with
91  * upgraded modules defined to the platform class loader.
92  */
93 
94 public class BuiltinClassLoader
95     extends SecureClassLoader
96 {
97     static {
98         if (!ClassLoader.registerAsParallelCapable())
99             throw new InternalError("Unable to register as parallel capable");
100     }
101 
102     // parent ClassLoader
103     private final BuiltinClassLoader parent;
104 
105     // the URL class path, or null if there is no class path
106     private final URLClassPath ucp;
107 
108 
109     /**
110      * A module defined/loaded by a built-in class loader.
111      *
112      * A LoadedModule encapsulates a ModuleReference along with its CodeSource
113      * URL to avoid needing to create this URL when defining classes.
114      */
115     private static class LoadedModule {
116         private final BuiltinClassLoader loader;
117         private final ModuleReference mref;
118         private final URL codeSourceURL;          // may be null
119 
LoadedModule(BuiltinClassLoader loader, ModuleReference mref)120         LoadedModule(BuiltinClassLoader loader, ModuleReference mref) {
121             URL url = null;
122             if (mref.location().isPresent()) {
123                 try {
124                     url = mref.location().get().toURL();
125                 } catch (MalformedURLException | IllegalArgumentException e) { }
126             }
127             this.loader = loader;
128             this.mref = mref;
129             this.codeSourceURL = url;
130         }
131 
loader()132         BuiltinClassLoader loader() { return loader; }
mref()133         ModuleReference mref() { return mref; }
name()134         String name() { return mref.descriptor().name(); }
codeSourceURL()135         URL codeSourceURL() { return codeSourceURL; }
136     }
137 
138 
139     // maps package name to loaded module for modules in the boot layer
140     private static final Map<String, LoadedModule> packageToModule
141         = new ConcurrentHashMap<>(1024);
142 
143     // maps a module name to a module reference
144     private final Map<String, ModuleReference> nameToModule;
145 
146     // maps a module reference to a module reader
147     private final Map<ModuleReference, ModuleReader> moduleToReader;
148 
149     // cache of resource name -> list of URLs.
150     // used only for resources that are not in module packages
151     private volatile SoftReference<Map<String, List<URL>>> resourceCache;
152 
153     /**
154      * Create a new instance.
155      */
BuiltinClassLoader(String name, BuiltinClassLoader parent, URLClassPath ucp)156     BuiltinClassLoader(String name, BuiltinClassLoader parent, URLClassPath ucp) {
157         // ensure getParent() returns null when the parent is the boot loader
158         super(name, parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
159 
160         this.parent = parent;
161         this.ucp = ucp;
162 
163         this.nameToModule = new ConcurrentHashMap<>();
164         this.moduleToReader = new ConcurrentHashMap<>();
165     }
166 
167     /**
168      * Returns {@code true} if there is a class path associated with this
169      * class loader.
170      */
hasClassPath()171     boolean hasClassPath() {
172         return ucp != null;
173     }
174 
175     /**
176      * Register a module this class loader. This has the effect of making the
177      * types in the module visible.
178      */
loadModule(ModuleReference mref)179     public void loadModule(ModuleReference mref) {
180         String mn = mref.descriptor().name();
181         if (nameToModule.putIfAbsent(mn, mref) != null) {
182             throw new InternalError(mn + " already defined to this loader");
183         }
184 
185         LoadedModule loadedModule = new LoadedModule(this, mref);
186         for (String pn : mref.descriptor().packages()) {
187             LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule);
188             if (other != null) {
189                 throw new InternalError(pn + " in modules " + mn + " and "
190                                         + other.mref().descriptor().name());
191             }
192         }
193 
194         // clear resources cache if VM is already initialized
195         if (VM.isModuleSystemInited() && resourceCache != null) {
196             resourceCache = null;
197         }
198     }
199 
200     /**
201      * Returns the {@code ModuleReference} for the named module defined to
202      * this class loader; or {@code null} if not defined.
203      *
204      * @param name The name of the module to find
205      */
findModule(String name)206     protected ModuleReference findModule(String name) {
207         return nameToModule.get(name);
208     }
209 
210 
211     // -- finding resources
212 
213     /**
214      * Returns a URL to a resource of the given name in a module defined to
215      * this class loader.
216      */
217     @Override
findResource(String mn, String name)218     public URL findResource(String mn, String name) throws IOException {
219         URL url = null;
220 
221         if (mn != null) {
222             // find in module
223             ModuleReference mref = nameToModule.get(mn);
224             if (mref != null) {
225                 url = findResource(mref, name);
226             }
227         } else {
228             // find on class path
229             url = findResourceOnClassPath(name);
230         }
231 
232         return checkURL(url);  // check access before returning
233     }
234 
235     /**
236      * Returns an input stream to a resource of the given name in a module
237      * defined to this class loader.
238      */
findResourceAsStream(String mn, String name)239     public InputStream findResourceAsStream(String mn, String name)
240         throws IOException
241     {
242         // Need URL to resource when running with a security manager so that
243         // the right permission check is done.
244         if (System.getSecurityManager() != null || mn == null) {
245             URL url = findResource(mn, name);
246             return (url != null) ? url.openStream() : null;
247         }
248 
249         // find in module defined to this loader, no security manager
250         ModuleReference mref = nameToModule.get(mn);
251         if (mref != null) {
252             return moduleReaderFor(mref).open(name).orElse(null);
253         } else {
254             return null;
255         }
256     }
257 
258     /**
259      * Finds a resource with the given name in the modules defined to this
260      * class loader or its class path.
261      */
262     @Override
findResource(String name)263     public URL findResource(String name) {
264         String pn = Resources.toPackageName(name);
265         LoadedModule module = packageToModule.get(pn);
266         if (module != null) {
267 
268             // resource is in a package of a module defined to this loader
269             if (module.loader() == this) {
270                 URL url;
271                 try {
272                     url = findResource(module.name(), name); // checks URL
273                 } catch (IOException ioe) {
274                     return null;
275                 }
276                 if (url != null
277                     && (name.endsWith(".class")
278                         || url.toString().endsWith("/")
279                         || isOpen(module.mref(), pn))) {
280                     return url;
281                 }
282             }
283 
284         } else {
285 
286             // not in a module package but may be in module defined to this loader
287             try {
288                 List<URL> urls = findMiscResource(name);
289                 if (!urls.isEmpty()) {
290                     URL url = urls.get(0);
291                     if (url != null) {
292                         return checkURL(url); // check access before returning
293                     }
294                 }
295             } catch (IOException ioe) {
296                 return null;
297             }
298 
299         }
300 
301         // search class path
302         URL url = findResourceOnClassPath(name);
303         return checkURL(url);
304     }
305 
306     /**
307      * Returns an enumeration of URL objects to all the resources with the
308      * given name in modules defined to this class loader or on the class
309      * path of this loader.
310      */
311     @Override
findResources(String name)312     public Enumeration<URL> findResources(String name) throws IOException {
313         List<URL> checked = new ArrayList<>();  // list of checked URLs
314 
315         String pn = Resources.toPackageName(name);
316         LoadedModule module = packageToModule.get(pn);
317         if (module != null) {
318 
319             // resource is in a package of a module defined to this loader
320             if (module.loader() == this) {
321                 URL url = findResource(module.name(), name); // checks URL
322                 if (url != null
323                     && (name.endsWith(".class")
324                         || url.toString().endsWith("/")
325                         || isOpen(module.mref(), pn))) {
326                     checked.add(url);
327                 }
328             }
329 
330         } else {
331             // not in a package of a module defined to this loader
332             for (URL url : findMiscResource(name)) {
333                 url = checkURL(url);
334                 if (url != null) {
335                     checked.add(url);
336                 }
337             }
338         }
339 
340         // class path (not checked)
341         Enumeration<URL> e = findResourcesOnClassPath(name);
342 
343         // concat the checked URLs and the (not checked) class path
344         return new Enumeration<>() {
345             final Iterator<URL> iterator = checked.iterator();
346             URL next;
347             private boolean hasNext() {
348                 if (next != null) {
349                     return true;
350                 } else if (iterator.hasNext()) {
351                     next = iterator.next();
352                     return true;
353                 } else {
354                     // need to check each URL
355                     while (e.hasMoreElements() && next == null) {
356                         next = checkURL(e.nextElement());
357                     }
358                     return next != null;
359                 }
360             }
361             @Override
362             public boolean hasMoreElements() {
363                 return hasNext();
364             }
365             @Override
366             public URL nextElement() {
367                 if (hasNext()) {
368                     URL result = next;
369                     next = null;
370                     return result;
371                 } else {
372                     throw new NoSuchElementException();
373                 }
374             }
375         };
376 
377     }
378 
379     /**
380      * Returns the list of URLs to a "miscellaneous" resource in modules
381      * defined to this loader. A miscellaneous resource is not in a module
382      * package, e.g. META-INF/services/p.S.
383      *
384      * The cache used by this method avoids repeated searching of all modules.
385      */
findMiscResource(String name)386     private List<URL> findMiscResource(String name) throws IOException {
387         SoftReference<Map<String, List<URL>>> ref = this.resourceCache;
388         Map<String, List<URL>> map = (ref != null) ? ref.get() : null;
389         if (map == null) {
390             map = new ConcurrentHashMap<>();
391             this.resourceCache = new SoftReference<>(map);
392         } else {
393             List<URL> urls = map.get(name);
394             if (urls != null)
395                 return urls;
396         }
397 
398         // search all modules for the resource
399         List<URL> urls;
400         try {
401             urls = AccessController.doPrivileged(
402                 new PrivilegedExceptionAction<>() {
403                     @Override
404                     public List<URL> run() throws IOException {
405                         List<URL> result = null;
406                         for (ModuleReference mref : nameToModule.values()) {
407                             URI u = moduleReaderFor(mref).find(name).orElse(null);
408                             if (u != null) {
409                                 try {
410                                     if (result == null)
411                                         result = new ArrayList<>();
412                                     result.add(u.toURL());
413                                 } catch (MalformedURLException |
414                                          IllegalArgumentException e) {
415                                 }
416                             }
417                         }
418                         return (result != null) ? result : Collections.emptyList();
419                     }
420                 });
421         } catch (PrivilegedActionException pae) {
422             throw (IOException) pae.getCause();
423         }
424 
425         // only cache resources after VM is fully initialized
426         if (VM.isModuleSystemInited()) {
427             map.putIfAbsent(name, urls);
428         }
429 
430         return urls;
431     }
432 
433     /**
434      * Returns the URL to a resource in a module or {@code null} if not found.
435      */
findResource(ModuleReference mref, String name)436     private URL findResource(ModuleReference mref, String name) throws IOException {
437         URI u;
438         if (System.getSecurityManager() == null) {
439             u = moduleReaderFor(mref).find(name).orElse(null);
440         } else {
441             try {
442                 u = AccessController.doPrivileged(new PrivilegedExceptionAction<> () {
443                     @Override
444                     public URI run() throws IOException {
445                         return moduleReaderFor(mref).find(name).orElse(null);
446                     }
447                 });
448             } catch (PrivilegedActionException pae) {
449                 throw (IOException) pae.getCause();
450             }
451         }
452         if (u != null) {
453             try {
454                 return u.toURL();
455             } catch (MalformedURLException | IllegalArgumentException e) { }
456         }
457         return null;
458     }
459 
460     /**
461      * Returns the URL to a resource in a module. Returns {@code null} if not found
462      * or an I/O error occurs.
463      */
findResourceOrNull(ModuleReference mref, String name)464     private URL findResourceOrNull(ModuleReference mref, String name) {
465         try {
466             return findResource(mref, name);
467         } catch (IOException ignore) {
468             return null;
469         }
470     }
471 
472     /**
473      * Returns a URL to a resource on the class path.
474      */
findResourceOnClassPath(String name)475     private URL findResourceOnClassPath(String name) {
476         if (hasClassPath()) {
477             if (System.getSecurityManager() == null) {
478                 return ucp.findResource(name, false);
479             } else {
480                 PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
481                 return AccessController.doPrivileged(pa);
482             }
483         } else {
484             // no class path
485             return null;
486         }
487     }
488 
489     /**
490      * Returns the URLs of all resources of the given name on the class path.
491      */
findResourcesOnClassPath(String name)492     private Enumeration<URL> findResourcesOnClassPath(String name) {
493         if (hasClassPath()) {
494             if (System.getSecurityManager() == null) {
495                 return ucp.findResources(name, false);
496             } else {
497                 PrivilegedAction<Enumeration<URL>> pa;
498                 pa = () -> ucp.findResources(name, false);
499                 return AccessController.doPrivileged(pa);
500             }
501         } else {
502             // no class path
503             return Collections.emptyEnumeration();
504         }
505     }
506 
507     // -- finding/loading classes
508 
509     /**
510      * Finds the class with the specified binary name.
511      */
512     @Override
findClass(String cn)513     protected Class<?> findClass(String cn) throws ClassNotFoundException {
514         // no class loading until VM is fully initialized
515         if (!VM.isModuleSystemInited())
516             throw new ClassNotFoundException(cn);
517 
518         // find the candidate module for this class
519         LoadedModule loadedModule = findLoadedModule(cn);
520 
521         Class<?> c = null;
522         if (loadedModule != null) {
523 
524             // attempt to load class in module defined to this loader
525             if (loadedModule.loader() == this) {
526                 c = findClassInModuleOrNull(loadedModule, cn);
527             }
528 
529         } else {
530 
531             // search class path
532             if (hasClassPath()) {
533                 c = findClassOnClassPathOrNull(cn);
534             }
535 
536         }
537 
538         // not found
539         if (c == null)
540             throw new ClassNotFoundException(cn);
541 
542         return c;
543     }
544 
545     /**
546      * Finds the class with the specified binary name in a module.
547      * This method returns {@code null} if the class cannot be found
548      * or not defined in the specified module.
549      */
550     @Override
findClass(String mn, String cn)551     protected Class<?> findClass(String mn, String cn) {
552         if (mn != null) {
553             // find the candidate module for this class
554             LoadedModule loadedModule = findLoadedModule(mn, cn);
555             if (loadedModule == null) {
556                 return null;
557             }
558 
559             // attempt to load class in module defined to this loader
560             assert loadedModule.loader() == this;
561             return findClassInModuleOrNull(loadedModule, cn);
562         }
563 
564         // search class path
565         if (hasClassPath()) {
566             return findClassOnClassPathOrNull(cn);
567         }
568 
569         return null;
570     }
571 
572     /**
573      * Loads the class with the specified binary name.
574      */
575     @Override
loadClass(String cn, boolean resolve)576     protected Class<?> loadClass(String cn, boolean resolve)
577         throws ClassNotFoundException
578     {
579         Class<?> c = loadClassOrNull(cn, resolve);
580         if (c == null)
581             throw new ClassNotFoundException(cn);
582         return c;
583     }
584 
585     /**
586      * A variation of {@code loadClass} to load a class with the specified
587      * binary name. This method returns {@code null} when the class is not
588      * found.
589      */
loadClassOrNull(String cn, boolean resolve)590     protected Class<?> loadClassOrNull(String cn, boolean resolve) {
591         synchronized (getClassLoadingLock(cn)) {
592             // check if already loaded
593             Class<?> c = findLoadedClass(cn);
594 
595             if (c == null) {
596 
597                 // find the candidate module for this class
598                 LoadedModule loadedModule = findLoadedModule(cn);
599                 if (loadedModule != null) {
600 
601                     // package is in a module
602                     BuiltinClassLoader loader = loadedModule.loader();
603                     if (loader == this) {
604                         if (VM.isModuleSystemInited()) {
605                             c = findClassInModuleOrNull(loadedModule, cn);
606                         }
607                     } else {
608                         // delegate to the other loader
609                         c = loader.loadClassOrNull(cn);
610                     }
611 
612                 } else {
613 
614                     // check parent
615                     if (parent != null) {
616                         c = parent.loadClassOrNull(cn);
617                     }
618 
619                     // check class path
620                     if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
621                         c = findClassOnClassPathOrNull(cn);
622                     }
623                 }
624 
625             }
626 
627             if (resolve && c != null)
628                 resolveClass(c);
629 
630             return c;
631         }
632     }
633 
634     /**
635      * A variation of {@code loadClass} to load a class with the specified
636      * binary name. This method returns {@code null} when the class is not
637      * found.
638      */
loadClassOrNull(String cn)639     protected Class<?> loadClassOrNull(String cn) {
640         return loadClassOrNull(cn, false);
641     }
642 
643     /**
644      * Finds the candidate loaded module for the given class name.
645      * Returns {@code null} if none of the modules defined to this
646      * class loader contain the API package for the class.
647      */
findLoadedModule(String cn)648     private LoadedModule findLoadedModule(String cn) {
649         int pos = cn.lastIndexOf('.');
650         if (pos < 0)
651             return null; // unnamed package
652 
653         String pn = cn.substring(0, pos);
654         return packageToModule.get(pn);
655     }
656 
657     /**
658      * Finds the candidate loaded module for the given class name
659      * in the named module.  Returns {@code null} if the named module
660      * is not defined to this class loader or does not contain
661      * the API package for the class.
662      */
findLoadedModule(String mn, String cn)663     private LoadedModule findLoadedModule(String mn, String cn) {
664         LoadedModule loadedModule = findLoadedModule(cn);
665         if (loadedModule != null && mn.equals(loadedModule.name())) {
666             return loadedModule;
667         } else {
668             return null;
669         }
670     }
671 
672     /**
673      * Finds the class with the specified binary name if in a module
674      * defined to this ClassLoader.
675      *
676      * @return the resulting Class or {@code null} if not found
677      */
findClassInModuleOrNull(LoadedModule loadedModule, String cn)678     private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
679         if (System.getSecurityManager() == null) {
680             return defineClass(cn, loadedModule);
681         } else {
682             PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
683             return AccessController.doPrivileged(pa);
684         }
685     }
686 
687     /**
688      * Finds the class with the specified binary name on the class path.
689      *
690      * @return the resulting Class or {@code null} if not found
691      */
findClassOnClassPathOrNull(String cn)692     private Class<?> findClassOnClassPathOrNull(String cn) {
693         String path = cn.replace('.', '/').concat(".class");
694         if (System.getSecurityManager() == null) {
695             Resource res = ucp.getResource(path, false);
696             if (res != null) {
697                 try {
698                     return defineClass(cn, res);
699                 } catch (IOException ioe) {
700                     // TBD on how I/O errors should be propagated
701                 }
702             }
703             return null;
704         } else {
705             // avoid use of lambda here
706             PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
707                 public Class<?> run() {
708                     Resource res = ucp.getResource(path, false);
709                     if (res != null) {
710                         try {
711                             return defineClass(cn, res);
712                         } catch (IOException ioe) {
713                             // TBD on how I/O errors should be propagated
714                         }
715                     }
716                     return null;
717                 }
718             };
719             return AccessController.doPrivileged(pa);
720         }
721     }
722 
723     /**
724      * Defines the given binary class name to the VM, loading the class
725      * bytes from the given module.
726      *
727      * @return the resulting Class or {@code null} if an I/O error occurs
728      */
defineClass(String cn, LoadedModule loadedModule)729     private Class<?> defineClass(String cn, LoadedModule loadedModule) {
730         ModuleReference mref = loadedModule.mref();
731         ModuleReader reader = moduleReaderFor(mref);
732 
733         try {
734             ByteBuffer bb = null;
735             URL csURL = null;
736 
737             // locate class file, special handling for patched modules to
738             // avoid locating the resource twice
739             String rn = cn.replace('.', '/').concat(".class");
740             if (reader instanceof PatchedModuleReader) {
741                 Resource r = ((PatchedModuleReader)reader).findResource(rn);
742                 if (r != null) {
743                     bb = r.getByteBuffer();
744                     csURL = r.getCodeSourceURL();
745                 }
746             } else {
747                 bb = reader.read(rn).orElse(null);
748                 csURL = loadedModule.codeSourceURL();
749             }
750 
751             if (bb == null) {
752                 // class not found
753                 return null;
754             }
755 
756             CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);
757             try {
758                 // define class to VM
759                 return defineClass(cn, bb, cs);
760 
761             } finally {
762                 reader.release(bb);
763             }
764 
765         } catch (IOException ioe) {
766             // TBD on how I/O errors should be propagated
767             return null;
768         }
769     }
770 
771     /**
772      * Defines the given binary class name to the VM, loading the class
773      * bytes via the given Resource object.
774      *
775      * @return the resulting Class
776      * @throws IOException if reading the resource fails
777      * @throws SecurityException if there is a sealing violation (JAR spec)
778      */
defineClass(String cn, Resource res)779     private Class<?> defineClass(String cn, Resource res) throws IOException {
780         URL url = res.getCodeSourceURL();
781 
782         // if class is in a named package then ensure that the package is defined
783         int pos = cn.lastIndexOf('.');
784         if (pos != -1) {
785             String pn = cn.substring(0, pos);
786             Manifest man = res.getManifest();
787             defineOrCheckPackage(pn, man, url);
788         }
789 
790         // defines the class to the runtime
791         ByteBuffer bb = res.getByteBuffer();
792         if (bb != null) {
793             CodeSigner[] signers = res.getCodeSigners();
794             CodeSource cs = new CodeSource(url, signers);
795             return defineClass(cn, bb, cs);
796         } else {
797             byte[] b = res.getBytes();
798             CodeSigner[] signers = res.getCodeSigners();
799             CodeSource cs = new CodeSource(url, signers);
800             return defineClass(cn, b, 0, b.length, cs);
801         }
802     }
803 
804 
805     // -- packages
806 
807     /**
808      * Defines a package in this ClassLoader. If the package is already defined
809      * then its sealing needs to be checked if sealed by the legacy sealing
810      * mechanism.
811      *
812      * @throws SecurityException if there is a sealing violation (JAR spec)
813      */
defineOrCheckPackage(String pn, Manifest man, URL url)814     protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
815         Package pkg = getAndVerifyPackage(pn, man, url);
816         if (pkg == null) {
817             try {
818                 if (man != null) {
819                     pkg = definePackage(pn, man, url);
820                 } else {
821                     pkg = definePackage(pn, null, null, null, null, null, null, null);
822                 }
823             } catch (IllegalArgumentException iae) {
824                 // defined by another thread so need to re-verify
825                 pkg = getAndVerifyPackage(pn, man, url);
826                 if (pkg == null)
827                     throw new InternalError("Cannot find package: " + pn);
828             }
829         }
830         return pkg;
831     }
832 
833     /**
834      * Gets the Package with the specified package name. If defined
835      * then verifies it against the manifest and code source.
836      *
837      * @throws SecurityException if there is a sealing violation (JAR spec)
838      */
getAndVerifyPackage(String pn, Manifest man, URL url)839     private Package getAndVerifyPackage(String pn, Manifest man, URL url) {
840         Package pkg = getDefinedPackage(pn);
841         if (pkg != null) {
842             if (pkg.isSealed()) {
843                 if (!pkg.isSealed(url)) {
844                     throw new SecurityException(
845                         "sealing violation: package " + pn + " is sealed");
846                 }
847             } else {
848                 // can't seal package if already defined without sealing
849                 if ((man != null) && isSealed(pn, man)) {
850                     throw new SecurityException(
851                         "sealing violation: can't seal package " + pn +
852                         ": already defined");
853                 }
854             }
855         }
856         return pkg;
857     }
858 
859     /**
860      * Defines a new package in this ClassLoader. The attributes in the specified
861      * Manifest are used to get the package version and sealing information.
862      *
863      * @throws IllegalArgumentException if the package name duplicates an
864      *      existing package either in this class loader or one of its ancestors
865      * @throws SecurityException if the package name is untrusted in the manifest
866      */
definePackage(String pn, Manifest man, URL url)867     private Package definePackage(String pn, Manifest man, URL url) {
868         String specTitle = null;
869         String specVersion = null;
870         String specVendor = null;
871         String implTitle = null;
872         String implVersion = null;
873         String implVendor = null;
874         String sealed = null;
875         URL sealBase = null;
876 
877         if (man != null) {
878             Attributes attr = SharedSecrets.javaUtilJarAccess()
879                     .getTrustedAttributes(man, pn.replace('.', '/').concat("/"));
880             if (attr != null) {
881                 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
882                 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
883                 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
884                 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
885                 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
886                 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
887                 sealed = attr.getValue(Attributes.Name.SEALED);
888             }
889 
890             attr = man.getMainAttributes();
891             if (attr != null) {
892                 if (specTitle == null)
893                     specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
894                 if (specVersion == null)
895                     specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
896                 if (specVendor == null)
897                     specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
898                 if (implTitle == null)
899                     implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
900                 if (implVersion == null)
901                     implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
902                 if (implVendor == null)
903                     implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
904                 if (sealed == null)
905                     sealed = attr.getValue(Attributes.Name.SEALED);
906             }
907 
908             // package is sealed
909             if ("true".equalsIgnoreCase(sealed))
910                 sealBase = url;
911         }
912         return definePackage(pn,
913                 specTitle,
914                 specVersion,
915                 specVendor,
916                 implTitle,
917                 implVersion,
918                 implVendor,
919                 sealBase);
920     }
921 
922     /**
923      * Returns {@code true} if the specified package name is sealed according to
924      * the given manifest.
925      *
926      * @throws SecurityException if the package name is untrusted in the manifest
927      */
isSealed(String pn, Manifest man)928     private boolean isSealed(String pn, Manifest man) {
929         Attributes attr = SharedSecrets.javaUtilJarAccess()
930                 .getTrustedAttributes(man, pn.replace('.', '/').concat("/"));
931         String sealed = null;
932         if (attr != null)
933             sealed = attr.getValue(Attributes.Name.SEALED);
934         if (sealed == null && (attr = man.getMainAttributes()) != null)
935             sealed = attr.getValue(Attributes.Name.SEALED);
936         return "true".equalsIgnoreCase(sealed);
937     }
938 
939     // -- permissions
940 
941     /**
942      * Returns the permissions for the given CodeSource.
943      */
944     @Override
getPermissions(CodeSource cs)945     protected PermissionCollection getPermissions(CodeSource cs) {
946         return new LazyCodeSourcePermissionCollection(super.getPermissions(cs), cs);
947     }
948 
949     // -- miscellaneous supporting methods
950 
951     /**
952      * Returns the ModuleReader for the given module, creating it if needed.
953      */
moduleReaderFor(ModuleReference mref)954     private ModuleReader moduleReaderFor(ModuleReference mref) {
955         ModuleReader reader = moduleToReader.get(mref);
956         if (reader == null) {
957             // avoid method reference during startup
958             Function<ModuleReference, ModuleReader> create = new Function<>() {
959                 public ModuleReader apply(ModuleReference moduleReference) {
960                     try {
961                         return mref.open();
962                     } catch (IOException e) {
963                         // Return a null module reader to avoid a future class
964                         // load attempting to open the module again.
965                         return new NullModuleReader();
966                     }
967                 }
968             };
969             reader = moduleToReader.computeIfAbsent(mref, create);
970         }
971         return reader;
972     }
973 
974     /**
975      * A ModuleReader that doesn't read any resources.
976      */
977     private static class NullModuleReader implements ModuleReader {
978         @Override
find(String name)979         public Optional<URI> find(String name) {
980             return Optional.empty();
981         }
982         @Override
list()983         public Stream<String> list() {
984             return Stream.empty();
985         }
986         @Override
close()987         public void close() {
988             throw new InternalError("Should not get here");
989         }
990     };
991 
992     /**
993      * Returns true if the given module opens the given package
994      * unconditionally.
995      *
996      * @implNote This method currently iterates over each of the open
997      * packages. This will be replaced once the ModuleDescriptor.Opens
998      * API is updated.
999      */
isOpen(ModuleReference mref, String pn)1000     private boolean isOpen(ModuleReference mref, String pn) {
1001         ModuleDescriptor descriptor = mref.descriptor();
1002         if (descriptor.isOpen() || descriptor.isAutomatic())
1003             return true;
1004         for (ModuleDescriptor.Opens opens : descriptor.opens()) {
1005             String source = opens.source();
1006             if (!opens.isQualified() && source.equals(pn)) {
1007                 return true;
1008             }
1009         }
1010         return false;
1011     }
1012 
1013     /**
1014      * Checks access to the given URL. We use URLClassPath for consistent
1015      * checking with java.net.URLClassLoader.
1016      */
checkURL(URL url)1017     private static URL checkURL(URL url) {
1018         return URLClassPath.checkURL(url);
1019     }
1020 }
1021