Skip to content

Commit 53cd15f

Browse files
committed
[GR-63220] Make ModuleLayerFeature layer aware
PullRequest: graal/20331
2 parents a589d16 + 72e430f commit 53cd15f

19 files changed

+999
-69
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2024, 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+
package com.oracle.svm.core.imagelayer;
26+
27+
import java.util.function.BooleanSupplier;
28+
29+
import org.graalvm.nativeimage.Platform;
30+
import org.graalvm.nativeimage.Platforms;
31+
32+
@Platforms(Platform.HOSTED_ONLY.class)
33+
public class BuildingInitialLayerPredicate implements BooleanSupplier {
34+
@Override
35+
public boolean getAsBoolean() {
36+
return ImageLayerBuildingSupport.buildingInitialLayer();
37+
}
38+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2025, 2025, 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+
package com.oracle.svm.core.jdk;
26+
27+
import java.util.Collection;
28+
import java.util.HashMap;
29+
import java.util.HashSet;
30+
import java.util.Map;
31+
import java.util.Set;
32+
33+
import org.graalvm.nativeimage.ImageSingletons;
34+
import org.graalvm.nativeimage.Platform;
35+
import org.graalvm.nativeimage.Platforms;
36+
37+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
38+
import com.oracle.svm.core.util.UserError;
39+
40+
/**
41+
* This singleton keeps track of the {@code Module#openPackages} and {@code Module#exportedPackages}
42+
* from all image layers.
43+
*/
44+
@Platforms(Platform.HOSTED_ONLY.class)
45+
public abstract class LayeredModuleSingleton implements LayeredImageSingleton {
46+
public static final String ALL_UNNAMED_MODULE_NAME = "native-image-all-unnamed";
47+
public static final String EVERYONE_MODULE_NAME = "native-image-everyone";
48+
49+
protected final Map<String, Map<String, Set<String>>> moduleOpenPackages;
50+
protected final Map<String, Map<String, Set<String>>> moduleExportedPackages;
51+
52+
private final Map<String, Module> moduleNames = new HashMap<>();
53+
54+
private Module everyoneModule;
55+
private Module allUnnamedModule;
56+
57+
public LayeredModuleSingleton() {
58+
this(new HashMap<>(), new HashMap<>());
59+
}
60+
61+
public LayeredModuleSingleton(Map<String, Map<String, Set<String>>> moduleOpenPackages, Map<String, Map<String, Set<String>>> moduleExportedPackages) {
62+
this.moduleOpenPackages = moduleOpenPackages;
63+
this.moduleExportedPackages = moduleExportedPackages;
64+
}
65+
66+
public static LayeredModuleSingleton singleton() {
67+
return ImageSingletons.lookup(LayeredModuleSingleton.class);
68+
}
69+
70+
public void setUnnamedModules(Module everyoneModule, Module allUnnamedModule) {
71+
this.everyoneModule = everyoneModule;
72+
this.allUnnamedModule = allUnnamedModule;
73+
}
74+
75+
public Map<String, Set<String>> getOpenPackages(Module module) {
76+
return moduleOpenPackages.get(module.getName());
77+
}
78+
79+
public Map<String, Set<String>> getExportedPackages(Module module) {
80+
return moduleExportedPackages.get(module.getName());
81+
}
82+
83+
public Collection<Module> getModules() {
84+
return moduleNames.values();
85+
}
86+
87+
public void setOpenPackages(Module module, Map<String, Set<Module>> openPackages) {
88+
setPackages(module, moduleOpenPackages, openPackages, "opened");
89+
}
90+
91+
public void setExportedPackages(Module module, Map<String, Set<Module>> exportedPackages) {
92+
setPackages(module, moduleExportedPackages, exportedPackages, "exported");
93+
}
94+
95+
private void setPackages(Module module, Map<String, Map<String, Set<String>>> modulePackages, Map<String, Set<Module>> packages, String mode) {
96+
Module oldValue = moduleNames.put(module.toString(), module);
97+
if (oldValue != null && oldValue != module) {
98+
throw UserError.abort("Layered images require all modules to have a different name because their identity hash code is not consistent across layers. " +
99+
"The modules %s and %s have the same name and were added to the %s packages", module, oldValue, mode);
100+
}
101+
Map<String, Set<String>> namesMap = modulePackages.computeIfAbsent(module.getName(), k -> new HashMap<>());
102+
for (var entry : packages.entrySet()) {
103+
Set<String> modules = namesMap.computeIfAbsent(entry.getKey(), k -> new HashSet<>());
104+
modules.addAll(entry.getValue().stream().map(Module::getName).toList());
105+
modules.remove(null);
106+
if (entry.getValue().contains(allUnnamedModule)) {
107+
modules.add(ALL_UNNAMED_MODULE_NAME);
108+
}
109+
if (entry.getValue().contains(everyoneModule)) {
110+
modules.add(EVERYONE_MODULE_NAME);
111+
}
112+
}
113+
}
114+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Objects;
4040
import java.util.Set;
4141
import java.util.function.BiConsumer;
42+
import java.util.function.Function;
4243
import java.util.stream.Collectors;
4344
import java.util.stream.StreamSupport;
4445

@@ -147,6 +148,9 @@ public record ModuleResourceKey(Module module, String resource) {
147148

148149
private GlobTrieNode<ConditionWithOrigin> resourcesTrieRoot;
149150

151+
@Platforms(Platform.HOSTED_ONLY.class) //
152+
private Function<Module, Module> hostedToRuntimeModuleMapper;
153+
150154
Resources() {
151155
}
152156

@@ -194,12 +198,17 @@ public static ModuleResourceKey createStorageKey(Module module, String resourceN
194198
Module m = module != null && module.isNamed() ? module : null;
195199
if (ImageInfo.inImageBuildtimeCode()) {
196200
if (m != null) {
197-
m = RuntimeModuleSupport.instance().getRuntimeModuleForHostedModule(m);
201+
m = currentLayer().hostedToRuntimeModuleMapper.apply(m);
198202
}
199203
}
200204
return new ModuleResourceKey(m, resourceName);
201205
}
202206

207+
@Platforms(Platform.HOSTED_ONLY.class) //
208+
public void setHostedToRuntimeModuleMapper(Function<Module, Module> hostedToRuntimeModuleMapper) {
209+
this.hostedToRuntimeModuleMapper = hostedToRuntimeModuleMapper;
210+
}
211+
203212
@Platforms(Platform.HOSTED_ONLY.class)
204213
public static Set<String> getIncludedResourcesModules() {
205214
return StreamSupport.stream(currentLayer().resources.getKeys().spliterator(), false)
@@ -472,7 +481,7 @@ public static InputStream createInputStream(Module module, String resourceName)
472481
* If module is not specified or is an unnamed module and entry was not found as
473482
* classpath-resource we have to search for the resource in all modules in the image.
474483
*/
475-
for (Module m : RuntimeModuleSupport.instance().getBootLayer().modules()) {
484+
for (Module m : RuntimeModuleSupport.singleton().getBootLayer().modules()) {
476485
entry = getAtRuntime(m, resourceName, false);
477486
if (entry != MISSING_METADATA_MARKER) {
478487
isInMetadata = true;
@@ -510,7 +519,7 @@ public static Enumeration<URL> createURLs(Module module, String resourceName) {
510519

511520
/* If moduleName was unspecified we have to consider all modules in the image */
512521
if (moduleName(module) == null) {
513-
for (Module m : RuntimeModuleSupport.instance().getBootLayer().modules()) {
522+
for (Module m : RuntimeModuleSupport.singleton().getBootLayer().modules()) {
514523
ResourceStorageEntryBase entry = getAtRuntime(m, resourceName, false);
515524
if (entry == MISSING_METADATA_MARKER) {
516525
continue;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27-
import java.util.function.Function;
27+
import java.util.EnumSet;
2828

2929
import org.graalvm.nativeimage.ImageSingletons;
3030
import org.graalvm.nativeimage.Platform;
3131
import org.graalvm.nativeimage.Platforms;
3232

3333
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
34+
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
3435
import com.oracle.svm.core.heap.UnknownObjectField;
36+
import com.oracle.svm.core.imagelayer.LastImageBuildPredicate;
37+
import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
38+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
39+
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
3540

3641
/**
3742
* Runtime module support singleton, containing the runtime boot module layer. The boot module layer
@@ -42,18 +47,15 @@
4247
* counterpart. The lookup function is implemented inside the module layer synthesis feature. See
4348
* {@code ModuleLayerFeature} for more information.
4449
*/
45-
public final class RuntimeModuleSupport {
46-
47-
public static RuntimeModuleSupport instance() {
50+
@AutomaticallyRegisteredImageSingleton(onlyWith = LastImageBuildPredicate.class)
51+
public final class RuntimeModuleSupport implements ApplicationLayerOnlyImageSingleton, UnsavedSingleton {
52+
public static RuntimeModuleSupport singleton() {
4853
return ImageSingletons.lookup(RuntimeModuleSupport.class);
4954
}
5055

5156
@UnknownObjectField(availability = AfterHostedUniverse.class) //
5257
private ModuleLayer bootLayer;
5358

54-
@Platforms(Platform.HOSTED_ONLY.class) //
55-
private Function<Module, Module> hostedToRuntimeModuleMapper;
56-
5759
@Platforms(Platform.HOSTED_ONLY.class) //
5860
public void setBootLayer(ModuleLayer bootLayer) {
5961
this.bootLayer = bootLayer;
@@ -63,13 +65,8 @@ public ModuleLayer getBootLayer() {
6365
return bootLayer;
6466
}
6567

66-
@Platforms(Platform.HOSTED_ONLY.class) //
67-
public void setHostedToRuntimeModuleMapper(Function<Module, Module> hostedToRuntimeModuleMapper) {
68-
this.hostedToRuntimeModuleMapper = hostedToRuntimeModuleMapper;
68+
@Override
69+
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
70+
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
6971
}
70-
71-
public Module getRuntimeModuleForHostedModule(Module hostedModule) {
72-
return hostedToRuntimeModuleMapper.apply(hostedModule);
73-
}
74-
7572
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import java.util.Objects;
28+
import java.util.Set;
29+
2730
import com.oracle.svm.core.annotate.Alias;
2831
import com.oracle.svm.core.annotate.RecomputeFieldValue;
2932
import com.oracle.svm.core.annotate.Substitute;
3033
import com.oracle.svm.core.annotate.TargetClass;
3134
import com.oracle.svm.core.annotate.TargetElement;
35+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
3236
import com.oracle.svm.core.util.BasedOnJDKFile;
3337

3438
/**
@@ -54,6 +58,17 @@ public final class Target_java_lang_Module {
5458
// @Stable (no effect currently GR-60154)
5559
private ModuleLayer layer;
5660

61+
/**
62+
* Creating an {@link Alias} directly for {@code ALL_UNNAMED_MODULE} and {@code EVERYONE_MODULE}
63+
* makes {@code java.util.regex.Pattern} reachable, which increases the size of the binary.
64+
*/
65+
// Checkstyle: stop
66+
@Alias //
67+
private static Set<Module> ALL_UNNAMED_MODULE_SET;
68+
@Alias //
69+
private static Set<Module> EVERYONE_SET;
70+
// Checkstyle: resume
71+
5772
@Substitute
5873
@TargetElement(onlyWith = ForeignDisabled.class)
5974
@SuppressWarnings("static-method")
@@ -93,4 +108,35 @@ private static void addExportsToAll0(Module from, String pn) {
93108
private static void addExportsToAllUnnamed0(Module from, String pn) {
94109
ModuleNative.addExportsToAllUnnamed(from, pn);
95110
}
111+
112+
@Substitute
113+
@SuppressWarnings("static-method")
114+
private boolean allows(Set<Module> targets, Module module) {
115+
if (targets != null) {
116+
Module everyoneModule = EVERYONE_SET.stream().findFirst().get();
117+
if (targets.contains(everyoneModule)) {
118+
return true;
119+
}
120+
if (module != everyoneModule) {
121+
if (targets.contains(module)) {
122+
return true;
123+
}
124+
if (!module.isNamed() && targets.contains(ALL_UNNAMED_MODULE_SET.stream().findFirst().get())) {
125+
return true;
126+
}
127+
if (ImageLayerBuildingSupport.buildingImageLayer()) {
128+
for (var m : targets) {
129+
/*
130+
* This is based on the assumption that in Layered Image, all modules have
131+
* different names. This is ensured in LayeredModuleSingleton.setPackages.
132+
*/
133+
if (Objects.equals(m.getName(), module.getName())) {
134+
return true;
135+
}
136+
}
137+
}
138+
}
139+
}
140+
return false;
141+
}
96142
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,24 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import java.util.List;
28+
29+
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
30+
2731
import com.oracle.svm.core.annotate.Alias;
2832
import com.oracle.svm.core.annotate.RecomputeFieldValue;
2933
import com.oracle.svm.core.annotate.Substitute;
3034
import com.oracle.svm.core.annotate.TargetClass;
31-
import jdk.internal.loader.ClassLoaderValue;
32-
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
3335

34-
import java.util.List;
36+
import jdk.internal.loader.ClassLoaderValue;
3537

3638
@SuppressWarnings("unused")
3739
@TargetClass(value = java.lang.ModuleLayer.class)
3840
final class Target_java_lang_ModuleLayer {
3941

4042
@Substitute
4143
public static ModuleLayer boot() {
42-
return RuntimeModuleSupport.instance().getBootLayer();
44+
return RuntimeModuleSupport.singleton().getBootLayer();
4345
}
4446

4547
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ModuleLayerCLVTransformer.class, isFinal = true) //

substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ struct SharedLayerSnapshot {
278278
dynamicHubInfos @18 :List(DynamicHubInfo);
279279
hostedMethods @19 :List(PersistedHostedMethod);
280280
nodeClassMapLocation @20 :Text;
281+
sharedLayerBootLayerModules @21 :List(Text);
282+
layeredModule @22 :LayeredModule;
281283
}
282284

283285
struct StaticFinalFieldFoldingSingleton {
@@ -293,6 +295,21 @@ struct LayeredRuntimeMetadataSingleton {
293295
fields @1 :List(FieldId);
294296
}
295297

298+
struct LayeredModule {
299+
openModulePackages @0 :List(ModulePackages);
300+
exportedModulePackages @1 :List(ModulePackages);
301+
}
302+
303+
struct ModulePackages {
304+
moduleKey @0 :Text;
305+
packages @1 :List(Packages);
306+
}
307+
308+
struct Packages {
309+
packageKey @0 :Text;
310+
modules @1 :List(Text);
311+
}
312+
296313
struct PrimitiveValue {
297314
typeChar @0 :Int8;
298315
rawValue @1 :Int64;

0 commit comments

Comments
 (0)