Skip to content

Commit fcc49d6

Browse files
committed
Forward LKQL engine options through a JSON encoded string
Remove all ad-hoc encoding methods and now forward all LKQL options as a JSON object which can be serialized into a Java class.
1 parent 3fbdb30 commit fcc49d6

File tree

21 files changed

+570
-535
lines changed

21 files changed

+570
-535
lines changed

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/GNATCheckWorker.java

Lines changed: 49 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
import java.nio.file.Paths;
1212
import java.util.*;
1313
import java.util.concurrent.Callable;
14+
import java.util.stream.Collectors;
1415
import org.graalvm.launcher.AbstractLanguageLauncher;
1516
import org.graalvm.options.OptionCategory;
1617
import org.graalvm.polyglot.Context;
1718
import org.graalvm.polyglot.Engine;
1819
import org.graalvm.polyglot.Source;
1920
import org.graalvm.polyglot.Value;
21+
import org.json.JSONObject;
2022
import picocli.CommandLine;
2123

2224
/**
@@ -166,106 +168,67 @@ protected void launch(Context.Builder contextBuilder) {
166168
* @return The exit code of the script.
167169
*/
168170
protected int executeScript(Context.Builder contextBuilder) {
169-
// Set the builder common options
170-
contextBuilder.allowIO(true);
171-
contextBuilder.option("lkql.diagnosticOutputMode", "GNATCHECK");
172-
173-
// If no rules are provided, don't do anything
174-
contextBuilder.option("lkql.fallbackToAllRules", "false");
171+
// Create the LKQL options object builder
172+
final var optionsBuilder = new LKQLOptions.Builder();
175173

176-
// Do not stop the worker's execution when a source file is missing
177-
contextBuilder.option("lkql.keepGoingOnMissingFile", "true");
174+
// Set the common configuration
175+
contextBuilder.allowIO(true);
176+
contextBuilder.engine(
177+
Engine.newBuilder()
178+
.allowExperimentalOptions(true)
179+
.option("engine.Compilation", "false")
180+
.build());
181+
optionsBuilder
182+
.diagnosticOutputMode(LKQLOptions.DiagnosticOutputMode.GNATCHECK)
183+
.fallbackToAllRules(false)
184+
.keepGoingOnMissingFile(true);
178185

179186
// If a LKQL rule config file has been provided, parse it and display the result
180187
if (this.args.lkqlConfigFile != null) {
181188
System.out.println(
182-
JSONUtils.serializeInstances(parseLKQLRuleFile(this.args.lkqlConfigFile)));
189+
new JSONObject(
190+
parseLKQLRuleFile(this.args.lkqlConfigFile).entrySet().stream()
191+
.map(e -> Map.entry(e.getKey(), e.getValue().toJson()))
192+
.collect(
193+
Collectors.toMap(
194+
Map.Entry::getKey, Map.Entry::getValue))));
183195
return 0;
184196
}
185197

