Skip to content

Commit d8f6907

Browse files
committed
[GR-53801] NullPointerException: Cannot invoke "com.oracle.truffle.polyglot.InternalResourceRoots$Root.path()" because "this.owningRoot" is null.
PullRequest: graal/17651
2 parents 6eea094 + 62cf889 commit d8f6907

File tree

11 files changed

+371
-176
lines changed

11 files changed

+371
-176
lines changed

truffle/src/com.oracle.truffle.api.test/src/META-INF/native-image/reflect-config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@
156156
{ "name": "log", "parameterTypes": ["java.lang.Throwable", "java.lang.String"] }
157157
]
158158
},
159+
{
160+
"name": "com.oracle.truffle.polyglot.InternalResourceRoots",
161+
"methods": [
162+
{ "name": "getInstance", "parameterTypes": [] }
163+
]
164+
},
159165
{
160166
"name": "com.oracle.truffle.polyglot.FileSystems$PreInitializeContextFileSystem",
161167
"allDeclaredConstructors": true, "allPublicConstructors": true, "allDeclaredMethods": true, "allPublicMethods": true, "allDeclaredFields": true, "allPublicFields": true

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextPreInitializationTest.java

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888
import com.oracle.truffle.api.InternalResource;
8989
import com.oracle.truffle.api.test.ReflectionUtils;
9090
import org.graalvm.collections.Pair;
91-
import org.graalvm.nativeimage.ImageInfo;
9291
import org.graalvm.options.OptionCategory;
9392
import org.graalvm.options.OptionDescriptor;
9493
import org.graalvm.options.OptionDescriptors;
@@ -2588,7 +2587,7 @@ public void testSandboxPolicyLanguageOptionFailure() throws Exception {
25882587
@Test
25892588
@SuppressWarnings("try")
25902589
public void testLanguageInternalResources() throws Exception {
2591-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2590+
TruffleTestAssumptions.assumeNotAOT();
25922591
setPatchable(FIRST);
25932592
List<TruffleFile> files = new ArrayList<>();
25942593
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {
@@ -2607,6 +2606,35 @@ public void testLanguageInternalResources() throws Exception {
26072606
}
26082607
}
26092608

2609+
@Test
2610+
@SuppressWarnings("try")
2611+
public void testInternalResourcesInNonPreInitializedEngine() throws Exception {
2612+
TruffleTestAssumptions.assumeNotAOT();
2613+
setPatchable(FIRST);
2614+
List<TruffleFile> files = new ArrayList<>();
2615+
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {
2616+
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, newResourceBuildTimeVerifier(files));
2617+
doContextPreinitialize(FIRST);
2618+
assertFalse(files.isEmpty());
2619+
}
2620+
Path runtimeCacheRoot = Files.createTempDirectory(null).toRealPath();
2621+
Engine.copyResources(runtimeCacheRoot, FIRST);
2622+
System.setProperty("polyglot.engine.resourcePath", runtimeCacheRoot.toString());
2623+
try (Engine engine = Engine.create()) {
2624+
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_PATCH_CONTEXT, (env) -> {
2625+
throw CompilerDirectives.shouldNotReachHere("Pre-initialized context should not be used.");
2626+
});
2627+
BaseLanguage.registerAction(ContextPreInitializationTestFirstLanguage.class, ActionKind.ON_INITIALIZE_CONTEXT, newResourceNonPreInitializedContextVerifier(runtimeCacheRoot.toString()));
2628+
try (Context ctx = Context.newBuilder().engine(engine).build()) {
2629+
Value res = ctx.eval(Source.create(FIRST, "test"));
2630+
assertEquals("test", res.asString());
2631+
}
2632+
} finally {
2633+
System.getProperties().remove("polyglot.engine.resourcePath");
2634+
TemporaryResourceCacheRoot.reset(false);
2635+
}
2636+
}
2637+
26102638
private static Consumer<Env> newResourceBuildTimeVerifier(List<TruffleFile> files) {
26112639
return (env) -> {
26122640
try {
@@ -2624,6 +2652,25 @@ private static Consumer<Env> newResourceBuildTimeVerifier(List<TruffleFile> file
26242652
};
26252653
}
26262654

2655+
private static Consumer<Env> newResourceNonPreInitializedContextVerifier(String expectedRootPrefix) {
2656+
return (env) -> {
2657+
try {
2658+
ContextPreInitializationResource.unpackCount = 0;
2659+
TruffleFile root = env.getInternalResource(ContextPreInitializationResource.class);
2660+
assertEquals(0, ContextPreInitializationResource.unpackCount);
2661+
assertNotNull(root);
2662+
assertTrue(root.isAbsolute());
2663+
TruffleFile resource = root.resolve(ContextPreInitializationResource.FILE_NAME);
2664+
assertNotNull(resource);
2665+
assertTrue(resource.isAbsolute());
2666+
assertTrue(resource.getAbsoluteFile().toString().startsWith(expectedRootPrefix));
2667+
assertEquals(ContextPreInitializationResource.FILE_CONTENT, new String(resource.readAllBytes(), StandardCharsets.UTF_8));
2668+
} catch (IOException ioe) {
2669+
throw new AssertionError(ioe);
2670+
}
2671+
};
2672+
}
2673+
26272674
private static Consumer<Env> newResourceExecutionTimeVerifier(List<TruffleFile> files, String expectedRootPrefix) {
26282675
return (env) -> {
26292676
try {
@@ -2651,7 +2698,7 @@ private static Consumer<Env> newResourceExecutionTimeVerifier(List<TruffleFile>
26512698
@Test
26522699
@SuppressWarnings("try")
26532700
public void testSourcesForInternalResources() throws Exception {
2654-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2701+
TruffleTestAssumptions.assumeNotAOT();
26552702
setPatchable(FIRST);
26562703
List<com.oracle.truffle.api.source.Source> sources = new ArrayList<>();
26572704
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {
@@ -2693,7 +2740,7 @@ public void testSourcesForInternalResources() throws Exception {
26932740
@Test
26942741
@SuppressWarnings("try")
26952742
public void testInstrumentInternalResources() throws Exception {
2696-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2743+
TruffleTestAssumptions.assumeNotAOT();
26972744
setPatchable(FIRST);
26982745
AtomicReference<TruffleFile> rootRef = new AtomicReference<>();
26992746
ContextPreInitializationFirstInstrument.actions = Collections.singletonMap("onContextCreated", (e) -> {
@@ -2728,7 +2775,7 @@ public void testInstrumentInternalResources() throws Exception {
27282775
@Test
27292776
@SuppressWarnings("try")
27302777
public void testOverriddenCacheRoot() throws Exception {
2731-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2778+
TruffleTestAssumptions.assumeNotAOT();
27322779
setPatchable(FIRST);
27332780
List<TruffleFile> files = new ArrayList<>();
27342781
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {
@@ -2756,7 +2803,7 @@ public void testOverriddenCacheRoot() throws Exception {
27562803
@Test
27572804
@SuppressWarnings("try")
27582805
public void testOverriddenComponentRoot() throws Exception {
2759-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2806+
TruffleTestAssumptions.assumeNotAOT();
27602807
setPatchable(FIRST);
27612808
List<TruffleFile> files = new ArrayList<>();
27622809
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {
@@ -2783,7 +2830,7 @@ public void testOverriddenComponentRoot() throws Exception {
27832830
@Test
27842831
@SuppressWarnings("try")
27852832
public void testOverriddenResourceRoot() throws Exception {
2786-
Assume.assumeFalse("Cannot run as native unittest", ImageInfo.inImageRuntimeCode());
2833+
TruffleTestAssumptions.assumeNotAOT();
27872834
setPatchable(FIRST);
27882835
List<TruffleFile> files = new ArrayList<>();
27892836
try (TemporaryResourceCacheRoot imageBuildTimeCacheRoot = new TemporaryResourceCacheRoot(false)) {

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@
9494
import java.util.Set;
9595
import java.util.function.Predicate;
9696

97+
import com.oracle.truffle.api.TruffleLanguage;
9798
import com.oracle.truffle.api.test.ReflectionUtils;
9899
import com.oracle.truffle.api.test.common.AbstractExecutableTestLanguage;
99100
import com.oracle.truffle.api.test.common.TestUtils;
100101
import com.oracle.truffle.tck.tests.TruffleTestAssumptions;
101102
import org.graalvm.home.Version;
102103
import org.graalvm.polyglot.Context;
104+
import org.graalvm.polyglot.Engine;
103105
import org.graalvm.polyglot.io.FileSystem;
104106
import org.graalvm.polyglot.io.IOAccess;
105107
import org.junit.AfterClass;
@@ -196,7 +198,7 @@ public static void createConfigurations() throws IOException, ReflectiveOperatio
196198
accessibleDir = createContent(Files.createTempDirectory(FileSystemsTest.class.getSimpleName()),
197199
fullIO);
198200
ctx = Context.newBuilder().allowIO(IOAccess.ALL).build();
199-
setCwd(ctx, accessibleDir, null);
201+
setCwd(ctx, accessibleDir);
200202
cfgs.put(FULL_IO, new Configuration(FULL_IO, ctx, accessibleDir, fullIO, true, true, true, true));
201203
}
202204

@@ -208,19 +210,24 @@ public static void createConfigurations() throws IOException, ReflectiveOperatio
208210

209211
// No IO under language home - public file
210212
if (TruffleTestAssumptions.isNoClassLoaderEncapsulation()) { // setCwd not supported
211-
ctx = Context.newBuilder().allowIO(IOAccess.NONE).build();
213+
Engine engine = Engine.create();
212214
privateDir = createContent(Files.createTempDirectory(FileSystemsTest.class.getSimpleName()).toRealPath(),
213215
fullIO);
214-
setCwd(ctx, privateDir, privateDir);
215-
cfgs.put(NO_IO_UNDER_LANGUAGE_HOME_PUBLIC_FILE, new Configuration(NO_IO_UNDER_LANGUAGE_HOME_PUBLIC_FILE, ctx, privateDir, fullIO, true, false, false, false));
216+
markAsLanguageHome(engine, SetCurrentWorkingDirectoryLanguage.class, privateDir);
217+
ctx = Context.newBuilder().engine(engine).allowIO(IOAccess.NONE).build();
218+
setCwd(ctx, privateDir);
219+
cfgs.put(NO_IO_UNDER_LANGUAGE_HOME_PUBLIC_FILE,
220+
new Configuration(NO_IO_UNDER_LANGUAGE_HOME_PUBLIC_FILE, ctx, privateDir, privateDir, fullIO, true, false, false, false, false, true, engine));
216221

217222
// No IO under language home - internal file
218-
ctx = Context.newBuilder().allowIO(IOAccess.NONE).build();
223+
engine = Engine.create();
219224
privateDir = createContent(Files.createTempDirectory(FileSystemsTest.class.getSimpleName()).toRealPath(),
220225
fullIO);
221-
setCwd(ctx, privateDir, privateDir);
226+
markAsLanguageHome(engine, SetCurrentWorkingDirectoryLanguage.class, privateDir);
227+
ctx = Context.newBuilder().engine(engine).allowIO(IOAccess.NONE).build();
228+
setCwd(ctx, privateDir);
222229
cfgs.put(NO_IO_UNDER_LANGUAGE_HOME_INTERNAL_FILE,
223-
new Configuration(NO_IO_UNDER_LANGUAGE_HOME_INTERNAL_FILE, ctx, privateDir, privateDir, fullIO, true, true, false, false, true, false));
230+
new Configuration(NO_IO_UNDER_LANGUAGE_HOME_INTERNAL_FILE, ctx, privateDir, privateDir, fullIO, true, true, false, false, true, false, engine));
224231
}
225232

226233
// Read Only
@@ -230,7 +237,7 @@ public static void createConfigurations() throws IOException, ReflectiveOperatio
230237
fullIO);
231238
ioAccess = IOAccess.newBuilder().fileSystem(FileSystem.newReadOnlyFileSystem(fullIO)).build();
232239
ctx = Context.newBuilder().allowIO(ioAccess).build();
233-
setCwd(ctx, accessibleDir, null);
240+
setCwd(ctx, accessibleDir);
234241
cfgs.put(READ_ONLY, new Configuration(READ_ONLY, ctx, accessibleDir, fullIO, true, true, false, true));
235242
}
236243

@@ -290,11 +297,13 @@ public static void createConfigurations() throws IOException, ReflectiveOperatio
290297
memDir = mkdirs(fileSystem.toAbsolutePath(fileSystem.parsePath("work")), fileSystem);
291298
fileSystem.setCurrentWorkingDirectory(memDir);
292299
privateDir = createContent(memDir, fileSystem);
300+
Engine engine = Engine.create();
301+
markAsLanguageHome(engine, SetCurrentWorkingDirectoryLanguage.class, privateDir);
293302
ioAccess = IOAccess.newBuilder().fileSystem(fileSystem).build();
294-
ctx = Context.newBuilder().allowIO(ioAccess).build();
295-
setCwd(ctx, privateDir, privateDir);
303+
ctx = Context.newBuilder().engine(engine).allowIO(ioAccess).build();
304+
setCwd(ctx, privateDir);
296305
cfgs.put(MEMORY_FILE_SYSTEM_WITH_LANGUAGE_HOMES_INTERNAL_FILE,
297-
new Configuration(MEMORY_FILE_SYSTEM_WITH_LANGUAGE_HOMES_INTERNAL_FILE, ctx, privateDir, privateDir, fileSystem, false, true, true, true));
306+
new Configuration(MEMORY_FILE_SYSTEM_WITH_LANGUAGE_HOMES_INTERNAL_FILE, ctx, privateDir, privateDir, fileSystem, false, true, true, true, true, true, engine));
298307

299308
// PreInitializeContextFileSystem in image build time
300309
fileSystem = createPreInitializeContextFileSystem();
@@ -324,7 +333,7 @@ private static void createDeprecatedConfigurations(FileSystem fullIO) throws IOE
324333
Path accessibleDir = createContent(Files.createTempDirectory(FileSystemsTest.class.getSimpleName()),
325334
fullIO);
326335
Context ctx = Context.newBuilder().allowIO(true).build();
327-
setCwd(ctx, accessibleDir, null);
336+
setCwd(ctx, accessibleDir);
328337
cfgs.put(FULL_IO_DEPRECATED, new Configuration(FULL_IO, ctx, accessibleDir, fullIO, true, true, true, true));
329338

330339
// No IO using deprecated Context.Builder methods
@@ -2742,6 +2751,7 @@ public static final class Configuration implements Closeable {
27422751
private final boolean allowsUserDir;
27432752
private final boolean allowsAbsolutePath;
27442753
private final boolean usePublicFile;
2754+
private final Engine engine;
27452755

27462756
Configuration(
27472757
final String name,
@@ -2765,11 +2775,11 @@ public static final class Configuration implements Closeable {
27652775
final boolean readableFileSystem,
27662776
final boolean writableFileSystem,
27672777
final boolean allowsUserDir) {
2768-
this(name, context, path, userDir, fileSystem, internalFileSystem, readableFileSystem, writableFileSystem, allowsUserDir, allowsUserDir, true);
2778+
this(name, context, path, userDir, fileSystem, internalFileSystem, readableFileSystem, writableFileSystem, allowsUserDir, allowsUserDir, true, null);
27692779
}
27702780

27712781
Configuration(String name, Context context, Path path, Path userDir, FileSystem fileSystem, boolean internalFileSystem, boolean readableFileSystem,
2772-
boolean writableFileSystem, boolean allowsUserDir, boolean allowsAbsolutePath, boolean usePublicFile) {
2782+
boolean writableFileSystem, boolean allowsUserDir, boolean allowsAbsolutePath, boolean usePublicFile, Engine engine) {
27732783
Objects.requireNonNull(name, "Name must be non null.");
27742784
Objects.requireNonNull(context, "Context must be non null.");
27752785
Objects.requireNonNull(path, "Path must be non null.");
@@ -2786,6 +2796,7 @@ public static final class Configuration implements Closeable {
27862796
this.allowsUserDir = allowsUserDir;
27872797
this.allowsAbsolutePath = allowsAbsolutePath;
27882798
this.usePublicFile = usePublicFile;
2799+
this.engine = engine;
27892800
}
27902801

27912802
String getName() {
@@ -2864,6 +2875,9 @@ public String toString() {
28642875
public void close() throws IOException {
28652876
try {
28662877
ctx.close();
2878+
if (engine != null) {
2879+
engine.close();
2880+
}
28672881
} finally {
28682882
deleteRecursively(path, fileSystem);
28692883
}
@@ -2977,10 +2991,9 @@ private static void resetLanguageHomes() {
29772991
*
29782992
* @param ctx the context to set the cwd for
29792993
* @param cwd the new current working directory
2980-
* @param langHome language home to set
29812994
*/
2982-
private static void setCwd(Context ctx, Path cwd, Path langHome) {
2983-
AbstractExecutableTestLanguage.evalTestLanguage(ctx, SetCurrentWorkingDirectoryLanguage.class, "", cwd.toString(), langHome != null ? langHome.toString() : "");
2995+
private static void setCwd(Context ctx, Path cwd) {
2996+
AbstractExecutableTestLanguage.evalTestLanguage(ctx, SetCurrentWorkingDirectoryLanguage.class, "", cwd.toString());
29842997
}
29852998

29862999
@Registration
@@ -2989,17 +3002,30 @@ public static final class SetCurrentWorkingDirectoryLanguage extends AbstractExe
29893002
@TruffleBoundary
29903003
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
29913004
String currentWorkingDirectory = (String) contextArguments[0];
2992-
String langHome = (String) contextArguments[1];
2993-
if (!langHome.isEmpty()) {
2994-
String languageId = TestUtils.getDefaultLanguageId(getClass());
2995-
System.setProperty("org.graalvm.language." + languageId + ".home", langHome);
2996-
resetLanguageHomes();
2997-
}
29983005
env.setCurrentWorkingDirectory(env.getInternalTruffleFile(currentWorkingDirectory));
29993006
return null;
30003007
}
30013008
}
30023009

3010+
static void markAsLanguageHome(Engine engine, Class<? extends TruffleLanguage<?>> language, Path languageHome) {
3011+
try (Context ctx = Context.newBuilder().engine(engine).build()) {
3012+
AbstractExecutableTestLanguage.evalTestLanguage(ctx, MarkAsLanguageHomeLanguage.class, "", TestUtils.getDefaultLanguageId(language), languageHome.toString());
3013+
}
3014+
}
3015+
3016+
@Registration
3017+
public static final class MarkAsLanguageHomeLanguage extends AbstractExecutableTestLanguage {
3018+
@Override
3019+
@TruffleBoundary
3020+
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
3021+
String languageId = (String) contextArguments[0];
3022+
String langHome = (String) contextArguments[1];
3023+
System.setProperty("org.graalvm.language." + languageId + ".home", langHome);
3024+
resetLanguageHomes();
3025+
return null;
3026+
}
3027+
}
3028+
30033029
private static FileSystem createPreInitializeContextFileSystem() throws ReflectiveOperationException {
30043030
Class<? extends FileSystem> clazz = Class.forName("com.oracle.truffle.polyglot.FileSystems$PreInitializeContextFileSystem").asSubclass(FileSystem.class);
30053031
Constructor<? extends FileSystem> init = clazz.getDeclaredConstructor(String.class);
@@ -3009,13 +3035,10 @@ private static FileSystem createPreInitializeContextFileSystem() throws Reflecti
30093035

30103036
private static void switchToImageExecutionTime(FileSystem fileSystem, Path cwd) throws ReflectiveOperationException {
30113037
String workDir = cwd.toString();
3012-
Class<? extends FileSystem> clazz = Class.forName("com.oracle.truffle.polyglot.FileSystems$PreInitializeContextFileSystem").asSubclass(FileSystem.class);
3013-
Method preInitClose = clazz.getDeclaredMethod("onPreInitializeContextEnd");
3014-
ReflectionUtils.setAccessible(preInitClose, true);
3015-
preInitClose.invoke(fileSystem);
3016-
Method patchStart = clazz.getDeclaredMethod("onLoadPreinitializedContext", FileSystem.class);
3017-
ReflectionUtils.setAccessible(patchStart, true);
3018-
patchStart.invoke(fileSystem, newFullIOFileSystem(Paths.get(workDir)));
3038+
Class<?> internalResourceRootsClass = Class.forName("com.oracle.truffle.polyglot.InternalResourceRoots");
3039+
Object roots = ReflectionUtils.invokeStatic(internalResourceRootsClass, "getInstance");
3040+
ReflectionUtils.invoke(fileSystem, "onPreInitializeContextEnd", new Class<?>[]{internalResourceRootsClass, Map.class}, roots, Map.of());
3041+
ReflectionUtils.invoke(fileSystem, "onLoadPreinitializedContext", new Class<?>[]{FileSystem.class}, newFullIOFileSystem(Paths.get(workDir)));
30193042
}
30203043

30213044
static FileSystem newFullIOFileSystem(final Path currentWorkingDirectory) {

0 commit comments

Comments
 (0)