]>
Commit | Line | Data |
---|---|---|
7a433bb6 WB |
1 | //! Module containing the [`Scalar`] and [`Mortal`] types. |
2 | ||
e62be4a7 WB |
3 | use std::convert::TryInto; |
4 | use std::mem; | |
5 | ||
f7cc8c37 WB |
6 | use bitflags::bitflags; |
7 | ||
8 | use crate::ffi::{self, SV}; | |
e62be4a7 | 9 | use crate::Error; |
f7cc8c37 WB |
10 | use crate::Value; |
11 | ||
12 | /// An owned reference to a perl value. | |
13 | /// | |
14 | /// This keeps a reference to a value which lives in the perl interpreter. | |
1182e7f5 WB |
15 | /// This derefs to a [`ScalarRef`] which implements most of the basic functionality common to all |
16 | /// [`SV`] related types. | |
f7cc8c37 WB |
17 | #[repr(transparent)] |
18 | pub struct Scalar(*mut SV); | |
19 | ||
f7cc8c37 WB |
20 | impl Scalar { |
21 | /// Turn this into a "mortal" value. This will move this value's owned reference onto the | |
22 | /// mortal stack to be cleaned up after the next perl statement if no more references exist. | |
23 | /// | |
24 | /// (To be garbage collected after this perl-statement.) | |
25 | pub fn into_mortal(self) -> Mortal { | |
26 | Mortal(unsafe { ffi::RSPL_sv_2mortal(self.into_raw()) }) | |
27 | } | |
28 | ||
1182e7f5 | 29 | /// Turn this into a raw [`SV`] transferring control of one reference count. |
f7cc8c37 WB |
30 | pub fn into_raw(self) -> *mut SV { |
31 | let ptr = self.0; | |
32 | core::mem::forget(self); | |
33 | ptr | |
34 | } | |
35 | ||
1182e7f5 WB |
36 | /// Create a wrapping [`Scalar`] from an [`SV`] pointer. The [`Scalar`] takes over the owned |
37 | /// reference from the passed [`SV`], which means it must not be a mortal reference. | |
f7cc8c37 WB |
38 | /// |
39 | /// # Safety | |
40 | /// | |
41 | /// This does not change the value's reference count, it is assumed that we're taking ownership | |
42 | /// of one reference. | |
43 | /// | |
44 | /// The caller must ensure that it is safe to decrease the reference count later on, or use | |
1182e7f5 | 45 | /// [`into_raw()`](Scalar::into_raw) instead of letting the [`Scalar`] get dropped. |
f7cc8c37 WB |
46 | pub unsafe fn from_raw_move(ptr: *mut SV) -> Self { |
47 | Self(ptr) | |
48 | } | |
49 | ||
1182e7f5 | 50 | /// Increase the reference count on an [`SV`] pointer. |
f7cc8c37 WB |
51 | /// |
52 | /// # Safety | |
53 | /// | |
54 | /// The caller may still need to decrease the reference count for the `ptr` source value. | |
55 | pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self { | |
56 | Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr)) | |
57 | } | |
58 | ||
59 | /// Create a reference to `PL_sv_undef`. | |
60 | pub fn new_undef() -> Self { | |
61 | unsafe { Self::from_raw_ref(ffi::RSPL_get_undef()) } | |
62 | } | |
63 | ||
64 | /// Create a reference to `PL_sv_yes`. | |
65 | pub fn new_yes() -> Self { | |
66 | unsafe { Self::from_raw_ref(ffi::RSPL_get_yes()) } | |
67 | } | |
68 | ||
69 | /// Create a reference to `PL_sv_no`. | |
70 | pub fn new_no() -> Self { | |
71 | unsafe { Self::from_raw_ref(ffi::RSPL_get_no()) } | |
72 | } | |
73 | ||
74 | /// Create a new integer value: | |
75 | pub fn new_int(v: isize) -> Self { | |
76 | unsafe { Self::from_raw_move(ffi::RSPL_newSViv(v)) } | |
77 | } | |
78 | ||
79 | /// Create a new unsigned integer value: | |
80 | pub fn new_uint(v: usize) -> Self { | |
81 | unsafe { Self::from_raw_move(ffi::RSPL_newSVuv(v)) } | |
82 | } | |
83 | ||
84 | /// Create a new floating point value. | |
85 | pub fn new_float(v: f64) -> Self { | |
86 | unsafe { Self::from_raw_move(ffi::RSPL_newSVnv(v)) } | |
87 | } | |
88 | ||
89 | /// Create a new string value. | |
90 | pub fn new_string(s: &str) -> Self { | |
3f791730 WB |
91 | if s.as_bytes().iter().any(|&b| b >= 0x80) { |
92 | unsafe { | |
93 | Self::from_raw_move(ffi::RSPL_newSVpvn_utf8( | |
94 | s.as_bytes().as_ptr() as *const libc::c_char, | |
95 | s.as_bytes().len() as libc::size_t, | |
96 | )) | |
97 | } | |
98 | } else { | |
99 | Self::new_bytes(s.as_bytes()) | |
100 | } | |
f7cc8c37 WB |
101 | } |
102 | ||
103 | /// Create a new byte string. | |
104 | pub fn new_bytes(s: &[u8]) -> Self { | |
105 | unsafe { | |
106 | Self::from_raw_move(ffi::RSPL_newSVpvn( | |
107 | s.as_ptr() as *const libc::c_char, | |
108 | s.len() as libc::size_t, | |
109 | )) | |
110 | } | |
111 | } | |
e62be4a7 WB |
112 | |
113 | /// Convenience method to create a new raw pointer value. Note that pointers are stored as | |
114 | /// arbitrary "byte strings" and any such byte string value can be interpreted as a raw pointer. | |
115 | pub fn new_pointer<T>(s: *mut T) -> Self { | |
116 | Self::new_bytes(&(s as usize).to_ne_bytes()) | |
117 | } | |
f7cc8c37 WB |
118 | } |
119 | ||
120 | impl Drop for Scalar { | |
121 | fn drop(&mut self) { | |
122 | unsafe { | |
123 | ffi::RSPL_SvREFCNT_dec(self.sv()); | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | impl core::ops::Deref for Scalar { | |
129 | type Target = ScalarRef; | |
130 | ||
131 | fn deref(&self) -> &Self::Target { | |
132 | unsafe { &*(self.0 as *mut ScalarRef) } | |
133 | } | |
134 | } | |
135 | ||
136 | impl core::ops::DerefMut for Scalar { | |
137 | fn deref_mut(&mut self) -> &mut Self::Target { | |
138 | unsafe { &mut *(self.0 as *mut ScalarRef) } | |
139 | } | |
140 | } | |
141 | ||
7a433bb6 | 142 | /// A value which has been pushed to perl's "mortal stack". |
f7cc8c37 WB |
143 | #[repr(transparent)] |
144 | pub struct Mortal(*mut SV); | |
145 | ||
146 | impl Mortal { | |
147 | /// Get the inner value. | |
148 | pub fn into_raw(self) -> *mut SV { | |
149 | self.0 | |
150 | } | |
151 | } | |
152 | ||
153 | impl core::ops::Deref for Mortal { | |
154 | type Target = ScalarRef; | |
155 | ||
156 | fn deref(&self) -> &Self::Target { | |
157 | unsafe { &*(self.0 as *mut ScalarRef) } | |
158 | } | |
159 | } | |
160 | ||
161 | impl core::ops::DerefMut for Mortal { | |
162 | fn deref_mut(&mut self) -> &mut Self::Target { | |
163 | unsafe { &mut *(self.0 as *mut ScalarRef) } | |
164 | } | |
165 | } | |
166 | ||
167 | pub struct ScalarRef; | |
168 | ||
169 | bitflags! { | |
170 | /// Represents the types a `Value` can contain. Values can usually contain multiple scalar types | |
171 | /// at once and it is unclear which is the "true" type, so we can only check whether a value | |
172 | /// contains something, not what it is originally meant to be! | |
173 | /// | |
174 | /// NOTE: The values must be the same as in our c glue code! | |
175 | pub struct Flags: u8 { | |
176 | const INTEGER = 1; | |
177 | const DOUBLE = 2; | |
178 | const STRING = 4; | |
179 | } | |
180 | } | |
181 | ||
182 | /// While scalar types aren't clearly different from another, complex types are, so we do | |
183 | /// distinguish between these: | |
184 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | |
185 | pub enum Type { | |
186 | Scalar(Flags), | |
187 | Reference, | |
188 | Array, | |
189 | Hash, | |
190 | Other(u8), | |
191 | } | |
192 | ||
193 | impl ScalarRef { | |
194 | pub(crate) fn sv(&self) -> *mut SV { | |
195 | self as *const ScalarRef as *const SV as *mut SV | |
196 | } | |
197 | ||
b5c53f3d | 198 | fn get_type(sv: *mut SV) -> Type { |
f7cc8c37 | 199 | unsafe { |
9acdb782 WB |
200 | if !ffi::RSPL_is_defined(sv) { |
201 | return Type::Scalar(Flags::empty()); | |
202 | } | |
203 | ||
b5c53f3d WB |
204 | // These are simple: |
205 | if ffi::RSPL_is_reference(sv) { | |
206 | return Type::Reference; | |
207 | } else if ffi::RSPL_is_array(sv) { | |
208 | return Type::Array; | |
209 | } else if ffi::RSPL_is_hash(sv) { | |
210 | return Type::Hash; | |
211 | } | |
212 | ||
213 | // Scalars have flags: | |
214 | let flags = ffi::RSPL_type_flags(sv); | |
215 | if flags != 0 { | |
216 | return Type::Scalar(Flags::from_bits_truncate(flags as u8)); | |
217 | } | |
218 | ||
b5c53f3d WB |
219 | let ty = ffi::RSPL_svtype(sv); |
220 | if ty == 0 { | |
221 | // Looks like undef | |
3d5b075b | 222 | Type::Scalar(Flags::empty()) |
b5c53f3d WB |
223 | } else if ty == ffi::RSPL_PVLV() { |
224 | // We don't support all kinds of magic, but some lvalues are simple: | |
225 | // Try to GET the value and then check for definedness. | |
226 | ffi::RSPL_SvGETMAGIC(sv); | |
227 | if !ffi::RSPL_SvOK(sv) { | |
228 | // This happens when the value points to a non-existing hash element we could | |
229 | // auto-vivify, but we won't: | |
230 | return Type::Scalar(Flags::empty()); | |
f7cc8c37 | 231 | } |
b5c53f3d WB |
232 | |
233 | // Otherwise we just try to "recurse", which will work for substrings. | |
3d5b075b | 234 | Self::get_type(ffi::RSPL_LvTARG(sv)) |
b5c53f3d | 235 | } else { |
3d5b075b | 236 | Type::Other(ty as u8) |
f7cc8c37 | 237 | } |
3d5b075b | 238 | } |
f7cc8c37 WB |
239 | } |
240 | ||
b5c53f3d WB |
241 | /// Get some information about the value's type. |
242 | pub fn ty(&self) -> Type { | |
243 | Self::get_type(self.sv()) | |
61143f5d WB |
244 | } |
245 | ||
f7cc8c37 WB |
246 | /// Dereference this reference. |
247 | pub fn dereference(&self) -> Option<Scalar> { | |
248 | let ptr = unsafe { ffi::RSPL_dereference(self.sv()) }; | |
249 | if ptr.is_null() { | |
250 | None | |
251 | } else { | |
252 | Some(unsafe { Scalar::from_raw_ref(ptr) }) | |
253 | } | |
254 | } | |
255 | ||
1182e7f5 | 256 | /// Coerce to a double value. (perlxs `SvNV`). |
f7cc8c37 WB |
257 | pub fn nv(&self) -> f64 { |
258 | unsafe { ffi::RSPL_SvNV(self.sv()) } | |
259 | } | |
260 | ||
1182e7f5 | 261 | /// Coerce to an integer value. (perlxs `SvIV`). |
f7cc8c37 WB |
262 | pub fn iv(&self) -> isize { |
263 | unsafe { ffi::RSPL_SvIV(self.sv()) } | |
264 | } | |
265 | ||
1182e7f5 | 266 | /// Coerce to an utf8 string value. (perlxs `SvPVutf8`) |
83147b11 | 267 | pub fn pv_string_utf8(&self) -> &str { |
f7cc8c37 WB |
268 | unsafe { |
269 | let mut len: libc::size_t = 0; | |
270 | let ptr = ffi::RSPL_SvPVutf8(self.sv(), &mut len) as *const u8; | |
271 | std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len)) | |
272 | } | |
273 | } | |
274 | ||
1182e7f5 | 275 | /// Coerce to a string without utf8 encoding. (perlxs `SvPV`) |
83147b11 | 276 | pub fn pv_bytes(&self) -> &[u8] { |
f7cc8c37 WB |
277 | unsafe { |
278 | let mut len: libc::size_t = 0; | |
279 | let ptr = ffi::RSPL_SvPV(self.sv(), &mut len) as *const u8; | |
280 | std::slice::from_raw_parts(ptr, len) | |
281 | } | |
282 | } | |
283 | ||
83147b11 WB |
284 | /// Coerce to a byte-string, downgrading from utf-8. (perlxs `SvPVbyte`) |
285 | /// | |
286 | /// May fail if there are values which don't fit into bytes in the contained utf-8 string, in | |
287 | /// which case `None` is returned. | |
288 | pub fn pv_utf8_to_bytes(&self) -> Option<&[u8]> { | |
f7cc8c37 WB |
289 | unsafe { |
290 | let mut len: libc::size_t = 0; | |
291 | let ptr = ffi::RSPL_SvPVbyte(self.sv(), &mut len) as *const u8; | |
83147b11 WB |
292 | if ptr.is_null() { |
293 | return None; | |
294 | } | |
295 | Some(std::slice::from_raw_parts(ptr, len)) | |
f7cc8c37 WB |
296 | } |
297 | } | |
298 | ||
e62be4a7 | 299 | /// Interpret the byte string as a raw pointer. |
c837c7cc | 300 | pub fn pv_raw<T>(&self) -> Result<*mut T, Error> { |
e62be4a7 WB |
301 | let bytes = self.pv_bytes(); |
302 | ||
303 | let bytes: [u8; mem::size_of::<usize>()] = bytes | |
304 | .try_into() | |
305 | .map_err(|err| Error(format!("invalid value for pointer: {}", err)))?; | |
306 | ||
307 | Ok(usize::from_ne_bytes(bytes) as *mut T) | |
308 | } | |
309 | ||
c837c7cc WB |
310 | /// Interpret the byte string as a pointer and return it as a reference for convenience. |
311 | /// | |
312 | /// # Safety | |
313 | /// | |
314 | /// The user is responsible for making sure the underlying pointer is correct. | |
315 | pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> { | |
2991a46a | 316 | self.pv_raw().map(|p| &*p) |
c837c7cc WB |
317 | } |
318 | ||
319 | /// Interpret the byte string as a pointer and return it as a mutable reference for | |
320 | /// convenience. | |
321 | /// | |
322 | /// # Safety | |
323 | /// | |
324 | /// The user is responsible for making sure the underlying pointer is correct. | |
325 | pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> { | |
2991a46a | 326 | self.pv_raw().map(|p| &mut *p) |
c837c7cc WB |
327 | } |
328 | ||
f7cc8c37 WB |
329 | /// Create another owned reference to this value. |
330 | pub fn clone_ref(&self) -> Scalar { | |
331 | unsafe { Scalar::from_raw_ref(self.sv()) } | |
332 | } | |
333 | ||
1182e7f5 | 334 | /// Convenience check for `SVt_NULL` |
f7cc8c37 WB |
335 | pub fn is_undef(&self) -> bool { |
336 | 0 == unsafe { ffi::RSPL_type_flags(self.sv()) } | |
337 | } | |
338 | ||
1182e7f5 | 339 | /// Turn this into a [`Value`]. |
f7cc8c37 WB |
340 | pub fn into_value(self) -> Value { |
341 | Value::from_scalar(self.clone_ref()) | |
342 | } | |
89989b0f WB |
343 | |
344 | /// Get the reference type for this value. (Similar to `ref` in perl). | |
345 | /// | |
346 | /// If `blessed` is true and the value is a blessed reference, the package name will be | |
347 | /// returned, otherwise the scalar type (`"SCALAR"`, `"ARRAY"`, ...) will be returned. | |
348 | pub fn reftype(&self, blessed: bool) -> &'static str { | |
349 | let ptr = unsafe { ffi::RSPL_sv_reftype(self.sv(), if blessed { 1 } else { 0 }) }; | |
350 | ||
351 | if ptr.is_null() { | |
352 | "<UNKNOWN>" | |
353 | } else { | |
354 | unsafe { | |
355 | std::ffi::CStr::from_ptr(ptr) | |
356 | .to_str() | |
357 | .unwrap_or("<NON-UTF8-CLASSNAME>") | |
358 | } | |
359 | } | |
360 | } | |
f7cc8c37 WB |
361 | } |
362 | ||
363 | impl std::fmt::Debug for Scalar { | |
364 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
3d5b075b | 365 | let this: &ScalarRef = self; |
f7cc8c37 WB |
366 | std::fmt::Debug::fmt(this, f) |
367 | } | |
368 | } | |
369 | ||
370 | impl std::fmt::Debug for ScalarRef { | |
371 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
372 | use std::fmt::Debug; | |
373 | match self.ty() { | |
374 | Type::Scalar(flags) => { | |
375 | if flags.intersects(Flags::STRING) { | |
83147b11 | 376 | Debug::fmt(self.pv_string_utf8(), f) |
f7cc8c37 WB |
377 | } else if flags.intersects(Flags::INTEGER) { |
378 | write!(f, "{}", self.iv()) | |
379 | } else if flags.intersects(Flags::DOUBLE) { | |
380 | write!(f, "{}", self.nv()) | |
381 | } else { | |
382 | write!(f, "<unhandled scalar>") | |
383 | } | |
384 | } | |
385 | Type::Reference => write!(f, "<*REFERENCE>"), | |
386 | Type::Array => write!(f, "<*ARRAY>"), | |
387 | Type::Hash => write!(f, "<*HASH>"), | |
388 | Type::Other(_) => write!(f, "<*PERLTYPE>"), | |
389 | } | |
390 | } | |
391 | } | |
392 | ||
393 | impl serde::Serialize for Scalar { | |
394 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
395 | where | |
396 | S: serde::Serializer, | |
397 | { | |
398 | use serde::ser::Error; | |
399 | ||
400 | match self.ty() { | |
401 | Type::Scalar(flags) => { | |
402 | if flags.contains(Flags::STRING) { | |
83147b11 | 403 | serializer.serialize_str(self.pv_string_utf8()) |
f7cc8c37 WB |
404 | } else if flags.contains(Flags::DOUBLE) { |
405 | serializer.serialize_f64(self.nv()) | |
406 | } else if flags.contains(Flags::INTEGER) { | |
407 | serializer.serialize_i64(self.iv() as i64) | |
e077d87d WB |
408 | } else if flags.is_empty() { |
409 | serializer.serialize_none() | |
f7cc8c37 WB |
410 | } else { |
411 | serializer.serialize_unit() | |
412 | } | |
413 | } | |
e077d87d WB |
414 | Type::Other(other) => Err(S::Error::custom(format!( |
415 | "cannot serialize weird magic perl values ({})", | |
b5c53f3d | 416 | other, |
e077d87d | 417 | ))), |
f7cc8c37 WB |
418 | |
419 | // These are impossible as they are all handled by different Value enum types: | |
420 | Type::Reference => Value::from( | |
421 | self.dereference() | |
422 | .ok_or_else(|| S::Error::custom("failed to dereference perl value"))?, | |
423 | ) | |
424 | .serialize(serializer), | |
425 | Type::Array => { | |
426 | let this = unsafe { crate::Array::from_raw_ref(self.sv() as *mut ffi::AV) }; | |
427 | this.serialize(serializer) | |
428 | } | |
429 | Type::Hash => { | |
430 | let this = unsafe { crate::Hash::from_raw_ref(self.sv() as *mut ffi::HV) }; | |
431 | this.serialize(serializer) | |
432 | } | |
433 | } | |
434 | } | |
435 | } |