186-
// Set the context options
187-
if (this.args.verbose) {
188-
contextBuilder.option("lkql.verbose", "true");
189-
}
190-
191-
// Set the project file
192-
if (this.args.project != null) {
193-
contextBuilder.option("lkql.projectFile", this.args.project);
194-
}
195-
196-
if (this.args.subProject != null) {
197-
contextBuilder.option("lkql.subprojectFile", this.args.subProject);
198-
}
199-
200-
if (this.args.debug) {
201-
contextBuilder.option("lkql.checkerDebug", "true");
202-
}
203-
204-
if (!this.args.scenarioVariables.isEmpty()) {
205-
StringBuilder scenarioVars = new StringBuilder();
206-
Base64.Encoder encoder = Base64.getEncoder();
207-
this.args.scenarioVariables.forEach(
208-
(key, val) -> {
209-
scenarioVars.append(
210-
new String(encoder.encode((key + "=" + val).getBytes())));
211-
scenarioVars.append(";");
212-
});
213-
contextBuilder.option("lkql.scenarioVars", scenarioVars.toString());
214-
}
215-
216-
if (this.args.target != null) {
217-
contextBuilder.option("lkql.target", this.args.target);
218-
}
219-
220-
if (this.args.RTS != null) {
221-
contextBuilder.option("lkql.runtime", this.args.RTS);
222-
}
223-
224-
if (this.args.configFile != null) {
225-
contextBuilder.option("lkql.configFile", this.args.configFile);
226-
}
227-
228-
// Set the files
198+
// Forward the command line options to the options object builder
199+
optionsBuilder
200+
.verbose(this.args.verbose)
201+
.projectFile(this.args.project)
202+
.subprojectFile(this.args.subProject)
203+
.scenarioVariables(this.args.scenarioVariables)
204+
.target(this.args.target)
205+
.runtime(this.args.RTS)
206+
.configFile(this.args.configFile)
207+
.charset(this.args.charset)
208+
.rulesDir(this.args.rulesDirs)
209+
.showInstantiationChain(this.args.showInstantiationChain)
210+
.checkerDebug(this.args.debug);
211+
212+
// Read the list of sources to analyze provided by GNATcheck driver
229213
if (this.args.filesFrom != null) {
230214
try {
231-
final List<String> lines = Files.readAllLines(Paths.get(this.args.filesFrom));
232-
final String files = String.join(File.pathSeparator, lines);
233-
contextBuilder.option("lkql.files", files);
215+
optionsBuilder.files(Files.readAllLines(Paths.get(this.args.filesFrom)));
234216
} catch (IOException e) {
235217
System.err.println("Could not read file: " + this.args.filesFrom);
236218
}
237219
}
238220

239-
// Set the charset
240-
if (this.args.charset != null) {
241-
contextBuilder.option("lkql.charset", this.args.charset);
242-
}
243-
244-
// Set the rule directories
245-
if (!this.args.rulesDirs.isEmpty()) {
246-
contextBuilder.option(
247-
"lkql.rulesDirs", String.join(File.pathSeparator, this.args.rulesDirs));
248-
}
249-
250-
// Set the generic instantiation displaying parameter
251-
if (this.args.showInstantiationChain) {
252-
contextBuilder.option("lkql.showInstantiationChain", "true");
253-
}
254-
255-
// Set the rule instances
221+
// Parse the rule instances provided by the GNATcheck driver
256222
final Map<String, RuleInstance> instances = new HashMap<>();
257223
for (var rulesFrom : this.args.rulesFroms) {
258224
if (!rulesFrom.isEmpty()) {
259225
instances.putAll(parseLKQLRuleFile(rulesFrom));
260226
}
261227
}
262-
contextBuilder.option("lkql.ruleInstances", JSONUtils.serializeInstances(instances));
228+
optionsBuilder.ruleInstances(instances);
263229

264-
contextBuilder.engine(
265-
Engine.newBuilder()
266-
.allowExperimentalOptions(true)
267-
.option("engine.Compilation", "false")
268-
.build());
230+
// Finally, pass the options to the LKQL engine
231+
contextBuilder.option("lkql.options", optionsBuilder.build().toJson().toString());
269232

