1#![allow(non_snake_case)]
2#![allow(unused_imports)]
3
4#[cfg(perlapi_ver26)]
5use std::convert::TryFrom;
6
7pub use libperl_sys::op;
8
9use if_chain::if_chain;
10
11use libperl_sys::*;
12use libperl_rs::Perl;
13
14use super::sv0::{Sv, VarName, sv_extract};
15use super::pad0::*;
16use super::op0::{op_sibling, op_sv_or, Name};
17pub use super::op0::op_name;
18
19use typed_arena::Arena;
20
21pub struct OpExtractor<'a> {
22 perl: &'a Perl,
23 ops: Arena<Op<'a>>,
24}
25
26#[derive(Debug, Clone)]
27pub struct PadNameType {
28 name: Option<String>,
29 typ: Option<String>,
30}
31
32struct OpcodeWrap<'a> (&'a opcode);
33
34impl<'a> std::fmt::Debug for OpcodeWrap<'a> {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.write_str("opcode::")?;
37 self.0.fmt(f)
38 }
39}
40
41#[allow(non_camel_case_types)]
42pub enum Op<'a> {
43 NULL,
44 OP (opcode, *const op, Option<PadNameType>, &'a Op<'a>),
45 UNOP (opcode, *const unop, &'a Op<'a>, &'a Op<'a>),
46 BINOP (opcode, *const binop, &'a Op<'a>, &'a Op<'a>),
47 LOGOP (opcode, *const logop, &'a Op<'a>, &'a Op<'a>),
48 LISTOP (opcode, *const listop, &'a Op<'a>, &'a Op<'a>), PMOP {opcode: opcode},
51 SVOP (opcode, Sv, &'a Op<'a>),
52 PADOP (opcode, *const padop, Sv),
53 PVOP (opcode),
54 LOOP (opcode, &'a Op<'a> ),
56 COP (opcode, &'a Op<'a>),
57 METHOP(opcode, Name),
58 #[cfg(perlapi_ver26)]
60 UNOP_AUX (opcode, &'a Op<'a>, &'a Op<'a>),
61}
62
63impl<'a> std::fmt::Debug for Op<'a> {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match &self {
66 Op::NULL => { f.debug_tuple("Op::NULL").finish() },
67 Op::OP(oc, _, padname, sibling) => {
68 f.debug_tuple("Op::OP")
69 .field(&OpcodeWrap(oc))
70 .field(&VarName("_"))
71 .field(&padname)
72 .field(&sibling)
73 .finish()
74 },
75 Op::UNOP(oc, _, first, sibling) => {
76 f.debug_tuple("Op::UNOP")
77 .field(&OpcodeWrap(oc))
78 .field(&VarName("_"))
79 .field(&first)
80 .field(&sibling)
81 .finish()
82 },
83 Op::BINOP(oc, _, first, sibling) => {
84 f.debug_tuple("Op::BINOP")
85 .field(&OpcodeWrap(oc))
86 .field(&VarName("_"))
87 .field(&first)
88 .field(&sibling)
89 .finish()
90 },
91 Op::LOGOP(oc, _, first, sibling) => {
92 f.debug_tuple("Op::LOGOP")
93 .field(&OpcodeWrap(oc))
94 .field(&VarName("_"))
95 .field(&first)
96 .field(&sibling)
97 .finish()
98 },
99 Op::LISTOP(oc, _, first, sibling) => {
100 f.debug_tuple("Op::LISTOP")
101 .field(&OpcodeWrap(oc))
102 .field(&VarName("_"))
103 .field(&first)
104 .field(&sibling)
105 .finish()
106 },
107 Op::PMOP {opcode: oc} => {
108 f.debug_struct("Op::PMOP")
109 .field("opcode", &OpcodeWrap(oc))
110 .finish()
112 },
113 Op::SVOP(oc, sv, sibling) => {
114 f.debug_tuple("Op::SVOP")
115 .field(&OpcodeWrap(oc))
116 .field(&sv)
117 .field(&sibling)
118 .finish()
119 },
120 Op::PADOP(oc, _, sibling) => {
121 f.debug_tuple("Op::PADOP")
122 .field(&OpcodeWrap(oc))
123 .field(&VarName("_"))
124 .field(&sibling)
125 .finish()
126 },
127 Op::PVOP(oc) => {
128 f.debug_tuple("Op::PVOP")
129 .field(&OpcodeWrap(oc))
130 .finish()
131 },
132 Op::LOOP(oc, sibling) => {
133 f.debug_tuple("Op::LOOP")
134 .field(&OpcodeWrap(oc))
135 .field(&sibling)
136 .finish()
137 },
138 Op::COP(oc, sibling) => {
139 f.debug_tuple("Op::COP")
140 .field(&OpcodeWrap(oc))
141 .field(sibling)
142 .finish()
143 },
144 Op::METHOP(oc, name) => {
145 f.debug_tuple("Op::METHOP")
146 .field(&OpcodeWrap(oc))
147 .field(&name)
148 .finish()
149 },
150 #[cfg(perlapi_ver26)]
151 Op::UNOP_AUX(oc, first, sibling) => {
152 f.debug_tuple("Op::UNOP_AUX")
153 .field(&OpcodeWrap(oc))
154 .field(&first)
155 .field(&sibling)
156 .finish()
157 },
158 }
159 }
160}
161
162#[cfg(perlapi_ver26)]
163impl<'a> OpExtractor<'a> {
164
165 pub fn new(perl: &'a Perl) -> Self {
166 Self {perl, ops: Arena::new()}
167 }
168
169 pub fn extract(&self, cv: *const cv, o: *const op) -> &'a Op {
170 if o.is_null() {
171 return self.ops.alloc(Op::NULL)
172 }
173 let cls = self.perl.op_class(o);
174 let oc = opcode::try_from(o).unwrap();
175 let eo = match cls {
176 OPclass::OPclass_NULL => Op::NULL,
177 OPclass::OPclass_BASEOP => {
178 let op = unsafe {o.as_ref().unwrap()};
179 let sibling = self.extract(cv, op_sibling(o as *const unop));
180 if_chain! {
181 if let Some(pl) = cv_padnamelist(cv);
182 if let Some(padname) = padnamelist_nth(pl, op.op_targ as usize);
183 then {
184 Op::OP (
185 oc, o,
186 Some(PadNameType {
187 name: PadnamePV(padname), typ: PadnameTYPE(padname)
188 }),
189 sibling
190 )
191 } else {
192 Op::OP (oc, o, None, sibling)
193 }
194 }
195 },
196 OPclass::OPclass_UNOP => {
197 let op = unsafe {(o as *const unop).as_ref()}.unwrap();
198 Op::UNOP (
199 oc, op,
200 self.extract(cv, op.op_first),
201 self.extract(cv, op_sibling(o as *const unop)),
202 )
203 },
204 OPclass::OPclass_BINOP => {
205 let op = unsafe {(o as *const binop).as_ref()}.unwrap();
206 Op::BINOP (
207 oc, op,
208 self.extract(cv, op.op_first),
209 self.extract(cv, op_sibling(o as *const unop)),
210 )
211 },
212 OPclass::OPclass_LOGOP => {
213 let op = unsafe {(o as *const logop).as_ref()}.unwrap();
214 Op::LOGOP (
215 oc, op,
216 self.extract(cv, op.op_first),
217 self.extract(cv, op_sibling(o as *const unop)),
218 )
219 },
220 OPclass::OPclass_LISTOP => {
221 let op = unsafe {(o as *const listop).as_ref()}.unwrap();
222 Op::LISTOP (
223 oc, op,
224 self.extract(cv, op.op_first),
225 self.extract(cv, op_sibling(o as *const unop)),
226 )
227 },
228 OPclass::OPclass_PMOP => Op::PMOP {opcode: oc},
230 OPclass::OPclass_SVOP => {
231 let sv = op_sv_or(o, |op| PAD_BASE_SV(CvPADLIST(cv), op.op_targ));
232 Op::SVOP (
233 oc, sv_extract(sv),
234 self.extract(cv, op_sibling(o as *const unop)),
235 )
236 },
237 OPclass::OPclass_PADOP => {
238 let op = unsafe {(o as *const padop).as_ref()}.unwrap();
239 let sv = PAD_BASE_SV(CvPADLIST(cv), op.op_padix);
240 Op::PADOP(oc, op, sv_extract(sv))
241 },
242 OPclass::OPclass_PVOP => Op::PVOP (oc),
244 OPclass::OPclass_LOOP => {
246 Op::LOOP (
247 oc,
248 self.extract(cv, op_sibling(o as *const unop)),
249 )
250 },
251 OPclass::OPclass_COP => {
252 Op::COP (
254 oc,
255 self.extract(cv, op_sibling(o as *const unop)),
256 )
257 },
258 OPclass::OPclass_METHOP => {
260 if (unsafe {*o}.op_flags & OPf_KIDS as u8) != 0 {
261 Op::METHOP (oc, Name::Dynamic)
262
263 } else {
264 let sv = op_sv_or(o, |op| PAD_BASE_SV(CvPADLIST(cv), op.op_targ));
265 Op::METHOP(oc, Name::Const(sv_extract(sv)))
266 }
267 },
268 #[cfg(perlapi_ver26)]
269 OPclass::OPclass_UNOP_AUX => {
270 let op = unsafe {(o as *const unop_aux).as_ref()}.unwrap();
271 Op::UNOP_AUX (
272 oc,
273 self.extract(cv, op.op_first),
274 self.extract(cv, op_sibling(o as *const unop)),
275 )
276 }
277 };
278
279 self.ops.alloc(eo)
280 }
281}