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}