1use crate::{
2 AsObject, PyObject,
3 builtins::{
4 PyByteArray, PyBytes, PyCapsule, PyComplex, PyDict, PyDictRef, PyEllipsis, PyFloat,
5 PyFrozenSet, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented, PyStr,
6 PyStrInterned, PyTuple, PyTupleRef, PyType, PyTypeRef, PyUtf8Str,
7 bool_::PyBool,
8 code::{self, PyCode},
9 descriptor::{
10 MemberAccess, MemberKind, PyDescriptorOwned, PyMemberDef, PyMemberDescriptor,
11 PyMemberFlags,
12 },
13 getset::PyGetSet,
14 object, pystr,
15 type_::PyAttributes,
16 },
17 class::StaticType,
18 common::rc::PyRc,
19 exceptions,
20 function::{
21 HeapMethodDef, IntoPyGetterFunc, IntoPyNativeFn, IntoPySetterFunc, ItemDoc, PyMethodDef,
22 PyMethodFlags, plain_doc,
23 },
24 intern::{InternableString, MaybeInternedString, StringPool},
25 object::{Py, PyObjectPayload, PyObjectRef, PyPayload, PyRef},
26 types::{PyTypeFlags, PyTypeSlots, TypeZoo},
27};
28use core::ffi::{CStr, c_void};
29use malachite_bigint::BigInt;
30use num_complex::Complex64;
31use num_traits::ToPrimitive;
32use rustpython_common::lock::PyRwLock;
33
34#[derive(Debug)]
35pub struct Context {
36 pub true_value: PyRef<PyBool>,
37 pub false_value: PyRef<PyBool>,
38 pub none: PyRef<PyNone>,
39 pub empty_tuple: PyTupleRef,
40 pub empty_frozenset: PyRef<PyFrozenSet>,
41 pub empty_str: &'static PyStrInterned,
42 pub empty_bytes: PyRef<PyBytes>,
43 pub ellipsis: PyRef<PyEllipsis>,
44 pub not_implemented: PyRef<PyNotImplemented>,
45
46 pub typing_no_default: PyRef<crate::stdlib::_typing::NoDefault>,
47
48 pub types: TypeZoo,
49 pub exceptions: exceptions::ExceptionZoo,
50 pub int_cache_pool: Vec<PyIntRef>,
51 pub(crate) latin1_char_cache: Vec<PyRef<PyStr>>,
52 pub(crate) ascii_char_cache: Vec<PyRef<PyStr>>,
53 pub(crate) string_pool: StringPool,
55 pub(crate) slot_new_wrapper: PyMethodDef,
56 pub names: ConstName,
57 }
59
60macro_rules! declare_const_name {
61 ($($name:ident$(: $s:literal)?,)*) => {
62 #[derive(Debug, Clone, Copy)]
63 #[allow(non_snake_case)]
64 pub struct ConstName {
65 $(pub $name: &'static PyStrInterned,)*
66 }
67
68 impl ConstName {
69 unsafe fn new(pool: &StringPool, typ: &Py<PyType>) -> Self {
70 Self {
71 $($name: unsafe { pool.intern(declare_const_name!(@string $name $($s)?), typ.to_owned()) },)*
72 }
73 }
74 }
75 };
76 (@string $name:ident) => { stringify!($name) };
77 (@string $name:ident $string:literal) => { $string };
78}
79
80declare_const_name! {
81 True,
82 False,
83 None,
84 NotImplemented,
85 Ellipsis,
86
87 __abs__,
89 __abstractmethods__,
90 __add__,
91 __aenter__,
92 __aexit__,
93 __aiter__,
94 __alloc__,
95 __all__,
96 __and__,
97 __anext__,
98 __annotate__,
99 __annotate_func__,
100 __annotations__,
101 __annotations_cache__,
102 __args__,
103 __await__,
104 __bases__,
105 __bool__,
106 __buffer__,
107 __build_class__,
108 __builtins__,
109 __bytes__,
110 __cached__,
111 __call__,
112 __ceil__,
113 __cformat__,
114 __class__,
115 __class_getitem__,
116 __classcell__,
117 __classdictcell__,
118 __complex__,
119 __contains__,
120 __copy__,
121 __deepcopy__,
122 __del__,
123 __delattr__,
124 __delete__,
125 __delitem__,
126 __dict__,
127 __dir__,
128 __div__,
129 __divmod__,
130 __doc__,
131 __enter__,
132 __eq__,
133 __exit__,
134 __file__,
135 __firstlineno__,
136 __float__,
137 __floor__,
138 __floordiv__,
139 __format__,
140 __fspath__,
141 __ge__,
142 __get__,
143 __getattr__,
144 __getattribute__,
145 __getformat__,
146 __getitem__,
147 __getnewargs__,
148 __getnewargs_ex__,
149 __getstate__,
150 __gt__,
151 __hash__,
152 __iadd__,
153 __iand__,
154 __idiv__,
155 __ifloordiv__,
156 __ilshift__,
157 __imatmul__,
158 __imod__,
159 __import__,
160 __imul__,
161 __index__,
162 __init__,
163 __init_subclass__,
164 __instancecheck__,
165 __int__,
166 __invert__,
167 __ior__,
168 __ipow__,
169 __irshift__,
170 __isub__,
171 __iter__,
172 __itruediv__,
173 __ixor__,
174 __jit__, __le__,
176 __len__,
177 __length_hint__,
178 __lshift__,
179 __lt__,
180 __main__,
181 __match_args__,
182 __matmul__,
183 __missing__,
184 __mod__,
185 __module__,
186 __mro_entries__,
187 __mul__,
188 __name__,
189 __ne__,
190 __neg__,
191 __new__,
192 __next__,
193 __objclass__,
194 __or__,
195 __orig_bases__,
196 __orig_class__,
197 __origin__,
198 __parameters__,
199 __pos__,
200 __pow__,
201 __prepare__,
202 __qualname__,
203 __radd__,
204 __rand__,
205 __rdiv__,
206 __rdivmod__,
207 __reduce__,
208 __reduce_ex__,
209 __release_buffer__,
210 __repr__,
211 __reversed__,
212 __rfloordiv__,
213 __rlshift__,
214 __rmatmul__,
215 __rmod__,
216 __rmul__,
217 __ror__,
218 __round__,
219 __rpow__,
220 __rrshift__,
221 __rshift__,
222 __rsub__,
223 __rtruediv__,
224 __rxor__,
225 __set__,
226 __setattr__,
227 __setitem__,
228 __setstate__,
229 __set_name__,
230 __slots__,
231 __slotnames__,
232 __str__,
233 __sub__,
234 __subclasscheck__,
235 __subclasshook__,
236 __subclasses__,
237 __sizeof__,
238 __truediv__,
239 __trunc__,
240 __type_params__,
241 __typing_subst__,
242 __typing_is_unpacked_typevartuple__,
243 __typing_prepare_subst__,
244 __typing_unpacked_tuple_args__,
245 __weakref__,
246 __xor__,
247
248 _attributes,
250 _fields,
251 _defaultaction,
252 _onceregistry,
253 _showwarnmsg,
254 defaultaction,
255 onceregistry,
256 filters,
257 backslashreplace,
258 close,
259 copy,
260 decode,
261 encode,
262 flush,
263 ignore,
264 items,
265 keys,
266 modules,
267 mro,
268 n_fields,
269 n_sequence_fields,
270 n_unnamed_fields,
271 namereplace,
272 replace,
273 strict,
274 surrogateescape,
275 surrogatepass,
276 update,
277 utf_8: "utf-8",
278 values,
279 version,
280 WarningMessage,
281 xmlcharrefreplace,
282}
283
284impl Context {
286 pub const INT_CACHE_POOL_RANGE: core::ops::RangeInclusive<i32> = (-5)..=256;
287 const INT_CACHE_POOL_MIN: i32 = *Self::INT_CACHE_POOL_RANGE.start();
288
289 #[must_use]
290 pub fn genesis() -> &'static PyRc<Self> {
291 rustpython_common::static_cell! {
292 static CONTEXT: PyRc<Context>;
293 }
294 CONTEXT.get_or_init(|| {
295 let ctx = PyRc::new(Self::init_genesis());
296 let ctx_ref: &'static Self = unsafe { &*PyRc::as_ptr(&ctx) };
299 crate::types::TypeZoo::extend(ctx_ref);
300 crate::exceptions::ExceptionZoo::extend(ctx_ref);
301 ctx
302 })
303 }
304
305 fn init_genesis() -> Self {
306 flame_guard!("init Context");
307 let (types, empty_tuple) = TypeZoo::init();
308 let exceptions = exceptions::ExceptionZoo::init();
309
310 #[inline]
311 fn create_object<T: PyObjectPayload>(payload: T, cls: &'static Py<PyType>) -> PyRef<T> {
312 PyRef::new_ref(payload, cls.to_owned(), None)
313 }
314
315 let none = create_object(PyNone, PyNone::static_type());
316 let ellipsis = create_object(PyEllipsis, PyEllipsis::static_type());
317 let not_implemented = create_object(PyNotImplemented, PyNotImplemented::static_type());
318
319 let typing_no_default = create_object(
320 crate::stdlib::_typing::NoDefault,
321 crate::stdlib::_typing::NoDefault::static_type(),
322 );
323
324 let int_cache_pool = Self::INT_CACHE_POOL_RANGE
325 .map(|v| {
326 PyRef::new_ref(
327 PyInt::from(BigInt::from(v)),
328 types.int_type.to_owned(),
329 None,
330 )
331 })
332 .collect::<Vec<PyIntRef>>();
333
334 let string_pool = StringPool::default();
335
336 let latin1_char_cache = (u8::MIN..=u8::MAX)
339 .map(|b| {
340 let s = unsafe {
341 string_pool.intern(char::from(b).to_string(), types.str_type.to_owned())
342 };
343 s.to_owned()
344 })
345 .collect::<Vec<PyRef<PyStr>>>();
346
347 let ascii_char_cache = latin1_char_cache[..128].to_vec();
348
349 let true_value = create_object(PyBool(PyInt::from(1)), types.bool_type);
350 let false_value = create_object(PyBool(PyInt::from(0)), types.bool_type);
351
352 let empty_frozenset = PyRef::new_ref(
353 PyFrozenSet::default(),
354 types.frozenset_type.to_owned(),
355 None,
356 );
357
358 let names = unsafe { ConstName::new(&string_pool, types.str_type) };
359
360 let slot_new_wrapper = PyMethodDef::new_const(
361 names.__new__.as_str(),
362 PyType::__new__,
363 PyMethodFlags::METHOD,
364 ItemDoc::static_text(
365 "__new__($type, /, *args, **kwargs)\n--\n\nCreate and return a new object. See help(type) for accurate signature.",
366 ),
367 );
368 let empty_str = unsafe { string_pool.intern("", types.str_type.to_owned()) };
369 let empty_bytes = create_object(PyBytes::from(Vec::new()), types.bytes_type);
370
371 for obj in [
379 none.as_object(),
380 ellipsis.as_object(),
381 not_implemented.as_object(),
382 typing_no_default.as_object(),
383 true_value.as_object(),
384 false_value.as_object(),
385 empty_tuple.as_object(),
386 empty_frozenset.as_object(),
387 empty_bytes.as_object(),
388 empty_str.as_object(),
389 ] {
390 obj.make_immortal();
391 }
392 for int in &int_cache_pool {
393 int.as_object().make_immortal();
394 }
395 for s in &latin1_char_cache {
399 s.as_object().make_immortal();
400 }
401
402 Self {
405 true_value,
406 false_value,
407 none,
408 empty_tuple,
409 empty_frozenset,
410 empty_str,
411 empty_bytes,
412 ellipsis,
413
414 not_implemented,
415 typing_no_default,
416
417 types,
418 exceptions,
419 int_cache_pool,
420 latin1_char_cache,
421 ascii_char_cache,
422 string_pool,
423 slot_new_wrapper,
424 names,
425 }
426 }
427
428 pub fn intern_str<S: InternableString>(&self, s: S) -> &'static PyStrInterned {
429 unsafe { self.string_pool.intern(s, self.types.str_type.to_owned()) }
430 }
431
432 pub fn interned_str<S: MaybeInternedString + ?Sized>(
433 &self,
434 s: &S,
435 ) -> Option<&'static PyStrInterned> {
436 self.string_pool.interned(s)
437 }
438
439 #[inline(always)]
440 pub fn none(&self) -> PyObjectRef {
441 self.none.clone().into()
442 }
443
444 #[inline(always)]
445 pub fn not_implemented(&self) -> PyObjectRef {
446 self.not_implemented.clone().into()
447 }
448
449 #[inline]
450 pub fn empty_tuple_typed<T>(&self) -> &Py<PyTuple<T>> {
451 let py: &Py<PyTuple> = &self.empty_tuple;
452 unsafe { core::mem::transmute(py) }
453 }
454
455 pub fn new_pyref<T, P>(&self, value: T) -> PyRef<P>
457 where
458 T: Into<P>,
459 P: PyPayload + core::fmt::Debug,
460 {
461 value.into().into_ref(self)
462 }
463
464 #[inline]
467 pub fn new_int<T: Into<BigInt> + ToPrimitive>(&self, i: T) -> PyIntRef {
468 if let Some(i) = i.to_i32()
469 && Self::INT_CACHE_POOL_RANGE.contains(&i)
470 {
471 let inner_idx = (i - Self::INT_CACHE_POOL_MIN) as usize;
472 return self.int_cache_pool[inner_idx].clone();
473 }
474 PyInt::from(i).into_ref(self)
475 }
476
477 #[inline(always)]
479 pub(crate) fn cached_int(&self, i: i32) -> &Py<PyInt> {
480 debug_assert!(Self::INT_CACHE_POOL_RANGE.contains(&i));
481 let inner_idx = (i - Self::INT_CACHE_POOL_MIN) as usize;
482 &self.int_cache_pool[inner_idx]
483 }
484
485 #[inline]
486 pub fn new_bigint(&self, i: &BigInt) -> PyIntRef {
487 if let Some(i) = i.to_i32()
488 && Self::INT_CACHE_POOL_RANGE.contains(&i)
489 {
490 let inner_idx = (i - Self::INT_CACHE_POOL_MIN) as usize;
491 return self.int_cache_pool[inner_idx].clone();
492 }
493 PyInt::from(i.clone()).into_ref(self)
494 }
495
496 #[inline]
497 pub fn new_float(&self, value: f64) -> PyRef<PyFloat> {
498 PyFloat::from(value).into_ref(self)
499 }
500
501 #[inline]
502 pub fn new_complex(&self, value: Complex64) -> PyRef<PyComplex> {
503 PyComplex::from(value).into_ref(self)
504 }
505
506 #[inline]
507 pub fn latin1_char(&self, ch: u8) -> PyRef<PyStr> {
508 self.latin1_char_cache[ch as usize].clone()
509 }
510
511 #[inline]
512 fn latin1_singleton_index(s: &PyStr) -> Option<u8> {
513 let mut cps = s.as_wtf8().code_points();
514 let cp = cps.next()?;
515
516 cps.next()
517 .is_none()
518 .then(|| u8::try_from(cp.to_u32()).ok())?
519 }
520
521 #[inline]
522 pub fn new_str(&self, s: impl Into<pystr::PyStr>) -> PyRef<PyStr> {
523 let s = s.into();
524 if s.is_empty() {
525 self.empty_str.to_owned()
526 } else if let Some(ch) = Self::latin1_singleton_index(&s) {
527 self.latin1_char(ch)
528 } else {
529 s.into_ref(self)
530 }
531 }
532
533 #[inline]
534 pub fn new_utf8_str(&self, s: impl Into<PyUtf8Str>) -> PyRef<PyUtf8Str> {
535 s.into().into_ref(self)
536 }
537
538 pub fn interned_or_new_str<S, M>(&self, s: S) -> PyRef<PyStr>
539 where
540 S: Into<PyStr> + AsRef<M>,
541 M: MaybeInternedString,
542 {
543 if let Some(s) = self.interned_str(s.as_ref()) {
544 s.to_owned()
545 } else {
546 self.new_str(s)
547 }
548 }
549
550 #[inline]
551 pub fn new_bytes(&self, data: Vec<u8>) -> PyRef<PyBytes> {
552 if data.is_empty() {
553 self.empty_bytes.clone()
554 } else {
555 PyBytes::from(data).into_ref(self)
556 }
557 }
558
559 #[inline]
560 pub fn new_bytearray(&self, data: Vec<u8>) -> PyRef<PyByteArray> {
561 PyByteArray::from(data).into_ref(self)
562 }
563
564 #[inline(always)]
565 pub fn new_bool(&self, b: bool) -> PyRef<PyBool> {
566 let value = if b {
567 &self.true_value
568 } else {
569 &self.false_value
570 };
571 value.to_owned()
572 }
573
574 #[inline(always)]
575 pub fn new_tuple(&self, elements: Vec<PyObjectRef>) -> PyTupleRef {
576 PyTuple::new_ref(elements, self)
577 }
578
579 #[inline(always)]
580 pub fn new_list(&self, elements: Vec<PyObjectRef>) -> PyListRef {
581 PyList::from(elements).into_ref(self)
582 }
583
584 #[inline(always)]
585 pub fn new_dict(&self) -> PyDictRef {
586 PyDict::default().into_ref(self)
587 }
588
589 pub fn new_class(
590 &self,
591 module: Option<&str>,
592 name: &str,
593 base: PyTypeRef,
594 slots: PyTypeSlots,
595 ) -> PyTypeRef {
596 let mut attrs = PyAttributes::default();
597 if let Some(module) = module {
598 attrs.insert(identifier!(self, __module__), self.new_str(module).into());
599 };
600 PyType::new_heap(
601 name,
602 vec![base],
603 attrs,
604 slots,
605 self.types.type_type.to_owned(),
606 self,
607 )
608 .unwrap()
609 }
610
611 pub fn new_exception_type(
612 &self,
613 module: &str,
614 name: &str,
615 bases: Option<Vec<PyTypeRef>>,
616 ) -> PyTypeRef {
617 self.new_exception_type_with_doc(module, name, bases, ItemDoc::NONE)
618 }
619
620 pub fn new_exception_type_with_doc(
622 &self,
623 module: &str,
624 name: &str,
625 bases: Option<Vec<PyTypeRef>>,
626 doc: ItemDoc,
627 ) -> PyTypeRef {
628 let bases = bases.unwrap_or_else(|| vec![self.exceptions.exception_type.to_owned()]);
629 let mut attrs = PyAttributes::default();
630 attrs.insert(identifier!(self, __module__), self.new_str(module).into());
631 if let Some(text) = plain_doc(doc) {
632 attrs.insert(identifier!(self, __doc__), self.new_str(text).into());
633 }
634
635 let interned_name = self.intern_str(name);
636 let slots = PyTypeSlots {
637 name: interned_name.as_str(),
638 basicsize: 0,
639 flags: crate::types::AtomicPyTypeFlags::from_plain(PyTypeFlags::HEAP_TYPE_WITH_DICT),
640 ..PyTypeSlots::default()
641 };
642 PyType::new_heap(
643 name,
644 bases,
645 attrs,
646 slots,
647 self.types.type_type.to_owned(),
648 self,
649 )
650 .unwrap()
651 }
652
653 pub fn new_method_def<F, FKind>(
654 &self,
655 name: &'static str,
656 f: F,
657 flags: PyMethodFlags,
658 doc: ItemDoc,
659 ) -> PyRef<HeapMethodDef>
660 where
661 F: IntoPyNativeFn<FKind>,
662 {
663 let def = PyMethodDef {
664 name,
665 func: Box::leak(Box::new(f.into_func())),
666 flags,
667 #[cfg(feature = "doc")]
668 doc_off: doc.offset,
669 #[cfg(feature = "doc")]
670 doc_len: doc.len,
671 #[cfg(feature = "doc")]
672 doc_body_pending: false,
673 doc: doc.text,
674 };
675 let payload = HeapMethodDef::new(def);
676 PyRef::new_ref(payload, self.types.method_def.to_owned(), None)
677 }
678
679 #[inline]
680 pub fn new_member(
681 &self,
682 name: &str,
683 kind: MemberKind,
684 offset: isize,
685 flags: PyMemberFlags,
686 class: &'static Py<PyType>,
687 doc: ItemDoc,
688 ) -> PyRef<PyMemberDescriptor> {
689 let member_descriptor = PyMemberDescriptor {
690 common: PyDescriptorOwned {
691 typ: class.to_owned(),
692 name: self.intern_str(name),
693 qualname: PyRwLock::new(None),
694 },
695 member: PyMemberDef {
696 name: name.to_owned(),
697 kind,
698 offset,
699 flags,
700 doc,
701 },
702 access: MemberAccess::Offset,
703 };
704 member_descriptor.into_ref(self)
705 }
706
707 pub fn new_readonly_tuple_member(
708 &self,
709 name: &str,
710 class: &'static Py<PyType>,
711 index: usize,
712 doc: ItemDoc,
713 ) -> PyRef<PyMemberDescriptor> {
714 let member_descriptor = PyMemberDescriptor {
715 common: PyDescriptorOwned {
716 typ: class.to_owned(),
717 name: self.intern_str(name),
718 qualname: PyRwLock::new(None),
719 },
720 member: PyMemberDef {
721 name: name.to_owned(),
722 kind: MemberKind::Object,
723 offset: index as isize,
724 flags: PyMemberFlags::READONLY,
725 doc,
726 },
727 access: MemberAccess::TupleItem,
728 };
729 member_descriptor.into_ref(self)
730 }
731
732 pub fn new_readonly_getset<F, T>(
733 &self,
734 name: &str,
735 class: &'static Py<PyType>,
736 f: F,
737 ) -> PyRef<PyGetSet>
738 where
739 F: IntoPyGetterFunc<T>,
740 {
741 let getset = PyGetSet::new(name, class, self).with_get(f);
742 PyRef::new_ref(getset, self.types.getset_type.to_owned(), None)
743 }
744
745 pub fn new_static_getset<G, S, T, U>(
746 &self,
747 name: &str,
748 class: &'static Py<PyType>,
749 g: G,
750 s: S,
751 ) -> PyRef<PyGetSet>
752 where
753 G: IntoPyGetterFunc<T>,
754 S: IntoPySetterFunc<U>,
755 {
756 let getset = PyGetSet::new(name, class, self).with_get(g).with_set(s);
757 PyRef::new_ref(getset, self.types.getset_type.to_owned(), None)
758 }
759
760 pub fn new_getset<G, S, T, U>(
762 &self,
763 name: &str,
764 class: &Py<PyType>,
765 g: G,
766 s: S,
767 ) -> PyRef<PyGetSet>
768 where
769 G: IntoPyGetterFunc<T>,
770 S: IntoPySetterFunc<U>,
771 {
772 let getset = PyGetSet::new(name, class, self).with_get(g).with_set(s);
773 PyRef::new_ref(getset, self.types.getset_type.to_owned(), None)
774 }
775
776 pub fn new_base_object(&self, class: PyTypeRef, dict: Option<PyDictRef>) -> PyObjectRef {
777 debug_assert_eq!(
778 class.slots.flags.has_feature(PyTypeFlags::HAS_DICT),
779 dict.is_some()
780 );
781 PyRef::new_ref(object::PyBaseObject, class, dict).into()
782 }
783
784 pub fn new_code(&self, code: impl code::IntoCodeObject) -> PyRef<PyCode> {
785 let code = code.into_code_object(self);
786 PyRef::new_ref(PyCode::new(code), self.types.code_type.to_owned(), None)
787 }
788
789 pub fn new_capsule(
790 &self,
791 ptr: *mut c_void,
792 name: Option<&'static CStr>,
793 destructor: Option<unsafe extern "C" fn(_: *mut PyObject)>,
794 ) -> PyRef<PyCapsule> {
795 PyCapsule::new(ptr, name, destructor).into_ref(self)
796 }
797}
798
799impl AsRef<Self> for Context {
800 fn as_ref(&self) -> &Self {
801 self
802 }
803}