diff --git a/lkql_jit/cli/pom.xml b/lkql_jit/cli/pom.xml index d56eef1bf..d9369d019 100644 --- a/lkql_jit/cli/pom.xml +++ b/lkql_jit/cli/pom.xml @@ -45,6 +45,7 @@ org.graalvm.sdk:launcher-common info.picocli:picocli + com.adacore:options org.json:json @@ -105,9 +106,9 @@ - org.json - json - 20240303 + com.adacore + options + 0.1.0 diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/GNATCheckWorker.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/GNATCheckWorker.java similarity index 83% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/GNATCheckWorker.java rename to lkql_jit/cli/src/main/java/com/adacore/lkql_jit/GNATCheckWorker.java index 111bc3a2c..a85de5fad 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/GNATCheckWorker.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/GNATCheckWorker.java @@ -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; /** @@ -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 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 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()) { @@ -307,7 +268,14 @@ private static Map parseLKQLRuleFile(final String lkqlRule final Map 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 diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLChecker.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLChecker.java similarity index 77% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLChecker.java rename to lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLChecker.java index 71459c0cf..252ac30b3 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLChecker.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLChecker.java @@ -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; @@ -103,7 +100,7 @@ enum PropertyErrorRecoveryMode { @CommandLine.Option( names = {"-I", "--ignores"}, description = "Ada files to ignore during analysis") - public String ignores = null; + public List ignores = new ArrayList<>(); @CommandLine.Option( names = "--keep-going-on-missing-file", @@ -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()) { diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLDoc.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLDoc.java similarity index 88% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLDoc.java rename to lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLDoc.java index b2d852fdd..5067b9462 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLDoc.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLDoc.java @@ -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; @@ -32,7 +32,14 @@ public class LKQLDoc implements Callable { @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) { diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLLauncher.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLLauncher.java similarity index 85% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLLauncher.java rename to lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLLauncher.java index 0786138c4..7630585d4 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/LKQLLauncher.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLLauncher.java @@ -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.File; import java.io.IOException; @@ -36,7 +36,7 @@ public static class LKQLRun implements Callable { @CommandLine.Spec public CommandLine.Model.CommandSpec spec; @CommandLine.Parameters(description = "Files to analyze") - public List files; + public List files = new ArrayList<>(); @CommandLine.Option( names = {"-C", "--charset"}, @@ -154,43 +154,24 @@ 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); - - // Set the context options - if (this.args.verbose) { - System.out.println("=== LKQL JIT is in verbose mode ==="); - contextBuilder.option("lkql.verbose", "true"); - } - - // Set the project file - if (this.args.project != null) { - contextBuilder.option("lkql.projectFile", this.args.project); - } - - if (this.args.RTS != null) { - contextBuilder.option("lkql.runtime", this.args.RTS); - } + // Create the LKQL options object builder + final var optionsBuilder = new LKQLOptions.Builder(); - if (this.args.target != null) { - contextBuilder.option("lkql.target", this.args.target); - } - - if (this.args.keepGoingOnMissingFile) { - contextBuilder.option("lkql.keepGoingOnMissingFile", "true"); - } - - // Set the files - if (this.args.files != null) { - contextBuilder.option("lkql.files", String.join(",", this.args.files)); - } + // Set the common configuration + contextBuilder.allowIO(true); - // Set the charset - if (this.args.charset != null - && !this.args.charset.isEmpty() - && !this.args.charset.isBlank()) { - contextBuilder.option("lkql.charset", this.args.charset); - } + // Forward the command line options to the options object builder + optionsBuilder + .verbose(this.args.verbose) + .projectFile(this.args.project) + .target(this.args.target) + .runtime(this.args.RTS) + .keepGoingOnMissingFile(this.args.keepGoingOnMissingFile) + .files(this.args.files) + .charset(this.args.charset); + + // 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()) { diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLMain.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLMain.java index 1807f21a5..ed926bb12 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLMain.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/LKQLMain.java @@ -5,10 +5,6 @@ package com.adacore.lkql_jit; -import com.adacore.lkql_jit.drivers.GNATCheckWorker; -import com.adacore.lkql_jit.drivers.LKQLChecker; -import com.adacore.lkql_jit.drivers.LKQLDoc; -import com.adacore.lkql_jit.drivers.LKQLLauncher; import java.util.concurrent.Callable; import picocli.CommandLine; import picocli.CommandLine.Command; diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/JsonUtils.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/JsonUtils.java deleted file mode 100644 index 5094fc386..000000000 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/JsonUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (C) 2005-2024, AdaCore -// SPDX-License-Identifier: GPL-3.0-or-later -// - -package com.adacore.lkql_jit.options; - -import java.util.Map; -import java.util.stream.Collectors; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; - -/** This class contains all util functions to handle JSON */ -public final class JsonUtils { - - /** Serialize the given rule instance map to a JSON string. */ - public static String serializeInstances(final Map instances) { - final JSONObject res = new JSONObject(); - for (var instance : instances.entrySet()) { - res.put(instance.getKey(), instance.getValue().toJson()); - } - return res.toString(); - } - - /** - * Deserialize the given JSON source to a rule instance map. - * - * @throws JSONException If the given source cannot be deserialized. - */ - public static Map deserializeInstances(final String jsonSources) - throws JSONException { - JSONObject jsonInstances = new JSONObject(new JSONTokener(jsonSources)); - return jsonInstances.keySet().stream() - .collect( - Collectors.toMap( - s -> s, - s -> RuleInstance.fromJson(jsonInstances.getJSONObject(s)))); - } -} diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/package-info.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/package-info.java deleted file mode 100644 index 7e3df0bf8..000000000 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** This package contains all components required to configure the LKQL JIT engine. */ -package com.adacore.lkql_jit.options; diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/package-info.java b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/package-info.java similarity index 78% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/package-info.java rename to lkql_jit/cli/src/main/java/com/adacore/lkql_jit/package-info.java index 6732330dc..74e7ee3a2 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/drivers/package-info.java +++ b/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/package-info.java @@ -2,4 +2,4 @@ * This package contains all drivers for the LKQL JIT engine. Those are standard entry points for * user and tools like GNATcheck. */ -package com.adacore.lkql_jit.drivers; +package com.adacore.lkql_jit; diff --git a/lkql_jit/language/pom.xml b/lkql_jit/language/pom.xml index c7aca4144..570d5117d 100644 --- a/lkql_jit/language/pom.xml +++ b/lkql_jit/language/pom.xml @@ -38,7 +38,7 @@ com.adacore:liblkqllang com.adacore:libadalang - com.adacore:cli + com.adacore:options org.json:json @@ -99,7 +99,7 @@ com.adacore - cli + options 0.1.0 diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLContext.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLContext.java index c26a7b3bd..7b0e7a5fb 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLContext.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLContext.java @@ -11,12 +11,9 @@ import com.adacore.lkql_jit.checker.*; import com.adacore.lkql_jit.checker.utils.CheckerUtils; import com.adacore.lkql_jit.exception.LKQLRuntimeException; -import com.adacore.lkql_jit.options.JsonUtils; -import com.adacore.lkql_jit.options.RuleInstance; import com.adacore.lkql_jit.runtime.CallStack; import com.adacore.lkql_jit.runtime.GlobalScope; import com.adacore.lkql_jit.utils.Constants; -import com.adacore.lkql_jit.utils.functions.ArrayUtils; import com.adacore.lkql_jit.utils.functions.StringUtils; import com.adacore.lkql_jit.utils.source_location.LalLocationWrapper; import com.oracle.truffle.api.CompilerDirectives; @@ -25,6 +22,8 @@ import java.io.File; import java.util.*; import java.util.function.BiConsumer; +import org.json.JSONObject; +import org.json.JSONTokener; /** * This class represents the execution context of an LKQL script. @@ -102,12 +101,6 @@ public final class LKQLContext { // ----- Checker attributes ----- - /** - * All rule instantiated by the user through the command-line. Mapped from their identifier to - * their value. - */ - private Map ruleInstances = null; - /** A cache for all rule arguments to avoid evaluating twice the same argument source. */ private Map> instancesArgsCache = new HashMap<>(); @@ -128,40 +121,16 @@ public final class LKQLContext { // ----- Option caches ----- - /** Whether the language is in the verbose mode. */ - @CompilerDirectives.CompilationFinal private Boolean isVerbose = null; - - @CompilerDirectives.CompilationFinal private Boolean keepGoingOnMissingFile = null; - - @CompilerDirectives.CompilationFinal private Boolean showInstantiationChain = null; - - /** The project file to analyse. */ - @CompilerDirectives.CompilationFinal private String projectFile = null; - - @CompilerDirectives.CompilationFinal private String target = null; - - @CompilerDirectives.CompilationFinal private String runtime = null; - - @CompilerDirectives.CompilationFinal private String configFile = null; + /** Options object passed to the LKQL engine. */ + @CompilerDirectives.CompilationFinal private LKQLOptions options = null; /** The project's scenario variables. */ @CompilerDirectives.CompilationFinal(dimensions = 1) private Libadalang.ScenarioVariable[] scenarioVars = null; - /** The ada files passed through the command line. */ - @CompilerDirectives.CompilationFinal(dimensions = 1) - private String[] files = null; - - /** Whether the checker is in debug mode. */ - @CompilerDirectives.CompilationFinal private Boolean checkerDebug = null; - - /** The directories where the rule files are located. */ + /** Directories to look for LKQL rules into. */ @CompilerDirectives.CompilationFinal(dimensions = 1) - private String[] ruleDirectories; - - /** The files to ignore during an analysis. */ - @CompilerDirectives.CompilationFinal(dimensions = 1) - private String[] ignores; + private String[] ruleDirectories = null; /** Tool to emit diagnostics in the wanted format. */ @CompilerDirectives.CompilationFinal private CheckerUtils.DiagnosticEmitter emitter; @@ -234,34 +203,33 @@ public void patchContext(TruffleLanguage.Env newEnv) { // ----- Options getting methods ----- + /** Parse the LKQL engine options passed as a JSON string, store it in a cache and return it. */ + public LKQLOptions getOptions() { + if (this.options == null) { + final var optionsSource = this.env.getOptions().get(LKQLLanguage.options); + final var jsonObject = new JSONObject(new JSONTokener(optionsSource)); + this.options = LKQLOptions.fromJson(jsonObject); + } + return this.options; + } + /** * Get if the language execution is in verbose mode. * * @return True if the verbose flag is on. */ public boolean isVerbose() { - if (this.isVerbose == null) { - this.isVerbose = this.env.getOptions().get(LKQLLanguage.verbose); - } - return this.isVerbose; + return this.getOptions().verbose(); } /** Return true if the engine should keep running when a required file is not found. */ public boolean keepGoingOnMissingFile() { - if (this.keepGoingOnMissingFile == null) { - this.keepGoingOnMissingFile = - this.env.getOptions().get(LKQLLanguage.keepGoingOnMissingFile); - } - return this.keepGoingOnMissingFile; + return this.getOptions().keepGoingOnMissingFile(); } /** Get whether to display instantiation chain in diagnostics. */ public boolean showInstantiationChain() { - if (this.showInstantiationChain == null) { - this.showInstantiationChain = - this.env.getOptions().get(LKQLLanguage.showInstantiationChain); - } - return this.showInstantiationChain; + return this.getOptions().showInstantiationChain(); } /** @@ -270,59 +238,30 @@ public boolean showInstantiationChain() { * @return The project file in a string. */ public String getProjectFile() { - if (this.projectFile == null) { - this.projectFile = this.env.getOptions().get(LKQLLanguage.projectFile); - } - return this.projectFile; + return this.getOptions().projectFile().orElse(""); } public String getTarget() { - if (this.target == null) { - this.target = this.env.getOptions().get(LKQLLanguage.target); - } - return this.target; + return this.getOptions().target().orElse(""); } public String getRuntime() { - if (this.runtime == null) { - this.runtime = this.env.getOptions().get(LKQLLanguage.runtime); - } - return this.runtime; + return this.getOptions().runtime().orElse(""); } public String getConfigFile() { - if (this.configFile == null) { - this.configFile = this.env.getOptions().get(LKQLLanguage.configFile); - } - return this.configFile; + return this.getOptions().configFile().orElse(""); } /** Return the list of scenario variables to specify when loading the GPR project file. */ public Libadalang.ScenarioVariable[] getScenarioVars() { if (this.scenarioVars == null) { - // Scenario variables are passed as semicolon-separated substrings encoded in Base64. - String[] bindings = this.env.getOptions().get(LKQLLanguage.scenarioVars).split(";"); - if (bindings.length == 1 && bindings[0].length() == 0) { - // No scenario variables were specified - this.scenarioVars = new Libadalang.ScenarioVariable[0]; - } else { - // Some scenario variables were specified. Decode them from Base64 and parse the - // `key=value` - // specification. - Base64.Decoder decoder = Base64.getDecoder(); - this.scenarioVars = new Libadalang.ScenarioVariable[bindings.length]; - for (int i = 0; i < bindings.length; ++i) { - String binding = new String(decoder.decode(bindings[i])); - int eqIndex = binding.indexOf('='); - if (eqIndex == -1) { - throw LKQLRuntimeException.fromMessage( - "Invalid scenario variable specification: " + binding); - } - String name = binding.substring(0, eqIndex); - String value = binding.substring(eqIndex + 1); - this.scenarioVars[i] = Libadalang.ScenarioVariable.create(name, value); - } + final var scenarioVarList = new ArrayList(); + for (var entry : this.getOptions().scenarioVariables().entrySet()) { + scenarioVarList.add( + Libadalang.ScenarioVariable.create(entry.getKey(), entry.getValue())); } + this.scenarioVars = scenarioVarList.toArray(new Libadalang.ScenarioVariable[0]); } return this.scenarioVars; } @@ -333,10 +272,7 @@ public Libadalang.ScenarioVariable[] getScenarioVars() { * @return The files to analyse in an array. */ public String[] getFiles() { - if (this.files == null) { - this.files = StringUtils.splitPaths(this.env.getOptions().get(LKQLLanguage.files)); - } - return this.files; + return this.getOptions().files().toArray(new String[0]); } /** @@ -346,10 +282,7 @@ public String[] getFiles() { */ @CompilerDirectives.TruffleBoundary public boolean isCheckerDebug() { - if (this.checkerDebug == null) { - this.checkerDebug = this.env.getOptions().get(LKQLLanguage.checkerDebug); - } - return this.checkerDebug; + return this.getOptions().checkerDebug(); } /** @@ -359,14 +292,12 @@ public boolean isCheckerDebug() { */ public String[] getRuleDirectories() { if (this.ruleDirectories == null) { - this.ruleDirectories = - StringUtils.splitPaths(this.env.getOptions().get(LKQLLanguage.rulesDirs)); - String additionalRulesDirs = System.getenv(Constants.LKQL_RULES_PATH); + final var rulesDirsList = new ArrayList<>(this.getOptions().rulesDirs()); + final var additionalRulesDirs = System.getenv(Constants.LKQL_RULES_PATH); if (additionalRulesDirs != null) { - this.ruleDirectories = - ArrayUtils.concat( - this.ruleDirectories, StringUtils.splitPaths(additionalRulesDirs)); + rulesDirsList.addAll(Arrays.asList(StringUtils.splitPaths(additionalRulesDirs))); } + this.ruleDirectories = rulesDirsList.toArray(new String[0]); } return this.ruleDirectories; } @@ -377,25 +308,13 @@ public String[] getRuleDirectories() { * @return The array containing all Ada files to ignore. */ public String[] getIgnores() { - if (this.ignores == null) { - this.ignores = StringUtils.splitPaths(this.env.getOptions().get(LKQLLanguage.ignores)); - this.ignores = - Arrays.stream(this.ignores) - .filter(s -> !s.isBlank() && !s.isEmpty()) - .toArray(String[]::new); - } - return this.ignores; + return this.getOptions().ignores().toArray(new String[0]); } /** Invalidate the option caches. */ private void invalidateOptionCaches() { - this.isVerbose = null; - this.projectFile = null; - this.files = null; - this.ruleInstances = null; + this.options = null; this.instancesArgsCache = new HashMap<>(); - this.ruleDirectories = null; - this.ignores = null; this.emitter = null; } @@ -440,7 +359,7 @@ public void println(String toPrint) { public CheckerUtils.DiagnosticEmitter getDiagnosticEmitter() { if (this.emitter == null) { this.emitter = - switch (this.env.getOptions().get(LKQLLanguage.diagnosticOutputMode)) { + switch (this.getOptions().diagnosticOutputMode()) { case PRETTY -> new CheckerUtils.DefaultEmitter(); case GNATCHECK -> new CheckerUtils.GNATcheckEmitter(); }; @@ -529,9 +448,8 @@ public void initSources() { } // Get the subproject provided by the user - final String subprojectName = this.env.getOptions().get(LKQLLanguage.subprojectFile); final String[] subprojects = - subprojectName.isEmpty() ? null : new String[] {subprojectName}; + this.getOptions().subprojectFile().map(s -> new String[] {s}).orElse(null); // If no files were specified by the user, the files to analyze are those of the root // project (i.e. without recursing into project dependencies) @@ -554,7 +472,7 @@ public void initSources() { this.adaContext = this.projectManager.createContext( - subprojectName.isEmpty() ? null : subprojectName, + this.getOptions().subprojectFile().orElse(null), this.eventHandler, true, 8); @@ -570,7 +488,7 @@ public void initSources() { // If the option is the empty string, the language implementation will end up setting it // to the default value for its language (e.g. iso-8859-1 for Ada). - String charset = this.env.getOptions().get(LKQLLanguage.charset); + String charset = this.getOptions().charset().orElse(""); // Load the implicit project this.projectManager = @@ -598,19 +516,7 @@ public void initSources() { @CompilerDirectives.TruffleBoundary public Map getRuleInstances() { - if (this.ruleInstances == null) { - try { - this.ruleInstances = - JsonUtils.deserializeInstances( - this.env.getOptions().get(LKQLLanguage.ruleInstances)); - } catch (Exception e) { - // Since the LKQL option is internal, the user cannot provide a raw value so this - // is not supposed to happen. - e.printStackTrace(); - throw LKQLRuntimeException.shouldNotHappen("Invalid JSON for rule instances"); - } - } - return this.ruleInstances; + return this.getOptions().ruleInstances(); } /** @@ -734,8 +640,7 @@ private void initCheckerCaches() { }; // If there are no required instance, check if we have to fall back on all checkers - if (this.getRuleInstances().isEmpty() - && this.env.getOptions().get(LKQLLanguage.fallbackToAllRules)) { + if (this.getRuleInstances().isEmpty() && this.getOptions().fallbackToAllRules()) { for (BaseChecker checker : allCheckers.values()) { dispatchChecker.accept(checker, generalNodeCheckers); } diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java index 7cf899eef..19a8f676c 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java @@ -17,7 +17,6 @@ import com.adacore.lkql_jit.nodes.root_nodes.TopLevelRootNode; import com.adacore.lkql_jit.runtime.GlobalScope; import com.adacore.lkql_jit.utils.Constants; -import com.adacore.lkql_jit.utils.enums.DiagnosticOutputMode; import com.adacore.lkql_jit.utils.source_location.SourceSectionWrapper; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Option; @@ -87,164 +86,12 @@ public final class LKQLLanguage extends TruffleLanguage { // ----- Options ----- - // --- Language options - - /** The option to define if the language is verbose. */ + /** The JSON encoded LKQL engine options. */ @Option( help = "If the language should be verbose", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey verbose = new OptionKey<>(false); - - // --- LKQL options - - /** The option to define the charset of the LKQL sources. */ - @Option( - help = "The LKQL source charset", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey charset = new OptionKey<>(""); - - /** The option to define the project file to analyze. */ - @Option( - help = "The GPR project file to load", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey projectFile = new OptionKey<>(""); - - /** The name of the subproject to analyze. If empty, use the root project instead */ - @Option( - help = "The name of the subproject to analyze.", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey subprojectFile = new OptionKey<>(""); - - /** The runtime to load the project with */ - @Option( - help = "The runtime to pass to GPR", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey runtime = new OptionKey<>(""); - - /** The target to load the project with */ - @Option( - help = "The hardware target to pass to GPR", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey target = new OptionKey<>(""); - - /** The config GPR file to pass during project loading. */ - @Option( - help = "The config file for GPR project loading", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey configFile = new OptionKey<>(""); - - /** - * The scenario variables to load the project file with, where "key=value" variable - * specifications are encoded as Base64 and separated by semicolons. - */ - @Option( - help = "The scenario variables to load the project file with", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey scenarioVars = new OptionKey<>(""); - - /** The option to define the files to analyze. */ - @Option( - help = "The ada files to analyze in LKQL", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey files = new OptionKey<>(""); - - /** The option to define the jobs. */ - @Option( - help = "The number of parallel jobs in the LKQL interpreter", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey jobs = new OptionKey<>(0); - - // --- Checker options - - /** The option to define the checker debug mode. */ - @Option( - help = "If the checker is in debug mode", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey checkerDebug = new OptionKey<>(false); - - /** The option to define the directories to look the rules from. */ - @Option( - help = "The directories to search rules in", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey rulesDirs = new OptionKey<>(""); - - /** - * Option to specify rules that the user instantiated for a run. The provided value must follow - * the JSON encoding standard for rule instances: - * - *
-     * {
-     *     "instance_id_1": {...},
-     *     "instance_id_2": {...}
-     * }
-     * 
- * - * The default value if this option is an empty object. See the {@link - * com.adacore.lkql_jit.options.RuleInstance RuleInstance} class for more information about rule - * instance JSON encoding. - */ - @Option( - help = "The JSON encoded rule instances, the value must be a JSON object", category = OptionCategory.INTERNAL, stability = OptionStability.STABLE) - public static final OptionKey ruleInstances = new OptionKey<>("{}"); - - /** The option to control what should be done when no rules are provided */ - @Option( - help = "If true, consider that an empty value for 'rules' means to run all the rules", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey fallbackToAllRules = new OptionKey<>(true); - - /** The option to control what should be done when a source file cannot be found. */ - @Option( - help = "If true, do not stop the engine when a source file cannot be found", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey keepGoingOnMissingFile = new OptionKey<>(false); - - /** - * The option to control whether to display the instantiation chains when reporting something on - * a generic construct. - */ - @Option( - help = "If true, display generic instantiation chain", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey showInstantiationChain = new OptionKey<>(false); - - /** The option to specify the files to ignore during the checking. */ - @Option( - help = "Files to ignore during the analysis", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey ignores = new OptionKey<>(""); - - /** The option to specify the error recovery mode. */ - @Option( - help = "The mode of error recovery in the checker", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey errorMode = new OptionKey<>(""); - - @Option( - help = "The message emitter", - category = OptionCategory.USER, - stability = OptionStability.STABLE) - static final OptionKey diagnosticOutputMode = - new OptionKey<>(DiagnosticOutputMode.PRETTY); + static final OptionKey options = new OptionKey<>("{}"); Liblkqllang.AnalysisContext lkqlAnalysisContext; diff --git a/lkql_jit/options/pom.xml b/lkql_jit/options/pom.xml new file mode 100644 index 000000000..88bfc3a3e --- /dev/null +++ b/lkql_jit/options/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + lkql_jit + com.adacore + 0.1.0 + + + options + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.40.0 + + true + + + + + + + + org.json + json + 20240303 + + + + diff --git a/lkql_jit/options/src/main/java/com/adacore/lkql_jit/JSONUtils.java b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/JSONUtils.java new file mode 100644 index 000000000..41db05a01 --- /dev/null +++ b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/JSONUtils.java @@ -0,0 +1,20 @@ +// +// Copyright (C) 2005-2024, AdaCore +// SPDX-License-Identifier: GPL-3.0-or-later +// + +package com.adacore.lkql_jit; // + +import java.util.Map; +import java.util.stream.Collectors; +import org.json.JSONObject; + +/** This class contains all util functions to handle JSON */ +public final class JSONUtils { + /** Given a JSON object, parse it as a String to String map and return the result. */ + public static Map parseStringMap(JSONObject jsonObject) { + return jsonObject.toMap().entrySet().stream() + .map(e -> Map.entry(e.getKey(), (String) e.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/lkql_jit/options/src/main/java/com/adacore/lkql_jit/LKQLOptions.java b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/LKQLOptions.java new file mode 100644 index 000000000..8018dcbc5 --- /dev/null +++ b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/LKQLOptions.java @@ -0,0 +1,294 @@ +// +// Copyright (C) 2005-2024, AdaCore +// SPDX-License-Identifier: GPL-3.0-or-later +// + +package com.adacore.lkql_jit; + +import java.util.*; +import java.util.stream.Collectors; +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * This record contains all options to tune the LKQL engine. It is serializable and deserializable + * using the JSON format. + */ +public record LKQLOptions( + boolean verbose, + Optional charset, + Optional projectFile, + Optional subprojectFile, + Optional runtime, + Optional target, + Optional configFile, + Map scenarioVariables, + List files, + List ignores, + List rulesDirs, + Map ruleInstances, + boolean checkerDebug, + boolean fallbackToAllRules, + boolean keepGoingOnMissingFile, + boolean showInstantiationChain, + DiagnosticOutputMode diagnosticOutputMode) { + + // ----- Constructors ----- + + public LKQLOptions { + // Ensure that there is no null values in the LKQL options, also ensure that all + // contained values are strictly unmodifiable. + if (scenarioVariables == null) { + scenarioVariables = Map.of(); + } else { + scenarioVariables = Collections.unmodifiableMap(scenarioVariables); + } + + if (files == null) { + files = List.of(); + } else { + files = Collections.unmodifiableList(files); + } + + if (ignores == null) { + ignores = List.of(); + } else { + ignores = Collections.unmodifiableList(ignores); + } + + if (rulesDirs == null) { + rulesDirs = List.of(); + } else { + rulesDirs = Collections.unmodifiableList(rulesDirs); + } + + if (ruleInstances == null) { + ruleInstances = Map.of(); + } else { + ruleInstances = Collections.unmodifiableMap(ruleInstances); + } + + if (diagnosticOutputMode == null) { + diagnosticOutputMode = DiagnosticOutputMode.PRETTY; + } + } + + // ----- Class methods ----- + + /** Create an LKQL options object from the provided JSON object. */ + public static LKQLOptions fromJson(JSONObject jsonLKQLOptions) { + final Map ruleInstances = new HashMap<>(); + final var ruleInstancesJson = jsonLKQLOptions.getJSONObject("ruleInstances"); + for (String instanceName : ruleInstancesJson.keySet()) { + ruleInstances.put( + instanceName, + RuleInstance.fromJson(ruleInstancesJson.getJSONObject(instanceName))); + } + return new LKQLOptions( + jsonLKQLOptions.getBoolean("verbose"), + Optional.ofNullable(jsonLKQLOptions.optString("charset", null)), + Optional.ofNullable(jsonLKQLOptions.optString("projectFile", null)), + Optional.ofNullable(jsonLKQLOptions.optString("subprojectFile", null)), + Optional.ofNullable(jsonLKQLOptions.optString("runtime", null)), + Optional.ofNullable(jsonLKQLOptions.optString("target", null)), + Optional.ofNullable(jsonLKQLOptions.optString("configFile", null)), + JSONUtils.parseStringMap(jsonLKQLOptions.getJSONObject("scenarioVariables")), + jsonLKQLOptions.getJSONArray("files").toList().stream() + .map(e -> (String) e) + .toList(), + jsonLKQLOptions.getJSONArray("ignores").toList().stream() + .map(e -> (String) e) + .toList(), + jsonLKQLOptions.getJSONArray("rulesDirs").toList().stream() + .map(e -> (String) e) + .toList(), + ruleInstances, + jsonLKQLOptions.getBoolean("checkerDebug"), + jsonLKQLOptions.getBoolean("fallbackToAllRules"), + jsonLKQLOptions.getBoolean("keepGoingOnMissingFile"), + jsonLKQLOptions.getBoolean("showInstantiationChain"), + DiagnosticOutputMode.valueOf(jsonLKQLOptions.getString("diagnosticOutputMode"))); + } + + // ----- Instance methods ----- + + /** Serialize the LKQL options to a JSON object. */ + public JSONObject toJson() { + final var ruleInstancesJson = + new JSONObject( + this.ruleInstances.entrySet().stream() + .map(e -> Map.entry(e.getKey(), e.getValue().toJson())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + return new JSONObject() + .put("verbose", verbose) + .put("charset", charset.orElse(null)) + .put("projectFile", projectFile.orElse(null)) + .put("subprojectFile", subprojectFile.orElse(null)) + .put("runtime", runtime.orElse(null)) + .put("target", target.orElse(null)) + .put("configFile", configFile.orElse(null)) + .put("scenarioVariables", new JSONObject(scenarioVariables)) + .put("files", new JSONArray(files)) + .put("ignores", new JSONArray(ignores)) + .put("rulesDirs", new JSONArray(rulesDirs)) + .put("ruleInstances", ruleInstancesJson) + .put("checkerDebug", checkerDebug) + .put("fallbackToAllRules", fallbackToAllRules) + .put("keepGoingOnMissingFile", keepGoingOnMissingFile) + .put("showInstantiationChain", showInstantiationChain) + .put("diagnosticOutputMode", diagnosticOutputMode.toString()); + } + + // ----- Inner classes ----- + + /** The way diagnostics are output by the LKQL engine. */ + public enum DiagnosticOutputMode { + /** + * Emit a pretty diagnostic with source listing where the diagnostic location is + * highlighted. + */ + PRETTY, + + /** Use a GNATCheck-compliant format: "{file}:{line}:{col} check: {message} [{check}]". */ + GNATCHECK + } + + /** Util class to build a new LKQL options object. */ + public static final class Builder { + + // ----- Options ----- + + private boolean verbose = false; + private Optional charset = Optional.empty(); + private Optional projectFile = Optional.empty(); + private Optional subprojectFile = Optional.empty(); + private Optional runtime = Optional.empty(); + private Optional target = Optional.empty(); + private Optional configFile = Optional.empty(); + private Map scenarioVariables = new HashMap<>(); + private List files = new ArrayList<>(); + private List ignores = new ArrayList<>(); + private List rulesDirs = new ArrayList<>(); + private Map ruleInstances = new HashMap<>(); + private boolean checkerDebug = false; + private boolean fallbackToAllRules = false; + private boolean keepGoingOnMissingFile = false; + private boolean showInstantiationChain = false; + private DiagnosticOutputMode diagnosticOutputMode = DiagnosticOutputMode.PRETTY; + + // ----- Setters ----- + + public Builder verbose(boolean v) { + verbose = v; + return this; + } + + public Builder charset(String c) { + charset = Optional.ofNullable(c); + return this; + } + + public Builder projectFile(String pf) { + projectFile = Optional.ofNullable(pf); + return this; + } + + public Builder subprojectFile(String spf) { + subprojectFile = Optional.ofNullable((spf)); + return this; + } + + public Builder runtime(String r) { + runtime = Optional.ofNullable(r); + return this; + } + + public Builder target(String t) { + target = Optional.ofNullable(t); + return this; + } + + public Builder configFile(String cf) { + configFile = Optional.ofNullable(cf); + return this; + } + + public Builder scenarioVariables(Map sv) { + this.scenarioVariables = sv; + return this; + } + + public Builder files(List files) { + this.files = files; + return this; + } + + public Builder addFile(String f) { + files.add(f); + return this; + } + + public Builder ignores(List i) { + this.ignores = i; + return this; + } + + public Builder rulesDir(List rd) { + this.rulesDirs = rd; + return this; + } + + public Builder ruleInstances(Map ri) { + this.ruleInstances = ri; + return this; + } + + public Builder checkerDebug(boolean cd) { + checkerDebug = cd; + return this; + } + + public Builder fallbackToAllRules(boolean fbtar) { + fallbackToAllRules = fbtar; + return this; + } + + public Builder keepGoingOnMissingFile(boolean kgomf) { + keepGoingOnMissingFile = kgomf; + return this; + } + + public Builder showInstantiationChain(boolean sic) { + showInstantiationChain = sic; + return this; + } + + public Builder diagnosticOutputMode(DiagnosticOutputMode dom) { + diagnosticOutputMode = dom; + return this; + } + + // ----- Instance methods ----- + + public LKQLOptions build() { + return new LKQLOptions( + verbose, + charset, + projectFile, + subprojectFile, + runtime, + target, + configFile, + scenarioVariables, + files, + ignores, + rulesDirs, + ruleInstances, + checkerDebug, + fallbackToAllRules, + keepGoingOnMissingFile, + showInstantiationChain, + diagnosticOutputMode); + } + } +} diff --git a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/RuleInstance.java b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/RuleInstance.java similarity index 91% rename from lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/RuleInstance.java rename to lkql_jit/options/src/main/java/com/adacore/lkql_jit/RuleInstance.java index 8816dd5cd..54db60906 100644 --- a/lkql_jit/cli/src/main/java/com/adacore/lkql_jit/options/RuleInstance.java +++ b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/RuleInstance.java @@ -3,11 +3,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later // -package com.adacore.lkql_jit.options; +package com.adacore.lkql_jit; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import org.json.JSONException; import org.json.JSONObject; @@ -51,14 +50,14 @@ public record RuleInstance( } } + // ----- Constructors ----- + public static RuleInstance fromJson(JSONObject jsonObject) throws JSONException { return new RuleInstance( jsonObject.getString("ruleName"), Optional.ofNullable(jsonObject.optString("instanceName", null)), SourceMode.valueOf(jsonObject.getString("sourceMode")), - jsonObject.getJSONObject("arguments").toMap().entrySet().stream() - .map(e -> Map.entry(e.getKey(), (String) e.getValue())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + JSONUtils.parseStringMap(jsonObject.getJSONObject("arguments"))); } // ----- Instance methods ----- diff --git a/lkql_jit/options/src/main/java/com/adacore/lkql_jit/package-info.java b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/package-info.java new file mode 100644 index 000000000..c7df8e5b5 --- /dev/null +++ b/lkql_jit/options/src/main/java/com/adacore/lkql_jit/package-info.java @@ -0,0 +1,2 @@ +/** This package contains all classes and tools to forward options to the LKQL engine. */ +package com.adacore.lkql_jit; diff --git a/lkql_jit/pom.xml b/lkql_jit/pom.xml index 795901057..d8c8e9295 100644 --- a/lkql_jit/pom.xml +++ b/lkql_jit/pom.xml @@ -10,6 +10,7 @@ 0.1.0 language + options cli native component @@ -122,6 +123,7 @@ language/src/main/java/**/*.java + options/src/main/java/**/*.java cli/src/main/java/**/*.java benchmarks/src/test/java/**/*.java diff --git a/testsuite/tests/interop/function/Main.java b/testsuite/tests/interop/function/Main.java index 0fcbf6c3b..463eb542b 100644 --- a/testsuite/tests/interop/function/Main.java +++ b/testsuite/tests/interop/function/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/iterator/Main.java b/testsuite/tests/interop/iterator/Main.java index 01755bd08..392a87540 100644 --- a/testsuite/tests/interop/iterator/Main.java +++ b/testsuite/tests/interop/iterator/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -7,7 +8,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql").build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", "val list = [1, \"Hello\", [1, 2]]"); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/list/Main.java b/testsuite/tests/interop/list/Main.java index 5098ce0a9..b6f384c44 100644 --- a/testsuite/tests/interop/list/Main.java +++ b/testsuite/tests/interop/list/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -13,9 +14,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/list_comp/Main.java b/testsuite/tests/interop/list_comp/Main.java index f7178bde9..328c44022 100644 --- a/testsuite/tests/interop/list_comp/Main.java +++ b/testsuite/tests/interop/list_comp/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -13,9 +14,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/namespace/Main.java b/testsuite/tests/interop/namespace/Main.java index 4c7bffc62..4cbef4271 100644 --- a/testsuite/tests/interop/namespace/Main.java +++ b/testsuite/tests/interop/namespace/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/null/Main.java b/testsuite/tests/interop/null/Main.java index 417d32951..5ac511cac 100644 --- a/testsuite/tests/interop/null/Main.java +++ b/testsuite/tests/interop/null/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/object/Main.java b/testsuite/tests/interop/object/Main.java index bc84053fd..9f9d788bd 100644 --- a/testsuite/tests/interop/object/Main.java +++ b/testsuite/tests/interop/object/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/pattern/Main.java b/testsuite/tests/interop/pattern/Main.java index 58acdbe45..e83f11f51 100644 --- a/testsuite/tests/interop/pattern/Main.java +++ b/testsuite/tests/interop/pattern/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/property/Main.java b/testsuite/tests/interop/property/Main.java index a7ad0e482..3a5482081 100644 --- a/testsuite/tests/interop/property/Main.java +++ b/testsuite/tests/interop/property/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/selector/Main.java b/testsuite/tests/interop/selector/Main.java index 68243fe30..1044eabf8 100644 --- a/testsuite/tests/interop/selector/Main.java +++ b/testsuite/tests/interop/selector/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -16,9 +17,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/tuple/Main.java b/testsuite/tests/interop/tuple/Main.java index e31b645fc..8344a82ef 100644 --- a/testsuite/tests/interop/tuple/Main.java +++ b/testsuite/tests/interop/tuple/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false); diff --git a/testsuite/tests/interop/unit/Main.java b/testsuite/tests/interop/unit/Main.java index 5e7437f55..d07655316 100644 --- a/testsuite/tests/interop/unit/Main.java +++ b/testsuite/tests/interop/unit/Main.java @@ -1,3 +1,4 @@ +import com.adacore.lkql_jit.LKQLOptions; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -12,9 +13,15 @@ private static void print(String messageName, Object value) { } public static void main(String[] args) { - Context context = Context.newBuilder("lkql") - .option("lkql.projectFile", "default_project/default.gpr") - .build(); + Context context = Context + .newBuilder("lkql") + .option( + "lkql.options", new LKQLOptions.Builder() + .projectFile("default_project/default.gpr") + .build() + .toJson() + .toString() + ).build(); Value executable = context.parse("lkql", LKQL_SOURCE); Value namespace = executable.execute(false);