Skip to content

Commit 851aaef

Browse files
committed
[GR-63201] Optional internal resources can't be used without the jar even when specifying polyglot.engine.resourcePath.*
1 parent df624d2 commit 851aaef

File tree

11 files changed

+195
-3
lines changed

11 files changed

+195
-3
lines changed

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
1111
* GR-57063 Bytecode-DSL: Added a `variadicStackLimit` parameter to `@GenerateBytecode` that allows to specify how many variable arguments are stored on the stack before they are collapsed into an object array.
1212
* GR-50017 TruffleStringIterator.NextNode and TruffleStringIterator.PreviousNode now require the iterated string's encoding as a parameter for performance reasons.
1313
* GR-63075 Java host interop again inherits public method methods from non-public base classes if public access is enabled. This was originally changed in 24.1.
14+
* GR-63201 Added `TruffleLanguage.Registration.optionalResources` and `TruffleInstrument.Registration.optionalResources` attributes to support optional resources which implementations are not available at the runtime. Optional resources, if omitted at runtime, can still be used as long as their resource path is specified via the `polyglot.engine.resourcePath.<componentId>` system property.
1415

1516
## Version 24.2.0
1617
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.

truffle/src/com.oracle.truffle.api.instrumentation/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ meth public abstract !hasdefault java.lang.String id()
483483
meth public abstract !hasdefault java.lang.String name()
484484
meth public abstract !hasdefault java.lang.String version()
485485
meth public abstract !hasdefault java.lang.String website()
486+
meth public abstract !hasdefault java.lang.String[] optionalResources()
486487
meth public abstract !hasdefault org.graalvm.polyglot.SandboxPolicy sandbox()
487488

488489
CLSS public abstract com.oracle.truffle.api.instrumentation.provider.TruffleInstrumentProvider

truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/TruffleInstrument.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,21 @@ Class<?>[] services() default {
14391439
* @since 23.1
14401440
*/
14411441
Class<? extends InternalResource>[] internalResources() default {};
1442+
1443+
/**
1444+
* A declarative list of optional {@link InternalResource} identifiers associated with this
1445+
* instrument. It is recommended to register all optional resource identifiers here.
1446+
* Specifying the resource identifier allows the instrument use the resource even if its
1447+
* implementation was omitted at runtime. To use the resource when its implementation was
1448+
* omitted the {@code polyglot.engine.resourcePath.instrumentId} system property must be
1449+
* specified and point to a path with the resources. This allows to omit the resource
1450+
* implementation class in standalone distributions.
1451+
*
1452+
* @see InternalResource
1453+
* @see Id
1454+
* @since 25.0
1455+
*/
1456+
String[] optionalResources() default {};
14421457
}
14431458

14441459
static {

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

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,130 @@ protected Object execute(RootNode node, Env env, Object[] contextArguments, Obje
667667
}
668668
}
669669

