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