diff --git a/bom/pom.xml b/bom/pom.xml
index af3f71462..afe9217ed 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -78,6 +78,11 @@
redline-bridge-experimental
${project.version}
+
+ run.endive
+ redline-build-time-compiler-experimental
+ ${project.version}
+
run.endive
redline-compiler-experimental
diff --git a/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java b/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java
index 9e4245caa..6a954e005 100644
--- a/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java
+++ b/build-time-compiler-cli/src/main/java/run/endive/experimental/compiler/cli/Cli.java
@@ -71,6 +71,14 @@ public String[] getVersion() {
"The indexes of functions that should be interpreted, separated by commas")
Set interpretedFunctions;
+ @CommandLine.Option(
+ order = 7,
+ names = "--module-interface",
+ description =
+ "Fully qualified class name for which to generate _ModuleExports and"
+ + " _ModuleImports wrappers")
+ String moduleInterface;
+
@Override
public void run() {
var config =
@@ -82,6 +90,7 @@ public void run() {
.withTargetWasmFolder(targetWasmFolder)
.withInterpreterFallback(interpreterFallback)
.withInterpretedFunctions(interpretedFunctions)
+ .withModuleInterface(moduleInterface)
.build();
var generator = new Generator(config);
@@ -90,13 +99,19 @@ public void run() {
var interpretedFunctions = generator.generateResources();
generator.generateMetaWasm(interpretedFunctions);
generator.generateSources();
+ if (moduleInterface != null && !moduleInterface.isEmpty()) {
+ generator.generateModuleInterface(moduleInterface);
+ }
} catch (IOException e) {
throw new CommandLine.PicocliException("Failed to execute the command", e);
}
}
+ public static int execute(String[] args) {
+ return new CommandLine(new Cli()).execute(args);
+ }
+
public static void main(String[] args) {
- int exitCode = new CommandLine(new Cli()).execute(args);
- System.exit(exitCode);
+ System.exit(execute(args));
}
}
diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java
index 1aba2b9b9..22a870d6e 100644
--- a/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java
+++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/Config.java
@@ -1,6 +1,7 @@
package run.endive.build.time.compiler;
import java.nio.file.Path;
+import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import run.endive.compiler.InterpreterFallback;
@@ -46,6 +47,16 @@ public final class Config {
*/
private final String moduleInterface;
+ /**
+ * target triples for redline native compilation (empty = no native compilation)
+ */
+ private final List redlineTargets;
+
+ /**
+ * the target resource folder for native code files
+ */
+ private final Path targetResourceFolder;
+
private Config(
Path wasmFile,
String name,
@@ -54,7 +65,9 @@ private Config(
Path targetWasmFolder,
InterpreterFallback interpreterFallback,
Set interpretedFunctions,
- String moduleInterface) {
+ String moduleInterface,
+ List redlineTargets,
+ Path targetResourceFolder) {
this.wasmFile = wasmFile;
this.name = name;
this.targetClassFolder = targetClassFolder;
@@ -63,6 +76,8 @@ private Config(
this.interpreterFallback = interpreterFallback;
this.interpretedFunctions = interpretedFunctions;
this.moduleInterface = moduleInterface;
+ this.redlineTargets = redlineTargets;
+ this.targetResourceFolder = targetResourceFolder;
}
public Path wasmFile() {
@@ -97,6 +112,18 @@ public String moduleInterface() {
return moduleInterface;
}
+ public List redlineTargets() {
+ return redlineTargets;
+ }
+
+ public Path targetResourceFolder() {
+ return targetResourceFolder;
+ }
+
+ public boolean hasRedlineTargets() {
+ return redlineTargets != null && !redlineTargets.isEmpty();
+ }
+
public static Builder builder() {
return new Builder();
}
@@ -126,6 +153,8 @@ public static final class Builder {
private InterpreterFallback interpreterFallback = InterpreterFallback.FAIL;
private Set interpretedFunctions;
private String moduleInterface;
+ private List redlineTargets = List.of();
+ private Path targetResourceFolder;
private Builder() {}
@@ -169,6 +198,16 @@ public Builder withModuleInterface(String moduleInterface) {
return this;
}
+ public Builder withRedlineTargets(List redlineTargets) {
+ this.redlineTargets = redlineTargets;
+ return this;
+ }
+
+ public Builder withTargetResourceFolder(Path targetResourceFolder) {
+ this.targetResourceFolder = targetResourceFolder;
+ return this;
+ }
+
public Config build() {
return new Config(
wasmFile,
@@ -178,7 +217,9 @@ public Config build() {
targetWasmFolder,
interpreterFallback,
interpretedFunctions,
- moduleInterface);
+ moduleInterface,
+ redlineTargets,
+ targetResourceFolder);
}
}
}
diff --git a/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java b/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java
new file mode 100644
index 000000000..fa24c8d3d
--- /dev/null
+++ b/build-time-compiler/src/main/java/run/endive/build/time/compiler/GeneratorMain.java
@@ -0,0 +1,40 @@
+package run.endive.build.time.compiler;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import run.endive.compiler.InterpreterFallback;
+
+public final class GeneratorMain {
+
+ private GeneratorMain() {}
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 5) {
+ throw new IllegalArgumentException(
+ "Usage: GeneratorMain "
+ + " "
+ + " [interpreterFallback] [moduleInterface]");
+ }
+ var configBuilder =
+ Config.builder()
+ .withWasmFile(Path.of(args[0]))
+ .withName(args[1])
+ .withTargetClassFolder(Path.of(args[2]))
+ .withTargetSourceFolder(Path.of(args[3]))
+ .withTargetWasmFolder(Path.of(args[4]));
+ if (args.length > 5 && !args[5].isEmpty()) {
+ configBuilder.withInterpreterFallback(InterpreterFallback.valueOf(args[5]));
+ }
+ if (args.length > 6 && !args[6].isEmpty()) {
+ configBuilder.withModuleInterface(args[6]);
+ }
+ var config = configBuilder.build();
+ var generator = new Generator(config);
+ var interpreted = generator.generateResources();
+ generator.generateMetaWasm(interpreted);
+ generator.generateSources();
+ if (config.moduleInterface() != null && !config.moduleInterface().isEmpty()) {
+ generator.generateModuleInterface(config.moduleInterface());
+ }
+ }
+}
diff --git a/compiler-maven-plugin/pom.xml b/compiler-maven-plugin/pom.xml
index 223b8df00..303c703b7 100644
--- a/compiler-maven-plugin/pom.xml
+++ b/compiler-maven-plugin/pom.xml
@@ -62,4 +62,5 @@
+
diff --git a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java
index 5866a32ed..21d30b5c9 100644
--- a/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java
+++ b/compiler-maven-plugin/src/main/java/run/endive/build/time/maven/EndiveCompilerGenMojo.java
@@ -2,6 +2,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.apache.maven.model.Resource;
@@ -21,65 +22,55 @@
@Mojo(name = "compile", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true)
public class EndiveCompilerGenMojo extends AbstractMojo {
- /**
- * the wasm module to be used
- */
@Parameter(required = true)
private File wasmFile;
- /**
- * the base name to be used for the generated classes
- */
@Parameter(required = true)
private String name;
- /**
- * the target folder to generate classes
- */
@Parameter(
required = true,
defaultValue = "${project.build.directory}/generated-resources/endive-compiler")
private File targetClassFolder;
- /**
- * the target source folder to generate the Machine implementation
- */
@Parameter(
required = true,
defaultValue = "${project.build.directory}/generated-sources/endive-compiler")
private File targetSourceFolder;
- /**
- * the target wasm folder to generate the stripped meta wasm module
- */
@Parameter(
required = true,
defaultValue = "${project.build.directory}/generated-resources/endive-compiler")
private File targetWasmFolder;
- /**
- * the action to take if the compiler needs to use the interpreter because a function is too big
- */
@Parameter(required = true, defaultValue = "FAIL")
InterpreterFallback interpreterFallback;
- /**
- * The indexes of functions that should be interpreted, separated by commas
- */
@Parameter(required = false, defaultValue = "")
Set interpretedFunctions;
- /**
- * Fully qualified name of the user's class that will use the compiled module.
- * When set, the plugin generates _ModuleExports and _ModuleImports wrapper classes,
- * eliminating the need for @WasmModuleInterface annotation and the annotation processor.
- */
@Parameter(required = false)
String moduleInterface;
/**
- * The current Maven project.
+ * Enable Redline native compilation (experimental) for all supported
+ * platforms (x86_64 and aarch64 on Linux, macOS, and Windows).
+ */
+ @Parameter(required = false, defaultValue = "false")
+ boolean redlineExperimental;
+
+ /**
+ * Target triples for Redline native compilation. Overrides {@code redlineExperimental}
+ * for fine-grained control over which platforms to cross-compile for.
*/
+ @Parameter(required = false)
+ List redlineTargets;
+
+ @Parameter(
+ required = true,
+ defaultValue = "${project.build.directory}/generated-resources/endive-compiler")
+ private File targetResourceFolder;
+
@Parameter(property = "project", required = true, readonly = true)
private MavenProject project;
@@ -87,7 +78,7 @@ public class EndiveCompilerGenMojo extends AbstractMojo {
public void execute() throws MojoExecutionException {
getLog().info("Compiling classes for " + name + " from " + wasmFile);
- var config =
+ var configBuilder =
Config.builder()
.withWasmFile(wasmFile.toPath())
.withName(name)
@@ -97,7 +88,13 @@ public void execute() throws MojoExecutionException {
.withInterpreterFallback(interpreterFallback)
.withInterpretedFunctions(interpretedFunctions)
.withModuleInterface(moduleInterface)
- .build();
+ .withTargetResourceFolder(targetResourceFolder.toPath());
+ if (redlineTargets != null && !redlineTargets.isEmpty()) {
+ configBuilder.withRedlineTargets(redlineTargets);
+ } else if (redlineExperimental) {
+ configBuilder.withRedlineTargets(redlineAllTargets());
+ }
+ var config = configBuilder.build();
var generator = new Generator(config);
@@ -106,6 +103,11 @@ public void execute() throws MojoExecutionException {
generator.generateMetaWasm(finalInterpretedFunctions);
generator.generateSources();
+ if (config.hasRedlineTargets()) {
+ getLog().info("Redline native compilation for targets: " + config.redlineTargets());
+ invokeRedlineGenerator(config);
+ }
+
if (moduleInterface != null && !moduleInterface.isEmpty()) {
generator.generateModuleInterface(moduleInterface);
}
@@ -133,4 +135,35 @@ public void execute() throws MojoExecutionException {
project.addResource(resource);
project.addCompileSourceRoot(targetSourceFolder.getPath());
}
+
+ @SuppressWarnings("unchecked")
+ private static List redlineAllTargets() throws MojoExecutionException {
+ try {
+ var clazz = Class.forName("run.endive.redline.experimental.build.RedlineGenerator");
+ return (List) clazz.getMethod("allTargets").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ throw new MojoExecutionException(
+ "Redline support requires redline-build-time-compiler on the classpath"
+ + " (build with -Predline)",
+ e);
+ }
+ }
+
+ private static void invokeRedlineGenerator(Config config) throws IOException {
+ try {
+ var clazz = Class.forName("run.endive.redline.experimental.build.RedlineGenerator");
+ var constructor = clazz.getConstructor(Config.class);
+ var generator = constructor.newInstance(config);
+ clazz.getMethod("generateNativeCode").invoke(generator);
+ clazz.getMethod("extendGeneratedSources").invoke(generator);
+ } catch (ReflectiveOperationException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ }
+ throw new RuntimeException(
+ "Redline support requires redline-build-time-compiler on the classpath"
+ + " (build with -Predline)",
+ e);
+ }
+ }
}
diff --git a/pom.xml b/pom.xml
index 0c33bf67e..44f6fc327 100644
--- a/pom.xml
+++ b/pom.xml
@@ -268,6 +268,11 @@
redline-bridge-experimental
${project.version}
+
+ run.endive
+ redline-build-time-compiler-experimental
+ ${project.version}
+
run.endive
redline-compiler-experimental
diff --git a/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java b/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java
index 933d2feef..dba0b0ab9 100644
--- a/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java
+++ b/redline/api/src/main/java/run/endive/redline/experimental/api/internal/RedlineTarget.java
@@ -27,6 +27,15 @@ public String resourceSuffix() {
return resourceSuffix;
}
+ public static Optional fromTriple(String triple) {
+ for (RedlineTarget target : values()) {
+ if (target.triple.equals(triple)) {
+ return Optional.of(target);
+ }
+ }
+ return Optional.empty();
+ }
+
public static Optional detectHost() {
String osName =
System.getProperty("endive.redline.os.name", System.getProperty("os.name", ""))
diff --git a/redline/bridge/pom.xml b/redline/bridge/pom.xml
index 412153b93..bbf6c3627 100644
--- a/redline/bridge/pom.xml
+++ b/redline/bridge/pom.xml
@@ -29,21 +29,76 @@
+
- run.endive
- endive-compiler-maven-plugin
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${build-helper-maven-plugin.version}
- redline-bridge
+ add-generated-sources
- compile
+ add-source
+ generate-sources
- run.endive.redline.experimental.bridge.internal.Cranelift
- ${project.basedir}/../cranelift_bridge.wasm
- WARN
- run.endive.redline.experimental.bridge.internal.CraneliftBridge
+
+ ${project.build.directory}/generated-sources/endive-compiler
+
+
+
+
+ add-generated-resources
+
+ add-resource
+
+ generate-resources
+
+
+
+ ${project.build.directory}/generated-resources/endive-compiler
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ run.endive
+ build-time-compiler
+ ${project.version}
+
+
+
+
+ redline-bridge-compile
+
+ java
+
+ generate-sources
+
+ run.endive.build.time.compiler.GeneratorMain
+
+ ${project.basedir}/../cranelift_bridge.wasm
+ run.endive.redline.experimental.bridge.internal.Cranelift
+ ${project.build.directory}/generated-resources/endive-compiler
+ ${project.build.directory}/generated-sources/endive-compiler
+ ${project.build.directory}/generated-resources/endive-compiler
+ WARN
+ run.endive.redline.experimental.bridge.internal.CraneliftBridge
+
+ true
+ false
diff --git a/redline/build-time-compiler/pom.xml b/redline/build-time-compiler/pom.xml
new file mode 100644
index 000000000..50ee68428
--- /dev/null
+++ b/redline/build-time-compiler/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+
+ run.endive
+ redline-parent-experimental
+ 999-SNAPSHOT
+ ../pom.xml
+
+ redline-build-time-compiler-experimental
+ jar
+ Endive - Redline Build Time Compiler
+ Build-time native code generation via Cranelift
+
+
+
+ com.github.javaparser
+ javaparser-core
+
+
+ run.endive
+ build-time-compiler
+
+
+ run.endive
+ redline-api-experimental
+
+
+ run.endive
+ redline-compiler-experimental
+
+
+ run.endive
+ wasm
+
+
+
diff --git a/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java
new file mode 100644
index 000000000..9ebad5534
--- /dev/null
+++ b/redline/build-time-compiler/src/main/java/run/endive/redline/experimental/build/RedlineGenerator.java
@@ -0,0 +1,182 @@
+package run.endive.redline.experimental.build;
+
+import static com.github.javaparser.StaticJavaParser.parseClassOrInterfaceType;
+import static com.github.javaparser.StaticJavaParser.parseType;
+
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.Modifier;
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.expr.FieldAccessExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import run.endive.build.time.compiler.Config;
+import run.endive.redline.experimental.api.NativeCodeSerializer;
+import run.endive.redline.experimental.api.internal.RedlineTarget;
+import run.endive.redline.experimental.compiler.internal.NativeCompiler;
+import run.endive.wasm.Parser;
+
+public final class RedlineGenerator {
+
+ private final Config config;
+
+ public static List allTargets() {
+ return Arrays.stream(RedlineTarget.values())
+ .map(RedlineTarget::triple)
+ .collect(Collectors.toList());
+ }
+
+ public RedlineGenerator(Config config) {
+ this.config = config;
+ }
+
+ public void generateNativeCode() throws IOException {
+ if (!config.hasRedlineTargets()) {
+ return;
+ }
+ var module = Parser.parse(config.wasmFile());
+ var packagePath = config.getPackageName().replace('.', '/');
+ var baseName = config.getBaseName();
+ var resourceDir = config.targetResourceFolder().resolve(packagePath);
+ Files.createDirectories(resourceDir);
+
+ for (String triple : config.redlineTargets()) {
+ byte[][] compiledCode = NativeCompiler.compileAll(triple, module);
+
+ var target =
+ RedlineTarget.fromTriple(triple)
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ "Unknown target triple: " + triple));
+ var nativeFile =
+ resourceDir.resolve(baseName + "." + target.resourceSuffix() + ".native");
+
+ try (var out = new FileOutputStream(nativeFile.toFile())) {
+ NativeCodeSerializer.serialize(compiledCode, out);
+ }
+ }
+ }
+
+ public void extendGeneratedSources() throws IOException {
+ if (!config.hasRedlineTargets()) {
+ return;
+ }
+ var packagePath = config.getPackageName().replace('.', '/');
+ var baseName = config.getBaseName();
+ var sourceFile =
+ config.targetSourceFolder().resolve(packagePath).resolve(baseName + ".java");
+
+ var cu = StaticJavaParser.parse(sourceFile);
+ var type = cu.getClassByName(baseName).orElseThrow();
+
+ cu.addImport("run.endive.redline.experimental.api.NativeCodeSerializer");
+ cu.addImport("run.endive.redline.experimental.api.NativeMachineFactoryProvider");
+ cu.addImport("run.endive.redline.experimental.api.internal.RedlineTarget");
+ cu.addImport("java.io.InputStream");
+ cu.addImport("java.io.IOException");
+ cu.addImport("java.io.UncheckedIOException");
+ cu.addImport("run.endive.runtime.Instance");
+
+ generateNativeCodeHolderInnerClass(type, baseName);
+ generateLoadNativeCodeMethod(type);
+ generateBuilderMethod(type, baseName);
+ generateSafeBuilderMethod(type, baseName);
+
+ Files.writeString(sourceFile, cu.toString());
+ }
+
+ private static void generateNativeCodeHolderInnerClass(
+ ClassOrInterfaceDeclaration type, String moduleName) {
+ var holderClass =
+ new ClassOrInterfaceDeclaration(
+ NodeList.nodeList(
+ new Modifier(Modifier.Keyword.PRIVATE),
+ new Modifier(Modifier.Keyword.STATIC)),
+ false,
+ "NativeCodeHolder");
+ type.addMember(holderClass);
+
+ holderClass.addField(
+ parseType("byte[][]"), "CODE", Modifier.Keyword.STATIC, Modifier.Keyword.FINAL);
+
+ var initBody = holderClass.addStaticInitializer();
+
+ initBody.addStatement(
+ StaticJavaParser.parseStatement(
+ "var host = RedlineTarget.detectHost().orElse(null);"));
+
+ initBody.addStatement(
+ StaticJavaParser.parseStatement(
+ "if (host == null) {\n"
+ + " CODE = null;\n"
+ + "} else {\n"
+ + " String resource = \""
+ + moduleName
+ + ".\" + host.resourceSuffix() + \".native\";\n"
+ + " try (InputStream in = "
+ + moduleName
+ + ".class.getResourceAsStream(resource)) {\n"
+ + " CODE = (in == null) ? null"
+ + " : NativeCodeSerializer.deserialize(in);\n"
+ + " } catch (IOException e) {\n"
+ + " throw new UncheckedIOException("
+ + "\"Failed to load native code\", e);\n"
+ + " }\n"
+ + "}"));
+ }
+
+ private static void generateLoadNativeCodeMethod(ClassOrInterfaceDeclaration type) {
+ var method =
+ type.addMethod("loadNativeCode", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC)
+ .setType(parseType("byte[][]"));
+ method.createBody()
+ .addStatement(
+ new ReturnStmt(
+ new FieldAccessExpr(new NameExpr("NativeCodeHolder"), "CODE")));
+ }
+
+ private static void generateBuilderMethod(ClassOrInterfaceDeclaration type, String moduleName) {
+ var method =
+ type.addMethod("builder", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC)
+ .setType(parseClassOrInterfaceType("Instance.Builder"));
+
+ var body = method.createBody();
+ body.addStatement(StaticJavaParser.parseStatement("var module = load();"));
+ body.addStatement(
+ StaticJavaParser.parseStatement("byte[][] nativeCode = loadNativeCode();"));
+ body.addStatement(
+ StaticJavaParser.parseStatement(
+ "if (nativeCode != null) {\n"
+ + " var provider = NativeMachineFactoryProvider.discover();\n"
+ + " if (provider.isPresent()) {\n"
+ + " return provider.get().builder(module, nativeCode);\n"
+ + " }\n"
+ + "}"));
+ body.addStatement(
+ StaticJavaParser.parseStatement(
+ "return Instance.builder(module).withMachineFactory("
+ + moduleName
+ + "::create);"));
+ }
+
+ private static void generateSafeBuilderMethod(
+ ClassOrInterfaceDeclaration type, String moduleName) {
+ var method =
+ type.addMethod("safeBuilder", Modifier.Keyword.PUBLIC, Modifier.Keyword.STATIC)
+ .setType(parseClassOrInterfaceType("Instance.Builder"));
+
+ method.createBody()
+ .addStatement(
+ StaticJavaParser.parseStatement(
+ "return Instance.builder(load()).withMachineFactory("
+ + moduleName
+ + "::create);"));
+ }
+}
diff --git a/redline/it/pom.xml b/redline/it/pom.xml
new file mode 100644
index 000000000..91c36dc9d
--- /dev/null
+++ b/redline/it/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+
+ run.endive
+ redline-parent-experimental
+ 999-SNAPSHOT
+ ../pom.xml
+
+ redline-it-experimental
+ jar
+ Endive - Redline IT
+ Integration tests for the Redline native compiler
+
+
+ true
+
+
+
+
+ run.endive
+ endive-compiler-maven-plugin
+
+
+ run.endive
+ redline-runner-jffi-experimental
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ run.endive:endive-compiler-maven-plugin
+ run.endive:redline-runner-jffi-experimental
+
+
+
+
+ org.apache.maven.plugins
+ maven-invoker-plugin
+
+ ${project.build.directory}/it
+ true
+ src/it/settings.xml
+ verify
+ true
+ ${skipTests}
+ true
+ invoker.properties
+
+
+
+ integration-tests
+
+ install
+ run
+
+
+
+
+
+
+
diff --git a/redline/it/src/it/redline-e2e/invoker.properties b/redline/it/src/it/redline-e2e/invoker.properties
new file mode 100644
index 000000000..84099bc5a
--- /dev/null
+++ b/redline/it/src/it/redline-e2e/invoker.properties
@@ -0,0 +1 @@
+invoker.goals=test
diff --git a/redline/it/src/it/redline-e2e/pom.xml b/redline/it/src/it/redline-e2e/pom.xml
new file mode 100644
index 000000000..123640cb7
--- /dev/null
+++ b/redline/it/src/it/redline-e2e/pom.xml
@@ -0,0 +1,80 @@
+
+
+
+ 4.0.0
+ run.endive
+ redline-e2e-it
+ 0.0-SNAPSHOT
+ jar
+
+
+
+ run.endive
+ redline-api-experimental
+ @project.version@
+
+
+ run.endive
+ redline-runner-jffi-experimental
+ @project.version@
+
+
+ run.endive
+ runtime
+ @project.version@
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ @junit.version@
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ @junit.version@
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ @maven-compiler-plugin.version@
+
+ 11
+
+
+
+ run.endive
+ endive-compiler-maven-plugin
+ @project.version@
+
+
+ run.endive
+ redline-build-time-compiler-experimental
+ @project.version@
+
+
+
+
+ compile-i32
+
+ compile
+
+
+ endive.test.AddModule
+ src/test/resources/add.wat.wasm
+ WARN
+ true
+
+
+
+
+
+
+
+
diff --git a/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java
new file mode 100644
index 000000000..0233cafb1
--- /dev/null
+++ b/redline/it/src/it/redline-e2e/src/test/java/endive/test/RedlineE2eTest.java
@@ -0,0 +1,69 @@
+package endive.test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import run.endive.redline.experimental.api.NativeMachineFactoryProvider;
+
+class RedlineE2eTest {
+
+ @Test
+ public void nativeBuilderProducesCorrectResults() {
+ try (var instance = AddModule.builder().build()) {
+ var add = instance.export("add");
+ assertArrayEquals(new long[] {3}, add.apply(1, 2));
+ assertArrayEquals(new long[] {0}, add.apply(0, 0));
+ assertEquals(
+ (int) add.apply(0, -1)[0],
+ -1,
+ "i32 add(0, -1) should be -1 when narrowed to int");
+ }
+ }
+
+ @Test
+ public void safeBuilderProducesCorrectResults() {
+ try (var instance = AddModule.safeBuilder().build()) {
+ var add = instance.export("add");
+ assertArrayEquals(new long[] {3}, add.apply(1, 2));
+ assertArrayEquals(new long[] {0}, add.apply(0, 0));
+ assertEquals(
+ (int) add.apply(0, -1)[0],
+ -1,
+ "i32 add(0, -1) should be -1 when narrowed to int");
+ }
+ }
+
+ @Test
+ public void nativeCodeIsAvailable() {
+ assertNotNull(
+ AddModule.loadNativeCode(), "Native code should be available on this platform");
+ }
+
+ @Test
+ public void spiDiscoversProvider() {
+ var provider = NativeMachineFactoryProvider.discover();
+ assertTrue(provider.isPresent(), "Should discover a native provider");
+ assertTrue(provider.get().priority() > 0, "Provider should have positive priority");
+ }
+
+ @Test
+ public void bothBuildersProduceSameResults() {
+ try (var nativeInstance = AddModule.builder().build();
+ var safeInstance = AddModule.safeBuilder().build()) {
+ var nativeAdd = nativeInstance.export("add");
+ var safeAdd = safeInstance.export("add");
+
+ for (int a = -10; a <= 10; a++) {
+ for (int b = -10; b <= 10; b++) {
+ assertEquals(
+ (int) safeAdd.apply(a, b)[0],
+ (int) nativeAdd.apply(a, b)[0],
+ "add(" + a + ", " + b + ") should match");
+ }
+ }
+ }
+ }
+}
diff --git a/redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm b/redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm
new file mode 100644
index 000000000..ad1f2f7a3
Binary files /dev/null and b/redline/it/src/it/redline-e2e/src/test/resources/add.wat.wasm differ
diff --git a/redline/it/src/it/settings.xml b/redline/it/src/it/settings.xml
new file mode 100644
index 000000000..2d90068bb
--- /dev/null
+++ b/redline/it/src/it/settings.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ it-repo
+
+ true
+
+
+
+ local.central
+ @localRepositoryUrl@
+
+ true
+
+
+ true
+
+
+
+
+
+ local.central
+ @localRepositoryUrl@
+
+ true
+
+
+ true
+
+
+
+
+
+
diff --git a/redline/pom.xml b/redline/pom.xml
index 13f29061f..2c617995f 100644
--- a/redline/pom.xml
+++ b/redline/pom.xml
@@ -17,7 +17,9 @@
api
bridge
+ build-time-compiler
compiler
+ it
runner-jffi
runner-jffi-tests