From b96dddfed14868331a6a4b2caa0b9bb23ed2ed38 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 24 Jul 2022 07:18:03 +0900 Subject: [PATCH] PyObject::try_int --- vm/src/builtins/int.rs | 2 +- vm/src/protocol/number.rs | 116 +++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 53 deletions(-) diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 232568ff23..991b3d7edf 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -266,7 +266,7 @@ impl Constructor for PyInt { val }; - val.to_number().int(vm).map(|x| x.as_bigint().clone()) + val.try_int(vm).map(|x| x.as_bigint().clone()) } } else if let OptionalArg::Present(_) = options.base { Err(vm.new_type_error("int() missing string argument".to_owned())) diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index b5c0b644af..da963b6ed2 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -41,6 +41,62 @@ impl PyObject { )) }) } + + pub fn try_int(&self, vm: &VirtualMachine) -> PyResult { + fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult { + let base = 10; + match int::bytes_to_int(lit, base) { + Some(i) => Ok(PyInt::from(i).into_ref(vm)), + None => Err(vm.new_value_error(format!( + "invalid literal for int() with base {}: {}", + base, + obj.repr(vm)?, + ))), + } + } + + if let Some(i) = self.downcast_ref_if_exact::(vm) { + Ok(i.to_owned()) + } else { + let number = self.to_number(); + if let Some(i) = number.int(vm)? { + Ok(i) + } else if let Some(i) = self.try_index_opt(vm) { + i + } else if let Ok(Ok(f)) = + vm.get_special_method(self.to_owned(), identifier!(vm, __trunc__)) + { + // TODO: Deprecate in 3.11 + // warnings::warn( + // vm.ctx.exceptions.deprecation_warning.clone(), + // "The delegation of int() to __trunc__ is deprecated.".to_owned(), + // 1, + // vm, + // )?; + let ret = f.invoke((), vm)?; + ret.try_index(vm).map_err(|_| { + vm.new_type_error(format!( + "__trunc__ returned non-Integral (type {})", + ret.class() + )) + }) + } else if let Some(s) = self.payload::() { + try_convert(self, s.as_str().as_bytes(), vm) + } else if let Some(bytes) = self.payload::() { + try_convert(self, bytes, vm) + } else if let Some(bytearray) = self.payload::() { + try_convert(self, &bytearray.borrow_buf(), vm) + } else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self) { + // TODO: replace to PyBuffer + try_convert(self, &buffer.borrow_buf(), vm) + } else { + Err(vm.new_type_error(format!( + "int() argument must be a string, a bytes-like object or a real number, not '{}'", + self.class() + ))) + } + } + } } #[derive(Default)] @@ -178,24 +234,11 @@ impl PyNumber<'_> { self.methods().index.load().is_some() } - pub fn int(&self, vm: &VirtualMachine) -> PyResult { - fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult { - let base = 10; - match int::bytes_to_int(lit, base) { - Some(i) => Ok(PyInt::from(i).into_ref(vm)), - None => Err(vm.new_value_error(format!( - "invalid literal for int() with base {}: {}", - base, - obj.repr(vm)?, - ))), - } - } - - if let Some(i) = self.obj.downcast_ref_if_exact::(vm) { - Ok(i.to_owned()) - } else if let Some(f) = self.methods().int.load() { + #[inline] + pub fn int(&self, vm: &VirtualMachine) -> PyResult> { + Ok(if let Some(f) = self.methods().int.load() { let ret = f(self, vm)?; - if !ret.class().is(PyInt::class(vm)) { + Some(if !ret.class().is(PyInt::class(vm)) { warnings::warn( vm.ctx.exceptions.deprecation_warning, format!( @@ -207,44 +250,13 @@ impl PyNumber<'_> { 1, vm, )?; - Ok(vm.ctx.new_bigint(ret.as_bigint())) + vm.ctx.new_bigint(ret.as_bigint()) } else { - Ok(ret) - } - } else if self.methods().index.load().is_some() { - self.obj.try_index(vm) - } else if let Ok(Ok(f)) = - vm.get_special_method(self.obj.to_owned(), identifier!(vm, __trunc__)) - { - // TODO: Deprecate in 3.11 - // warnings::warn( - // vm.ctx.exceptions.deprecation_warning.clone(), - // "The delegation of int() to __trunc__ is deprecated.".to_owned(), - // 1, - // vm, - // )?; - let ret = f.invoke((), vm)?; - ret.try_index(vm).map_err(|_| { - vm.new_type_error(format!( - "__trunc__ returned non-Integral (type {})", - ret.class() - )) + ret }) - } else if let Some(s) = self.obj.payload::() { - try_convert(self.obj, s.as_str().as_bytes(), vm) - } else if let Some(bytes) = self.obj.payload::() { - try_convert(self.obj, bytes, vm) - } else if let Some(bytearray) = self.obj.payload::() { - try_convert(self.obj, &bytearray.borrow_buf(), vm) - } else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self.obj) { - // TODO: replace to PyBuffer - try_convert(self.obj, &buffer.borrow_buf(), vm) } else { - Err(vm.new_type_error(format!( - "int() argument must be a string, a bytes-like object or a real number, not '{}'", - self.obj.class() - ))) - } + None + }) } #[inline]