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.ModuleReference;
  33 import java.lang.module.ModuleReader;
  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.Permission;
  42 import java.security.PermissionCollection;
  43 import java.security.PrivilegedAction;
  44 import java.security.PrivilegedActionException;
  45 import java.security.PrivilegedExceptionAction;
  46 import java.security.SecureClassLoader;
  47 import java.util.ArrayList;
  48 import java.util.Collections;
  49 import java.util.Enumeration;
  50 import java.util.List;
  51 import java.util.Map;
  52 import java.util.Optional;
  53 import java.util.concurrent.ConcurrentHashMap;
  54 import java.util.jar.Attributes;
  55 import java.util.jar.Manifest;
  56 
  57 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
  58 import jdk.internal.misc.VM;
  59 import sun.misc.URLClassPath;
  60 import sun.misc.Resource;
  61 
  62 
  63 /**
  64  * The extension or application class loader. Resources loaded from modules
  65  * defined to the boot class loader are also loaded via an instance of this
  66  * ClassLoader type.
  67  *
  68  * <p> This ClassLoader supports loading of classes and resources from modules.
  69  * Modules are defined to the ClassLoader by invoking the {@link #loadModule}
  70  * method. Defining a module to this ClassLoader has the effect of making the
  71  * types in the module visible. </p>
  72  *
  73  * <p> This ClassLoader also supports loading of classes and resources from a
  74  * class path of URLs that are specified to the ClassLoader at construction
  75  * time. The class path may expand at runtime (the Class-Path attribute in JAR
  76  * files or via instrumentation agents). </p>
  77  *
  78  * <p> The delegation model used by this ClassLoader differs to the regular
  79  * delegation model. When requested to load a class then this ClassLoader first
  80  * maps the class name to its package name. If there is a module defined to a
  81  * BuiltinClassLoader containing this package then the class loader delegates
  82  * directly to that class loader. If there isn't a module containing the
  83  * package then it delegates the search to the parent class loader and if not
  84  * found in the parent then it searches the class path. The main difference
  85  * between this and the usual delegation model is that it allows the extension
  86  * class loader to delegate to the application class loader, important with
  87  * upgraded modules defined to the extension class loader.
  88  */
  89 
  90 public class BuiltinClassLoader
  91     extends SecureClassLoader
  92 {
  93     static {
  94         if (!ClassLoader.registerAsParallelCapable())
  95             throw new InternalError();
  96     }
  97 
  98     // parent ClassLoader
  99     private final BuiltinClassLoader parent;
 100 
 101     // the URL class path or null if there is no class path
 102     private final URLClassPath ucp;
 103 
 104 
 105     /**
 106      * A module defined/loaded by a built-in class loader.
 107      *
 108      * A LoadedModule encapsulates a ModuleReference along with its CodeSource
 109      * URL to avoid needing to create this URL when define classes.
 110      */
 111     private static class LoadedModule {
 112         private final BuiltinClassLoader loader;
 113         private final ModuleReference mref;
 114         private final URL codeSourceURL;          // may be null
 115 
 116         LoadedModule(BuiltinClassLoader loader, ModuleReference mref) {
 117             URL url = null;
 118             if (mref.location().isPresent()) {
 119                 try {
 120                     url = mref.location().get().toURL();
 121                 } catch (MalformedURLException e) { }
 122             }
 123             this.loader = loader;
 124             this.mref = mref;
 125             this.codeSourceURL = url;
 126         }
 127 
 128         BuiltinClassLoader loader() { return loader; }
 129         ModuleReference mref() { return mref; }
 130         String name() { return mref.descriptor().name(); }
 131         URL codeSourceURL() { return codeSourceURL; }
 132     }
 133 
 134 
 135     // maps package name to loaded module for modules in the boot layer
 136     private static final Map<String, LoadedModule> packageToModule
 137         = new ConcurrentHashMap<>(1024);
 138 
 139     // maps a module name to a module reference
 140     private final Map<String, ModuleReference> nameToModule;
 141 
 142     // maps a module reference to a module reader
 143     private final Map<ModuleReference, ModuleReader> moduleToReader;
 144 
 145 
 146     /**
 147      * Create a new instance.
 148      */
 149     BuiltinClassLoader(BuiltinClassLoader parent, URLClassPath ucp) {
 150         // ensure getParent() returns null when the parent is the boot loader
 151         super(parent == null || parent == ClassLoaders.bootLoader() ? null : parent);
 152 
 153         this.parent = parent;
 154         this.ucp = ucp;
 155 
 156         this.nameToModule = new ConcurrentHashMap<>();
 157         this.moduleToReader = new ConcurrentHashMap<>();
 158     }
 159 
 160     /**
 161      * Register a module this this class loader. This has the effect of making
 162      * the types in the module visible.
 163      */
 164     public void loadModule(ModuleReference mref) {
 165         String mn = mref.descriptor().name();
 166         if (nameToModule.putIfAbsent(mn, mref) != null) {
 167             throw new InternalError(mn + " already defined to this loader");
 168         }
 169 
 170         LoadedModule loadedModule = new LoadedModule(this, mref);
 171         for (String pn : mref.descriptor().packages()) {
 172             LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule);
 173             if (other != null) {
 174                 throw new InternalError(pn + " in modules " + mn + " and "
 175                                         + other.mref().descriptor().name());
 176             }
 177         }
 178     }
 179 
 180     /**
 181      * Returns the {@code ModuleReference} for the named module defined to
 182      * this class loader; or {@code null} if not defined.
 183      *
 184      * @param name The name of the module to find
 185      */
 186     protected ModuleReference findModule(String name) {
 187         return nameToModule.get(name);
 188     }
 189 
 190 
 191     // -- finding resources
 192 
 193     /**
 194      * Returns a URL to a resource of the given name in a module defined to
 195      * this class loader.
 196      */
 197     @Override
 198     public URL findResource(String mn, String name) throws IOException {
 199         ModuleReference mref = nameToModule.get(mn);
 200         if (mref == null)
 201             return null;   // not defined to this class loader
 202 
 203         URL url;
 204 
 205         try {
 206             url = AccessController.doPrivileged(
 207                 new PrivilegedExceptionAction<URL>() {
 208                     @Override
 209                     public URL run() throws IOException {
 210                         URI u = moduleReaderFor(mref).find(name).orElse(null);
 211                         if (u != null) {
 212                             try {
 213                                 return u.toURL();
 214                             } catch (MalformedURLException e) { }
 215                         }
 216                         return null;
 217                     }
 218                 });
 219         } catch (PrivilegedActionException pae) {
 220             throw (IOException) pae.getCause();
 221         }
 222 
 223         // check access to the URL
 224         return checkURL(url);
 225     }
 226 
 227     /**
 228      * Returns an input stream to a resource of the given name in a module
 229      * defined to this class loader.
 230      */
 231     public InputStream findResourceAsStream(String mn, String name)
 232         throws IOException
 233     {
 234         // Need URL to resource when running with a security manager so that
 235         // the right permission check is done.
 236         SecurityManager sm = System.getSecurityManager();
 237         if (sm != null) {
 238 
 239             URL url = findResource(mn, name);
 240             return (url != null) ? url.openStream() : null;
 241 
 242         } else {
 243 
 244             ModuleReference mref = nameToModule.get(mn);
 245             if (mref == null)
 246                 return null;   // not defined to this class loader
 247 
 248             try {
 249                 return AccessController.doPrivileged(
 250                     new PrivilegedExceptionAction<InputStream>() {
 251                         @Override
 252                         public InputStream run() throws IOException {
 253                             return moduleReaderFor(mref).open(name).orElse(null);
 254                         }
 255                     });
 256             } catch (PrivilegedActionException pae) {
 257                 throw (IOException) pae.getCause();
 258             }
 259         }
 260     }
 261 
 262     /**
 263      * Finds the resource with the given name on the class path of this class
 264      * loader.
 265      */
 266     @Override
 267     public URL findResource(String name) {
 268         if (ucp != null) {
 269             PrivilegedAction<URL> pa = () -> ucp.findResource(name, false);
 270             URL url = AccessController.doPrivileged(pa);
 271             return checkURL(url);
 272         } else {
 273             return null;
 274         }
 275     }
 276 
 277     /**
 278      * Returns an enumeration of URL objects to all the resources with the
 279      * given name on the class path of this class loader.
 280      */
 281     @Override
 282     public Enumeration<URL> findResources(String name) throws IOException {
 283         if (ucp != null) {
 284             List<URL> result = new ArrayList<>();
 285             PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false);
 286             Enumeration<URL> e = AccessController.doPrivileged(pa);
 287             while (e.hasMoreElements()) {
 288                 URL url = checkURL(e.nextElement());
 289                 if (url != null) {
 290                     result.add(url);
 291                 }
 292             }
 293             return Collections.enumeration(result); // checked URLs
 294         } else {
 295             return Collections.emptyEnumeration();
 296         }
 297     }
 298 
 299 
 300     // -- finding/loading classes
 301 
 302     /**
 303      * Finds the class with the specified binary name.
 304      */
 305     @Override
 306     protected Class<?> findClass(String cn) throws ClassNotFoundException {
 307         // no class loading until VM is fully initialized
 308         if (!VM.isModuleSystemInited())
 309             throw new ClassNotFoundException(cn);
 310 
 311         // find the candidate module for this class
 312         LoadedModule loadedModule = findLoadedModule(cn);
 313 
 314         Class<?> c = null;
 315         if (loadedModule != null) {
 316 
 317             // attempt to load class in module defined to this loader
 318             if (loadedModule.loader() == this) {
 319                 c = findClassInModuleOrNull(loadedModule, cn);
 320             }
 321 
 322         } else {
 323 
 324             // search class path
 325             if (ucp != null) {
 326                 c = findClassOnClassPathOrNull(cn);
 327             }
 328 
 329         }
 330 
 331         // not found
 332         if (c == null)
 333             throw new ClassNotFoundException(cn);
 334 
 335         return c;
 336     }
 337 
 338     /**
 339      * Finds the class with the specified binary name in a given module.
 340      * This method returns {@code null} if the class cannot be found.
 341      */
 342     @Override
 343     protected Class<?> findClass(String mn, String cn) {
 344         ModuleReference mref = nameToModule.get(mn);
 345         if (mref == null)
 346             return null;   // not defined to this class loader
 347 
 348         // find the candidate module for this class
 349         LoadedModule loadedModule = findLoadedModule(cn);
 350         if (loadedModule == null || !loadedModule.name().equals(mn)) {
 351             return null;   // module name does not match
 352         }
 353 
 354         // attempt to load class in module defined to this loader
 355         assert loadedModule.loader() == this;
 356         return findClassInModuleOrNull(loadedModule, cn);
 357     }
 358 
 359     /**
 360      * Loads the class with the specified binary name.
 361      */
 362     @Override
 363     protected Class<?> loadClass(String cn, boolean resolve)
 364         throws ClassNotFoundException
 365     {
 366         Class<?> c = loadClassOrNull(cn, resolve);
 367         if (c == null)
 368             throw new ClassNotFoundException(cn);
 369         return c;
 370     }
 371 
 372     /**
 373      * A variation of {@code loadCass} to load a class with the specified
 374      * binary name. This method returns {@code null} when the class is not
 375      * found.
 376      */
 377     protected Class<?> loadClassOrNull(String cn, boolean resolve) {
 378         synchronized (getClassLoadingLock(cn)) {
 379             // check if already loaded
 380             Class<?> c = findLoadedClass(cn);
 381 
 382             if (c == null) {
 383 
 384                 // find the candidate module for this class
 385                 LoadedModule loadedModule = findLoadedModule(cn);
 386                 if (loadedModule != null) {
 387 
 388                     // package is in a module
 389                     BuiltinClassLoader loader = loadedModule.loader();
 390                     if (loader == this) {
 391                         if (VM.isModuleSystemInited()) {
 392                             c = findClassInModuleOrNull(loadedModule, cn);
 393                         }
 394                     } else {
 395                         // delegate to the other loader
 396                         c = loader.loadClassOrNull(cn);
 397                     }
 398 
 399                 } else {
 400 
 401                     // check parent
 402                     if (parent != null) {
 403                         c = parent.loadClassOrNull(cn);
 404                     }
 405 
 406                     // check class path
 407                     if (c == null && ucp != null && VM.isModuleSystemInited()) {
 408                         c = findClassOnClassPathOrNull(cn);
 409                     }
 410                 }
 411 
 412             }
 413 
 414             if (resolve && c != null)
 415                 resolveClass(c);
 416 
 417             return c;
 418         }
 419     }
 420 
 421     /**
 422      * A variation of {@code loadCass} to load a class with the specified
 423      * binary name. This method returns {@code null} when the class is not
 424      * found.
 425      */
 426     protected  Class<?> loadClassOrNull(String cn) {
 427         return loadClassOrNull(cn, false);
 428     }
 429 
 430     /**
 431      * Find the candidate loaded module for the given class name.
 432      * Returns {@code null} if none of the modules defined to this
 433      * class loader contain the API package for the class.
 434      */
 435     private LoadedModule findLoadedModule(String cn) {
 436         int pos = cn.lastIndexOf('.');
 437         if (pos < 0)
 438             return null; // unnamed package
 439 
 440         String pn = cn.substring(0, pos);
 441         return packageToModule.get(pn);
 442     }
 443 
 444     /**
 445      * Finds the class with the specified binary name if in a module
 446      * defined to this ClassLoader.
 447      *
 448      * @return the resulting Class or {@code null} if not found
 449      */
 450     private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
 451         PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
 452         return AccessController.doPrivileged(pa);
 453     }
 454 
 455     /**
 456      * Finds the class with the specified binary name on the class path.
 457      *
 458      * @return the resulting Class or {@code null} if not found
 459      */
 460     private Class<?> findClassOnClassPathOrNull(String cn) {
 461         return AccessController.doPrivileged(
 462             new PrivilegedAction<Class<?>>() {
 463                 public Class<?> run() {
 464                     String path = cn.replace('.', '/').concat(".class");
 465                     Resource res = ucp.getResource(path, false);
 466                     if (res != null) {
 467                         try {
 468                             return defineClass(cn, res);
 469                         } catch (IOException ioe) {
 470                             // TBD on how I/O errors should be propagated
 471                         }
 472                     }
 473                     return null;
 474                 }
 475             });
 476     }
 477 
 478     /**
 479      * Defines the given binary class name to the VM, loading the class
 480      * bytes from the given module.
 481      *
 482      * @return the resulting Class or {@code null} if an I/O error occurs
 483      */
 484     private Class<?> defineClass(String cn, LoadedModule loadedModule) {
 485         ModuleReference mref = loadedModule.mref();
 486         ModuleReader reader = moduleReaderFor(mref);
 487 
 488         try {
 489             ByteBuffer bb = null;
 490             URL csURL = null;
 491 
 492             // locate class file, special handling for patched modules to
 493             // avoid locating the resource twice
 494             String rn = cn.replace('.', '/').concat(".class");
 495             if (reader instanceof PatchedModuleReader) {
 496                 Resource r = ((PatchedModuleReader)reader).findResource(rn);
 497                 if (r != null) {
 498                     bb = r.getByteBuffer();
 499                     csURL = r.getCodeSourceURL();
 500                 }
 501             } else {
 502                 bb = reader.read(rn).orElse(null);
 503                 csURL = loadedModule.codeSourceURL();
 504             }
 505 
 506             if (bb == null) {
 507                 // class not found
 508                 return null;
 509             }
 510 
 511             CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);
 512             try {
 513                 // define class to VM
 514                 return defineClass(cn, bb, cs);
 515 
 516             } finally {
 517                 reader.release(bb);
 518             }
 519 
 520         } catch (IOException ioe) {
 521             // TBD on how I/O errors should be propagated
 522             return null;
 523         }
 524     }
 525 
 526     /**
 527      * Defines the given binary class name to the VM, loading the class
 528      * bytes via the given Resource object.
 529      *
 530      * @return the resulting Class
 531      * @throws IOException if reading the resource fails
 532      * @throws SecurityException if there is a sealing violation (JAR spec)
 533      */
 534     private Class<?> defineClass(String cn, Resource res) throws IOException {
 535         URL url = res.getCodeSourceURL();
 536 
 537         // if class is in a named package then ensure that the package is defined
 538         int pos = cn.lastIndexOf('.');
 539         if (pos != -1) {
 540             String pn = cn.substring(0, pos);
 541             Manifest man = res.getManifest();
 542             defineOrCheckPackage(pn, man, url);
 543         }
 544 
 545         // defines the class to the runtime
 546         ByteBuffer bb = res.getByteBuffer();
 547         if (bb != null) {
 548             CodeSigner[] signers = res.getCodeSigners();
 549             CodeSource cs = new CodeSource(url, signers);
 550             return defineClass(cn, bb, cs);
 551         } else {
 552             byte[] b = res.getBytes();
 553             CodeSigner[] signers = res.getCodeSigners();
 554             CodeSource cs = new CodeSource(url, signers);
 555             return defineClass(cn, b, 0, b.length, cs);
 556         }
 557     }
 558 
 559 
 560     // -- packages
 561 
 562     /**
 563      * Defines a package in this ClassLoader. If the package is already defined
 564      * then its sealing needs to be checked if sealed by the legacy sealing
 565      * mechanism.
 566      *
 567      * @throws SecurityException if there is a sealing violation (JAR spec)
 568      */
 569     protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
 570         Package pkg = getAndVerifyPackage(pn, man, url);
 571         if (pkg == null) {
 572             try {
 573                 if (man != null) {
 574                     pkg = definePackage(pn, man, url);
 575                 } else {
 576                     pkg = definePackage(pn, null, null, null, null, null, null, null);
 577                 }
 578             } catch (IllegalArgumentException iae) {
 579                 // defined by another thread so need to re-verify
 580                 pkg = getAndVerifyPackage(pn, man, url);
 581                 if (pkg == null)
 582                     throw new InternalError("Cannot find package: " + pn);
 583             }
 584         }
 585         return pkg;
 586     }
 587 
 588     /**
 589      * Get the Package with the specified package name. If defined
 590      * then verify that it against the manifest and code source.
 591      *
 592      * @throws SecurityException if there is a sealing violation (JAR spec)
 593      */
 594     private Package getAndVerifyPackage(String pn, Manifest man, URL url) {
 595         Package pkg = getDefinedPackage(pn);
 596         if (pkg != null) {
 597             if (pkg.isSealed()) {
 598                 if (!pkg.isSealed(url)) {
 599                     throw new SecurityException(
 600                         "sealing violation: package " + pn + " is sealed");
 601                 }
 602             } else {
 603                 // can't seal package if already defined without sealing
 604                 if ((man != null) && isSealed(pn, man)) {
 605                     throw new SecurityException(
 606                         "sealing violation: can't seal package " + pn +
 607                         ": already defined");
 608                 }
 609             }
 610         }
 611         return pkg;
 612     }
 613 
 614     /**
 615      * Defines a new package in this ClassLoader. The attributes in the specified
 616      * Manifest are use to get the package version and sealing information.
 617      *
 618      * @throws IllegalArgumentException if the package name duplicates an
 619      * existing package either in this class loader or one of its ancestors
 620      */
 621     private Package definePackage(String pn, Manifest man, URL url) {
 622         String specTitle = null;
 623         String specVersion = null;
 624         String specVendor = null;
 625         String implTitle = null;
 626         String implVersion = null;
 627         String implVendor = null;
 628         String sealed = null;
 629         URL sealBase = null;
 630 
 631         if (man != null) {
 632             Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/"));
 633             if (attr != null) {
 634                 specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
 635                 specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
 636                 specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
 637                 implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
 638                 implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
 639                 implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
 640                 sealed = attr.getValue(Attributes.Name.SEALED);
 641             }
 642 
 643             attr = man.getMainAttributes();
 644             if (attr != null) {
 645                 if (specTitle == null)
 646                     specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
 647                 if (specVersion == null)
 648                     specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
 649                 if (specVendor == null)
 650                     specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
 651                 if (implTitle == null)
 652                     implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
 653                 if (implVersion == null)
 654                     implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
 655                 if (implVendor == null)
 656                     implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
 657                 if (sealed == null)
 658                     sealed = attr.getValue(Attributes.Name.SEALED);
 659             }
 660 
 661             // package is sealed
 662             if ("true".equalsIgnoreCase(sealed))
 663                 sealBase = url;
 664         }
 665         return definePackage(pn,
 666                              specTitle,
 667                              specVersion,
 668                              specVendor,
 669                              implTitle,
 670                              implVersion,
 671                              implVendor,
 672                              sealBase);
 673     }
 674 
 675     /**
 676      * Returns {@code true} if the specified package name is sealed according to
 677      * the given manifest.
 678      */
 679     private boolean isSealed(String pn, Manifest man) {
 680         String path = pn.replace('.', '/').concat("/");
 681         Attributes attr = man.getAttributes(path);
 682         String sealed = null;
 683         if (attr != null)
 684             sealed = attr.getValue(Attributes.Name.SEALED);
 685         if (sealed == null && (attr = man.getMainAttributes()) != null)
 686             sealed = attr.getValue(Attributes.Name.SEALED);
 687         return "true".equalsIgnoreCase(sealed);
 688     }
 689 
 690     // -- permissions
 691 
 692     /**
 693      * Returns the permissions for the given CodeSource.
 694      */
 695     @Override
 696     protected PermissionCollection getPermissions(CodeSource cs) {
 697         PermissionCollection perms = super.getPermissions(cs);
 698 
 699         // add the permission to access the resource
 700         URL url = cs.getLocation();
 701         if (url == null)
 702             return perms;
 703         Permission p = null;
 704         try {
 705             p = url.openConnection().getPermission();
 706             if (p != null) {
 707                 // for directories then need recursive access
 708                 if (p instanceof FilePermission) {
 709                     String path = p.getName();
 710                     if (path.endsWith(File.separator)) {
 711                         path += "-";
 712                         p = new FilePermission(path, "read");
 713                     }
 714                 }
 715                 perms.add(p);
 716             }
 717         } catch (IOException ioe) { }
 718 
 719         return perms;
 720     }
 721 
 722 
 723     // -- miscellaneous supporting methods
 724 
 725     /**
 726      * Returns the ModuleReader for the given module.
 727      */
 728     private ModuleReader moduleReaderFor(ModuleReference mref) {
 729         return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref));
 730     }
 731 
 732     /**
 733      * Creates a ModuleReader for the given module.
 734      */
 735     private ModuleReader createModuleReader(ModuleReference mref) {
 736         try {
 737             return mref.open();
 738         } catch (IOException e) {
 739             // Return a null module reader to avoid a future class load
 740             // attempting to open the module again.
 741             return new NullModuleReader();
 742         }
 743     }
 744 
 745     /**
 746      * A ModuleReader that doesn't read any resources.
 747      */
 748     private static class NullModuleReader implements ModuleReader {
 749         @Override
 750         public Optional<URI> find(String name) {
 751             return Optional.empty();
 752         }
 753         @Override
 754         public void close() {
 755             throw new InternalError("Should not get here");
 756         }
 757     };
 758 
 759     /**
 760      * Checks access to the given URL. We use URLClassPath for consistent
 761      * checking with java.net.URLClassLoader.
 762      */
 763     private static URL checkURL(URL url) {
 764         return URLClassPath.checkURL(url);
 765     }
 766 }