Skip to content

Commit 068def2

Browse files
committed
[GR-31495] Implement language and instrument specific source option support.
PullRequest: graal/19869
2 parents b035c61 + 36a7ac4 commit 068def2

File tree

41 files changed

+1228
-251
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1228
-251
lines changed

sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
99
* `PolyglotException#printStackTrace` now always starts with the string returned by `PolyglotException#toString()` like for regular Java Throwable objects.
1010
* GR-54673 Added the option `engine.MaximumCompilations` to protect against too many repeated compilations of the same call target. The default value is `100`.
1111
* GR-61448 Compilation id (`CompId`) was added to the `opt done` truffle compilation logs. This id matches the compilation id in the output of deoptimization, compilation and code cache logs on HotSpot and SubstrateVM.
12+
* GR-31495 Added the ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. See the language and or tool specific documentation for available options. Available source options may also be reflected using `Instrument.getSourceOptions()` and `Language.getSourceOptions()`.
1213

1314
## Version 24.2.0
1415
* 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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ meth public java.lang.String getName()
352352
meth public java.lang.String getVersion()
353353
meth public java.lang.String getWebsite()
354354
meth public org.graalvm.options.OptionDescriptors getOptions()
355+
meth public org.graalvm.options.OptionDescriptors getSourceOptions()
355356
supr java.lang.Object
356357
hfds dispatch,engine,receiver
357358

@@ -367,6 +368,7 @@ meth public java.lang.String getVersion()
367368
meth public java.lang.String getWebsite()
368369
meth public java.util.Set<java.lang.String> getMimeTypes()
369370
meth public org.graalvm.options.OptionDescriptors getOptions()
371+
meth public org.graalvm.options.OptionDescriptors getSourceOptions()
370372
supr java.lang.Object
371373
hfds dispatch,engine,receiver
372374

@@ -517,9 +519,11 @@ meth public org.graalvm.polyglot.Source$Builder interactive(boolean)
517519
meth public org.graalvm.polyglot.Source$Builder internal(boolean)
518520
meth public org.graalvm.polyglot.Source$Builder mimeType(java.lang.String)
519521
meth public org.graalvm.polyglot.Source$Builder name(java.lang.String)
522+
meth public org.graalvm.polyglot.Source$Builder option(java.lang.String,java.lang.String)
523+
meth public org.graalvm.polyglot.Source$Builder options(java.util.Map<java.lang.String,java.lang.String>)
520524
meth public org.graalvm.polyglot.Source$Builder uri(java.net.URI)
521525
supr java.lang.Object
522-
hfds cached,content,fileEncoding,interactive,internal,language,mimeType,name,origin,uri
526+
hfds cached,content,fileEncoding,interactive,internal,language,mimeType,name,options,origin,uri
523527

