rustpython_vm/vm/
vm_ops.rs

1use super::VirtualMachine;
2use crate::{
3    builtins::{PyInt, PyIntRef, PyStr, PyStrRef},
4    object::{AsObject, PyObject, PyObjectRef, PyResult},
5    protocol::{PyIterReturn, PyNumberBinaryOp, PyNumberTernaryOp, PySequence},
6    types::PyComparisonOp,
7};
8use num_traits::ToPrimitive;
9
10macro_rules! binary_func {
11    ($fn:ident, $op_slot:ident, $op:expr) => {
12        pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult {
13            self.binary_op(a, b, PyNumberBinaryOp::$op_slot, $op)
14        }
15    };
16}
17
18macro_rules! ternary_func {
19    ($fn:ident, $op_slot:ident, $op:expr) => {
20        pub fn $fn(&self, a: &PyObject, b: &PyObject, c: &PyObject) -> PyResult {
21            self.ternary_op(a, b, c, PyNumberTernaryOp::$op_slot, $op)
22        }
23    };
24}
25
26macro_rules! inplace_binary_func {
27    ($fn:ident, $iop_slot:ident, $op_slot:ident, $op:expr) => {
28        pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult {
29            self.binary_iop(
30                a,
31                b,
32                PyNumberBinaryOp::$iop_slot,
33                PyNumberBinaryOp::$op_slot,
34                $op,
35            )
36        }
37    };
38}
39
40macro_rules! inplace_ternary_func {
41    ($fn:ident, $iop_slot:ident, $op_slot:ident, $op:expr) => {
42        pub fn $fn(&self, a: &PyObject, b: &PyObject, c: &PyObject) -> PyResult {
43            self.ternary_iop(
44                a,
45                b,
46                c,
47                PyNumberTernaryOp::$iop_slot,
48                PyNumberTernaryOp::$op_slot,
49                $op,
50            )
51        }
52    };
53}
54
55/// Collection of operators
56impl VirtualMachine {
57    #[inline]
58    pub fn bool_eq(&self, a: &PyObject, b: &PyObject) -> PyResult<bool> {
59        a.rich_compare_bool(b, PyComparisonOp::Eq, self)
60    }
61
62    pub fn identical_or_equal(&self, a: &PyObject, b: &PyObject) -> PyResult<bool> {
63        if a.is(b) {
64            Ok(true)
65        } else {
66            self.bool_eq(a, b)
67        }
68    }
69
70    pub fn bool_seq_lt(&self, a: &PyObject, b: &PyObject) -> PyResult<Option<bool>> {
71        let value = if a.rich_compare_bool(b, PyComparisonOp::Lt, self)? {
72            Some(true)
73        } else if !self.bool_eq(a, b)? {
74            Some(false)
75        } else {
76            None
77        };
78        Ok(value)
79    }
80
81    pub fn bool_seq_gt(&self, a: &PyObject, b: &PyObject) -> PyResult<Option<bool>> {
82        let value = if a.rich_compare_bool(b, PyComparisonOp::Gt, self)? {
83            Some(true)
84        } else if !self.bool_eq(a, b)? {
85            Some(false)
86        } else {
87            None
88        };
89        Ok(value)
90    }
91
92    pub fn length_hint_opt(&self, iter: PyObjectRef) -> PyResult<Option<usize>> {
93        match iter.length(self) {
94            Ok(len) => return Ok(Some(len)),
95            Err(e) => {
96                if !e.fast_isinstance(self.ctx.exceptions.type_error) {
97                    return Err(e);
98                }
99            }
100        }
101        let hint = match self.get_method(iter, identifier!(self, __length_hint__)) {
102            Some(hint) => hint?,
103            None => return Ok(None),
104        };
105        let result = match hint.call((), self) {
106            Ok(res) => {
107                if res.is(&self.ctx.not_implemented) {
108                    return Ok(None);
109                }
110                res
111            }
112            Err(e) => {
113                return if e.fast_isinstance(self.ctx.exceptions.type_error) {
114                    Ok(None)
115                } else {
116                    Err(e)
117                }
118            }
119        };
120        let hint = result
121            .payload_if_subclass::<PyInt>(self)
122            .ok_or_else(|| {
123                self.new_type_error(format!(
124                    "'{}' object cannot be interpreted as an integer",
125                    result.class().name()
126                ))
127            })?
128            .try_to_primitive::<isize>(self)?;
129        if hint.is_negative() {
130            Err(self.new_value_error("__length_hint__() should return >= 0".to_owned()))
131        } else {
132            Ok(Some(hint as usize))
133        }
134    }
135
136    /// Checks that the multiplication is able to be performed. On Ok returns the
137    /// index as a usize for sequences to be able to use immediately.
138    pub fn check_repeat_or_overflow_error(&self, length: usize, n: isize) -> PyResult<usize> {
139        if n <= 0 {
140            Ok(0)
141        } else {
142            let n = n as usize;
143            if length > crate::stdlib::sys::MAXSIZE as usize / n {
144                Err(self.new_overflow_error("repeated value are too long".to_owned()))
145            } else {
146                Ok(n)
147            }
148        }
149    }
150
151    /// Calling scheme used for binary operations:
152    ///
153    /// Order operations are tried until either a valid result or error:
154    ///   b.rop(b,a)[*], a.op(a,b), b.rop(b,a)
155    ///
156    /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a)
157    pub fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: PyNumberBinaryOp) -> PyResult {
158        let class_a = a.class();
159        let class_b = b.class();
160
161        let slot_a = class_a.slots.as_number.left_binary_op(op_slot);
162        let mut slot_b = None;
163
164        if !class_a.is(class_b) {
165            let slot_bb = class_b.slots.as_number.right_binary_op(op_slot);
166            if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
167                slot_b = slot_bb;
168            }
169        }
170
171        if let Some(slot_a) = slot_a {
172            if let Some(slot_bb) = slot_b {
173                if class_b.fast_issubclass(class_a) {
174                    let ret = slot_bb(a, b, self)?;
175                    if !ret.is(&self.ctx.not_implemented) {
176                        return Ok(ret);
177                    }
178                    slot_b = None;
179                }
180            }
181            let ret = slot_a(a, b, self)?;
182            if !ret.is(&self.ctx.not_implemented) {
183                return Ok(ret);
184            }
185        }
186
187        if let Some(slot_b) = slot_b {
188            let ret = slot_b(a, b, self)?;
189            if !ret.is(&self.ctx.not_implemented) {
190                return Ok(ret);
191            }
192        }
193
194        Ok(self.ctx.not_implemented())
195    }
196
197    pub fn binary_op(
198        &self,
199        a: &PyObject,
200        b: &PyObject,
201        op_slot: PyNumberBinaryOp,
202        op: &str,
203    ) -> PyResult {
204        let result = self.binary_op1(a, b, op_slot)?;
205        if !result.is(&self.ctx.not_implemented) {
206            return Ok(result);
207        }
208        Err(self.new_unsupported_binop_error(a, b, op))
209    }
210
211    /// Binary in-place operators
212    ///
213    /// The in-place operators are defined to fall back to the 'normal',
214    /// non in-place operations, if the in-place methods are not in place.
215    ///
216    /// - If the left hand object has the appropriate struct members, and
217    ///   they are filled, call the appropriate function and return the
218    ///   result.  No coercion is done on the arguments; the left-hand object
219    ///   is the one the operation is performed on, and it's up to the
220    ///   function to deal with the right-hand object.
221    ///
222    /// - Otherwise, in-place modification is not supported. Handle it exactly as
223    ///   a non in-place operation of the same kind.
224    fn binary_iop1(
225        &self,
226        a: &PyObject,
227        b: &PyObject,
228        iop_slot: PyNumberBinaryOp,
229        op_slot: PyNumberBinaryOp,
230    ) -> PyResult {
231        if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) {
232            let x = slot(a, b, self)?;
233            if !x.is(&self.ctx.not_implemented) {
234                return Ok(x);
235            }
236        }
237        self.binary_op1(a, b, op_slot)
238    }
239
240    fn binary_iop(
241        &self,
242        a: &PyObject,
243        b: &PyObject,
244        iop_slot: PyNumberBinaryOp,
245        op_slot: PyNumberBinaryOp,
246        op: &str,
247    ) -> PyResult {
248        let result = self.binary_iop1(a, b, iop_slot, op_slot)?;
249        if !result.is(&self.ctx.not_implemented) {
250            return Ok(result);
251        }
252        Err(self.new_unsupported_binop_error(a, b, op))
253    }
254
255    fn ternary_op(
256        &self,
257        a: &PyObject,
258        b: &PyObject,
259        c: &PyObject,
260        op_slot: PyNumberTernaryOp,
261        op_str: &str,
262    ) -> PyResult {
263        let class_a = a.class();
264        let class_b = b.class();
265        let class_c = c.class();
266
267        let slot_a = class_a.slots.as_number.left_ternary_op(op_slot);
268        let mut slot_b = None;
269
270        if !class_a.is(class_b) {
271            let slot_bb = class_b.slots.as_number.right_ternary_op(op_slot);
272            if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
273                slot_b = slot_bb;
274            }
275        }
276
277        if let Some(slot_a) = slot_a {
278            if let Some(slot_bb) = slot_b {
279                if class_b.fast_issubclass(class_a) {
280                    let ret = slot_bb(a, b, c, self)?;
281                    if !ret.is(&self.ctx.not_implemented) {
282                        return Ok(ret);
283                    }
284                    slot_b = None;
285                }
286            }
287            let ret = slot_a(a, b, c, self)?;
288            if !ret.is(&self.ctx.not_implemented) {
289                return Ok(ret);
290            }
291        }
292
293        if let Some(slot_b) = slot_b {
294            let ret = slot_b(a, b, c, self)?;
295            if !ret.is(&self.ctx.not_implemented) {
296                return Ok(ret);
297            }
298        }
299
300        if let Some(slot_c) = class_c.slots.as_number.left_ternary_op(op_slot) {
301            if slot_a.map_or(false, |slot_a| (slot_a as usize) != (slot_c as usize))
302                && slot_b.map_or(false, |slot_b| (slot_b as usize) != (slot_c as usize))
303            {
304                let ret = slot_c(a, b, c, self)?;
305                if !ret.is(&self.ctx.not_implemented) {
306                    return Ok(ret);
307                }
308            }
309        }
310
311        Err(if self.is_none(c) {
312            self.new_type_error(format!(
313                "unsupported operand type(s) for {}: \
314                '{}' and '{}'",
315                op_str,
316                a.class(),
317                b.class()
318            ))
319        } else {
320            self.new_type_error(format!(
321                "unsupported operand type(s) for {}: \
322                '{}' and '{}', '{}'",
323                op_str,
324                a.class(),
325                b.class(),
326                c.class()
327            ))
328        })
329    }
330
331    fn ternary_iop(
332        &self,
333        a: &PyObject,
334        b: &PyObject,
335        c: &PyObject,
336        iop_slot: PyNumberTernaryOp,
337        op_slot: PyNumberTernaryOp,
338        op_str: &str,
339    ) -> PyResult {
340        if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) {
341            let x = slot(a, b, c, self)?;
342            if !x.is(&self.ctx.not_implemented) {
343                return Ok(x);
344            }
345        }
346        self.ternary_op(a, b, c, op_slot, op_str)
347    }
348
349    binary_func!(_sub, Subtract, "-");
350    binary_func!(_mod, Remainder, "%");
351    binary_func!(_divmod, Divmod, "divmod");
352    binary_func!(_lshift, Lshift, "<<");
353    binary_func!(_rshift, Rshift, ">>");
354    binary_func!(_and, And, "&");
355    binary_func!(_xor, Xor, "^");
356    binary_func!(_or, Or, "|");
357    binary_func!(_floordiv, FloorDivide, "//");
358    binary_func!(_truediv, TrueDivide, "/");
359    binary_func!(_matmul, MatrixMultiply, "@");
360
361    inplace_binary_func!(_isub, InplaceSubtract, Subtract, "-=");
362    inplace_binary_func!(_imod, InplaceRemainder, Remainder, "%=");
363    inplace_binary_func!(_ilshift, InplaceLshift, Lshift, "<<=");
364    inplace_binary_func!(_irshift, InplaceRshift, Rshift, ">>=");
365    inplace_binary_func!(_iand, InplaceAnd, And, "&=");
366    inplace_binary_func!(_ixor, InplaceXor, Xor, "^=");
367    inplace_binary_func!(_ior, InplaceOr, Or, "|=");
368    inplace_binary_func!(_ifloordiv, InplaceFloorDivide, FloorDivide, "//=");
369    inplace_binary_func!(_itruediv, InplaceTrueDivide, TrueDivide, "/=");
370    inplace_binary_func!(_imatmul, InplaceMatrixMultiply, MatrixMultiply, "@=");
371
372    ternary_func!(_pow, Power, "** or pow()");
373    inplace_ternary_func!(_ipow, InplacePower, Power, "**=");
374
375    pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult {
376        let result = self.binary_op1(a, b, PyNumberBinaryOp::Add)?;
377        if !result.is(&self.ctx.not_implemented) {
378            return Ok(result);
379        }
380        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
381            let result = seq_a.concat(b, self)?;
382            if !result.is(&self.ctx.not_implemented) {
383                return Ok(result);
384            }
385        }
386        Err(self.new_unsupported_binop_error(a, b, "+"))
387    }
388
389    pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult {
390        let result = self.binary_iop1(a, b, PyNumberBinaryOp::InplaceAdd, PyNumberBinaryOp::Add)?;
391        if !result.is(&self.ctx.not_implemented) {
392            return Ok(result);
393        }
394        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
395            let result = seq_a.inplace_concat(b, self)?;
396            if !result.is(&self.ctx.not_implemented) {
397                return Ok(result);
398            }
399        }
400        Err(self.new_unsupported_binop_error(a, b, "+="))
401    }
402
403    pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult {
404        let result = self.binary_op1(a, b, PyNumberBinaryOp::Multiply)?;
405        if !result.is(&self.ctx.not_implemented) {
406            return Ok(result);
407        }
408        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
409            let n =
410                b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
411                    self.new_overflow_error("repeated bytes are too long".to_owned())
412                })?;
413            return seq_a.repeat(n, self);
414        } else if let Ok(seq_b) = PySequence::try_protocol(b, self) {
415            let n =
416                a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
417                    self.new_overflow_error("repeated bytes are too long".to_owned())
418                })?;
419            return seq_b.repeat(n, self);
420        }
421        Err(self.new_unsupported_binop_error(a, b, "*"))
422    }
423
424    pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult {
425        let result = self.binary_iop1(
426            a,
427            b,
428            PyNumberBinaryOp::InplaceMultiply,
429            PyNumberBinaryOp::Multiply,
430        )?;
431        if !result.is(&self.ctx.not_implemented) {
432            return Ok(result);
433        }
434        if let Ok(seq_a) = PySequence::try_protocol(a, self) {
435            let n =
436                b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
437                    self.new_overflow_error("repeated bytes are too long".to_owned())
438                })?;
439            return seq_a.inplace_repeat(n, self);
440        } else if let Ok(seq_b) = PySequence::try_protocol(b, self) {
441            let n =
442                a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| {
443                    self.new_overflow_error("repeated bytes are too long".to_owned())
444                })?;
445            /* Note that the right hand operand should not be
446             * mutated in this case so inplace_repeat is not
447             * used. */
448            return seq_b.repeat(n, self);
449        }
450        Err(self.new_unsupported_binop_error(a, b, "*="))
451    }
452
453    pub fn _abs(&self, a: &PyObject) -> PyResult<PyObjectRef> {
454        self.get_special_method(a, identifier!(self, __abs__))?
455            .ok_or_else(|| self.new_unsupported_unary_error(a, "abs()"))?
456            .invoke((), self)
457    }
458
459    pub fn _pos(&self, a: &PyObject) -> PyResult {
460        self.get_special_method(a, identifier!(self, __pos__))?
461            .ok_or_else(|| self.new_unsupported_unary_error(a, "unary +"))?
462            .invoke((), self)
463    }
464
465    pub fn _neg(&self, a: &PyObject) -> PyResult {
466        self.get_special_method(a, identifier!(self, __neg__))?
467            .ok_or_else(|| self.new_unsupported_unary_error(a, "unary -"))?
468            .invoke((), self)
469    }
470
471    pub fn _invert(&self, a: &PyObject) -> PyResult {
472        self.get_special_method(a, identifier!(self, __invert__))?
473            .ok_or_else(|| self.new_unsupported_unary_error(a, "unary ~"))?
474            .invoke((), self)
475    }
476
477    // PyObject_Format
478    pub fn format(&self, obj: &PyObject, format_spec: PyStrRef) -> PyResult<PyStrRef> {
479        if format_spec.is_empty() {
480            let obj = match obj.to_owned().downcast_exact::<PyStr>(self) {
481                Ok(s) => return Ok(s.into_pyref()),
482                Err(obj) => obj,
483            };
484            if obj.class().is(self.ctx.types.int_type) {
485                return obj.str(self);
486            }
487        }
488        let bound_format = self
489            .get_special_method(obj, identifier!(self, __format__))?
490            .ok_or_else(|| {
491                self.new_type_error(format!(
492                    "Type {} doesn't define __format__",
493                    obj.class().name()
494                ))
495            })?;
496        let formatted = bound_format.invoke((format_spec,), self)?;
497        formatted.downcast().map_err(|result| {
498            self.new_type_error(format!(
499                "__format__ must return a str, not {}",
500                &result.class().name()
501            ))
502        })
503    }
504
505    // https://docs.python.org/3/reference/expressions.html#membership-test-operations
506    fn _membership_iter_search(
507        &self,
508        haystack: &PyObject,
509        needle: PyObjectRef,
510    ) -> PyResult<PyIntRef> {
511        let iter = haystack.get_iter(self)?;
512        loop {
513            if let PyIterReturn::Return(element) = iter.next(self)? {
514                if self.bool_eq(&element, &needle)? {
515                    return Ok(self.ctx.new_bool(true));
516                } else {
517                    continue;
518                }
519            } else {
520                return Ok(self.ctx.new_bool(false));
521            }
522        }
523    }
524
525    pub fn _contains(&self, haystack: &PyObject, needle: &PyObject) -> PyResult<bool> {
526        let seq = haystack.to_sequence();
527        seq.contains(needle, self)
528    }
529}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.