From a65018f72cd1bf1c6ef36b67901c2c3fb06b3a91 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 7 Mar 2026 19:48:57 +0000 Subject: [PATCH] implements PR-O4.7 --- .../BackendGateIIntegrationTest.java | 5 +- .../integration/CompatibilityError.java | 1 + .../integration/CompatibilityResult.java | 7 +- .../LocalRuntimeCompatibilityAdapter.java | 14 +- .../RuntimeBackedCompatibilityAdapter.java | 137 ++++++++++++++++++ ...RuntimeBackedCompatibilityAdapterTest.java | 43 ++++++ .../RuntimeCompatibilityAdapters.java | 10 ++ 7 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapter.java create mode 100644 prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapterTest.java create mode 100644 prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeCompatibilityAdapters.java diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java index 9a06220b..1e7325a0 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java @@ -31,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; class BackendGateIIntegrationTest { - private final RuntimeCompatibilityAdapter compatibilityAdapter = new LocalRuntimeCompatibilityAdapter(); + private final RuntimeCompatibilityAdapter compatibilityAdapter = RuntimeCompatibilityAdapters.createDefault(); @Test void gateI_syscSectionPresentAndEmpty() { @@ -52,8 +52,9 @@ class BackendGateIIntegrationTest { final var check = compatibilityAdapter.check(module); assertEquals(CompatibilityError.NONE, check.error()); assertTrue(check.hostcallCount() > 0); - assertEquals("local", check.adapterMode()); + assertTrue(Set.of("runtime-backed", "local-fallback", "local").contains(check.adapterMode())); assertTrue(!check.runtimeLine().isBlank()); + assertTrue(!check.reason().isBlank()); } @Test diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityError.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityError.java index 2d1fce23..7b3944c9 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityError.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityError.java @@ -6,4 +6,5 @@ enum CompatibilityError { HOSTCALL_INDEX_OUT_OF_BOUNDS, UNUSED_SYSC_DECL, MISSING_CAPABILITY, + RUNTIME_REJECTED, } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityResult.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityResult.java index 6406f7a0..62d30fdf 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityResult.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/CompatibilityResult.java @@ -4,5 +4,10 @@ record CompatibilityResult( CompatibilityError error, int hostcallCount, String runtimeLine, - String adapterMode) { + String adapterMode, + String reason) { + + CompatibilityResult { + reason = reason == null ? "" : reason; + } } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/LocalRuntimeCompatibilityAdapter.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/LocalRuntimeCompatibilityAdapter.java index 2d7d52e3..d5cf4e5c 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/LocalRuntimeCompatibilityAdapter.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/LocalRuntimeCompatibilityAdapter.java @@ -48,7 +48,8 @@ final class LocalRuntimeCompatibilityAdapter implements RuntimeCompatibilityAdap CompatibilityError.RAW_SYSCALL_IN_PRELOAD, hostcallCount, runtimeLine, - "local"); + "local", + "raw syscall opcode is forbidden"); } case 0x71 -> { final var syscIndex = readU32(module.code(), pc + 2); @@ -57,7 +58,8 @@ final class LocalRuntimeCompatibilityAdapter implements RuntimeCompatibilityAdap CompatibilityError.HOSTCALL_INDEX_OUT_OF_BOUNDS, hostcallCount, runtimeLine, - "local"); + "local", + "hostcall index out of bounds"); } used[syscIndex] = true; hostcallCount++; @@ -69,7 +71,8 @@ final class LocalRuntimeCompatibilityAdapter implements RuntimeCompatibilityAdap CompatibilityError.MISSING_CAPABILITY, hostcallCount, runtimeLine, - "local"); + "local", + "required capability is missing"); } pc += 6; } @@ -85,10 +88,11 @@ final class LocalRuntimeCompatibilityAdapter implements RuntimeCompatibilityAdap CompatibilityError.UNUSED_SYSC_DECL, hostcallCount, runtimeLine, - "local"); + "local", + "unused syscall declaration"); } } - return new CompatibilityResult(CompatibilityError.NONE, hostcallCount, runtimeLine, "local"); + return new CompatibilityResult(CompatibilityError.NONE, hostcallCount, runtimeLine, "local", "ok"); } private int readU16(final byte[] code, final int offset) { diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapter.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapter.java new file mode 100644 index 00000000..9d9fcd9e --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapter.java @@ -0,0 +1,137 @@ +package p.studio.compiler.integration; + +import p.studio.compiler.backend.bytecode.BytecodeModule; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +final class RuntimeBackedCompatibilityAdapter implements RuntimeCompatibilityAdapter { + private static final String DEFAULT_RUNTIME_LINE = "runtime-line-unknown"; + private static final String FALLBACK_MODE = "local-fallback"; + + private final RuntimeCompatibilityAdapter fallback; + private final Path runtimeRoot; + private final String runtimeLine; + private final String runtimeCheckCommand; + + RuntimeBackedCompatibilityAdapter(final RuntimeCompatibilityAdapter fallback) { + this( + fallback == null ? new LocalRuntimeCompatibilityAdapter() : fallback, + Path.of("../runtime"), + System.getenv("PROMETEU_RUNTIME_CHECK_CMD")); + } + + RuntimeBackedCompatibilityAdapter( + final RuntimeCompatibilityAdapter fallback, + final Path runtimeRoot, + final String runtimeCheckCommand) { + this.fallback = fallback == null ? new LocalRuntimeCompatibilityAdapter() : fallback; + this.runtimeRoot = runtimeRoot == null ? Path.of("../runtime") : runtimeRoot; + this.runtimeLine = readRuntimeLine(this.runtimeRoot); + this.runtimeCheckCommand = runtimeCheckCommand == null ? "" : runtimeCheckCommand.trim(); + } + + @Override + public CompatibilityResult check(final BytecodeModule module) { + final var local = fallback.check(module); + if (runtimeCheckCommand.isBlank()) { + return fallbackWithReason(local, "runtime command not configured"); + } + if (!Files.isDirectory(runtimeRoot)) { + return fallbackWithReason(local, "runtime root is unavailable: " + runtimeRoot); + } + + Path artifact = null; + try { + artifact = Files.createTempFile("gate-i-runtime-", ".pbx"); + Files.write(artifact, module.serialize()); + final var command = renderCommand(runtimeCheckCommand, artifact); + final var process = new ProcessBuilder(List.of("sh", "-lc", command)) + .directory(runtimeRoot.toFile()) + .redirectErrorStream(true) + .start(); + final var output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); + final var exitCode = process.waitFor(); + if (exitCode != 0) { + return new CompatibilityResult( + CompatibilityError.RUNTIME_REJECTED, + local.hostcallCount(), + runtimeLine, + "runtime-backed", + "runtime command rejected artifact (exit=%d): %s".formatted(exitCode, shrink(output))); + } + return new CompatibilityResult( + local.error(), + local.hostcallCount(), + runtimeLine, + "runtime-backed", + "runtime command accepted artifact"); + } catch (IOException | InterruptedException e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + return fallbackWithReason(local, "runtime command failed: " + e.getMessage()); + } finally { + if (artifact != null) { + try { + Files.deleteIfExists(artifact); + } catch (IOException ignored) { + } + } + } + } + + private CompatibilityResult fallbackWithReason( + final CompatibilityResult local, + final String reason) { + return new CompatibilityResult( + local.error(), + local.hostcallCount(), + runtimeLine, + FALLBACK_MODE, + reason); + } + + private String renderCommand( + final String commandTemplate, + final Path artifactPath) { + final var quotedPath = shellQuote(artifactPath.toAbsolutePath().toString()); + if (commandTemplate.contains("{artifact}")) { + return commandTemplate.replace("{artifact}", quotedPath); + } + return commandTemplate + " " + quotedPath; + } + + private String shellQuote(final String value) { + return "'" + value.replace("'", "'\"'\"'") + "'"; + } + + private static String readRuntimeLine(final Path runtimeRoot) { + final var versionFile = runtimeRoot.resolve("VERSION.txt"); + if (!Files.exists(versionFile)) { + return DEFAULT_RUNTIME_LINE; + } + try { + final var content = Files.readString(versionFile).trim(); + if (content.isBlank()) { + return DEFAULT_RUNTIME_LINE; + } + return "runtime-v" + content; + } catch (IOException e) { + return DEFAULT_RUNTIME_LINE; + } + } + + private String shrink(final String output) { + if (output == null || output.isBlank()) { + return ""; + } + if (output.length() <= 160) { + return output; + } + return output.substring(0, 160) + "..."; + } +} diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapterTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapterTest.java new file mode 100644 index 00000000..d304bbdf --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeBackedCompatibilityAdapterTest.java @@ -0,0 +1,43 @@ +package p.studio.compiler.integration; + +import org.junit.jupiter.api.Test; +import p.studio.compiler.backend.bytecode.BytecodeModule; +import p.studio.utilities.structures.ReadOnlyList; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RuntimeBackedCompatibilityAdapterTest { + + @Test + void checkMustFallbackToLocalModeWhenRuntimeCommandIsUnavailable() { + final var adapter = new RuntimeBackedCompatibilityAdapter( + new LocalRuntimeCompatibilityAdapter(), + Path.of("./missing-runtime"), + ""); + final var module = new BytecodeModule( + 0, + ReadOnlyList.empty(), + ReadOnlyList.from(new BytecodeModule.FunctionMeta(0, 2, 0, 0, 0, 1)), + codeRet(), + null, + ReadOnlyList.empty(), + ReadOnlyList.empty()); + + final var result = adapter.check(module); + assertEquals(CompatibilityError.NONE, result.error()); + assertEquals("local-fallback", result.adapterMode()); + assertTrue(!result.reason().isBlank()); + assertTrue(!result.runtimeLine().isBlank()); + } + + private byte[] codeRet() { + final var out = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN); + out.putShort((short) 0x51); + return out.array(); + } +} diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeCompatibilityAdapters.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeCompatibilityAdapters.java new file mode 100644 index 00000000..bb5081cc --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/RuntimeCompatibilityAdapters.java @@ -0,0 +1,10 @@ +package p.studio.compiler.integration; + +final class RuntimeCompatibilityAdapters { + private RuntimeCompatibilityAdapters() { + } + + static RuntimeCompatibilityAdapter createDefault() { + return new RuntimeBackedCompatibilityAdapter(new LocalRuntimeCompatibilityAdapter()); + } +}