savefile_abi/
lib.rs

1#![allow(clippy::len_zero)]
2#![deny(warnings)]
3#![deny(missing_docs)]
4#![allow(clippy::needless_late_init)]
5#![allow(clippy::uninlined_format_args)]
6#![allow(clippy::unnecessary_cast)]
7
8/*!
9This is the documentation for `savefile-abi`
10
11# Welcome to savefile-abi!
12
13Savefile-abi is a crate that is primarily meant to help building binary plugins using Rust.
14
15Note! Savefile-abi now supports methods returning boxed futures! See the chapter 'async' below.
16
17# Example
18
19Let's say we have a crate that defines this trait for adding u32s:
20
21*InterfaceCrate*
22```
23extern crate savefile_derive;
24use savefile_derive::savefile_abi_exportable;
25
26#[savefile_abi_exportable(version=0)]
27pub trait AdderInterface {
28    fn add(&self, x: u32, y: u32) -> u32;
29}
30
31```
32
33Now, we want to implement addition in a different crate, compile it to a shared library
34(.dll or .so), and use it in the first crate (or some other crate):
35
36*ImplementationCrate*
37```
38 # extern crate savefile_derive;
39 # use savefile_derive::{savefile_abi_exportable};
40 # #[savefile_abi_exportable(version=0)]
41 # pub trait AdderInterface {
42 #   fn add(&self, x: u32, y: u32) -> u32;
43 # }
44 #
45use savefile_derive::{savefile_abi_export};
46#[derive(Default)]
47struct MyAdder { }
48
49impl AdderInterface for MyAdder {
50    fn add(&self, x: u32, y: u32) -> u32 {
51        x + y
52    }
53}
54
55/// Export this implementation as the default-implementation for
56/// the interface 'AdderInterface', for the current library.
57savefile_abi_export!(MyAdder, AdderInterface);
58
59```
60
61We add the following to Cargo.toml in our implementation crate:
62
63```toml
64[lib]
65crate-type = ["cdylib"]
66```
67
68Now, in our application, we add a dependency to *InterfaceCrate*, but not
69to *ImplementationCrate*.
70
71We then load the implementation dynamically at runtime:
72
73*ApplicationCrate*
74
75```rust,no_run
76 # extern crate savefile_derive;
77 # mod adder_interface {
78 #   use savefile_derive::savefile_abi_exportable;
79 #   #[savefile_abi_exportable(version=0)]
80 #   pub trait AdderInterface {
81 #     fn add(&self, x: u32, y: u32) -> u32;
82 #   }
83 # }
84 #
85use adder_interface::AdderInterface;
86use savefile_abi::AbiConnection;
87
88
89// Load the implementation of `dyn AdderInterface` that was published
90// using the `savefile_abi_export!` above.
91let connection = AbiConnection::<dyn AdderInterface>
92        ::load_shared_library("ImplementationCrate.so").unwrap();
93
94// The type `AbiConnection::<dyn AdderInterface>` implements
95// the `AdderInterface`-trait, so we can use it to call its methods.
96assert_eq!(connection.add(1, 2), 3);
97
98```
99
100# More advanced examples
101
102Interface containing closure arguments:
103```
104 # extern crate savefile_derive;
105 # use savefile_derive::savefile_abi_exportable;
106#[savefile_abi_exportable(version=0)]
107pub trait CallMeBack {
108    fn call_me(&self, x: &dyn Fn(u32) -> u32) -> u32;
109    fn call_me_mut(&self, x: &mut dyn FnMut(u32) -> u32) -> u32;
110}
111
112```
113
114Interface containing more complex types:
115```
116 # extern crate savefile_derive;
117 # use savefile_derive::savefile_abi_exportable;
118 # use std::collections::{HashMap, BinaryHeap};
119#[savefile_abi_exportable(version=0)]
120pub trait Processor {
121    fn process(&self, x: &HashMap<String,String>, parameters: f32) -> BinaryHeap<u32>;
122}
123
124```
125
126Interface containing user defined types:
127```
128 # extern crate savefile_derive;
129 # use savefile_derive::{Savefile,savefile_abi_exportable};
130 # use std::collections::{HashMap, BinaryHeap};
131
132#[derive(Savefile)]
133pub struct MyCustomType {
134    pub name: String,
135    pub age: u8,
136    pub length: f32,
137}
138
139#[savefile_abi_exportable(version=0)]
140pub trait Processor {
141    fn insert(&self, x: &MyCustomType) -> Result<u32, String>;
142}
143
144```
145
146# Versioning
147
148Let's say the last example from the previous chapter needed to be evolved.
149The type now needs a 'city' field.
150
151We can add this while retaining compatibility with clients expecting the old API:
152
153```
154extern crate savefile_derive;
155
156 # use savefile::prelude::SavefileError;
157 # use savefile_derive::{Savefile,savefile_abi_exportable};
158 # use savefile_abi::verify_compatiblity;
159 # use std::collections::{HashMap, BinaryHeap};
160
161#[derive(Savefile)]
162pub struct MyCustomType {
163    pub name: String,
164    pub age: u8,
165    pub length: f32,
166    #[savefile_versions="1.."]
167    pub city: String,
168}
169
170#[savefile_abi_exportable(version=1)]
171pub trait Processor {
172    fn insert(&self, x: &MyCustomType) -> Result<u32, String>;
173}
174
175#[cfg(test)]
176{
177    #[test]
178    pub fn test_backward_compatibility() {
179       // Automatically verify backward compatibility isn't broken.
180       // Schemas for each version are stored in directory 'schemas',
181       // and consulted on next run to ensure no change.
182       // You should check the schemas in to source control.
183       // If check fails for an unreleased version, just remove the schema file from
184       // within 'schemas' directory.
185       verify_compatiblity::<dyn Processor>("schemas").unwrap()
186    }
187}
188
189
190```
191
192Older clients, not aware of the 'city' field, can still call newer implementations. The 'city'
193field will receive an empty string (Default::default()). Newer clients, calling older implementations,
194will simply, automatically, omit the 'city' field.
195
196
197# Background
198
199Savefile-abi is a crate that is primarily meant to help building binary plugins using Rust.
200
201The primary usecase is that a binary rust program is to be shipped to some customer,
202who should then be able to load various binary modules into the program at runtime.
203Savefile-abi defines ABI-stable rust-to-rust FFI for calling between a program and
204a runtime-loaded shared library.
205
206For now, both the main program and the plugins need to be written in rust. They can,
207however, be written using different versions of the rust compiler, and the API may
208be allowed to evolve. That is, data structures can be modified, and methods can be added
209(or removed).
210
211The reason savefile-abi is needed, is that rust does not have a stable 'ABI'. This means that
212if shared libraries are built using rust, all libraries must be compiled by the same version of
213rust, using the exact same source code. This means that rust cannot, 'out of the box', support
214a binary plugin system, without something like savefile-abi. This restriction may be lifted
215in the future, which would make this crate (savefile-abi) mostly redundant.
216
217Savefile-abi does not solve the general 'stable ABI'-problem. Rather, it defines a limited
218set of features, which allows useful calls between shared libraries, without allowing any
219and all rust construct.
220
221# Why another stable ABI-crate for Rust?
222
223There are other crates also providing ABI-stability. Savefile-abi has the following properties:
224
225 * It is able to completely specify the protocol used over the FFI-boundary. I.e, it can
226   isolate two shared libraries completely, making minimal assumptions about data type
227   memory layouts.
228
229 * When it cannot prove that memory layouts are identical, it falls back to (fast) serialization.
230   This has a performance penalty, and may require heap allocation.
231
232 * It tries to require a minimum of configuration needed by the user, while still being safe.
233
234 * It supports versioning of data structures (with a performance penalty).
235
236 * It supports trait objects as arguments, including FnMut() and Fn().
237
238 * Boxed trait objects, including Fn-traits, can be transferred across FFI-boundaries, passing
239   ownership, safely. No unsafe code is needed by the user.
240
241 * It requires enums to be `#[repr(uX)]` in order to pass them by reference. Other enums
242   will still work correctly, but will be serialized under the hood at a performance penalty.
243
244 * It places severe restrictions on types of arguments, since they must be serializable
245   using the Savefile-crate for serialization. Basically, arguments must be 'simple', in that
246   they must own all their contents, and be free of cycles. I.e, the type of the arguments must
247   have lifetime `&'static`. Note, arguments may still be references, and the contents of the
248   argument types may include Box, Vec etc, so this does not mean that only primitive types are
249   supported.
250
251Arguments cannot be mutable, since if serialization is needed, it would be impractical to detect and
252handle updates to arguments made by the callee. This said, arguments can still have types such as
253HashMap, IndexMap, Vec, String and custom defined structs or enums.
254
255# How it all works
256
257The basic principle is that savefile-abi makes sure to send function parameters in a way
258that is certain to be understood by the code on the other end of the FFI-boundary.
259It analyses if memory layouts of reference-parameters are the same on both sides of the
260FFI-boundary, and if they are, the references are simply copied. In all other cases, including
261all non-reference parameters, the data is simply serialized and sent as a binary buffer.
262
263The callee cannot rely on any particular lifetimes of arguments, since if the arguments
264were serialized, the arguments the callee sees will only have a lifetime of a single call,
265regardless of the original lifetime. Savefile-abi inspects all lifetimes and ensures
266that reference parameters don't have non-default lifetimes. Argument types must have static
267lifetimes (otherwise they can't be serialized). The only exception is that the argument
268can be reference types, but the type referenced must itself be `&'static`.
269
270# About Safety
271
272Savefile-Abi uses copious amounts of unsafe code. It has a test suite, and the
273test suite passes with miri.
274
275One thing to be aware of is that, at present, the AbiConnection::load_shared_library-method
276is not marked as unsafe. However, if the .so-file given as argument is corrupt, using this
277method can cause any amount of UB. Thus, it could be argued that it should be marked unsafe.
278
279However, the same is true for _any_ shared library used by a rust program, including the
280system C-library. It is also true that rust programs rely on the rust
281compiler being implemented correctly. Thus, it has been
282judged that the issue of corrupt binary files is beyond the scope of safety for Savefile-Abi.
283
284As long as the shared library is a real Savefile-Abi shared library, it should be sound to use,
285even if it contains code that is completely incompatible. This will be detected at runtime,
286and either AbiConnection::load_shared_library will panic, or any calls made after will panic.
287
288# About Vec and String references
289
290Savefile-Abi allows passing references containing Vec and/or String across the FFI-boundary.
291This is not normally guaranteed to be sound. However, Savefile-Abi uses heuristics to determine
292the actual memory layout of both Vec and String, and verifies that the two libraries agree
293on the layout. If they do not, the data is serialized instead. Also, since
294parameters can never be mutable in Savefile-abi (except for closures), we know
295the callee is not going to be freeing something allocated by the caller. Parameters
296called by value are always serialized.
297
298# Async
299
300Savefile-abi now supports methods returning futures:
301
302```rust
303
304use savefile_derive::savefile_abi_exportable;
305use std::pin::Pin;
306use std::future::Future;
307use std::time::Duration;
308
309#[savefile_abi_exportable(version = 0)]
310pub trait BoxedAsyncInterface {
311    fn add_async(&mut self, x: u32, y: u32) -> Pin<Box<dyn Future<Output=String>>>;
312
313}
314
315struct SimpleImpl;
316
317impl BoxedAsyncInterface for SimpleImpl {
318    fn add_async(&mut self, x: u32, y: u32) -> Pin<Box<dyn Future<Output=String>>> {
319        Box::pin(
320            async move {
321                /* any async code, using .await */
322                format!("{}",x+y)
323            }
324        )
325    }
326}
327
328
329```
330
331It also supports the `#[async_trait]` proc macro crate. Use it like this:
332
333```rust
334use async_trait::async_trait;
335use savefile_derive::savefile_abi_exportable;
336use std::time::Duration;
337
338#[async_trait]
339#[savefile_abi_exportable(version = 0)]
340pub trait SimpleAsyncInterface {
341    async fn add_async(&mut self, x: u32, y: u32) -> u32;
342}
343
344struct SimpleImpl;
345
346#[async_trait]
347impl SimpleAsyncInterface for SimpleImpl {
348    async fn add_async(&mut self, x: u32, y: u32) -> u32 {
349        /* any async code, using .await */
350        x + y
351    }
352}
353
354```
355
356
357
358*/
359
360extern crate core;
361extern crate savefile;
362extern crate savefile_derive;
363
364use byteorder::ReadBytesExt;
365use libloading::{Library, Symbol};
366use savefile::{
367    diff_schema, load_file_noschema, load_noschema, save_file_noschema, AbiMethodInfo, AbiTraitDefinition, Deserialize,
368    Deserializer, LittleEndian, SavefileError, Schema, Serializer, CURRENT_SAVEFILE_LIB_VERSION,
369};
370use std::any::TypeId;
371use std::collections::hash_map::Entry;
372use std::collections::HashMap;
373use std::hash::Hash;
374use std::io::{Cursor, Read, Write};
375use std::marker::PhantomData;
376use std::mem::MaybeUninit;
377use std::panic::catch_unwind;
378use std::path::Path;
379use std::ptr::null;
380use std::sync::{Arc, Mutex, MutexGuard};
381use std::task::Wake;
382use std::{ptr, slice};
383
384/// This trait is meant to be exported for a 'dyn SomeTrait'.
385/// It can be automatically implemented by using the
386/// macro `#[savefile_abi_exportable(version=0)]` on
387/// a trait that is to be exportable.
388///
389/// NOTE!
390/// If trait `MyExampleTrait` is to be exportable, the trait `AbiExportable` must
391/// be implemented for `dyn MyExampleTrait`.
392///
393/// NOTE!
394/// This trait is not meant to be implemented manually. It is mostly an implementation
395/// detail of SavefileAbi, it is only ever meant to be implemented by the savefile-derive
396/// proc macro.
397///
398/// # Safety
399/// The implementor must:
400///  * Make sure that the ABI_ENTRY function implements all parts of AbiProtocol
401///    in a correct manner
402///  * Has a correct 'get_definition' function, which must return a AbiTraitDefinition instance
403///    that is truthful.
404///  * Implement 'call' correctly
405#[diagnostic::on_unimplemented(
406    message = "`{Self}` cannot be used across an ABI-boundary. Try adding a `#[savefile_abi_exportable(version=X)]` attribute to the declaration of the relevant trait.",
407    label = "`{Self}` cannot be called across an ABI-boundary",
408    note = "This error probably occurred because `{Self}` occurred as a return-value or argument to a method in a trait marked with `#[savefile_abi_exportable(version=X)]`, or because savefile_abi_export!-macro was used to export `{Self}`."
409)]
410pub unsafe trait AbiExportable {
411    /// A function which implements the savefile-abi contract.
412    const ABI_ENTRY: unsafe extern "C" fn(AbiProtocol);
413    /// Must return a truthful description about all the methods in the
414    /// `dyn trait` that AbiExportable is implemented for (i.e, `Self`).
415    fn get_definition(version: u32) -> AbiTraitDefinition;
416    /// Must return the current latest version of the interface. I.e,
417    /// the version which Self represents. Of course, there may be future higher versions,
418    /// but none such are known by the code.
419    fn get_latest_version() -> u32;
420    /// Implement method calling. Must deserialize data from 'data', and
421    /// must return an outcome (result) by calling `receiver`.
422    ///
423    /// The return value is either Ok, or an error if the method to be called could
424    /// not be found or for some reason not called (mismatched actual ABI, for example).
425    ///
426    /// `receiver` must be given 'abi_result' as its 'result_receiver' parameter, so that
427    /// the receiver may set the result. The receiver executes at the caller-side of the ABI-divide,
428    /// but it receives as first argument an RawAbiCallResult that has been created by the callee.
429    fn call(
430        trait_object: TraitObject,
431        method_number: u16,
432        effective_version: u32,
433        compatibility_mask: u64,
434        data: &[u8],
435        abi_result: *mut (),
436        receiver: unsafe extern "C" fn(
437            outcome: *const RawAbiCallResult,
438            result_receiver: *mut (), /* actual type: Result<T,SaveFileError>>*/
439        ),
440    ) -> Result<(), SavefileError>;
441}
442
443/// Trait that is to be implemented for the implementation of a trait whose `dyn trait` type
444/// implements AbiExportable.
445///
446/// If `MyExampleTrait` is an ABI-exportable trait, and `MyExampleImplementation` is an
447/// implementation of `MyExampleTrait`, then:
448///  * The `AbiInterface` associated type must be `dyn MyExampleTrait`
449///  * `AbiExportableImplementation` must be implemented for `MyExampleImplementation`
450///
451/// # Safety
452/// The following must be fulfilled:
453/// * ABI_ENTRY must be a valid function, implementing the AbiProtocol-protocol.
454/// * AbiInterface must be 'dyn SomeTrait', where 'SomeTrait' is an exported trait.
455///
456#[diagnostic::on_unimplemented(
457    message = "`{Self}` cannot be the concrete type of an AbiExportable dyn trait.",
458    label = "Does not implement `AbiExportableImplementation`",
459    note = "You should not be using this trait directly, and should never see this error."
460)]
461pub unsafe trait AbiExportableImplementation {
462    /// An entry point which implements the AbiProtocol protocol
463    const ABI_ENTRY: unsafe extern "C" fn(AbiProtocol);
464    /// The type 'dyn SomeTrait'.
465    type AbiInterface: ?Sized + AbiExportable;
466    /// A method which must be able to return a default-implementation of `dyn SomeTrait`.
467    /// I.e, the returned box is a boxed dyn trait, not 'Self' (the actual implementation type).
468    fn new() -> Box<Self::AbiInterface>;
469}
470
471/// Given a boxed trait object pointer, expressed as a data ptr and a vtable pointer,
472/// of type T (which must be a `dyn SomeTrait` type), drop the boxed trait object.
473/// I.e, `trait_object` is a type erased instance of Box<T> , where T is for example
474/// `dyn MyTrait`.
475/// # Safety
476/// The given `trait_object` must be a boxed trait object.
477unsafe fn destroy_trait_obj<T: AbiExportable + ?Sized>(trait_object: TraitObject) {
478    let mut raw_ptr: MaybeUninit<*mut T> = MaybeUninit::uninit();
479    ptr::copy(
480        &trait_object as *const TraitObject as *const MaybeUninit<*mut T>,
481        &mut raw_ptr as *mut MaybeUninit<*mut T>,
482        1,
483    );
484
485    let _ = Box::from_raw(raw_ptr.assume_init());
486}
487
488/// Call the given method, on the trait object.
489///
490/// trait_object - Type erased version of Box<dyn SomeTrait>
491/// method_number - The method to be called. This is an ordinal number, with 0 being the first method in definition order in the trait.
492/// effective_version - The version number in the serialized format, negotiated previously.
493/// compatibility_mask - For each method, one bit which says if the argument can be sent as just a reference, without having to use serialization to do a deep copy
494/// data - All the arguments, in a slice
495/// abi_result - Pointer to a place which will receiver the return value. This points to a Result<T, SaveFileError>, but since that type may have a different layout in callee and caller, we can't just use that type.
496/// receiver - A function which will receiver the actual serialized return value, and an error code.
497///
498/// If the callee panics, this will be encoded into the RawAbiCallResult given to the `receiver`. The `reveiver` will always be called with the return value/return status.
499///
500/// # Safety
501/// Every detail of all the arguments must be correct. Any little error is overwhelmingly likely to cause
502/// a segfault or worse.
503unsafe fn call_trait_obj<T: AbiExportable + ?Sized>(
504    trait_object: TraitObject,
505    method_number: u16,
506    effective_version: u32,
507    compatibility_mask: u64,
508    data: &[u8],
509    abi_result: *mut (),
510    receiver: unsafe extern "C" fn(
511        outcome: *const RawAbiCallResult,
512        result_receiver: *mut (), /*Result<T,SaveFileError>>*/
513    ),
514) -> Result<(), SavefileError> {
515    <T>::call(
516        trait_object,
517        method_number,
518        effective_version,
519        compatibility_mask,
520        data,
521        abi_result,
522        receiver,
523    )
524}
525
526/// Describes a method in a trait
527#[derive(Debug)]
528pub struct AbiConnectionMethod {
529    /// The name of the method
530    pub method_name: String,
531    /// This is mostly for debugging, it's not actually used
532    pub caller_info: AbiMethodInfo,
533    /// The ordinal number of this method at the callee, or None if callee doesn't have
534    /// method.
535    pub callee_method_number: Option<u16>,
536    /// For each of the up to 64 different arguments,
537    /// a bit value of 1 means layout is identical, and in such a way that
538    /// references can be just binary copied (owned arguments must still be cloned, and
539    /// we can just as well do that using serialization, it will be approx as fast).
540    pub compatibility_mask: u64,
541}
542
543/// Type erased carrier of a dyn trait fat pointer
544#[repr(C)]
545#[derive(Clone, Copy, Debug)]
546pub struct TraitObject {
547    ptr: *const (),
548    vtable: *const (),
549}
550
551unsafe impl Sync for TraitObject {}
552unsafe impl Send for TraitObject {}
553
554impl TraitObject {
555    /// Returns a TraitObject with two null ptrs. This value must never be used,
556    /// but can serve as a default before the real value is written.
557    pub fn zero() -> TraitObject {
558        TraitObject {
559            ptr: null(),
560            vtable: null(),
561        }
562    }
563
564    /// Interpret this TraitObject as *mut T.
565    /// *mut T *MUST* be a fat pointer of the same type as was used to create this TraitObject
566    /// instance.
567    pub fn as_mut_ptr<T: ?Sized>(self) -> *mut T {
568        assert_eq!(
569            std::mem::size_of::<*mut T>(),
570            16,
571            "TraitObject must only be used with dyn trait, not any other kind of trait"
572        );
573
574        let mut target: MaybeUninit<*mut T> = MaybeUninit::zeroed();
575        unsafe {
576            ptr::copy(
577                &self as *const TraitObject as *const MaybeUninit<*mut T>,
578                &mut target as *mut MaybeUninit<*mut T>,
579                1,
580            );
581            target.assume_init()
582        }
583    }
584    /// Interpret this TraitObject as *const T.
585    /// *const T *MUST* be a fat pointer of the same type as was used to create this TraitObject
586    /// instance.
587    pub fn as_const_ptr<T: ?Sized>(self) -> *const T {
588        assert_eq!(
589            std::mem::size_of::<*const T>(),
590            16,
591            "TraitObject must only be used with dyn trait, not any other kind of trait"
592        );
593
594        let mut target: MaybeUninit<*const T> = MaybeUninit::zeroed();
595        unsafe {
596            ptr::copy(
597                &self as *const TraitObject as *const MaybeUninit<*const T>,
598                &mut target as *mut MaybeUninit<*const T>,
599                1,
600            );
601            target.assume_init()
602        }
603    }
604    /// Convert the given fat pointer to a TraitObject instance.
605    #[inline]
606    pub fn new_from_ptr<T: ?Sized>(raw: *const T) -> TraitObject {
607        debug_assert_eq!(
608            std::mem::size_of::<*const T>(),
609            16,
610            "TraitObject::new_from_ptr() must only be used with dyn trait, not any other kind of trait"
611        );
612        debug_assert_eq!(std::mem::size_of::<TraitObject>(), 16);
613
614        let mut trait_object = TraitObject::zero();
615
616        unsafe {
617            ptr::copy(
618                &raw as *const *const T,
619                &mut trait_object as *mut TraitObject as *mut *const T,
620                1,
621            )
622        };
623        trait_object
624    }
625    /// Note: This only works for boxed dyn Trait.
626    /// T must be `dyn SomeTrait`.
627    #[inline]
628    pub fn new<T: ?Sized>(input: Box<T>) -> TraitObject {
629        let raw = Box::into_raw(input);
630        debug_assert_eq!(
631            std::mem::size_of::<*mut T>(),
632            16,
633            "TraitObject::new() must only be used with Boxed dyn trait, not any other kind of Box"
634        );
635        debug_assert_eq!(std::mem::size_of::<TraitObject>(), 16);
636
637        let mut trait_object = TraitObject::zero();
638
639        unsafe {
640            ptr::copy(
641                &raw as *const *mut T,
642                &mut trait_object as *mut TraitObject as *mut *mut T,
643                1,
644            )
645        };
646        trait_object
647    }
648}
649
650/// Information about an entry point and the trait
651/// it corresponds to.
652#[derive(Debug, Clone)]
653#[repr(C)]
654pub struct AbiConnectionTemplate {
655    /// The negotiated effective serialization version.
656    /// See 'savefile' crate for more information about version handling.
657    #[doc(hidden)]
658    pub effective_version: u32,
659    /// All the methods of the trait.
660    #[doc(hidden)]
661    pub methods: &'static [AbiConnectionMethod],
662    /// The entry point which will actually be used for calls. Typically,
663    /// this entry point points into a different shared object/dll compared to
664    /// the caller.
665    #[doc(hidden)]
666    pub entry: unsafe extern "C" fn(flag: AbiProtocol),
667}
668
669/// Information about an ABI-connection.
670///
671/// I.e,
672/// a caller and callee. The caller is in one
673/// particular shared object, the callee in another.
674/// Any modifiable state is stored in this object,
675/// and the actual callee is stateless (allowing multiple
676/// different incoming 'connections').
677///
678/// The fields are public, so that they can be easily written by the
679/// proc macros. But the user should never interact with them directly,
680/// so they are marked as doc(hidden).
681#[repr(C)]
682#[derive(Debug)]
683pub struct AbiConnection<T: ?Sized> {
684    /// Cachable information about the interface
685    #[doc(hidden)]
686    pub template: AbiConnectionTemplate,
687    /// Information on whether we *own* the trait object.
688    /// If we do, we must arrange for the foreign library code to drop it when we're done.
689    /// Otherwise, we must not drop it.
690    #[doc(hidden)]
691    pub owning: Owning,
692    /// The concrete trait object for this instance.
693    /// I.e, type erased trait object in the foreign library
694    #[doc(hidden)]
695    pub trait_object: TraitObject,
696    /// Phantom, to make this valid rust (since we don't otherwise carry a T).
697    #[doc(hidden)]
698    pub phantom: PhantomData<*const T>,
699}
700unsafe impl<T: ?Sized> Sync for AbiConnection<T> where T: Sync {}
701unsafe impl<T: ?Sized> Send for AbiConnection<T> where T: Send {}
702
703/// A trait object together with its entry point
704#[repr(C)]
705#[derive(Debug)]
706pub struct PackagedTraitObject {
707    /// Type erased trait object for an ABI-exported trait
708    pub trait_object: TraitObject,
709    /// The low level entry point
710    pub entry: unsafe extern "C" fn(flag: AbiProtocol),
711}
712
713impl PackagedTraitObject {
714    /// Create a PackagedTraitObject from a `Box<T>`    . T must be a trait object.
715    /// T must implement AbiExportable, which means it has an ::ABI_ENTRY associated
716    /// type that gives the entry point.
717    pub fn new<T: AbiExportable + ?Sized>(boxed: Box<T>) -> PackagedTraitObject {
718        let trait_object = TraitObject::new(boxed);
719        let entry = T::ABI_ENTRY;
720        PackagedTraitObject { trait_object, entry }
721    }
722
723    /// Create a PackagedTraitObject from a &T. T must be a trait object.
724    /// T must implement AbiExportable, which means it has an ::ABI_ENTRY associated
725    /// type that gives the entry point.
726    /// Note, we use `*const T` here even for mutable cases, but it doesn't matter
727    /// since it's never dereferenced, it's just cast to other stuff and then finally
728    /// back to the right type.
729    #[inline]
730    pub fn new_from_ptr<T>(r: *const T) -> PackagedTraitObject
731    where
732        T: AbiExportable + ?Sized,
733    {
734        assert_eq!(std::mem::size_of::<*const T>(), 16);
735        let trait_object = TraitObject::new_from_ptr(r);
736        let entry = T::ABI_ENTRY;
737        PackagedTraitObject { trait_object, entry }
738    }
739
740    /// 'Serialize' this object. I.e, write it to a binary buffer, so that we can send it
741    /// to a foreign library.
742    pub fn serialize(self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
743        serializer.write_ptr(self.trait_object.ptr)?;
744        serializer.write_ptr(self.trait_object.vtable)?;
745        serializer.write_ptr(self.entry as *const ())?;
746
747        Ok(())
748    }
749    /// 'Deserialize' this object. I.e, read it from a binary buffer, so that we can receive it
750    /// from a foreign library.
751    ///
752    /// # Safety
753    /// The data available to read from Deserializer must be correct, and contain
754    /// a valid serialized PackagedTraitObject.
755    pub unsafe fn deserialize(
756        deserializer: &mut Deserializer<impl Read>,
757    ) -> Result<PackagedTraitObject, SavefileError> {
758        let mut trait_object = TraitObject::zero();
759        trait_object.ptr = deserializer.read_ptr()? as *mut ();
760        trait_object.vtable = deserializer.read_ptr()? as *mut ();
761        let entry = deserializer.read_ptr()? as *mut ();
762        assert_eq!(std::mem::size_of::<unsafe extern "C" fn(flag: AbiProtocol)>(), 8);
763        Ok(PackagedTraitObject {
764            trait_object,
765            entry: unsafe { std::mem::transmute::<*mut (), unsafe extern "C" fn(AbiProtocol)>(entry) },
766        })
767    }
768}
769
770impl<T: ?Sized> Drop for AbiConnection<T> {
771    fn drop(&mut self) {
772        match &self.owning {
773            Owning::Owned => unsafe {
774                (self.template.entry)(AbiProtocol::DropInstance {
775                    trait_object: self.trait_object,
776                });
777            },
778            Owning::NotOwned => {}
779        }
780    }
781}
782
783/// Helper struct carrying a pointer and length to an utf8 message.
784/// We use this instead of &str, to guard against the hypothetical possibility
785/// that the layout of &str would ever change.
786#[repr(C)]
787pub struct AbiErrorMsg {
788    /// Pointer to utf8 error message
789    pub error_msg_utf8: *const u8,
790    /// The length of the message, in bytes
791    pub len: usize,
792}
793impl AbiErrorMsg {
794    /// Attempt to convert the given data to a String.
795    /// Any invalid UTF8-chars are replaced.
796    pub fn convert_to_string(&self) -> String {
797        if self.len == 0 {
798            return "".to_string();
799        }
800        let data = unsafe { slice::from_raw_parts(self.error_msg_utf8, self.len) };
801        String::from_utf8_lossy(data).into()
802    }
803}
804
805/// The result of calling a method in a foreign library.
806#[repr(C, u8)]
807pub enum RawAbiCallResult {
808    /// Successful operation
809    Success {
810        /// A pointer to the return value, serialized
811        data: *const u8,
812        /// The size of the serialized return value, in bytes
813        len: usize,
814    },
815    /// The method that was called, panicked. Since the way panic unwinding in Rust
816    /// could change between rust-versions, we can't allow any panics to unwind
817    /// across the boundary between two different libraries.
818    Panic(AbiErrorMsg),
819    /// There was an error in the ABI-framework. This will happen if code tries
820    /// to call a method that is not actually available on the target, or if method
821    /// signatures change in non ABI-compatible ways.
822    AbiError(AbiErrorMsg),
823}
824
825/// This struct carries all information between different libraries.
826/// I.e, it is the sole carrier of information accross an FFI-boundary.
827#[repr(C, u8)]
828pub enum AbiProtocol {
829    /// Call a method on a trait object.
830    RegularCall {
831        /// Type-erased actual trait object. This is the 16 bytes o trait fat pointer.
832        trait_object: TraitObject,
833        /// For every argument, a bit '1' if said argument is a reference that can just
834        /// be binary copied, as a pointer
835        compatibility_mask: u64,
836        /// Data for parameters, possibly serialized
837        data: *const u8,
838        /// Length of parameters-data
839        data_length: usize,
840        /// Instance of type `AbiCallResult<T>`, to which the return-value callback will
841        /// write deserialized results or panic-message.
842        abi_result: *mut (),
843        /// Callback which will be called by callee in order to supply the return value
844        /// (without having to allocate heap-memory)
845        receiver: unsafe extern "C" fn(
846            outcome: *const RawAbiCallResult,
847            result_receiver: *mut (), /*Result<T,SaveFileError>>*/
848        ),
849        /// The negotiated protocol version
850        effective_version: u32,
851        /// The method to call. This is the method number using the
852        /// numbering of the callee.
853        method_number: u16,
854    },
855    /// Get callee version
856    InterrogateVersion {
857        /// The version of the callee savefile schema. This can only change if the savefile library
858        /// is upgraded.
859        schema_version_receiver: *mut u16,
860        /// The version of the data schema, on the callee.
861        abi_version_receiver: *mut u32,
862    },
863    /// Get schema
864    InterrogateMethods {
865        /// The version of the schema that the caller expects.
866        schema_version_required: u16,
867        /// The schema version that the caller expects the callee to communicate using.
868        /// I.e, if callee has a later version of the 'savefile' library, this can be used
869        /// to arrange for it to speak an older dialect. In theory, but savefile is still
870        /// involving and there is always a risk that ABI-breaks will be necessary.
871        callee_schema_version_interrogated: u32,
872        /// A pointer pointing at the location that that caller will expect the return value to be written.
873        /// Note, callee does not actually write to this, it just calls `callback`, which allows caller
874        /// to write to the result_receiver. The field is still needed here, since the `callback` is a bare function,
875        /// and cannot capture any data.
876        result_receiver: *mut (), /*Result<AbiTraitDefinition, SavefileError>*/
877        /// Called by callee to convey information back to caller.
878        /// `receiver` is place the caller will want to write the result.
879        callback: unsafe extern "C" fn(
880            receiver: *mut (), /*Result<AbiTraitDefinition, SavefileError>*/
881            callee_schema_version: u16,
882            data: *const u8,
883            len: usize,
884        ),
885    },
886    /// Create a new trait object.
887    CreateInstance {
888        /// Pointer which will receive the fat pointer to the dyn trait object, allocated on heap using Box.
889        trait_object_receiver: *mut TraitObject,
890        /// Opaque pointer to callers representation of error (String)
891        error_receiver: *mut (), /*String*/
892        /// Called by callee if instance creation fails (by panic)
893        error_callback: unsafe extern "C" fn(error_receiver: *mut (), error: *const AbiErrorMsg),
894    },
895    /// Drop a trait object.
896    DropInstance {
897        /// dyn trait fat pointer
898        trait_object: TraitObject,
899    },
900}
901
902/// Parse the given RawAbiCallResult. If it concerns a success, then deserialize a return value using the given closure.
903pub fn parse_return_value_impl<T>(
904    outcome: &RawAbiCallResult,
905    deserialize_action: impl FnOnce(&mut Deserializer<Cursor<&[u8]>>) -> Result<T, SavefileError>,
906) -> Result<T, SavefileError> {
907    match outcome {
908        RawAbiCallResult::Success { data, len } => {
909            let data = unsafe { std::slice::from_raw_parts(*data, *len) };
910            let mut reader = Cursor::new(data);
911            let file_version = reader.read_u32::<LittleEndian>()?;
912            let mut deserializer = Deserializer {
913                reader: &mut reader,
914                file_version,
915                ephemeral_state: HashMap::new(),
916            };
917            deserialize_action(&mut deserializer)
918        }
919        RawAbiCallResult::Panic(AbiErrorMsg { error_msg_utf8, len }) => {
920            let errdata = unsafe { std::slice::from_raw_parts(*error_msg_utf8, *len) };
921            Err(SavefileError::CalleePanic {
922                msg: String::from_utf8_lossy(errdata).into(),
923            })
924        }
925        RawAbiCallResult::AbiError(AbiErrorMsg { error_msg_utf8, len }) => {
926            let errdata = unsafe { std::slice::from_raw_parts(*error_msg_utf8, *len) };
927            Err(SavefileError::GeneralError {
928                msg: String::from_utf8_lossy(errdata).into(),
929            })
930        }
931    }
932}
933
934/// Parse an RawAbiCallResult instance into a `Result<Box<dyn T>, SavefileError>` .
935///
936/// This is used on the caller side, and the type T will always be statically known.
937/// TODO: There's some duplicated code here, compare parse_return_value
938pub fn parse_return_boxed_trait<T>(outcome: &RawAbiCallResult) -> Result<Box<AbiConnection<T>>, SavefileError>
939where
940    T: AbiExportable + ?Sized + 'static,
941{
942    parse_return_value_impl(outcome, |deserializer| {
943        let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? };
944        unsafe {
945            Ok(Box::new(AbiConnection::<T>::from_raw_packaged(
946                packaged,
947                Owning::Owned,
948            )?))
949        }
950    })
951}
952/// We never unload libraries which have been dynamically loaded, because of all the problems with
953/// doing so.
954static LIBRARY_CACHE: Mutex<Option<HashMap<String /*filename*/, Library>>> = Mutex::new(None);
955static ENTRY_CACHE: Mutex<
956    Option<HashMap<(String /*filename*/, String /*trait name*/), unsafe extern "C" fn(flag: AbiProtocol)>>,
957> = Mutex::new(None);
958
959static ABI_CONNECTION_TEMPLATES: Mutex<
960    Option<HashMap<(TypeId, unsafe extern "C" fn(flag: AbiProtocol)), AbiConnectionTemplate>>,
961> = Mutex::new(None);
962
963struct Guard<'a, K: Hash + Eq, V> {
964    guard: MutexGuard<'a, Option<HashMap<K, V>>>,
965}
966
967impl<K: Hash + Eq, V> std::ops::Deref for Guard<'_, K, V> {
968    type Target = HashMap<K, V>;
969    fn deref(&self) -> &HashMap<K, V> {
970        self.guard.as_ref().unwrap()
971    }
972}
973
974impl<K: Hash + Eq, V> std::ops::DerefMut for Guard<'_, K, V> {
975    fn deref_mut(&mut self) -> &mut HashMap<K, V> {
976        &mut *self.guard.as_mut().unwrap()
977    }
978}
979
980// Avoid taking a dependency on OnceCell or lazy_static or something, just for this little thing
981impl<'a, K: Hash + Eq, V> Guard<'a, K, V> {
982    pub fn lock(map: &'a Mutex<Option<HashMap<K /*filename*/, V>>>) -> Guard<'a, K, V> {
983        let mut guard = map.lock().unwrap();
984        if guard.is_none() {
985            *guard = Some(HashMap::new());
986        }
987        Guard { guard }
988    }
989}
990
991/// Helper to determine if something is owned, or not
992#[derive(Debug, Clone, Copy)]
993pub enum Owning {
994    /// The object is owned
995    Owned,
996    /// The object is not owned
997    NotOwned,
998}
999
1000const FLEX_BUFFER_SIZE: usize = 64;
1001/// Stack allocated buffer that overflows on heap if needed
1002#[doc(hidden)]
1003pub enum FlexBuffer {
1004    /// Allocated on stack>
1005    Stack {
1006        /// The current write position. This is the same as
1007        /// the logical size of the buffer, since we can only write at the end.
1008        position: usize,
1009        /// The data backing this buffer, on the stack
1010        data: MaybeUninit<[u8; FLEX_BUFFER_SIZE]>,
1011    },
1012    /// Allocated on heap
1013    Spill(Vec<u8>),
1014}
1015impl Write for FlexBuffer {
1016    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
1017        match self {
1018            FlexBuffer::Stack { position, data } => {
1019                if *position + buf.len() <= FLEX_BUFFER_SIZE {
1020                    let rawdata = data as *mut MaybeUninit<_> as *mut u8;
1021                    unsafe { ptr::copy(buf.as_ptr(), rawdata.add(*position), buf.len()) };
1022                    *position += buf.len();
1023                } else {
1024                    let mut spill = Vec::with_capacity(2 * FLEX_BUFFER_SIZE + buf.len());
1025                    let rawdata = data as *mut MaybeUninit<_> as *mut u8;
1026                    let dataslice = unsafe { slice::from_raw_parts(rawdata, *position) };
1027                    spill.extend(dataslice);
1028                    spill.extend(buf);
1029                    *self = FlexBuffer::Spill(spill);
1030                }
1031            }
1032            FlexBuffer::Spill(v) => v.extend(buf),
1033        }
1034        Ok(buf.len())
1035    }
1036
1037    fn flush(&mut self) -> std::io::Result<()> {
1038        Ok(())
1039    }
1040}
1041
1042/// Raw entry point for receiving return values from other shared libraries
1043#[doc(hidden)]
1044pub unsafe extern "C" fn abi_result_receiver<T: Deserialize>(
1045    outcome: *const RawAbiCallResult,
1046    result_receiver: *mut (),
1047) {
1048    let outcome = unsafe { &*outcome };
1049    let result_receiver = unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit<Result<T, SavefileError>>) };
1050    result_receiver.write(parse_return_value_impl(outcome, |deserializer| {
1051        T::deserialize(deserializer)
1052    }));
1053}
1054
1055/// Raw entry point for receiving return values from other shared libraries
1056#[doc(hidden)]
1057pub unsafe extern "C" fn abi_boxed_trait_receiver<T>(outcome: *const RawAbiCallResult, result_receiver: *mut ())
1058where
1059    T: AbiExportable + ?Sized + 'static,
1060{
1061    let outcome = unsafe { &*outcome };
1062    let result_receiver =
1063        unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit<Result<Box<AbiConnection<T>>, SavefileError>>) };
1064    result_receiver.write(parse_return_value_impl(outcome, |deserializer| {
1065        let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? };
1066        unsafe {
1067            Ok(Box::new(AbiConnection::<T>::from_raw_packaged(
1068                packaged,
1069                Owning::Owned,
1070            )?))
1071        }
1072    }));
1073}
1074
1075// Flex buffer is only used internally, and we don't need to provide
1076// any of the regular convenience.
1077#[allow(clippy::new_without_default)]
1078#[allow(clippy::len_without_is_empty)]
1079impl FlexBuffer {
1080    /// Create a new buffer instance, allocated from the stack
1081    pub fn new() -> FlexBuffer {
1082        FlexBuffer::Stack {
1083            position: 0,
1084            data: MaybeUninit::uninit(),
1085        }
1086    }
1087    /// Get a pointer to the buffer contents
1088    pub fn as_ptr(&self) -> *const u8 {
1089        match self {
1090            FlexBuffer::Stack { data, .. } => data as *const MaybeUninit<_> as *const u8,
1091            FlexBuffer::Spill(v) => v.as_ptr(),
1092        }
1093    }
1094    /// Get the number of bytes in the buffer
1095    pub fn len(&self) -> usize {
1096        match self {
1097            FlexBuffer::Stack { position, .. } => *position,
1098            FlexBuffer::Spill(v) => v.len(),
1099        }
1100    }
1101}
1102
1103/// Arguments are layout compatible if their native versions are layout_compatible,
1104/// or if they are traits and the effective version of the traits are compatible.
1105/// For traits, the actual fat pointer is always compatible, so can always be used.
1106/// The trait-objects themselves can never be serialized, so they can only be used as references.
1107///
1108/// b is the callee, a is the caller
1109fn arg_layout_compatible(
1110    a_native: &Schema,
1111    b_native: &Schema,
1112    a_effective: &Schema,
1113    b_effective: &Schema,
1114    effective_version: u32,
1115    is_return_position: bool,
1116) -> Result<bool, SavefileError> {
1117    match (a_native, b_native) {
1118        (Schema::Future(_a, _, _, _), Schema::Future(_b, _, _, _)) => {
1119            let (
1120                Schema::Future(effective_a2, a_send, a_sync, a_unpin),
1121                Schema::Future(effective_b2, b_send, b_sync, b_unpin),
1122            ) = (a_effective, b_effective)
1123            else {
1124                return Err(SavefileError::IncompatibleSchema {
1125                    message: "Type has changed".to_string(),
1126                });
1127            };
1128            for (a, b, bound) in [
1129                (*a_send, *b_send, "Send"),
1130                (*a_sync, *b_sync, "Sync"),
1131                (*a_unpin, *b_unpin, "Unpin"),
1132            ] {
1133                if a && !b {
1134                    return Err(SavefileError::IncompatibleSchema{message: format!(
1135                        "Caller expects a future with an {}-bound, but implementation provides one without. This is an incompatible difference.",
1136                         bound)
1137                    });
1138                }
1139            }
1140
1141            effective_a2.verify_backward_compatible(effective_version, effective_b2, is_return_position)?;
1142            Ok(true)
1143        }
1144        (Schema::FnClosure(a1, _a2), Schema::FnClosure(b1, _b2)) => {
1145            let (Schema::FnClosure(effective_a1, effective_a2), Schema::FnClosure(effective_b1, effective_b2)) =
1146                (a_effective, b_effective)
1147            else {
1148                return Err(SavefileError::IncompatibleSchema {
1149                    message: "Type has changed".to_string(),
1150                });
1151            };
1152
1153            effective_a2.verify_backward_compatible(effective_version, effective_b2, is_return_position)?;
1154            Ok(a1 == b1 && a1 == effective_a1 && a1 == effective_b1)
1155        }
1156        (Schema::Boxed(native_a), Schema::Boxed(native_b)) => {
1157            let (Schema::Boxed(effective_a2), Schema::Boxed(effective_b2)) = (a_effective, b_effective) else {
1158                return Err(SavefileError::IncompatibleSchema {
1159                    message: "Type has changed".to_string(),
1160                });
1161            };
1162            arg_layout_compatible(
1163                native_a,
1164                native_b,
1165                effective_a2,
1166                effective_b2,
1167                effective_version,
1168                is_return_position,
1169            )
1170        }
1171        (Schema::Trait(s_a, _), Schema::Trait(s_b, _)) => {
1172            if s_a != s_b {
1173                return Err(SavefileError::IncompatibleSchema {
1174                    message: "Type has changed".to_string(),
1175                });
1176            }
1177            let (Schema::Trait(e_a2, effective_a2), Schema::Trait(e_b2, effective_b2)) = (a_effective, b_effective)
1178            else {
1179                return Err(SavefileError::IncompatibleSchema {
1180                    message: "Type has changed".to_string(),
1181                });
1182            };
1183            if e_a2 != e_b2 {
1184                return Err(SavefileError::IncompatibleSchema {
1185                    message: "Type has changed".to_string(),
1186                });
1187            }
1188
1189            effective_a2.verify_backward_compatible(effective_version, effective_b2, is_return_position)?;
1190            Ok(true)
1191        }
1192        (a, b) => Ok(a.layout_compatible(b)),
1193    }
1194}
1195
1196impl<T: AbiExportable + ?Sized + 'static> AbiConnection<T> {
1197    /// Analyse the difference in definitions between the two sides,
1198    /// and create an AbiConnection
1199    #[allow(clippy::too_many_arguments)]
1200    fn analyze_and_create(
1201        trait_name: &str,
1202        remote_entry: unsafe extern "C" fn(flag: AbiProtocol),
1203        effective_version: u32,
1204        caller_effective_definition: AbiTraitDefinition,
1205        callee_effective_definition: AbiTraitDefinition,
1206        caller_native_definition: AbiTraitDefinition,
1207        callee_native_definition: AbiTraitDefinition,
1208    ) -> Result<AbiConnectionTemplate, SavefileError> {
1209        let mut methods = Vec::with_capacity(caller_native_definition.methods.len());
1210        if caller_native_definition.methods.len() > 64 {
1211            panic!("Too many method arguments, max 64 are supported!");
1212        }
1213        for caller_native_method in caller_native_definition.methods.into_iter() {
1214            let Some((callee_native_method_number, callee_native_method)) = callee_native_definition
1215                .methods
1216                .iter()
1217                .enumerate()
1218                .find(|x| x.1.name == caller_native_method.name)
1219            else {
1220                methods.push(AbiConnectionMethod {
1221                    method_name: caller_native_method.name,
1222                    caller_info: caller_native_method.info,
1223                    callee_method_number: None,
1224                    compatibility_mask: 0,
1225                });
1226                continue;
1227            };
1228
1229            let Some(callee_effective_method) = callee_effective_definition
1230                .methods
1231                .iter()
1232                .find(|x| x.name == caller_native_method.name)
1233            else {
1234                return Err(SavefileError::GeneralError {msg: format!("Internal error - missing method definition {} in signature when calculating serializable version of call (1).", caller_native_method.name)});
1235            };
1236
1237            let Some(caller_effective_method) = caller_effective_definition
1238                .methods
1239                .iter()
1240                .find(|x| x.name == caller_native_method.name)
1241            else {
1242                return Err(SavefileError::GeneralError {msg: format!("Internal error - missing method definition {} in signature when calculating serializable version of call (2).", caller_native_method.name)});
1243            };
1244
1245            if caller_native_method.info.arguments.len() != callee_native_method.info.arguments.len() {
1246                return Err(SavefileError::GeneralError {msg: format!("Number of arguments for method {} was expected by caller to be {} but was {} in implementation.", caller_native_method.name, caller_native_method.info.arguments.len(), callee_native_method.info.arguments.len())});
1247            }
1248
1249            if caller_native_method.info.arguments.len() != caller_effective_method.info.arguments.len() {
1250                return Err(SavefileError::GeneralError {
1251                    msg: format!(
1252                        "Internal error - number of arguments for method {} has differs between {} to {} (1).",
1253                        caller_native_method.name,
1254                        caller_native_method.info.arguments.len(),
1255                        caller_effective_method.info.arguments.len()
1256                    ),
1257                });
1258            }
1259
1260            if caller_native_method.info.arguments.len() != callee_effective_method.info.arguments.len() {
1261                return Err(SavefileError::GeneralError {
1262                    msg: format!(
1263                        "Internal error - number of arguments for method {} has differs between {} to {} (2).",
1264                        caller_native_method.name,
1265                        caller_native_method.info.arguments.len(),
1266                        callee_effective_method.info.arguments.len()
1267                    ),
1268                });
1269            }
1270
1271            if caller_native_method.info.arguments.len() > 64 {
1272                return Err(SavefileError::TooManyArguments);
1273            }
1274
1275            let retval_effective_schema_diff = diff_schema(
1276                &caller_effective_method.info.return_value,
1277                &callee_effective_method.info.return_value,
1278                "".to_string(),
1279                true,
1280            );
1281            if let Some(diff) = retval_effective_schema_diff {
1282                return Err(SavefileError::IncompatibleSchema {
1283                    message: format!(
1284                        "Incompatible ABI detected. Trait: {}, method: {}, return value error: {}",
1285                        trait_name, &caller_native_method.name, diff
1286                    ),
1287                });
1288            }
1289            let mut mask = 0;
1290            let mut verify_compatibility = |effective1, effective2, native1, native2, index: Option<usize>| {
1291                let effective_schema_diff = diff_schema(effective1, effective2, "".to_string(), index.is_none());
1292                if let Some(diff) = effective_schema_diff {
1293                    return Err(SavefileError::IncompatibleSchema {
1294                        message: if let Some(index) = index {
1295                            format!(
1296                                "Incompatible ABI detected. Trait: {}, method: {}, argument: #{}: {}",
1297                                trait_name, &caller_native_method.name, index, diff
1298                            )
1299                        } else {
1300                            format!(
1301                                "Incompatible ABI detected. Trait: {}, method: {}, return value differs: {}",
1302                                trait_name, &caller_native_method.name, diff
1303                            )
1304                        },
1305                    });
1306                }
1307
1308                let comp = arg_layout_compatible(
1309                    native1,
1310                    native2,
1311                    effective1,
1312                    effective2,
1313                    effective_version,
1314                    index.is_none(),
1315                )?;
1316
1317                if comp {
1318                    if let Some(index) = index {
1319                        mask |= 1 << index;
1320                    }
1321                }
1322                Ok(())
1323            };
1324
1325            for index in 0..caller_native_method.info.arguments.len() {
1326                let effective1 = &caller_effective_method.info.arguments[index].schema;
1327                let effective2 = &callee_effective_method.info.arguments[index].schema;
1328                let native1 = &caller_native_method.info.arguments[index].schema;
1329                let native2 = &callee_native_method.info.arguments[index].schema;
1330                verify_compatibility(effective1, effective2, native1, native2, Some(index))?;
1331            }
1332
1333            verify_compatibility(
1334                &caller_effective_method.info.return_value,
1335                &callee_effective_method.info.return_value,
1336                &caller_native_method.info.return_value,
1337                &callee_native_method.info.return_value,
1338                None, /*return value*/
1339            )?;
1340
1341            methods.push(AbiConnectionMethod {
1342                method_name: caller_native_method.name,
1343                caller_info: caller_native_method.info,
1344                callee_method_number: Some(callee_native_method_number as u16),
1345                compatibility_mask: mask,
1346            })
1347        }
1348
1349        Ok(AbiConnectionTemplate {
1350            effective_version,
1351            methods: Box::leak(methods.into_boxed_slice()),
1352            entry: remote_entry,
1353        })
1354    }
1355
1356    /// Gets the function pointer for the entry point of the given interface, in the given
1357    /// shared library.
1358    fn get_symbol_for(
1359        shared_library_path: &str,
1360        trait_name: &str,
1361    ) -> Result<unsafe extern "C" fn(flag: AbiProtocol), SavefileError> {
1362        let mut entry_guard = Guard::lock(&ENTRY_CACHE);
1363        let mut lib_guard = Guard::lock(&LIBRARY_CACHE);
1364
1365        if let Some(item) = entry_guard.get(&(shared_library_path.to_string(), trait_name.to_string())) {
1366            return Ok(*item);
1367        }
1368
1369        let filename = shared_library_path.to_string();
1370        let trait_name = trait_name.to_string();
1371        let library;
1372        match lib_guard.entry(filename.clone()) {
1373            Entry::Occupied(item) => {
1374                library = item.into_mut();
1375            }
1376            Entry::Vacant(vacant) => unsafe {
1377                library = vacant.insert(Library::new(&filename).map_err(|x| SavefileError::LoadLibraryFailed {
1378                    libname: filename.to_string(),
1379                    msg: x.to_string(),
1380                })?);
1381            },
1382        }
1383
1384        match entry_guard.entry((filename.clone(), trait_name.clone())) {
1385            Entry::Occupied(item) => Ok(*item.get()),
1386            Entry::Vacant(vacant) => {
1387                let symbol_name = format!("abi_entry_{}\0", trait_name);
1388                let symbol: Symbol<unsafe extern "C" fn(flag: AbiProtocol)> = unsafe {
1389                    library
1390                        .get(symbol_name.as_bytes())
1391                        .map_err(|x| SavefileError::LoadSymbolFailed {
1392                            libname: filename.to_string(),
1393                            symbol: symbol_name,
1394                            msg: x.to_string(),
1395                        })?
1396                };
1397                let func: unsafe extern "C" fn(flag: AbiProtocol) =
1398                    unsafe { std::mem::transmute(symbol.into_raw().into_raw()) };
1399                vacant.insert(func);
1400                Ok(func)
1401            }
1402        }
1403    }
1404
1405    /// Determines the name, without namespace, of the implemented
1406    /// trait.
1407    fn trait_name() -> &'static str {
1408        let n = std::any::type_name::<T>();
1409        let n = n.split("::").last().unwrap();
1410        n
1411    }
1412    /// Load the shared library given by 'filename', and find a savefile-abi-implementation of
1413    /// the trait 'T'. Returns an object that implements the
1414    ///
1415    /// # Safety
1416    /// The shared library referenced by 'filename' must be safely implemented,
1417    /// and must contain an ABI-exported implementation of T, which must be a dyn trait.
1418    /// However, this kind of guarantee is really needed for all execution of any rust code,
1419    /// so we don't mark this as unsafe. Symbols are unlikely to match by mistake.
1420    pub fn load_shared_library(filename: &str) -> Result<AbiConnection<T>, SavefileError> {
1421        let remote_entry = Self::get_symbol_for(filename, Self::trait_name())?;
1422        Self::new_internal(remote_entry, None, Owning::Owned)
1423    }
1424
1425    /// Creates an AbiConnection from a PackagedTraitObject
1426    /// This is the way the derive macro crates AbiConnection instances.
1427    ///
1428    /// # Safety
1429    /// * entry_point of `packed` must implement AbiProtocol
1430    /// * trait_object of `packed` must be a type erased trait object reference
1431    /// * owning must be correct
1432    #[doc(hidden)]
1433    pub unsafe fn from_raw_packaged(
1434        packed: PackagedTraitObject,
1435        owning: Owning,
1436    ) -> Result<AbiConnection<T>, SavefileError> {
1437        Self::from_raw(packed.entry, packed.trait_object, owning)
1438    }
1439
1440    /// Check if the given argument 'arg' in method 'method' is memory compatible such that
1441    /// it will be sent as a reference, not copied. This will depend on the memory layout
1442    /// of the code being called into. It will not change during the lifetime of an
1443    /// AbiConnector, but it may change if the target library is recompiled.
1444    pub fn get_arg_passable_by_ref(&self, method: &str, arg: usize) -> bool {
1445        if let Some(found) = self.template.methods.iter().find(|var| var.method_name == method) {
1446            let abi_method: &AbiConnectionMethod = found;
1447            if arg >= abi_method.caller_info.arguments.len() {
1448                panic!(
1449                    "Method '{}' has only {} arguments, so there is no argument #{}",
1450                    method,
1451                    abi_method.caller_info.arguments.len(),
1452                    arg
1453                );
1454            }
1455            (abi_method.compatibility_mask & (1 << (arg as u64))) != 0
1456        } else {
1457            let arg_names: Vec<_> = self.template.methods.iter().map(|x| x.method_name.as_str()).collect();
1458            panic!(
1459                "Trait has no method with name '{}'. Available methods: {}",
1460                method,
1461                arg_names.join(", ")
1462            );
1463        }
1464    }
1465
1466    /// This routine is mostly for tests.
1467    /// It allows using a raw external API entry point directly.
1468    /// This is mostly useful for internal testing of the savefile-abi-library.
1469    /// 'miri' does not support loading dynamic libraries. Using this function
1470    /// from within the same image as the implementation, can be a workaround for this.
1471    ///
1472    /// # Safety
1473    /// * entry_point must implement AbiProtocol
1474    /// * trait_object must be a type erased trait object reference
1475    /// * owning must be correct
1476    #[doc(hidden)]
1477    pub unsafe fn from_raw(
1478        entry_point: unsafe extern "C" fn(AbiProtocol),
1479        trait_object: TraitObject,
1480        owning: Owning,
1481    ) -> Result<AbiConnection<T>, SavefileError> {
1482        Self::new_internal(entry_point, Some(trait_object), owning)
1483    }
1484
1485    /// Crate a AbiConnection from an entry point and a boxed trait object.
1486    /// This is undocumented, since it's basically useless except for tests.
1487    /// If you have a Box<dyn Example>, you'd want to just use it directly,
1488    /// not make an AbiConnection wrapping it.
1489    ///
1490    /// This method is still useful during testing.
1491    ///
1492    /// # Safety
1493    ///  * The entry point must contain a correct implementation matching the type T.
1494    ///  * T must be a dyn trait object
1495    #[doc(hidden)]
1496    pub fn from_boxed_trait(trait_object: Box<T>) -> Result<AbiConnection<T>, SavefileError> {
1497        let trait_object = TraitObject::new(trait_object);
1498        Self::new_internal(T::ABI_ENTRY, Some(trait_object), Owning::Owned)
1499    }
1500
1501    /// Crate a AbiConnection from an entry point and a boxed trait object.
1502    /// This allows using a different interface trait for the backing implementation, for
1503    /// test cases which want to test version evolution.
1504    ///
1505    /// # Safety
1506    ///  * The entry point must contain a correct implementation matching the type T.
1507    ///  * T must be a dyn trait object
1508    #[doc(hidden)]
1509    pub unsafe fn from_boxed_trait_for_test<O: AbiExportable + ?Sized>(
1510        entry_point: unsafe extern "C" fn(AbiProtocol),
1511        trait_object: Box<O>,
1512    ) -> Result<AbiConnection<T>, SavefileError> {
1513        let trait_object = TraitObject::new(trait_object);
1514        Self::new_internal(entry_point, Some(trait_object), Owning::Owned)
1515    }
1516
1517    fn new_internal(
1518        remote_entry: unsafe extern "C" fn(AbiProtocol),
1519        trait_object: Option<TraitObject>,
1520        owning: Owning,
1521    ) -> Result<AbiConnection<T>, SavefileError> {
1522        let mut templates = Guard::lock(&ABI_CONNECTION_TEMPLATES);
1523
1524        let typeid = TypeId::of::<T>();
1525        // In principle, it would be enough to key 'templates' based on 'remote_entry'.
1526        // However, if we do, and the user ever uses AbiConnection<T> with the _wrong_ entry point,
1527        // we risk poisoning the cache with erroneous data.
1528        let template = match templates.entry((typeid, remote_entry)) {
1529            Entry::Occupied(template) => template.get().clone(),
1530            Entry::Vacant(vacant) => {
1531                let own_version = T::get_latest_version();
1532                let own_native_definition = T::get_definition(own_version);
1533
1534                let mut callee_abi_version = 0u32;
1535                let mut callee_schema_version = 0u16;
1536                unsafe {
1537                    (remote_entry)(AbiProtocol::InterrogateVersion {
1538                        schema_version_receiver: &mut callee_schema_version as *mut _,
1539                        abi_version_receiver: &mut callee_abi_version as *mut _,
1540                    });
1541                }
1542
1543                let effective_schema_version = callee_schema_version.min(CURRENT_SAVEFILE_LIB_VERSION);
1544                let effective_version = own_version.min(callee_abi_version);
1545
1546                let mut callee_abi_native_definition = Err(SavefileError::ShortRead); //Uust dummy-values
1547                let mut callee_abi_effective_definition = Err(SavefileError::ShortRead);
1548
1549                unsafe extern "C" fn definition_receiver(
1550                    receiver: *mut (), //Result<AbiTraitDefinition, SavefileError>,
1551                    schema_version: u16,
1552                    data: *const u8,
1553                    len: usize,
1554                ) {
1555                    let receiver = unsafe { &mut *(receiver as *mut Result<AbiTraitDefinition, SavefileError>) };
1556                    let slice = unsafe { slice::from_raw_parts(data, len) };
1557                    let mut cursor = Cursor::new(slice);
1558
1559                    *receiver = load_noschema(&mut cursor, schema_version.into());
1560                }
1561
1562                unsafe {
1563                    (remote_entry)(AbiProtocol::InterrogateMethods {
1564                        schema_version_required: effective_schema_version,
1565                        callee_schema_version_interrogated: callee_abi_version,
1566                        result_receiver: &mut callee_abi_native_definition as *mut _ as *mut _,
1567                        callback: definition_receiver,
1568                    });
1569                }
1570
1571                unsafe {
1572                    (remote_entry)(AbiProtocol::InterrogateMethods {
1573                        schema_version_required: effective_schema_version,
1574                        callee_schema_version_interrogated: effective_version,
1575                        result_receiver: &mut callee_abi_effective_definition as *mut _ as *mut _,
1576                        callback: definition_receiver,
1577                    });
1578                }
1579
1580                let callee_abi_native_definition = callee_abi_native_definition?;
1581                let callee_abi_effective_definition = callee_abi_effective_definition?;
1582
1583                let own_effective_definition = T::get_definition(effective_version);
1584                let trait_name = Self::trait_name();
1585                let template = Self::analyze_and_create(
1586                    trait_name,
1587                    remote_entry,
1588                    effective_version,
1589                    own_effective_definition,
1590                    callee_abi_effective_definition,
1591                    own_native_definition,
1592                    callee_abi_native_definition,
1593                )?;
1594                vacant.insert(template).clone()
1595            }
1596        };
1597
1598        let trait_object = if let Some(obj) = trait_object {
1599            obj
1600        } else {
1601            let mut trait_object = TraitObject::zero();
1602            let mut error_msg: String = Default::default();
1603            unsafe extern "C" fn error_callback(error_receiver: *mut (), error: *const AbiErrorMsg) {
1604                let error_msg = unsafe { &mut *(error_receiver as *mut String) };
1605                *error_msg = unsafe { &*error }.convert_to_string();
1606            }
1607            unsafe {
1608                (remote_entry)(AbiProtocol::CreateInstance {
1609                    trait_object_receiver: &mut trait_object as *mut _,
1610                    error_receiver: &mut error_msg as *mut String as *mut _,
1611                    error_callback,
1612                });
1613            }
1614
1615            if error_msg.len() > 0 {
1616                return Err(SavefileError::CalleePanic { msg: error_msg });
1617            }
1618            trait_object
1619        };
1620
1621        Ok(AbiConnection {
1622            template,
1623            owning,
1624            trait_object,
1625            phantom: PhantomData,
1626        })
1627    }
1628}
1629
1630/// Helper implementation of ABI entry point.
1631/// The actual low level `extern "C"` functions call into this.
1632/// This is an entry point meant to be used by the derive macro.
1633///
1634/// This version, the 'light version', does not support instance
1635/// creation.
1636///
1637/// # Safety
1638/// The 'AbiProtocol' protocol must only contain valid data.
1639pub unsafe fn abi_entry_light<T: AbiExportable + ?Sized>(flag: AbiProtocol) {
1640    match flag {
1641        AbiProtocol::RegularCall {
1642            trait_object,
1643            method_number,
1644            effective_version,
1645            compatibility_mask,
1646            data,
1647            data_length,
1648            abi_result,
1649            receiver,
1650        } => {
1651            let result = catch_unwind(|| {
1652                let data = unsafe { slice::from_raw_parts(data, data_length) };
1653
1654                match unsafe {
1655                    call_trait_obj::<T>(
1656                        trait_object,
1657                        method_number,
1658                        effective_version,
1659                        compatibility_mask,
1660                        data,
1661                        abi_result,
1662                        receiver,
1663                    )
1664                } {
1665                    Ok(_) => {}
1666                    Err(err) => {
1667                        let msg = format!("{:?}", err);
1668                        let err = RawAbiCallResult::AbiError(AbiErrorMsg {
1669                            error_msg_utf8: msg.as_ptr(),
1670                            len: msg.len(),
1671                        });
1672                        receiver(&err, abi_result)
1673                    }
1674                }
1675            });
1676            match result {
1677                Ok(()) => {}
1678                Err(err) => {
1679                    let msg: &str;
1680                    let temp;
1681                    if let Some(err) = err.downcast_ref::<&str>() {
1682                        msg = err;
1683                    } else {
1684                        temp = format!("{:?}", err);
1685                        msg = &temp;
1686                    }
1687                    let err = RawAbiCallResult::Panic(AbiErrorMsg {
1688                        error_msg_utf8: msg.as_ptr(),
1689                        len: msg.len(),
1690                    });
1691                    receiver(&err, abi_result)
1692                }
1693            }
1694        }
1695        AbiProtocol::InterrogateVersion {
1696            schema_version_receiver,
1697            abi_version_receiver,
1698        } => {
1699            // # SAFETY
1700            // The pointers come from another savefile-implementation, and are known to be valid
1701            unsafe {
1702                *schema_version_receiver = CURRENT_SAVEFILE_LIB_VERSION;
1703                *abi_version_receiver = <T as AbiExportable>::get_latest_version();
1704            }
1705        }
1706        AbiProtocol::InterrogateMethods {
1707            schema_version_required,
1708            callee_schema_version_interrogated,
1709            result_receiver,
1710            callback,
1711        } => {
1712            // Note! Any conforming implementation must send a 'schema_version_required' number that is
1713            // within the ability of the receiving implementation. It can interrogate this using 'AbiProtocol::InterrogateVersion'.
1714            let abi = <T as AbiExportable>::get_definition(callee_schema_version_interrogated);
1715            let mut temp = vec![];
1716            let Ok(_) = Serializer::save_noschema_internal(
1717                &mut temp,
1718                schema_version_required as u32,
1719                &abi,
1720                schema_version_required.min(CURRENT_SAVEFILE_LIB_VERSION),
1721            ) else {
1722                return;
1723            };
1724            callback(result_receiver, schema_version_required, temp.as_ptr(), temp.len());
1725        }
1726        AbiProtocol::CreateInstance {
1727            trait_object_receiver: _,
1728            error_receiver,
1729            error_callback,
1730        } => {
1731            let msg = format!("Internal error - attempt to create an instance of {} using the interface crate, not an implementation crate", std::any::type_name::<T>());
1732            let err = AbiErrorMsg {
1733                error_msg_utf8: msg.as_ptr(),
1734                len: msg.len(),
1735            };
1736            error_callback(error_receiver, &err as *const _)
1737        }
1738        AbiProtocol::DropInstance { trait_object } => unsafe {
1739            destroy_trait_obj::<T>(trait_object);
1740        },
1741    }
1742}
1743/// Helper implementation of ABI entry point.
1744/// The actual low level `extern "C"` functions call into this.
1745/// This is an entry point meant to be used by the derive macro.
1746///
1747/// This version, the 'full version', does support instance
1748/// creation.
1749///
1750/// # Safety
1751/// The 'AbiProtocol' protocol must only contain valid data.
1752pub unsafe fn abi_entry<T: AbiExportableImplementation>(flag: AbiProtocol) {
1753    match flag {
1754        AbiProtocol::CreateInstance {
1755            trait_object_receiver,
1756            error_receiver,
1757            error_callback,
1758        } => {
1759            let result = catch_unwind(|| {
1760                let obj: Box<T::AbiInterface> = T::new();
1761                let raw = Box::into_raw(obj);
1762                assert_eq!(std::mem::size_of::<*mut T::AbiInterface>(), 16);
1763                assert_eq!(std::mem::size_of::<TraitObject>(), 16);
1764
1765                let mut trait_object = TraitObject::zero();
1766
1767                unsafe {
1768                    ptr::copy(
1769                        &raw as *const *mut T::AbiInterface,
1770                        &mut trait_object as *mut TraitObject as *mut *mut T::AbiInterface,
1771                        1,
1772                    )
1773                };
1774
1775                unsafe {
1776                    *trait_object_receiver = trait_object;
1777                }
1778            });
1779            match result {
1780                Ok(_) => {}
1781                Err(err) => {
1782                    let msg: &str;
1783                    let temp;
1784                    if let Some(err) = err.downcast_ref::<&str>() {
1785                        msg = err;
1786                    } else {
1787                        temp = format!("{:?}", err);
1788                        msg = &temp;
1789                    }
1790                    let err = AbiErrorMsg {
1791                        error_msg_utf8: msg.as_ptr(),
1792                        len: msg.len(),
1793                    };
1794                    error_callback(error_receiver, &err as *const _)
1795                }
1796            }
1797        }
1798        flag => {
1799            abi_entry_light::<T::AbiInterface>(flag);
1800        }
1801    }
1802}
1803
1804/// Verify compatibility with old versions.
1805///
1806/// If files representing the given AbiExportable definition is not already present,
1807/// create one file per supported version, with the definition of the ABI.
1808/// If files are present, verify that the definition is the same as that in the files.
1809///
1810/// This allows us to detect if the data structure as we've declared it is modified
1811/// in a non-backward compatible way.
1812///
1813/// 'path' is a path where files defining the Abi schema are stored. These files
1814/// should be checked in to version control.
1815pub fn verify_compatiblity<T: AbiExportable + ?Sized>(path: &str) -> Result<(), SavefileError> {
1816    std::fs::create_dir_all(path)?;
1817    for version in 0..=T::get_latest_version() {
1818        let def = T::get_definition(version);
1819        let schema_file_name = Path::join(Path::new(path), format!("savefile_{}_{}.schema", def.name, version));
1820        if std::fs::metadata(&schema_file_name).is_ok() {
1821            let previous_schema = load_file_noschema(&schema_file_name, 1)?;
1822
1823            def.verify_backward_compatible(version, &previous_schema, false)?;
1824        } else {
1825            save_file_noschema(&schema_file_name, 1, &def)?;
1826        }
1827    }
1828    Ok(())
1829}
1830
1831#[doc(hidden)]
1832pub struct AbiWaker {
1833    #[doc(hidden)]
1834    waker: Box<dyn Fn() + Send + Sync>,
1835}
1836impl AbiWaker {
1837    pub fn new(waker: Box<dyn Fn() + Send + Sync>) -> Self {
1838        Self { waker }
1839    }
1840}
1841impl Wake for AbiWaker {
1842    fn wake(self: Arc<Self>) {
1843        (self.waker)();
1844    }
1845    fn wake_by_ref(self: &Arc<Self>) {
1846        (self.waker)();
1847    }
1848}
1849
1850#[cfg(feature = "bytes")]
1851mod bytes;