Use i-methods for in-place operations

This commit is contained in:
Joey Hain
2019-02-17 10:47:58 -08:00
parent fe3f45f655
commit b310d5e24b
4 changed files with 197 additions and 72 deletions

View File

@@ -71,6 +71,7 @@ pub enum Instruction {
},
BinaryOperation {
op: BinaryOperator,
inplace: bool,
},
LoadAttr {
name: String,

View File

@@ -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 } => {

View File

@@ -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),
}?;

View File

@@ -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<F>(
&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, ">="))
})
}