1 /*
   2  * Copyright (c) 2015, 2016, 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.misc.VM;
  64 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
  65 import jdk.internal.module.Resources;
  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 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 
 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 
 132         BuiltinClassLoader loader() { return loader; }
 133         ModuleReference mref() { return mref; }
 134         String name() { return mref.descriptor().name(); }
 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      */
 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      */
 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      */
 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      */
 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
 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      */
 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
 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
 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      */
 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      */
 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      */
 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      */
 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      */
 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
 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
 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
 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 loadCass} to load a class with the specified
 587      * binary name. This method returns {@code null} when the class is not
 588      * found.
 589      */
 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 loadCass} to load a class with the specified
 636      * binary name. This method returns {@code null} when the class is not
 637      * found.
 638      */
 639     protected  Class<?> loadClassOrNull(String cn) {
 640         return loadClassOrNull(cn, false);
 641     }
 642 
 643     /**
 644      * Find 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      */
 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      * Find 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      * Get the Package with the specified package name. If defined
 835      * then verify that it against the manifest and code source.
 836      *
 837      * @throws SecurityException if there is a sealing violation (JAR spec)
 838      */
 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 use 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      */
 866     private Package definePackage(String pn, Manifest man, URL url) {
 867         String specTitle = null;
 868         String specVersion = null;
 869         String specVendor = null;
 870         String implTitle = null;
 871         String implVersion = null;
 872         String implVendor = null;
 873         String sealed = null;
 874         URL sealBase = null;
 875 
 876         if (man != null) {
 877             Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/"));
 878             if (attr != null) {
 879                 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
 880                 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
 881                 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
 882                 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
 883                 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
 884                 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
 885                 sealed = attr.getValue(Attributes.Name.SEALED);
 886             }
 887 
 888             attr = man.getMainAttributes();
 889             if (attr != null) {
 890                 if (specTitle == null)
 891                     specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
 892                 if (specVersion == null)
 893                     specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
 894                 if (specVendor == null)
 895                     specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
 896                 if (implTitle == null)
 897                     implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
 898                 if (implVersion == null)
 899                     implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
 900                 if (implVendor == null)
 901                     implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
 902                 if (sealed == null)
 903                     sealed = attr.getValue(Attributes.Name.SEALED);
 904             }
 905 
 906             // package is sealed
 907             if ("true".equalsIgnoreCase(sealed))
 908                 sealBase = url;
 909         }
 910         return definePackage(pn,
 911                 specTitle,
 912                 specVersion,
 913                 specVendor,
 914                 implTitle,
 915                 implVersion,
 916                 implVendor,
 917                 sealBase);
 918     }
 919 
 920     /**
 921      * Returns {@code true} if the specified package name is sealed according to
 922      * the given manifest.
 923      */
 924     private boolean isSealed(String pn, Manifest man) {
 925         String path = pn.replace('.', '/').concat("/");
 926         Attributes attr = man.getAttributes(path);
 927         String sealed = null;
 928         if (attr != null)
 929             sealed = attr.getValue(Attributes.Name.SEALED);
 930         if (sealed == null && (attr = man.getMainAttributes()) != null)
 931             sealed = attr.getValue(Attributes.Name.SEALED);
 932         return "true".equalsIgnoreCase(sealed);
 933     }
 934 
 935     // -- permissions
 936 
 937     /**
 938      * Returns the permissions for the given CodeSource.
 939      */
 940     @Override
 941     protected PermissionCollection getPermissions(CodeSource cs) {
 942         PermissionCollection perms = super.getPermissions(cs);
 943 
 944         // add the permission to access the resource
 945         URL url = cs.getLocation();
 946         if (url == null)
 947             return perms;
 948 
 949         // avoid opening connection when URL is to resource in run-time image
 950         if (url.getProtocol().equals("jrt")) {
 951             perms.add(new RuntimePermission("accessSystemModules"));
 952             return perms;
 953         }
 954 
 955         // open connection to determine the permission needed
 956         try {
 957             Permission p = url.openConnection().getPermission();
 958             if (p != null) {
 959                 // for directories then need recursive access
 960                 if (p instanceof FilePermission) {
 961                     String path = p.getName();
 962                     if (path.endsWith(File.separator)) {
 963                         path += "-";
 964                         p = new FilePermission(path, "read");
 965                     }
 966                 }
 967                 perms.add(p);
 968             }
 969         } catch (IOException ioe) { }
 970 
 971         return perms;
 972     }
 973 
 974 
 975     // -- miscellaneous supporting methods
 976 
 977     /**
 978      * Returns the ModuleReader for the given module, creating it if needed
 979      */
 980     private ModuleReader moduleReaderFor(ModuleReference mref) {
 981         ModuleReader reader = moduleToReader.get(mref);
 982         if (reader == null) {
 983             // avoid method reference during startup
 984             Function<ModuleReference, ModuleReader> create = new Function<>() {
 985                 public ModuleReader apply(ModuleReference moduleReference) {
 986                     try {
 987                         return mref.open();
 988                     } catch (IOException e) {
 989                         // Return a null module reader to avoid a future class
 990                         // load attempting to open the module again.
 991                         return new NullModuleReader();
 992                     }
 993                 }
 994             };
 995             reader = moduleToReader.computeIfAbsent(mref, create);
 996         }
 997         return reader;
 998     }
 999 
1000     /**
1001      * A ModuleReader that doesn't read any resources.
1002      */
1003     private static class NullModuleReader implements ModuleReader {
1004         @Override
1005         public Optional<URI> find(String name) {
1006             return Optional.empty();
1007         }
1008         @Override
1009         public Stream<String> list() {
1010             return Stream.empty();
1011         }
1012         @Override
1013         public void close() {
1014             throw new InternalError("Should not get here");
1015         }
1016     };
1017 
1018     /**
1019      * Returns true if the given module opens the given package
1020      * unconditionally.
1021      *
1022      * @implNote This method currently iterates over each of the open
1023      * packages. This will be replaced once the ModuleDescriptor.Opens
1024      * API is updated.
1025      */
1026     private boolean isOpen(ModuleReference mref, String pn) {
1027         ModuleDescriptor descriptor = mref.descriptor();
1028         if (descriptor.isOpen() || descriptor.isAutomatic())
1029             return true;
1030         for (ModuleDescriptor.Opens opens : descriptor.opens()) {
1031             String source = opens.source();
1032             if (!opens.isQualified() && source.equals(pn)) {
1033                 return true;
1034             }
1035         }
1036         return false;
1037     }
1038 
1039     /**
1040      * Checks access to the given URL. We use URLClassPath for consistent
1041      * checking with java.net.URLClassLoader.
1042      */
1043     private static URL checkURL(URL url) {
1044         return URLClassPath.checkURL(url);
1045     }
1046 }