Skip to content

Commit 75e8f81

Browse files
Merge pull request RustPython#767 from skinny121/slice_new
Refactor slice.__new__ to new style function
2 parents 780ee8a + 9b71424 commit 75e8f81

File tree

7 files changed

+132
-88
lines changed

7 files changed

+132
-88
lines changed

tests/snippets/builtin_slice.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
b = [1, 2]
1212

1313
assert b[:] == [1, 2]
14+
assert b[slice(None)] == [1, 2]
1415
assert b[: 2 ** 100] == [1, 2]
1516
assert b[-2 ** 100 :] == [1, 2]
1617
assert b[2 ** 100 :] == []
@@ -60,6 +61,12 @@
6061
assert slice_c.stop == 5
6162
assert slice_c.step == 2
6263

64+
a = object()
65+
slice_d = slice(a, "v", 1.0)
66+
assert slice_d.start is a
67+
assert slice_d.stop == "v"
68+
assert slice_d.step == 1.0
69+
6370

6471
class SubScript(object):
6572
def __getitem__(self, item):
@@ -74,6 +81,18 @@ def __setitem__(self, key, value):
7481
ss[:1] = 1
7582

7683

84+
class CustomIndex:
85+
def __init__(self, x):
86+
self.x = x
87+
88+
def __index__(self):
89+
return self.x
90+
91+
92+
assert c[CustomIndex(1):CustomIndex(3)] == [1, 2]
93+
assert d[CustomIndex(1):CustomIndex(3)] == "23"
94+
95+
7796
def test_all_slices():
7897
"""
7998
test all possible slices except big number

vm/src/frame.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use std::cell::RefCell;
22
use std::fmt;
33
use std::rc::Rc;
44

5-
use num_bigint::BigInt;
6-
75
use rustpython_parser::ast;
86

97
use crate::builtins;
@@ -12,7 +10,6 @@ use crate::function::PyFuncArgs;
1210
use crate::obj::objbool;
1311
use crate::obj::objcode::PyCodeRef;
1412
use crate::obj::objdict::{PyDict, PyDictRef};
15-
use crate::obj::objint::PyInt;
1613
use crate::obj::objiter;
1714
use crate::obj::objlist;
1815
use crate::obj::objslice::PySlice;
@@ -435,26 +432,21 @@ impl Frame {
435432
}
436433
bytecode::Instruction::BuildSlice { size } => {
437434
assert!(*size == 2 || *size == 3);
438-
let elements = self.pop_multiple(*size);
439-
440-
let mut out: Vec<Option<BigInt>> = elements
441-
.into_iter()
442-
.map(|x| {
443-
if x.is(&vm.ctx.none()) {
444-
None
445-
} else if let Some(i) = x.payload::<PyInt>() {
446-
Some(i.as_bigint().clone())
447-
} else {
448-
panic!("Expect Int or None as BUILD_SLICE arguments")
449-
}
450-
})
451-
.collect();
452435

453-
let start = out[0].take();
454-
let stop = out[1].take();
455-
let step = if out.len() == 3 { out[2].take() } else { None };
436+
let step = if *size == 3 {
437+
Some(self.pop_value())
438+
} else {
439+
None
440+
};
441+
let stop = self.pop_value();
442+
let start = self.pop_value();
456443

457-
let obj = PySlice { start, stop, step }.into_ref(vm);
444+
let obj = PySlice {
445+
start: Some(start),
446+
stop,
447+
step,
448+
}
449+
.into_ref(vm);
458450
self.push_value(obj.into_object());
459451
Ok(None)
460452
}

vm/src/function.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::HashMap;
2+
use std::mem;
23
use std::ops::RangeInclusive;
34

45
use crate::obj::objtype::{isinstance, PyClassRef};
@@ -48,6 +49,12 @@ impl From<(&Args, &KwArgs)> for PyFuncArgs {
4849
}
4950
}
5051

52+
impl FromArgs for PyFuncArgs {
53+
fn from_args(_vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result<Self, ArgumentError> {
54+
Ok(mem::replace(args, Default::default()))
55+
}
56+
}
57+
5158
impl PyFuncArgs {
5259
pub fn new(mut args: Vec<PyObjectRef>, kwarg_names: Vec<String>) -> PyFuncArgs {
5360
let mut kwargs = vec![];

vm/src/obj/objlist.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,12 @@ impl PyListRef {
210210
}
211211

212212
fn setslice(self, slice: PySliceRef, sec: PyIterable, vm: &VirtualMachine) -> PyResult {
213-
let step = slice.step.clone().unwrap_or_else(BigInt::one);
213+
let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one);
214214

215215
if step.is_zero() {
216216
Err(vm.new_value_error("slice step cannot be zero".to_string()))
217217
} else if step.is_positive() {
218-
let range = self.get_slice_range(&slice.start, &slice.stop);
218+
let range = self.get_slice_range(&slice.start_index(vm)?, &slice.stop_index(vm)?);
219219
if range.start < range.end {
220220
match step.to_i32() {
221221
Some(1) => self._set_slice(range, sec, vm),
@@ -237,14 +237,14 @@ impl PyListRef {
237237
} else {
238238
// calculate the range for the reverse slice, first the bounds needs to be made
239239
// exclusive around stop, the lower number
240-
let start = &slice.start.as_ref().map(|x| {
240+
let start = &slice.start_index(vm)?.as_ref().map(|x| {
241241
if *x == (-1).to_bigint().unwrap() {
242242
self.get_len() + BigInt::one() //.to_bigint().unwrap()
243243
} else {
244244
x + 1
245245
}
246246
});
247-
let stop = &slice.stop.as_ref().map(|x| {
247+
let stop = &slice.stop_index(vm)?.as_ref().map(|x| {
248248
if *x == (-1).to_bigint().unwrap() {
249249
self.get_len().to_bigint().unwrap()
250250
} else {
@@ -552,9 +552,9 @@ impl PyListRef {
552552
}
553553

554554
fn delslice(self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult {
555-
let start = &slice.start;
556-
let stop = &slice.stop;
557-
let step = slice.step.clone().unwrap_or_else(BigInt::one);
555+
let start = slice.start_index(vm)?;
556+
let stop = slice.stop_index(vm)?;
557+
let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one);
558558

559559
if step.is_zero() {
560560
Err(vm.new_value_error("slice step cannot be zero".to_string()))

vm/src/obj/objrange.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ impl PyRange {
273273
}
274274
}
275275
RangeIndex::Slice(slice) => {
276-
let new_start = if let Some(int) = slice.start.as_ref() {
276+
let new_start = if let Some(int) = slice.start_index(vm)? {
277277
if let Some(i) = self.get(int) {
278278
PyInt::new(i).into_ref(vm)
279279
} else {
@@ -283,7 +283,7 @@ impl PyRange {
283283
self.start.clone()
284284
};
285285

286-
let new_end = if let Some(int) = slice.stop.as_ref() {
286+
let new_end = if let Some(int) = slice.stop_index(vm)? {
287287
if let Some(i) = self.get(int) {
288288
PyInt::new(i).into_ref(vm)
289289
} else {
@@ -293,7 +293,7 @@ impl PyRange {
293293
self.stop.clone()
294294
};
295295

296-
let new_step = if let Some(int) = slice.step.as_ref() {
296+
let new_step = if let Some(int) = slice.step_index(vm)? {
297297
PyInt::new(int * self.step.as_bigint()).into_ref(vm)
298298
} else {
299299
self.step.clone()

vm/src/obj/objsequence.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,15 @@ pub trait PySliceableSequence {
6565
where
6666
Self: Sized,
6767
{
68-
// TODO: we could potentially avoid this copy and use slice
69-
match slice.payload() {
70-
Some(PySlice { start, stop, step }) => {
71-
let step = step.clone().unwrap_or_else(BigInt::one);
68+
match slice.clone().downcast::<PySlice>() {
69+
Ok(slice) => {
70+
let start = slice.start_index(vm)?;
71+
let stop = slice.stop_index(vm)?;
72+
let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one);
7273
if step.is_zero() {
7374
Err(vm.new_value_error("slice step cannot be zero".to_string()))
7475
} else if step.is_positive() {
75-
let range = self.get_slice_range(start, stop);
76+
let range = self.get_slice_range(&start, &stop);
7677
if range.start < range.end {
7778
#[allow(clippy::range_plus_one)]
7879
match step.to_i32() {

vm/src/obj/objslice.rs

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
use num_bigint::BigInt;
2-
3-
use crate::function::PyFuncArgs;
4-
use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue};
1+
use crate::function::{OptionalArg, PyFuncArgs};
2+
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
53
use crate::vm::VirtualMachine;
64

7-
use super::objint;
8-
use crate::obj::objtype::PyClassRef;
5+
use crate::obj::objint::PyInt;
6+
use crate::obj::objtype::{class_has_attr, PyClassRef};
7+
use num_bigint::BigInt;
98

109
#[derive(Debug)]
1110
pub struct PySlice {
12-
// TODO: should be private
13-
pub start: Option<BigInt>,
14-
pub stop: Option<BigInt>,
15-
pub step: Option<BigInt>,
11+
pub start: Option<PyObjectRef>,
12+
pub stop: PyObjectRef,
13+
pub step: Option<PyObjectRef>,
1614
}
1715

1816
impl PyValue for PySlice {
@@ -23,52 +21,35 @@ impl PyValue for PySlice {
2321

2422
pub type PySliceRef = PyRef<PySlice>;
2523

26-
fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
27-
no_kwargs!(vm, args);
28-
let (cls, start, stop, step): (
29-
&PyObjectRef,
30-
Option<&PyObjectRef>,
31-
Option<&PyObjectRef>,
32-
Option<&PyObjectRef>,
33-
) = match args.args.len() {
34-
0 | 1 => Err(vm.new_type_error("slice() must have at least one arguments.".to_owned())),
35-
2 => {
36-
arg_check!(
37-
vm,
38-
args,
39-
required = [
40-
(cls, Some(vm.ctx.type_type())),
41-
(stop, Some(vm.ctx.int_type()))
42-
]
43-
);
44-
Ok((cls, None, Some(stop), None))
24+
fn slice_new(cls: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<PySliceRef> {
25+
let slice: PySlice = match args.args.len() {
26+
0 => {
27+
return Err(vm.new_type_error("slice() must have at least one arguments.".to_owned()));
28+
}
29+
1 => {
30+
let stop = args.bind(vm)?;
31+
PySlice {
32+
start: None,
33+
stop,
34+
step: None,
35+
}
4536
}
4637
_ => {
47-
arg_check!(
48-
vm,
49-
args,
50-
required = [
51-
(cls, Some(vm.ctx.type_type())),
52-
(start, Some(vm.ctx.int_type())),
53-
(stop, Some(vm.ctx.int_type()))
54-
],
55-
optional = [(step, Some(vm.ctx.int_type()))]
56-
);
57-
Ok((cls, Some(start), Some(stop), step))
38+
let (start, stop, step): (PyObjectRef, PyObjectRef, OptionalArg<PyObjectRef>) =
39+
args.bind(vm)?;
40+
PySlice {
41+
start: Some(start),
42+
stop,
43+
step: step.into_option(),
44+
}
5845
}
59-
}?;
60-
PySlice {
61-
start: start.map(|x| objint::get_value(x).clone()),
62-
stop: stop.map(|x| objint::get_value(x).clone()),
63-
step: step.map(|x| objint::get_value(x).clone()),
64-
}
65-
.into_ref_with_type(vm, cls.clone().downcast().unwrap())
66-
.map(PyRef::into_object)
46+
};
47+
slice.into_ref_with_type(vm, cls)
6748
}
6849

69-
fn get_property_value(vm: &VirtualMachine, value: &Option<BigInt>) -> PyObjectRef {
50+
fn get_property_value(vm: &VirtualMachine, value: &Option<PyObjectRef>) -> PyObjectRef {
7051
if let Some(value) = value {
71-
vm.ctx.new_int(value.clone())
52+
value.clone()
7253
} else {
7354
vm.get_none()
7455
}
@@ -79,13 +60,57 @@ impl PySliceRef {
7960
get_property_value(vm, &self.start)
8061
}
8162

82-
fn stop(self, vm: &VirtualMachine) -> PyObjectRef {
83-
get_property_value(vm, &self.stop)
63+
fn stop(self, _vm: &VirtualMachine) -> PyObjectRef {
64+
self.stop.clone()
8465
}
8566

8667
fn step(self, vm: &VirtualMachine) -> PyObjectRef {
8768
get_property_value(vm, &self.step)
8869
}
70+
71+
pub fn start_index(&self, vm: &VirtualMachine) -> PyResult<Option<BigInt>> {
72+
if let Some(obj) = &self.start {
73+
to_index_value(vm, obj)
74+
} else {
75+
Ok(None)
76+
}
77+
}
78+
79+
pub fn stop_index(&self, vm: &VirtualMachine) -> PyResult<Option<BigInt>> {
80+
to_index_value(vm, &self.stop)
81+
}
82+
83+
pub fn step_index(&self, vm: &VirtualMachine) -> PyResult<Option<BigInt>> {
84+
if let Some(obj) = &self.step {
85+
to_index_value(vm, obj)
86+
} else {
87+
Ok(None)
88+
}
89+
}
90+
}
91+
92+
fn to_index_value(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Option<BigInt>> {
93+
if obj.is(&vm.ctx.none) {
94+
return Ok(None);
95+
}
96+
97+
if let Some(val) = obj.payload::<PyInt>() {
98+
Ok(Some(val.as_bigint().clone()))
99+
} else {
100+
let cls = obj.class();
101+
if class_has_attr(&cls, "__index__") {
102+
let index_result = vm.call_method(obj, "__index__", vec![])?;
103+
if let Some(val) = index_result.payload::<PyInt>() {
104+
Ok(Some(val.as_bigint().clone()))
105+
} else {
106+
Err(vm.new_type_error("__index__ method returned non integer".to_string()))
107+
}
108+
} else {
109+
Err(vm.new_type_error(
110+
"slice indices must be integers or None or have an __index__ method".to_string(),
111+
))
112+
}
113+
}
89114
}
90115

91116
pub fn init(context: &PyContext) {

0 commit comments

Comments
 (0)