Skip to main content

runmat_runtime/native/
version.rs

1use std::mem::{align_of, size_of};
2
3use super::{
4    NativeCall, NativeCancellation, NativeDeoptimization, NativeException, NativeExit, NativeFrame,
5    NativeHostStatus, NativeHostVTable, NativeResumeState, NativeRoot, NativeRootSet,
6    NativeSafepoint, NativeSiteOutcome, NativeSiteOutcomeKind, NativeSitePhase, NativeSiteRequest,
7    NativeSourceLocation, NativeSourceMapEntry, NativeSourceMapView, NativeSuspension, NativeUtf8,
8    NativeValueRef,
9};
10
11pub const NATIVE_ABI_SCHEMA_VERSION: u16 = 4;
12
13#[repr(C)]
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct NativeAbiVersion {
16    pub major: u16,
17    pub minor: u16,
18}
19
20impl NativeAbiVersion {
21    pub const fn encoded(self) -> u32 {
22        ((self.major as u32) << 16) | self.minor as u32
23    }
24}
25
26pub const NATIVE_ABI_VERSION: NativeAbiVersion = NativeAbiVersion { major: 1, minor: 3 };
27
28/// Target-independent identity used by executable/runtime compatibility keys.
29pub fn native_abi_contract_fingerprint() -> runmat_execution::Digest {
30    runmat_execution::Digest::sha256(
31        b"runmat-native-abi-v4\0opaque-values\0generation-roots\0explicit-host-table\0typed-host-status\0out-parameter-exits\0no-cross-boundary-unwind\0exact-site-resume\0transactional-exit\0path-free-sources\0typed-native-site-dispatch\0no-obsolete-slow-call-slot",
32    )
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub struct NativeTypeLayout {
37    pub size: usize,
38    pub alignment: usize,
39}
40
41impl NativeTypeLayout {
42    fn of<T>() -> Self {
43        Self {
44            size: size_of::<T>(),
45            alignment: align_of::<T>(),
46        }
47    }
48}
49
50/// Complete target layout used by native-code cache keys and compatibility
51/// checks. Portable executable identity uses the contract fingerprint above;
52/// native objects additionally bind this target-specific fingerprint.
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub struct NativeAbiLayout {
55    pub pointer_width: u16,
56    pub host_status: NativeTypeLayout,
57    pub value: NativeTypeLayout,
58    pub root: NativeTypeLayout,
59    pub root_set: NativeTypeLayout,
60    pub source: NativeTypeLayout,
61    pub utf8: NativeTypeLayout,
62    pub source_map_entry: NativeTypeLayout,
63    pub source_map_view: NativeTypeLayout,
64    pub resume: NativeTypeLayout,
65    pub frame: NativeTypeLayout,
66    pub call: NativeTypeLayout,
67    pub host: NativeTypeLayout,
68    pub exception: NativeTypeLayout,
69    pub cancellation: NativeTypeLayout,
70    pub suspension: NativeTypeLayout,
71    pub deoptimization: NativeTypeLayout,
72    pub exit: NativeTypeLayout,
73    pub safepoint: NativeTypeLayout,
74    pub site_phase: NativeTypeLayout,
75    pub site_request: NativeTypeLayout,
76    pub site_outcome_kind: NativeTypeLayout,
77    pub site_outcome: NativeTypeLayout,
78}
79
80pub fn native_abi_layout() -> NativeAbiLayout {
81    NativeAbiLayout {
82        pointer_width: usize::BITS as u16,
83        host_status: NativeTypeLayout::of::<NativeHostStatus>(),
84        value: NativeTypeLayout::of::<NativeValueRef>(),
85        root: NativeTypeLayout::of::<NativeRoot>(),
86        root_set: NativeTypeLayout::of::<NativeRootSet>(),
87        source: NativeTypeLayout::of::<NativeSourceLocation>(),
88        utf8: NativeTypeLayout::of::<NativeUtf8>(),
89        source_map_entry: NativeTypeLayout::of::<NativeSourceMapEntry>(),
90        source_map_view: NativeTypeLayout::of::<NativeSourceMapView>(),
91        resume: NativeTypeLayout::of::<NativeResumeState>(),
92        frame: NativeTypeLayout::of::<NativeFrame>(),
93        call: NativeTypeLayout::of::<NativeCall>(),
94        host: NativeTypeLayout::of::<NativeHostVTable>(),
95        exception: NativeTypeLayout::of::<NativeException>(),
96        cancellation: NativeTypeLayout::of::<NativeCancellation>(),
97        suspension: NativeTypeLayout::of::<NativeSuspension>(),
98        deoptimization: NativeTypeLayout::of::<NativeDeoptimization>(),
99        exit: NativeTypeLayout::of::<NativeExit>(),
100        safepoint: NativeTypeLayout::of::<NativeSafepoint>(),
101        site_phase: NativeTypeLayout::of::<NativeSitePhase>(),
102        site_request: NativeTypeLayout::of::<NativeSiteRequest>(),
103        site_outcome_kind: NativeTypeLayout::of::<NativeSiteOutcomeKind>(),
104        site_outcome: NativeTypeLayout::of::<NativeSiteOutcome>(),
105    }
106}
107
108pub fn native_abi_layout_fingerprint() -> runmat_execution::Digest {
109    let layout = native_abi_layout();
110    runmat_execution::Digest::sha256(format!(
111        "runmat-native-layout-v1\0{}\0{:?}",
112        std::env::consts::ARCH,
113        layout
114    ))
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
122    #[cfg_attr(not(target_arch = "wasm32"), test)]
123    fn version_and_contract_identity_are_stable() {
124        assert_eq!(NATIVE_ABI_VERSION.encoded(), 0x0001_0003);
125        assert_eq!(
126            native_abi_contract_fingerprint().to_string(),
127            "sha256:f550a4e40b7d2f75c72231767205f88b68c73b1d7ae89b057bbbdcf4de67b225"
128        );
129    }
130
131    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
132    #[cfg_attr(not(target_arch = "wasm32"), test)]
133    fn layouts_are_c_compatible_and_offsets_are_explicit() {
134        let layout = native_abi_layout();
135        assert_eq!(layout.pointer_width, usize::BITS as u16);
136        assert_eq!(
137            layout.host_status,
138            NativeTypeLayout {
139                size: 4,
140                alignment: 4
141            }
142        );
143        assert_eq!(
144            layout.value,
145            NativeTypeLayout {
146                size: 16,
147                alignment: 8
148            }
149        );
150        assert_eq!(
151            layout.root,
152            NativeTypeLayout {
153                size: 24,
154                alignment: 8
155            }
156        );
157        assert_eq!(
158            layout.source,
159            NativeTypeLayout {
160                size: 24,
161                alignment: 8
162            }
163        );
164        assert_eq!(std::mem::offset_of!(NativeFrame, caller), 16);
165        assert_eq!(std::mem::offset_of!(NativeCall, host), 16);
166        assert_eq!(std::mem::offset_of!(NativeExit, exception), 16);
167        assert_eq!(std::mem::offset_of!(NativeResumeState, phase), 12);
168        assert_eq!(std::mem::offset_of!(NativeResumeState, ordinal), 16);
169        assert_eq!(std::mem::offset_of!(NativeResumeState, bytecode_pc), 32);
170        assert_eq!(
171            layout.site_request,
172            NativeTypeLayout {
173                size: 24,
174                alignment: 4
175            }
176        );
177        assert_eq!(
178            layout.site_outcome,
179            NativeTypeLayout {
180                size: 16,
181                alignment: 4
182            }
183        );
184        assert!(
185            std::mem::offset_of!(NativeHostVTable, execute_site)
186                > std::mem::offset_of!(NativeHostVTable, source_lookup)
187        );
188    }
189
190    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
191    #[cfg_attr(not(target_arch = "wasm32"), test)]
192    fn target_layout_fingerprint_is_deterministic() {
193        assert_eq!(
194            native_abi_layout_fingerprint(),
195            native_abi_layout_fingerprint()
196        );
197        assert_ne!(
198            native_abi_layout_fingerprint(),
199            native_abi_contract_fingerprint()
200        );
201    }
202}