mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix marshal recursive reference loading (#8501)
* Fix marshal recursive reference loading Create reference-tracked containers before reading their children so recursive list, dict, set, and tuple graphs can be unmarshaled. Preserve interned string markers through the runtime bag and add an initialization-only tuple construction path. Assisted-by: Codex:gpt-5 * Preserve marshal container insertion errors Keep Python exceptions raised while constructing unmarshaled sets, frozensets, and dictionaries instead of collapsing them into ValueError. This makes abnormal recursive hash-container streams report TypeError like CPython and removes the remaining test_marshal expected failure. Assisted-by: Codex:gpt-5 * Enable full abnormal marshal reference loop test * Test direct marshal tuple reference loop Assisted-by: Codex:gpt-5
This commit is contained in:
1 parent
9de06ccdb3
commit
d64cc2cff5
4 files changed
+366
-89
No files matched your search
Vendored
+7
-14
@@ -319,7 +319,6 @@ class BugsTestCase(unittest.TestCase):
|
||||
last.append([0])
|
||||
self.assertRaises(ValueError, marshal.dumps, head)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_reference_loop_list(self):
|
||||
a = []
|
||||
a.append(a)
|
||||
@@ -331,7 +330,6 @@ class BugsTestCase(unittest.TestCase):
|
||||
self.assertIsInstance(b, list)
|
||||
self.assertIs(b[0], b)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_reference_loop_dict(self):
|
||||
a = {}
|
||||
a[None] = a
|
||||
@@ -343,7 +341,6 @@ class BugsTestCase(unittest.TestCase):
|
||||
self.assertIsInstance(b, dict)
|
||||
self.assertIs(b[None], b)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_reference_loop_tuple(self):
|
||||
a = ([],)
|
||||
a[0].append(a)
|
||||
@@ -387,21 +384,18 @@ class BugsTestCase(unittest.TestCase):
|
||||
for v in range(marshal.version + 1):
|
||||
self.assertRaises(ValueError, marshal.dumps, a, v)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_loads_reference_loop_list(self):
|
||||
data = b'\xdb\x01\x00\x00\x00r\x00\x00\x00\x00' # [<R>]
|
||||
a = marshal.loads(data)
|
||||
self.assertIsInstance(a, list)
|
||||
self.assertIs(a[0], a)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_loads_reference_loop_dict(self):
|
||||
data = b'\xfbNr\x00\x00\x00\x000' # {None: <R>}
|
||||
a = marshal.loads(data)
|
||||
self.assertIsInstance(a, dict)
|
||||
self.assertIs(a[None], a)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError: bad marshal data
|
||||
def test_loads_abnormal_reference_loops(self):
|
||||
# Indirect self-references of tuples.
|
||||
data = b'\xa8\x01\x00\x00\x00[\x01\x00\x00\x00r\x00\x00\x00\x00' # ([<R>],)
|
||||
@@ -416,13 +410,13 @@ class BugsTestCase(unittest.TestCase):
|
||||
self.assertIsInstance(a[0], dict)
|
||||
self.assertIs(a[0][None], a)
|
||||
|
||||
# Direct self-reference which cannot be created in Python.
|
||||
# This creates a reference loop which cannot be collected.
|
||||
if False:
|
||||
data = b'\xa8\x01\x00\x00\x00r\x00\x00\x00\x00' # (<R>,)
|
||||
a = marshal.loads(data)
|
||||
self.assertIsInstance(a, tuple)
|
||||
self.assertIs(a[0], a)
|
||||
# Direct self-reference which cannot be created in Python. CPython
|
||||
# leaves this disabled because its reference counting cannot collect
|
||||
# the resulting cycle; RustPython's tracing collector can.
|
||||
data = b'\xa8\x01\x00\x00\x00r\x00\x00\x00\x00' # (<R>,)
|
||||
a = marshal.loads(data)
|
||||
self.assertIsInstance(a, tuple)
|
||||
self.assertIs(a[0], a)
|
||||
|
||||
# Direct self-references which cannot be created in Python
|
||||
# because of unhashability.
|
||||
@@ -748,7 +742,6 @@ class InterningTestCase(unittest.TestCase, HelperMixin):
|
||||
strobj = "this is an interned string"
|
||||
strobj = sys.intern(strobj)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def testIntern(self):
|
||||
s = marshal.loads(marshal.dumps(self.strobj))
|
||||
self.assertEqual(s, self.strobj)
|
||||
|
||||
@@ -516,7 +516,7 @@ fn read_const_value<R: Read, Bag: ConstantBag>(
|
||||
let code = deserialize_code_inner(rdr, bag, depth - 1, refs)?;
|
||||
bag.make_code(code)
|
||||
} else {
|
||||
deserialize_value_typed(rdr, bag, depth, refs, typ)?
|
||||
deserialize_value_typed(rdr, bag, depth, refs, typ, slot)?
|
||||
};
|
||||
if let Some(idx) = slot {
|
||||
refs[idx] = Some(value.clone());
|
||||
@@ -540,6 +540,10 @@ pub trait MarshalBag: Copy {
|
||||
|
||||
fn make_str(&self, value: &Wtf8) -> Self::Value;
|
||||
|
||||
fn make_interned_str(&self, value: &Wtf8) -> Self::Value {
|
||||
self.make_str(value)
|
||||
}
|
||||
|
||||
fn make_bytes(&self, value: &[u8]) -> Self::Value;
|
||||
|
||||
fn make_int(&self, value: BigInt) -> Self::Value;
|
||||
@@ -564,6 +568,51 @@ pub trait MarshalBag: Copy {
|
||||
it: impl Iterator<Item = (Self::Value, Self::Value)>,
|
||||
) -> Result<Self::Value>;
|
||||
|
||||
/// Install partially-built containers in the marshal reference table
|
||||
/// before reading their children, as CPython's `r_object()` does.
|
||||
/// Runtime bags can opt in; constant bags retain collect-then-construct.
|
||||
fn make_tuple_placeholder(&self, _len: usize) -> Option<Self::Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_tuple_item(
|
||||
&self,
|
||||
_tuple: &Self::Value,
|
||||
_index: usize,
|
||||
_value: Self::Value,
|
||||
) -> Result<()> {
|
||||
Err(MarshalError::BadType)
|
||||
}
|
||||
|
||||
fn make_list_placeholder(&self, _len: usize) -> Option<Self::Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_list_item(&self, _list: &Self::Value, _index: usize, _value: Self::Value) -> Result<()> {
|
||||
Err(MarshalError::BadType)
|
||||
}
|
||||
|
||||
fn make_set_placeholder(&self) -> Option<Self::Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn insert_set_item(&self, _set: &Self::Value, _value: Self::Value) -> Result<()> {
|
||||
Err(MarshalError::BadType)
|
||||
}
|
||||
|
||||
fn make_dict_placeholder(&self) -> Option<Self::Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn insert_dict_item(
|
||||
&self,
|
||||
_dict: &Self::Value,
|
||||
_key: Self::Value,
|
||||
_value: Self::Value,
|
||||
) -> Result<()> {
|
||||
Err(MarshalError::BadType)
|
||||
}
|
||||
|
||||
fn make_slice(
|
||||
&self,
|
||||
_start: Self::Value,
|
||||
@@ -755,7 +804,7 @@ fn deserialize_value_after_header<R: Read, Bag: MarshalBag>(
|
||||
let code = deserialize_code_inner(rdr, bag.constant_bag(), depth - 1, &mut inner_refs)?;
|
||||
bag.make_code(code)
|
||||
} else {
|
||||
deserialize_value_typed(rdr, bag, depth, refs, typ)?
|
||||
deserialize_value_typed(rdr, bag, depth, refs, typ, slot)?
|
||||
};
|
||||
|
||||
if let Some(idx) = slot {
|
||||
@@ -770,6 +819,7 @@ fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
|
||||
depth: usize,
|
||||
refs: &mut Vec<Option<Bag::Value>>,
|
||||
typ: Type,
|
||||
slot: Option<usize>,
|
||||
) -> Result<Bag::Value> {
|
||||
if depth == 0 {
|
||||
return Err(MarshalError::InvalidBytecode);
|
||||
@@ -806,21 +856,42 @@ fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
|
||||
let value = Complex64 { re, im };
|
||||
bag.make_complex(value)
|
||||
}
|
||||
Type::Ascii | Type::AsciiInterned | Type::Unicode | Type::Interned => {
|
||||
Type::Ascii | Type::Unicode => {
|
||||
let len = rdr.read_u32()?;
|
||||
let value = rdr.read_wtf8(len)?;
|
||||
bag.make_str(value)
|
||||
}
|
||||
Type::ShortAscii | Type::ShortAsciiInterned => {
|
||||
Type::AsciiInterned | Type::Interned => {
|
||||
let len = rdr.read_u32()?;
|
||||
let value = rdr.read_wtf8(len)?;
|
||||
bag.make_interned_str(value)
|
||||
}
|
||||
Type::ShortAscii => {
|
||||
let len = rdr.read_u8()? as u32;
|
||||
let value = rdr.read_wtf8(len)?;
|
||||
bag.make_str(value)
|
||||
}
|
||||
Type::ShortAsciiInterned => {
|
||||
let len = rdr.read_u8()? as u32;
|
||||
let value = rdr.read_wtf8(len)?;
|
||||
bag.make_interned_str(value)
|
||||
}
|
||||
Type::SmallTuple => {
|
||||
let len = rdr.read_u8()? as usize;
|
||||
let d = depth - 1;
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_tuple(it))?
|
||||
if let Some(index) = slot
|
||||
&& let Some(tuple) = bag.make_tuple_placeholder(len)
|
||||
{
|
||||
refs[index] = Some(tuple.clone());
|
||||
for item_index in 0..len {
|
||||
let item = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
bag.set_tuple_item(&tuple, item_index, item)?;
|
||||
}
|
||||
tuple
|
||||
} else {
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_tuple(it))?
|
||||
}
|
||||
}
|
||||
Type::Null => {
|
||||
return Err(MarshalError::BadType);
|
||||
@@ -830,22 +901,55 @@ fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
|
||||
return Err(MarshalError::BadType);
|
||||
}
|
||||
Type::Tuple => {
|
||||
let len = rdr.read_u32()?;
|
||||
let len = rdr.read_u32()? as usize;
|
||||
let d = depth - 1;
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_tuple(it))?
|
||||
if let Some(index) = slot
|
||||
&& let Some(tuple) = bag.make_tuple_placeholder(len)
|
||||
{
|
||||
refs[index] = Some(tuple.clone());
|
||||
for item_index in 0..len {
|
||||
let item = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
bag.set_tuple_item(&tuple, item_index, item)?;
|
||||
}
|
||||
tuple
|
||||
} else {
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_tuple(it))?
|
||||
}
|
||||
}
|
||||
Type::List => {
|
||||
let len = rdr.read_u32()?;
|
||||
let len = rdr.read_u32()? as usize;
|
||||
let d = depth - 1;
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_list(it))??
|
||||
if let Some(index) = slot
|
||||
&& let Some(list) = bag.make_list_placeholder(len)
|
||||
{
|
||||
refs[index] = Some(list.clone());
|
||||
for item_index in 0..len {
|
||||
let item = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
bag.set_list_item(&list, item_index, item)?;
|
||||
}
|
||||
list
|
||||
} else {
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_list(it))??
|
||||
}
|
||||
}
|
||||
Type::Set => {
|
||||
let len = rdr.read_u32()?;
|
||||
let len = rdr.read_u32()? as usize;
|
||||
let d = depth - 1;
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_set(it))??
|
||||
if let Some(index) = slot
|
||||
&& let Some(set) = bag.make_set_placeholder()
|
||||
{
|
||||
refs[index] = Some(set.clone());
|
||||
for _ in 0..len {
|
||||
let item = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
bag.insert_set_item(&set, item)?;
|
||||
}
|
||||
set
|
||||
} else {
|
||||
let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
|
||||
itertools::process_results(it, |it| bag.make_set(it))??
|
||||
}
|
||||
}
|
||||
Type::FrozenSet => {
|
||||
let len = rdr.read_u32()?;
|
||||
@@ -855,17 +959,33 @@ fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
|
||||
}
|
||||
Type::Dict => {
|
||||
let d = depth - 1;
|
||||
let mut pairs = Vec::new();
|
||||
loop {
|
||||
let raw = rdr.read_u8()?;
|
||||
if raw & !FLAG_REF == b'0' {
|
||||
break;
|
||||
if let Some(index) = slot
|
||||
&& let Some(dict) = bag.make_dict_placeholder()
|
||||
{
|
||||
refs[index] = Some(dict.clone());
|
||||
loop {
|
||||
let raw = rdr.read_u8()?;
|
||||
if raw & !FLAG_REF == b'0' {
|
||||
break;
|
||||
}
|
||||
let key = deserialize_value_after_header(rdr, bag, d, refs, raw)?;
|
||||
let value = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
bag.insert_dict_item(&dict, key, value)?;
|
||||
}
|
||||
let k = deserialize_value_after_header(rdr, bag, d, refs, raw)?;
|
||||
let v = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
pairs.push((k, v));
|
||||
dict
|
||||
} else {
|
||||
let mut pairs = Vec::new();
|
||||
loop {
|
||||
let raw = rdr.read_u8()?;
|
||||
if raw & !FLAG_REF == b'0' {
|
||||
break;
|
||||
}
|
||||
let key = deserialize_value_after_header(rdr, bag, d, refs, raw)?;
|
||||
let value = deserialize_value_depth(rdr, bag, d, refs)?;
|
||||
pairs.push((key, value));
|
||||
}
|
||||
bag.make_dict(pairs.into_iter())?
|
||||
}
|
||||
bag.make_dict(pairs.into_iter())?
|
||||
}
|
||||
Type::Bytes => {
|
||||
// After marshaling, byte arrays are converted into bytes.
|
||||
|
||||
@@ -23,12 +23,60 @@ use crate::{
|
||||
vm::VirtualMachine,
|
||||
};
|
||||
use alloc::fmt;
|
||||
use core::cell::Cell;
|
||||
use core::cell::{Cell, UnsafeCell};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
#[pyclass(module = false, name = "tuple", traverse = "manual")]
|
||||
pub struct PyTuple<R = PyObjectRef> {
|
||||
elements: Box<[R]>,
|
||||
elements: TupleElements<R>,
|
||||
}
|
||||
|
||||
/// Tuple storage is immutable after publication, but marshal must publish a
|
||||
/// tuple in its reference table before recursively reading its children.
|
||||
/// This mirrors CPython's `PyTuple_New` followed by `PyTuple_SET_ITEM`.
|
||||
struct TupleElements<R>(UnsafeCell<Box<[R]>>);
|
||||
|
||||
unsafe impl<R: Send> Send for TupleElements<R> {}
|
||||
unsafe impl<R: Sync> Sync for TupleElements<R> {}
|
||||
|
||||
impl<R> TupleElements<R> {
|
||||
const fn new(elements: Box<[R]>) -> Self {
|
||||
Self(UnsafeCell::new(elements))
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> &[R] {
|
||||
// SAFETY: initialization writes happen only while the tuple is owned by
|
||||
// the synchronous marshal decoder; afterwards the storage is immutable.
|
||||
unsafe { &*self.0.get() }
|
||||
}
|
||||
|
||||
fn get_mut(&mut self) -> &mut Box<[R]> {
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The tuple must still be in its private initialization phase, and each
|
||||
/// placeholder index must be replaced at most once before it is observable.
|
||||
unsafe fn set_initializing(&self, index: usize, value: R) {
|
||||
unsafe { (*self.0.get())[index] = value };
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> core::ops::Deref for TupleElements<R> {
|
||||
type Target = [R];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> IntoIterator for &'a TupleElements<R> {
|
||||
type Item = &'a R;
|
||||
type IntoIter = core::slice::Iter<'a, R>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> fmt::Debug for PyTuple<R> {
|
||||
@@ -42,11 +90,11 @@ impl<R> fmt::Debug for PyTuple<R> {
|
||||
// Note: Only impl for PyTuple<PyObjectRef> (the default)
|
||||
unsafe impl Traverse for PyTuple {
|
||||
fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
|
||||
self.elements.traverse(traverse_fn);
|
||||
self.elements.as_slice().traverse(traverse_fn);
|
||||
}
|
||||
|
||||
fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
|
||||
let elements = core::mem::take(&mut self.elements);
|
||||
let elements = core::mem::take(self.elements.get_mut());
|
||||
out.extend(elements.into_vec());
|
||||
}
|
||||
}
|
||||
@@ -206,7 +254,7 @@ impl Constructor for PyTuple {
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, elements: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
elements: elements.into_boxed_slice(),
|
||||
elements: TupleElements::new(elements.into_boxed_slice()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -245,19 +293,19 @@ impl<'a, R> core::iter::IntoIterator for &'a Py<PyTuple<R>> {
|
||||
|
||||
impl<R> PyTuple<R> {
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[R] {
|
||||
pub fn as_slice(&self) -> &[R] {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elements.is_empty()
|
||||
}
|
||||
|
||||
@@ -274,7 +322,13 @@ impl PyTuple<PyObjectRef> {
|
||||
ctx.empty_tuple.clone()
|
||||
} else {
|
||||
let elements = elements.into_boxed_slice();
|
||||
PyRef::new_ref(Self { elements }, ctx.types.tuple_type.to_owned(), None)
|
||||
PyRef::new_ref(
|
||||
Self {
|
||||
elements: TupleElements::new(elements),
|
||||
},
|
||||
ctx.types.tuple_type.to_owned(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +337,20 @@ impl PyTuple<PyObjectRef> {
|
||||
/// Calling this function implies trying micro optimization for non-zero-sized tuple.
|
||||
#[must_use]
|
||||
pub const fn new_unchecked(elements: Box<[PyObjectRef]>) -> Self {
|
||||
Self { elements }
|
||||
Self {
|
||||
elements: TupleElements::new(elements),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_marshal_placeholder(len: usize, ctx: &Context) -> PyRef<Self> {
|
||||
Self::new_ref(vec![ctx.none(); len], ctx)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This tuple must be a marshal placeholder which has not escaped the
|
||||
/// decoder, and `index` must not have been replaced previously.
|
||||
pub(crate) unsafe fn set_marshal_item(&self, index: usize, value: PyObjectRef) {
|
||||
unsafe { self.elements.set_initializing(index, value) };
|
||||
}
|
||||
|
||||
fn repeat(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
@@ -298,7 +365,10 @@ impl PyTuple<PyObjectRef> {
|
||||
} else {
|
||||
let v = zelf.elements.mul(vm, value)?;
|
||||
let elements = v.into_boxed_slice();
|
||||
Self { elements }.into_ref(&vm.ctx)
|
||||
Self {
|
||||
elements: TupleElements::new(elements),
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -341,7 +411,10 @@ impl PyTuple {
|
||||
.chain(other.as_slice())
|
||||
.cloned()
|
||||
.collect::<Box<[_]>>();
|
||||
Self { elements }.into_ref(&vm.ctx)
|
||||
Self {
|
||||
elements: TupleElements::new(elements),
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
}
|
||||
});
|
||||
PyArithmeticValue::from_option(added.ok())
|
||||
@@ -360,7 +433,7 @@ impl PyTuple {
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn __len__(&self) -> usize {
|
||||
pub fn __len__(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
@@ -425,7 +498,7 @@ impl PyTuple {
|
||||
let tup_arg = if zelf.class().is(vm.ctx.types.tuple_type) {
|
||||
zelf
|
||||
} else {
|
||||
Self::new_ref(zelf.elements.clone().into_vec(), &vm.ctx)
|
||||
Self::new_ref(zelf.elements.as_slice().to_vec(), &vm.ctx)
|
||||
};
|
||||
(tup_arg,)
|
||||
}
|
||||
|
||||
+128
-37
@@ -9,14 +9,16 @@ mod decl {
|
||||
use crate::{
|
||||
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{
|
||||
PyBool, PyByteArray, PyBytes, PyCode, PyComplex, PyDict, PyEllipsis, PyFloat,
|
||||
PyFrozenSet, PyInt, PyList, PyNone, PySet, PyStopIteration, PyStr, PyTuple,
|
||||
PyBaseExceptionRef, PyBool, PyByteArray, PyBytes, PyCode, PyComplex, PyDict,
|
||||
PyEllipsis, PyFloat, PyFrozenSet, PyInt, PyList, PyNone, PySet, PyStopIteration, PyStr,
|
||||
PyTuple,
|
||||
},
|
||||
convert::ToPyObject,
|
||||
function::{ArgBytesLike, OptionalArg},
|
||||
object::{AsObject, PyPayload},
|
||||
protocol::PyBuffer,
|
||||
};
|
||||
use core::cell::RefCell;
|
||||
use malachite_bigint::BigInt;
|
||||
use num_traits::Zero;
|
||||
use rustpython_compiler_core::marshal::{self, DumpableValue};
|
||||
@@ -386,79 +388,165 @@ mod decl {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct PyMarshalBag<'a>(&'a VirtualMachine);
|
||||
struct PyMarshalBag<'a> {
|
||||
vm: &'a VirtualMachine,
|
||||
pending_error: &'a RefCell<Option<PyBaseExceptionRef>>,
|
||||
}
|
||||
|
||||
impl<'a> PyMarshalBag<'a> {
|
||||
fn new(
|
||||
vm: &'a VirtualMachine,
|
||||
pending_error: &'a RefCell<Option<PyBaseExceptionRef>>,
|
||||
) -> Self {
|
||||
Self { vm, pending_error }
|
||||
}
|
||||
|
||||
fn remember_python_error(&self, error: PyBaseExceptionRef) -> marshal::MarshalError {
|
||||
let mut pending = self.pending_error.borrow_mut();
|
||||
if pending.is_none() {
|
||||
*pending = Some(error);
|
||||
}
|
||||
marshal::MarshalError::BadType
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> marshal::MarshalBag for PyMarshalBag<'a> {
|
||||
type Value = PyObjectRef;
|
||||
type ConstantBag = PyVmBag<'a>;
|
||||
|
||||
fn make_bool(&self, value: bool) -> Self::Value {
|
||||
self.0.ctx.new_bool(value).into()
|
||||
self.vm.ctx.new_bool(value).into()
|
||||
}
|
||||
fn make_none(&self) -> Self::Value {
|
||||
self.0.ctx.none()
|
||||
self.vm.ctx.none()
|
||||
}
|
||||
fn make_ellipsis(&self) -> Self::Value {
|
||||
self.0.ctx.ellipsis.clone().into()
|
||||
self.vm.ctx.ellipsis.clone().into()
|
||||
}
|
||||
fn make_float(&self, value: f64) -> Self::Value {
|
||||
self.0.ctx.new_float(value).into()
|
||||
self.vm.ctx.new_float(value).into()
|
||||
}
|
||||
fn make_complex(&self, value: num_complex::Complex64) -> Self::Value {
|
||||
self.0.ctx.new_complex(value).into()
|
||||
self.vm.ctx.new_complex(value).into()
|
||||
}
|
||||
fn make_str(&self, value: &Wtf8) -> Self::Value {
|
||||
self.0.ctx.new_str(value).into()
|
||||
self.vm.ctx.new_str(value).into()
|
||||
}
|
||||
fn make_interned_str(&self, value: &Wtf8) -> Self::Value {
|
||||
self.vm.ctx.intern_str(value).to_owned().into()
|
||||
}
|
||||
fn make_bytes(&self, value: &[u8]) -> Self::Value {
|
||||
self.0.ctx.new_bytes(value.to_vec()).into()
|
||||
self.vm.ctx.new_bytes(value.to_vec()).into()
|
||||
}
|
||||
fn make_int(&self, value: BigInt) -> Self::Value {
|
||||
self.0.ctx.new_int(value).into()
|
||||
self.vm.ctx.new_int(value).into()
|
||||
}
|
||||
fn make_tuple(&self, elements: impl Iterator<Item = Self::Value>) -> Self::Value {
|
||||
self.0.ctx.new_tuple(elements.collect()).into()
|
||||
self.vm.ctx.new_tuple(elements.collect()).into()
|
||||
}
|
||||
fn make_tuple_placeholder(&self, len: usize) -> Option<Self::Value> {
|
||||
Some(PyTuple::new_marshal_placeholder(len, &self.vm.ctx).into())
|
||||
}
|
||||
fn set_tuple_item(
|
||||
&self,
|
||||
tuple: &Self::Value,
|
||||
index: usize,
|
||||
value: Self::Value,
|
||||
) -> Result<(), marshal::MarshalError> {
|
||||
let tuple = tuple
|
||||
.downcast_ref::<PyTuple>()
|
||||
.ok_or(marshal::MarshalError::BadType)?;
|
||||
// SAFETY: compiler-core calls this only on a fresh placeholder,
|
||||
// once per index, before returning it to Python code.
|
||||
unsafe { tuple.set_marshal_item(index, value) };
|
||||
Ok(())
|
||||
}
|
||||
fn make_code(&self, code: CodeObject) -> Self::Value {
|
||||
crate::builtins::PyCode::new_ref_with_bag(self.0, code).into()
|
||||
crate::builtins::PyCode::new_ref_with_bag(self.vm, code).into()
|
||||
}
|
||||
fn make_stop_iter(&self) -> Result<Self::Value, marshal::MarshalError> {
|
||||
Ok(self.0.ctx.exceptions.stop_iteration.to_owned().into())
|
||||
Ok(self.vm.ctx.exceptions.stop_iteration.to_owned().into())
|
||||
}
|
||||
fn make_list(
|
||||
&self,
|
||||
it: impl Iterator<Item = Self::Value>,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
Ok(self.0.ctx.new_list(it.collect()).into())
|
||||
Ok(self.vm.ctx.new_list(it.collect()).into())
|
||||
}
|
||||
fn make_list_placeholder(&self, len: usize) -> Option<Self::Value> {
|
||||
Some(self.vm.ctx.new_list(vec![self.vm.ctx.none(); len]).into())
|
||||
}
|
||||
fn set_list_item(
|
||||
&self,
|
||||
list: &Self::Value,
|
||||
index: usize,
|
||||
value: Self::Value,
|
||||
) -> Result<(), marshal::MarshalError> {
|
||||
let list = list
|
||||
.downcast_ref::<PyList>()
|
||||
.ok_or(marshal::MarshalError::BadType)?;
|
||||
list.borrow_vec_mut()[index] = value;
|
||||
Ok(())
|
||||
}
|
||||
fn make_set(
|
||||
&self,
|
||||
it: impl Iterator<Item = Self::Value>,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
let set = PySet::default().into_ref(&self.0.ctx);
|
||||
let set = PySet::default().into_ref(&self.vm.ctx);
|
||||
for elem in it {
|
||||
set.add(elem, self.0).unwrap()
|
||||
set.add(elem, self.vm)
|
||||
.map_err(|error| self.remember_python_error(error))?;
|
||||
}
|
||||
Ok(set.into())
|
||||
}
|
||||
fn make_set_placeholder(&self) -> Option<Self::Value> {
|
||||
Some(PySet::default().into_ref(&self.vm.ctx).into())
|
||||
}
|
||||
fn insert_set_item(
|
||||
&self,
|
||||
set: &Self::Value,
|
||||
value: Self::Value,
|
||||
) -> Result<(), marshal::MarshalError> {
|
||||
let set = set
|
||||
.downcast_ref::<PySet>()
|
||||
.ok_or(marshal::MarshalError::BadType)?;
|
||||
set.add(value, self.vm)
|
||||
.map_err(|error| self.remember_python_error(error))
|
||||
}
|
||||
fn make_frozenset(
|
||||
&self,
|
||||
it: impl Iterator<Item = Self::Value>,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
Ok(PyFrozenSet::from_iter(self.0, it)
|
||||
.unwrap()
|
||||
.to_pyobject(self.0))
|
||||
PyFrozenSet::from_iter(self.vm, it)
|
||||
.map(|set| set.to_pyobject(self.vm))
|
||||
.map_err(|error| self.remember_python_error(error))
|
||||
}
|
||||
fn make_dict(
|
||||
&self,
|
||||
it: impl Iterator<Item = (Self::Value, Self::Value)>,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
let dict = self.0.ctx.new_dict();
|
||||
let dict = self.vm.ctx.new_dict();
|
||||
for (k, v) in it {
|
||||
dict.set_item(&*k, v, self.0).unwrap()
|
||||
dict.set_item(&*k, v, self.vm)
|
||||
.map_err(|error| self.remember_python_error(error))?;
|
||||
}
|
||||
Ok(dict.into())
|
||||
}
|
||||
fn make_dict_placeholder(&self) -> Option<Self::Value> {
|
||||
Some(self.vm.ctx.new_dict().into())
|
||||
}
|
||||
fn insert_dict_item(
|
||||
&self,
|
||||
dict: &Self::Value,
|
||||
key: Self::Value,
|
||||
value: Self::Value,
|
||||
) -> Result<(), marshal::MarshalError> {
|
||||
let dict = dict
|
||||
.downcast_ref::<PyDict>()
|
||||
.ok_or(marshal::MarshalError::BadType)?;
|
||||
dict.set_item(&*key, value, self.vm)
|
||||
.map_err(|error| self.remember_python_error(error))
|
||||
}
|
||||
fn make_slice(
|
||||
&self,
|
||||
start: Self::Value,
|
||||
@@ -466,7 +554,7 @@ mod decl {
|
||||
step: Self::Value,
|
||||
) -> Result<Self::Value, marshal::MarshalError> {
|
||||
use crate::builtins::PySlice;
|
||||
let vm = self.0;
|
||||
let vm = self.vm;
|
||||
Ok(PySlice {
|
||||
start: if vm.is_none(&start) {
|
||||
None
|
||||
@@ -480,7 +568,21 @@ mod decl {
|
||||
.into())
|
||||
}
|
||||
fn constant_bag(self) -> Self::ConstantBag {
|
||||
PyVmBag(self.0)
|
||||
PyVmBag(self.vm)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_value(
|
||||
rdr: &mut impl marshal::Read,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyObjectRef> {
|
||||
let pending_error = RefCell::new(None);
|
||||
match marshal::deserialize_value(rdr, PyMarshalBag::new(vm, &pending_error)) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(error) => Err(pending_error.into_inner().unwrap_or_else(|| match error {
|
||||
marshal::MarshalError::Eof => vm.new_eof_error("marshal data too short"),
|
||||
_ => vm.new_value_error("bad marshal data"),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,11 +604,7 @@ mod decl {
|
||||
vm.new_buffer_error("Buffer provided to marshal.loads() is not contiguous")
|
||||
})?;
|
||||
|
||||
let result =
|
||||
marshal::deserialize_value(&mut &buf[..], PyMarshalBag(vm)).map_err(|e| match e {
|
||||
marshal::MarshalError::Eof => vm.new_eof_error("marshal data too short"),
|
||||
_ => vm.new_value_error("bad marshal data"),
|
||||
})?;
|
||||
let result = deserialize_value(&mut &buf[..], vm)?;
|
||||
if !allow_code {
|
||||
check_no_code(&result, vm)?;
|
||||
}
|
||||
@@ -534,14 +632,7 @@ mod decl {
|
||||
|
||||
let mut rdr: &[u8] = &buf;
|
||||
let len_before = rdr.len();
|
||||
let result =
|
||||
marshal::deserialize_value(&mut rdr, PyMarshalBag(vm)).map_err(|e| match e {
|
||||
marshal::MarshalError::Eof => vm.new_exception_msg(
|
||||
vm.ctx.exceptions.eof_error.to_owned(),
|
||||
"marshal data too short".into(),
|
||||
),
|
||||
_ => vm.new_value_error("bad marshal data"),
|
||||
})?;
|
||||
let result = deserialize_value(&mut rdr, vm)?;
|
||||
let consumed = len_before - rdr.len();
|
||||
|
||||
// Seek file to just after the consumed bytes
|
||||
|
||||
Reference in new issue
Block a user