]> git.proxmox.com Git - perlmod.git/blame - perlmod/src/ffi.rs
add 'pseudo_block' helper
[perlmod.git] / perlmod / src / ffi.rs
CommitLineData
7a433bb6 1//! Unsafe ffi code.
f7cc8c37
WB
2//!
3//! You should not use this code directly. This is used by the binding generator to implement xsubs
4//! for exported functions.
5
8a062e94 6/// Raw perl subroutine pointer value. This should not be used directly.
f7cc8c37
WB
7#[repr(C)]
8pub struct CV {
9 _ffi: usize,
10}
11
8a062e94 12/// Raw scalar-ish perl value. This should not be used directly.
f7cc8c37
WB
13#[repr(C)]
14pub struct SV {
15 _ffi: usize,
16}
17
8a062e94 18/// Raw perl array value. This should not be used directly.
f7cc8c37
WB
19#[repr(C)]
20pub struct AV {
21 _ffi: usize,
22}
23
8a062e94 24/// Raw perl hash value. This should not be used directly.
f7cc8c37
WB
25#[repr(C)]
26pub struct HV {
27 _ffi: usize,
28}
29
8a062e94 30/// Raw perl hash entry iterator. This should not be used directly.
f7cc8c37
WB
31#[repr(C)]
32pub struct HE {
33 _ffi: usize,
34}
35
36// in our glue:
37#[link(name = "glue", kind = "static")]
38extern "C" {
39 pub fn RSPL_StackMark_count(this: usize) -> usize;
40
41 pub fn RSPL_stack_get(offset: usize) -> *mut SV;
42
43 pub fn RSPL_croak_sv(sv: *mut SV) -> !;
44 pub fn RSPL_SvNV(sv: *mut SV) -> f64;
45 pub fn RSPL_SvIV(sv: *mut SV) -> isize;
46 pub fn RSPL_SvPVutf8(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
47 pub fn RSPL_SvPV(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
48 pub fn RSPL_SvPVbyte(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
49 pub fn RSPL_sv_2mortal(sv: *mut SV) -> *mut SV;
50 pub fn RSPL_get_undef() -> *mut SV;
51 pub fn RSPL_get_yes() -> *mut SV;
52 pub fn RSPL_get_no() -> *mut SV;
53 pub fn RSPL_pop_markstack_ptr() -> usize;
54 pub fn RSPL_stack_resize_by(count: isize);
55 pub fn RSPL_stack_shrink_to(count: usize);
56 pub fn RSPL_stack_sp() -> *mut *mut SV;
57 pub fn RSPL_newRV_inc(sv: *mut SV) -> *mut SV;
58 pub fn RSPL_newSViv(v: isize) -> *mut SV;
59 pub fn RSPL_newSVuv(v: usize) -> *mut SV;
60 pub fn RSPL_newSVnv(v: f64) -> *mut SV;
61 pub fn RSPL_newSVpvn(v: *const libc::c_char, len: libc::size_t) -> *mut SV;
62 pub fn RSPL_SvREFCNT_inc(sv: *mut SV) -> *mut SV;
63 pub fn RSPL_SvREFCNT_dec(sv: *mut SV);
64 pub fn RSPL_is_reference(sv: *mut SV) -> bool;
65 pub fn RSPL_dereference(sv: *mut SV) -> *mut SV;
66 pub fn RSPL_is_array(sv: *mut SV) -> bool;
67 pub fn RSPL_is_hash(sv: *mut SV) -> bool;
68 pub fn RSPL_type_flags(sv: *mut SV) -> u32;
69 pub fn RSPL_svtype(sv: *mut SV) -> u32;
70 pub fn RSPL_SvTRUE(sv: *mut SV) -> bool;
71
72 pub fn RSPL_newAV() -> *mut AV;
73 pub fn RSPL_av_extend(av: *mut AV, len: libc::ssize_t);
74 pub fn RSPL_av_push(av: *mut AV, sv: *mut SV);
75 pub fn RSPL_av_pop(av: *mut AV) -> *mut SV;
76 pub fn RSPL_av_len(av: *mut AV) -> usize;
77 pub fn RSPL_av_fetch(av: *mut AV, index: libc::ssize_t, lval: i32) -> *mut *mut SV;
78
79 pub fn RSPL_newHV() -> *mut HV;
80 pub fn RSPL_HvTOTALKEYS(hv: *mut HV) -> usize;
81 pub fn RSPL_hv_fetch(
82 hv: *mut HV,
83 key: *const libc::c_char,
84 klen: i32,
85 lval: i32,
86 ) -> *mut *mut SV;
87 /// Always consumes ownership of `value`.
88 pub fn RSPL_hv_store(hv: *mut HV, key: *const libc::c_char, klen: i32, value: *mut SV) -> bool;
89 pub fn RSPL_hv_store_ent(hv: *mut HV, key: *mut SV, value: *mut SV) -> bool;
90 pub fn RSPL_hv_iterinit(hv: *mut HV);
91 pub fn RSPL_hv_iternextsv(
92 hv: *mut HV,
93 key: *mut *mut libc::c_char,
94 retlen: *mut i32,
95 ) -> *mut SV;
96 pub fn RSPL_hv_iternext(hv: *mut HV) -> *mut HE;
97 pub fn RSPL_hv_iterkeysv(he: *mut HE) -> *mut SV;
98 pub fn RSPL_hv_iterval(hv: *mut HV, he: *mut HE) -> *mut SV;
87c10237
WB
99
100 pub fn RSPL_gv_stashsv(name: *const SV, flags: i32) -> *mut HV;
101 pub fn RSPL_sv_bless(sv: *mut SV, stash: *mut HV) -> *mut SV;
ccde914a
WB
102
103 pub fn RSPL_ENTER();
104 pub fn RSPL_SAVETMPS();
105 pub fn RSPL_FREETMPS();
106 pub fn RSPL_LEAVE();
f7cc8c37
WB
107}
108
109/// Argument marker for the stack.
110pub struct StackMark(usize);
111
112impl StackMark {
113 pub fn count(&self) -> usize {
114 unsafe { RSPL_StackMark_count(self.0) }
115 }
116
117 pub fn iter(&self) -> StackIter {
118 StackIter {
119 at: self.0 + 1,
120 end: self.0 + 1 + self.count(),
121 }
122 }
123
f0475cd5
WB
124 /// Shrink the perl stack to this mark.
125 ///
126 /// # Safety
127 ///
128 /// This is only valid if the mark is still valid (smaller than `PL_stack_sp`) and all values
129 /// still remaining on the stack are mortal (which should normally be the case anyway).
f7cc8c37
WB
130 pub unsafe fn set_stack(self) {
131 RSPL_stack_shrink_to(self.0);
132 }
133}
134
8a062e94 135/// Iterator over the stack up to the [`StackMark`].
f7cc8c37
WB
136pub struct StackIter {
137 at: usize,
138 end: usize,
139}
140
141impl Iterator for StackIter {
142 type Item = crate::Scalar;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 let at = self.at;
146 if at == self.end {
147 return None;
148 }
149 unsafe {
150 let ptr = RSPL_stack_get(self.at);
151 self.at += 1;
152 if ptr.is_null() {
153 None
154 } else {
155 Some(crate::Scalar::from_raw_ref(ptr))
156 }
157 }
158 }
159}
160
f0475cd5
WB
161/// Pop the current argument marker off of the argument marker stack.
162///
163/// # Safety
164///
165/// Read up on `PL_markstack_ptr` in perlguts. This is equivalent to `*PL_markstack_ptr--` in C.
f7cc8c37
WB
166pub unsafe fn pop_arg_mark() -> StackMark {
167 StackMark(RSPL_pop_markstack_ptr())
168}
169
f0475cd5
WB
170/// Push a value to the stack.
171///
172/// # Safety
173///
174/// Read up on mortals and the stack and when it is legal to put a value onto it. Typically a
175/// mortal value with no more references to it to avoid leaking if they aren't used later on.
f7cc8c37
WB
176pub unsafe fn stack_push_raw(value: *mut SV) {
177 RSPL_stack_resize_by(1);
178 *RSPL_stack_sp() = value;
179}
180
181pub fn stack_push(value: crate::Mortal) {
182 unsafe {
183 stack_push_raw(value.into_raw());
184 }
185}
186
f0475cd5
WB
187/// This calls perl's `croak_sv`.
188///
189/// # Safety
190///
191/// This seems to perform a `longjmp` and is thus never truly safe in rust code. You really want to
192/// limit this to the top entry point of your rust call stack in a separate `extern "C" fn` where
193/// no rust values with `Drop` handlers or anything similar are active.
194///
195/// The `perlmod_macro`'s `export` attribute typically creates 2 wrapper functions of the form:
196///
197/// ```no_run
198/// # use serde::Serialize;
199///
200/// # #[derive(Serialize)]
201/// # struct Output;
202///
203/// # fn code_to_extract_parameters() {}
893f7299 204/// # fn actual_rust_function(_arg: ()) -> Result<Output, String> { Ok(Output) }
f0475cd5
WB
205/// #[no_mangle]
206/// pub extern "C" fn exported_name(cv: &::perlmod::ffi::CV) {
207/// unsafe {
208/// match private_implementation_name(cv) {
209/// Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
210/// Err(sv) => ::perlmod::ffi::croak(sv),
211/// }
212/// }
213/// }
214///
215/// #[inline(never)]
216/// fn private_implementation_name(
217/// _cv: &::perlmod::ffi::CV,
218/// ) -> Result<*mut ::perlmod::ffi::SV, *mut ::perlmod::ffi::SV> {
219/// let args = code_to_extract_parameters();
220/// // ...
221/// let result = match actual_rust_function(args) {
222/// Ok(output) => output,
223/// Err(err) => {
224/// return Err(::perlmod::Value::new_string(&err.to_string())
225/// .into_mortal()
226/// .into_raw());
227/// }
228/// };
229///
230/// match ::perlmod::to_value(&result) {
231/// Ok(value) => Ok(value.into_mortal().into_raw()),
232/// Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
233/// .into_mortal()
234/// .into_raw()),
235/// }
236/// }
237/// ```
238pub unsafe fn croak(sv: *mut SV) -> ! {
f7cc8c37
WB
239 RSPL_croak_sv(sv);
240}
ccde914a
WB
241
242/// Create a pseudo-block for mortals & temps to be freed after it.
243/// This calls `ENTER; SAVETMPS;` before and `FREETMPS; LEAVE;` after the provided closure.
244pub fn pseudo_block<F, R>(func: F) -> R
245where
246 F: FnOnce() -> R,
247{
248 unsafe {
249 RSPL_ENTER();
250 RSPL_SAVETMPS();
251 }
252
253 let res = func();
254
255 unsafe {
256 RSPL_FREETMPS();
257 RSPL_LEAVE();
258 }
259
260 res
261}