rquickjs_core/
js_lifetime.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use crate::{
    atom, value::Constructor, Array, Atom, BigInt, Exception, Function, Module, Object, Promise,
    String, Symbol, Value,
};

/// The trait which signifies a type using the rquickjs `'js` lifetime trick for maintaining safety around Javascript values.
///
/// # Safety
///
/// This trait can only be implemented for types which derive a `'js` lifetime from a Javascript
/// value, directly or indirectly.
///
/// All of the base Javascript types used in rquickjs like [`Value`] have a `'js` lifetime. If a
/// type wants to contains one of those types it must define a lifetime generic. This trait is for
/// indicating that that lifetime is one derived from a Javascript value. Rquickjs needs to know
/// about this lifetime so that it is able to ensure safe use of types.
///
/// This trait can be derived with `#[derive(JsLifetime)]` in most cases, however sometimes a manual
/// implementation is required.
///
/// This trait must be implemented correctly, failing to do so will make it possible to create
/// unsound behavior. Correct implementions have the `'js` lifetime in `JsLifetime<'js>` be the
/// same as the lifetime on the container, furthermore the associated type `Changed<'to>` is
/// defined as the exact same type with the only difference being that the `'js` lifetime is now
/// `'to`.
///
/// The following is a correct implementation of the [`JsLifetime`] trait.
/// ```
/// # use rquickjs::JsLifetime;
/// struct Container<'js>(rquickjs::Object<'js>);
///
/// unsafe impl<'js> JsLifetime<'js> for Container<'js>{
///     type Changed<'to> = Container<'to>;
/// }
/// ```
///
/// If a type does not have any lifetimes associated with it or all the lifetimes are `'static`
/// then if is always save to implement `JsLifetime`.
///
/// See correct example for a static type below.
/// ```
/// # use rquickjs::JsLifetime;
/// struct Bytes(Vec<u8>);
///
/// unsafe impl<'js> JsLifetime<'js> for Bytes{
///     type Changed<'to> = Bytes;
/// }
///
/// ```
///
///
/// ## Incorrect examples
///
/// For example the following is unsound!
/// ```no_run
/// # use rquickjs::JsLifetime;
/// struct Container<'js>(rquickjs::Object<'js>);
///
/// unsafe impl<'a,'js> JsLifetime<'js> for Container<'a>{ // WRONG LIFETIME!
///     type Changed<'to> = Container<'to>;
/// }
/// ```
/// `Container` here is defined as having a `'a` lifetime where it should be `'js`.
///
/// The following is also incorrect
///
/// ```no_run
/// # use rquickjs::JsLifetime;
/// // Her 'a is not derived from an Javascript value type, but instead the lifetime of a reference.
/// struct Container<'a,'js>(&'a rquickjs::Object<'js>);
///
/// // Non 'js lifetime marked as a 'js lifetime. Unsound!
/// unsafe impl<'js> JsLifetime<'js> for Container<'js, 'js>{
///     type Changed<'to> = Container<'to,'to>;
/// }
/// ```
/// The lifetime marked must be from an rquickjs type with a defined `<'js>` lifetime, it cannot be a
/// the lifetime of reference!
///
pub unsafe trait JsLifetime<'js> {
    /// The target which has the same type as a `Self` but with another lifetime `'t`
    type Changed<'to>: 'to;
}

macro_rules! outlive_impls {
    ($($type:ident,)*) => {
        $(
            unsafe impl<'js> JsLifetime<'js> for $type<'js> {
                type Changed<'to> = $type<'to>;
                //type Static = $type<'static>;
            }
        )*
    };
}

outlive_impls! {
    Value,
    Symbol,
    String,
    Object,
    Array,
    BigInt,
    Function,
    Constructor,
    Promise,
    Exception,
    Atom,
}

macro_rules! impl_outlive{
    ($($($ty:ident)::+$(<$($g:ident),+>)*),*$(,)?) => {
        $(
            unsafe impl<'js,$($($g,)*)*> JsLifetime<'js> for $($ty)::*$(<$($g,)*>)*
            where
                  $($($g: JsLifetime<'js>,)*)*
            {
                type Changed<'to> = $($ty)::*$(<$($g::Changed<'to>,)*>)*;
                //type Static = $($ty)::*$(<$($g::Static,)*>)*;
            }
        )*
    };
}

impl_outlive!(
    u8,
    u16,
    u32,
    u64,
    usize,
    u128,
    i8,
    i16,
    i32,
    i64,
    isize,
    i128,
    char,
    std::string::String,
    Vec<T>,
    Box<T>,
    Option<T>,
    std::result::Result<T,E>,
    std::backtrace::Backtrace,
    std::cell::Cell<T>,
    std::cell::RefCell<T>,
    std::cell::UnsafeCell<T>,
    std::collections::BTreeMap<K,V>,
    std::collections::BTreeSet<K>,
    std::collections::BinaryHeap<K>,
    std::collections::HashMap<K,V>,
    std::collections::HashSet<K>,
    std::collections::LinkedList<T>,
    std::collections::VecDeque<T>,
    std::ffi::CString,
    std::ffi::OsString,
    std::ops::Range<T>,
    std::ops::RangeFrom<T>,
    std::ops::RangeFull,
    std::ops::RangeInclusive<T>,
    std::ops::RangeTo<T>,
    std::ops::RangeToInclusive<T>,
    std::ops::Bound<T>,
    std::ops::ControlFlow<B,C>,
    std::process::Child,
    std::process::Command,
    std::process::ExitCode,
    std::process::ExitStatus,
    std::process::Output,
    std::process::Stdio,
    std::path::PathBuf,
    std::rc::Rc<T>,
    std::sync::Arc<T>,
    std::sync::Mutex<T>,
    std::sync::RwLock<T>,
    atom::PredefinedAtom,
);

unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Module<'js, T> {
    type Changed<'to> = Module<'to, T::Changed<'to>>;
}