rquickjs_core/
js_lifetime.rs

1use crate::{
2    atom, value::Constructor, Array, Atom, BigInt, CString, Exception, Function, Module, Object,
3    Promise, String, Symbol, Value,
4};
5
6/// The trait which signifies a type using the rquickjs `'js` lifetime trick for maintaining safety around Javascript values.
7///
8/// # Safety
9///
10/// This trait can only be implemented for types which derive a `'js` lifetime from a Javascript
11/// value, directly or indirectly.
12///
13/// All of the base Javascript types used in rquickjs like [`Value`] have a `'js` lifetime. If a
14/// type wants to contains one of those types it must define a lifetime generic. This trait is for
15/// indicating that that lifetime is one derived from a Javascript value. Rquickjs needs to know
16/// about this lifetime so that it is able to ensure safe use of types.
17///
18/// This trait can be derived with `#[derive(JsLifetime)]` in most cases, however sometimes a manual
19/// implementation is required.
20///
21/// This trait must be implemented correctly, failing to do so will make it possible to create
22/// unsound behavior. Correct implementions have the `'js` lifetime in `JsLifetime<'js>` be the
23/// same as the lifetime on the container, furthermore the associated type `Changed<'to>` is
24/// defined as the exact same type with the only difference being that the `'js` lifetime is now
25/// `'to`.
26///
27/// The following is a correct implementation of the [`JsLifetime`] trait.
28/// ```
29/// # use rquickjs::JsLifetime;
30/// struct Container<'js>(rquickjs::Object<'js>);
31///
32/// unsafe impl<'js> JsLifetime<'js> for Container<'js>{
33///     type Changed<'to> = Container<'to>;
34/// }
35/// ```
36///
37/// If a type does not have any lifetimes associated with it or all the lifetimes are `'static`
38/// then if is always save to implement `JsLifetime`.
39///
40/// See correct example for a static type below.
41/// ```
42/// # use rquickjs::JsLifetime;
43/// struct Bytes(Vec<u8>);
44///
45/// unsafe impl<'js> JsLifetime<'js> for Bytes{
46///     type Changed<'to> = Bytes;
47/// }
48///
49/// ```
50///
51///
52/// ## Incorrect examples
53///
54/// For example the following is unsound!
55/// ```no_run
56/// # use rquickjs::JsLifetime;
57/// struct Container<'js>(rquickjs::Object<'js>);
58///
59/// unsafe impl<'a,'js> JsLifetime<'js> for Container<'a>{ // WRONG LIFETIME!
60///     type Changed<'to> = Container<'to>;
61/// }
62/// ```
63/// `Container` here is defined as having a `'a` lifetime where it should be `'js`.
64///
65/// The following is also incorrect
66///
67/// ```no_run
68/// # use rquickjs::JsLifetime;
69/// // Her 'a is not derived from an Javascript value type, but instead the lifetime of a reference.
70/// struct Container<'a,'js>(&'a rquickjs::Object<'js>);
71///
72/// // Non 'js lifetime marked as a 'js lifetime. Unsound!
73/// unsafe impl<'js> JsLifetime<'js> for Container<'js, 'js>{
74///     type Changed<'to> = Container<'to,'to>;
75/// }
76/// ```
77/// The lifetime marked must be from an rquickjs type with a defined `<'js>` lifetime, it cannot be a
78/// the lifetime of reference!
79///
80pub unsafe trait JsLifetime<'js> {
81    /// The target which has the same type as a `Self` but with another lifetime `'t`
82    type Changed<'to>: 'to;
83}
84
85macro_rules! outlive_impls {
86    ($($type:ident,)*) => {
87        $(
88            unsafe impl<'js> JsLifetime<'js> for $type<'js> {
89                type Changed<'to> = $type<'to>;
90                //type Static = $type<'static>;
91            }
92        )*
93    };
94}
95
96outlive_impls! {
97    Value,
98    Symbol,
99    String,
100    CString,
101    Object,
102    Array,
103    BigInt,
104    Function,
105    Constructor,
106    Promise,
107    Exception,
108    Atom,
109}
110
111macro_rules! impl_outlive{
112    ($($($ty:ident)::+$(<$($g:ident),+>)*),*$(,)?) => {
113        $(
114            unsafe impl<'js,$($($g,)*)*> JsLifetime<'js> for $($ty)::*$(<$($g,)*>)*
115            where
116                  $($($g: JsLifetime<'js>,)*)*
117            {
118                type Changed<'to> = $($ty)::*$(<$($g::Changed<'to>,)*>)*;
119                //type Static = $($ty)::*$(<$($g::Static,)*>)*;
120            }
121        )*
122    };
123}
124
125impl_outlive!(
126    u8,
127    u16,
128    u32,
129    u64,
130    usize,
131    u128,
132    i8,
133    i16,
134    i32,
135    i64,
136    isize,
137    i128,
138    char,
139    std::string::String,
140    Vec<T>,
141    Box<T>,
142    Option<T>,
143    std::result::Result<T,E>,
144    std::backtrace::Backtrace,
145    std::cell::Cell<T>,
146    std::cell::RefCell<T>,
147    std::cell::UnsafeCell<T>,
148    std::collections::BTreeMap<K,V>,
149    std::collections::BTreeSet<K>,
150    std::collections::BinaryHeap<K>,
151    std::collections::HashMap<K,V>,
152    std::collections::HashSet<K>,
153    std::collections::LinkedList<T>,
154    std::collections::VecDeque<T>,
155    std::ffi::CString,
156    std::ffi::OsString,
157    std::ops::Range<T>,
158    std::ops::RangeFrom<T>,
159    std::ops::RangeFull,
160    std::ops::RangeInclusive<T>,
161    std::ops::RangeTo<T>,
162    std::ops::RangeToInclusive<T>,
163    std::ops::Bound<T>,
164    std::ops::ControlFlow<B,C>,
165    std::process::Child,
166    std::process::Command,
167    std::process::ExitCode,
168    std::process::ExitStatus,
169    std::process::Output,
170    std::process::Stdio,
171    std::path::PathBuf,
172    std::rc::Rc<T>,
173    std::sync::Arc<T>,
174    std::sync::Mutex<T>,
175    std::sync::RwLock<T>,
176    atom::PredefinedAtom,
177);
178
179unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Module<'js, T> {
180    type Changed<'to> = Module<'to, T::Changed<'to>>;
181}
182
183unsafe impl<'js> JsLifetime<'js> for () {
184    type Changed<'to> = ();
185}