Skip to content

Commit

Permalink
Merge branch 'topic/lkql_jit_options' into 'master'
Browse files Browse the repository at this point in the history
Forward all LKQL engine options through a JSON encoded string

Closes #346

See merge request eng/libadalang/langkit-query-language!297
  • Loading branch information
HugoGGuerrier committed Oct 7, 2024
2 parents d42dc54 + fcc49d6 commit a9d5802
Show file tree
Hide file tree
Showing 30 changed files with 638 additions and 573 deletions.
7 changes: 4 additions & 3 deletions lkql_jit/cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<includes>
<include>org.graalvm.sdk:launcher-common</include>
<include>info.picocli:picocli</include>
<include>com.adacore:options</include>
<include>org.json:json</include>
</includes>
</artifactSet>
Expand Down Expand Up @@ -105,9 +106,9 @@
</dependency>

<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
<groupId>com.adacore</groupId>
<artifactId>options</artifactId>
<version>0.1.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//

package com.adacore.lkql_jit.drivers;
package com.adacore.lkql_jit;

import com.adacore.lkql_jit.options.JsonUtils;
import com.adacore.lkql_jit.options.RuleInstance;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.graalvm.launcher.AbstractLanguageLauncher;
import org.graalvm.options.OptionCategory;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.json.JSONObject;
import picocli.CommandLine;

/**
Expand Down Expand Up @@ -168,106 +168,67 @@ protected void launch(Context.Builder contextBuilder) {
* @return The exit code of the script.
*/
protected int executeScript(Context.Builder contextBuilder) {
// Set the builder common options
contextBuilder.allowIO(true);
contextBuilder.option("lkql.diagnosticOutputMode", "GNATCHECK");

// If no rules are provided, don't do anything
contextBuilder.option("lkql.fallbackToAllRules", "false");
// Create the LKQL options object builder
final var optionsBuilder = new LKQLOptions.Builder();

// Do not stop the worker's execution when a source file is missing
contextBuilder.option("lkql.keepGoingOnMissingFile", "true");
// Set the common configuration
contextBuilder.allowIO(true);
contextBuilder.engine(
Engine.newBuilder()
.allowExperimentalOptions(true)
.option("engine.Compilation", "false")
.build());
optionsBuilder
.diagnosticOutputMode(LKQLOptions.DiagnosticOutputMode.GNATCHECK)
.fallbackToAllRules(false)
.keepGoingOnMissingFile(true);

// If a LKQL rule config file has been provided, parse it and display the result
if (this.args.lkqlConfigFile != null) {
System.out.println(
JsonUtils.serializeInstances(parseLKQLRuleFile(this.args.lkqlConfigFile)));
new JSONObject(
parseLKQLRuleFile(this.args.lkqlConfigFile).entrySet().stream()
.map(e -> Map.entry(e.getKey(), e.getValue().toJson()))
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue))));
return 0;
}

// Set the context options
if (this.args.verbose) {
contextBuilder.option("lkql.verbose", "true");
}

// Set the project file
if (this.args.project != null) {
contextBuilder.option("lkql.projectFile", this.args.project);
}

if (this.args.subProject != null) {
contextBuilder.option("lkql.subprojectFile", this.args.subProject);
}

if (this.args.debug) {
contextBuilder.option("lkql.checkerDebug", "true");
}

if (!this.args.scenarioVariables.isEmpty()) {
StringBuilder scenarioVars = new StringBuilder();
Base64.Encoder encoder = Base64.getEncoder();
this.args.scenarioVariables.forEach(
(key, val) -> {
scenarioVars.append(
new String(encoder.encode((key + "=" + val).getBytes())));
scenarioVars.append(";");
});
contextBuilder.option("lkql.scenarioVars", scenarioVars.toString());
}

if (this.args.target != null) {
contextBuilder.option("lkql.target", this.args.target);
}

if (this.args.RTS != null) {
contextBuilder.option("lkql.runtime", this.args.RTS);
}

