From f854b06b351a0cf0b767ef53cc703f36459894bb Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 4 Nov 2026 16:59:05 -0800 Subject: [PATCH 1/5] feat(native): adopt Myna binding ABI 2 and refresh toolchains --- .elide-version | 2 +- .github/workflows/job.build.yml | 2 +- docs/generated-seam.md | 38 +- mise.lock | 46 ++- mise.toml | 2 +- .../elide/bemo/ffm/FfmTransportNative.java | 6 +- .../bemo/transport/FfmTransportNative.java | 18 +- .../elide/bemo/svm/generated/BemoNatives.java | 377 ++++++++++++++++-- rust-toolchain.toml | 2 +- seams/bemo.seam | 182 ++++----- tools/build.py | 17 +- tools/check_java.py | 4 +- tools/seam.py | 4 + tools/test_bitcode.py | 6 +- tools/test_seam.py | 20 + tools/verify_package.py | 11 +- tools/versions.json | 37 +- 17 files changed, 587 insertions(+), 187 deletions(-) diff --git a/.elide-version b/.elide-version index 9c6d629..fdd3be6 100644 --- a/.elide-version +++ b/.elide-version @@ -1 +1 @@ -1.6.1 +1.6.2 diff --git a/.github/workflows/job.build.yml b/.github/workflows/job.build.yml index 916f8a9..081a976 100644 --- a/.github/workflows/job.build.yml +++ b/.github/workflows/job.build.yml @@ -26,7 +26,7 @@ jobs: run: python3 -c 'import json; print("version=" + json.load(open("tools/versions.json"))["llvm"]["toolchain_version"])' >> "$GITHUB_OUTPUT" - name: Install Elide native toolchain id: native-toolchain - uses: elide-dev/toolchain/action@171eb7cde379e17822e042cfbda26c9740c10b03 + uses: elide-dev/toolchain/action@4677dd77b520b609bb0e548e6e7a4dadadbb2d14 with: version: ${{ steps.native-version.outputs.version }} target: ${{ runner.os == 'Linux' && 'x86_64-unknown-linux-gnu' || 'arm64-apple-darwin' }} diff --git a/docs/generated-seam.md b/docs/generated-seam.md index b131e69..00c6c0b 100644 --- a/docs/generated-seam.md +++ b/docs/generated-seam.md @@ -20,10 +20,10 @@ make test make test-native-image ``` -The build pins Myna 0.6.0 and Elide 1.6.1 in `tools/versions.json`. +The build pins Myna 0.8.0 and Elide 1.6.2 in `tools/versions.json`. `make test-native-image` provisions the checksummed GraalVM -`25.5.5-elide.21` distribution matching the SDK dependencies. The SDK artifacts -come from `https://maven.elide.dev/gvm-elide/25.5.5-elide.21`; Native Image +`25.5.5-elide.24` distribution matching the SDK dependencies. The SDK artifacts +come from `https://maven.elide.dev/gvm-elide/25.5.5-elide.24`; Native Image consumers must configure this Maven repository. API and FFM artifacts remain independent of GraalVM. It compiles only the dependency-free generator CLI’s tracked sources through @@ -45,6 +45,34 @@ supported seam targets have the same scalar widths and record layouts. Native classifiers carry the actual target's `seam.json`, `seam.abi`, and `seam.ll`. This keeps common Java JARs identical across platform builds. +## Binding ABI 2 call audit + +Myna's binding ABI is 2; Bemo's public transport protocol stays at ABI 3. +Every import declares its call policy explicitly, so new imports cannot silently +inherit Myna's leaf default. Twelve imports use generated typed function pointers +and relocated symbols: + +- ABI versions and capabilities return constants; allocation accounting reads one atomic. +- Last-error access reads and clears a constant-initialized thread-local cell. +- HTTP method, version, header count, keep-alive, and view read a live exchange + lease without registry locks or allocation. Spans copies at most 404 integers, + bounded by the HTTP/1 and HTTP/2 parsers' 100-header limit. +- Helper-token capture reads thread-local state and retains one atomic reference. + +Helper-token release uses a normal transition because its retirement CAS loop can +retry under contention. Allocation, cleanup, registry operations, I/O, TLS, +callbacks, and other unreviewed operations also retain normal transitions. No new +LLVM memory-effect promises are made. FFM uses critical downcalls without heap +access for the corresponding bounded metadata operations; its existing TLS heap +adapters retain their separate contracts. + +The Native Image module compiles against three distribution jars (`svm-shared`, +`svm-guest-staging`, and `svm-guest`) without packaging them or adding them to the +application runtime classpath. Generated `META-INF/native-image` metadata travels +with the classes and JAR; it exports the compiler API and separates build-time +symbol metadata from runtime pointer-holder initialization. Native Image consumers +must use the matching Elide Graal distribution. API and FFM need none of these jars. + ## Bitcode archive variant ```sh @@ -76,8 +104,8 @@ Use compatible LLVM tools for building, application, and final linkage. on `PATH` (for example, Homebrew's separately installed LLD). The build requires matching LLVM majors between Rust and the native toolchain, and exact pinned versions for Clang, LLD, and LLVM helper libraries. Package CI -uses the commit-pinned `elide-dev/toolchain/action` with release `2026.10.0` -(LLVM 23.1.2). The action verifies bundle checksums and supplies target compiler, +uses the commit-pinned `elide-dev/toolchain/action` with release `2026.11.1` +(LLVM 23.1.4). The action verifies bundle checksums and supplies target compiler, linker, and SDK settings. Its native library archives also carry ThinLTO bitcode. `tools/setup_llvm.py` can provision the same release locally using the SHA-256 digests in `tools/versions.json`. Activate the bundle’s target diff --git a/mise.lock b/mise.lock index 828aa28..dfa1e8e 100644 --- a/mise.lock +++ b/mise.lock @@ -43,39 +43,45 @@ url = "https://github.com/EmbarkStudios/cargo-deny/releases/download/0.20.2/carg url_api = "https://api.github.com/repos/EmbarkStudios/cargo-deny/releases/assets/471599057" [[tools."github:elide-dev/elide"]] -version = "1.6.1" +version = "1.6.2" backend = "github:elide-dev/elide" -specifiers = ["1.6.1"] +specifiers = ["1.6.2"] [tools."github:elide-dev/elide"."platforms.linux-arm64"] -checksum = "sha256:257caaffad4b784926dd30b093f6fa94b54d71fa27077d95f1db7ec310f8cc1f" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.linux-arm64.txz" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686387161" +checksum = "sha256:4f973920fffca735407420f2c5ec0523cedb76ba369f3a20660ddbe2fd224a17" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.linux-arm64.txz" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698677467" [tools."github:elide-dev/elide"."platforms.linux-arm64-musl"] -checksum = "sha256:257caaffad4b784926dd30b093f6fa94b54d71fa27077d95f1db7ec310f8cc1f" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.linux-arm64.txz" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686387161" +checksum = "sha256:4f973920fffca735407420f2c5ec0523cedb76ba369f3a20660ddbe2fd224a17" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.linux-arm64.txz" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698677467" [tools."github:elide-dev/elide"."platforms.linux-x64"] -checksum = "sha256:754895c49d13e42e6961b3399ed732acd70e9a116b7c165812f6246a11246635" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.linux-amd64.txz" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686386845" +checksum = "sha256:736cb9a7573a122a50a670c3d7d1542c0b8d0016db5a0512e6e3c9fc65871c6b" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.linux-amd64.txz" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698677151" [tools."github:elide-dev/elide"."platforms.linux-x64-musl"] -checksum = "sha256:f47dacb7f3365e9f3926a2b0202c384dd0c32d5e0749cac2617572fed20e66fe" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.linux-amd64-musl.txz" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686384887" +checksum = "sha256:5fb4a4e5b1cf38dea918f67464cf5f60b753ab302139dbb2ee115a9fd2134feb" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.linux-amd64-musl.txz" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698676551" [tools."github:elide-dev/elide"."platforms.macos-arm64"] -checksum = "sha256:87a5baa56238f68744e541bd510261a919d561b2fc20d066264bfeb428083c23" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.macos-arm64.txz" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686388993" +checksum = "sha256:f88344b9ba6acef216f095e0355886bffee96c11e801397ec27d37ae8e1b81ac" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.macos-arm64.txz" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698678364" [tools."github:elide-dev/elide"."platforms.windows-x64"] -checksum = "sha256:33f136d539e51b5771326223c739249fba7cb99fe52debd259fa6296e3ed9349" -url = "https://github.com/elide-dev/elide/releases/download/v1.6.1/elide.windows-amd64.zip" -url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/686393494" +checksum = "sha256:1fa37d5315e7c123df49ef820af8238652ac97b73ad3d9fab74bd90fad27e772" +checksum_source = "mise-versions" +url = "https://github.com/elide-dev/elide/releases/download/v1.6.2/elide.windows-amd64.zip" +url_api = "https://api.github.com/repos/elide-dev/elide/releases/assets/698680021" [[tools."github:nextest-rs/nextest"]] version = "0.9.135" diff --git a/mise.toml b/mise.toml index 9741594..6ad124e 100644 --- a/mise.toml +++ b/mise.toml @@ -1,7 +1,7 @@ min_version = "2026.8.3" [tools] -"github:elide-dev/elide" = "1.6.1" +"github:elide-dev/elide" = "1.6.2" java = "openjdk-25.0.2" "aqua:EmbarkStudios/cargo-deny" = "0.20.2" hk = "1.55.0" diff --git a/packages/ffm/src/main/java/dev/elide/bemo/ffm/FfmTransportNative.java b/packages/ffm/src/main/java/dev/elide/bemo/ffm/FfmTransportNative.java index ea1e011..8382c41 100644 --- a/packages/ffm/src/main/java/dev/elide/bemo/ffm/FfmTransportNative.java +++ b/packages/ffm/src/main/java/dev/elide/bemo/ffm/FfmTransportNative.java @@ -38,12 +38,14 @@ public FfmTransportNative(Path library) { version = linker.downcallHandle( symbols.find("bemo_abi_version").orElseThrow(), - FunctionDescriptor.of(ValueLayout.JAVA_INT)); + FunctionDescriptor.of(ValueLayout.JAVA_INT), + Linker.Option.critical(false)); requireCompatible(); capabilities = linker.downcallHandle( symbols.find("bemo_capabilities").orElseThrow(), - FunctionDescriptor.of(ValueLayout.JAVA_LONG)); + FunctionDescriptor.of(ValueLayout.JAVA_LONG), + Linker.Option.critical(false)); } catch (Throwable failure) { arena.close(); throw failure; diff --git a/packages/ffm/src/main/java/dev/elide/bemo/transport/FfmTransportNative.java b/packages/ffm/src/main/java/dev/elide/bemo/transport/FfmTransportNative.java index 0682cad..aba4fb0 100644 --- a/packages/ffm/src/main/java/dev/elide/bemo/transport/FfmTransportNative.java +++ b/packages/ffm/src/main/java/dev/elide/bemo/transport/FfmTransportNative.java @@ -833,10 +833,26 @@ public FfmTransportNative(Path library) { private static MethodHandle bind( SymbolLookup symbols, String name, ValueLayout result, ValueLayout... args) { + // Match the bounded, nonblocking imports audited in seams/bemo.seam. + boolean leaf = + switch (name) { + case "abi_version", + "last_error", + "serving_helper_prepare", + "http_method", + "http_version", + "http_header_count", + "http_keep_alive", + "http_view", + "http_spans" -> + true; + default -> false; + }; return Linker.nativeLinker() .downcallHandle( symbols.find("elide_transport_" + name).orElseThrow(), - FunctionDescriptor.of(result, args)); + FunctionDescriptor.of(result, args), + leaf ? new Linker.Option[] {Linker.Option.critical(false)} : new Linker.Option[0]); } @Override diff --git a/packages/native-image/src/main/java/dev/elide/bemo/svm/generated/BemoNatives.java b/packages/native-image/src/main/java/dev/elide/bemo/svm/generated/BemoNatives.java index 82ed6e3..9c45ab6 100644 --- a/packages/native-image/src/main/java/dev/elide/bemo/svm/generated/BemoNatives.java +++ b/packages/native-image/src/main/java/dev/elide/bemo/svm/generated/BemoNatives.java @@ -1,9 +1,15 @@ // Generated by myna. Unsigned integer carriers preserve their raw bits. package dev.elide.bemo.svm.generated; +import com.oracle.svm.guest.staging.c.CGlobalData; +import com.oracle.svm.guest.staging.c.CGlobalDataFactory; +import com.oracle.svm.shared.AlwaysInline; +import com.oracle.svm.shared.Uninterruptible; import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.function.CLibrary; +import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CLongPointer; @@ -17,7 +23,215 @@ public final class BemoNatives { private BemoNatives() {} public static final String ABI_FINGERPRINT = - "7457763aef4845f54949461dca4ca610d01e7dab6231bc9fe16bafe071a5879e"; + "7c0888c3332b5a3e3d74731fe7e3199d47f8f8fca9d0c96d79d71433d8827c39"; + public static final int BINDING_ABI_VERSION = 2; + + public interface bemo_abi_version_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(); + } + + public interface bemo_alloc_live_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + long invoke(); + } + + public interface bemo_capabilities_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + long invoke(); + } + + public interface elide_transport_abi_version_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(); + } + + public interface elide_transport_http_header_count_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange); + } + + public interface elide_transport_http_keep_alive_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange); + } + + public interface elide_transport_http_method_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange); + } + + public interface elide_transport_http_spans_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange, CIntPointer output, int capacity); + } + + public interface elide_transport_http_version_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange); + } + + public interface elide_transport_http_view_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(long exchange, int kind, int index, CLongPointer output); + } + + public interface elide_transport_last_error_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + int invoke(); + } + + public interface elide_transport_serving_helper_prepare_Leaf extends CFunctionPointer { + @AlwaysInline("Myna binding ABI 2 native leaf") + @Uninterruptible( + reason = "Bounded primitive/word native leaf; no Java reentry", + mayBeInlined = true) + @InvokeCFunctionPointer(transition = CFunction.Transition.NO_TRANSITION) + long invoke(); + } + + /** Linker metadata only: initialized at image build time. */ + private static final class MynaSymbols { + static final CGlobalData bemo_abi_version = + CGlobalDataFactory.forSymbol("bemo_abi_version"); + static final CGlobalData bemo_alloc_live = + CGlobalDataFactory.forSymbol("bemo_alloc_live"); + static final CGlobalData bemo_capabilities = + CGlobalDataFactory.forSymbol("bemo_capabilities"); + static final CGlobalData elide_transport_abi_version = + CGlobalDataFactory.forSymbol("elide_transport_abi_version"); + static final CGlobalData + elide_transport_http_header_count = + CGlobalDataFactory.forSymbol("elide_transport_http_header_count"); + static final CGlobalData elide_transport_http_keep_alive = + CGlobalDataFactory.forSymbol("elide_transport_http_keep_alive"); + static final CGlobalData elide_transport_http_method = + CGlobalDataFactory.forSymbol("elide_transport_http_method"); + static final CGlobalData elide_transport_http_spans = + CGlobalDataFactory.forSymbol("elide_transport_http_spans"); + static final CGlobalData elide_transport_http_version = + CGlobalDataFactory.forSymbol("elide_transport_http_version"); + static final CGlobalData elide_transport_http_view = + CGlobalDataFactory.forSymbol("elide_transport_http_view"); + static final CGlobalData elide_transport_last_error = + CGlobalDataFactory.forSymbol("elide_transport_last_error"); + static final CGlobalData + elide_transport_serving_helper_prepare = + CGlobalDataFactory.forSymbol("elide_transport_serving_helper_prepare"); + } + + /** Runtime address: never initialized in the build host. */ + private static final class bemo_abi_version_Binding { + static final bemo_abi_version_Leaf ADDRESS = MynaSymbols.bemo_abi_version.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class bemo_alloc_live_Binding { + static final bemo_alloc_live_Leaf ADDRESS = MynaSymbols.bemo_alloc_live.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class bemo_capabilities_Binding { + static final bemo_capabilities_Leaf ADDRESS = MynaSymbols.bemo_capabilities.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_abi_version_Binding { + static final elide_transport_abi_version_Leaf ADDRESS = + MynaSymbols.elide_transport_abi_version.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_header_count_Binding { + static final elide_transport_http_header_count_Leaf ADDRESS = + MynaSymbols.elide_transport_http_header_count.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_keep_alive_Binding { + static final elide_transport_http_keep_alive_Leaf ADDRESS = + MynaSymbols.elide_transport_http_keep_alive.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_method_Binding { + static final elide_transport_http_method_Leaf ADDRESS = + MynaSymbols.elide_transport_http_method.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_spans_Binding { + static final elide_transport_http_spans_Leaf ADDRESS = + MynaSymbols.elide_transport_http_spans.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_version_Binding { + static final elide_transport_http_version_Leaf ADDRESS = + MynaSymbols.elide_transport_http_version.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_http_view_Binding { + static final elide_transport_http_view_Leaf ADDRESS = + MynaSymbols.elide_transport_http_view.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_last_error_Binding { + static final elide_transport_last_error_Leaf ADDRESS = + MynaSymbols.elide_transport_last_error.get(); + } + + /** Runtime address: never initialized in the build host. */ + private static final class elide_transport_serving_helper_prepare_Binding { + static final elide_transport_serving_helper_prepare_Leaf ADDRESS = + MynaSymbols.elide_transport_serving_helper_prepare.get(); + } public static final class elide_transport_buffer_view_t { private elide_transport_buffer_view_t() {} @@ -97,23 +311,55 @@ public static void result(PointerBase record, long value) { } } - @CFunction(value = "bemo_abi_version") - public static native int bemo_abi_version(); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static bemo_abi_version_Leaf bemo_abi_versionLeafPointer() { + return bemo_abi_version_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int bemo_abi_version() { + return MynaSymbols.bemo_abi_version.get().invoke(); + } @CFunction(value = "bemo_alloc") public static native VoidPointer bemo_alloc(long size); - @CFunction(value = "bemo_alloc_live") - public static native long bemo_alloc_live(); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static bemo_alloc_live_Leaf bemo_alloc_liveLeafPointer() { + return bemo_alloc_live_Binding.ADDRESS; + } - @CFunction(value = "bemo_capabilities") - public static native long bemo_capabilities(); + @AlwaysInline("Myna native leaf call") + public static long bemo_alloc_live() { + return MynaSymbols.bemo_alloc_live.get().invoke(); + } + + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static bemo_capabilities_Leaf bemo_capabilitiesLeafPointer() { + return bemo_capabilities_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static long bemo_capabilities() { + return MynaSymbols.bemo_capabilities.get().invoke(); + } @CFunction(value = "bemo_free") public static native void bemo_free(VoidPointer pointer, long size); - @CFunction(value = "elide_transport_abi_version") - public static native int elide_transport_abi_version(); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_abi_version_Leaf elide_transport_abi_versionLeafPointer() { + return elide_transport_abi_version_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int elide_transport_abi_version() { + return MynaSymbols.elide_transport_abi_version.get().invoke(); + } @CFunction(value = "elide_transport_buffer_freeze") public static native int elide_transport_buffer_freeze(long buffer, long length); @@ -238,18 +484,39 @@ public static native int elide_transport_http_chunk_send( @CFunction(value = "elide_transport_http_head_release") public static native int elide_transport_http_head_release(long head); - @CFunction( - value = "elide_transport_http_header_count", - transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_header_count(long exchange); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_header_count_Leaf + elide_transport_http_header_countLeafPointer() { + return elide_transport_http_header_count_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_header_count(long exchange) { + return MynaSymbols.elide_transport_http_header_count.get().invoke(exchange); + } + + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_keep_alive_Leaf elide_transport_http_keep_aliveLeafPointer() { + return elide_transport_http_keep_alive_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_keep_alive(long exchange) { + return MynaSymbols.elide_transport_http_keep_alive.get().invoke(exchange); + } - @CFunction( - value = "elide_transport_http_keep_alive", - transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_keep_alive(long exchange); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_method_Leaf elide_transport_http_methodLeafPointer() { + return elide_transport_http_method_Binding.ADDRESS; + } - @CFunction(value = "elide_transport_http_method", transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_method(long exchange); + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_method(long exchange) { + return MynaSymbols.elide_transport_http_method.get().invoke(exchange); + } @CFunction(value = "elide_transport_http_prepare") public static native long elide_transport_http_prepare(long exchange, long capacity); @@ -287,21 +554,50 @@ public static native int elide_transport_http_respond( public static native int elide_transport_http_send( long driver, long exchange, long length, int flags); - @CFunction(value = "elide_transport_http_spans", transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_spans( - long exchange, CIntPointer output, int capacity); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_spans_Leaf elide_transport_http_spansLeafPointer() { + return elide_transport_http_spans_Binding.ADDRESS; + } - @CFunction( - value = "elide_transport_http_version", - transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_version(long exchange); + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_spans(long exchange, CIntPointer output, int capacity) { + return MynaSymbols.elide_transport_http_spans.get().invoke(exchange, output, capacity); + } - @CFunction(value = "elide_transport_http_view", transition = CFunction.Transition.NO_TRANSITION) - public static native int elide_transport_http_view( - long exchange, int kind, int index, CLongPointer output); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_version_Leaf elide_transport_http_versionLeafPointer() { + return elide_transport_http_version_Binding.ADDRESS; + } - @CFunction(value = "elide_transport_last_error") - public static native int elide_transport_last_error(); + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_version(long exchange) { + return MynaSymbols.elide_transport_http_version.get().invoke(exchange); + } + + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_http_view_Leaf elide_transport_http_viewLeafPointer() { + return elide_transport_http_view_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int elide_transport_http_view( + long exchange, int kind, int index, CLongPointer output) { + return MynaSymbols.elide_transport_http_view.get().invoke(exchange, kind, index, output); + } + + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_last_error_Leaf elide_transport_last_errorLeafPointer() { + return elide_transport_last_error_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static int elide_transport_last_error() { + return MynaSymbols.elide_transport_last_error.get().invoke(); + } @CFunction(value = "elide_transport_owner_new") public static native long elide_transport_owner_new(long limit); @@ -331,14 +627,19 @@ public static native int elide_transport_http_view( public static native long elide_transport_serving_driver( long workload, long application, int replica, int backend, int limit, int affinity); - @CFunction( - value = "elide_transport_serving_helper_prepare", - transition = CFunction.Transition.NO_TRANSITION) - public static native long elide_transport_serving_helper_prepare(); + /** Resolve before entering an uninterruptible caller; retain the linked library. */ + @AlwaysInline("Myna native leaf address") + public static elide_transport_serving_helper_prepare_Leaf + elide_transport_serving_helper_prepareLeafPointer() { + return elide_transport_serving_helper_prepare_Binding.ADDRESS; + } + + @AlwaysInline("Myna native leaf call") + public static long elide_transport_serving_helper_prepare() { + return MynaSymbols.elide_transport_serving_helper_prepare.get().invoke(); + } - @CFunction( - value = "elide_transport_serving_helper_release", - transition = CFunction.Transition.NO_TRANSITION) + @CFunction(value = "elide_transport_serving_helper_release") public static native int elide_transport_serving_helper_release(long token); @CFunction(value = "elide_transport_serving_helper_start") diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 17d401c..b11a182 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-09-29" +channel = "nightly-2026-10-09" profile = "minimal" components = ["clippy", "rustfmt"] diff --git a/seams/bemo.seam b/seams/bemo.seam index 84ed565..e969e2b 100644 --- a/seams/bemo.seam +++ b/seams/bemo.seam @@ -1,5 +1,5 @@ # Bemo public C ABI inventory. Handles are integers; pointers borrow caller memory. -# Leaf contracts preserve reviewed existing Native Image transition policies. +# Every import explicitly records its audited binding ABI 2 call policy. module bemo abi=3 target=aarch64-apple-darwin java_package=dev.elide.bemo.svm.generated java_class=BemoNatives c_context=dev.elide.bemo.svm.BemoDirectives c_library=bemo_ffi c_library_static=true struct elide_transport_buffer_view_t size=32 align=8 @@ -16,83 +16,83 @@ struct elide_transport_receive_result_t size=32 align=8 field result type=i64 offset=24 end -import bemo_abi_version symbol=bemo_abi_version return=u32 error=no_failure +import bemo_abi_version symbol=bemo_abi_version return=u32 error=no_failure native_leaf=true@explicit_contract end -import bemo_capabilities symbol=bemo_capabilities return=u64 error=no_failure +import bemo_capabilities symbol=bemo_capabilities return=u64 error=no_failure native_leaf=true@explicit_contract end -import bemo_alloc symbol=bemo_alloc return=ptr error=no_failure +import bemo_alloc symbol=bemo_alloc return=ptr error=no_failure native_leaf=false@explicit_contract param size type=u64 end -import bemo_free symbol=bemo_free return=void error=no_failure +import bemo_free symbol=bemo_free return=void error=no_failure native_leaf=false@explicit_contract param pointer type=ptr nullable=true ownership=transferred_to_callee access=read_write param size type=u64 end -import bemo_alloc_live symbol=bemo_alloc_live return=u64 error=no_failure +import bemo_alloc_live symbol=bemo_alloc_live return=u64 error=no_failure native_leaf=true@explicit_contract end -import elide_transport_abi_version symbol=elide_transport_abi_version return=u32 error=no_failure +import elide_transport_abi_version symbol=elide_transport_abi_version return=u32 error=no_failure native_leaf=true@explicit_contract end -import elide_transport_buffer_freeze symbol=elide_transport_buffer_freeze return=i32 error=no_failure +import elide_transport_buffer_freeze symbol=elide_transport_buffer_freeze return=i32 error=no_failure native_leaf=false@explicit_contract param buffer type=u64 param length type=u64 end -import elide_transport_buffer_new symbol=elide_transport_buffer_new return=u64 error=no_failure +import elide_transport_buffer_new symbol=elide_transport_buffer_new return=u64 error=no_failure native_leaf=false@explicit_contract param owner type=u64 param capacity type=u64 end -import elide_transport_buffer_release symbol=elide_transport_buffer_release return=i32 error=no_failure +import elide_transport_buffer_release symbol=elide_transport_buffer_release return=i32 error=no_failure native_leaf=false@explicit_contract param buffer type=u64 end -import elide_transport_buffer_slice symbol=elide_transport_buffer_slice return=u64 error=no_failure +import elide_transport_buffer_slice symbol=elide_transport_buffer_slice return=u64 error=no_failure native_leaf=false@explicit_contract param buffer type=u64 param offset type=u64 param length type=u64 end -import elide_transport_buffer_view symbol=elide_transport_buffer_view return=i32 error=no_failure +import elide_transport_buffer_view symbol=elide_transport_buffer_view return=i32 error=no_failure native_leaf=false@explicit_contract param buffer type=u64 param output type=ptr nullable=false ownership=borrowed access=write end -import elide_transport_driver_backend symbol=elide_transport_driver_backend return=i32 error=no_failure +import elide_transport_driver_backend symbol=elide_transport_driver_backend return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_driver_fallback symbol=elide_transport_driver_fallback return=i32 error=no_failure +import elide_transport_driver_fallback symbol=elide_transport_driver_fallback return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param output type=u64 end -import elide_transport_driver_new symbol=elide_transport_driver_new return=u64 error=no_failure +import elide_transport_driver_new symbol=elide_transport_driver_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param backend type=u32 param limit type=u32 end -import elide_transport_driver_poll symbol=elide_transport_driver_poll return=i32 error=no_failure +import elide_transport_driver_poll symbol=elide_transport_driver_poll return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param timeoutNanos type=u64 param batch type=u64 param maximum type=u32 end -import elide_transport_driver_release symbol=elide_transport_driver_release return=i32 error=no_failure +import elide_transport_driver_release symbol=elide_transport_driver_release return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_driver_wake symbol=elide_transport_driver_wake return=i32 error=no_failure +import elide_transport_driver_wake symbol=elide_transport_driver_wake return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_engine_context_new symbol=elide_transport_engine_context_new return=u64 error=no_failure +import elide_transport_engine_context_new symbol=elide_transport_engine_context_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param flags type=u32 param certificates type=ptr nullable=true ownership=borrowed access=read @@ -103,16 +103,16 @@ import elide_transport_engine_context_new symbol=elide_transport_engine_context_ param alpnLength type=u64 end -import elide_transport_engine_context_release symbol=elide_transport_engine_context_release return=i32 error=no_failure +import elide_transport_engine_context_release symbol=elide_transport_engine_context_release return=i32 error=no_failure native_leaf=false@explicit_contract param context type=u64 end -import elide_transport_engine_control symbol=elide_transport_engine_control return=i64 error=no_failure +import elide_transport_engine_control symbol=elide_transport_engine_control return=i64 error=no_failure native_leaf=false@explicit_contract param engine type=u64 param operation type=u32 end -import elide_transport_engine_info symbol=elide_transport_engine_info return=i32 error=no_failure +import elide_transport_engine_info symbol=elide_transport_engine_info return=i32 error=no_failure native_leaf=false@explicit_contract param engine type=u64 param kind type=u32 param index type=u32 @@ -120,18 +120,18 @@ import elide_transport_engine_info symbol=elide_transport_engine_info return=i32 param capacity type=u64 end -import elide_transport_engine_new symbol=elide_transport_engine_new return=u64 error=no_failure +import elide_transport_engine_new symbol=elide_transport_engine_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param context type=u64 param name type=ptr nullable=true ownership=borrowed access=read param nameLength type=u64 end -import elide_transport_engine_release symbol=elide_transport_engine_release return=i32 error=no_failure +import elide_transport_engine_release symbol=elide_transport_engine_release return=i32 error=no_failure native_leaf=false@explicit_contract param engine type=u64 end -import elide_transport_engine_unwrap symbol=elide_transport_engine_unwrap return=i64 error=no_failure +import elide_transport_engine_unwrap symbol=elide_transport_engine_unwrap return=i64 error=no_failure native_leaf=false@explicit_contract param engine type=u64 param source type=ptr nullable=true ownership=borrowed access=read_write param sourceLength type=u64 @@ -140,7 +140,7 @@ import elide_transport_engine_unwrap symbol=elide_transport_engine_unwrap return param flags type=u32 end -import elide_transport_engine_wrap symbol=elide_transport_engine_wrap return=i64 error=no_failure +import elide_transport_engine_wrap symbol=elide_transport_engine_wrap return=i64 error=no_failure native_leaf=false@explicit_contract param engine type=u64 param source type=ptr nullable=true ownership=borrowed access=read param sourceLength type=u64 @@ -148,13 +148,13 @@ import elide_transport_engine_wrap symbol=elide_transport_engine_wrap return=i64 param destinationLength type=u64 end -import elide_transport_http_chunk_prepare symbol=elide_transport_http_chunk_prepare return=u64 error=no_failure +import elide_transport_http_chunk_prepare symbol=elide_transport_http_chunk_prepare return=u64 error=no_failure native_leaf=false@explicit_contract param exchange type=u64 param capacity type=u64 param address type=ptr nullable=false ownership=borrowed access=write end -import elide_transport_http_chunk_send symbol=elide_transport_http_chunk_send return=i32 error=no_failure +import elide_transport_http_chunk_send symbol=elide_transport_http_chunk_send return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param exchange type=u64 param buffer type=u64 @@ -162,16 +162,16 @@ import elide_transport_http_chunk_send symbol=elide_transport_http_chunk_send re param flags type=u32 end -import elide_transport_http_drain symbol=elide_transport_http_drain return=i32 error=no_failure +import elide_transport_http_drain symbol=elide_transport_http_drain return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_http_free symbol=elide_transport_http_free return=i32 error=no_failure +import elide_transport_http_free symbol=elide_transport_http_free return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param exchange type=u64 end -import elide_transport_http_head_release symbol=elide_transport_http_head_release return=i32 error=no_failure +import elide_transport_http_head_release symbol=elide_transport_http_head_release return=i32 error=no_failure native_leaf=false@explicit_contract param head type=u64 end @@ -187,17 +187,17 @@ import elide_transport_http_method symbol=elide_transport_http_method return=i32 param exchange type=u64 end -import elide_transport_http_prepare symbol=elide_transport_http_prepare return=u64 error=no_failure +import elide_transport_http_prepare symbol=elide_transport_http_prepare return=u64 error=no_failure native_leaf=false@explicit_contract param exchange type=u64 param capacity type=u64 end -import elide_transport_http_release symbol=elide_transport_http_release return=i32 error=no_failure +import elide_transport_http_release symbol=elide_transport_http_release return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param exchange type=u64 end -import elide_transport_http_respond symbol=elide_transport_http_respond return=i32 error=no_failure +import elide_transport_http_respond symbol=elide_transport_http_respond return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param exchange type=u64 param status type=u32 @@ -208,30 +208,30 @@ import elide_transport_http_respond symbol=elide_transport_http_respond return=i param flags type=u32 end -import elide_transport_http_retain symbol=elide_transport_http_retain return=u64 error=no_failure +import elide_transport_http_retain symbol=elide_transport_http_retain return=u64 error=no_failure native_leaf=false@explicit_contract param exchange type=u64 param length type=ptr nullable=false ownership=borrowed access=write end -import elide_transport_http_retire symbol=elide_transport_http_retire return=i32 error=no_failure +import elide_transport_http_retire symbol=elide_transport_http_retire return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_http_segment_ack symbol=elide_transport_http_segment_ack return=i32 error=no_failure +import elide_transport_http_segment_ack symbol=elide_transport_http_segment_ack return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param segment type=u64 end -import elide_transport_http_segment_release symbol=elide_transport_http_segment_release return=i32 error=no_failure +import elide_transport_http_segment_release symbol=elide_transport_http_segment_release return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param segment type=u64 end -import elide_transport_http_segment_release_retired symbol=elide_transport_http_segment_release_retired return=i32 error=no_failure +import elide_transport_http_segment_release_retired symbol=elide_transport_http_segment_release_retired return=i32 error=no_failure native_leaf=false@explicit_contract param segment type=u64 end -import elide_transport_http_send symbol=elide_transport_http_send return=i32 error=no_failure +import elide_transport_http_send symbol=elide_transport_http_send return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param exchange type=u64 param length type=u64 @@ -255,41 +255,41 @@ import elide_transport_http_view symbol=elide_transport_http_view return=i32 err param output type=ptr nullable=false ownership=borrowed access=write end -import elide_transport_last_error symbol=elide_transport_last_error return=i32 error=no_failure +import elide_transport_last_error symbol=elide_transport_last_error return=i32 error=no_failure native_leaf=true@explicit_contract end -import elide_transport_owner_new symbol=elide_transport_owner_new return=u64 error=no_failure +import elide_transport_owner_new symbol=elide_transport_owner_new return=u64 error=no_failure native_leaf=false@explicit_contract param limit type=u64 end -import elide_transport_owner_release symbol=elide_transport_owner_release return=i32 error=no_failure +import elide_transport_owner_release symbol=elide_transport_owner_release return=i32 error=no_failure native_leaf=false@explicit_contract param owner type=u64 end -import elide_transport_owner_used symbol=elide_transport_owner_used return=u64 error=no_failure +import elide_transport_owner_used symbol=elide_transport_owner_used return=u64 error=no_failure native_leaf=false@explicit_contract param owner type=u64 end -import elide_transport_serving_available_cores symbol=elide_transport_serving_available_cores return=i32 error=no_failure +import elide_transport_serving_available_cores symbol=elide_transport_serving_available_cores return=i32 error=no_failure native_leaf=false@explicit_contract end -import elide_transport_serving_close symbol=elide_transport_serving_close return=i32 error=no_failure +import elide_transport_serving_close symbol=elide_transport_serving_close return=i32 error=no_failure native_leaf=false@explicit_contract param application type=u64 end -import elide_transport_serving_context_enter symbol=elide_transport_serving_context_enter return=i32 error=no_failure +import elide_transport_serving_context_enter symbol=elide_transport_serving_context_enter return=i32 error=no_failure native_leaf=false@explicit_contract param application type=u64 param replica type=u32 end -import elide_transport_serving_context_leave symbol=elide_transport_serving_context_leave return=i32 error=no_failure +import elide_transport_serving_context_leave symbol=elide_transport_serving_context_leave return=i32 error=no_failure native_leaf=false@explicit_contract end -import elide_transport_serving_cpu symbol=elide_transport_serving_cpu return=i32 error=no_failure +import elide_transport_serving_cpu symbol=elide_transport_serving_cpu return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_serving_driver symbol=elide_transport_serving_driver return=u64 error=no_failure +import elide_transport_serving_driver symbol=elide_transport_serving_driver return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param application type=u64 param replica type=u32 @@ -301,15 +301,15 @@ end import elide_transport_serving_helper_prepare symbol=elide_transport_serving_helper_prepare return=u64 error=no_failure native_leaf=true@explicit_contract end -import elide_transport_serving_helper_release symbol=elide_transport_serving_helper_release return=i32 error=no_failure native_leaf=true@explicit_contract +import elide_transport_serving_helper_release symbol=elide_transport_serving_helper_release return=i32 error=no_failure native_leaf=false@explicit_contract param token type=u64 end -import elide_transport_serving_helper_start symbol=elide_transport_serving_helper_start return=i32 error=no_failure +import elide_transport_serving_helper_start symbol=elide_transport_serving_helper_start return=i32 error=no_failure native_leaf=false@explicit_contract param token type=u64 end -import elide_transport_serving_listen symbol=elide_transport_serving_listen return=u64 error=no_failure +import elide_transport_serving_listen symbol=elide_transport_serving_listen return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param endpoint type=u64 @@ -317,24 +317,24 @@ import elide_transport_serving_listen symbol=elide_transport_serving_listen retu param backlog type=i32 end -import elide_transport_serving_listener_close symbol=elide_transport_serving_listener_close return=i32 error=no_failure +import elide_transport_serving_listener_close symbol=elide_transport_serving_listener_close return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param listener type=u64 end -import elide_transport_serving_new symbol=elide_transport_serving_new return=u64 error=no_failure +import elide_transport_serving_new symbol=elide_transport_serving_new return=u64 error=no_failure native_leaf=false@explicit_contract param contexts type=u32 end -import elide_transport_serving_ready symbol=elide_transport_serving_ready return=i32 error=no_failure +import elide_transport_serving_ready symbol=elide_transport_serving_ready return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 end -import elide_transport_serving_split_new symbol=elide_transport_serving_split_new return=u64 error=no_failure +import elide_transport_serving_split_new symbol=elide_transport_serving_split_new return=u64 error=no_failure native_leaf=false@explicit_contract param contexts type=u32 end -import elide_transport_serving_worker_driver symbol=elide_transport_serving_worker_driver return=u64 error=no_failure +import elide_transport_serving_worker_driver symbol=elide_transport_serving_worker_driver return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param application type=u64 param context type=u32 @@ -343,46 +343,46 @@ import elide_transport_serving_worker_driver symbol=elide_transport_serving_work param limit type=u32 end -import elide_transport_serving_workers symbol=elide_transport_serving_workers return=i32 error=no_failure +import elide_transport_serving_workers symbol=elide_transport_serving_workers return=i32 error=no_failure native_leaf=false@explicit_contract param application type=u64 param context type=u32 end -import elide_transport_socket_accept symbol=elide_transport_socket_accept return=u64 error=no_failure +import elide_transport_socket_accept symbol=elide_transport_socket_accept return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param listener type=u64 end -import elide_transport_socket_address symbol=elide_transport_socket_address return=i32 error=no_failure +import elide_transport_socket_address symbol=elide_transport_socket_address return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param socket type=u64 param peer type=u32 param output type=u64 end -import elide_transport_socket_adopt symbol=elide_transport_socket_adopt return=i32 error=no_failure +import elide_transport_socket_adopt symbol=elide_transport_socket_adopt return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 end -import elide_transport_socket_close symbol=elide_transport_socket_close return=i32 error=no_failure +import elide_transport_socket_close symbol=elide_transport_socket_close return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param socket type=u64 end -import elide_transport_socket_connect symbol=elide_transport_socket_connect return=u64 error=no_failure +import elide_transport_socket_connect symbol=elide_transport_socket_connect return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param endpoint type=u64 end -import elide_transport_socket_discard symbol=elide_transport_socket_discard return=i32 error=no_failure +import elide_transport_socket_discard symbol=elide_transport_socket_discard return=i32 error=no_failure native_leaf=false@explicit_contract param socket type=u64 end -import elide_transport_socket_http symbol=elide_transport_socket_http return=i32 error=no_failure +import elide_transport_socket_http symbol=elide_transport_socket_http return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -390,7 +390,7 @@ import elide_transport_socket_http symbol=elide_transport_socket_http return=i32 param capacity type=u64 end -import elide_transport_socket_http_tls symbol=elide_transport_socket_http_tls return=i32 error=no_failure +import elide_transport_socket_http_tls symbol=elide_transport_socket_http_tls return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -399,7 +399,7 @@ import elide_transport_socket_http_tls symbol=elide_transport_socket_http_tls re param context type=u64 end -import elide_transport_socket_listen symbol=elide_transport_socket_listen return=u64 error=no_failure +import elide_transport_socket_listen symbol=elide_transport_socket_listen return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param endpoint type=u64 @@ -407,21 +407,21 @@ import elide_transport_socket_listen symbol=elide_transport_socket_listen return param reuse type=u32 end -import elide_transport_socket_option symbol=elide_transport_socket_option return=i32 error=no_failure +import elide_transport_socket_option symbol=elide_transport_socket_option return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param socket type=u64 param option type=u32 param value type=i32 end -import elide_transport_socket_receive symbol=elide_transport_socket_receive return=u64 error=no_failure +import elide_transport_socket_receive symbol=elide_transport_socket_receive return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 param buffer type=u64 end -import elide_transport_socket_receive_new symbol=elide_transport_socket_receive_new return=u64 error=no_failure +import elide_transport_socket_receive_new symbol=elide_transport_socket_receive_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -429,7 +429,7 @@ import elide_transport_socket_receive_new symbol=elide_transport_socket_receive_ param capacity type=u64 end -import elide_transport_socket_receive_new_result symbol=elide_transport_socket_receive_new_result return=i32 error=no_failure +import elide_transport_socket_receive_new_result symbol=elide_transport_socket_receive_new_result return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -438,7 +438,7 @@ import elide_transport_socket_receive_new_result symbol=elide_transport_socket_r param output type=ptr nullable=false ownership=borrowed access=write end -import elide_transport_socket_send symbol=elide_transport_socket_send return=u64 error=no_failure +import elide_transport_socket_send symbol=elide_transport_socket_send return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -447,7 +447,7 @@ import elide_transport_socket_send symbol=elide_transport_socket_send return=u64 param length type=u64 end -import elide_transport_socket_send_inline_vectored symbol=elide_transport_socket_send_inline_vectored return=i64 error=no_failure +import elide_transport_socket_send_inline_vectored symbol=elide_transport_socket_send_inline_vectored return=i64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -455,7 +455,7 @@ import elide_transport_socket_send_inline_vectored symbol=elide_transport_socket param count type=u32 end -import elide_transport_socket_send_gathered symbol=elide_transport_socket_send_gathered return=u64 error=no_failure +import elide_transport_socket_send_gathered symbol=elide_transport_socket_send_gathered return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -463,7 +463,7 @@ import elide_transport_socket_send_gathered symbol=elide_transport_socket_send_g param count type=u32 end -import elide_transport_socket_send_inline symbol=elide_transport_socket_send_inline return=i64 error=no_failure +import elide_transport_socket_send_inline symbol=elide_transport_socket_send_inline return=i64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param socket type=u64 @@ -471,52 +471,52 @@ import elide_transport_socket_send_inline symbol=elide_transport_socket_send_inl param length type=u64 end -import elide_transport_socket_shutdown symbol=elide_transport_socket_shutdown return=i32 error=no_failure +import elide_transport_socket_shutdown symbol=elide_transport_socket_shutdown return=i32 error=no_failure native_leaf=false@explicit_contract param driver type=u64 param socket type=u64 param direction type=u32 end -import elide_transport_tls_client symbol=elide_transport_tls_client return=u64 error=no_failure +import elide_transport_tls_client symbol=elide_transport_tls_client return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param roots type=u64 param alpn type=u64 end -import elide_transport_tls_context_release symbol=elide_transport_tls_context_release return=i32 error=no_failure +import elide_transport_tls_context_release symbol=elide_transport_tls_context_release return=i32 error=no_failure native_leaf=false@explicit_contract param context type=u64 end -import elide_transport_tls_feed symbol=elide_transport_tls_feed return=i32 error=no_failure +import elide_transport_tls_feed symbol=elide_transport_tls_feed return=i32 error=no_failure native_leaf=false@explicit_contract param session type=u64 param input type=u64 param length type=u64 end -import elide_transport_tls_new symbol=elide_transport_tls_new return=u64 error=no_failure +import elide_transport_tls_new symbol=elide_transport_tls_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param context type=u64 param owner type=u64 param name type=u64 end -import elide_transport_tls_protocol symbol=elide_transport_tls_protocol return=i32 error=no_failure +import elide_transport_tls_protocol symbol=elide_transport_tls_protocol return=i32 error=no_failure native_leaf=false@explicit_contract param session type=u64 param output type=u64 end -import elide_transport_tls_release symbol=elide_transport_tls_release return=i32 error=no_failure +import elide_transport_tls_release symbol=elide_transport_tls_release return=i32 error=no_failure native_leaf=false@explicit_contract param session type=u64 end -import elide_transport_tls_server symbol=elide_transport_tls_server return=u64 error=no_failure +import elide_transport_tls_server symbol=elide_transport_tls_server return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param chain type=u64 param key type=u64 param alpn type=u64 end -import elide_transport_tls_step symbol=elide_transport_tls_step return=i32 error=no_failure +import elide_transport_tls_step symbol=elide_transport_tls_step return=i32 error=no_failure native_leaf=false@explicit_contract param session type=u64 param action type=u32 param plaintext type=u64 @@ -525,7 +525,7 @@ import elide_transport_tls_step symbol=elide_transport_tls_step return=i32 error param output type=u64 end -import elide_transport_workload_close symbol=elide_transport_workload_close return=i32 error=no_failure +import elide_transport_workload_close symbol=elide_transport_workload_close return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 end @@ -540,7 +540,7 @@ callback elide_transport_event_batch_callback_t return=i32 java_type=dev.elide.b param count type=u32 end -import elide_transport_driver_poll_callback symbol=elide_transport_driver_poll_callback return=i32 error=no_failure +import elide_transport_driver_poll_callback symbol=elide_transport_driver_poll_callback return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param timeoutNanos type=u64 @@ -549,7 +549,7 @@ import elide_transport_driver_poll_callback symbol=elide_transport_driver_poll_c param context type=ptr end -import elide_transport_driver_poll_batch_callback symbol=elide_transport_driver_poll_batch_callback return=i32 error=no_failure +import elide_transport_driver_poll_batch_callback symbol=elide_transport_driver_poll_batch_callback return=i32 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param driver type=u64 param timeoutNanos type=u64 @@ -558,17 +558,17 @@ import elide_transport_driver_poll_batch_callback symbol=elide_transport_driver_ param context type=ptr end -import elide_transport_gzip_new symbol=elide_transport_gzip_new return=u64 error=no_failure +import elide_transport_gzip_new symbol=elide_transport_gzip_new return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param level type=u32 end -import elide_transport_gzip_compress symbol=elide_transport_gzip_compress return=u64 error=no_failure +import elide_transport_gzip_compress symbol=elide_transport_gzip_compress return=u64 error=no_failure native_leaf=false@explicit_contract param workload type=u64 param encoder type=u64 param input type=u64 end -import elide_transport_gzip_release symbol=elide_transport_gzip_release return=i32 error=no_failure +import elide_transport_gzip_release symbol=elide_transport_gzip_release return=i32 error=no_failure native_leaf=false@explicit_contract param encoder type=u64 end diff --git a/tools/build.py b/tools/build.py index e8ff801..5edae68 100644 --- a/tools/build.py +++ b/tools/build.py @@ -48,6 +48,16 @@ def sdk(): for name in ("nativeimage", "word")] +def native_binding_sdk(): + """Myna binding ABI 2 implementation APIs, for compilation only.""" + import setup_graalvm + directory = setup_graalvm.home() / "lib/svm" + jars = [directory / name for name in ("svm-shared.jar", "svm-guest-staging.jar", "svm-guest.jar")] + if not all(path.is_file() for path in jars): + raise RuntimeError("Pinned GraalVM lacks Myna binding ABI 2 compile-only jars") + return jars + + def netty(): return [jar_dependency("io.netty", name, VERSIONS["netty"]) for name in ( "netty-common", "netty-buffer", "netty-transport", "netty-resolver", "netty-handler", @@ -173,7 +183,7 @@ def jvm(): for module in MODULES: cp = [jspecify()] if module == "api" else [classes("api"), jspecify()] if module == "native-image": - cp += sdk() + cp += sdk() + native_binding_sdk() if module == "netty": cp += netty() if module == "bali": @@ -184,6 +194,9 @@ def jvm(): compile_kotlin(classes(module), sorted((ROOT / "packages/ktor/src/main/kotlin").rglob("*.kt"))) else: compile_java(classes(module), sources(module), cp) + if module == "native-image": + shutil.copytree(seam.output(seam.CANONICAL_TARGET) / "META-INF", + classes(module) / "META-INF", dirs_exist_ok=True) resources = ROOT / "packages" / module / "src/main/resources" if resources.is_dir(): shutil.copytree(resources, classes(module), dirs_exist_ok=True) @@ -508,7 +521,7 @@ def package(): shutil.rmtree(docs, ignore_errors=True) cp = [jspecify()] if module == "api" else [classes("api"), jspecify()] if module == "native-image": - cp += sdk() + cp += sdk() + native_binding_sdk() if module == "netty": cp += netty() if module == "bali": diff --git a/tools/check_java.py b/tools/check_java.py index 2cc4002..54fafb3 100644 --- a/tools/check_java.py +++ b/tools/check_java.py @@ -5,7 +5,7 @@ import subprocess import contextlib -from build import BUILD, ELIDE, MODULES, ROOT, VERSIONS, bali_api_classpath, classpath, deps, jar_dependency, netty, run, sdk, sources, spring +from build import BUILD, ELIDE, MODULES, ROOT, VERSIONS, bali_api_classpath, classpath, deps, jar_dependency, native_binding_sdk, netty, run, sdk, sources, spring def compiler(): @@ -26,7 +26,7 @@ def analyze(kind, inputs, output, **kwargs): if kind == "error-prone" else [jar_dependency("org.checkerframework", "checker", VERSIONS["checker"]), jar_dependency("org.checkerframework", "checker-util", VERSIONS["checker"])]) - cp = [*sdk(), *netty(), *spring(), *bali_api_classpath(), jar_dependency("org.jspecify", "jspecify", VERSIONS["jspecify"]), + cp = [*sdk(), *native_binding_sdk(), *netty(), *spring(), *bali_api_classpath(), jar_dependency("org.jspecify", "jspecify", VERSIONS["jspecify"]), jar_dependency("org.checkerframework", "checker-qual", VERSIONS["checker"])] processors += [jar_dependency("org.checkerframework", "checker-qual", VERSIONS["checker"])] flags = (["-proc:none", "-XDcompilePolicy=simple", "--should-stop=ifError=FLOW", diff --git a/tools/seam.py b/tools/seam.py index c347e7d..a5ee31c 100644 --- a/tools/seam.py +++ b/tools/seam.py @@ -80,6 +80,10 @@ def verify_signatures(headers, descriptors): def validate_descriptor(descriptor=None): descriptor = descriptor if descriptor is not None else DESCRIPTOR.read_text() + # Myna 0.8 defaults missing policies to leaf. Require an explicit audit for every import. + for declaration in re.findall(r'^import .*$', descriptor, re.M): + if not re.search(r' native_leaf=(?:true|false)@explicit_contract(?: |$)', declaration): + raise RuntimeError('Every import requires an explicit native_leaf policy: ' + declaration) headers = {} for name in ('bemo.h', 'elide_transport.h'): headers.update(header_functions((ROOT / 'include' / name).read_text())) diff --git a/tools/test_bitcode.py b/tools/test_bitcode.py index 73bcd57..b17a426 100644 --- a/tools/test_bitcode.py +++ b/tools/test_bitcode.py @@ -12,11 +12,11 @@ def archive(name, data): class BitcodeTest(unittest.TestCase): def test_same_major_rust_bitcode_accepts_pinned_patch_version(self): - outputs = ['rustc 1.101\nLLVM version: 23.1.1\n', 'clang version 23.1.2\n', - 'LLD 23.1.2\n', '23.1.2\n', '2026.10.0\n'] + outputs = ['rustc 1.101\nLLVM version: 23.1.1\n', 'clang version 23.1.4\n', + 'LLD 23.1.4\n', '23.1.4\n', '2026.11.1\n'] with patch.object(bitcode, 'tool', side_effect=lambda name: name), \ patch.object(bitcode.subprocess, 'check_output', side_effect=outputs): - self.assertEqual(bitcode.toolchain()['llvm'], '23.1.2') + self.assertEqual(bitcode.toolchain()['llvm'], '23.1.4') def test_different_rust_llvm_major_is_rejected(self): with patch.object(bitcode.subprocess, 'check_output', return_value='LLVM version: 22.1.1\n'): diff --git a/tools/test_seam.py b/tools/test_seam.py index 12adb40..06c3aa0 100644 --- a/tools/test_seam.py +++ b/tools/test_seam.py @@ -55,6 +55,26 @@ def test_empty_and_probe_pointer_metadata(self): self.assertTrue(pointers) self.assertTrue(all('nullable=true' in line for line in pointers), name) + def test_missing_call_policy_is_rejected(self): + descriptor = seam.DESCRIPTOR.read_text().replace(' native_leaf=false@explicit_contract', '', 1) + with self.assertRaisesRegex(RuntimeError, 'explicit native_leaf'): + seam.validate_descriptor(descriptor) + + def test_audited_leaf_inventory(self): + leaves = set(re.findall(r'^import (\w+).* native_leaf=true@explicit_contract$', + seam.DESCRIPTOR.read_text(), re.M)) + expected = {'bemo_abi_version', 'bemo_capabilities', 'bemo_alloc_live', + 'elide_transport_abi_version', 'elide_transport_last_error', + 'elide_transport_serving_helper_prepare'} + expected.update('elide_transport_http_' + name for name in + ('method', 'version', 'header_count', 'keep_alive', 'view', 'spans')) + self.assertEqual(leaves, expected) + java = seam.JAVA.read_text() + self.assertIn('BINDING_ABI_VERSION = 2', java) + self.assertEqual(set(re.findall(r'public interface (\w+)_Leaf ', java)), expected) + # Leaf calls must use typed pointers, never resurrect ordinary CFunction leaf adapters. + self.assertNotRegex(java, r'@CFunction\([^)]*NO_TRANSITION') + def test_real_inventory(self): seam.validate_descriptor() seam.validate_rust_signatures() diff --git a/tools/verify_package.py b/tools/verify_package.py index 16ea371..9193319 100644 --- a/tools/verify_package.py +++ b/tools/verify_package.py @@ -50,6 +50,12 @@ def verify(): assert entry.startswith("dev/elide/bemo/") or entry == "io/netty/handler/ssl/ApplicationProtocolSslEngine.class", \ f"Unexpected published Java namespace: {entry}" assert int.from_bytes(jar.read(entry)[6:8], "big") <= 66, "JDK 22 baseline exceeded" + if module == "native-image": + metadata = "META-INF/native-image/dev.elide.myna/dev.elide.bemo.svm.generated.BemoNatives/native-image.properties" + assert metadata in names, "Missing Myna binding ABI 2 initialization metadata" + properties = jar.read(metadata).decode() + assert "--initialize-at-run-time=" in properties + assert "--add-exports=org.graalvm.nativeimage.guest.staging/" in properties if module == "netty": metadata = "META-INF/native-image/dev.elide.bemo/bemo-netty/" assert metadata + "reflect-config.json" in names @@ -158,7 +164,10 @@ def loader_check(extra=(), properties=(), expected=()): assert manifest["sha256"] == hashlib.sha256(binary.read_bytes()).hexdigest() assert manifest["members"] == bitcode.inventory(binary.read_bytes()) assert manifest["seamFingerprint"] == (variant_dir / "seam.abi").read_text().strip() - assert json.loads((variant_dir / "seam.json").read_text())["targetTriple"] == manifest["target"] + descriptor = json.loads((variant_dir / "seam.json").read_text()) + assert descriptor["targetTriple"] == manifest["target"] + assert descriptor["schemaVersion"] == descriptor["bindingAbiVersion"] == 2 + assert descriptor["abiVersion"] == 3 bitcode.verify(binary, variant_dir / "verify") print("Maven package contracts passed (resource loading, static linkage, and ThinLTO)") diff --git a/tools/versions.json b/tools/versions.json index a3de7ee..d63f0b1 100644 --- a/tools/versions.json +++ b/tools/versions.json @@ -1,8 +1,8 @@ { - "elide": "1.6.1", + "elide": "1.6.2", "java": 25, "jvm_release": 22, - "graalvm_sdk": "25.5.5-elide.21", + "graalvm_sdk": "25.5.5-elide.24", "java_format": "1.28.0", "netty": "4.2.19.Final", "jacoco": "0.8.15", @@ -17,34 +17,35 @@ "nullaway_dataflow": "4.2.3", "dataflow": "3.49.5-eisop1", "llvm": { - "version": "23.1.2", - "toolchain_version": "2026.10.0", + "version": "23.1.4", + "toolchain_version": "2026.11.1", "archives": { "x86_64-unknown-linux-gnu": { - "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.10.0/elide-toolchain-2026.10.0-linux-amd64.tar.xz", - "sha256": "e966f3a9e8fa210e5018ae6e4a834874577e2288b27a6c9edfe5e264de0fd035" + "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.11.1/elide-toolchain-2026.11.1-linux-amd64.tar.xz", + "sha256": "4610627acc96f647776fd4256b3c1fce04c14b2757df692d8923ec299aed0e58" }, "aarch64-apple-darwin": { - "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.10.0/elide-toolchain-2026.10.0-darwin-arm64.tar.xz", - "sha256": "e39b6430f36b3d3d20439a42e9047b6edb64d0c2a9277e69afd35e290dc93064" + "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.11.1/elide-toolchain-2026.11.1-darwin-arm64.tar.xz", + "sha256": "37e56bad391f8397c99951a529ef84bdc99c9d19bdf91edbc153b5bdecb69e88" } }, "sanitizers": { "x86_64-unknown-linux-gnu": { "asan": { - "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.10.0/elide-toolchain-2026.10.0-linux-amd64-sanitizer-asan.tar.xz", - "sha256": "8309af8c55f0e009a898e2cb304c2629ed650272e0d510cc93266d81c8db20d7" + "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.11.1/elide-toolchain-2026.11.1-linux-amd64-sanitizer-asan.tar.xz", + "sha256": "b7e5a9cf4ba282b782763075009b5bda1fa626819748423b366f049ef16eddde" }, "tsan": { - "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.10.0/elide-toolchain-2026.10.0-linux-amd64-sanitizer-tsan.tar.xz", - "sha256": "707abcdf926f00e1bffa66c42f80bd85c39f84af438788beda6aea3435d3f8df" + "url": "https://github.com/elide-dev/toolchain/releases/download/v2026.11.1/elide-toolchain-2026.11.1-linux-amd64-sanitizer-tsan.tar.xz", + "sha256": "6afc7161b3d7bd9f6f05e40aa790415c3801540351b0b187003282fadcbf32a1" } } } }, "myna": { "repository": "https://github.com/elide-dev/myna.git", - "revision": "66ee758fa3073cfac1dc386510105e41f219bad3" + "revision": "42d7ab484bef7a944b374a4683922c0a10c4ec1c", + "version": "0.8.0" }, "ktor": "3.6.1", "kotlin": "2.4.21", @@ -53,15 +54,15 @@ "reactor": "3.8.8", "spring_boot": "4.1.2", "graalvm": { - "version": "25.5.5-elide.21", + "version": "25.5.5-elide.24", "archives": { "aarch64-apple-darwin": { - "url": "https://maven.elide.dev/gvm-elide/25.5.5-elide.21/dist/graalvm-ee-25.5.5-elide.21-macos-aarch64.tar.gz", - "sha256": "7ec06b440e5365255371cfcb73e0fbfed5519d475c6fc9e49d06f4512633bf0e" + "url": "https://maven.elide.dev/gvm-elide/25.5.5-elide.24/dist/graalvm-ce-25.5.5-elide.24-macos-aarch64.tar.gz", + "sha256": "8b03207cb35ab5b9bec6422b4266178315f7a056ae88e90f57cc22b2362c44a6" }, "x86_64-unknown-linux-gnu": { - "url": "https://maven.elide.dev/gvm-elide/25.5.5-elide.21/dist/graalvm-ee-25.5.5-elide.21-linux-amd64.tar.gz", - "sha256": "1100390d588667a288d7d2e51db56ab88c8858874d0f2f602387166370d19ea3" + "url": "https://maven.elide.dev/gvm-elide/25.5.5-elide.24/dist/graalvm-ce-25.5.5-elide.24-linux-amd64.tar.gz", + "sha256": "83bd7b3b78e76de00bb3879eeae79a8b439f9356fefd3bf3b84d2818af8961e7" } } } From 9ecdb6cfae02059f78a96ae0b7b10b7923b9f8ed Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 4 Nov 2026 17:06:48 -0800 Subject: [PATCH 2/5] fix(build): keep Elide cc aliases out of native compiler discovery --- tools/build.py | 4 +++ tools/native_tools.py | 54 +++++++++++++++++++++++++++++++++ tools/seam.py | 2 ++ tools/test_git_dependency.py | 3 ++ tools/test_native_tools.py | 59 ++++++++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 tools/native_tools.py create mode 100644 tools/test_native_tools.py diff --git a/tools/build.py b/tools/build.py index 5edae68..770a9b6 100644 --- a/tools/build.py +++ b/tools/build.py @@ -17,6 +17,9 @@ from reports import Reports import seam import bitcode +import native_tools + +native_tools.configure() ROOT = Path(__file__).resolve().parents[1] BUILD = ROOT / "build" @@ -422,6 +425,7 @@ def check(): run(sys.executable, ROOT / "tools/test_native_baseline.py") run(sys.executable, ROOT / "tools/test_bench.py") run(sys.executable, ROOT / "tools/test_seam.py") + run(sys.executable, ROOT / "tools/test_native_tools.py") run(sys.executable, ROOT / "tools/test_bitcode.py") run(sys.executable, ROOT / "tools/test_setup_llvm.py") run(sys.executable, ROOT / "tools/generate_exports.py", "--check") diff --git a/tools/native_tools.py b/tools/native_tools.py new file mode 100644 index 0000000..bab7f82 --- /dev/null +++ b/tools/native_tools.py @@ -0,0 +1,54 @@ +"""Keep Elide's cc language entrypoint out of native compiler discovery.""" +import os +from pathlib import Path +import shutil +import subprocess + + +def environment(env, directory): + env = dict(env) + cc = shutil.which('cc', path=env.get('PATH', '')) + if not cc: + return env + resolved = Path(cc).resolve() + if resolved.name == 'mise': + # Mise shims all resolve to its executable; inspect the selected cc instead. + result = subprocess.run([resolved, 'which', 'cc'], env=env, text=True, + capture_output=True, check=False) + if result.returncode: + return env + resolved = Path(result.stdout.strip()) + with resolved.open('rb') as source: + alias = b'Dist entrypoint for alias: cc' in source.read(512) + if not alias: + return env + compiler = None + for entry in env.get('PATH', '').split(os.pathsep): + candidate = shutil.which('clang', path=entry) + if not candidate: + continue + if Path(candidate).resolve().name == 'mise': + result = subprocess.run([Path(candidate).resolve(), 'which', 'clang'], env=env, + text=True, capture_output=True, check=False) + if result.returncode: + continue + candidate = result.stdout.strip() + compiler = candidate + break + if not compiler: + raise RuntimeError('Elide cc shadows the native compiler; install Clang or select a native toolchain') + # Only cc is shadowed. Keep OpenSSL, Java, Python, Cargo and all user compiler + # flags in their existing order; a whole system-bin PATH prepend changes them. + directory = Path(directory).absolute() + directory.mkdir(parents=True, exist_ok=True) + wrapper = directory / 'cc' + if not wrapper.is_symlink() or os.readlink(wrapper) != compiler: + wrapper.unlink(missing_ok=True) + wrapper.symlink_to(compiler) + env['PATH'] = str(directory) + os.pathsep + env.get('PATH', '') + return env + + +def configure(): + directory = Path(__file__).resolve().parents[1] / 'build/tools/native-cc' + os.environ.update(environment(os.environ, directory)) diff --git a/tools/seam.py b/tools/seam.py index a5ee31c..6f8b6d6 100644 --- a/tools/seam.py +++ b/tools/seam.py @@ -219,6 +219,8 @@ def generator(): def generate(check=False, target=None): + import native_tools + native_tools.configure() validate_descriptor() validate_rust_signatures() cli = generator() diff --git a/tools/test_git_dependency.py b/tools/test_git_dependency.py index d0b5cb0..555f7cf 100644 --- a/tools/test_git_dependency.py +++ b/tools/test_git_dependency.py @@ -7,6 +7,9 @@ import subprocess import tempfile import tomllib +import native_tools + +native_tools.configure() ROOT = Path(__file__).resolve().parents[1] diff --git a/tools/test_native_tools.py b/tools/test_native_tools.py new file mode 100644 index 0000000..61af0f4 --- /dev/null +++ b/tools/test_native_tools.py @@ -0,0 +1,59 @@ +"""Elide aliases must not intercept Cargo or Native Image's C compiler.""" +import os +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch +import subprocess + +import native_tools + + +class NativeToolsTest(unittest.TestCase): + def test_only_elide_cc_is_shadowed(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + for name in ('elide', 'native'): + (root / name).mkdir() + for name in ('elide/elide', 'elide/cc', 'native/clang'): + file = root / name + file.write_text('#!/bin/sh\n' + ('# Dist entrypoint for alias: cc\n' if name == 'elide/cc' else '') + 'exit 0\n') + file.chmod(0o755) + original = {'PATH': os.pathsep.join(map(str, (root / 'elide', root / 'native'))), + 'CC': 'custom-cc', 'RUSTFLAGS': '-Cdebuginfo=1'} + result = native_tools.environment(original, root / 'shim') + self.assertEqual(result['PATH'], str(root / 'shim') + os.pathsep + original['PATH']) + self.assertEqual((root / 'shim/cc').resolve(), (root / 'native/clang').resolve()) + self.assertEqual(result['CC'], original['CC']) + self.assertEqual(result['RUSTFLAGS'], original['RUSTFLAGS']) + self.assertNotIn('ELIDE', original) + + def test_native_path_without_elide_is_preserved(self): + env = {'PATH': '', 'CC': 'custom-cc'} + self.assertEqual(native_tools.environment(env, 'unused'), env) + + def test_native_cc_is_preserved(self): + with tempfile.TemporaryDirectory() as tmp: + cc = Path(tmp) / 'cc' + cc.write_text('#!/bin/sh\nexit 0\n') + cc.chmod(0o755) + env = {'PATH': tmp} + self.assertEqual(native_tools.environment(env, Path(tmp) / 'shim'), env) + + def test_mise_cc_shim_resolves_to_elide_alias(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + for name in ('mise', 'clang', 'elide-cc'): + file = root / name + file.write_text('#!/bin/sh\n# Dist entrypoint for alias: cc\n') + file.chmod(0o755) + (root / 'cc').symlink_to(root / 'mise') + with patch.object(native_tools.subprocess, 'run', return_value= + subprocess.CompletedProcess([], 0, str(root / 'elide-cc') + '\n')): + result = native_tools.environment({'PATH': tmp}, root / 'shim') + self.assertEqual(result['PATH'], str(root / 'shim') + os.pathsep + tmp) + self.assertEqual((root / 'shim/cc').resolve(), (root / 'clang').resolve()) + + +if __name__ == '__main__': + unittest.main() From 5fbac52cda71f033d82d600466a3dbdc97d58520 Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 4 Nov 2026 17:08:05 -0800 Subject: [PATCH 3/5] test(safety): synchronize the deliberate TSAN race canary --- tests/sanitizer/SanitizerCanary.java | 7 +++++++ tests/sanitizer/canary.c | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/sanitizer/SanitizerCanary.java b/tests/sanitizer/SanitizerCanary.java index fb8d2d8..fd525e2 100644 --- a/tests/sanitizer/SanitizerCanary.java +++ b/tests/sanitizer/SanitizerCanary.java @@ -5,6 +5,7 @@ import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.nio.file.Path; +import java.util.concurrent.CountDownLatch; /** Calls a deliberately defective native library through FFM; the sanitizer must abort it. */ public final class SanitizerCanary { @@ -26,18 +27,24 @@ public static void main(String[] args) throws Throwable { symbols.find("bemo_canary_race").orElseThrow(), FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT)); Thread[] threads = new Thread[4]; + CountDownLatch ready = new CountDownLatch(threads.length); + CountDownLatch start = new CountDownLatch(1); for (int index = 0; index < threads.length; index++) { threads[index] = Thread.ofPlatform() .start( () -> { try { + ready.countDown(); + start.await(); race.invokeExact(100_000); } catch (Throwable failure) { throw new AssertionError(failure); } }); } + ready.await(); + start.countDown(); for (Thread thread : threads) { thread.join(); } diff --git a/tests/sanitizer/canary.c b/tests/sanitizer/canary.c index 2e704a0..78fe904 100644 --- a/tests/sanitizer/canary.c +++ b/tests/sanitizer/canary.c @@ -14,7 +14,9 @@ int32_t bemo_canary_overflow(void) { } /* Unsynchronized counter written from several Java threads: TSAN must report it. */ -static uint64_t counter; +/* Keep repeated loads/stores; an optimized counter += iterations can finish + * before the other callers enter. Volatile does not make these accesses atomic. */ +static volatile uint64_t counter; void bemo_canary_race(int32_t iterations) { for (int32_t index = 0; index < iterations; index++) { From 1bcbfe63a181b52c48d96c920bd39b341b0a20e2 Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 4 Nov 2026 17:09:49 -0800 Subject: [PATCH 4/5] fix(build): preserve the platform compiler behind Elide aliases --- tools/native_tools.py | 53 ++++++++++++++++++-------------------- tools/test_native_tools.py | 15 ++++++----- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/tools/native_tools.py b/tools/native_tools.py index bab7f82..a1fad10 100644 --- a/tools/native_tools.py +++ b/tools/native_tools.py @@ -5,46 +5,43 @@ import subprocess -def environment(env, directory): - env = dict(env) - cc = shutil.which('cc', path=env.get('PATH', '')) - if not cc: - return env - resolved = Path(cc).resolve() +def compiler(path, env): + resolved = Path(path).resolve() if resolved.name == 'mise': # Mise shims all resolve to its executable; inspect the selected cc instead. result = subprocess.run([resolved, 'which', 'cc'], env=env, text=True, capture_output=True, check=False) if result.returncode: - return env - resolved = Path(result.stdout.strip()) + return None + resolved = Path(result.stdout.strip()).resolve() with resolved.open('rb') as source: - alias = b'Dist entrypoint for alias: cc' in source.read(512) - if not alias: + if b'Dist entrypoint for alias: cc' in source.read(512): + return None + return resolved + + +def environment(env, directory): + env = dict(env) + cc = shutil.which('cc', path=env.get('PATH', '')) + if not cc or compiler(cc, env) is not None: return env - compiler = None + selected = None for entry in env.get('PATH', '').split(os.pathsep): - candidate = shutil.which('clang', path=entry) - if not candidate: - continue - if Path(candidate).resolve().name == 'mise': - result = subprocess.run([Path(candidate).resolve(), 'which', 'clang'], env=env, - text=True, capture_output=True, check=False) - if result.returncode: - continue - candidate = result.stdout.strip() - compiler = candidate - break - if not compiler: - raise RuntimeError('Elide cc shadows the native compiler; install Clang or select a native toolchain') - # Only cc is shadowed. Keep OpenSSL, Java, Python, Cargo and all user compiler - # flags in their existing order; a whole system-bin PATH prepend changes them. + candidate = shutil.which('cc', path=entry) + if candidate: + selected = compiler(candidate, env) + if selected is not None: + break + if selected is None: + raise RuntimeError('Elide cc shadows the native compiler; select a native toolchain') + # Only cc is shadowed. Preserve the platform compiler (Apple Clang on macOS), + # OpenSSL, Java, Python, Cargo, and all explicit compiler flags and overrides. directory = Path(directory).absolute() directory.mkdir(parents=True, exist_ok=True) wrapper = directory / 'cc' - if not wrapper.is_symlink() or os.readlink(wrapper) != compiler: + if not wrapper.is_symlink() or os.readlink(wrapper) != str(selected): wrapper.unlink(missing_ok=True) - wrapper.symlink_to(compiler) + wrapper.symlink_to(selected) env['PATH'] = str(directory) + os.pathsep + env.get('PATH', '') return env diff --git a/tools/test_native_tools.py b/tools/test_native_tools.py index 61af0f4..865f861 100644 --- a/tools/test_native_tools.py +++ b/tools/test_native_tools.py @@ -15,7 +15,7 @@ def test_only_elide_cc_is_shadowed(self): root = Path(tmp) for name in ('elide', 'native'): (root / name).mkdir() - for name in ('elide/elide', 'elide/cc', 'native/clang'): + for name in ('elide/elide', 'elide/cc', 'native/cc'): file = root / name file.write_text('#!/bin/sh\n' + ('# Dist entrypoint for alias: cc\n' if name == 'elide/cc' else '') + 'exit 0\n') file.chmod(0o755) @@ -23,7 +23,7 @@ def test_only_elide_cc_is_shadowed(self): 'CC': 'custom-cc', 'RUSTFLAGS': '-Cdebuginfo=1'} result = native_tools.environment(original, root / 'shim') self.assertEqual(result['PATH'], str(root / 'shim') + os.pathsep + original['PATH']) - self.assertEqual((root / 'shim/cc').resolve(), (root / 'native/clang').resolve()) + self.assertEqual((root / 'shim/cc').resolve(), (root / 'native/cc').resolve()) self.assertEqual(result['CC'], original['CC']) self.assertEqual(result['RUSTFLAGS'], original['RUSTFLAGS']) self.assertNotIn('ELIDE', original) @@ -43,16 +43,17 @@ def test_native_cc_is_preserved(self): def test_mise_cc_shim_resolves_to_elide_alias(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) - for name in ('mise', 'clang', 'elide-cc'): + (root / 'native').mkdir() + for name in ('mise', 'native/cc', 'elide-cc'): file = root / name - file.write_text('#!/bin/sh\n# Dist entrypoint for alias: cc\n') + file.write_text('#!/bin/sh\n' + ('# Dist entrypoint for alias: cc\n' if name == 'elide-cc' else '')) file.chmod(0o755) (root / 'cc').symlink_to(root / 'mise') with patch.object(native_tools.subprocess, 'run', return_value= subprocess.CompletedProcess([], 0, str(root / 'elide-cc') + '\n')): - result = native_tools.environment({'PATH': tmp}, root / 'shim') - self.assertEqual(result['PATH'], str(root / 'shim') + os.pathsep + tmp) - self.assertEqual((root / 'shim/cc').resolve(), (root / 'clang').resolve()) + result = native_tools.environment({'PATH': tmp + os.pathsep + str(root / 'native')}, root / 'shim') + self.assertEqual(result['PATH'], os.pathsep.join(map(str, (root / 'shim', root, root / 'native')))) + self.assertEqual((root / 'shim/cc').resolve(), (root / 'native/cc').resolve()) if __name__ == '__main__': From 9262262b675702fee7cd83a0d2fac47d9494fa6d Mon Sep 17 00:00:00 2001 From: Sam Gammon Date: Wed, 4 Nov 2026 17:46:32 -0800 Subject: [PATCH 5/5] fix(ci): preserve native compiler discovery for direct Cargo commands --- .github/actions/setup/action.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index eebbb6b..ef1f878 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -26,6 +26,19 @@ runs: with: version: ${{ steps.versions.outputs.elide }} channel: release + - name: Preserve the native compiler for direct Cargo commands + shell: bash + run: | + python3 - <<'PY' + import os, sys + sys.path.insert(0, "tools") + import native_tools + previous = os.environ["PATH"] + native_tools.configure() + if os.environ["PATH"] != previous: + with open(os.environ["GITHUB_PATH"], "a") as output: + output.write(os.environ["PATH"].split(os.pathsep)[0] + "\n") + PY - name: Bootstrap Rust from the repository toolchain pin shell: bash run: |