libperl_rs/pad.rs
1//! Pad-name access — the lexical (`my` / `our`) variable names of a
2//! CV, read from its PADLIST's name list. Step 2's "lexical pad
3//! resolution" piece (`docs/plan/README.md` §4 Step 3.2), extracted
4//! from libperl-proto0's `eg/pad0.rs` / example `102_padname_type.rs`
5//! and perl-optree-analyzer's `raw.rs`.
6//!
7//! All accessors go through the macrogen-emitted official API
8//! (`PadlistNAMES`, `PadnamelistMAX` / `PadnamelistARRAY`,
9//! `PadnamePV` / `PadnameLEN` / `PadnameTYPE`). On perl 5.28/5.30
10//! those `Padname*` accessors come from the hand-written compat block
11//! in `libperl-sys/src/perl_core.rs` (macrogen still suppresses them
12//! there); the call sites here are identical either way.
13//!
14//! Entry point: [`Cv::pad_names`](crate::Cv::pad_names).
15
16use std::ptr::NonNull;
17
18use libperl_sys::{PADLIST, PADNAME, PADNAMELIST};
19
20/// Non-null pointer to a Perl `PADNAME` — one lexical's name slot.
21/// Non-owning, like the other newtypes.
22#[derive(Clone, Copy)]
23#[repr(transparent)]
24pub struct PadName(NonNull<PADNAME>);
25
26impl PadName {
27 /// Wrap a raw PADNAME pointer, returning `None` on null input.
28 #[inline]
29 pub fn from_raw(p: *const PADNAME) -> Option<Self> {
30 NonNull::new(p as *mut PADNAME).map(PadName)
31 }
32
33 /// Raw pointer for FFI calls.
34 #[inline]
35 pub fn as_ptr(&self) -> *mut PADNAME {
36 self.0.as_ptr()
37 }
38
39 /// The lexical's name including sigil (`"$x"`, `"@args"`, ...),
40 /// or `None` for unnamed slots (targets, sub-op temporaries).
41 pub fn pv(&self) -> Option<String> {
42 let pv = unsafe { libperl_sys::PadnamePV(self.as_ptr()) };
43 if pv.is_null() {
44 return None;
45 }
46 let len = unsafe { libperl_sys::PadnameLEN(self.as_ptr()) };
47 let bytes = unsafe { std::slice::from_raw_parts(pv as *const u8, len as usize) };
48 Some(String::from_utf8_lossy(bytes).into_owned())
49 }
50
51 /// For `my Foo $x`-style typed lexicals: the type stash's name
52 /// (`"Foo"`). `None` for untyped lexicals.
53 pub fn type_stash_name(&self) -> Option<String> {
54 let stash = unsafe { libperl_sys::PadnameTYPE(self.as_ptr()) };
55 if stash.is_null() {
56 return None;
57 }
58 let p = unsafe { libperl_sys::HvNAME(stash) };
59 if p.is_null() {
60 None
61 } else {
62 Some(
63 unsafe { std::ffi::CStr::from_ptr(p) }
64 .to_string_lossy()
65 .into_owned(),
66 )
67 }
68 }
69}
70
71/// Iterator over a CV's pad-name slots, yielded by
72/// [`Cv::pad_names`](crate::Cv::pad_names). Each item corresponds to
73/// one pad offset (starting at 0); `None` items are allocated but
74/// nameless slots. Pair with `.enumerate()` when the pad offsets
75/// matter.
76pub struct PadNames {
77 arr: *mut *mut PADNAME,
78 ix: isize,
79 max: isize,
80}
81
82impl PadNames {
83 /// Build from a CV's PADLIST pointer (null-safe: a null padlist —
84 /// e.g. an XSUB's — yields an empty iterator).
85 pub(crate) fn from_padlist(pl: *const PADLIST) -> PadNames {
86 let empty = PadNames {
87 arr: std::ptr::null_mut(),
88 ix: 0,
89 max: -1,
90 };
91 if pl.is_null() {
92 return empty;
93 }
94 let pnl: *const PADNAMELIST = unsafe { libperl_sys::PadlistNAMES(pl) };
95 if pnl.is_null() {
96 return empty;
97 }
98 PadNames {
99 arr: unsafe { libperl_sys::PadnamelistARRAY(pnl) },
100 ix: 0,
101 // `PadnamelistMAX` is the last used index (xpadnl_fill),
102 // -1 when empty — same convention as AvFILL.
103 max: unsafe { libperl_sys::PadnamelistMAX(pnl) as isize },
104 }
105 }
106}
107
108impl Iterator for PadNames {
109 type Item = Option<PadName>;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 if self.ix > self.max || self.arr.is_null() {
113 return None;
114 }
115 let p = unsafe { *self.arr.offset(self.ix) };
116 self.ix += 1;
117 Some(PadName::from_raw(p))
118 }
119
120 fn size_hint(&self) -> (usize, Option<usize>) {
121 let r = (self.max + 1 - self.ix).max(0) as usize;
122 (r, Some(r))
123 }
124}