670+
@Test
671+
public void testMissingResourceConfiguredByResourcePath() throws Exception {
672+
TruffleTestAssumptions.assumeNotAOT();
673+
// Prepare standalone resources
674+
Path cacheRoot = Files.createTempDirectory(null);
675+
Engine.copyResources(cacheRoot, TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class));
676+
// Reset cached resource root
677+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
678+
try {
679+
Path libPath = cacheRoot.resolve(TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class)).resolve(LibraryResource.ID).toRealPath();
680+
Path srcPath = cacheRoot.resolve(TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class)).resolve(SourcesResource.ID).toRealPath();
681+
682+
// Set explicit resource cache root
683+
System.setProperty(String.format("polyglot.engine.resourcePath.%s.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class), LibraryResource.ID), libPath.toString());
684+
System.setProperty(String.format("polyglot.engine.resourcePath.%s.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class), SourcesResource.ID), srcPath.toString());
685+
try (Context context = Context.create()) {
686+
AbstractExecutableTestLanguage.execute(context, TestConfiguredMissingResource.class, libPath.toString(), srcPath.toString());
687+
} finally {
688+
// Reset cached resource root
689+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
690+
}
691+
} finally {
692+
// Clean explicit resource root
693+
System.getProperties().remove(String.format("polyglot.engine.resourcePath.%s.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class), LibraryResource.ID));
694+
System.getProperties().remove(String.format("polyglot.engine.resourcePath.%s.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class), SourcesResource.ID));
695+
delete(cacheRoot);
696+
}
697+
}
698+
699+
@Test
700+
public void testMissingResourceConfiguredByComponentPath() throws Exception {
701+
TruffleTestAssumptions.assumeNotAOT();
702+
// Prepare standalone resources
703+
Path cacheRoot = Files.createTempDirectory(null);
704+
Engine.copyResources(cacheRoot, TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class));
705+
// Reset cached resource root
706+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
707+
try {
708+
Path componentPath = cacheRoot.resolve(TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class)).toRealPath();
709+
Path libPath = componentPath.resolve(LibraryResource.ID).toRealPath();
710+
Path srcPath = componentPath.resolve(SourcesResource.ID).toRealPath();
711+
712+
// Set explicit component resource cache root
713+
System.setProperty(String.format("polyglot.engine.resourcePath.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class)), componentPath.toString());
714+
try (Context context = Context.create()) {
715+
AbstractExecutableTestLanguage.execute(context, TestConfiguredMissingResource.class, libPath.toString(), srcPath.toString());
716+
} finally {
717+
// Reset cached resource root
718+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
719+
}
720+
} finally {
721+
// Clean explicit resource root
722+
System.getProperties().remove(String.format("polyglot.engine.resourcePath.%s", TestUtils.getDefaultLanguageId(TestConfiguredMissingResource.class)));
723+
delete(cacheRoot);
724+
}
725+
}
726+
727+
@Registration(optionalResources = {LibraryResource.ID, SourcesResource.ID})
728+
public static class TestConfiguredMissingResource extends AbstractExecutableTestLanguage {
729+
730+
@Override
731+
@TruffleBoundary
732+
@SuppressWarnings("try")
733+
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
734+
try (TemporaryResourceCacheRoot cache = new TemporaryResourceCacheRoot()) {
735+
TruffleFile libRoot = env.getInternalResource(LibraryResource.ID);
736+
verifyResources(libRoot, LibraryResource.RESOURCES);
737+
TruffleFile srcRoot = env.getInternalResource(SourcesResource.ID);
738+
verifyResources(srcRoot, SourcesResource.RESOURCES);
739+
return "";
740+
}
741+
}
742+
}
743+
744+
@Test
745+
public void testMissingResourceUnConfigured() throws Exception {
746+
TruffleTestAssumptions.assumeNotAOT();
747+
// Prepare standalone resources
748+
Path cacheRoot = Files.createTempDirectory(null);
749+
Engine.copyResources(cacheRoot, TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class));
750+
// Reset cached resource root
751+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
752+
try {
753+
Path componentPath = cacheRoot.resolve(TestUtils.getDefaultLanguageId(TestOverriddenResourceRoot.class)).toRealPath();
754+
Path libPath = componentPath.resolve(LibraryResource.ID).toRealPath();
755+
Path srcPath = componentPath.resolve(SourcesResource.ID).toRealPath();
756+
757+
// Set explicit component resource cache root
758+
try (Context context = Context.create()) {
759+
AbstractExecutableTestLanguage.execute(context, TestUnConfiguredMissingResource.class, libPath.toString(), srcPath.toString());
760+
} finally {
761+
// Reset cached resource root
762+
TemporaryResourceCacheRoot.setTestCacheRoot(null, false);
763+
}
764+
} finally {
765+
delete(cacheRoot);
766+
}
767+
}
768+
769+
@Registration(optionalResources = {LibraryResource.ID, SourcesResource.ID})
770+
public static class TestUnConfiguredMissingResource extends AbstractExecutableTestLanguage {
771+
772+
@Override
773+
@TruffleBoundary
774+
@SuppressWarnings("try")
775+
protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) throws Exception {
776+
try (TemporaryResourceCacheRoot cache = new TemporaryResourceCacheRoot()) {
777+
AbstractPolyglotTest.assertFails(() -> {
778+
env.getInternalResource(LibraryResource.ID);
779+
return null;
780+
}, IllegalStateException.class, (e) -> {
781+
assertTrue(e.getMessage().contains("-Dpolyglot.engine.resourcePath"));
782+
});
783+
AbstractPolyglotTest.assertFails(() -> {
784+
env.getInternalResource(SourcesResource.ID);
785+
return null;
786+
}, IllegalStateException.class, (e) -> {
787+
assertTrue(e.getMessage().contains("-Dpolyglot.engine.resourcePath"));
788+
});
789+
return "";
790+
}
791+
}
792+
}
793+
670794
@Test
671795
public void testLanguageResourcesLookedUpById() {
672796
TruffleTestAssumptions.assumeNotAOT();

truffle/src/com.oracle.truffle.api/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ meth public abstract !hasdefault java.lang.String website()
674674
meth public abstract !hasdefault java.lang.String[] byteMimeTypes()
675675
meth public abstract !hasdefault java.lang.String[] characterMimeTypes()
676676
meth public abstract !hasdefault java.lang.String[] dependentLanguages()
677+
meth public abstract !hasdefault java.lang.String[] optionalResources()
677678
meth public abstract !hasdefault org.graalvm.polyglot.SandboxPolicy sandbox()
678679

679680
CLSS public final com.oracle.truffle.api.TruffleLogger

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,21 @@ String[] dependentLanguages() default {
536536
* @since 23.1
537537
*/
538538
Class<? extends InternalResource>[] internalResources() default {};
539+
540+
/**
541+
* A declarative list of optional {@link InternalResource} identifiers associated with this
542+
* language. It is recommended to register all optional resource identifiers here.
543+
* Specifying the resource identifier allows the language use the resource even if its
544+
* implementation was omitted at runtime. To use the resource when its implementation was
545+
* omitted the {@code polyglot.engine.resourcePath.languageId} system property must be
546+
* specified and point to a path with the resources. This allows to omit the resource
547+
* implementation class in standalone distributions.
548+
*
549+
* @see InternalResource
550+
* @see Id
551+
* @since 25.0
552+
*/
553+
String[] optionalResources() default {};
539554
}
540555

541556
/**

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InstrumentCache.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ private static void loadInstrumentImpl(TruffleInstrumentProvider provider, List<
234234
throw InternalResourceCache.throwDuplicateOptionalResourceException(old, resource);
235235
}
236236
}
237+
for (String optionalResourceId : reg.optionalResources()) {
238+
if (!resources.containsKey(optionalResourceId)) {
239+
resources.put(optionalResourceId, new InternalResourceCache(id, optionalResourceId, InternalResourceCache.nonExistingResource(id, optionalResourceId)));
240+
}
241+
}
237242
// we don't want multiple instruments with the same class name
238243
if (!classNamesUsed.contains(className)) {
239244
classNamesUsed.add(className);

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceCache.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@ static boolean copyResourcesForNativeImage(Path target, String... components) th
380380
}
381381

382382
private boolean copyResourcesForNativeImage(Path target) throws IOException {
383+
if (isMissingOptionalResource()) {
384+
return false;
385+
}
383386
Path root = findStandaloneResourceRoot(target);
384387
unlink(root);
385388
Files.createDirectories(root);
@@ -394,6 +397,10 @@ private boolean copyResourcesForNativeImage(Path target) throws IOException {
394397
}
395398
}
396399

400+
private boolean isMissingOptionalResource() {
401+
return path == null && resourceFactory instanceof NonExistingResourceSupplier;
402+
}
403+
397404
@SuppressWarnings("unused")
398405
static void includeResourcesForNativeImage(Path tempDir, BiConsumer<Module, Pair<String, byte[]>> resourceLocationConsumer) throws Exception {
399406
walkAllResources((componentId, resources) -> {
@@ -417,6 +424,9 @@ static String bytesToHex(byte[] bytes) {
417424
}
418425

419426
private void includeResourcesForNativeImageImpl(Path tempDir, BiConsumer<Module, Pair<String, byte[]>> resourceLocationConsumer) throws IOException, NoSuchAlgorithmException {
427+
if (isMissingOptionalResource()) {
428+
return;
429+
}
420430
Path root = findStandaloneResourceRoot(tempDir);
421431
unlink(root);
422432
Files.createDirectories(root);
@@ -603,6 +613,20 @@ public InternalResourceCache get() {
603613
return res;
604614
}
605615
}
616+
617+
static Supplier<InternalResource> nonExistingResource(String component, String resource) {
618+
return new NonExistingResourceSupplier(component, resource);
619+
}
620+
621+
private record NonExistingResourceSupplier(String component, String resource) implements Supplier<InternalResource> {
622+
623+
@Override
624+
public InternalResource get() {
625+
throw new IllegalStateException(String.format("Optional resource '%s' for component '%s' is missing. " +
626+
"Use `-Dpolyglot.engine.resourcePath.%s.%s=<path>` to configure a path to the internal resource root or include resource jar file to the module-path.",
627+
resource, component, component, resource));
628+
}
629+
}
606630
}
607631

608632
/**

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InternalResourceRoots.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Root findRoot(Path hostPath) {
160160
InternalResourceCache findInternalResource(Path hostPath) {
161161
Root root = findRoot(hostPath);
162162
if (root != null) {
163-
for (InternalResourceCache cache : root.caches) {
163+
for (InternalResourceCache cache : root.resources) {
164164
Path resourceRoot = cache.getPathOrNull();
165165
// Used InternalResourceCache instances always have non-null root.
166166
if (resourceRoot != null && hostPath.startsWith(resourceRoot)) {
@@ -202,7 +202,7 @@ private static void setTestCacheRoot(Path newRoot, boolean nativeImageRuntime) {
202202
InternalResourceRoots resourceRoots = runtimeCaches.computeIfAbsent(loaders, (k) -> new InternalResourceRoots());
203203
if (resourceRoots.roots != null) {
204204
for (Root root : resourceRoots.roots) {
205-
for (InternalResourceCache cache : root.caches()) {
205+
for (InternalResourceCache cache : root.resources()) {
206206
cache.clearCache();
207207
}
208208
}
@@ -361,7 +361,7 @@ private static void emitWarning(String message, Object... args) {
361361
PolyglotEngineImpl.logFallback(String.format(message + "%n", args));
362362
}
363363

364-
record Root(Path path, Kind kind, List<InternalResourceCache> caches) {
364+
record Root(Path path, Kind kind, List<InternalResourceCache> resources) {
365365

366366
enum Kind {
367367
COMPONENT,

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LanguageCache.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ private static void loadLanguageImpl(TruffleLanguageProvider provider, List<Lang
385385
throw InternalResourceCache.throwDuplicateOptionalResourceException(old, resource);
386386
}
387387
}
388+
for (String optionalResourceId : reg.optionalResources()) {
389+
if (!resources.containsKey(optionalResourceId)) {
390+
resources.put(optionalResourceId, new InternalResourceCache(id, optionalResourceId, InternalResourceCache.nonExistingResource(id, optionalResourceId)));
391+
}
392+
}
388393
into.add(new LanguageCache(id, name, implementationName, version, className, languageHome,
389394
characterMimes, byteMimeTypes, defaultMime, dependentLanguages, interactive, internal, needsAllEncodings,
390395
servicesClassNames, reg.contextPolicy(), provider, reg.website(), sandboxPolicy, Collections.unmodifiableMap(resources)));

0 commit comments

Comments
 (0)