pub struct Op(/* private fields */);Expand description
Non-null pointer to a Perl OP. Same ABI as *mut OP.
Like Sv, an Op does not own its referent —
dropping it is a no-op. OP lifetimes follow their owning CV
(perl_destruct / op_free invalidate them), which the type does
not track; keep walks inside the scope where the CV is known live.
Implementations§
Source§impl Op
impl Op
Sourcepub unsafe fn from_raw_unchecked(p: *const OP) -> Self
pub unsafe fn from_raw_unchecked(p: *const OP) -> Self
Wrap a raw OP pointer without checking for null.
§Safety
Caller must guarantee p is non-null and points to a valid OP
for at least the lifetime of the resulting Op.
Sourcepub fn op_type_raw(&self) -> u32
pub fn op_type_raw(&self) -> u32
The op’s type as a plain integer. The underlying bitfield is
u16 on modern Perl but u32 on 5.30 and older — normalising
to u32 here keeps callers version-portable.
Sourcepub fn opcode(&self) -> Option<opcode>
pub fn opcode(&self) -> Option<opcode>
The op’s type as the opcode enum, or None for
out-of-range values (custom ops).
Sourcepub fn name(&self) -> Option<&'static str>
pub fn name(&self) -> Option<&'static str>
The op’s name ("nextstate", "add", …) from the
PL_op_name table, or None for out-of-range op types.
Examples found in repository?
More examples
19fn main() {
20 let mut perl = Perl::new();
21 perl.parse_env_args(env::args(), env::vars());
22
23 let sv0 = perl.get_sv("0", 0).expect("$0 is always set");
24 let main_file = String::from_utf8_lossy(sv0.pv(&perl)).into_owned();
25 println!("$0 = {main_file:?}");
26
27 let mut walker = StashWalker::new(&perl);
28 walker.walk("main", &mut |e| {
29 if e.cv.file().as_deref() != Some(main_file.as_str()) {
30 return;
31 }
32 let qual = e
33 .cv
34 .names(&perl)
35 .map(|(full, _)| full)
36 .unwrap_or_else(|| format!("{}::{}", e.package, e.name));
37 let line = e.cv.first_cop(&perl).map(|c| c.line());
38 println!("sub {qual} (first statement at line {line:?})");
39
40 let lexicals: Vec<String> = e
41 .cv
42 .pad_names()
43 .flatten() // skip unnamed slots
44 .filter_map(|pn| {
45 // Target/temporary slots have a non-null but empty PV;
46 // only real `my`/`our` names are interesting here.
47 let pv = pn.pv().filter(|s| !s.is_empty())?;
48 Some(match pn.type_stash_name() {
49 Some(t) => format!("{pv}: {t}"),
50 None => pv,
51 })
52 })
53 .collect();
54 println!(" lexicals: {lexicals:?}");
55
56 let ops: Vec<&str> = e
57 .cv
58 .start_op()
59 .into_iter()
60 .flat_map(|s| s.next_iter())
61 .take(20)
62 .map(|o| o.name().unwrap_or("<custom>"))
63 .collect();
64 println!(" ops (execution order, first 20): {}", ops.join(" "));
65 });
66}Sourcepub fn next(&self) -> Option<Op>
pub fn next(&self) -> Option<Op>
Next op in execution order (op_next), or None at the end
of the chain.
Sourcepub fn sibling(&self) -> Option<Op>
pub fn sibling(&self) -> Option<Op>
Next sibling in tree order (official OpSIBLING: the
op_moresib check terminates at the parent back-pointer on
5.26+ layouts), or None for the last sibling.
Sourcepub fn first(&self) -> Option<Op>
pub fn first(&self) -> Option<Op>
First child (cUNOPx(o)->op_first equivalent), or None when
the op has no kids (OPf_KIDS unset). No public C macro exists
for this — the struct read matches what B does.
Sourcepub fn kids(&self) -> OpSiblingIter ⓘ
pub fn kids(&self) -> OpSiblingIter ⓘ
Iterate this op’s children in tree order (first child, then its siblings). Empty for kid-less ops.
Sourcepub fn next_iter(&self) -> OpNextIter ⓘ
pub fn next_iter(&self) -> OpNextIter ⓘ
Iterate in execution order starting from (and including) this
op, following op_next until null.
Note: the static op_next chain of a finished sub is not
acyclic — loop constructs point back to their condition — so an
unbounded walk over arbitrary code may not terminate. Cap with
.take(n) unless the code is known to be straight-line.
Examples found in repository?
19fn main() {
20 let mut perl = Perl::new();
21 perl.parse_env_args(env::args(), env::vars());
22
23 let sv0 = perl.get_sv("0", 0).expect("$0 is always set");
24 let main_file = String::from_utf8_lossy(sv0.pv(&perl)).into_owned();
25 println!("$0 = {main_file:?}");
26
27 let mut walker = StashWalker::new(&perl);
28 walker.walk("main", &mut |e| {
29 if e.cv.file().as_deref() != Some(main_file.as_str()) {
30 return;
31 }
32 let qual = e
33 .cv
34 .names(&perl)
35 .map(|(full, _)| full)
36 .unwrap_or_else(|| format!("{}::{}", e.package, e.name));
37 let line = e.cv.first_cop(&perl).map(|c| c.line());
38 println!("sub {qual} (first statement at line {line:?})");
39
40 let lexicals: Vec<String> = e
41 .cv
42 .pad_names()
43 .flatten() // skip unnamed slots
44 .filter_map(|pn| {
45 // Target/temporary slots have a non-null but empty PV;
46 // only real `my`/`our` names are interesting here.
47 let pv = pn.pv().filter(|s| !s.is_empty())?;
48 Some(match pn.type_stash_name() {
49 Some(t) => format!("{pv}: {t}"),
50 None => pv,
51 })
52 })
53 .collect();
54 println!(" lexicals: {lexicals:?}");
55
56 let ops: Vec<&str> = e
57 .cv
58 .start_op()
59 .into_iter()
60 .flat_map(|s| s.next_iter())
61 .take(20)
62 .map(|o| o.name().unwrap_or("<custom>"))
63 .collect();
64 println!(" ops (execution order, first 20): {}", ops.join(" "));
65 });
66}