270233
// Create the context and run the script in it
271234
try (Context context = contextBuilder.build()) {
@@ -305,7 +268,14 @@ private static Map<String, RuleInstance> parseLKQLRuleFile(final String lkqlRule
305268
final Map<String, RuleInstance> res = new HashMap<>();
306269
try (Context context =
307270
Context.newBuilder()
308-
.option("lkql.diagnosticOutputMode", "GNATCHECK")
271+
.option(
272+
"lkql.options",
273+
new LKQLOptions.Builder()
274+
.diagnosticOutputMode(
275+
LKQLOptions.DiagnosticOutputMode.GNATCHECK)
276+
.build()
277+
.toJson()
278+
.toString())
309279
.allowIO(true)
310280
.build()) {
311281
// Parse the LKQL rule configuration file with a polyglot context

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLChecker.java

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
package com.adacore.lkql_jit;
77

8-
import java.io.File;
98
import java.util.*;
109
import java.util.concurrent.Callable;
1110
import org.graalvm.launcher.AbstractLanguageLauncher;
@@ -101,7 +100,7 @@ enum PropertyErrorRecoveryMode {
101100
@CommandLine.Option(
102101
names = {"-I", "--ignores"},
103102
description = "Ada files to ignore during analysis")
104-
public String ignores = null;
103+
public List<String> ignores = new ArrayList<>();
105104

106105
@CommandLine.Option(
107106
names = "--keep-going-on-missing-file",
@@ -174,70 +173,35 @@ protected void launch(Context.Builder contextBuilder) {
174173
* @return The exit code of the script.
175174
*/
176175
protected int executeScript(Context.Builder contextBuilder) {
177-
// Set the builder common options
178-
contextBuilder.allowIO(true);
179-
180-
contextBuilder.option("lkql.checkerDebug", "true");
181-
182-
// Set the context options
183-
if (this.args.verbose) {
184-
System.out.println("=== LKQL JIT is in verbose mode ===");
185-
contextBuilder.option("lkql.verbose", "true");
186-
}
187-
188-
if (this.args.keepGoingOnMissingFile) {
189-
contextBuilder.option("lkql.keepGoingOnMissingFile", "true");
190-
}
191-
192-
// Set the project file
193-
if (this.args.project != null) {
194-
contextBuilder.option("lkql.projectFile", this.args.project);
195-
}
196-
197-
// Set the files
198-
if (!this.args.files.isEmpty()) {
199-
contextBuilder.option("lkql.files", String.join(File.pathSeparator, this.args.files));
200-
}
201-
202-
// Set the charset
203-
if (this.args.charset != null
204-
&& !this.args.charset.isEmpty()
205-
&& !this.args.charset.isBlank()) {
206-
contextBuilder.option("lkql.charset", this.args.charset);
207-
}
208-
209-
if (this.args.RTS != null) {
210-
contextBuilder.option("lkql.runtime", this.args.RTS);
211-
}
212-
213-
if (this.args.target != null) {
214-
contextBuilder.option("lkql.target", this.args.target);
215-
}
216-
217-
// Set the rule directories
218-
if (!this.args.rulesDirs.isEmpty()) {
219-
contextBuilder.option(
220-
"lkql.rulesDirs", String.join(File.pathSeparator, this.args.rulesDirs));
221-
}
222-
223-
// Pass the rule instances to the LKQL engine
224-
try {
225-
contextBuilder.option(
226-
"lkql.ruleInstances", JSONUtils.serializeInstances(this.getRuleInstances()));
227-
} catch (Exception e) {
228-
System.err.println(e.getMessage());
229-
}
230-
231-
// Set the Ada files to ignore during the analysis
232-
if (this.args.ignores != null) {
233-
contextBuilder.option("lkql.ignores", this.args.ignores);
234-
}
235-
236-
// This is needed to make sure that calls to `exitContext` done from within an isolate
237-
// thread (e.g. executing a Java callback from Ada code) directly stop the program instead
238-
// of going it the normal way by raising a special exception, as such exceptions won't be
239-
// handled by the caller when thrown from inside the isolate thread.
240-
contextBuilder.useSystemExit(true);
176+
// Create the LKQL options object builder
177+
final var optionsBuilder = new LKQLOptions.Builder();
178+
179+
// Set the common configurations
180+
contextBuilder
181+
.allowIO(true)
182+
// This is needed to make sure that calls to `exitContext` done from within an
183+
// isolate thread (e.g. executing a Java callback from Ada code) directly stop
184+
// the program instead of going it the normal way by raising a special exception,
185+
// as such exceptions won't be handled by the caller when thrown from inside the
186+
// isolate thread.
187+
.useSystemExit(true);
188+
optionsBuilder.checkerDebug(true);
189+
190+
// Forward the command line options to the options object builder
191+
optionsBuilder
192+
.verbose(this.args.verbose)
193+
.keepGoingOnMissingFile(this.args.keepGoingOnMissingFile)
194+
.projectFile(this.args.project)
195+
.files(this.args.files)
196+
.ignores(this.args.ignores)
197+
.charset(this.args.charset)
198+
.target(this.args.target)
199+
.runtime(this.args.RTS)
200+
.rulesDir(this.args.rulesDirs)
201+
.ruleInstances(this.getRuleInstances());
202+
203+
// Finally, pass the options to the LKQL engine
204+
contextBuilder.option("lkql.options", optionsBuilder.build().toJson().toString());
241205

242206
// Create the context and run the script in it
243207
try (Context context = contextBuilder.build()) {

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLDoc.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ public class LKQLDoc implements Callable<Integer> {
3232

3333
@Override
3434
public Integer call() {
35-
Context context = Context.newBuilder("lkql").allowAllAccess(true).build();
35+
Context context =
36+
Context.newBuilder("lkql")
37+
.allowAllAccess(true)
38+
// Set default LKQL options
39+
.option(
40+
"lkql.options",
41+
new LKQLOptions.Builder().build().toJson().toString())
42+
.build();
3643
try {
3744
Files.createDirectories(FileSystems.getDefault().getPath(outputDir));
3845
} catch (IOException e) {

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLLauncher.java

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static class LKQLRun implements Callable<Integer> {
3636
@CommandLine.Spec public CommandLine.Model.CommandSpec spec;
3737

3838
@CommandLine.Parameters(description = "Files to analyze")
39-
public List<String> files;
39+
public List<String> files = new ArrayList<>();
4040

4141
@CommandLine.Option(
4242
names = {"-C", "--charset"},
@@ -154,43 +154,24 @@ protected void launch(Context.Builder contextBuilder) {
154154
* @return The exit code of the script.
155155
*/
156156
protected int executeScript(Context.Builder contextBuilder) {
157-
// Set the builder common options
158-
contextBuilder.allowIO(true);
159-
160-
// Set the context options
161-
if (this.args.verbose) {
162-
System.out.println("=== LKQL JIT is in verbose mode ===");
163-
contextBuilder.option("lkql.verbose", "true");
164-
}
165-
166-
// Set the project file
167-
if (this.args.project != null) {
168-
contextBuilder.option("lkql.projectFile", this.args.project);
169-
}
170-
171-
if (this.args.RTS != null) {
172-
contextBuilder.option("lkql.runtime", this.args.RTS);
173-
}
157+
// Create the LKQL options object builder
158+
final var optionsBuilder = new LKQLOptions.Builder();
174159

175-
if (this.args.target != null) {
176-
contextBuilder.option("lkql.target", this.args.target);
177-
}
178-
179-
if (this.args.keepGoingOnMissingFile) {
180-
contextBuilder.option("lkql.keepGoingOnMissingFile", "true");
181-
}
182-
183-
// Set the files
184-
if (this.args.files != null) {
185-
contextBuilder.option("lkql.files", String.join(",", this.args.files));
186-
}
160+
// Set the common configuration
161+
contextBuilder.allowIO(true);
187162

188-
// Set the charset
189-
if (this.args.charset != null
190-
&& !this.args.charset.isEmpty()
191-
&& !this.args.charset.isBlank()) {
192-
contextBuilder.option("lkql.charset", this.args.charset);
193-
}
163+
// Forward the command line options to the options object builder
164+
optionsBuilder
165+
.verbose(this.args.verbose)
166+
.projectFile(this.args.project)
167+
.target(this.args.target)
168+
.runtime(this.args.RTS)
169+
.keepGoingOnMissingFile(this.args.keepGoingOnMissingFile)
170+
.files(this.args.files)
171+
.charset(this.args.charset);
172+
173+
// Finally, pass the options to the LKQL engine
174+
contextBuilder.option("lkql.options", optionsBuilder.build().toJson().toString());
194175

195176
// Create the context and run the script in it
196177
try (Context context = contextBuilder.build()) {

0 commit comments

Comments
 (0)