diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs index 9fbe14663..951659f06 100644 --- a/vm/src/bytecode.rs +++ b/vm/src/bytecode.rs @@ -71,6 +71,7 @@ pub enum Instruction { }, BinaryOperation { op: BinaryOperator, + inplace: bool, }, LoadAttr { name: String, diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 34f1e7eb0..067bfa442 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -608,7 +608,7 @@ impl Compiler { self.compile_expression(value)?; // Perform operation: - self.compile_op(op); + self.compile_op(op, true); self.compile_store(target)?; } ast::Statement::Delete { targets } => { @@ -757,7 +757,7 @@ impl Compiler { Ok(()) } - fn compile_op(&mut self, op: &ast::Operator) { + fn compile_op(&mut self, op: &ast::Operator, inplace: bool) { let i = match op { ast::Operator::Add => bytecode::BinaryOperator::Add, ast::Operator::Sub => bytecode::BinaryOperator::Subtract, @@ -773,7 +773,7 @@ impl Compiler { ast::Operator::BitXor => bytecode::BinaryOperator::Xor, ast::Operator::BitAnd => bytecode::BinaryOperator::And, }; - self.emit(Instruction::BinaryOperation { op: i }); + self.emit(Instruction::BinaryOperation { op: i, inplace }); } fn compile_test( @@ -852,13 +852,14 @@ impl Compiler { self.compile_expression(b)?; // Perform operation: - self.compile_op(op); + self.compile_op(op, false); } ast::Expression::Subscript { a, b } => { self.compile_expression(a)?; self.compile_expression(b)?; self.emit(Instruction::BinaryOperation { op: bytecode::BinaryOperator::Subscript, + inplace: false, }); } ast::Expression::Unop { op, a } => { diff --git a/vm/src/frame.rs b/vm/src/frame.rs index f7c7fa3ba..d57c9de73 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -316,7 +316,9 @@ impl Frame { vm.call_method(&dict_obj, "__setitem__", vec![key, value])?; Ok(None) } - bytecode::Instruction::BinaryOperation { ref op } => self.execute_binop(vm, op), + bytecode::Instruction::BinaryOperation { ref op, inplace } => { + self.execute_binop(vm, op, *inplace) + } bytecode::Instruction::LoadAttr { ref name } => self.load_attr(vm, name), bytecode::Instruction::StoreAttr { ref name } => self.store_attr(vm, name), bytecode::Instruction::DeleteAttr { ref name } => self.delete_attr(vm, name), @@ -893,23 +895,39 @@ impl Frame { &mut self, vm: &mut VirtualMachine, op: &bytecode::BinaryOperator, + inplace: bool, ) -> FrameResult { let b_ref = self.pop_value(); let a_ref = self.pop_value(); let value = match *op { + bytecode::BinaryOperator::Subtract if inplace => vm._isub(a_ref, b_ref), bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref), + bytecode::BinaryOperator::Add if inplace => vm._iadd(a_ref, b_ref), bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref), + bytecode::BinaryOperator::Multiply if inplace => vm._imul(a_ref, b_ref), bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref), + bytecode::BinaryOperator::MatrixMultiply if inplace => vm._imatmul(a_ref, b_ref), bytecode::BinaryOperator::MatrixMultiply => vm._matmul(a_ref, b_ref), + bytecode::BinaryOperator::Power if inplace => vm._ipow(a_ref, b_ref), bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref), + bytecode::BinaryOperator::Divide if inplace => vm._itruediv(a_ref, b_ref), bytecode::BinaryOperator::Divide => vm._truediv(a_ref, b_ref), + bytecode::BinaryOperator::FloorDivide if inplace => vm._ifloordiv(a_ref, b_ref), bytecode::BinaryOperator::FloorDivide => vm._floordiv(a_ref, b_ref), + // TODO: Subscript should probably have its own op + bytecode::BinaryOperator::Subscript if inplace => unreachable!(), bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref), + bytecode::BinaryOperator::Modulo if inplace => vm._imod(a_ref, b_ref), bytecode::BinaryOperator::Modulo => vm._mod(a_ref, b_ref), + bytecode::BinaryOperator::Lshift if inplace => vm._ilshift(a_ref, b_ref), bytecode::BinaryOperator::Lshift => vm._lshift(a_ref, b_ref), + bytecode::BinaryOperator::Rshift if inplace => vm._irshift(a_ref, b_ref), bytecode::BinaryOperator::Rshift => vm._rshift(a_ref, b_ref), + bytecode::BinaryOperator::Xor if inplace => vm._ixor(a_ref, b_ref), bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref), + bytecode::BinaryOperator::Or if inplace => vm._ior(a_ref, b_ref), bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref), + bytecode::BinaryOperator::And if inplace => vm._iand(a_ref, b_ref), bytecode::BinaryOperator::And => vm._and(a_ref, b_ref), }?; diff --git a/vm/src/vm.rs b/vm/src/vm.rs index bfa2fe57d..5302bbf4c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -499,171 +499,276 @@ impl VirtualMachine { } } - /// Calls default method, reverse method or exception + /// Calls a method on `obj` passing `arg`, if the method exists. /// - /// * `a` - First argument. - /// * `b` - Second argument. - /// * `d` - Default method to try and call (such as `__and__`). - /// * `r` - Reverse method to try and call (such as `__rand__`), in case first one fails. - /// * `op` - Operator for the exception text, for example `&`. - /// - /// Given the above example, it will - /// 1. Try to call `__and__` with `a` and `b` - /// 2. If above fails try to call `__rand__` with `a` and `b` - /// 3. If above in not implemented, call unsupported(a, b) for result. - /// - pub fn call_or_unsupported( + /// Otherwise, or if the result is the special `NotImplemented` built-in constant, + /// calls `unsupported` to determine fallback value. + pub fn call_or_unsupported( &mut self, - a: PyObjectRef, - b: PyObjectRef, - d: &str, - r: &str, + obj: PyObjectRef, + arg: PyObjectRef, + method: &str, + unsupported: F, + ) -> PyResult + where + F: Fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, + { + if let Ok(method) = self.get_method(obj.clone(), method) { + let result = self.invoke( + method, + PyFuncArgs { + args: vec![arg.clone()], + kwargs: vec![], + }, + )?; + if !result.is(&self.ctx.not_implemented()) { + return Ok(result); + } + } + + unsupported(self, obj, arg) + } + + /// Calls a method, falling back to its reflection with the operands + /// reversed, and then to the value provided by `unsupported`. + /// + /// For example: the following: + /// + /// `call_or_reflection(lhs, rhs, "__and__", "__rand__", unsupported)` + /// + /// 1. Calls `__and__` with `lhs` and `rhs`. + /// 2. If above is not implemented, calls `__rand__` with `rhs` and `lhs`. + /// 3. If above is not implemented, invokes `unsupported` for the result. + pub fn call_or_reflection( + &mut self, + lhs: PyObjectRef, + rhs: PyObjectRef, + default: &str, + reflection: &str, unsupported: fn(&mut VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult, ) -> PyResult { - // Try to call the first method - if let Ok(method) = self.get_method(a.clone(), d) { - let result = self.invoke( - method, - PyFuncArgs { - args: vec![b.clone()], - kwargs: vec![], - }, - )?; - - if !result.is(&self.ctx.not_implemented()) { - return Ok(result); - } - } - - // 2. Try to call reverse method - if let Ok(method) = self.get_method(b.clone(), r) { - let result = self.invoke( - method, - PyFuncArgs { - args: vec![a.clone()], - kwargs: vec![], - }, - )?; - - if !result.is(&self.ctx.not_implemented()) { - return Ok(result); - } - } - - unsupported(self, a, b) + // Try to call the default method + self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| { + // Try to call the reflection method + vm.call_or_unsupported(rhs, lhs, reflection, unsupported) + }) } pub fn _sub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__sub__", "__rsub__", |vm, a, b| { + self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "-")) }) } + pub fn _isub(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__isub__", |vm, a, b| { + vm.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "-=")) + }) + }) + } + pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__add__", "__radd__", |vm, a, b| { + self.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "+")) }) } + pub fn _iadd(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__iadd__", |vm, a, b| { + vm.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "+=")) + }) + }) + } + pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mul__", "__rmul__", |vm, a, b| { + self.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "*")) }) } + pub fn _imul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__imul__", |vm, a, b| { + vm.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "*=")) + }) + }) + } + pub fn _matmul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__matmul__", "__rmatmul__", |vm, a, b| { + self.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "@")) }) } + pub fn _imatmul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__imatmul__", |vm, a, b| { + vm.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "@=")) + }) + }) + } + pub fn _truediv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { + self.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "/")) }) } + pub fn _itruediv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__itruediv__", |vm, a, b| { + vm.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "/=")) + }) + }) + } + pub fn _floordiv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| { + self.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "//")) }) } + pub fn _ifloordiv(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ifloordiv__", |vm, a, b| { + vm.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "//=")) + }) + }) + } + pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__pow__", "__rpow__", |vm, a, b| { + self.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "**")) }) } + pub fn _ipow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ipow__", |vm, a, b| { + vm.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "**=")) + }) + }) + } + pub fn _mod(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__mod__", "__rmod__", |vm, a, b| { + self.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "%")) }) } + pub fn _imod(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__imod__", |vm, a, b| { + vm.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "%=")) + }) + }) + } + pub fn _lshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__lshift__", "__rlshift__", |vm, a, b| { + self.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<<")) }) } + pub fn _ilshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ilshift__", |vm, a, b| { + vm.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "<<=")) + }) + }) + } + pub fn _rshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__rshift__", "__rrshift__", |vm, a, b| { + self.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">>")) }) } + pub fn _irshift(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__irshift__", |vm, a, b| { + vm.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, ">>=")) + }) + }) + } + pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__xor__", "__rxor__", |vm, a, b| { + self.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "^")) }) } + pub fn _ixor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ixor__", |vm, a, b| { + vm.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "^=")) + }) + }) + } + pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__or__", "__ror__", |vm, a, b| { + self.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "|")) }) } + pub fn _ior(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__ior__", |vm, a, b| { + vm.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "|=")) + }) + }) + } + pub fn _and(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__and__", "__rand__", |vm, a, b| { + self.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "&")) }) } + pub fn _iand(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { + self.call_or_unsupported(a, b, "__iand__", |vm, a, b| { + vm.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| { + Err(vm.new_unsupported_operand_error(a, b, "&=")) + }) + }) + } + pub fn _eq(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__eq__", "__eq__", |vm, a, b| { + self.call_or_reflection(a, b, "__eq__", "__eq__", |vm, a, b| { Ok(vm.new_bool(a.is(&b))) }) } pub fn _ne(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__ne__", "__ne__", |vm, a, b| { + self.call_or_reflection(a, b, "__ne__", "__ne__", |vm, a, b| { let eq = vm._eq(a, b)?; objbool::not(vm, &eq) }) } pub fn _lt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__lt__", "__gt__", |vm, a, b| { + self.call_or_reflection(a, b, "__lt__", "__gt__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<")) }) } pub fn _le(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__le__", "__ge__", |vm, a, b| { + self.call_or_reflection(a, b, "__le__", "__ge__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, "<=")) }) } pub fn _gt(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__gt__", "__lt__", |vm, a, b| { + self.call_or_reflection(a, b, "__gt__", "__lt__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">")) }) } pub fn _ge(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult { - self.call_or_unsupported(a, b, "__ge__", "__le__", |vm, a, b| { + self.call_or_reflection(a, b, "__ge__", "__le__", |vm, a, b| { Err(vm.new_unsupported_operand_error(a, b, ">=")) }) }