From c3484c612c7a94ea272e382fe2f9412deabc6c8f Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Thu, 15 Oct 2020 17:47:32 +0200 Subject: [PATCH 1/3] fix buffer resizable non-thread safe --- vm/src/builtins/bytearray.rs | 70 +++++++++++++----------- vm/src/builtins/bytes.rs | 4 -- vm/src/builtins/memory.rs | 32 +++++------ vm/src/stdlib/array.rs | 102 +++++++++++++++++++---------------- vm/src/stdlib/io.rs | 25 ++++++--- 5 files changed, 127 insertions(+), 106 deletions(-) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 2db530089..7d284349d 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -1,7 +1,7 @@ //! Implementation of the python bytearray object. use super::bytes::{PyBytes, PyBytesRef}; use super::int::PyIntRef; -use super::memory::{Buffer, BufferOptions}; +use super::memory::{Buffer, BufferOptions, ResizeGuard}; use super::pystr::PyStrRef; use super::pytype::PyTypeRef; use super::tuple::PyTupleRef; @@ -141,8 +141,7 @@ impl PyByteArray { #[pymethod(name = "__iadd__")] fn iadd(zelf: PyRef, other: PyBytesLike, vm: &VirtualMachine) -> PyResult> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().iadd(&*other.borrow_value()); + zelf.try_resizable(vm)?.iadd(&*other.borrow_value()); Ok(zelf) } @@ -169,21 +168,26 @@ impl PyByteArray { ) -> PyResult<()> { match needle { SequenceIndex::Int(int) => zelf.borrow_value_mut().setindex(int, value, vm), - SequenceIndex::Slice(slice) => match (zelf.is(&value), zelf.is_resizable()) { - (true, true) => zelf.borrow_value_mut().setslice_from_self(slice, vm), - (true, false) => zelf - .borrow_value_mut() - .setslice_from_self_no_resize(slice, vm), - (false, true) => zelf.borrow_value_mut().setslice(slice, value, vm), - (false, false) => zelf.borrow_value_mut().setslice_no_resize(slice, value, vm), - }, + SequenceIndex::Slice(slice) => { + if let Ok(mut w) = zelf.try_resizable(vm) { + if zelf.is(&value) { + w.setslice_from_self(slice, vm) + } else { + w.setslice(slice, value, vm) + } + } else if zelf.is(&value) { + zelf.borrow_value_mut() + .setslice_from_self_no_resize(slice, vm) + } else { + zelf.borrow_value_mut().setslice_no_resize(slice, value, vm) + } + } } } #[pymethod(name = "__delitem__")] fn delitem(zelf: PyRef, needle: SequenceIndex, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().delitem(needle, vm) + zelf.try_resizable(vm)?.delitem(needle, vm) } #[pymethod(name = "isalnum")] @@ -350,7 +354,7 @@ impl PyByteArray { #[pymethod(name = "remove")] fn remove(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.borrow_value_mut().remove(value, vm) + self.try_resizable(vm)?.remove(value, vm) } #[pymethod(name = "translate")] @@ -470,8 +474,7 @@ impl PyByteArray { #[pymethod(name = "clear")] fn clear(zelf: PyRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().elements.clear(); + zelf.try_resizable(vm)?.elements.clear(); Ok(()) } @@ -482,18 +485,16 @@ impl PyByteArray { #[pymethod(name = "append")] fn append(zelf: PyRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().append(value, vm) + zelf.try_resizable(vm)?.append(value, vm) } #[pymethod(name = "extend")] fn extend(zelf: PyRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; if zelf.is(&value) { - zelf.borrow_value_mut().irepeat(2); + zelf.try_resizable(vm)?.irepeat(2); Ok(()) } else { - zelf.borrow_value_mut().extend(value, vm) + zelf.try_resizable(vm)?.extend(value, vm) } } @@ -504,15 +505,13 @@ impl PyByteArray { value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().insert(index, value, vm) + zelf.try_resizable(vm)?.insert(index, value, vm) } #[pymethod(name = "pop")] fn pop(zelf: PyRef, index: OptionalArg, vm: &VirtualMachine) -> PyResult { - zelf.try_resizable(vm)?; let index = index.unwrap_or(-1); - zelf.borrow_value_mut().pop(index, vm) + zelf.try_resizable(vm)?.pop(index, vm) } #[pymethod(name = "title")] @@ -528,8 +527,7 @@ impl PyByteArray { #[pymethod(name = "__imul__")] fn imul(zelf: PyRef, n: isize, vm: &VirtualMachine) -> PyResult> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().irepeat(n); + zelf.try_resizable(vm)?.irepeat(n); Ok(zelf) } @@ -607,10 +605,6 @@ impl Buffer for PyByteArrayRef { } } - fn is_resizable(&self) -> bool { - self.exports.load() == 0 - } - fn get_options(&self) -> BorrowedValue { let guard = self.buffer_options.upgradable_read(); let guard = if guard.is_none() { @@ -628,6 +622,20 @@ impl Buffer for PyByteArrayRef { } } +impl<'a> ResizeGuard<'a> for PyByteArray { + type Resizable = PyRwLockWriteGuard<'a, PyBytesInner>; + + fn try_resizable(&'a self, vm: &VirtualMachine) -> PyResult { + let w = self.borrow_value_mut(); + if self.exports.load() == 0 { + Ok(w) + } else { + Err(vm + .new_buffer_error("Existing exports of data: object cannot be re-sized".to_owned())) + } + } +} + impl Unhashable for PyByteArray {} // fn set_value(obj: &PyObjectRef, value: Vec) { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 91af6e525..96b73440f 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -493,10 +493,6 @@ impl Buffer for PyBytesRef { unreachable!("bytes is not mutable") } - fn is_resizable(&self) -> bool { - false - } - fn release(&self) {} fn get_options(&self) -> BorrowedValue { diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index e2c64a392..b35561517 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -47,7 +47,7 @@ pub trait Buffer: Debug + PyThreadingConstraint { fn obj_bytes(&self) -> BorrowedValue<[u8]>; fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]>; fn release(&self); - fn is_resizable(&self) -> bool; + // fn is_resizable(&self) -> bool; fn as_contiguous(&self) -> Option> { let options = self.get_options(); @@ -69,16 +69,16 @@ pub trait Buffer: Debug + PyThreadingConstraint { self.obj_bytes().to_vec() } - fn try_resizable(&self, vm: &VirtualMachine) -> PyResult<()> { - if self.is_resizable() { - Ok(()) - } else { - Err(vm.new_exception_msg( - vm.ctx.exceptions.buffer_error.clone(), - "Existing exports of data: object cannot be re-sized".to_owned(), - )) - } - } + // fn try_resizable(&self, vm: &VirtualMachine) -> PyResult<()> { + // if self.is_resizable() { + // Ok(()) + // } else { + // Err(vm.new_exception_msg( + // vm.ctx.exceptions.buffer_error.clone(), + // "Existing exports of data: object cannot be re-sized".to_owned(), + // )) + // } + // } } #[derive(Debug, Clone)] @@ -94,6 +94,11 @@ pub struct BufferOptions { pub strides: Vec, } +pub(crate) trait ResizeGuard<'a> { + type Resizable: 'a; + fn try_resizable(&'a self, vm: &VirtualMachine) -> PyResult; +} + impl Default for BufferOptions { fn default() -> Self { BufferOptions { @@ -709,11 +714,6 @@ impl Buffer for PyMemoryViewRef { } } - fn is_resizable(&self) -> bool { - // memoryview cannot resize - false - } - fn as_contiguous(&self) -> Option> { let options = self.get_options(); if !options.contiguous { diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index 35c550ada..625148671 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -1,7 +1,7 @@ use crate::builtins::bytes::PyBytesRef; use crate::builtins::float::try_float; use crate::builtins::list::PyList; -use crate::builtins::memory::{Buffer, BufferOptions}; +use crate::builtins::memory::{Buffer, BufferOptions, ResizeGuard}; use crate::builtins::pystr::PyStrRef; use crate::builtins::pytype::PyTypeRef; use crate::builtins::slice::PySliceRef; @@ -39,7 +39,7 @@ impl fmt::Display for ArrayTypeSpecifierError { macro_rules! def_array_enum { ($(($n:ident, $t:ident, $c:literal)),*$(,)?) => { #[derive(Debug, Clone)] - enum ArrayContentType { + pub(crate) enum ArrayContentType { $($n(Vec<$t>),)* } @@ -508,13 +508,15 @@ impl PyArray { let spec = spec.borrow_value().chars().exactly_one().map_err(|_| { vm.new_type_error("array() argument 1 must be a unicode character, not str".to_owned()) })?; - let array = + let mut array = ArrayContentType::from_char(spec).map_err(|err| vm.new_value_error(err.to_string()))?; - let zelf: PyArray = array.into(); + // TODO: support buffer protocol if let OptionalArg::Present(init) = init { - zelf.extend_from_iterable(init, vm)?; + for obj in init.iter(vm)? { + array.push(obj?, vm)?; + } } - zelf.into_ref_with_type(vm, cls) + Self::from(array).into_ref_with_type(vm, cls) } #[pyproperty] @@ -529,8 +531,7 @@ impl PyArray { #[pymethod] fn append(zelf: PyRef, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().push(x, vm) + zelf.try_resizable(vm)?.push(x, vm) } #[pymethod] @@ -546,42 +547,44 @@ impl PyArray { #[pymethod] fn remove(zelf: PyRef, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().remove(x, vm) + zelf.try_resizable(vm)?.remove(x, vm) } - fn extend_from_iterable(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> { - let mut array = self.borrow_value_mut(); - for obj in iter.iter(vm)? { - array.push(obj?, vm)?; - } - Ok(()) - } + // fn extend_from_iterable(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> { + // let mut array = self.borrow_value_mut(); + // for obj in iter.iter(vm)? { + // array.push(obj?, vm)?; + // } + // Ok(()) + // } #[pymethod] fn extend(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; + let mut w = zelf.try_resizable(vm)?; if zelf.is(&obj) { - zelf.borrow_value_mut().imul(2); + w.imul(2); Ok(()) } else if let Some(array) = obj.payload::() { - zelf.borrow_value_mut().iadd(&*array.borrow_value(), vm) + w.iadd(&*array.borrow_value(), vm) } else { let iter = PyIterable::try_from_object(vm, obj)?; - zelf.extend_from_iterable(iter, vm) + // zelf.extend_from_iterable(iter, vm) + for obj in iter.iter(vm)? { + w.push(obj?, vm)?; + } + Ok(()) } } #[pymethod] fn frombytes(zelf: PyRef, b: PyBytesRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; let b = b.borrow_value(); let itemsize = zelf.borrow_value().itemsize(); if b.len() % itemsize != 0 { return Err(vm.new_value_error("bytes length not a multiple of item size".to_owned())); } if b.len() / itemsize > 0 { - zelf.borrow_value_mut().frombytes(&b); + zelf.try_resizable(vm)?.frombytes(&b); } Ok(()) } @@ -598,19 +601,19 @@ impl PyArray { #[pymethod] fn insert(zelf: PyRef, i: isize, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; - let i = saturate_index(i, zelf.len()); - zelf.borrow_value_mut().insert(i, x, vm) + let mut w = zelf.try_resizable(vm)?; + let i = saturate_index(i, w.len()); + w.insert(i, x, vm) } #[pymethod] fn pop(zelf: PyRef, i: OptionalArg, vm: &VirtualMachine) -> PyResult { - zelf.try_resizable(vm)?; - if zelf.len() == 0 { + let mut w = zelf.try_resizable(vm)?; + if w.len() == 0 { Err(vm.new_index_error("pop from empty array".to_owned())) } else { - let i = zelf.borrow_value().idx(i.unwrap_or(-1), "pop", vm)?; - Ok(zelf.borrow_value_mut().pop(i, vm)) + let i = w.idx(i.unwrap_or(-1), "pop", vm)?; + Ok(w.pop(i, vm)) } } @@ -639,9 +642,8 @@ impl PyArray { #[pymethod] fn fromlist(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - zelf.try_resizable(vm)?; if let Some(list) = obj.payload::() { - zelf.borrow_value_mut().fromlist(list, vm) + zelf.try_resizable(vm)?.fromlist(list, vm) } else { Err(vm.new_type_error("arg must be list".to_owned())) } @@ -674,7 +676,6 @@ impl PyArray { obj: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - // FIXME check resizable match needle { Either::A(i) => zelf.borrow_value_mut().setitem_by_idx(i, obj, vm), Either::B(slice) => { @@ -697,8 +698,8 @@ impl PyArray { } } }; - if zelf.is_resizable() { - zelf.borrow_value_mut().setitem_by_slice(slice, items, vm) + if let Ok(mut w) = zelf.try_resizable(vm) { + w.setitem_by_slice(slice, items, vm) } else { zelf.borrow_value_mut() .setitem_by_slice_no_resize(slice, items, vm) @@ -713,10 +714,9 @@ impl PyArray { needle: Either, vm: &VirtualMachine, ) -> PyResult<()> { - zelf.try_resizable(vm)?; match needle { - Either::A(i) => zelf.borrow_value_mut().delitem_by_idx(i, vm), - Either::B(slice) => zelf.borrow_value_mut().delitem_by_slice(slice, vm), + Either::A(i) => zelf.try_resizable(vm)?.delitem_by_idx(i, vm), + Either::B(slice) => zelf.try_resizable(vm)?.delitem_by_slice(slice, vm), } } @@ -736,12 +736,11 @@ impl PyArray { #[pymethod(name = "__iadd__")] fn iadd(zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - zelf.try_resizable(vm)?; if zelf.is(&other) { - zelf.borrow_value_mut().imul(2); + zelf.try_resizable(vm)?.imul(2); Ok(zelf) } else if let Some(other) = other.payload::() { - let result = zelf.borrow_value_mut().iadd(&*other.borrow_value(), vm); + let result = zelf.try_resizable(vm)?.iadd(&*other.borrow_value(), vm); result.map(|_| zelf) } else { Err(vm.new_type_error(format!( @@ -763,8 +762,7 @@ impl PyArray { #[pymethod(name = "__imul__")] fn imul(zelf: PyRef, counter: isize, vm: &VirtualMachine) -> PyResult> { - zelf.try_resizable(vm)?; - zelf.borrow_value_mut().imul(counter); + zelf.try_resizable(vm)?.imul(counter); Ok(zelf) } @@ -882,10 +880,6 @@ impl Buffer for PyArrayRef { } } - fn is_resizable(&self) -> bool { - self.exports.load() == 0 - } - fn get_options(&self) -> BorrowedValue { let guard = self.buffer_options.upgradable_read(); let guard = if guard.is_none() { @@ -906,6 +900,20 @@ impl Buffer for PyArrayRef { } } +impl<'a> ResizeGuard<'a> for PyArray { + type Resizable = PyRwLockWriteGuard<'a, ArrayContentType>; + + fn try_resizable(&'a self, vm: &VirtualMachine) -> PyResult { + let w = self.borrow_value_mut(); + if self.exports.load() == 0 { + Ok(w) + } else { + Err(vm + .new_buffer_error("Existing exports of data: object cannot be re-sized".to_owned())) + } + } +} + #[pyclass(module = "array", name = "array_iterator")] #[derive(Debug)] pub struct PyArrayIter { diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 3d4b46d56..caad996de 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -20,7 +20,7 @@ mod _io { use crate::builtins::bytearray::PyByteArray; use crate::builtins::bytes::PyBytesRef; use crate::builtins::int; - use crate::builtins::memory::{Buffer, BufferOptions, BufferRef, PyMemoryView}; + use crate::builtins::memory::{Buffer, BufferOptions, BufferRef, PyMemoryView, ResizeGuard}; use crate::builtins::pybool; use crate::builtins::pystr::{self, PyStr, PyStrRef}; use crate::builtins::pytype::PyTypeRef; @@ -986,8 +986,7 @@ mod _io { impl BytesIORef { #[pymethod] fn write(self, data: PyBytesLike, vm: &VirtualMachine) -> PyResult { - self.try_resizable(vm)?; - let mut buffer = self.buffer(vm)?; + let mut buffer = self.try_resizable(vm)?; match data.with_ref(|b| buffer.write(b)) { Some(value) => Ok(value), None => Err(vm.new_type_error("Error Writing Bytes".to_owned())), @@ -1054,8 +1053,7 @@ mod _io { #[pymethod] fn truncate(self, pos: OptionalSize, vm: &VirtualMachine) -> PyResult<()> { - self.try_resizable(vm)?; - let mut buffer = self.buffer(vm)?; + let mut buffer = self.try_resizable(vm)?; buffer.truncate(pos.try_usize(vm)?)?; Ok(()) } @@ -1067,7 +1065,7 @@ mod _io { #[pymethod] fn close(self, vm: &VirtualMachine) -> PyResult<()> { - self.try_resizable(vm)?; + let _ = self.try_resizable(vm)?; self.closed.store(true); Ok(()) } @@ -1114,9 +1112,20 @@ mod _io { *w = None; } } + } - fn is_resizable(&self) -> bool { - self.exports.load() == 0 + impl<'a> ResizeGuard<'a> for BytesIO { + type Resizable = PyRwLockWriteGuard<'a, BufferedIO>; + + fn try_resizable(&'a self, vm: &VirtualMachine) -> PyResult { + let buffer = self.buffer(vm)?; + if self.exports.load() == 0 { + Ok(buffer) + } else { + Err(vm.new_buffer_error( + "Existing exports of data: object cannot be re-sized".to_owned(), + )) + } } } From c2d46b9df31c979abf0e32afee0f464abcf12dee Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Fri, 16 Oct 2020 07:30:59 +0200 Subject: [PATCH 2/3] remove comment --- vm/src/builtins/memory.rs | 12 ------------ vm/src/stdlib/array.rs | 8 -------- 2 files changed, 20 deletions(-) diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index b35561517..3f13d043f 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -47,7 +47,6 @@ pub trait Buffer: Debug + PyThreadingConstraint { fn obj_bytes(&self) -> BorrowedValue<[u8]>; fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]>; fn release(&self); - // fn is_resizable(&self) -> bool; fn as_contiguous(&self) -> Option> { let options = self.get_options(); @@ -68,17 +67,6 @@ pub trait Buffer: Debug + PyThreadingConstraint { fn to_contiguous(&self) -> Vec { self.obj_bytes().to_vec() } - - // fn try_resizable(&self, vm: &VirtualMachine) -> PyResult<()> { - // if self.is_resizable() { - // Ok(()) - // } else { - // Err(vm.new_exception_msg( - // vm.ctx.exceptions.buffer_error.clone(), - // "Existing exports of data: object cannot be re-sized".to_owned(), - // )) - // } - // } } #[derive(Debug, Clone)] diff --git a/vm/src/stdlib/array.rs b/vm/src/stdlib/array.rs index 625148671..5a5b2956e 100644 --- a/vm/src/stdlib/array.rs +++ b/vm/src/stdlib/array.rs @@ -550,14 +550,6 @@ impl PyArray { zelf.try_resizable(vm)?.remove(x, vm) } - // fn extend_from_iterable(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> { - // let mut array = self.borrow_value_mut(); - // for obj in iter.iter(vm)? { - // array.push(obj?, vm)?; - // } - // Ok(()) - // } - #[pymethod] fn extend(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { let mut w = zelf.try_resizable(vm)?; From 64f4282edeb251b503f0f22bb3c4ead26220f205 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Fri, 16 Oct 2020 09:34:35 +0200 Subject: [PATCH 3/3] fix error type --- Lib/test/test_bytes.py | 2 -- vm/src/sliceable.rs | 19 +++++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 0affea87c..b28c2d5c7 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1541,8 +1541,6 @@ class ByteArrayTest(BaseBytesTest, unittest.TestCase): self.assertEqual(b, b"") self.assertEqual(c, b"") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_resize_forbidden(self): # #4509: can't resize a bytearray when there are buffer exports, even # if it wouldn't reallocate the underlying buffer. diff --git a/vm/src/sliceable.rs b/vm/src/sliceable.rs index f53917962..1af55078b 100644 --- a/vm/src/sliceable.rs +++ b/vm/src/sliceable.rs @@ -32,8 +32,7 @@ pub trait PySliceableSequenceMut { self.do_set_range(range, items); Ok(()) } else { - Err(vm.new_exception_msg( - vm.ctx.exceptions.buffer_error.clone(), + Err(vm.new_buffer_error( "Existing exports of data: object cannot be re-sized".to_owned(), )) }; @@ -54,11 +53,9 @@ pub trait PySliceableSequenceMut { self.do_replace_indexes(indexes, items); Ok(()) } else { - Err(vm.new_value_error(format!( - "attempt to assign sequence of size {} to extended slice of size {}", - items.len(), - slicelen - ))) + Err(vm.new_buffer_error( + "Existing exports of data: object cannot be re-sized".to_owned(), + )) } } else { // edge case, step is too big for usize @@ -74,11 +71,9 @@ pub trait PySliceableSequenceMut { } { Ok(()) } else { - Err(vm.new_value_error(format!( - "attempt to assign sequence of size {} to extended slice of size {}", - items.len(), - slicelen - ))) + Err(vm.new_buffer_error( + "Existing exports of data: object cannot be re-sized".to_owned(), + )) } } }