if (this.args.configFile != null) {
contextBuilder.option("lkql.configFile", this.args.configFile);
}

// Set the files
// Forward the command line options to the options object builder
optionsBuilder
.verbose(this.args.verbose)
.projectFile(this.args.project)
.subprojectFile(this.args.subProject)
.scenarioVariables(this.args.scenarioVariables)
.target(this.args.target)
.runtime(this.args.RTS)
.configFile(this.args.configFile)
.charset(this.args.charset)
.rulesDir(this.args.rulesDirs)
.showInstantiationChain(this.args.showInstantiationChain)
.checkerDebug(this.args.debug);

// Read the list of sources to analyze provided by GNATcheck driver
if (this.args.filesFrom != null) {
try {
final List<String> lines = Files.readAllLines(Paths.get(this.args.filesFrom));
final String files = String.join(File.pathSeparator, lines);
contextBuilder.option("lkql.files", files);
optionsBuilder.files(Files.readAllLines(Paths.get(this.args.filesFrom)));
} catch (IOException e) {
System.err.println("Could not read file: " + this.args.filesFrom);
}
}

// Set the charset
if (this.args.charset != null) {
contextBuilder.option("lkql.charset", this.args.charset);
}

// Set the rule directories
if (!this.args.rulesDirs.isEmpty()) {
contextBuilder.option(
"lkql.rulesDirs", String.join(File.pathSeparator, this.args.rulesDirs));
}

// Set the generic instantiation displaying parameter
if (this.args.showInstantiationChain) {
contextBuilder.option("lkql.showInstantiationChain", "true");
}

// Set the rule instances
// Parse the rule instances provided by the GNATcheck driver
final Map<String, RuleInstance> instances = new HashMap<>();
for (var rulesFrom : this.args.rulesFroms) {
if (!rulesFrom.isEmpty()) {
instances.putAll(parseLKQLRuleFile(rulesFrom));
}
}
contextBuilder.option("lkql.ruleInstances", JsonUtils.serializeInstances(instances));
optionsBuilder.ruleInstances(instances);

contextBuilder.engine(
Engine.newBuilder()
.allowExperimentalOptions(true)
.option("engine.Compilation", "false")
.build());
// Finally, pass the options to the LKQL engine
contextBuilder.option("lkql.options", optionsBuilder.build().toJson().toString());

