Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/loader/Loader.java
41159 views
1
/*
2
* Copyright (c) 2015, 2021, 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.lang.module.Configuration;
32
import java.lang.module.ModuleDescriptor;
33
import java.lang.module.ModuleReader;
34
import java.lang.module.ModuleReference;
35
import java.lang.module.ResolvedModule;
36
import java.net.MalformedURLException;
37
import java.net.URI;
38
import java.net.URL;
39
import java.nio.ByteBuffer;
40
import java.security.AccessControlContext;
41
import java.security.AccessController;
42
import java.security.CodeSigner;
43
import java.security.CodeSource;
44
import java.security.Permission;
45
import java.security.PermissionCollection;
46
import java.security.PrivilegedAction;
47
import java.security.PrivilegedActionException;
48
import java.security.PrivilegedExceptionAction;
49
import java.security.SecureClassLoader;
50
import java.util.ArrayList;
51
import java.util.Collection;
52
import java.util.Collections;
53
import java.util.Enumeration;
54
import java.util.HashMap;
55
import java.util.Iterator;
56
import java.util.List;
57
import java.util.Map;
58
import java.util.Objects;
59
import java.util.Optional;
60
import java.util.concurrent.ConcurrentHashMap;
61
import java.util.stream.Stream;
62
63
import jdk.internal.access.SharedSecrets;
64
import jdk.internal.module.Resources;
65
66
/**
67
* A class loader that loads classes and resources from a collection of
68
* modules, or from a single module where the class loader is a member
69
* of a pool of class loaders.
70
*
71
* <p> The delegation model used by this ClassLoader differs to the regular
72
* delegation model. When requested to load a class then this ClassLoader first
73
* maps the class name to its package name. If there a module defined to the
74
* Loader containing the package then the class loader attempts to load from
75
* that module. If the package is instead defined to a module in a "remote"
76
* ClassLoader then this class loader delegates directly to that class loader.
77
* The map of package name to remote class loader is created based on the
78
* modules read by modules defined to this class loader. If the package is not
79
* local or remote then this class loader will delegate to the parent class
80
* loader. This allows automatic modules (for example) to link to types in the
81
* unnamed module of the parent class loader.
82
*
83
* @see ModuleLayer#defineModulesWithOneLoader
84
* @see ModuleLayer#defineModulesWithManyLoaders
85
*/
86
87
public final class Loader extends SecureClassLoader {
88
89
static {
90
ClassLoader.registerAsParallelCapable();
91
}
92
93
// the pool this loader is a member of; can be null
94
private final LoaderPool pool;
95
96
// parent ClassLoader, can be null
97
private final ClassLoader parent;
98
99
// maps a module name to a module reference
100
private final Map<String, ModuleReference> nameToModule;
101
102
// maps package name to a module loaded by this class loader
103
private final Map<String, LoadedModule> localPackageToModule;
104
105
// maps package name to a remote class loader, populated post initialization
106
private final Map<String, ClassLoader> remotePackageToLoader
107
= new ConcurrentHashMap<>();
108
109
// maps a module reference to a module reader, populated lazily
110
private final Map<ModuleReference, ModuleReader> moduleToReader
111
= new ConcurrentHashMap<>();
112
113
// ACC used when loading classes and resources
114
@SuppressWarnings("removal")
115
private final AccessControlContext acc;
116
117
/**
118
* A module defined/loaded to a {@code Loader}.
119
*/
120
private static class LoadedModule {
121
private final ModuleReference mref;
122
private final URL url; // may be null
123
private final CodeSource cs;
124
125
LoadedModule(ModuleReference mref) {
126
URL url = null;
127
if (mref.location().isPresent()) {
128
try {
129
url = mref.location().get().toURL();
130
} catch (MalformedURLException | IllegalArgumentException e) { }
131
}
132
this.mref = mref;
133
this.url = url;
134
this.cs = new CodeSource(url, (CodeSigner[]) null);
135
}
136
137
ModuleReference mref() { return mref; }
138
String name() { return mref.descriptor().name(); }
139
URL location() { return url; }
140
CodeSource codeSource() { return cs; }
141
}
142
143
144
/**
145
* Creates a {@code Loader} in a loader pool that loads classes/resources
146
* from one module.
147
*/
148
@SuppressWarnings("removal")
149
public Loader(ResolvedModule resolvedModule,
150
LoaderPool pool,
151
ClassLoader parent)
152
{
153
super("Loader-" + resolvedModule.name(), parent);
154
155
this.pool = pool;
156
this.parent = parent;
157
158
ModuleReference mref = resolvedModule.reference();
159
ModuleDescriptor descriptor = mref.descriptor();
160
String mn = descriptor.name();
161
this.nameToModule = Map.of(mn, mref);
162
163
Map<String, LoadedModule> localPackageToModule = new HashMap<>();
164
LoadedModule lm = new LoadedModule(mref);
165
descriptor.packages().forEach(pn -> localPackageToModule.put(pn, lm));
166
this.localPackageToModule = localPackageToModule;
167
168
this.acc = AccessController.getContext();
169
}
170
171
/**
172
* Creates a {@code Loader} that loads classes/resources from a collection
173
* of modules.
174
*
175
* @throws IllegalArgumentException
176
* If two or more modules have the same package
177
*/
178
@SuppressWarnings("removal")
179
public Loader(Collection<ResolvedModule> modules, ClassLoader parent) {
180
super(parent);
181
182
this.pool = null;
183
this.parent = parent;
184
185
Map<String, ModuleReference> nameToModule = new HashMap<>();
186
Map<String, LoadedModule> localPackageToModule = new HashMap<>();
187
for (ResolvedModule resolvedModule : modules) {
188
ModuleReference mref = resolvedModule.reference();
189
ModuleDescriptor descriptor = mref.descriptor();
190
nameToModule.put(descriptor.name(), mref);
191
descriptor.packages().forEach(pn -> {
192
LoadedModule lm = new LoadedModule(mref);
193
if (localPackageToModule.put(pn, lm) != null)
194
throw new IllegalArgumentException("Package "
195
+ pn + " in more than one module");
196
});
197
}
198
this.nameToModule = nameToModule;
199
this.localPackageToModule = localPackageToModule;
200
201
this.acc = AccessController.getContext();
202
}
203
204
/**
205
* Completes initialization of this Loader. This method populates
206
* remotePackageToLoader with the packages of the remote modules, where
207
* "remote modules" are the modules read by modules defined to this loader.
208
*
209
* @param cf the Configuration containing at least modules to be defined to
210
* this class loader
211
*
212
* @param parentModuleLayers the parent ModuleLayers
213
*/
214
public Loader initRemotePackageMap(Configuration cf,
215
List<ModuleLayer> parentModuleLayers)
216
{
217
for (String name : nameToModule.keySet()) {
218
ResolvedModule resolvedModule = cf.findModule(name).get();
219
assert resolvedModule.configuration() == cf;
220
221
for (ResolvedModule other : resolvedModule.reads()) {
222
String mn = other.name();
223
ClassLoader loader;
224
225
if (other.configuration() == cf) {
226
227
// The module reads another module in the newly created
228
// layer. If all modules are defined to the same class
229
// loader then the packages are local.
230
if (pool == null) {
231
assert nameToModule.containsKey(mn);
232
continue;
233
}
234
235
loader = pool.loaderFor(mn);
236
assert loader != null;
237
238
} else {
239
240
// find the layer for the target module
241
ModuleLayer layer = parentModuleLayers.stream()
242
.map(parent -> findModuleLayer(parent, other.configuration()))
243
.flatMap(Optional::stream)
244
.findAny()
245
.orElseThrow(() ->
246
new InternalError("Unable to find parent layer"));
247
248
// find the class loader for the module
249
// For now we use the platform loader for modules defined to the
250
// boot loader
251
assert layer.findModule(mn).isPresent();
252
loader = layer.findLoader(mn);
253
if (loader == null)
254
loader = ClassLoaders.platformClassLoader();
255
}
256
257
// find the packages that are exported to the target module
258
ModuleDescriptor descriptor = other.reference().descriptor();
259
if (descriptor.isAutomatic()) {
260
ClassLoader l = loader;
261
descriptor.packages().forEach(pn -> remotePackage(pn, l));
262
} else {
263
String target = resolvedModule.name();
264
for (ModuleDescriptor.Exports e : descriptor.exports()) {
265
boolean delegate;
266
if (e.isQualified()) {
267
// qualified export in same configuration
268
delegate = (other.configuration() == cf)
269
&& e.targets().contains(target);
270
} else {
271
// unqualified
272
delegate = true;
273
}
274
275
if (delegate) {
276
remotePackage(e.source(), loader);
277
}
278
}
279
}
280
}
281
282
}
283
284
return this;
285
}
286
287
/**
288
* Adds to remotePackageToLoader so that an attempt to load a class in
289
* the package delegates to the given class loader.
290
*
291
* @throws IllegalStateException
292
* if the package is already mapped to a different class loader
293
*/
294
private void remotePackage(String pn, ClassLoader loader) {
295
ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader);
296
if (l != null && l != loader) {
297
throw new IllegalStateException("Package "
298
+ pn + " cannot be imported from multiple loaders");
299
}
300
}
301
302
303
/**
304
* Find the layer corresponding to the given configuration in the tree
305
* of layers rooted at the given parent.
306
*/
307
private Optional<ModuleLayer> findModuleLayer(ModuleLayer parent, Configuration cf) {
308
return SharedSecrets.getJavaLangAccess().layers(parent)
309
.filter(l -> l.configuration() == cf)
310
.findAny();
311
}
312
313
314
/**
315
* Returns the loader pool that this loader is in or {@code null} if this
316
* loader is not in a loader pool.
317
*/
318
public LoaderPool pool() {
319
return pool;
320
}
321
322
323
// -- resources --
324
325
/**
326
* Returns a URL to a resource of the given name in a module defined to
327
* this class loader.
328
*/
329
@SuppressWarnings("removal")
330
@Override
331
protected URL findResource(String mn, String name) throws IOException {
332
ModuleReference mref = (mn != null) ? nameToModule.get(mn) : null;
333
if (mref == null)
334
return null; // not defined to this class loader
335
336
// locate resource
337
URL url = null;
338
try {
339
url = AccessController.doPrivileged(
340
new PrivilegedExceptionAction<URL>() {
341
@Override
342
public URL run() throws IOException {
343
Optional<URI> ouri = moduleReaderFor(mref).find(name);
344
if (ouri.isPresent()) {
345
try {
346
return ouri.get().toURL();
347
} catch (MalformedURLException |
348
IllegalArgumentException e) { }
349
}
350
return null;
351
}
352
});
353
} catch (PrivilegedActionException pae) {
354
throw (IOException) pae.getCause();
355
}
356
357
// check access with permissions restricted by ACC
358
if (url != null && System.getSecurityManager() != null) {
359
try {
360
URL urlToCheck = url;
361
url = AccessController.doPrivileged(
362
new PrivilegedExceptionAction<URL>() {
363
@Override
364
public URL run() throws IOException {
365
return URLClassPath.checkURL(urlToCheck);
366
}
367
}, acc);
368
} catch (PrivilegedActionException pae) {
369
url = null;
370
}
371
}
372
373
return url;
374
}
375
376
@Override
377
public URL findResource(String name) {
378
String pn = Resources.toPackageName(name);
379
LoadedModule module = localPackageToModule.get(pn);
380
381
if (module != null) {
382
try {
383
URL url = findResource(module.name(), name);
384
if (url != null
385
&& (name.endsWith(".class")
386
|| url.toString().endsWith("/")
387
|| isOpen(module.mref(), pn))) {
388
return url;
389
}
390
} catch (IOException ioe) {
391
// ignore
392
}
393
394
} else {
395
for (ModuleReference mref : nameToModule.values()) {
396
try {
397
URL url = findResource(mref.descriptor().name(), name);
398
if (url != null) return url;
399
} catch (IOException ioe) {
400
// ignore
401
}
402
}
403
}
404
405
return null;
406
}
407
408
@Override
409
public Enumeration<URL> findResources(String name) throws IOException {
410
return Collections.enumeration(findResourcesAsList(name));
411
}
412
413
@Override
414
public URL getResource(String name) {
415
Objects.requireNonNull(name);
416
417
// this loader
418
URL url = findResource(name);
419
if (url == null) {
420
// parent loader
421
if (parent != null) {
422
url = parent.getResource(name);
423
} else {
424
url = BootLoader.findResource(name);
425
}
426
}
427
return url;
428
}
429
430
@Override
431
public Enumeration<URL> getResources(String name) throws IOException {
432
Objects.requireNonNull(name);
433
434
// this loader
435
List<URL> urls = findResourcesAsList(name);
436
437
// parent loader
438
Enumeration<URL> e;
439
if (parent != null) {
440
e = parent.getResources(name);
441
} else {
442
e = BootLoader.findResources(name);
443
}
444
445
// concat the URLs with the URLs returned by the parent
446
return new Enumeration<>() {
447
final Iterator<URL> iterator = urls.iterator();
448
@Override
449
public boolean hasMoreElements() {
450
return (iterator.hasNext() || e.hasMoreElements());
451
}
452
@Override
453
public URL nextElement() {
454
if (iterator.hasNext()) {
455
return iterator.next();
456
} else {
457
return e.nextElement();
458
}
459
}
460
};
461
}
462
463
/**
464
* Finds the resources with the given name in this class loader.
465
*/
466
private List<URL> findResourcesAsList(String name) throws IOException {
467
String pn = Resources.toPackageName(name);
468
LoadedModule module = localPackageToModule.get(pn);
469
if (module != null) {
470
URL url = findResource(module.name(), name);
471
if (url != null
472
&& (name.endsWith(".class")
473
|| url.toString().endsWith("/")
474
|| isOpen(module.mref(), pn))) {
475
return List.of(url);
476
} else {
477
return Collections.emptyList();
478
}
479
} else {
480
List<URL> urls = new ArrayList<>();
481
for (ModuleReference mref : nameToModule.values()) {
482
URL url = findResource(mref.descriptor().name(), name);
483
if (url != null) {
484
urls.add(url);
485
}
486
}
487
return urls;
488
}
489
}
490
491
492
// -- finding/loading classes
493
494
/**
495
* Finds the class with the specified binary name.
496
*/
497
@Override
498
protected Class<?> findClass(String cn) throws ClassNotFoundException {
499
Class<?> c = null;
500
LoadedModule loadedModule = findLoadedModule(cn);
501
if (loadedModule != null)
502
c = findClassInModuleOrNull(loadedModule, cn);
503
if (c == null)
504
throw new ClassNotFoundException(cn);
505
return c;
506
}
507
508
/**
509
* Finds the class with the specified binary name in the given module.
510
* This method returns {@code null} if the class cannot be found.
511
*/
512
@Override
513
protected Class<?> findClass(String mn, String cn) {
514
Class<?> c = null;
515
LoadedModule loadedModule = findLoadedModule(cn);
516
if (loadedModule != null && loadedModule.name().equals(mn))
517
c = findClassInModuleOrNull(loadedModule, cn);
518
return c;
519
}
520
521
/**
522
* Loads the class with the specified binary name.
523
*/
524
@Override
525
protected Class<?> loadClass(String cn, boolean resolve)
526
throws ClassNotFoundException
527
{
528
@SuppressWarnings("removal")
529
SecurityManager sm = System.getSecurityManager();
530
if (sm != null) {
531
String pn = packageName(cn);
532
if (!pn.isEmpty()) {
533
sm.checkPackageAccess(pn);
534
}
535
}
536
537
synchronized (getClassLoadingLock(cn)) {
538
// check if already loaded
539
Class<?> c = findLoadedClass(cn);
540
541
if (c == null) {
542
543
LoadedModule loadedModule = findLoadedModule(cn);
544
545
if (loadedModule != null) {
546
547
// class is in module defined to this class loader
548
c = findClassInModuleOrNull(loadedModule, cn);
549
550
} else {
551
552
// type in another module or visible via the parent loader
553
String pn = packageName(cn);
554
ClassLoader loader = remotePackageToLoader.get(pn);
555
if (loader == null) {
556
// type not in a module read by any of the modules
557
// defined to this loader, so delegate to parent
558
// class loader
559
loader = parent;
560
}
561
if (loader == null) {
562
c = BootLoader.loadClassOrNull(cn);
563
} else {
564
c = loader.loadClass(cn);
565
}
566
567
}
568
}
569
570
if (c == null)
571
throw new ClassNotFoundException(cn);
572
573
if (resolve)
574
resolveClass(c);
575
576
return c;
577
}
578
}
579
580
581
/**
582
* Finds the class with the specified binary name if in a module
583
* defined to this ClassLoader.
584
*
585
* @return the resulting Class or {@code null} if not found
586
*/
587
@SuppressWarnings("removal")
588
private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
589
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule);
590
return AccessController.doPrivileged(pa, acc);
591
}
592
593
/**
594
* Defines the given binary class name to the VM, loading the class
595
* bytes from the given module.
596
*
597
* @return the resulting Class or {@code null} if an I/O error occurs
598
*/
599
private Class<?> defineClass(String cn, LoadedModule loadedModule) {
600
ModuleReader reader = moduleReaderFor(loadedModule.mref());
601
602
try {
603
// read class file
604
String rn = cn.replace('.', '/').concat(".class");
605
ByteBuffer bb = reader.read(rn).orElse(null);
606
if (bb == null) {
607
// class not found
608
return null;
609
}
610
611
try {
612
return defineClass(cn, bb, loadedModule.codeSource());
613
} finally {
614
reader.release(bb);
615
}
616
617
} catch (IOException ioe) {
618
// TBD on how I/O errors should be propagated
619
return null;
620
}
621
}
622
623
624
// -- permissions
625
626
/**
627
* Returns the permissions for the given CodeSource.
628
*/
629
@Override
630
protected PermissionCollection getPermissions(CodeSource cs) {
631
PermissionCollection perms = super.getPermissions(cs);
632
633
URL url = cs.getLocation();
634
if (url == null)
635
return perms;
636
637
// add the permission to access the resource
638
try {
639
Permission p = url.openConnection().getPermission();
640
if (p != null) {
641
// for directories then need recursive access
642
if (p instanceof FilePermission) {
643
String path = p.getName();
644
if (path.endsWith(File.separator)) {
645
path += "-";
646
p = new FilePermission(path, "read");
647
}
648
}
649
perms.add(p);
650
}
651
} catch (IOException ioe) { }
652
653
return perms;
654
}
655
656
657
// -- miscellaneous supporting methods
658
659
/**
660
* Find the candidate 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
String pn = packageName(cn);
666
return pn.isEmpty() ? null : localPackageToModule.get(pn);
667
}
668
669
/**
670
* Returns the package name for the given class name
671
*/
672
private String packageName(String cn) {
673
int pos = cn.lastIndexOf('.');
674
return (pos < 0) ? "" : cn.substring(0, pos);
675
}
676
677
678
/**
679
* Returns the ModuleReader for the given module.
680
*/
681
private ModuleReader moduleReaderFor(ModuleReference mref) {
682
return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref));
683
}
684
685
/**
686
* Creates a ModuleReader for the given module.
687
*/
688
private ModuleReader createModuleReader(ModuleReference mref) {
689
try {
690
return mref.open();
691
} catch (IOException e) {
692
// Return a null module reader to avoid a future class load
693
// attempting to open the module again.
694
return new NullModuleReader();
695
}
696
}
697
698
/**
699
* A ModuleReader that doesn't read any resources.
700
*/
701
private static class NullModuleReader implements ModuleReader {
702
@Override
703
public Optional<URI> find(String name) {
704
return Optional.empty();
705
}
706
@Override
707
public Stream<String> list() {
708
return Stream.empty();
709
}
710
@Override
711
public void close() {
712
throw new InternalError("Should not get here");
713
}
714
}
715
716
/**
717
* Returns true if the given module opens the given package
718
* unconditionally.
719
*
720
* @implNote This method currently iterates over each of the open
721
* packages. This will be replaced once the ModuleDescriptor.Opens
722
* API is updated.
723
*/
724
private boolean isOpen(ModuleReference mref, String pn) {
725
ModuleDescriptor descriptor = mref.descriptor();
726
if (descriptor.isOpen() || descriptor.isAutomatic())
727
return true;
728
for (ModuleDescriptor.Opens opens : descriptor.opens()) {
729
String source = opens.source();
730
if (!opens.isQualified() && source.equals(pn)) {
731
return true;
732
}
733
}
734
return false;
735
}
736
}
737
738