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