Merge pull request #3703 from killme2008/feat/issue-3702

PyStr::mul optimization for i=0 or str=''
This commit is contained in:
Jeong YunWon
2022-05-15 16:15:45 +09:00
committed by GitHub
7 changed files with 19 additions and 8 deletions

View File

@@ -1188,6 +1188,8 @@ class MixinStrUnicodeUserStringTest:
slice(start, stop, step))
def test_mul(self):
self.assertTrue("('' * 3) is ''");
self.assertTrue("('a' * 0) is ''");
self.checkequal('', 'abc', '__mul__', -1)
self.checkequal('', 'abc', '__mul__', 0)
self.checkequal('abc', 'abc', '__mul__', 1)

View File

@@ -509,8 +509,13 @@ impl PyStr {
#[pymethod(name = "__rmul__")]
#[pymethod(magic)]
fn mul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
if value == 1 && zelf.class().is(&vm.ctx.types.str_type) {
// Special case: when some `str` is multiplied by `1`,
if value == 0 && zelf.class().is(&vm.ctx.types.str_type) {
// Special case: when some `str` is multiplied by `0`,
// returns the empty `str`.
return Ok(vm.ctx.empty_str.clone());
}
if (value == 1 || zelf.is_empty()) && zelf.class().is(&vm.ctx.types.str_type) {
// Special case: when some `str` is multiplied by `1` or is the empty `str`,
// nothing really happens, we need to return an object itself
// with the same `id()` to be compatible with CPython.
// This only works for `str` itself, not its subclasses.

View File

@@ -507,7 +507,7 @@ impl PyBaseException {
pub(super) fn str(&self, vm: &VirtualMachine) -> PyStrRef {
let str_args = vm.exception_args_as_string(self.args(), true);
match str_args.into_iter().exactly_one() {
Err(i) if i.len() == 0 => PyStr::from("").into_ref(vm),
Err(i) if i.len() == 0 => vm.ctx.empty_str.clone(),
Ok(s) => s,
Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(vm),
}

View File

@@ -1125,7 +1125,7 @@ impl ExecutingFrame<'_> {
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn import(&mut self, vm: &VirtualMachine, module: Option<PyStrRef>) -> FrameResult {
let module = module.unwrap_or_else(|| PyStr::from("").into_ref(vm));
let module = module.unwrap_or_else(|| vm.ctx.empty_str.clone());
let from_list = <Option<PyTupleTyped<PyStrRef>>>::try_from_object(vm, self.pop_value())?;
let level = usize::try_from_object(vm, self.pop_value())?;

View File

@@ -305,7 +305,7 @@ mod builtins {
) -> PyResult<PyStrRef> {
let format_spec = format_spec
.into_option()
.unwrap_or_else(|| PyStr::from("").into_ref(vm));
.unwrap_or_else(|| vm.ctx.empty_str.clone());
call_object_format(vm, value, None, format_spec.as_str())
}

View File

@@ -2588,7 +2588,7 @@ mod _io {
}
}
if chunks.is_empty() {
PyStr::from("").into_ref(vm)
vm.ctx.empty_str.clone()
} else if chunks.len() == 1 {
chunks.pop().unwrap()
} else {
@@ -2850,7 +2850,7 @@ mod _io {
} else if let Some(cur_line) = cur_line {
cur_line.slice_pystr(vm)
} else {
PyStr::from("").into_ref(vm)
vm.ctx.empty_str.clone()
};
Ok(line)
}
@@ -3009,7 +3009,7 @@ mod _io {
append: Option<PyStrRef>,
vm: &VirtualMachine,
) -> PyStrRef {
let empty_str = || PyStr::from("").into_ref(vm);
let empty_str = || vm.ctx.empty_str.clone();
let chars_pos = std::mem::take(&mut self.decoded_chars_used).bytes;
let decoded_chars = match std::mem::take(&mut self.decoded_chars) {
None => return append.unwrap_or_else(empty_str),

View File

@@ -25,6 +25,7 @@ pub struct Context {
pub none: PyRef<PyNone>,
pub empty_tuple: PyTupleRef,
pub empty_frozenset: PyRef<PyFrozenSet>,
pub empty_str: PyRef<PyStr>,
pub ellipsis: PyRef<PyEllipsis>,
pub not_implemented: PyRef<PyNotImplemented>,
@@ -83,6 +84,7 @@ impl Context {
let true_str = unsafe { string_pool.intern("True", types.str_type.clone()) }.into_pyref();
let false_str = unsafe { string_pool.intern("False", types.str_type.clone()) }.into_pyref();
let empty_str = unsafe { string_pool.intern("", types.str_type.clone()) }.into_pyref();
let context = Context {
true_value,
@@ -90,6 +92,8 @@ impl Context {
none,
empty_tuple,
empty_frozenset,
empty_str,
ellipsis,
not_implemented,