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}