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