524528
CLSS public final org.graalvm.polyglot.SourceSection
525529
meth public boolean equals(java.lang.Object)

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -406,7 +406,9 @@ public Engine getEngine() {
406406
* @throws IllegalStateException if the context is already closed and the current thread is not
407407
* allowed to access it.
408408
* @throws IllegalArgumentException if the language of the given source is not installed or the
409-
* {@link Source#getMimeType() MIME type} is not supported with the language.
409+
* {@link Source#getMimeType() MIME type} is not supported with the language or the
410+
* validation of a {@link Source.Builder#option(String, String) source option}
411+
* failed.
410412
* @return the evaluation result. The returned instance is never <code>null</code>, but the
411413
* result might represent a {@link Value#isNull() null} value.
412414
* @since 19.0
@@ -495,7 +497,10 @@ public Value eval(String languageId, CharSequence source) {
495497
*
496498
* @param source a source object to parse
497499
* @throws PolyglotException in case the guest language code parsing or validation failed.
498-
* @throws IllegalArgumentException if the language does not exist or is not accessible.
500+
* @throws IllegalArgumentException if the language of the given source is not installed or the
501+
* {@link Source#getMimeType() MIME type} is not supported with the language or the
502+
* validation of a {@link Source.Builder#option(String, String) source option}
503+
* failed.
499504
* @throws IllegalStateException if the context is already closed and the current thread is not
500505
* allowed to access it, or if the given language is not installed.
501506
* @since 20.2

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -2013,7 +2013,7 @@ public <S, T> Object newTargetTypeMapping(Class<S> sourceType, Class<T> targetTy
20132013
@Override
20142014
public Source buildSource(String language, Object origin, URI uri, String name, String mimeType, Object content, boolean interactive, boolean internal, boolean cached, Charset encoding,
20152015
URL url,
2016-
String path)
2016+
String path, Map<String, String> options)
20172017
throws IOException {
20182018
throw noPolyglotImplementationFound();
20192019
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -102,6 +102,17 @@ public OptionDescriptors getOptions() {
102102
return dispatch.getOptions(receiver);
103103
}
104104

105+
/**
106+
* Returns the source options descriptors available for sources of this instrument.
107+
*
108+
* @see #getOptions()
109+
* @see Source.Builder#option(String, String)
110+
* @since 25.0
111+
*/
112+
public OptionDescriptors getSourceOptions() {
113+
return dispatch.getSourceOptions(receiver);
114+
}
115+
105116
/**
106117
* Gets the version of this instrument.
107118
*

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -134,6 +134,17 @@ public OptionDescriptors getOptions() {
134134
return dispatch.getOptions(receiver);
135135
}
136136

137+
/**
138+
* Returns the source options descriptors available for sources of this language.
139+
*
140+
* @see #getOptions()
141+
* @see Source.Builder#option(String, String)
142+
* @since 25.0
143+
*/
144+
public OptionDescriptors getSourceOptions() {
145+
return dispatch.getSourceOptions(receiver);
146+
}
147+
137148
/**
138149
* Returns the default MIME type that is in use by a language. The default MIME type specifies
139150
* whether a source is loaded as character or binary based source by default. Returns

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

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -47,6 +47,8 @@
4747
import java.net.URI;
4848
import java.net.URL;
4949
import java.nio.charset.Charset;
50+
import java.util.HashMap;
51+
import java.util.Map;
5052
import java.util.Objects;
5153

5254
import org.graalvm.polyglot.impl.AbstractPolyglotImpl;
@@ -533,7 +535,7 @@ public static Builder newBuilder(String language, CharSequence characters, Strin
533535
* <pre>
534536
*
535537
* </pre>
536-
*
538+
*
537539
* @param language the language id, must not be <code>null</code>
538540
* @param bytes the byte sequence or string, must not be <code>null</code>
539541
* @param name the name of the source, if <code>null</code> then <code>"Unnamed"</code> will be
@@ -551,7 +553,7 @@ public static Builder newBuilder(String language, ByteSequence bytes, String nam
551553
* language, the {@link Builder#content(ByteSequence) contents} or the specified
552554
* {@link Builder#mimeType(String) MIME type}. A language may be detected from an existing file
553555
* using {@link #findLanguage(File)}.
554-
*
556+
*
555557
* <pre>
556558
* File file = new File(dir, name);
557559
* assert name.endsWith(".java") : "Imagine proper file";
@@ -581,7 +583,7 @@ public static Builder newBuilder(String language, File file) {
581583
* using {@link #findLanguage(URL)}.
582584
* <p>
583585
* Example usage:
584-
*
586+
*
585587
* <pre>
586588
* URL resource = relativeClass.getResource("sample.js");
587589
* Source source = Source.newBuilder("js", resource).build();
@@ -741,7 +743,7 @@ private static IllegalArgumentException invalidMimeType(String mimeType) {
741743
* Represents a builder to build {@link Source} objects.
742744
* <p>
743745
* To load a source from disk one can use:
744-
*
746+
*
745747
* <pre>
746748
* File file = new File(dir, name);
747749
* assert name.endsWith(".java") : "Imagine proper file";
@@ -753,35 +755,35 @@ private static IllegalArgumentException invalidMimeType(String mimeType) {
753755
* assert file.getPath().equals(source.getPath());
754756
* assert file.toURI().equals(source.getURI());
755757
* </pre>
756-
*
758+
*
757759
* To load source from a {@link URL} one can use:
758-
*
760+
*
759761
* <pre>
760762
* URL resource = relativeClass.getResource("sample.js");
761763
* Source source = Source.newBuilder("js", resource).build();
762764
* assert resource.toExternalForm().equals(source.getPath());
763765
* assert "sample.js".equals(source.getName());
764766
* assert resource.toURI().equals(source.getURI());
765767
* </pre>
766-
*
768+
*
767769
* To create a source representing characters:
768-
*
770+
*
769771
* <pre>
770772
* Source source = Source.newBuilder("js", "function() {\n" + " return 'Hi';\n" + "}\n", "hi.js").buildLiteral();
771773
* assert "hi.js".equals(source.getName());
772774
* </pre>
773-
*
775+
*
774776
* or read a source from a {@link Reader}:
775-
*
777+
*
776778
* <pre>
777779
* Reader stream = new InputStreamReader(
778780
* relativeClass.getResourceAsStream("sample.js"));
779781
* Source source = Source.newBuilder("js", stream, "sample.js").build();
780782
* assert "sample.js".equals(source.getName());
781783
* </pre>
782-
*
784+
*
783785
* To create a source representing bytes:
784-
*
786+
*
785787
* <pre>
786788
* byte[] bytes = new byte[]{ /* Binary &#42;/ };
787789
* Source source = Source.newBuilder("llvm",
@@ -806,6 +808,7 @@ public class Builder {
806808
private Object content;
807809
private String mimeType;
808810
private Charset fileEncoding;
811+
private Map<String, String> options;
809812

810813
Builder(String language, Object origin) {
811814
Objects.requireNonNull(language);
@@ -1016,6 +1019,52 @@ public Builder encoding(Charset encoding) {
10161019
return this;
10171020
}
10181021

1022+
/**
1023+
* Sets a source option key and value for the built source. Source options allow you to
1024+
* attach language- or instrument-specific configuration to a source. These options are
1025+
* parsed and validated only when {@link Context#eval(Source)} or
1026+
* {@link Context#parse(Source)} is invoked, rather than when the source is created.
1027+
* <p>
1028+
* If the source is validated, all source options will be validated. Therefore, each
1029+
* language or instrument associated with a source option must be installed.
1030+
* <p>
1031+
* Source options of any language or instrument can be supplied to all sources. Some source
1032+
* options may only have an effect when provided to sources of the language with the same
1033+
* id. The available source option keys can be found in the respective language or
1034+
* instrument documentation, or at runtime via {@link Language#getSourceOptions()} or
1035+
* {@link Instrument#getSourceOptions()}.
1036+
*
1037+
* @param key the option key (must not be null)
1038+
* @param value the option value (must not be null)
1039+
* @see Context#eval(Source)
1040+
* @see Context#parse(Source)
1041+
* @see Language#getSourceOptions()
1042+
* @see Instrument#getSourceOptions()
1043+
* @since 25.0
1044+
*/
1045+
public Builder option(String key, String value) {
1046+
Objects.requireNonNull(key);
1047+
Objects.requireNonNull(value);
1048+
if (this.options == null) {
1049+
this.options = new HashMap<>();
1050+
}
1051+
this.options.put(key, value);
1052+
return this;
1053+
}
1054+
1055+
/**
1056+
* Shortcut for setting multiple {@link #option(String, String) options} using a map. All
1057+
* values of the provided map must be non-null.
1058+
*
1059+
* @since 25.0
1060+
*/
1061+
public Builder options(@SuppressWarnings("hiding") Map<String, String> options) {
1062+
for (var entry : options.entrySet()) {
1063+
option(entry.getKey(), entry.getValue());
1064+
}
1065+
return this;
1066+
}
1067+
10191068
/**
10201069
* Uses configuration of this builder to create new {@link Source} object. The method throws
10211070
* an {@link IOException} if an error loading the source occurred.
@@ -1024,7 +1073,7 @@ public Builder encoding(Charset encoding) {
10241073
* @since 19.0
10251074
*/
10261075
public Source build() throws IOException {
1027-
Source source = (Source) getImpl().buildSource(language, origin, uri, name, mimeType, content, interactive, internal, cached, fileEncoding, null, null);
1076+
Source source = (Source) getImpl().buildSource(language, origin, uri, name, mimeType, content, interactive, internal, cached, fileEncoding, null, null, options);
10281077

10291078
// make sure origin is not consumed again if builder is used twice
10301079
if (source.hasBytes()) {

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,9 @@ public void resetPreInitializedEngine() {
559559
}
560560

561561
public Object buildSource(String language, Object origin, URI uri, String name, String mimeType, Object content, boolean interactive, boolean internal, boolean cached, Charset encoding, URL url,
562-
String path)
562+
String path, Map<String, String> options)
563563
throws IOException {
564-
return getNext().buildSource(language, origin, uri, name, mimeType, content, interactive, internal, cached, encoding, url, path);
564+
return getNext().buildSource(language, origin, uri, name, mimeType, content, interactive, internal, cached, encoding, url, path, options);
565565
}
566566

567567
public String findLanguage(File file) throws IOException {
@@ -708,6 +708,8 @@ protected AbstractSourceDispatch(AbstractPolyglotImpl engineImpl) {
708708

709709
public abstract String getLanguage(Object impl);
710710

711+
public abstract Map<String, String> getOptions(Object impl);
712+
711713
}
712714

713715
public abstract static class AbstractSourceSectionDispatch extends AbstractDispatchClass {
@@ -939,6 +941,8 @@ protected AbstractInstrumentDispatch(AbstractPolyglotImpl engineImpl) {
939941

940942
public abstract OptionDescriptors getOptions(Object receiver);
941943

944+
public abstract OptionDescriptors getSourceOptions(Object receiver);
945+
942946
public abstract String getVersion(Object receiver);
943947

944948
public abstract <T> T lookup(Object receiver, Class<T> type);
@@ -971,6 +975,8 @@ protected AbstractLanguageDispatch(AbstractPolyglotImpl engineImpl) {
971975

972976
public abstract OptionDescriptors getOptions(Object receiver);
973977

978+
public abstract OptionDescriptors getSourceOptions(Object receiver);
979+
974980
public abstract Set<String> getMimeTypes(Object receiver);
975981

976982
public abstract String getDefaultMimeType(Object receiver);

tools/src/com.oracle.truffle.tools.chromeinspector.test/src/com/oracle/truffle/tools/chromeinspector/test/SLInspectProfileTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,11 @@ public void testDetailedCodeCoverage() throws Exception {
161161
tester.eval(source).get();
162162
tester.sendMessage("{\"id\":5,\"method\":\"Profiler.takePreciseCoverage\"}");
163163
assertEquals("{\"result\":{\"result\":[{\"scriptId\":\"1\",\"functions\":["
164-
+ "{\"ranges\":[{\"endOffset\":90,\"startOffset\":69,\"count\":1},{\"endOffset\":66,\"startOffset\":57,\"count\":1}],\"functionName\":\"main\",\"isBlockCoverage\":true},"
165-
+ "{\"ranges\":[{\"endOffset\":34,\"startOffset\":22,\"count\":2}],\"functionName\":\"add\",\"isBlockCoverage\":true}],"
164+
+ "{\"ranges\":[{\"endOffset\":34,\"startOffset\":22,\"count\":2}],\"functionName\":\"add\",\"isBlockCoverage\":true},"
165+
+ "{\"ranges\":["
166+
+ "{\"endOffset\":66,\"startOffset\":57,\"count\":1},"
167+
+ "{\"endOffset\":90,\"startOffset\":69,\"count\":1}],"
168+
+ "\"functionName\":\"main\",\"isBlockCoverage\":true}],"
166169
+ "\"url\":\"" + slTestURI + "\"}]},\"id\":5}", tester.getMessages(true).trim());
167170
tester.sendMessage("{\"id\":6,\"method\":\"Profiler.takePreciseCoverage\"}");
168171
assertEquals("{\"result\":{\"result\":[]},\"id\":6}", tester.getMessages(true).trim());

truffle/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
This changelog summarizes major changes between Truffle versions relevant to languages implementors building upon the Truffle framework. The main focus is on APIs exported by Truffle.
44

5+
## Version 25.0
6+
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.
7+
8+
59
## Version 24.2.0
610
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
711

truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Breakpoint.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.truffle.api.debug;
4242

43+
import java.io.IOException;
4344
import java.lang.ref.Reference;
4445
import java.lang.ref.WeakReference;
4546
import java.net.URI;
@@ -130,8 +131,8 @@
130131
* <p>
131132
* Example usage:
132133
*
133-
* {@snippet file="com/oracle/truffle/api/debug/Breakpoint.java"
134-
* region="BreakpointSnippets#example"}
134+
* {@snippet file = "com/oracle/truffle/api/debug/Breakpoint.java" region =
135+
* "BreakpointSnippets#example"}
135136
*
136137
* @since 0.9
137138
*/
@@ -1671,7 +1672,12 @@ private void initializeConditional(MaterializedFrame frame) {
16711672
conditionSnippet = insert(snippet);
16721673
notifyInserted(snippet);
16731674
} else {
1674-
CallTarget callTarget = Debugger.ACCESSOR.parse(conditionSource, instrumentedNode, new String[0]);
1675+
CallTarget callTarget;
1676+
try {
1677+
callTarget = breakpoint.debugger.getEnv().parse(conditionSource);
1678+
} catch (IOException e) {
1679+
throw new IllegalStateException("Error parsing condition.", e);
1680+
}
16751681
conditionCallNode = insert(Truffle.getRuntime().createDirectCallNode(callTarget));
16761682
}
16771683
}

0 commit comments

Comments
 (0)