1use 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 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}