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