rustpython_vm/builtins/
code.rs

1/*! Infamous code object. The python class `code`
2
3*/
4
5use super::{PyStrRef, PyTupleRef, PyType, PyTypeRef};
6use crate::{
7    builtins::PyStrInterned,
8    bytecode::{self, AsBag, BorrowedConstant, CodeFlags, Constant, ConstantBag},
9    class::{PyClassImpl, StaticType},
10    convert::ToPyObject,
11    frozen,
12    function::{FuncArgs, OptionalArg},
13    source_code::OneIndexed,
14    types::Representable,
15    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
16};
17use malachite_bigint::BigInt;
18use num_traits::Zero;
19use std::{borrow::Borrow, fmt, ops::Deref};
20
21#[derive(FromArgs)]
22pub struct ReplaceArgs {
23    #[pyarg(named, optional)]
24    co_posonlyargcount: OptionalArg<u32>,
25    #[pyarg(named, optional)]
26    co_argcount: OptionalArg<u32>,
27    #[pyarg(named, optional)]
28    co_kwonlyargcount: OptionalArg<u32>,
29    #[pyarg(named, optional)]
30    co_filename: OptionalArg<PyStrRef>,
31    #[pyarg(named, optional)]
32    co_firstlineno: OptionalArg<u32>,
33    #[pyarg(named, optional)]
34    co_consts: OptionalArg<Vec<PyObjectRef>>,
35    #[pyarg(named, optional)]
36    co_name: OptionalArg<PyStrRef>,
37    #[pyarg(named, optional)]
38    co_names: OptionalArg<Vec<PyObjectRef>>,
39    #[pyarg(named, optional)]
40    co_flags: OptionalArg<u16>,
41    #[pyarg(named, optional)]
42    co_varnames: OptionalArg<Vec<PyObjectRef>>,
43}
44
45#[derive(Clone)]
46#[repr(transparent)]
47pub struct Literal(PyObjectRef);
48
49impl Borrow<PyObject> for Literal {
50    fn borrow(&self) -> &PyObject {
51        &self.0
52    }
53}
54
55impl From<Literal> for PyObjectRef {
56    fn from(obj: Literal) -> Self {
57        obj.0
58    }
59}
60
61fn borrow_obj_constant(obj: &PyObject) -> BorrowedConstant<Literal> {
62    match_class!(match obj {
63        ref i @ super::int::PyInt => {
64            let value = i.as_bigint();
65            if obj.class().is(super::bool_::PyBool::static_type()) {
66                BorrowedConstant::Boolean {
67                    value: !value.is_zero(),
68                }
69            } else {
70                BorrowedConstant::Integer { value }
71            }
72        }
73        ref f @ super::float::PyFloat => BorrowedConstant::Float { value: f.to_f64() },
74        ref c @ super::complex::PyComplex => BorrowedConstant::Complex {
75            value: c.to_complex()
76        },
77        ref s @ super::pystr::PyStr => BorrowedConstant::Str { value: s.as_str() },
78        ref b @ super::bytes::PyBytes => BorrowedConstant::Bytes {
79            value: b.as_bytes()
80        },
81        ref c @ PyCode => {
82            BorrowedConstant::Code { code: &c.code }
83        }
84        ref t @ super::tuple::PyTuple => {
85            let elements = t.as_slice();
86            // SAFETY: Literal is repr(transparent) over PyObjectRef, and a Literal tuple only ever
87            //         has other literals as elements
88            let elements = unsafe { &*(elements as *const [PyObjectRef] as *const [Literal]) };
89            BorrowedConstant::Tuple { elements }
90        }
91        super::singletons::PyNone => BorrowedConstant::None,
92        super::slice::PyEllipsis => BorrowedConstant::Ellipsis,
93        _ => panic!("unexpected payload for constant python value"),
94    })
95}
96
97impl Constant for Literal {
98    type Name = &'static PyStrInterned;
99    fn borrow_constant(&self) -> BorrowedConstant<Self> {
100        borrow_obj_constant(&self.0)
101    }
102}
103
104impl<'a> AsBag for &'a Context {
105    type Bag = PyObjBag<'a>;
106    fn as_bag(self) -> PyObjBag<'a> {
107        PyObjBag(self)
108    }
109}
110impl<'a> AsBag for &'a VirtualMachine {
111    type Bag = PyObjBag<'a>;
112    fn as_bag(self) -> PyObjBag<'a> {
113        PyObjBag(&self.ctx)
114    }
115}
116
117#[derive(Clone, Copy)]
118pub struct PyObjBag<'a>(pub &'a Context);
119
120impl ConstantBag for PyObjBag<'_> {
121    type Constant = Literal;
122
123    fn make_constant<C: Constant>(&self, constant: BorrowedConstant<C>) -> Self::Constant {
124        let ctx = self.0;
125        let obj = match constant {
126            bytecode::BorrowedConstant::Integer { value } => ctx.new_bigint(value).into(),
127            bytecode::BorrowedConstant::Float { value } => ctx.new_float(value).into(),
128            bytecode::BorrowedConstant::Complex { value } => ctx.new_complex(value).into(),
129            bytecode::BorrowedConstant::Str { value } if value.len() <= 20 => {
130                ctx.intern_str(value).to_object()
131            }
132            bytecode::BorrowedConstant::Str { value } => ctx.new_str(value).into(),
133            bytecode::BorrowedConstant::Bytes { value } => ctx.new_bytes(value.to_vec()).into(),
134            bytecode::BorrowedConstant::Boolean { value } => ctx.new_bool(value).into(),
135            bytecode::BorrowedConstant::Code { code } => {
136                ctx.new_code(code.map_clone_bag(self)).into()
137            }
138            bytecode::BorrowedConstant::Tuple { elements } => {
139                let elements = elements
140                    .iter()
141                    .map(|constant| self.make_constant(constant.borrow_constant()).0)
142                    .collect();
143                ctx.new_tuple(elements).into()
144            }
145            bytecode::BorrowedConstant::None => ctx.none(),
146            bytecode::BorrowedConstant::Ellipsis => ctx.ellipsis(),
147        };
148        Literal(obj)
149    }
150
151    fn make_name(&self, name: &str) -> &'static PyStrInterned {
152        self.0.intern_str(name)
153    }
154
155    fn make_int(&self, value: BigInt) -> Self::Constant {
156        Literal(self.0.new_int(value).into())
157    }
158
159    fn make_tuple(&self, elements: impl Iterator<Item = Self::Constant>) -> Self::Constant {
160        Literal(self.0.new_tuple(elements.map(|lit| lit.0).collect()).into())
161    }
162
163    fn make_code(&self, code: CodeObject) -> Self::Constant {
164        Literal(self.0.new_code(code).into())
165    }
166}
167
168pub type CodeObject = bytecode::CodeObject<Literal>;
169
170pub trait IntoCodeObject {
171    fn into_code_object(self, ctx: &Context) -> CodeObject;
172}
173
174impl IntoCodeObject for CodeObject {
175    fn into_code_object(self, _ctx: &Context) -> CodeObject {
176        self
177    }
178}
179
180impl IntoCodeObject for bytecode::CodeObject {
181    fn into_code_object(self, ctx: &Context) -> CodeObject {
182        self.map_bag(PyObjBag(ctx))
183    }
184}
185
186impl<B: AsRef<[u8]>> IntoCodeObject for frozen::FrozenCodeObject<B> {
187    fn into_code_object(self, ctx: &Context) -> CodeObject {
188        self.decode(ctx)
189    }
190}
191
192#[pyclass(module = false, name = "code")]
193pub struct PyCode {
194    pub code: CodeObject,
195}
196
197impl Deref for PyCode {
198    type Target = CodeObject;
199    fn deref(&self) -> &Self::Target {
200        &self.code
201    }
202}
203
204impl PyCode {
205    pub fn new(code: CodeObject) -> PyCode {
206        PyCode { code }
207    }
208}
209
210impl fmt::Debug for PyCode {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        write!(f, "code: {:?}", self.code)
213    }
214}
215
216impl PyPayload for PyCode {
217    fn class(ctx: &Context) -> &'static Py<PyType> {
218        ctx.types.code_type
219    }
220}
221
222impl Representable for PyCode {
223    #[inline]
224    fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
225        let code = &zelf.code;
226        Ok(format!(
227            "<code object {} at {:#x} file {:?}, line {}>",
228            code.obj_name,
229            zelf.get_id(),
230            code.source_path.as_str(),
231            code.first_line_number.map_or(-1, |n| n.get() as i32)
232        ))
233    }
234}
235
236#[pyclass(with(Representable))]
237impl PyCode {
238    #[pyslot]
239    fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
240        Err(vm.new_type_error("Cannot directly create code object".to_owned()))
241    }
242
243    #[pygetset]
244    fn co_posonlyargcount(&self) -> usize {
245        self.code.posonlyarg_count as usize
246    }
247
248    #[pygetset]
249    fn co_argcount(&self) -> usize {
250        self.code.arg_count as usize
251    }
252
253    #[pygetset]
254    fn co_stacksize(&self) -> u32 {
255        self.code.max_stackdepth
256    }
257
258    #[pygetset]
259    pub fn co_filename(&self) -> PyStrRef {
260        self.code.source_path.to_owned()
261    }
262
263    #[pygetset]
264    pub fn co_cellvars(&self, vm: &VirtualMachine) -> PyTupleRef {
265        let cellvars = self
266            .code
267            .cellvars
268            .deref()
269            .iter()
270            .map(|name| name.to_pyobject(vm))
271            .collect();
272        vm.ctx.new_tuple(cellvars)
273    }
274
275    #[pygetset]
276    fn co_nlocals(&self) -> usize {
277        self.varnames.len()
278    }
279
280    #[pygetset]
281    fn co_firstlineno(&self) -> u32 {
282        self.code.first_line_number.map_or(0, |n| n.get())
283    }
284
285    #[pygetset]
286    fn co_kwonlyargcount(&self) -> usize {
287        self.code.kwonlyarg_count as usize
288    }
289
290    #[pygetset]
291    fn co_consts(&self, vm: &VirtualMachine) -> PyTupleRef {
292        let consts = self.code.constants.iter().map(|x| x.0.clone()).collect();
293        vm.ctx.new_tuple(consts)
294    }
295
296    #[pygetset]
297    fn co_name(&self) -> PyStrRef {
298        self.code.obj_name.to_owned()
299    }
300
301    #[pygetset]
302    fn co_names(&self, vm: &VirtualMachine) -> PyTupleRef {
303        let names = self
304            .code
305            .names
306            .deref()
307            .iter()
308            .map(|name| name.to_pyobject(vm))
309            .collect();
310        vm.ctx.new_tuple(names)
311    }
312
313    #[pygetset]
314    fn co_flags(&self) -> u16 {
315        self.code.flags.bits()
316    }
317
318    #[pygetset]
319    pub fn co_varnames(&self, vm: &VirtualMachine) -> PyTupleRef {
320        let varnames = self.code.varnames.iter().map(|s| s.to_object()).collect();
321        vm.ctx.new_tuple(varnames)
322    }
323
324    #[pygetset]
325    pub fn co_freevars(&self, vm: &VirtualMachine) -> PyTupleRef {
326        let names = self
327            .code
328            .freevars
329            .deref()
330            .iter()
331            .map(|name| name.to_pyobject(vm))
332            .collect();
333        vm.ctx.new_tuple(names)
334    }
335
336    #[pymethod]
337    pub fn replace(&self, args: ReplaceArgs, vm: &VirtualMachine) -> PyResult<PyCode> {
338        let posonlyarg_count = match args.co_posonlyargcount {
339            OptionalArg::Present(posonlyarg_count) => posonlyarg_count,
340            OptionalArg::Missing => self.code.posonlyarg_count,
341        };
342
343        let arg_count = match args.co_argcount {
344            OptionalArg::Present(arg_count) => arg_count,
345            OptionalArg::Missing => self.code.arg_count,
346        };
347
348        let source_path = match args.co_filename {
349            OptionalArg::Present(source_path) => source_path,
350            OptionalArg::Missing => self.code.source_path.to_owned(),
351        };
352
353        let first_line_number = match args.co_firstlineno {
354            OptionalArg::Present(first_line_number) => OneIndexed::new(first_line_number),
355            OptionalArg::Missing => self.code.first_line_number,
356        };
357
358        let kwonlyarg_count = match args.co_kwonlyargcount {
359            OptionalArg::Present(kwonlyarg_count) => kwonlyarg_count,
360            OptionalArg::Missing => self.code.kwonlyarg_count,
361        };
362
363        let constants = match args.co_consts {
364            OptionalArg::Present(constants) => constants,
365            OptionalArg::Missing => self.code.constants.iter().map(|x| x.0.clone()).collect(),
366        };
367
368        let obj_name = match args.co_name {
369            OptionalArg::Present(obj_name) => obj_name,
370            OptionalArg::Missing => self.code.obj_name.to_owned(),
371        };
372
373        let names = match args.co_names {
374            OptionalArg::Present(names) => names,
375            OptionalArg::Missing => self
376                .code
377                .names
378                .deref()
379                .iter()
380                .map(|name| name.to_pyobject(vm))
381                .collect(),
382        };
383
384        let flags = match args.co_flags {
385            OptionalArg::Present(flags) => flags,
386            OptionalArg::Missing => self.code.flags.bits(),
387        };
388
389        let varnames = match args.co_varnames {
390            OptionalArg::Present(varnames) => varnames,
391            OptionalArg::Missing => self.code.varnames.iter().map(|s| s.to_object()).collect(),
392        };
393
394        Ok(PyCode {
395            code: CodeObject {
396                flags: CodeFlags::from_bits_truncate(flags),
397                posonlyarg_count,
398                arg_count,
399                kwonlyarg_count,
400                source_path: source_path.as_object().as_interned_str(vm).unwrap(),
401                first_line_number,
402                obj_name: obj_name.as_object().as_interned_str(vm).unwrap(),
403
404                max_stackdepth: self.code.max_stackdepth,
405                instructions: self.code.instructions.clone(),
406                locations: self.code.locations.clone(),
407                constants: constants.into_iter().map(Literal).collect(),
408                names: names
409                    .into_iter()
410                    .map(|o| o.as_interned_str(vm).unwrap())
411                    .collect(),
412                varnames: varnames
413                    .into_iter()
414                    .map(|o| o.as_interned_str(vm).unwrap())
415                    .collect(),
416                cellvars: self.code.cellvars.clone(),
417                freevars: self.code.freevars.clone(),
418                cell2arg: self.code.cell2arg.clone(),
419            },
420        })
421    }
422}
423
424impl fmt::Display for PyCode {
425    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426        (**self).fmt(f)
427    }
428}
429
430impl ToPyObject for CodeObject {
431    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
432        vm.ctx.new_code(self).into()
433    }
434}
435
436impl ToPyObject for bytecode::CodeObject {
437    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
438        vm.ctx.new_code(self).into()
439    }
440}
441
442pub fn init(ctx: &Context) {
443    PyCode::extend_class(ctx, ctx.types.code_type);
444}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.