rquickjs_core/js_lifetime.rs
1use crate::{
2 atom, value::Constructor, Array, Atom, BigInt, CString, Exception, Function, Module, Object,
3 Promise, Proxy, 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 Proxy,
108 Exception,
109 Atom,
110}
111
112macro_rules! impl_outlive{
113 ($($($ty:ident)::+$(<$($g:ident),+>)*),*$(,)?) => {
114 $(
115 unsafe impl<'js,$($($g,)*)*> JsLifetime<'js> for $($ty)::*$(<$($g,)*>)*
116 where
117 $($($g: JsLifetime<'js>,)*)*
118 {
119 type Changed<'to> = $($ty)::*$(<$($g::Changed<'to>,)*>)*;
120 //type Static = $($ty)::*$(<$($g::Static,)*>)*;
121 }
122 )*
123 };
124}
125
126impl_outlive!(
127 u8,
128 u16,
129 u32,
130 u64,
131 usize,
132 u128,
133 i8,
134 i16,
135 i32,
136 i64,
137 isize,
138 i128,
139 char,
140 alloc::string::String,
141 alloc::vec::Vec<T>,
142 alloc::boxed::Box<T>,
143 core::option::Option<T>,
144 core::result::Result<T,E>,
145 core::cell::Cell<T>,
146 core::cell::RefCell<T>,
147 core::cell::UnsafeCell<T>,
148 alloc::collections::BTreeMap<K,V>,
149 alloc::collections::BTreeSet<K>,
150 alloc::collections::BinaryHeap<K>,
151 alloc::collections::LinkedList<T>,
152 alloc::collections::VecDeque<T>,
153 alloc::ffi::CString,
154 core::ops::Range<T>,
155 core::ops::RangeFrom<T>,
156 core::ops::RangeFull,
157 core::ops::RangeInclusive<T>,
158 core::ops::RangeTo<T>,
159 core::ops::RangeToInclusive<T>,
160 core::ops::Bound<T>,
161 core::ops::ControlFlow<B,C>,
162 alloc::rc::Rc<T>,
163 alloc::sync::Arc<T>,
164 atom::PredefinedAtom,
165);
166
167#[cfg(feature = "std")]
168impl_outlive!(
169 std::backtrace::Backtrace,
170 std::collections::HashMap<K,V>,
171 std::collections::HashSet<K>,
172 std::ffi::OsString,
173 std::process::Child,
174 std::process::Command,
175 std::process::ExitCode,
176 std::process::ExitStatus,
177 std::process::Output,
178 std::process::Stdio,
179 std::path::PathBuf,
180 std::sync::Mutex<T>,
181 std::sync::RwLock<T>,
182);
183
184unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Module<'js, T> {
185 type Changed<'to> = Module<'to, T::Changed<'to>>;
186}
187
188unsafe impl<'js> JsLifetime<'js> for () {
189 type Changed<'to> = ();
190}