Skip to main content

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        #[cfg(perlapi_ver22)]
47        let len = unsafe { libperl_sys::PadnameLEN(self.as_ptr()) };
48        // 5.20 (PADNAME = SV 時代): 生成体 PadnameLEN は THX 付きで、
49        // Perl ハンドルを持たないここからは呼べない。pad.h 5.20 の定義
50        // `(pn == &PL_sv_undef ? 0 : SvCUR(pn))` の undef 分岐は直前の
51        // PV null チェック (undef は POKp でない) で除外済みなので、
52        // SvCUR 直読みで等価。
53        #[cfg(not(perlapi_ver22))]
54        let len = unsafe { libperl_sys::SvCUR(self.as_ptr() as *const libperl_sys::SV) };
55        let bytes = unsafe { std::slice::from_raw_parts(pv as *const u8, len as usize) };
56        Some(String::from_utf8_lossy(bytes).into_owned())
57    }
58
59    /// For `my Foo $x`-style typed lexicals: the type stash's name
60    /// (`"Foo"`). `None` for untyped lexicals.
61    pub fn type_stash_name(&self) -> Option<String> {
62        let stash = unsafe { libperl_sys::PadnameTYPE(self.as_ptr()) };
63        if stash.is_null() {
64            return None;
65        }
66        let p = unsafe { libperl_sys::HvNAME(stash) };
67        if p.is_null() {
68            None
69        } else {
70            Some(
71                unsafe { std::ffi::CStr::from_ptr(p) }
72                    .to_string_lossy()
73                    .into_owned(),
74            )
75        }
76    }
77}
78
79/// Iterator over a CV's pad-name slots, yielded by
80/// [`Cv::pad_names`](crate::Cv::pad_names). Each item corresponds to
81/// one pad offset (starting at 0); `None` items are allocated but
82/// nameless slots. Pair with `.enumerate()` when the pad offsets
83/// matter.
84pub struct PadNames {
85    arr: *mut *mut PADNAME,
86    ix: isize,
87    max: isize,
88}
89
90impl PadNames {
91    /// Build from a CV's PADLIST pointer (null-safe: a null padlist —
92    /// e.g. an XSUB's — yields an empty iterator).
93    pub(crate) fn from_padlist(pl: *const PADLIST) -> PadNames {
94        let empty = PadNames {
95            arr: std::ptr::null_mut(),
96            ix: 0,
97            max: -1,
98        };
99        if pl.is_null() {
100            return empty;
101        }
102        let pnl: *const PADNAMELIST = unsafe { libperl_sys::PadlistNAMES(pl) };
103        if pnl.is_null() {
104            return empty;
105        }
106        PadNames {
107            arr: unsafe { libperl_sys::PadnamelistARRAY(pnl) },
108            ix: 0,
109            // `PadnamelistMAX` is the last used index (xpadnl_fill),
110            // -1 when empty — same convention as AvFILL.
111            max: unsafe { libperl_sys::PadnamelistMAX(pnl) as isize },
112        }
113    }
114}
115
116impl Iterator for PadNames {
117    type Item = Option<PadName>;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        if self.ix > self.max || self.arr.is_null() {
121            return None;
122        }
123        let p = unsafe { *self.arr.offset(self.ix) };
124        self.ix += 1;
125        Some(PadName::from_raw(p))
126    }
127
128    fn size_hint(&self) -> (usize, Option<usize>) {
129        let r = (self.max + 1 - self.ix).max(0) as usize;
130        (r, Some(r))
131    }
132}