// Create the context and run the script in it
try (Context context = contextBuilder.build()) {
Expand Down Expand Up @@ -307,7 +268,14 @@ private static Map<String, RuleInstance> parseLKQLRuleFile(final String lkqlRule
final Map<String, RuleInstance> res = new HashMap<>();
try (Context context =
Context.newBuilder()
.option("lkql.diagnosticOutputMode", "GNATCHECK")
.option(
"lkql.options",
new LKQLOptions.Builder()
.diagnosticOutputMode(
LKQLOptions.DiagnosticOutputMode.GNATCHECK)
.build()
.toJson()
.toString())
.allowIO(true)
.build()) {
// Parse the LKQL rule configuration file with a polyglot context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//

package com.adacore.lkql_jit.drivers;
package com.adacore.lkql_jit;

import com.adacore.lkql_jit.options.JsonUtils;
import com.adacore.lkql_jit.options.RuleInstance;
import java.io.File;
import java.util.*;
import java.util.concurrent.Callable;
import org.graalvm.launcher.AbstractLanguageLauncher;
Expand Down Expand Up @@ -103,7 +100,7 @@ enum PropertyErrorRecoveryMode {
@CommandLine.Option(
names = {"-I", "--ignores"},
description = "Ada files to ignore during analysis")
public String ignores = null;
public List<String> ignores = new ArrayList<>();

@CommandLine.Option(
names = "--keep-going-on-missing-file",
Expand Down Expand Up @@ -176,70 +173,35 @@ protected void launch(Context.Builder contextBuilder) {
* @return The exit code of the script.
*/
protected int executeScript(Context.Builder contextBuilder) {
// Set the builder common options
contextBuilder.allowIO(true);

contextBuilder.option("lkql.checkerDebug", "true");

// Set the context options
if (this.args.verbose) {
System.out.println("=== LKQL JIT is in verbose mode ===");
contextBuilder.option("lkql.verbose", "true");
}

if (this.args.keepGoingOnMissingFile) {
contextBuilder.option("lkql.keepGoingOnMissingFile", "true");
}

// Set the project file
if (this.args.project != null) {
contextBuilder.option("lkql.projectFile", this.args.project);
}

// Set the files
if (!this.args.files.isEmpty()) {
contextBuilder.option("lkql.files", String.join(File.pathSeparator, this.args.files));
}

// Set the charset
if (this.args.charset != null
&& !this.args.charset.isEmpty()
&& !this.args.charset.isBlank()) {
contextBuilder.option("lkql.charset", this.args.charset);
}

if (this.args.RTS != null) {
contextBuilder.option("lkql.runtime", this.args.RTS);
}

if (this.args.target != null) {
contextBuilder.option("lkql.target", this.args.target);
}

// Set the rule directories
if (!this.args.rulesDirs.isEmpty()) {
contextBuilder.option(
"lkql.rulesDirs", String.join(File.pathSeparator, this.args.rulesDirs));
}

// Pass the rule instances to the LKQL engine
try {
contextBuilder.option(
"lkql.ruleInstances", JsonUtils.serializeInstances(this.getRuleInstances()));
} catch (Exception e) {
System.err.println(e.getMessage());
}

// Set the Ada files to ignore during the analysis
if (this.args.ignores != null) {
contextBuilder.option("lkql.ignores", this.args.ignores);
}

// This is needed to make sure that calls to `exitContext` done from within an isolate
// thread (e.g. executing a Java callback from Ada code) directly stop the program instead
// of going it the normal way by raising a special exception, as such exceptions won't be
// handled by the caller when thrown from inside the isolate thread.
contextBuilder.useSystemExit(true);
// Create the LKQL options object builder
final var optionsBuilder = new LKQLOptions.Builder();

// Set the common configurations
contextBuilder
.allowIO(true)
// This is needed to make sure that calls to `exitContext` done from within an
// isolate thread (e.g. executing a Java callback from Ada code) directly stop
// the program instead of going it the normal way by raising a special exception,
// as such exceptions won't be handled by the caller when thrown from inside the
// isolate thread.
.useSystemExit(true);
optionsBuilder.checkerDebug(true);

// Forward the command line options to the options object builder
optionsBuilder
.verbose(this.args.verbose)
.keepGoingOnMissingFile(this.args.keepGoingOnMissingFile)
.projectFile(this.args.project)
.files(this.args.files)
.ignores(this.args.ignores)
.charset(this.args.charset)
.target(this.args.target)
.runtime(this.args.RTS)
.rulesDir(this.args.rulesDirs)
.ruleInstances(this.getRuleInstances());

// Finally, pass the options to the LKQL engine
contextBuilder.option("lkql.options", optionsBuilder.build().toJson().toString());

// Create the context and run the script in it
try (Context context = contextBuilder.build()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//

package com.adacore.lkql_jit.drivers;
package com.adacore.lkql_jit;

import java.io.BufferedWriter;
import java.io.IOException;
Expand Down Expand Up @@ -32,7 +32,14 @@ public class LKQLDoc implements Callable<Integer> {

@Override
public Integer call() {
Context context = Context.newBuilder("lkql").allowAllAccess(true).build();
Context context =
Context.newBuilder("lkql")
.allowAllAccess(true)
// Set default LKQL options
.option(
"lkql.options",
new LKQLOptions.Builder().build().toJson().toString())
.build();
try {
Files.createDirectories(FileSystems.getDefault().getPath(outputDir));
} catch (IOException e) {
Expand Down
Loading

0 comments on commit a9d5802

Please sign in to comment.