Skip to content

Commit 75f6db7

Browse files
committed
[GR-65561] API for composable Context configuration.
PullRequest: graal/21041
2 parents 3b24427 + b11dd77 commit 75f6db7

File tree

6 files changed

+683
-10
lines changed

6 files changed

+683
-10
lines changed

sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
2020
* GR-66339 Truffle now checks that its multi-release classes ([JEP 238](https://openjdk.org/jeps/238)) are loaded correctly and provides a descriptive error message if they are not.
2121
* GR-55996 Added the options `engine.SourceCacheStatistics` and `engine.SourceCacheStatisticDetails` to print polyglot source cache statistics on engine close.
2222

23+
* GR-65561 Added `Context.Builder#apply`, `ContextBuilder#extendIO`, and `ContextBuilder#extendHostAccess` to enable composable Context configuration
2324

2425
## Version 24.2.0
2526
* GR-54905 When using Truffle NFI with the Panama backend, native access must now be granted to the Truffle module instead of the NFI Panama module. Use the `--enable-native-access=org.graalvm.truffle` Java command line option to enable the native access for the NFI Panama backend.

sdk/src/org.graalvm.polyglot/snapshot.sigtest

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ meth public void printStackTrace(java.io.PrintStream)
9090
meth public void printStackTrace(java.io.PrintWriter)
9191
meth public void setStackTrace(java.lang.StackTraceElement[])
9292
supr java.lang.Object
93-
hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,jfrTracing,serialVersionUID,stackTrace,suppressedExceptions
93+
hfds CAUSE_CAPTION,EMPTY_THROWABLE_ARRAY,NULL_CAUSE_MESSAGE,SELF_SUPPRESSION_MESSAGE,SUPPRESSED_CAPTION,SUPPRESSED_SENTINEL,UNASSIGNED_STACK,backtrace,cause,depth,detailMessage,serialVersionUID,stackTrace,suppressedExceptions
9494
hcls PrintStreamOrWriter,SentinelHolder,WrappedPrintStream,WrappedPrintWriter
9595

9696
CLSS public abstract interface java.lang.annotation.Annotation
@@ -178,12 +178,15 @@ meth public org.graalvm.polyglot.Context$Builder allowInnerContextOptions(boolea
178178
meth public org.graalvm.polyglot.Context$Builder allowNativeAccess(boolean)
179179
meth public org.graalvm.polyglot.Context$Builder allowPolyglotAccess(org.graalvm.polyglot.PolyglotAccess)
180180
meth public org.graalvm.polyglot.Context$Builder allowValueSharing(boolean)
181+
meth public org.graalvm.polyglot.Context$Builder apply(java.util.function.Consumer<org.graalvm.polyglot.Context$Builder>)
181182
meth public org.graalvm.polyglot.Context$Builder arguments(java.lang.String,java.lang.String[])
182183
meth public org.graalvm.polyglot.Context$Builder currentWorkingDirectory(java.nio.file.Path)
183184
meth public org.graalvm.polyglot.Context$Builder engine(org.graalvm.polyglot.Engine)
184185
meth public org.graalvm.polyglot.Context$Builder environment(java.lang.String,java.lang.String)
185186
meth public org.graalvm.polyglot.Context$Builder environment(java.util.Map<java.lang.String,java.lang.String>)
186187
meth public org.graalvm.polyglot.Context$Builder err(java.io.OutputStream)
188+
meth public org.graalvm.polyglot.Context$Builder extendHostAccess(org.graalvm.polyglot.HostAccess,java.util.function.Consumer<org.graalvm.polyglot.HostAccess$Builder>)
189+
meth public org.graalvm.polyglot.Context$Builder extendIO(org.graalvm.polyglot.io.IOAccess,java.util.function.Consumer<org.graalvm.polyglot.io.IOAccess$Builder>)
187190
meth public org.graalvm.polyglot.Context$Builder fileSystem(org.graalvm.polyglot.io.FileSystem)
188191
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="23.0")
189192
meth public org.graalvm.polyglot.Context$Builder hostClassFilter(java.util.function.Predicate<java.lang.String>)

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import java.util.Map.Entry;
5959
import java.util.Objects;
6060
import java.util.concurrent.TimeoutException;
61+
import java.util.function.Consumer;
6162
import java.util.function.Predicate;
6263
import java.util.logging.Handler;
6364
import java.util.logging.Level;
@@ -1124,6 +1125,38 @@ public final class Builder {
11241125
this.permittedLanguages = permittedLanguages;
11251126
}
11261127

1128+
/**
1129+
* Applies the provided Consumer to this Builder instance.
1130+
* <p>
1131+
* This method allows encapsulating complex Builder configuration logic into separate
1132+
* methods while maintaining the fluent API of {@link Context.Builder}.
1133+
* <p>
1134+
* Example
1135+
*
1136+
* <pre>
1137+
* private void configureEnv(Builder builder) {
1138+
* return builder.environment("DEBUG", appConfiguration.isDebugMode())
1139+
* .environment("APP_NAME", appConfiguration.getApplicationName())
1140+
* .environment("JAVA_VER", System.getProperty("java.version"))&#64;
1141+
* }
1142+
*
1143+
* public Context createContext() {
1144+
* return Context.newBuilder()
1145+
* .apply(this::configureEnv)
1146+
* .allowNativeAccess(true)
1147+
* .build();
1148+
* }
1149+
* </pre>
1150+
*
1151+
* @param action The Consumer that configures this Builder instance; it is not supposed to
1152+
* finalize the Builder using {@link #build()}.
1153+
* @since 25.0
1154+
*/
1155+
public Builder apply(Consumer<Builder> action) {
1156+
action.accept(this);
1157+
return this;
1158+
}
1159+
11271160
/**
11281161
* Explicitly sets the underlying engine to use. By default, every context has its own
11291162
* isolated engine. If multiple contexts are created from one engine, then they may
@@ -1196,13 +1229,20 @@ public Builder allowHostAccess(boolean enabled) {
11961229
* by guest applications. By default if {@link #allowAllAccess(boolean)} is
11971230
* <code>false</code> the {@link HostAccess#EXPLICIT} policy will be used, otherwise
11981231
* {@link HostAccess#ALL}.
1232+
* <p>
1233+
* Repeated calls of this method on the same {@link Context.Builder} instance override the
1234+
* previously set {@link HostAccess} configuration. Use
1235+
* {@link #extendHostAccess(HostAccess, Consumer)} for composable {@link HostAccess}
1236+
* configuration.
11991237
*
12001238
* @see HostAccess#EXPLICIT EXPLICIT - to allow explicitly annotated constructors, methods
12011239
* or fields.
12021240
* @see HostAccess#ALL ALL - to allow unrestricted access (use only for trusted guest
1203-
* applications)
1204-
* @see HostAccess#NONE NONE - to not allow any access
1241+
* applications).
1242+
* @see HostAccess#NONE NONE - to not allow any access.
12051243
* @see HostAccess#newBuilder() newBuilder() - to create a custom configuration.
1244+
* @see #extendHostAccess(HostAccess, Consumer) - for composable {@code HostAccess}
1245+
* configuration.
12061246
*
12071247
* @since 19.0
12081248
*/
@@ -1211,6 +1251,69 @@ public Builder allowHostAccess(HostAccess config) {
12111251
return this;
12121252
}
12131253

1254+
/**
1255+
* Extends the existing host access configuration that determines, which public
1256+
* constructors, methods or fields of public classes are accessible by guest applications.
1257+
* <p>
1258+
* This method allows composing code that configures {@link HostAccess}.
1259+
* <p>
1260+
* The provided {@code setup} is applied to a {@link HostAccess.Builder} instance, which is
1261+
* initialized with the current {@link HostAccess} configuration. If no {@link HostAccess}
1262+
* configuration exists, the {@code defaultInitialValue} is used to initialize the builder.
1263+
* <p>
1264+
* Example usage
1265+
*
1266+
* <pre>
1267+
* public static final class MyArrayWrapper {
1268+
* private final Value v;
1269+
*
1270+
* private MyArrayWrapper(Value v) {
1271+
* this.v = v;
1272+
* }
1273+
*
1274+
* // ...
1275+
*
1276+
* public static void addMapping(Context.Builder builder) {
1277+
* builder.extendHostAccess(HostAccess.NONE,
1278+
* b -> b.targetTypeMapping(Value.class, MyArrayWrapper.class,
1279+
* Value::hasArrayElements,
1280+
* MyArrayWrapper::new));
1281+
* }
1282+
* }
1283+
*
1284+
* // ...
1285+
*
1286+
* public Context createContext() {
1287+
* return Context.newBuilder().
1288+
* .allowHostAccess(HostAccess.ALL)
1289+
* .apply(MyArrayWrapper::addMapping)
1290+
* // ...
1291+
* .build();
1292+
* }
1293+
* </pre>
1294+
*
1295+
* In this example the call to {@code extendHostAccess} in
1296+
* {@code MyArrayWrapper::addMapping} does not override the {@link HostAccess#ALL}
1297+
* configured before. Using {@link #allowHostAccess(HostAccess)} in this situation would
1298+
* override the previous configuration.
1299+
*
1300+
* @see #allowHostAccess - to set or override existing {@code HostAccess} configuration.
1301+
* @see #extendIO(IOAccess, Consumer) - to extend existing {@code IOAccess} configuration.
1302+
* @param defaultInitialValue - the initial value to use if no {@code HostAccess} is
1303+
* configured.
1304+
* @param setup - Consumer that receives a {@link HostAccess.Builder} preconfigured with the
1305+
* current {@link HostAccess} configuration.
1306+
* @return the {@link Builder}
1307+
* @since 25.0
1308+
*/
1309+
public Builder extendHostAccess(HostAccess defaultInitialValue, Consumer<HostAccess.Builder> setup) {
1310+
HostAccess prototype = hostAccess != null ? hostAccess : defaultInitialValue;
1311+
HostAccess.Builder builder = HostAccess.newBuilder(prototype);
1312+
setup.accept(builder);
1313+
allowHostAccess(builder.build());
1314+
return this;
1315+
}
1316+
12141317
/**
12151318
* Allows guest languages to access the native interface.
12161319
*
@@ -1554,10 +1657,15 @@ public Builder arguments(String language, String[] args) {
15541657
* {@link IOAccess#ALL} is used unless explicitly set. This method can be used to virtualize
15551658
* file system access in the guest language by using an {@link IOAccess} with a custom
15561659
* filesystem.
1660+
* <p>
1661+
* Repeated calls of this method on the same {@link Context.Builder} instance override the
1662+
* previously set {@link IOAccess} configuration. Use {@link #extendIO(IOAccess, Consumer)}
1663+
* for composable {@link IOAccess} configuration.
15571664
*
1558-
* @see IOAccess#ALL - to allow full host IO access
1559-
* @see IOAccess#NONE - to disable host IO access
1665+
* @see IOAccess#ALL - to allow full host IO access.
1666+
* @see IOAccess#NONE - to disable host IO access.
15601667
* @see IOAccess#newBuilder() - to create a custom configuration.
1668+
* @see #extendIO(IOAccess, Consumer) - for composable IOAccess configuration.
15611669
*
15621670
* @param ioAccess the IO configuration
15631671
* @return the {@link Builder}
@@ -1568,6 +1676,52 @@ public Builder allowIO(IOAccess ioAccess) {
15681676
return this;
15691677
}
15701678

1679+
/**
1680+
* Extends the existing guest language access to host IO configuration. This method allows
1681+
* composing code that configures {@link IOAccess}.
1682+
* <p>
1683+
* The provided {@code setup} is applied to a {@link IOAccess.Builder} instance, which is
1684+
* initialized with the current {@link IOAccess} configuration. If no {@link IOAccess}
1685+
* configuration exists, the {@code defaultInitialValue} is used to initialize the builder.
1686+
* <p>
1687+
* Example usage
1688+
*
1689+
* <pre>
1690+
* private void addFileSystem(IOAccess.Builder builder) {
1691+
* builder.fileSystem(new MyCustomFileSystem());
1692+
* }
1693+
*
1694+
* public Context createContext() {
1695+
* return Context.newBuilder().extendIOAccess(IOAccess.NONE, this::addFileSystem)
1696+
* // ...
1697+
* .extendIOAccess(IOAccess.NONE, b -> b.allowHostSocketAccess(true))
1698+
* // ...
1699+
* .build();
1700+
* }
1701+
* </pre>
1702+
*
1703+
* In this example the call to {@code extendIOAccess} does not override the
1704+
* {@link FileSystem} configured in {@code addFileSystem}. Using {@link #allowIO(IOAccess)}
1705+
* in this situation would override the configured {@link FileSystem}.
1706+
*
1707+
* @see #allowIO(IOAccess) - to set or override existing {@code IOAccess}.
1708+
* @see #extendHostAccess(HostAccess, Consumer) - to extend existing {@code HostAccess}
1709+
* configuration.
1710+
* @param defaultInitialValue - the initial value to use if no {@link IOAccess} is
1711+
* configured.
1712+
* @param setup - Consumer that receives a {@link IOAccess.Builder} preconfigured with the
1713+
* current {@link IOAccess} configuration.
1714+
* @return the {@link Builder}
1715+
* @since 25.0
1716+
*/
1717+
public Builder extendIO(IOAccess defaultInitialValue, Consumer<IOAccess.Builder> setup) {
1718+
IOAccess prototype = ioAccess != null ? ioAccess : defaultInitialValue;
1719+
IOAccess.Builder builder = IOAccess.newBuilder(prototype);
1720+
setup.accept(builder);
1721+
allowIO(builder.build());
1722+
return this;
1723+
}
1724+
15711725
/**
15721726
* If <code>true</code>, allows guest language to perform unrestricted IO operations on host
15731727
* system. Default is <code>false</code>. If {@link #allowAllAccess(boolean) all access} is
@@ -1866,10 +2020,12 @@ public Context build() {
18662020
throw new IllegalArgumentException("The method Context.Builder.allowHostAccess(boolean) and the method Context.Builder.allowHostAccess(HostAccess) are mutually exclusive.");
18672021
}
18682022
if (ioAccess != null && allowIO != null) {
1869-
throw new IllegalArgumentException("The method Context.Builder.allowIO(boolean) and the method Context.Builder.allowIO(IOAccess) are mutually exclusive.");
2023+
throw new IllegalArgumentException(
2024+
"The method Context.Builder.allowIO(boolean) and the method Context.Builder.allowIO(IOAccess) or Context.Builder.extendIO(IOAccess, Consumer<IOAccess.Builder>) are mutually exclusive.");
18702025
}
18712026
if (ioAccess != null && customFileSystem != null) {
1872-
throw new IllegalArgumentException("The method Context.Builder.allowIO(IOAccess) and the method Context.Builder.fileSystem(FileSystem) are mutually exclusive.");
2027+
throw new IllegalArgumentException(
2028+
"The method Context.Builder.allowIO(IOAccess) or Context.Builder.extendIO(IOAccess, Consumer<IOAccess.Builder>) and the method Context.Builder.fileSystem(FileSystem) are mutually exclusive.");
18732029
}
18742030
SandboxPolicy useSandboxPolicy = resolveSandboxPolicy();
18752031
validateSandbox(useSandboxPolicy);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,11 @@
281281
"allPublicMethods": true
282282
},
283283
{
284-
"name": "com.oracle.truffle.api.test.host.GR63075Test$NonPublicSubClass",
284+
"name": "com.oracle.truffle.api.test.polyglot.ContextBuilderExtendAPITest$DenyAccessToMe",
285+
"allPublicMethods": true
286+
},
287+
{
288+
"name": "com.oracle.truffle.api.test.polyglot.ContextBuilderExtendAPITest$ShouldBeAccessible",
285289
"allPublicMethods": true
286290
}
287291
]

0 commit comments

Comments
 (0)