PyAttributes key is PyStrInterned

This commit is contained in:
Jeong Yunwon committed 2022-05-23 09:03:13 +09:00
1 parent f8b1c65ede
commit fb52694e41
50 files changed
+1494 -898

No files matched your search

+2 -2
View File
@@ -389,9 +389,9 @@ class ClassDefVisitor(EmitVisitor):
self.emit(f"#[extend_class]", depth + 1)
self.emit("fn extend_class_with_fields(ctx: &Context, class: &PyTypeRef) {", depth + 1)
fields = ",".join(f"ctx.new_str(ascii!({json.dumps(f.name)})).into()" for f in fields)
self.emit(f'class.set_str_attr("_fields", ctx.new_list(vec![{fields}]));', depth + 2)
self.emit(f'class.set_attr(interned!(ctx, _fields), ctx.new_list(vec![{fields}]).into());', depth + 2)
attrs = ",".join(f"ctx.new_str(ascii!({json.dumps(attr.name)})).into()" for attr in attrs)
self.emit(f'class.set_str_attr("_attributes", ctx.new_list(vec![{attrs}]));', depth + 2)
self.emit(f'class.set_attr(interned!(ctx, _attributes), ctx.new_list(vec![{attrs}]).into());', depth + 2)
self.emit("}", depth + 1)
self.emit("}", depth)
+1 -1
View File
@@ -116,7 +116,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
.map(|entry| {
let path = entry.unwrap().path();
(
path.file_name().unwrap().to_str().unwrap().to_owned(),
path.file_name().unwrap().to_owned().unwrap().to_owned(),
std::fs::read_to_string(path).unwrap(),
)
})
+5 -3
View File
@@ -503,7 +503,8 @@ where
#py_name,
ctx.make_funcdef(#py_name, Self::#ident)
#doc
#build_func
#build_func,
ctx,
);
}
};
@@ -611,7 +612,7 @@ where
ident,
py_name.clone(),
quote! {
class.set_str_attr(#py_name, #value);
class.set_str_attr(#py_name, #value, ctx);
},
)
} else {
@@ -736,7 +737,8 @@ impl ToTokens for GetSetNursery {
::rustpython_vm::builtins::PyGetSet::new(#name.into(), class.clone())
.with_get(&Self::#getter)
#setter #deleter,
ctx.types.getset_type.clone(), None)
ctx.types.getset_type.clone(), None),
ctx
);
}
});
+1 -1
View File
@@ -433,7 +433,7 @@ impl ModuleItem for ClassItem {
};
let class_new = quote_spanned!(ident.span() =>
let new_class = <#ident as ::rustpython_vm::class::PyClassImpl>::make_class(&vm.ctx);
new_class.set_str_attr("__module__", vm.new_pyobj(#module_name));
new_class.set_attr(rustpython_vm::identifier!(vm, __module__), vm.new_pyobj(#module_name));
);
(class_name, class_new)
};
+9 -4
View File
@@ -2,7 +2,7 @@
use rustpython_vm::{
builtins::{PyDictRef, PyStrRef},
function::ArgIterable,
PyResult, TryFromObject, VirtualMachine,
identifier, PyResult, TryFromObject, VirtualMachine,
};
pub struct ShellHelper<'vm> {
@@ -81,14 +81,19 @@ impl<'vm> ShellHelper<'vm> {
current = current.get_attr(attr.as_str(), self.vm).ok()?;
}
let current_iter = str_iter_method(current, "__dir__").ok()?;
let current_iter = str_iter_method(current, identifier!(self.vm, __dir__)).ok()?;
(last, current_iter, None)
} else {
// we need to get a variable based off of globals/builtins
let globals = str_iter_method(self.globals.clone().into(), "keys").ok()?;
let builtins = str_iter_method(self.vm.builtins.clone().into(), "__dir__").ok()?;
let globals =
str_iter_method(self.globals.clone().into(), identifier!(self.vm, keys)).ok()?;
let builtins = str_iter_method(
self.vm.builtins.clone().into(),
identifier!(self.vm, __dir__),
)
.ok()?;
(first, globals, Some(builtins))
};
Some((word_start, iter1.chain(iter2.into_iter().flatten())))
+11 -7
View File
@@ -3,9 +3,9 @@ pub(crate) use math::make_module;
#[pymodule]
mod math {
use crate::vm::{
builtins::{try_bigint_to_f64, try_f64_to_bigint, PyFloat, PyInt, PyIntRef},
builtins::{try_bigint_to_f64, try_f64_to_bigint, PyFloat, PyInt, PyIntRef, PyStrInterned},
function::{ArgIntoFloat, ArgIterable, Either, OptionalArg, PosArgs},
AsObject, PyObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
identifier, AsObject, PyObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
};
use num_bigint::BigInt;
use num_traits::{One, Signed, Zero};
@@ -430,12 +430,16 @@ mod math {
}
}
fn try_magic_method(func_name: &str, vm: &VirtualMachine, value: &PyObject) -> PyResult {
fn try_magic_method(
func_name: &'static PyStrInterned,
vm: &VirtualMachine,
value: &PyObject,
) -> PyResult {
let method = vm.get_method_or_type_error(value.to_owned(), func_name, || {
format!(
"type '{}' doesn't define '{}' method",
value.class().name(),
func_name,
func_name.as_str(),
)
})?;
vm.invoke(&method, ())
@@ -443,12 +447,12 @@ mod math {
#[pyfunction]
fn trunc(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
try_magic_method("__trunc__", vm, &x)
try_magic_method(identifier!(vm, __trunc__), vm, &x)
}
#[pyfunction]
fn ceil(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let result_or_err = try_magic_method("__ceil__", vm, &x);
let result_or_err = try_magic_method(identifier!(vm, __ceil__), vm, &x);
if result_or_err.is_err() {
if let Ok(Some(v)) = x.try_to_f64(vm) {
let v = try_f64_to_bigint(v.ceil(), vm)?;
@@ -460,7 +464,7 @@ mod math {
#[pyfunction]
fn floor(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let result_or_err = try_magic_method("__floor__", vm, &x);
let result_or_err = try_magic_method(identifier!(vm, __floor__), vm, &x);
if result_or_err.is_err() {
if let Ok(Some(v)) = x.try_to_f64(vm) {
let v = try_f64_to_bigint(v.floor(), vm)?;
+1 -1
View File
@@ -25,7 +25,7 @@ macro_rules! create_property {
move |this: &PyExpatLikeXmlParser, func: PyObjectRef| *this.$element.write() = func,
);
$attributes.insert($name.to_owned(), attr.into());
$attributes.insert($ctx.intern_str($name), attr.into());
};
}
+5 -3
View File
@@ -75,9 +75,11 @@ struct Selectable {
impl TryFromObject for Selectable {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let fno = obj.try_to_value(vm).or_else(|_| {
let meth = vm.get_method_or_type_error(obj.clone(), "fileno", || {
"select arg must be an int or object with a fileno() method".to_owned()
})?;
let meth = vm.get_method_or_type_error(
obj.clone(),
vm.ctx.interned_str("fileno").unwrap(),
|| "select arg must be an int or object with a fileno() method".to_owned(),
)?;
vm.invoke(&meth, ())?.try_into_value(vm)
})?;
Ok(Selectable { obj, fno })
+7 -6
View File
@@ -1,7 +1,8 @@
use super::{PyInt, PyStrRef, PyTypeRef};
use crate::{
class::PyClassImpl, convert::ToPyObject, function::OptionalArg, types::Constructor, AsObject,
Context, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine,
class::PyClassImpl, convert::ToPyObject, function::OptionalArg, identifier, types::Constructor,
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject,
VirtualMachine,
};
use num_bigint::Sign;
use num_traits::Zero;
@@ -32,7 +33,7 @@ impl PyObjectRef {
if self.is(&vm.ctx.false_value) {
return Ok(false);
}
let rs_bool = match vm.get_method(self.clone(), "__bool__") {
let rs_bool = match vm.get_method(self.clone(), identifier!(vm, __bool__)) {
Some(method_or_err) => {
// If descriptor returns Error, propagate it further
let method = method_or_err?;
@@ -46,7 +47,7 @@ impl PyObjectRef {
get_value(&bool_obj)
}
None => match vm.get_method(self, "__len__") {
None => match vm.get_method(self, identifier!(vm, __len__)) {
Some(method_or_err) => {
let method = method_or_err?;
let bool_obj = vm.invoke(&method, ())?;
@@ -112,9 +113,9 @@ impl PyBool {
#[pymethod(magic)]
fn repr(zelf: bool, vm: &VirtualMachine) -> PyStrRef {
if zelf {
vm.ctx.true_str
vm.ctx.names.True
} else {
vm.ctx.false_str
vm.ctx.names.False
}
.to_owned()
}
+2 -1
View File
@@ -7,6 +7,7 @@ use crate::{
PyArithmeticValue::{self, *},
PyComparisonValue,
},
identifier,
types::{Comparable, Constructor, Hashable, PyComparisonOp},
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -48,7 +49,7 @@ impl PyObjectRef {
if let Some(complex) = self.payload_if_exact::<PyComplex>(vm) {
return Ok(Some((complex.value, true)));
}
if let Some(method) = vm.get_method(self.clone(), "__complex__") {
if let Some(method) = vm.get_method(self.clone(), identifier!(vm, __complex__)) {
let result = vm.invoke(&method?, ())?;
// TODO: returning strict subclasses of complex in __complex__ is deprecated
return match result.payload::<PyComplex>() {
+8 -7
View File
@@ -83,7 +83,7 @@ impl PyDict {
Ok(dict_other) => return Self::merge_dict(dict, dict_other, vm),
Err(other) => other,
};
if let Some(keys) = vm.get_method(other.clone(), "keys") {
if let Some(keys) = vm.get_method(other.clone(), vm.ctx.intern_str("keys")) {
let keys = vm.invoke(&keys?, ())?.get_iter(vm)?;
while let PyIterReturn::Return(key) = keys.next(vm)? {
let val = other.get_item(&*key, vm)?;
@@ -196,13 +196,13 @@ impl PyDict {
}
pub fn from_attributes(attrs: PyAttributes, vm: &VirtualMachine) -> PyResult<Self> {
let dict = DictContentType::default();
let entries = DictContentType::default();
for (key, value) in attrs {
dict.insert(vm, key.as_str(), value)?;
entries.insert(vm, key, value)?;
}
Ok(PyDict { entries: dict })
Ok(Self { entries })
}
pub fn contains_key<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> bool {
@@ -527,7 +527,7 @@ impl Py<PyDict> {
key: &K,
vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
vm.get_method(self.to_owned().into(), "__missing__")
vm.get_method(self.to_owned().into(), identifier!(vm, __missing__))
.map(|methods| vm.invoke(&methods?, (key.to_pyobject(vm),)))
.transpose()
}
@@ -548,11 +548,12 @@ impl Py<PyDict> {
}
/// Take a python dictionary and convert it to attributes.
pub fn to_attributes(&self) -> PyAttributes {
pub fn to_attributes(&self, vm: &VirtualMachine) -> PyAttributes {
let mut attrs = PyAttributes::default();
for (key, value) in self {
// TODO: use PyRefExact for interning
let key: PyStrRef = key.downcast().expect("dict has non-string keys");
attrs.insert(key.as_str().to_owned(), value);
attrs.insert(vm.ctx.intern_str(key.as_str()), value);
}
attrs
}
+2 -1
View File
@@ -9,6 +9,7 @@ use crate::{
PyArithmeticValue::{self, *},
PyComparisonValue,
},
identifier,
types::{Comparable, Constructor, Hashable, PyComparisonOp},
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
TryFromObject, VirtualMachine,
@@ -59,7 +60,7 @@ impl PyObject {
if let Some(float) = self.payload_if_exact::<PyFloat>(vm) {
return Ok(Some(float.value));
}
if let Some(method) = vm.get_method(self.to_owned(), "__float__") {
if let Some(method) = vm.get_method(self.to_owned(), identifier!(vm, __float__)) {
let result = vm.invoke(&method?, ())?;
// TODO: returning strict subclasses of float in __float__ is deprecated
return match result.payload::<PyFloat>() {
+4 -1
View File
@@ -458,7 +458,10 @@ impl Comparable for PyBoundMethod {
impl GetAttr for PyBoundMethod {
fn getattro(zelf: &Py<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
let class_attr = zelf.get_class_attr(name.as_str());
let class_attr = vm
.ctx
.interned_str(&*name)
.and_then(|attr_name| zelf.get_class_attr(attr_name));
if let Some(obj) = class_attr {
return vm.call_if_get_descriptor(obj, zelf.to_owned().into());
}
+15 -11
View File
@@ -82,16 +82,20 @@ impl PyGenericAlias {
return Ok("...".to_string());
}
if vm.get_attribute_opt(obj.clone(), "__origin__")?.is_some()
&& vm.get_attribute_opt(obj.clone(), "__args__")?.is_some()
if vm
.get_attribute_opt(obj.clone(), identifier!(vm, __origin__))?
.is_some()
&& vm
.get_attribute_opt(obj.clone(), identifier!(vm, __args__))?
.is_some()
{
return Ok(obj.repr(vm)?.as_str().to_string());
}
match (
vm.get_attribute_opt(obj.clone(), "__qualname__")?
vm.get_attribute_opt(obj.clone(), identifier!(vm, __qualname__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|n| n.as_str().to_string())),
vm.get_attribute_opt(obj.clone(), "__module__")?
vm.get_attribute_opt(obj.clone(), identifier!(vm, __module__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|m| m.as_str().to_string())),
) {
(None, _) | (_, None) => Ok(obj.repr(vm)?.as_str().to_string()),
@@ -186,11 +190,11 @@ impl PyGenericAlias {
}
}
fn is_typevar(obj: &PyObjectRef) -> bool {
fn is_typevar(obj: &PyObjectRef, vm: &VirtualMachine) -> bool {
let class = obj.class();
class.slot_name() == "TypeVar"
&& class
.get_attr("__module__")
.get_attr(identifier!(vm, __module__))
.and_then(|o| o.downcast_ref::<PyStr>().map(|s| s.as_str() == "typing"))
.unwrap_or(false)
}
@@ -198,13 +202,13 @@ fn is_typevar(obj: &PyObjectRef) -> bool {
fn make_parameters(args: &PyTupleRef, vm: &VirtualMachine) -> PyTupleRef {
let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len());
for arg in args {
if is_typevar(arg) {
if is_typevar(arg, vm) {
if !parameters.iter().any(|param| param.is(arg)) {
parameters.push(arg.clone());
}
} else if let Ok(subparams) = arg
.clone()
.get_attr("__parameters__", vm)
.get_attr(identifier!(vm, __parameters__), vm)
.and_then(|obj| PyTupleRef::try_from_object(vm, obj))
{
for subparam in &subparams {
@@ -231,7 +235,7 @@ fn subs_tvars(
vm: &VirtualMachine,
) -> PyResult {
obj.clone()
.get_attr("__parameters__", vm)
.get_attr(identifier!(vm, __parameters__), vm)
.ok()
.and_then(|sub_params| {
PyTupleRef::try_from_object(vm, sub_params)
@@ -290,7 +294,7 @@ pub fn subs_parameters<F: Fn(&VirtualMachine) -> PyResult<String>>(
let new_args = args
.iter()
.map(|arg| {
if is_typevar(arg) {
if is_typevar(arg, vm) {
let idx = tuple_index(&parameters, arg).unwrap();
Ok(arg_items[idx].clone())
} else {
@@ -322,7 +326,7 @@ impl Callable for PyGenericAlias {
type Args = FuncArgs;
fn call(zelf: &crate::Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
PyType::call(&zelf.origin, args, vm).map(|obj| {
if let Err(exc) = obj.set_attr("__orig_class__", zelf.to_owned(), vm) {
if let Err(exc) = obj.set_attr(identifier!(vm, __orig_class__), zelf.to_owned(), vm) {
if !exc.fast_isinstance(&vm.ctx.exceptions.attribute_error)
&& !exc.fast_isinstance(&vm.ctx.exceptions.type_error)
{
+2 -2
View File
@@ -952,7 +952,7 @@ pub(crate) fn try_int(obj: &PyObject, vm: &VirtualMachine) -> PyResult<BigInt> {
}
// call __int__, then __index__, then __trunc__ (converting the __trunc__ result via __index__ if needed)
// TODO: using __int__ is deprecated and removed in Python 3.10
if let Some(method) = vm.get_method(obj.to_owned(), "__int__") {
if let Some(method) = vm.get_method(obj.to_owned(), identifier!(vm, __int__)) {
let result = vm.invoke(&method?, ())?;
return match result.payload::<PyInt>() {
Some(int_obj) => Ok(int_obj.as_bigint().clone()),
@@ -966,7 +966,7 @@ pub(crate) fn try_int(obj: &PyObject, vm: &VirtualMachine) -> PyResult<BigInt> {
if let Some(r) = vm.to_index_opt(obj.to_owned()).transpose()? {
return Ok(r.as_bigint().clone());
}
if let Some(method) = vm.get_method(obj.to_owned(), "__trunc__") {
if let Some(method) = vm.get_method(obj.to_owned(), identifier!(vm, __trunc__)) {
let result = vm.invoke(&method?, ())?;
return vm
.to_index_opt(result.clone())
+12 -18
View File
@@ -1,14 +1,13 @@
use std::borrow::Cow;
use super::{PyDict, PyGenericAlias, PyList, PyStr, PyStrRef, PyTuple, PyTypeRef};
use super::{PyDict, PyGenericAlias, PyList, PyTuple, PyTypeRef};
use crate::{
class::PyClassImpl,
convert::ToPyObject,
function::OptionalArg,
protocol::{PyMapping, PyMappingMethods, PySequence, PySequenceMethods},
types::{AsMapping, AsSequence, Constructor, Iterable},
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
VirtualMachine,
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
#[pyclass(module = false, name = "mappingproxy")]
@@ -63,10 +62,9 @@ impl Constructor for PyMappingProxy {
impl PyMappingProxy {
fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
let opt = match &self.mapping {
MappingProxyInner::Class(class) => {
let key = PyStrRef::try_from_object(vm, key)?;
class.attributes.read().get(key.as_str()).cloned()
}
MappingProxyInner::Class(class) => key
.as_interned_str(vm)
.and_then(|key| class.attributes.read().get(key).cloned()),
MappingProxyInner::Dict(obj) => obj.get_item(&*key, vm).ok(),
};
Ok(opt)
@@ -92,13 +90,9 @@ impl PyMappingProxy {
fn _contains(&self, key: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
match &self.mapping {
MappingProxyInner::Class(class) => {
// let key = PyStrRef::try_from_object(vm, key)?;
let key = key
.payload::<PyStr>()
.ok_or_else(|| vm.new_downcast_type_error(PyStr::class(vm), key))?;
Ok(class.attributes.read().contains_key(key.as_str()))
}
MappingProxyInner::Class(class) => Ok(key
.as_interned_str(vm)
.map_or(false, |key| class.attributes.read().contains_key(key))),
MappingProxyInner::Dict(obj) => PySequence::from(obj.as_ref()).contains(key, vm),
}
}
@@ -116,7 +110,7 @@ impl PyMappingProxy {
PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
}
};
vm.call_method(&obj, "items", ())
vm.call_method(&obj, identifier!(vm, items).as_str(), ())
}
#[pymethod]
pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
@@ -126,7 +120,7 @@ impl PyMappingProxy {
PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
}
};
vm.call_method(&obj, "keys", ())
vm.call_method(&obj, identifier!(vm, keys).as_str(), ())
}
#[pymethod]
pub fn values(&self, vm: &VirtualMachine) -> PyResult {
@@ -136,12 +130,12 @@ impl PyMappingProxy {
PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
}
};
vm.call_method(&obj, "values", ())
vm.call_method(&obj, identifier!(vm, values).as_str(), ())
}
#[pymethod]
pub fn copy(&self, vm: &VirtualMachine) -> PyResult {
match &self.mapping {
MappingProxyInner::Dict(d) => vm.call_method(d, "copy", ()),
MappingProxyInner::Dict(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()),
MappingProxyInner::Class(c) => {
Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm))
}
+14 -5
View File
@@ -38,7 +38,7 @@ impl PyBaseObject {
};
// Ensure that all abstract methods are implemented before instantiating instance.
if let Some(abs_methods) = cls.get_attr("__abstractmethods__") {
if let Some(abs_methods) = cls.get_attr(identifier!(vm, __abstractmethods__)) {
if let Some(unimplemented_abstract_method_count) = abs_methods.length_opt(vm) {
if unimplemented_abstract_method_count? > 0 {
return Err(
@@ -230,7 +230,11 @@ impl PyBaseObject {
// Get instance attributes:
if let Some(object_dict) = obj.dict() {
vm.call_method(dict.as_object(), "update", (object_dict,))?;
vm.call_method(
dict.as_object(),
identifier!(vm, update).as_str(),
(object_dict,),
)?;
}
let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect();
@@ -305,10 +309,15 @@ impl PyBaseObject {
#[pymethod(magic)]
fn reduce_ex(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult {
if let Some(reduce) = vm.get_attribute_opt(obj.clone(), "__reduce__")? {
let object_reduce = vm.ctx.types.object_type.get_attr("__reduce__").unwrap();
if let Some(reduce) = vm.get_attribute_opt(obj.clone(), identifier!(vm, __reduce__))? {
let object_reduce = vm
.ctx
.types
.object_type
.get_attr(identifier!(vm, __reduce__))
.unwrap();
let typ_obj: PyObjectRef = obj.class().clone().into();
let class_reduce = typ_obj.get_attr("__reduce__", vm)?;
let class_reduce = typ_obj.get_attr(identifier!(vm, __reduce__), vm)?;
if !class_reduce.is(&object_reduce) {
return vm.invoke(&reduce, ());
}
+2 -2
View File
@@ -409,7 +409,7 @@ impl PyStr {
Self::new_str_unchecked(bytes.into_bytes(), kind)
}
.to_pyobject(vm))
} else if let Some(radd) = vm.get_method(other.clone(), "__radd__") {
} else if let Some(radd) = vm.get_method(other.clone(), identifier!(vm, __radd__)) {
// hack to get around not distinguishing number add from seq concat
vm.invoke(&radd?, (zelf,))
} else {
@@ -1149,7 +1149,7 @@ impl PyStr {
// https://docs.python.org/3/library/stdtypes.html#str.translate
#[pymethod]
fn translate(&self, table: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
vm.get_method_or_type_error(table.clone(), "__getitem__", || {
vm.get_method_or_type_error(table.clone(), identifier!(vm, __getitem__), || {
format!("'{}' object is not subscriptable", table.class().name())
})?;
+18 -16
View File
@@ -148,22 +148,24 @@ impl GetAttr for PySuper {
return skip(zelf, name);
}
// skip the classes in start_type.mro up to and including zelf.typ
let mro: Vec<_> = start_type
.iter_mro()
.skip_while(|cls| !cls.is(&zelf.typ))
.skip(1) // skip su->type (if any)
.collect();
for cls in mro {
if let Some(descr) = cls.get_direct_attr(name.as_str()) {
return vm
.call_get_descriptor_specific(
descr.clone(),
// Only pass 'obj' param if this is instance-mode super (See https://bugs.python.org/issue743267)
if obj.is(&start_type) { None } else { Some(obj) },
Some(start_type.as_object().to_owned()),
)
.unwrap_or(Ok(descr));
if let Some(name) = vm.ctx.interned_str(&*name) {
// skip the classes in start_type.mro up to and including zelf.typ
let mro: Vec<_> = start_type
.iter_mro()
.skip_while(|cls| !cls.is(&zelf.typ))
.skip(1) // skip su->type (if any)
.collect();
for cls in mro {
if let Some(descr) = cls.get_direct_attr(name) {
return vm
.call_get_descriptor_specific(
descr.clone(),
// Only pass 'obj' param if this is instance-mode super (See https://bugs.python.org/issue743267)
if obj.is(&start_type) { None } else { Some(obj) },
Some(start_type.as_object().to_owned()),
)
.unwrap_or(Ok(descr));
}
}
}
skip(zelf, name)
+83 -55
View File
@@ -1,6 +1,6 @@
use super::{
mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStaticMethod,
PyStr, PyStrRef, PyTuple, PyTupleRef, PyWeak,
PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
};
use crate::common::{
ascii,
@@ -8,8 +8,10 @@ use crate::common::{
lock::{PyRwLock, PyRwLockReadGuard},
};
use crate::{
builtins::PyBaseExceptionRef,
class::{PyClassImpl, StaticType},
function::{FuncArgs, KwArgs, OptionalArg},
identifier,
types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
@@ -35,7 +37,7 @@ pub type PyTypeRef = PyRef<PyType>;
/// For attributes we do not use a dict, but an IndexMap, which is an Hash Table
/// that maintains order and is compatible with the standard HashMap This is probably
/// faster and only supports strings as keys.
pub type PyAttributes = IndexMap<String, PyObjectRef, ahash::RandomState>;
pub type PyAttributes = IndexMap<&'static PyStrInterned, PyObjectRef, ahash::RandomState>;
impl fmt::Display for PyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -116,7 +118,7 @@ impl PyType {
);
for attr_name in new_type.attributes.read().keys() {
if attr_name.starts_with("__") && attr_name.ends_with("__") {
if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
new_type.update_slot(attr_name, true);
}
}
@@ -155,34 +157,40 @@ impl PyType {
}
// This is used for class initialisation where the vm is not yet available.
pub fn set_str_attr<V: Into<PyObjectRef>>(&self, attr_name: &str, value: V) {
self._set_str_attr(attr_name, value.into())
pub fn set_str_attr<V: Into<PyObjectRef>>(
&self,
attr_name: &str,
value: V,
ctx: impl AsRef<Context>,
) {
let attr_name = ctx.as_ref().intern_str(attr_name);
self.set_attr(attr_name, value.into())
}
fn _set_str_attr(&self, attr_name: &str, value: PyObjectRef) {
self.attributes.write().insert(attr_name.to_owned(), value);
pub fn set_attr(&self, attr_name: &'static PyStrInterned, value: PyObjectRef) {
self.attributes.write().insert(attr_name, value);
}
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn get_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
pub fn get_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
flame_guard!(format!("class_get_attr({:?})", attr_name));
self.get_direct_attr(attr_name)
.or_else(|| self.get_super_attr(attr_name))
}
pub fn get_direct_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
pub fn get_direct_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
self.attributes.read().get(attr_name).cloned()
}
pub fn get_super_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
pub fn get_super_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
self.mro
.iter()
.find_map(|class| class.attributes.read().get(attr_name).cloned())
}
// This is the internal has_attr implementation for fast lookup on a class.
pub fn has_attr(&self, attr_name: &str) -> bool {
pub fn has_attr(&self, attr_name: &'static PyStrInterned) -> bool {
self.attributes.read().contains_key(attr_name)
|| self
.mro
@@ -264,11 +272,11 @@ impl PyType {
}
#[pymethod(magic)]
fn dir(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyList {
fn dir(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyList {
let attributes: Vec<PyObjectRef> = zelf
.get_attributes()
.into_iter()
.map(|(k, _)| vm.ctx.new_str(k).into())
.map(|(k, _)| k.to_object())
.collect();
PyList::from(attributes)
}
@@ -330,7 +338,7 @@ impl PyType {
pub fn qualname(&self, vm: &VirtualMachine) -> PyObjectRef {
self.attributes
.read()
.get("__qualname__")
.get(identifier!(vm, __qualname__))
.cloned()
// We need to exclude this method from going into recursion:
.and_then(|found| {
@@ -347,7 +355,7 @@ impl PyType {
pub fn module(&self, vm: &VirtualMachine) -> PyObjectRef {
self.attributes
.read()
.get("__module__")
.get(identifier!(vm, __module__))
.cloned()
// We need to exclude this method from going into recursion:
.and_then(|found| {
@@ -361,10 +369,10 @@ impl PyType {
}
#[pyproperty(magic, setter)]
fn set_module(&self, value: PyObjectRef) {
fn set_module(&self, value: PyObjectRef, vm: &VirtualMachine) {
self.attributes
.write()
.insert("__module__".to_owned(), value);
.insert(identifier!(vm, __module__), value);
}
#[pyclassmethod(magic)]
@@ -441,7 +449,10 @@ impl PyType {
.iter()
.map(|obj| {
obj.clone().downcast::<PyType>().or_else(|obj| {
if vm.get_attribute_opt(obj, "__mro_entries__")?.is_some() {
if vm
.get_attribute_opt(obj, identifier!(vm, __mro_entries__))?
.is_some()
{
Err(vm.new_type_error(
"type() doesn't support MRO entry resolution; \
use types.new_class()"
@@ -471,51 +482,56 @@ impl PyType {
(metatype, base, bases)
};
let mut attributes = dict.to_attributes();
if let Some(f) = attributes.get_mut("__new__") {
let mut attributes = dict.to_attributes(vm);
if let Some(f) = attributes.get_mut(identifier!(vm, __new__)) {
if f.class().is(&vm.ctx.types.function_type) {
*f = PyStaticMethod::from(f.clone()).into_pyobject(vm);
}
}
if let Some(f) = attributes.get_mut("__init_subclass__") {
if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__)) {
if f.class().is(&vm.ctx.types.function_type) {
*f = PyClassMethod::from(f.clone()).into_pyobject(vm);
}
}
if let Some(f) = attributes.get_mut("__class_getitem__") {
if let Some(f) = attributes.get_mut(identifier!(vm, __class_getitem__)) {
if f.class().is(&vm.ctx.types.function_type) {
*f = PyClassMethod::from(f.clone()).into_pyobject(vm);
}
}
if let Some(current_frame) = vm.current_frame() {
let entry = attributes.entry("__module__".to_owned());
let entry = attributes.entry(identifier!(vm, __module__));
if matches!(entry, Entry::Vacant(_)) {
let module_name =
vm.unwrap_or_none(current_frame.globals.get_item_opt("__name__", vm)?);
let module_name = vm.unwrap_or_none(
current_frame
.globals
.get_item_opt(identifier!(vm, __name__), vm)?,
);
entry.or_insert(module_name);
}
}
attributes
.entry("__qualname__".to_owned())
.entry(identifier!(vm, __qualname__))
.or_insert_with(|| vm.ctx.new_str(name.as_str()).into());
// All *classes* should have a dict. Exceptions are *instances* of
// classes that define __slots__ and instances of built-in classes
// (with exceptions, e.g function)
attributes.entry("__dict__".to_owned()).or_insert_with(|| {
vm.ctx
.new_getset(
"__dict__",
vm.ctx.types.object_type.clone(),
subtype_get_dict,
subtype_set_dict,
)
.into()
});
attributes
.entry(identifier!(vm, __dict__))
.or_insert_with(|| {
vm.ctx
.new_getset(
"__dict__",
vm.ctx.types.object_type.clone(),
subtype_get_dict,
subtype_set_dict,
)
.into()
});
// TODO: Flags is currently initialized with HAS_DICT. Should be
// updated when __slots__ are supported (toggling the flag off if
@@ -532,13 +548,12 @@ impl PyType {
.read()
.iter()
.filter_map(|(name, obj)| {
vm.get_method(obj.clone(), "__set_name__").map(|res| {
res.map(|meth| (obj.clone(), PyStr::from(name.clone()).into_ref(vm), meth))
})
vm.get_method(obj.clone(), identifier!(vm, __set_name__))
.map(|res| res.map(|meth| (obj.clone(), name.to_owned(), meth)))
})
.collect::<PyResult<Vec<_>>>()?;
for (obj, name, set_name) in attributes {
vm.invoke(&set_name, (typ.clone(), name.clone()))
vm.invoke(&set_name, (typ.clone(), name.to_owned()))
.map_err(|e| {
let err = vm.new_runtime_error(format!(
"Error calling __set_name__ on '{}' instance {} in '{}'",
@@ -551,7 +566,7 @@ impl PyType {
})?;
}
if let Some(initter) = typ.get_super_attr("__init_subclass__") {
if let Some(initter) = typ.get_super_attr(identifier!(vm, __init_subclass__)) {
let initter = vm
.call_get_descriptor_specific(initter.clone(), None, Some(typ.clone().into()))
.unwrap_or(Ok(initter))?;
@@ -629,10 +644,26 @@ pub(crate) fn get_text_signature_from_internal_doc<'a>(
impl GetAttr for PyType {
fn getattro(zelf: &Py<Self>, name_str: PyStrRef, vm: &VirtualMachine) -> PyResult {
let name = name_str.as_str();
#[cold]
fn attribute_error(
zelf: &Py<PyType>,
name: &str,
vm: &VirtualMachine,
) -> PyBaseExceptionRef {
vm.new_attribute_error(format!(
"type object '{}' has no attribute '{}'",
zelf.slot_name(),
name,
))
}
let name = if let Some(name) = vm.ctx.interned_str(&*name_str) {
name
} else {
return Err(attribute_error(zelf, name_str.as_str(), vm));
};
vm_trace!("type.__getattribute__({:?}, {:?})", zelf, name);
let mcl = zelf.class();
let mcl_attr = mcl.get_attr(name);
if let Some(ref attr) = mcl_attr {
@@ -663,11 +694,7 @@ impl GetAttr for PyType {
drop(mcl);
vm.call_if_get_descriptor(attr, zelf.to_owned().into())
} else {
Err(vm.new_attribute_error(format!(
"type object '{}' has no attribute '{}'",
zelf.slot_name(),
name
)))
return Err(attribute_error(zelf, name_str.as_str(), vm));
}
}
}
@@ -679,7 +706,9 @@ impl SetAttr for PyType {
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
if let Some(attr) = zelf.get_class_attr(attr_name.as_str()) {
// TODO: pass PyRefExact instead of &str
let attr_name = vm.ctx.intern_str(attr_name.as_str());
if let Some(attr) = zelf.get_class_attr(attr_name) {
let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load());
if let Some(descriptor) = descr_set {
return descriptor(attr, zelf.to_owned().into(), value, vm);
@@ -689,18 +718,17 @@ impl SetAttr for PyType {
let mut attributes = zelf.attributes.write();
if let Some(value) = value {
attributes.insert(attr_name.as_str().to_owned(), value);
attributes.insert(attr_name, value);
} else {
let prev_value = attributes.remove(attr_name.as_str());
let prev_value = attributes.remove(attr_name);
if prev_value.is_none() {
return Err(vm.new_exception(
vm.ctx.exceptions.attribute_error.clone(),
vec![attr_name.into()],
vec![attr_name.to_object()],
));
}
}
let attr_name = attr_name.as_str();
if attr_name.starts_with("__") && attr_name.ends_with("__") {
if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
zelf.update_slot(attr_name, assign);
}
Ok(())
@@ -731,7 +759,7 @@ fn find_base_dict_descr(cls: &PyTypeRef, vm: &VirtualMachine) -> Option<PyObject
// TODO: should actually be some translation of:
// cls.slot_dictoffset != 0 && !cls.flags.contains(HEAPTYPE)
if cls.is(&vm.ctx.types.type_type) {
cls.get_attr("__dict__")
cls.get_attr(identifier!(vm, __dict__))
} else {
None
}
+12 -8
View File
@@ -46,16 +46,20 @@ impl PyUnion {
return Ok("None".to_string());
}
if vm.get_attribute_opt(obj.clone(), "__origin__")?.is_some()
&& vm.get_attribute_opt(obj.clone(), "__args__")?.is_some()
if vm
.get_attribute_opt(obj.clone(), identifier!(vm, __origin__))?
.is_some()
&& vm
.get_attribute_opt(obj.clone(), identifier!(vm, __args__))?
.is_some()
{
return Ok(obj.repr(vm)?.as_str().to_string());
}
match (
vm.get_attribute_opt(obj.clone(), "__qualname__")?
vm.get_attribute_opt(obj.clone(), identifier!(vm, __qualname__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|n| n.as_str().to_string())),
vm.get_attribute_opt(obj.clone(), "__module__")?
vm.get_attribute_opt(obj.clone(), identifier!(vm, __module__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|m| m.as_str().to_string())),
) {
(None, _) | (_, None) => Ok(obj.repr(vm)?.as_str().to_string()),
@@ -105,11 +109,11 @@ pub fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool {
|| obj.class().is(&vm.ctx.types.union_type)
}
fn is_typevar(obj: &PyObjectRef) -> bool {
fn is_typevar(obj: &PyObjectRef, vm: &VirtualMachine) -> bool {
let class = obj.class();
class.slot_name() == "TypeVar"
&& class
.get_attr("__module__")
.get_attr(identifier!(vm, __module__))
.and_then(|o| o.downcast_ref::<PyStr>().map(|s| s.as_str() == "typing"))
.unwrap_or(false)
}
@@ -117,13 +121,13 @@ fn is_typevar(obj: &PyObjectRef) -> bool {
fn make_parameters(args: &PyTupleRef, vm: &VirtualMachine) -> PyTupleRef {
let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len());
for arg in args {
if is_typevar(arg) {
if is_typevar(arg, vm) {
if !parameters.iter().any(|param| param.is(arg)) {
parameters.push(arg.clone());
}
} else if let Ok(subparams) = arg
.clone()
.get_attr("__parameters__", vm)
.get_attr(identifier!(vm, __parameters__), vm)
.and_then(|obj| PyTupleRef::try_from_object(vm, obj))
{
for subparam in &subparams {
+2 -1
View File
@@ -5,6 +5,7 @@ use crate::{
},
cformat::CFormatBytes,
function::{ArgIterable, Either, OptionalArg, OptionalOption, PyComparisonValue},
identifier,
protocol::PyBuffer,
sequence::{SequenceMutOp, SequenceOp},
types::PyComparisonOp,
@@ -86,7 +87,7 @@ impl ByteInnerNewOptions {
obj
};
if let Some(bytes_method) = vm.get_method(obj, "__bytes__") {
if let Some(bytes_method) = vm.get_method(obj, identifier!(vm, __bytes__)) {
// construct an exact bytes from __bytes__ slot.
// if __bytes__ return a bytes, use the bytes object except we are the subclass of the bytes
let bytes = vm.invoke(&bytes_method?, ())?;
+5 -5
View File
@@ -395,7 +395,7 @@ impl CFormatSpec {
Ok(buffer.contiguous_or_collect(|bytes| self.format_bytes(bytes)))
} else {
let bytes = vm
.get_special_method(obj, "__bytes__")?
.get_special_method(obj, identifier!(vm, __bytes__))?
.map_err(|obj| {
vm.new_type_error(format!(
"%b requires a bytes-like object, or an object that \
@@ -420,7 +420,7 @@ impl CFormatSpec {
.into_bytes())
}
obj => {
if let Some(method) = vm.get_method(obj.clone(), "__int__") {
if let Some(method) = vm.get_method(obj.clone(), identifier!(vm, __int__)) {
let result = vm.invoke(&method?, ())?;
if let Some(i) = result.payload::<PyInt>() {
return Ok(self.format_number(i.as_bigint()).into_bytes());
@@ -514,7 +514,7 @@ impl CFormatSpec {
Ok(self.format_number(&try_f64_to_bigint(f.to_f64(), vm)?))
}
obj => {
if let Some(method) = vm.get_method(obj.clone(), "__int__") {
if let Some(method) = vm.get_method(obj.clone(), identifier!(vm, __int__)) {
let result = vm.invoke(&method?, ())?;
if let Some(i) = result.payload::<PyInt>() {
return Ok(self.format_number(i.as_bigint()));
@@ -689,7 +689,7 @@ impl CFormatBytes {
let (num_specifiers, mapping_required) = check_specifiers(&self.parts, vm)?;
let mut result = vec![];
let is_mapping = values_obj.class().has_attr("__getitem__")
let is_mapping = values_obj.class().has_attr(identifier!(vm, __getitem__))
&& !values_obj.fast_isinstance(&vm.ctx.types.tuple_type)
&& !values_obj.fast_isinstance(&vm.ctx.types.bytes_type)
&& !values_obj.fast_isinstance(&vm.ctx.types.bytearray_type);
@@ -841,7 +841,7 @@ impl CFormatString {
let (num_specifiers, mapping_required) = check_specifiers(&self.parts, vm)?;
let mut result = String::new();
let is_mapping = values_obj.class().has_attr("__getitem__")
let is_mapping = values_obj.class().has_attr(identifier!(vm, __getitem__))
&& !values_obj.fast_isinstance(&vm.ctx.types.tuple_type)
&& !values_obj.fast_isinstance(&vm.ctx.types.str_type);
+12 -6
View File
@@ -2,6 +2,7 @@
use crate::{
builtins::{PyBaseObject, PyBoundMethod, PyType, PyTypeRef},
identifier,
object::{PyObjectPayload, PyObjectRef, PyRef},
types::{PyTypeFlags, PyTypeSlots},
vm::Context,
@@ -83,28 +84,33 @@ pub trait PyClassImpl: PyClassDef {
assert!(class.slots.flags.is_created_with_flags());
}
if Self::TP_FLAGS.has_feature(PyTypeFlags::HAS_DICT) {
class.set_str_attr(
"__dict__",
let __dict__ = identifier!(ctx, __dict__);
class.set_attr(
__dict__,
ctx.new_getset(
"__dict__",
class.clone(),
crate::builtins::object::object_get_dict,
crate::builtins::object::object_set_dict,
),
)
.into(),
);
}
Self::impl_extend_class(ctx, class);
if let Some(doc) = Self::DOC {
class.set_str_attr("__doc__", ctx.new_str(doc));
class.set_attr(identifier!(ctx, __doc__), ctx.new_str(doc).into());
}
if let Some(module_name) = Self::MODULE_NAME {
class.set_str_attr("__module__", ctx.new_str(module_name));
class.set_attr(
identifier!(ctx, __module__),
ctx.new_str(module_name).into(),
);
}
if class.slots.new.load().is_some() {
let bound: PyObjectRef =
PyBoundMethod::new_ref(class.clone().into(), ctx.slot_new_wrapper.clone(), ctx)
.into();
class.set_str_attr("__new__", bound);
class.set_attr(identifier!(ctx, __new__), bound);
}
}
+28 -3
View File
@@ -8,7 +8,7 @@ use crate::common::{
lock::{PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard},
};
use crate::{
builtins::{PyInt, PyStr, PyStrRef},
builtins::{PyInt, PyStr, PyStrInterned, PyStrRef},
convert::ToPyObject,
AsObject, Py, PyExact, PyObject, PyObjectRef, PyRefExact, PyResult, VirtualMachine,
};
@@ -751,9 +751,34 @@ impl DictKey for Py<PyStr> {
}
}
impl DictKey for PyStrInterned {
type Owned = PyRefExact<PyStr>;
#[inline]
fn _to_owned(&self, _vm: &VirtualMachine) -> Self::Owned {
let zelf: &'static PyStrInterned = unsafe { &*(self as *const _) };
zelf.to_exact()
}
#[inline]
fn key_hash(&self, vm: &VirtualMachine) -> PyResult<HashValue> {
(**self).key_hash(vm)
}
#[inline]
fn key_is(&self, other: &PyObject) -> bool {
(**self).key_is(other)
}
#[inline]
fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult<bool> {
(**self).key_eq(vm, other_key)
}
#[inline]
fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult<isize> {
(**self).key_as_isize(vm)
}
}
impl DictKey for PyExact<PyStr> {
type Owned = PyRefExact<PyStr>;
#[inline(always)]
#[inline]
fn _to_owned(&self, _vm: &VirtualMachine) -> Self::Owned {
self.to_owned()
}
@@ -761,7 +786,7 @@ impl DictKey for PyExact<PyStr> {
fn key_hash(&self, vm: &VirtualMachine) -> PyResult<HashValue> {
(**self).key_hash(vm)
}
#[inline]
#[inline(always)]
fn key_is(&self, other: &PyObject) -> bool {
(**self).key_is(other)
}
+2 -1
View File
@@ -765,7 +765,8 @@ impl ExceptionZoo {
});
// TODO: this isn't really accurate
#[cfg(windows)]
excs.os_error.set_str_attr("winerror", errno_getter.clone());
excs.os_error
.set_str_attr("winerror", errno_getter.clone(), ctx);
extend_exception!(PyBlockingIOError, ctx, &excs.blocking_io_error);
extend_exception!(PyChildProcessError, ctx, &excs.child_process_error);
+4 -2
View File
@@ -892,10 +892,12 @@ pub fn call_object_format(
Some(FormatPreconversor::Str) => argument.str(vm)?.into(),
Some(FormatPreconversor::Repr) => argument.repr(vm)?.into(),
Some(FormatPreconversor::Ascii) => vm.ctx.new_str(builtins::ascii(argument, vm)?).into(),
Some(FormatPreconversor::Bytes) => vm.call_method(&argument, "decode", ())?,
Some(FormatPreconversor::Bytes) => {
vm.call_method(&argument, identifier!(vm, decode).as_str(), ())?
}
None => argument,
};
let result = vm.call_special_method(argument, "__format__", (format_spec,))?;
let result = vm.call_special_method(argument, identifier!(vm, __format__), (format_spec,))?;
result.downcast().map_err(|result| {
vm.new_type_error(format!(
"__format__ must return a str, not {}",
+29 -22
View File
@@ -723,6 +723,7 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::YieldFrom => self.execute_yield_from(vm),
bytecode::Instruction::SetupAnnotation => {
let __annotations__ = identifier!(vm, __annotations__);
// Try using locals as dict first, if not, fallback to generic method.
let has_annotations = match self
.locals
@@ -730,15 +731,15 @@ impl ExecutingFrame<'_> {
.into_object()
.downcast_exact::<PyDict>(vm)
{
Ok(d) => d.contains_key("__annotations__", vm),
Ok(d) => d.contains_key(__annotations__, vm),
Err(o) => {
let needle = vm.new_pyobj("__annotations__");
let needle = __annotations__.to_object();
self._in(vm, needle, o)?
}
};
if !has_annotations {
self.locals.as_object().set_item(
"__annotations__",
__annotations__,
vm.ctx.new_dict().into(),
vm,
)?;
@@ -786,19 +787,20 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::SetupWith { end } => {
let context_manager = self.pop_value();
let exit = context_manager.get_attr("__exit__", vm)?;
let exit = context_manager.get_attr(identifier!(vm, __exit__), vm)?;
self.push_value(exit);
// Call enter:
let enter_res = vm.call_special_method(context_manager, "__enter__", ())?;
let enter_res =
vm.call_special_method(context_manager, identifier!(vm, __enter__), ())?;
self.push_block(BlockType::Finally { handler: *end });
self.push_value(enter_res);
Ok(None)
}
bytecode::Instruction::BeforeAsyncWith => {
let mgr = self.pop_value();
let aexit = mgr.get_attr("__aexit__", vm)?;
let aexit = mgr.get_attr(identifier!(vm, __aexit__), vm)?;
self.push_value(aexit);
let aenter_res = vm.call_special_method(mgr, "__aenter__", ())?;
let aenter_res = vm.call_special_method(mgr, identifier!(vm, __aenter__), ())?;
self.push_value(aenter_res);
Ok(None)
@@ -866,13 +868,16 @@ impl ExecutingFrame<'_> {
let awaitable = if awaited_obj.payload_is::<PyCoroutine>() {
awaited_obj
} else {
let await_method =
vm.get_method_or_type_error(awaited_obj.clone(), "__await__", || {
let await_method = vm.get_method_or_type_error(
awaited_obj.clone(),
identifier!(vm, __await__),
|| {
format!(
"object {} can't be used in 'await' expression",
awaited_obj.class().name(),
)
})?;
},
)?;
vm.invoke(&await_method, ())?
};
self.push_value(awaitable);
@@ -880,17 +885,17 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::GetAIter => {
let aiterable = self.pop_value();
let aiter = vm.call_special_method(aiterable, "__aiter__", ())?;
let aiter = vm.call_special_method(aiterable, identifier!(vm, __aiter__), ())?;
self.push_value(aiter);
Ok(None)
}
bytecode::Instruction::GetANext => {
let aiter = self.last_value();
let awaitable = vm.call_special_method(aiter, "__anext__", ())?;
let awaitable = vm.call_special_method(aiter, identifier!(vm, __anext__), ())?;
let awaitable = if awaitable.payload_is::<PyCoroutine>() {
awaitable
} else {
vm.call_special_method(awaitable, "__await__", ())?
vm.call_special_method(awaitable, identifier!(vm, __await__), ())?
};
self.push_value(awaitable);
Ok(None)
@@ -1009,7 +1014,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadBuildClass => {
self.push_value(vm.builtins.get_attr("__build_class__", vm)?);
self.push_value(vm.builtins.get_attr(identifier!(vm, __build_class__), vm)?);
Ok(None)
}
bytecode::Instruction::UnpackSequence { size } => {
@@ -1135,7 +1140,9 @@ impl ExecutingFrame<'_> {
return Ok(obj);
}
// fallback to importing '{module.__name__}.{name}' from sys.modules
let mod_name = module.get_attr("__name__", vm).map_err(|_| err())?;
let mod_name = module
.get_attr(identifier!(vm, __name__), vm)
.map_err(|_| err())?;
let mod_name = mod_name.downcast::<PyStr>().map_err(|_| err())?;
let full_mod_name = format!("{}.{}", mod_name, name);
let sys_modules = vm
@@ -1153,7 +1160,7 @@ impl ExecutingFrame<'_> {
// Grab all the names from the module and put them in the context
if let Some(dict) = module.dict() {
let filter_pred: Box<dyn Fn(&str) -> bool> =
if let Ok(all) = dict.get_item("__all__", vm) {
if let Ok(all) = dict.get_item(identifier!(vm, __all__), vm) {
let all: Vec<PyStrRef> = all.try_to_value(vm)?;
let all: Vec<String> = all
.into_iter()
@@ -1595,14 +1602,14 @@ impl ExecutingFrame<'_> {
)
.into_pyobject(vm);
func_obj.set_attr("__doc__", vm.ctx.none(), vm)?;
func_obj.set_attr(identifier!(vm, __doc__), vm.ctx.none(), vm)?;
let name = qualified_name.as_str().split('.').next_back().unwrap();
func_obj.set_attr("__name__", vm.new_pyobj(name), vm)?;
func_obj.set_attr("__qualname__", qualified_name, vm)?;
let module = vm.unwrap_or_none(self.globals.get_item_opt("__name__", vm)?);
func_obj.set_attr("__module__", module, vm)?;
func_obj.set_attr("__annotations__", annotations, vm)?;
func_obj.set_attr(identifier!(vm, __name__), vm.new_pyobj(name), vm)?;
func_obj.set_attr(identifier!(vm, __qualname__), qualified_name, vm)?;
let module = vm.unwrap_or_none(self.globals.get_item_opt(identifier!(vm, __name__), vm)?);
func_obj.set_attr(identifier!(vm, __module__), module, vm)?;
func_obj.set_attr(identifier!(vm, __annotations__), annotations, vm)?;
self.push_value(func_obj);
Ok(None)
+2 -1
View File
@@ -2,6 +2,7 @@ use super::IntoFuncArgs;
use crate::{
builtins::{iter::PySequenceIterator, PyDict, PyDictRef},
convert::ToPyObject,
identifier,
protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods},
AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
};
@@ -86,7 +87,7 @@ where
{
let cls = obj.class();
iterfn = cls.mro_find_map(|x| x.slots.iter.load());
if iterfn.is_none() && !cls.has_attr("__getitem__") {
if iterfn.is_none() && !cls.has_attr(identifier!(vm, __getitem__)) {
return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name())));
}
}
+9 -2
View File
@@ -30,7 +30,7 @@ macro_rules! py_class {
(@extract_slots($ctx:expr, $class:expr, $name:expr, $value:expr)) => {};
(@extract_attrs($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {};
(@extract_attrs($ctx:expr, $class:expr, $name:expr, $value:expr)) => {
$class.set_str_attr($name, $value);
$class.set_attr($name, $value);
};
}
@@ -38,7 +38,7 @@ macro_rules! py_class {
macro_rules! extend_class {
( $ctx:expr, $class:expr, { $($name:expr => $value:expr),* $(,)* }) => {
$(
$class.set_str_attr($name, $value);
$class.set_attr($ctx.intern_str($name), $value.into());
)*
};
}
@@ -177,6 +177,13 @@ macro_rules! match_class {
};
}
#[macro_export]
macro_rules! identifier(
($as_ctx:expr, $name:ident) => {
$as_ctx.as_ref().names.$name
};
);
/// Super detailed logging. Might soon overflow your logbuffers
/// Default, this logging is discarded, except when a the `vm-tracing-logging`
/// build feature is enabled.
+1 -1
View File
@@ -744,7 +744,7 @@ impl PyObject {
let ret = crate::vm::thread::with_vm(zelf, |vm| {
zelf.0.ref_count.inc();
if let Err(e) = slot_del(zelf, vm) {
let del_method = zelf.get_class_attr("__del__").unwrap();
let del_method = zelf.get_class_attr(identifier!(vm, __del__)).unwrap();
vm.run_unraisable(e, None, del_method);
}
zelf.0.ref_count.dec()
+2 -2
View File
@@ -4,7 +4,7 @@ use super::{
};
use crate::common::lock::PyRwLockReadGuard;
use crate::{
builtins::{PyBaseExceptionRef, PyType},
builtins::{PyBaseExceptionRef, PyStrInterned, PyType},
convert::{ToPyException, ToPyObject, ToPyResult, TryFromObject},
VirtualMachine,
};
@@ -251,7 +251,7 @@ where
self.as_object().lease_class()
}
fn get_class_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
fn get_class_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
self.class().get_attr(attr_name)
}
+11 -7
View File
@@ -1,7 +1,7 @@
use crate::{
builtins::{
dict::{PyDictItems, PyDictKeys, PyDictValues},
PyDict,
PyDict, PyStrInterned,
},
common::lock::OnceCell,
convert::ToPyResult,
@@ -135,7 +135,7 @@ impl PyMapping<'_> {
if let Some(dict) = self.obj.downcast_ref_if_exact::<PyDict>(vm) {
PyDictKeys::new(dict.to_owned()).to_pyresult(vm)
} else {
self.method_output_as_list("keys", vm)
self.method_output_as_list(identifier!(vm, keys), vm)
}
}
@@ -143,7 +143,7 @@ impl PyMapping<'_> {
if let Some(dict) = self.obj.downcast_ref_if_exact::<PyDict>(vm) {
PyDictValues::new(dict.to_owned()).to_pyresult(vm)
} else {
self.method_output_as_list("values", vm)
self.method_output_as_list(identifier!(vm, values), vm)
}
}
@@ -151,12 +151,16 @@ impl PyMapping<'_> {
if let Some(dict) = self.obj.downcast_ref_if_exact::<PyDict>(vm) {
PyDictItems::new(dict.to_owned()).to_pyresult(vm)
} else {
self.method_output_as_list("items", vm)
self.method_output_as_list(identifier!(vm, items), vm)
}
}
fn method_output_as_list(&self, method_name: &str, vm: &VirtualMachine) -> PyResult {
let meth_output = vm.call_method(self.obj, method_name, ())?;
fn method_output_as_list(
&self,
method_name: &'static PyStrInterned,
vm: &VirtualMachine,
) -> PyResult {
let meth_output = vm.call_method(self.obj, method_name.as_str(), ())?;
if meth_output.is(&vm.ctx.types.list_type) {
return Ok(meth_output);
}
@@ -165,7 +169,7 @@ impl PyMapping<'_> {
vm.new_type_error(format!(
"{}.{}() returned a non-iterable (type {})",
self.obj.class(),
method_name,
method_name.as_str(),
meth_output.class()
))
})?;
+37 -24
View File
@@ -143,8 +143,11 @@ impl PyObject {
vm: &VirtualMachine,
) -> PyResult<()> {
vm_trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value);
if let Some(attr) = self.get_class_attr(attr_name.as_str()) {
if let Some(attr) = vm
.ctx
.interned_str(&*attr_name)
.and_then(|attr_name| self.get_class_attr(attr_name))
{
let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load());
if let Some(descriptor) = descr_set {
return descriptor(attr, self.to_owned(), value, vm);
@@ -160,7 +163,7 @@ impl PyObject {
vm.new_attribute_error(format!(
"'{}' object has no attribute '{}'",
self.class().name(),
attr_name,
attr_name.as_str(),
))
} else {
e
@@ -172,7 +175,7 @@ impl PyObject {
Err(vm.new_attribute_error(format!(
"'{}' object has no attribute '{}'",
self.class().name(),
attr_name,
attr_name.as_str(),
)))
}
}
@@ -191,7 +194,8 @@ impl PyObject {
) -> PyResult<Option<PyObjectRef>> {
let name = name_str.as_str();
let obj_cls = self.class();
let cls_attr = match obj_cls.get_attr(name) {
let cls_attr_name = vm.ctx.interned_str(&*name_str);
let cls_attr = match cls_attr_name.and_then(|name| obj_cls.get_attr(name)) {
Some(descr) => {
let descr_cls = descr.class();
let descr_get = descr_cls.mro_find_map(|cls| cls.slots.descr_get.load());
@@ -229,7 +233,7 @@ impl PyObject {
}
None => Ok(Some(attr)),
}
} else if let Some(getter) = obj_cls.get_attr("__getattr__") {
} else if let Some(getter) = obj_cls.get_attr(identifier!(vm, __getattr__)) {
drop(obj_cls);
vm.invoke(&getter, (self.to_owned(), name_str)).map(Some)
} else {
@@ -310,7 +314,7 @@ impl PyObject {
pub fn repr(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
vm.with_recursion("while getting the repr of an object", || {
let repr = vm.call_special_method(self.to_owned(), "__repr__", ())?;
let repr = vm.call_special_method(self.to_owned(), identifier!(vm, __repr__), ())?;
repr.try_into_value(vm)
})
}
@@ -326,7 +330,7 @@ impl PyObject {
if self.class().is(&vm.ctx.types.str_type) {
Ok(self.to_owned().downcast().unwrap())
} else {
let s = vm.call_special_method(self.to_owned(), "__str__", ())?;
let s = vm.call_special_method(self.to_owned(), identifier!(vm, __str__), ())?;
s.try_into_value(vm)
}
}
@@ -337,14 +341,16 @@ impl PyObject {
where
F: Fn() -> String,
{
cls.to_owned().get_attr("__bases__", vm).map_err(|e| {
// Only mask AttributeErrors.
if e.class().is(&vm.ctx.exceptions.attribute_error) {
vm.new_type_error(msg())
} else {
e
}
})
cls.to_owned()
.get_attr(identifier!(vm, __bases__), vm)
.map_err(|e| {
// Only mask AttributeErrors.
if e.class().is(&vm.ctx.exceptions.attribute_error) {
vm.new_type_error(msg())
} else {
e
}
})
}
fn abstract_issubclass(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
@@ -355,7 +361,9 @@ impl PyObject {
return Ok(true);
}
let bases = derived.to_owned().get_attr("__bases__", vm)?;
let bases = derived
.to_owned()
.get_attr(identifier!(vm, __bases__), vm)?;
let tuple = PyTupleRef::try_from_object(vm, bases)?;
let n = tuple.len();
@@ -420,7 +428,9 @@ impl PyObject {
return Ok(false);
}
if let Ok(meth) = vm.get_special_method(cls.to_owned(), "__subclasscheck__")? {
if let Ok(meth) =
vm.get_special_method(cls.to_owned(), identifier!(vm, __subclasscheck__))?
{
let ret = vm.with_recursion("in __subclasscheck__", || {
meth.invoke((self.to_owned(),), vm)
})?;
@@ -434,9 +444,10 @@ impl PyObject {
if let Ok(typ) = PyTypeRef::try_from_object(vm, cls.to_owned()) {
if self.class().fast_issubclass(&typ) {
Ok(true)
} else if let Ok(icls) =
PyTypeRef::try_from_object(vm, self.to_owned().get_attr("__class__", vm)?)
{
} else if let Ok(icls) = PyTypeRef::try_from_object(
vm,
self.to_owned().get_attr(identifier!(vm, __class__), vm)?,
) {
if icls.is(&self.class()) {
Ok(false)
} else {
@@ -453,7 +464,7 @@ impl PyObject {
)
})
.and_then(|_| {
let icls: PyObjectRef = self.to_owned().get_attr("__class__", vm)?;
let icls: PyObjectRef = self.to_owned().get_attr(identifier!(vm, __class__), vm)?;
if vm.is_none(&icls) {
Ok(false)
} else {
@@ -485,7 +496,9 @@ impl PyObject {
return Ok(false);
}
if let Ok(meth) = vm.get_special_method(cls.to_owned(), "__instancecheck__")? {
if let Ok(meth) =
vm.get_special_method(cls.to_owned(), identifier!(vm, __instancecheck__))?
{
let ret = vm.with_recursion("in __instancecheck__", || {
meth.invoke((self.to_owned(),), vm)
})?;
@@ -538,7 +551,7 @@ impl PyObject {
}
if let Some(class_getitem) =
vm.get_attribute_opt(self.to_owned(), "__class_getitem__")?
vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class_getitem__))?
{
return vm.invoke(&class_getitem, (needle,));
}
+540 -424
View File
File diff suppressed because it is too large. Load diff
+26 -18
View File
@@ -218,9 +218,9 @@ mod builtins {
fn make_scope(self, vm: &VirtualMachine) -> PyResult<Scope> {
let (globals, locals) = match self.globals {
Some(globals) => {
if !globals.contains_key("__builtins__", vm) {
if !globals.contains_key(identifier!(vm, __builtins__), vm) {
let builtins_dict = vm.builtins.dict().into();
globals.set_item("__builtins__", builtins_dict, vm)?;
globals.set_item(identifier!(vm, __builtins__), builtins_dict, vm)?;
}
(
globals.clone(),
@@ -424,7 +424,7 @@ mod builtins {
#[pyfunction]
fn aiter(iter_target: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if iter_target.payload_is::<PyAsyncGen>() {
vm.call_special_method(iter_target, "__aiter__", ())
vm.call_special_method(iter_target, identifier!(vm, __aiter__), ())
} else {
Err(vm.new_type_error("wrong argument type".to_owned()))
}
@@ -591,14 +591,18 @@ mod builtins {
modulus,
} = args;
match modulus {
None => vm.call_or_reflection(&x, &y, "__pow__", "__rpow__", |vm, x, y| {
Err(vm.new_unsupported_binop_error(x, y, "pow"))
}),
None => vm.call_or_reflection(
&x,
&y,
identifier!(vm, __pow__),
identifier!(vm, __rpow__),
|vm, x, y| Err(vm.new_unsupported_binop_error(x, y, "pow")),
),
Some(z) => {
let try_pow_value = |obj: &PyObject,
args: (PyObjectRef, PyObjectRef, PyObjectRef)|
-> Option<PyResult> {
let method = obj.get_class_attr("__pow__")?;
let method = obj.get_class_attr(identifier!(vm, __pow__))?;
let result = match vm.invoke(&method, args) {
Ok(x) => x,
Err(e) => return Some(Err(e)),
@@ -685,10 +689,10 @@ mod builtins {
#[pyfunction]
fn reversed(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(reversed_method) = vm.get_method(obj.clone(), "__reversed__") {
if let Some(reversed_method) = vm.get_method(obj.clone(), identifier!(vm, __reversed__)) {
vm.invoke(&reversed_method?, ())
} else {
vm.get_method_or_type_error(obj.clone(), "__getitem__", || {
vm.get_method_or_type_error(obj.clone(), identifier!(vm, __getitem__), || {
"argument to reversed() must be a sequence".to_owned()
})?;
let len = obj.length(vm)?;
@@ -707,7 +711,7 @@ mod builtins {
#[pyfunction]
fn round(RoundArgs { number, ndigits }: RoundArgs, vm: &VirtualMachine) -> PyResult {
let meth = vm
.get_special_method(number, "__round__")?
.get_special_method(number, identifier!(vm, __round__))?
.map_err(|number| {
vm.new_type_error(format!(
"type {} doesn't define __round__",
@@ -792,9 +796,10 @@ mod builtins {
#[pyfunction]
fn vars(obj: OptionalArg, vm: &VirtualMachine) -> PyResult {
if let OptionalArg::Present(obj) = obj {
obj.get_attr("__dict__", vm).map_err(|_| {
vm.new_type_error("vars() argument must have __dict__ attribute".to_owned())
})
obj.get_attr(identifier!(vm, __dict__).to_owned(), vm)
.map_err(|_| {
vm.new_type_error("vars() argument must have __dict__ attribute".to_owned())
})
} else {
Ok(vm.current_locals()?.into())
}
@@ -821,7 +826,8 @@ mod builtins {
}
continue;
}
let mro_entries = vm.get_attribute_opt(base.clone(), "__mro_entries__")?;
let mro_entries =
vm.get_attribute_opt(base.clone(), identifier!(vm, __mro_entries__))?;
let entries = match mro_entries {
Some(meth) => vm.invoke(&meth, (bases.clone(),))?,
None => {
@@ -874,7 +880,7 @@ mod builtins {
// Prepare uses full __getattribute__ resolution chain.
let namespace = vm
.get_attribute_opt(metaclass.clone(), "__prepare__")?
.get_attribute_opt(metaclass.clone(), identifier!(vm, __prepare__))?
.map_or(Ok(vm.ctx.new_dict().into()), |prepare| {
vm.invoke(
&prepare,
@@ -895,9 +901,11 @@ mod builtins {
let classcell = <Option<PyCellRef>>::try_from_object(vm, classcell)?;
if let Some(orig_bases) = orig_bases {
namespace
.as_object()
.set_item("__orig_bases__", orig_bases.into(), vm)?;
namespace.as_object().set_item(
identifier!(vm, __orig_bases__),
orig_bases.into(),
vm,
)?;
}
let class = vm.invoke(
+1 -1
View File
@@ -1192,7 +1192,7 @@ mod _io {
}
let readall = vm
.get_method(self.raw.clone().unwrap(), "readall")
.get_str_method(self.raw.clone().unwrap(), "readall")
.transpose()?;
if let Some(readall) = readall {
let res = vm.invoke(&readall, ())?;
+9 -4
View File
@@ -10,6 +10,7 @@ mod decl {
builtins::{int, PyGenericAlias, PyInt, PyIntRef, PyTuple, PyTupleRef, PyTypeRef},
convert::ToPyObject,
function::{ArgCallable, FuncArgs, OptionalArg, OptionalOption, PosArgs},
identifier,
protocol::{PyIter, PyIterReturn},
stdlib::sys,
types::{Constructor, IterNext, IterNextIterable},
@@ -1019,15 +1020,19 @@ mod decl {
) -> PyResult {
let n = n.unwrap_or(2);
let copyable = if iterable.class().has_attr("__copy__") {
vm.call_method(&iterable, "__copy__", ())?
let copyable = if iterable.class().has_attr(identifier!(vm, __copy__)) {
vm.call_special_method(iterable.into(), identifier!(vm, __copy__), ())?
} else {
PyItertoolsTee::from_iter(iterable, vm)?
};
let mut tee_vec: Vec<PyObjectRef> = Vec::with_capacity(n);
for _ in 0..n {
tee_vec.push(vm.call_method(&copyable, "__copy__", ())?);
tee_vec.push(vm.call_special_method(
copyable.clone(),
identifier!(vm, __copy__),
(),
)?);
}
Ok(PyTuple::new_ref(tee_vec, &vm.ctx).into())
@@ -1039,7 +1044,7 @@ mod decl {
fn from_iter(iterator: PyIter, vm: &VirtualMachine) -> PyResult {
let class = PyItertoolsTee::class(vm);
if iterator.class().is(PyItertoolsTee::class(vm)) {
return vm.call_method(&iterator, "__copy__", ());
return vm.call_special_method(iterator.into(), identifier!(vm, __copy__), ());
}
Ok(PyItertoolsTee {
tee_data: PyItertoolsTeeData::new(iterator, vm)?,
+7 -2
View File
@@ -14,6 +14,7 @@ mod _operator {
builtins::{PyInt, PyIntRef, PyStrRef, PyTupleRef, PyTypeRef},
function::Either,
function::{ArgBytesLike, FuncArgs, KwArgs, OptionalArg},
identifier,
protocol::PyIter,
recursion::ReprGuard,
types::{Callable, Constructor, PyComparisonOp},
@@ -196,7 +197,9 @@ mod _operator {
#[pyfunction]
fn concat(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// Best attempt at checking that a is sequence-like.
if !a.class().has_attr("__getitem__") || a.fast_isinstance(&vm.ctx.types.dict_type) {
if !a.class().has_attr(identifier!(vm, __getitem__))
|| a.fast_isinstance(&vm.ctx.types.dict_type)
{
return Err(
vm.new_type_error(format!("{} object can't be concatenated", a.class().name()))
);
@@ -299,7 +302,9 @@ mod _operator {
#[pyfunction]
fn iconcat(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// Best attempt at checking that a is sequence-like.
if !a.class().has_attr("__getitem__") || a.fast_isinstance(&vm.ctx.types.dict_type) {
if !a.class().has_attr(identifier!(vm, __getitem__))
|| a.fast_isinstance(&vm.ctx.types.dict_type)
{
return Err(
vm.new_type_error(format!("{} object can't be concatenated", a.class().name()))
);
+8 -6
View File
@@ -3,6 +3,7 @@ use crate::{
common::crt_fd::Fd,
convert::{ToPyException, ToPyObject},
function::{ArgumentError, FromArgs, FuncArgs},
identifier,
protocol::PyBuffer,
AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, TryFromObject,
VirtualMachine,
@@ -137,12 +138,13 @@ impl FsPath {
Ok(pathlike) => return Ok(pathlike),
Err(obj) => obj,
};
let method = vm.get_method_or_type_error(obj.clone(), "__fspath__", || {
format!(
"should be string, bytes, os.PathLike or integer, not {}",
obj.class().name()
)
})?;
let method =
vm.get_method_or_type_error(obj.clone(), identifier!(vm, __fspath__), || {
format!(
"should be string, bytes, os.PathLike or integer, not {}",
obj.class().name()
)
})?;
let result = vm.invoke(&method, ())?;
match1(result)?.map_err(|result| {
vm.new_type_error(format!(
+1 -1
View File
@@ -282,7 +282,7 @@ mod _sre {
must_advance: AtomicCell::new(false),
}
.into_ref(vm);
let search = vm.get_method(scanner.into(), "search").unwrap()?;
let search = vm.get_str_method(scanner.into(), "search").unwrap()?;
let search = ArgCallable::try_from_object(vm, search)?;
let iterator = PyCallableIterator::new(search, vm.ctx.none());
Ok(iterator)
+2 -1
View File
@@ -245,8 +245,9 @@ pub(crate) mod _thread {
let args = FuncArgs::new(
args.to_vec(),
kwargs
.map_or_else(Default::default, |k| k.to_attributes())
.map_or_else(Default::default, |k| k.to_attributes(vm))
.into_iter()
.map(|(k, v)| (k.as_str().to_owned(), v))
.collect::<KwArgs>(),
);
let mut thread_builder = thread::Builder::new();
+75 -49
View File
@@ -1,13 +1,15 @@
use crate::common::{hash::PyHash, lock::PyRwLock};
use crate::{
builtins::{PyInt, PyStrRef, PyType, PyTypeRef},
builtins::{PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
bytecode::ComparisonOperator,
convert::{ToPyObject, ToPyResult},
function::Either,
function::{FromArgs, FuncArgs, OptionalArg, PyComparisonValue},
identifier,
protocol::{
PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PySequence, PySequenceMethods,
},
vm::Context,
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
@@ -173,7 +175,7 @@ macro_rules! then_some_closure {
}
fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
let ret = vm.call_special_method(obj, "__len__", ())?;
let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?;
let len = ret.payload::<PyInt>().ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
@@ -190,31 +192,37 @@ fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Ok(len as usize)
}
fn as_mapping_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods {
fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods {
PyMappingMethods {
length: then_some_closure!(zelf.class().has_attr("__len__"), |mapping, vm| {
length_wrapper(mapping.obj.to_owned(), vm)
}),
length: then_some_closure!(
zelf.class().has_attr(identifier!(vm, __len__)),
|mapping, vm| { length_wrapper(mapping.obj.to_owned(), vm) }
),
subscript: then_some_closure!(
zelf.class().has_attr("__getitem__"),
zelf.class().has_attr(identifier!(vm, __getitem__)),
|mapping, needle, vm| {
vm.call_special_method(mapping.obj.to_owned(), "__getitem__", (needle.to_owned(),))
vm.call_special_method(
mapping.obj.to_owned(),
identifier!(vm, __getitem__),
(needle.to_owned(),),
)
}
),
ass_subscript: then_some_closure!(
zelf.class().has_attr("__setitem__") | zelf.class().has_attr("__delitem__"),
zelf.class().has_attr(identifier!(vm, __setitem__))
| zelf.class().has_attr(identifier!(vm, __delitem__)),
|mapping, needle, value, vm| match value {
Some(value) => vm
.call_special_method(
mapping.obj.to_owned(),
"__setitem__",
identifier!(vm, __setitem__),
(needle.to_owned(), value),
)
.map(|_| Ok(()))?,
None => vm
.call_special_method(
mapping.obj.to_owned(),
"__delitem__",
identifier!(vm, __delitem__),
(needle.to_owned(),)
)
.map(|_| Ok(()))?,
@@ -223,30 +231,40 @@ fn as_mapping_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods
}
}
fn as_sequence_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
if !zelf.class().has_attr("__getitem__") {
fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
if !zelf.class().has_attr(identifier!(vm, __getitem__)) {
return Cow::Borrowed(PySequenceMethods::not_implemented());
}
Cow::Owned(PySequenceMethods {
length: then_some_closure!(zelf.class().has_attr("__len__"), |seq, vm| {
length_wrapper(seq.obj.to_owned(), vm)
}),
length: then_some_closure!(
zelf.class().has_attr(identifier!(vm, __len__)),
|seq, vm| { length_wrapper(seq.obj.to_owned(), vm) }
),
item: Some(|seq, i, vm| {
vm.call_special_method(seq.obj.to_owned(), "__getitem__", (i.to_pyobject(vm),))
vm.call_special_method(
seq.obj.to_owned(),
identifier!(vm, __getitem__),
(i.to_pyobject(vm),),
)
}),
ass_item: then_some_closure!(
zelf.class().has_attr("__setitem__") | zelf.class().has_attr("__delitem__"),
zelf.class().has_attr(identifier!(vm, __setitem__))
| zelf.class().has_attr(identifier!(vm, __delitem__)),
|seq, i, value, vm| match value {
Some(value) => vm
.call_special_method(
seq.obj.to_owned(),
"__setitem__",
identifier!(vm, __setitem__),
(i.to_pyobject(vm), value),
)
.map(|_| Ok(()))?,
None => vm
.call_special_method(seq.obj.to_owned(), "__delitem__", (i.to_pyobject(vm),))
.call_special_method(
seq.obj.to_owned(),
identifier!(vm, __delitem__),
(i.to_pyobject(vm),)
)
.map(|_| Ok(()))?,
}
),
@@ -255,7 +273,7 @@ fn as_sequence_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, Py
}
fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
let hash_obj = vm.call_special_method(zelf.to_owned(), "__hash__", ())?;
let hash_obj = vm.call_special_method(zelf.to_owned(), identifier!(vm, __hash__), ())?;
match hash_obj.payload_if_subclass::<PyInt>(vm) {
Some(py_int) => Ok(rustpython_common::hash::hash_bigint(py_int.as_bigint())),
None => Err(vm.new_type_error("__hash__ method should return an integer".to_owned())),
@@ -263,11 +281,11 @@ fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
}
fn call_wrapper(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(zelf.to_owned(), "__call__", args)
vm.call_special_method(zelf.to_owned(), identifier!(vm, __call__), args)
}
fn getattro_wrapper(zelf: &PyObject, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(zelf.to_owned(), "__getattribute__", (name,))
vm.call_special_method(zelf.to_owned(), identifier!(vm, __getattribute__), (name,))
}
fn setattro_wrapper(
@@ -279,10 +297,10 @@ fn setattro_wrapper(
let zelf = zelf.to_owned();
match value {
Some(value) => {
vm.call_special_method(zelf, "__setattr__", (name, value))?;
vm.call_special_method(zelf, identifier!(vm, __setattr__), (name, value))?;
}
None => {
vm.call_special_method(zelf, "__delattr__", (name,))?;
vm.call_special_method(zelf, identifier!(vm, __delattr__), (name,))?;
}
};
Ok(())
@@ -294,16 +312,23 @@ pub(crate) fn richcompare_wrapper(
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
vm.call_special_method(zelf.to_owned(), op.method_name(), (other.to_owned(),))
.map(Either::A)
vm.call_special_method(
zelf.to_owned(),
op.method_name(&vm.ctx),
(other.to_owned(),),
)
.map(Either::A)
}
fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
vm.call_special_method(zelf, "__iter__", ())
vm.call_special_method(zelf, identifier!(vm, __iter__), ())
}
fn iternext_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
PyIterReturn::from_pyresult(vm.call_special_method(zelf.to_owned(), "__next__", ()), vm)
PyIterReturn::from_pyresult(
vm.call_special_method(zelf.to_owned(), identifier!(vm, __next__), ()),
vm,
)
}
fn descr_get_wrapper(
@@ -312,7 +337,7 @@ fn descr_get_wrapper(
cls: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
vm.call_special_method(zelf, "__get__", (obj, cls))
vm.call_special_method(zelf, identifier!(vm, __get__), (obj, cls))
}
fn descr_set_wrapper(
@@ -322,14 +347,14 @@ fn descr_set_wrapper(
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
Some(val) => vm.call_special_method(zelf, "__set__", (obj, val)),
None => vm.call_special_method(zelf, "__delete__", (obj,)),
Some(val) => vm.call_special_method(zelf, identifier!(vm, __set__), (obj, val)),
None => vm.call_special_method(zelf, identifier!(vm, __delete__), (obj,)),
}
.map(drop)
}
fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let res = vm.call_special_method(obj, "__init__", args)?;
let res = vm.call_special_method(obj, identifier!(vm, __init__), args)?;
if !vm.is_none(&res) {
return Err(vm.new_type_error("__init__ must return None".to_owned()));
}
@@ -337,29 +362,27 @@ fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResu
}
fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let new = vm
.get_attribute_opt(cls.as_object().to_owned(), "__new__")?
.unwrap();
let new = cls.get_attr(identifier!(vm, __new__)).unwrap();
args.prepend_arg(cls.into());
vm.invoke(&new, args)
}
fn del_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
vm.call_special_method(zelf.to_owned(), "__del__", ())?;
vm.call_special_method(zelf.to_owned(), identifier!(vm, __del__), ())?;
Ok(())
}
impl PyType {
pub(crate) fn update_slot(&self, name: &str, add: bool) {
debug_assert!(name.starts_with("__"));
debug_assert!(name.ends_with("__"));
pub(crate) fn update_slot(&self, name: &'static PyStrInterned, add: bool) {
debug_assert!(name.as_str().starts_with("__"));
debug_assert!(name.as_str().ends_with("__"));
macro_rules! update_slot {
($name:ident, $func:expr) => {{
self.slots.$name.store(if add { Some($func) } else { None });
}};
}
match name {
match name.as_str() {
"__len__" | "__getitem__" | "__setitem__" | "__delitem__" => {
update_slot!(as_mapping, as_mapping_wrapper);
update_slot!(as_sequence, as_sequence_wrapper);
@@ -623,7 +646,10 @@ pub trait Comparable: PyPayload {
if let Some(zelf) = zelf.downcast_ref() {
Self::cmp(zelf, other, op, vm).map(Either::B)
} else {
Err(vm.new_type_error(format!("unexpected payload for {}", op.method_name())))
Err(vm.new_type_error(format!(
"unexpected payload for {}",
op.method_name(&vm.ctx).as_str()
)))
}
}
@@ -741,14 +767,14 @@ impl PyComparisonOp {
}
}
pub fn method_name(self) -> &'static str {
pub fn method_name(self, ctx: &Context) -> &'static PyStrInterned {
match self {
Self::Lt => "__lt__",
Self::Le => "__le__",
Self::Eq => "__eq__",
Self::Ne => "__ne__",
Self::Ge => "__ge__",
Self::Gt => "__gt__",
Self::Lt => identifier!(ctx, __lt__),
Self::Le => identifier!(ctx, __le__),
Self::Eq => identifier!(ctx, __eq__),
Self::Ne => identifier!(ctx, __ne__),
Self::Ge => identifier!(ctx, __ge__),
Self::Gt => identifier!(ctx, __gt__),
}
}
+4 -3
View File
@@ -78,11 +78,12 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static {
// cast i to a u8 so there's less to store in the getter closure.
// Hopefully there's not struct sequences with >=256 elements :P
let i = i as u8;
class.set_str_attr(
name,
class.set_attr(
ctx.intern_str(name),
ctx.new_readonly_getset(name, class.clone(), move |zelf: &PyTuple| {
zelf.fast_getitem(i.into())
}),
})
.into(),
);
}
}
+181 -12
View File
@@ -32,15 +32,181 @@ pub struct Context {
pub ellipsis: PyRef<PyEllipsis>,
pub not_implemented: PyRef<PyNotImplemented>,
pub(crate) true_str: &'static PyStrInterned,
pub(crate) false_str: &'static PyStrInterned,
pub types: TypeZoo,
pub exceptions: exceptions::ExceptionZoo,
pub int_cache_pool: Vec<PyIntRef>,
// there should only be exact objects of str in here, no non-strs and no subclasses
pub(crate) string_pool: StringPool,
pub(crate) slot_new_wrapper: PyObjectRef,
pub names: ConstName,
}
macro_rules! declare_const_name {
($($name:ident,)*) => {
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
pub struct ConstName {
$(pub $name: &'static PyStrInterned,)*
}
impl ConstName {
unsafe fn new(pool: &StringPool, typ: &PyTypeRef) -> Self {
Self {
$($name: pool.intern(stringify!($name), typ.clone()),)*
}
}
}
}
}
declare_const_name! {
True,
False,
// magic methods
__abs__,
__abstractmethods__,
__add__,
__aenter__,
__aexit__,
__aiter__,
__all__,
__and__,
__anext__,
__annotations__,
__args__,
__await__,
__bases__,
__bool__,
__build_class__,
__builtins__,
__bytes__,
__call__,
__ceil__,
__cformat__,
__class__,
__class_getitem__,
__complex__,
__contains__,
__copy__,
__deepcopy__,
__del__,
__delattr__,
__delete__,
__delitem__,
__dict__,
__dir__,
__div__,
__divmod__,
__doc__,
__enter__,
__eq__,
__exit__,
__file__,
__float__,
__floor__,
__floordiv__,
__format__,
__fspath__,
__ge__,
__get__,
__getattr__,
__getattribute__,
__getitem__,
__gt__,
__hash__,
__iadd__,
__iand__,
__idiv__,
__ifloordiv__,
__ilshift__,
__imatmul__,
__imod__,
__import__,
__imul__,
__index__,
__init__,
__init_subclass__,
__instancecheck__,
__int__,
__invert__,
__ior__,
__ipow__,
__irshift__,
__isub__,
__iter__,
__itruediv__,
__ixor__,
__le__,
__len__,
__length_hint__,
__lshift__,
__lt__,
__main__,
__matmul__,
__missing__,
__mod__,
__module__,
__mro_entries__,
__mul__,
__name__,
__ne__,
__neg__,
__new__,
__next__,
__or__,
__orig_bases__,
__orig_class__,
__origin__,
__parameters__,
__pos__,
__pow__,
__prepare__,
__qualname__,
__radd__,
__rand__,
__rdiv__,
__rdivmod__,
__reduce__,
__reduce_ex__,
__repr__,
__reversed__,
__rfloordiv__,
__rlshift__,
__rmatmul__,
__rmod__,
__rmul__,
__ror__,
__round__,
__rpow__,
__rrshift__,
__rshift__,
__rsub__,
__rtruediv__,
__rxor__,
__set__,
__set_name__,
__setattr__,
__setitem__,
__str__,
__sub__,
__subclasscheck__,
__truediv__,
__trunc__,
__xor__,
// common names
_attributes,
_fields,
decode,
encode,
keys,
items,
values,
update,
copy,
flush,
close,
}
// Basic objects:
@@ -77,16 +243,15 @@ impl Context {
PyRef::new_ref(PyFrozenSet::default(), types.frozenset_type.clone(), None);
let string_pool = StringPool::default();
let names = unsafe { ConstName::new(&string_pool, &types.str_type) };
let new_str = unsafe { string_pool.intern("__new__", types.str_type.clone()) };
let slot_new_wrapper = create_object(
PyNativeFuncDef::new(PyType::__new__.into_func(), new_str.to_owned()).into_function(),
PyNativeFuncDef::new(PyType::__new__.into_func(), names.__new__.to_owned())
.into_function(),
&types.builtin_function_or_method_type,
)
.into();
let true_str = unsafe { string_pool.intern("True", types.str_type.clone()) };
let false_str = unsafe { string_pool.intern("False", types.str_type.clone()) };
let empty_str = unsafe { string_pool.intern("", types.str_type.clone()) }.to_owned();
let context = Context {
@@ -100,14 +265,12 @@ impl Context {
ellipsis,
not_implemented,
true_str,
false_str,
types,
exceptions,
int_cache_pool,
string_pool,
slot_new_wrapper,
names,
};
TypeZoo::extend(&context);
exceptions::ExceptionZoo::extend(&context);
@@ -219,7 +382,7 @@ impl Context {
) -> PyTypeRef {
let mut attrs = PyAttributes::default();
if let Some(module) = module {
attrs.insert("__module__".to_string(), self.new_str(module).into());
attrs.insert(identifier!(self, __module__), self.new_str(module).into());
};
PyType::new_ref(
name,
@@ -243,7 +406,7 @@ impl Context {
vec![self.exceptions.exception_type.clone()]
};
let mut attrs = PyAttributes::default();
attrs.insert("__module__".to_owned(), self.new_str(module).into());
attrs.insert(identifier!(self, __module__), self.new_str(module).into());
PyType::new_ref(
name,
@@ -339,3 +502,9 @@ impl Default for Context {
CONTEXT.get_or_init(Self::init).clone()
}
}
impl AsRef<Context> for Context {
fn as_ref(&self) -> &Self {
self
}
}
+2 -2
View File
@@ -82,10 +82,10 @@ impl Interpreter {
fn flush_std(vm: &VirtualMachine) {
if let Ok(stdout) = sys::get_stdout(vm) {
let _ = vm.call_method(&stdout, "flush", ());
let _ = vm.call_method(&stdout, identifier!(vm, flush).as_str(), ());
}
if let Ok(stderr) = sys::get_stderr(vm) {
let _ = vm.call_method(&stderr, "flush", ());
let _ = vm.call_method(&stderr, identifier!(vm, flush).as_str(), ());
}
}
+5 -4
View File
@@ -3,7 +3,7 @@
use super::VirtualMachine;
use crate::{
builtins::{PyBaseObject, PyStrRef},
builtins::{PyBaseObject, PyStrInterned, PyStrRef},
function::IntoFuncArgs,
object::{AsObject, PyObjectRef, PyResult},
types::PyTypeFlags,
@@ -27,9 +27,10 @@ impl PyMethod {
return obj.get_attr(name, vm).map(Self::Attribute);
}
let interned_name = vm.ctx.interned_str(&*name);
let mut is_method = false;
let cls_attr = match cls.get_attr(name.as_str()) {
let cls_attr = match interned_name.and_then(|name| cls.get_attr(name)) {
Some(descr) => {
let descr_cls = descr.class();
let descr_get = if descr_cls.slots.flags.has_feature(PyTypeFlags::METHOD_DESCR) {
@@ -76,7 +77,7 @@ impl PyMethod {
}
None => Ok(Self::Attribute(attr)),
}
} else if let Some(getter) = cls.get_attr("__getattr__") {
} else if let Some(getter) = cls.get_attr(identifier!(vm, __getattr__)) {
drop(cls);
vm.invoke(&getter, (obj, name)).map(Self::Attribute)
} else {
@@ -92,7 +93,7 @@ impl PyMethod {
pub(crate) fn get_special(
obj: PyObjectRef,
name: &str,
name: &'static PyStrInterned,
vm: &VirtualMachine,
) -> PyResult<Result<Self, PyObjectRef>> {
let obj_cls = obj.class();
+34 -14
View File
@@ -19,7 +19,7 @@ use crate::{
code::PyCode,
pystr::IntoPyStrRef,
tuple::{PyTuple, PyTupleTyped},
PyBaseExceptionRef, PyDictRef, PyInt, PyList, PyModule, PyStrRef, PyTypeRef,
PyBaseExceptionRef, PyDictRef, PyInt, PyList, PyModule, PyStrInterned, PyStrRef, PyTypeRef,
},
bytecode,
codecs::CodecsRegistry,
@@ -425,13 +425,13 @@ impl VirtualMachine {
}
}
None => {
let import_func =
self.builtins
.clone()
.get_attr("__import__", self)
.map_err(|_| {
self.new_import_error("__import__ not found".to_owned(), module.clone())
})?;
let import_func = self
.builtins
.clone()
.get_attr(identifier!(self, __import__), self)
.map_err(|_| {
self.new_import_error("__import__ not found".to_owned(), module.clone())
})?;
let (locals, globals) = if let Some(frame) = self.current_frame() {
(Some(frame.locals.clone()), Some(frame.globals.clone()))
@@ -559,7 +559,7 @@ impl VirtualMachine {
pub fn get_method_or_type_error<F>(
&self,
obj: PyObjectRef,
method_name: &str,
method_name: &'static PyStrInterned,
err_msg: F,
) -> PyResult
where
@@ -573,11 +573,20 @@ impl VirtualMachine {
}
// TODO: remove + transfer over to get_special_method
pub(crate) fn get_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
pub(crate) fn get_method(
&self,
obj: PyObjectRef,
method_name: &'static PyStrInterned,
) -> Option<PyResult> {
let method = obj.get_class_attr(method_name)?;
Some(self.call_if_get_descriptor(method, obj))
}
pub(crate) fn get_str_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
let method_name = self.ctx.interned_str(method_name)?;
self.get_method(obj, method_name)
}
pub fn is_callable(&self, obj: &PyObject) -> bool {
obj.class()
.mro_find_map(|cls| cls.slots.call.load())
@@ -706,7 +715,10 @@ impl VirtualMachine {
self.insert_sys_path(self.new_pyobj(path))?;
let runpy = self.import("runpy", None, 0)?;
let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?;
self.invoke(&run_module_as_main, (self.ctx.new_str("__main__"), false))?;
self.invoke(
&run_module_as_main,
(identifier!(self, __main__).to_owned(), false),
)?;
return Ok(());
}
@@ -734,9 +746,11 @@ impl VirtualMachine {
.compile(source, crate::compile::Mode::Exec, source_path.clone())
.map_err(|err| self.new_syntax_error(&err))?;
// trace!("Code object: {:?}", code_obj.borrow());
scope
.globals
.set_item("__file__", self.new_pyobj(source_path), self)?;
scope.globals.set_item(
identifier!(self, __file__),
self.new_pyobj(source_path),
self,
)?;
self.run_code_obj(code_obj, scope)
}
@@ -775,3 +789,9 @@ fn get_importer(path: &str, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>
None
})
}
impl AsRef<Context> for VirtualMachine {
fn as_ref(&self) -> &Context {
&self.ctx
}
}
+16 -12
View File
@@ -1,7 +1,8 @@
use super::PyMethod;
use crate::{
builtins::{PyBaseExceptionRef, PyList, PyStr},
builtins::{PyBaseExceptionRef, PyList, PyStr, PyStrInterned},
function::{FuncArgs, IntoFuncArgs},
identifier,
object::{AsObject, PyObject, PyObjectRef, PyPayload, PyResult},
vm::VirtualMachine,
};
@@ -118,21 +119,24 @@ impl VirtualMachine {
{
flame_guard!(format!("call_method({:?})", method_name));
PyMethod::get(
obj.to_owned(),
PyStr::from(method_name).into_ref(self),
self,
)?
.invoke(args, self)
let name = self
.ctx
.interned_str(method_name)
.map_or_else(|| PyStr::from(method_name).into_ref(self), |s| s.to_owned());
PyMethod::get(obj.to_owned(), name, self)?.invoke(args, self)
}
pub fn dir(&self, obj: Option<PyObjectRef>) -> PyResult<PyList> {
let seq = match obj {
Some(obj) => self
.get_special_method(obj, "__dir__")?
.get_special_method(obj, identifier!(self, __dir__))?
.map_err(|_obj| self.new_type_error("object does not provide __dir__".to_owned()))?
.invoke((), self)?,
None => self.call_method(self.current_locals()?.as_object(), "keys", ())?,
None => self.call_method(
self.current_locals()?.as_object(),
identifier!(self, keys).as_str(),
(),
)?,
};
let items: Vec<_> = seq.try_to_value(self)?;
let lst = PyList::from(items);
@@ -144,7 +148,7 @@ impl VirtualMachine {
pub(crate) fn get_special_method(
&self,
obj: PyObjectRef,
method: &str,
method: &'static PyStrInterned,
) -> PyResult<Result<PyMethod, PyObjectRef>> {
PyMethod::get_special(obj, method, self)
}
@@ -154,11 +158,11 @@ impl VirtualMachine {
pub fn call_special_method(
&self,
obj: PyObjectRef,
method: &str,
method: &'static PyStrInterned,
args: impl IntoFuncArgs,
) -> PyResult {
self.get_special_method(obj, method)?
.map_err(|_obj| self.new_attribute_error(method.to_owned()))?
.map_err(|_obj| self.new_attribute_error(method.as_str().to_owned()))?
.invoke(args, self)
}
+223 -113
View File
@@ -1,6 +1,6 @@
use super::{PyMethod, VirtualMachine};
use crate::{
builtins::{PyInt, PyIntRef},
builtins::{PyInt, PyIntRef, PyStrInterned},
function::PyArithmeticValue,
object::{AsObject, PyObject, PyObjectRef, PyResult},
protocol::PyIterReturn,
@@ -12,15 +12,17 @@ impl VirtualMachine {
pub fn to_index_opt(&self, obj: PyObjectRef) -> Option<PyResult<PyIntRef>> {
match obj.downcast() {
Ok(val) => Some(Ok(val)),
Err(obj) => self.get_method(obj, "__index__").map(|index| {
// TODO: returning strict subclasses of int in __index__ is deprecated
self.invoke(&index?, ())?.downcast().map_err(|bad| {
self.new_type_error(format!(
"__index__ returned non-int (type {})",
bad.class().name()
))
})
}),
Err(obj) => self
.get_method(obj, identifier!(self, __index__))
.map(|index| {
// TODO: returning strict subclasses of int in __index__ is deprecated
self.invoke(&index?, ())?.downcast().map_err(|bad| {
self.new_type_error(format!(
"__index__ returned non-int (type {})",
bad.class().name()
))
})
}),
}
}
@@ -77,7 +79,7 @@ impl VirtualMachine {
}
}
}
let hint = match self.get_method(iter, "__length_hint__") {
let hint = match self.get_method(iter, identifier!(self, __length_hint__)) {
Some(hint) => hint?,
None => return Ok(None),
};
@@ -135,7 +137,7 @@ impl VirtualMachine {
&self,
obj: &PyObject,
arg: &PyObject,
method: &str,
method: &'static PyStrInterned,
unsupported: F,
) -> PyResult
where
@@ -166,8 +168,8 @@ impl VirtualMachine {
&self,
lhs: &PyObject,
rhs: &PyObject,
default: &str,
reflection: &str,
default: &'static PyStrInterned,
reflection: &'static PyStrInterned,
unsupported: fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult,
) -> PyResult {
if rhs.fast_isinstance(&lhs.class()) {
@@ -199,213 +201,321 @@ impl VirtualMachine {
}
pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__sub__", "__rsub__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "-"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __sub__),
identifier!(self, __rsub__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-")),
)
}
pub fn _isub(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "-="))
})
self.call_or_unsupported(a, b, identifier!(self, __isub__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __sub__),
identifier!(self, __rsub__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-=")),
)
})
}
pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__add__", "__radd__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "+"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __add__),
identifier!(self, __radd__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+")),
)
}
pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "+="))
})
self.call_or_unsupported(a, b, identifier!(self, __iadd__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __add__),
identifier!(self, __radd__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+=")),
)
})
}
pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__mul__", "__rmul__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "*"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __mul__),
identifier!(self, __rmul__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*")),
)
}
pub fn _imul(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "*="))
})
self.call_or_unsupported(a, b, identifier!(self, __imul__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __mul__),
identifier!(self, __rmul__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*=")),
)
})
}
pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__matmul__", "__rmatmul__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "@"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __matmul__),
identifier!(self, __rmatmul__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@")),
)
}
pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "@="))
})
self.call_or_unsupported(a, b, identifier!(self, __imatmul__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __matmul__),
identifier!(self, __rmatmul__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@=")),
)
})
}
pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__truediv__", "__rtruediv__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "/"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __truediv__),
identifier!(self, __rtruediv__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/")),
)
}
pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "/="))
})
self.call_or_unsupported(a, b, identifier!(self, __itruediv__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __truediv__),
identifier!(self, __rtruediv__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/=")),
)
})
}
pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__floordiv__", "__rfloordiv__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "//"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __floordiv__),
identifier!(self, __rfloordiv__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//")),
)
}
pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "//="))
})
self.call_or_unsupported(a, b, identifier!(self, __ifloordiv__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __floordiv__),
identifier!(self, __rfloordiv__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//=")),
)
})
}
pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__pow__", "__rpow__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "**"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __pow__),
identifier!(self, __rpow__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**")),
)
}
pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "**="))
})
self.call_or_unsupported(a, b, identifier!(self, __ipow__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __pow__),
identifier!(self, __rpow__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**=")),
)
})
}
pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__mod__", "__rmod__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "%"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __mod__),
identifier!(self, __rmod__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%")),
)
}
pub fn _imod(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "%="))
})
self.call_or_unsupported(a, b, identifier!(self, __imod__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __mod__),
identifier!(self, __rmod__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%=")),
)
})
}
pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__divmod__", "__rdivmod__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "divmod"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __divmod__),
identifier!(self, __rdivmod__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "divmod")),
)
}
pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__lshift__", "__rlshift__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "<<"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __lshift__),
identifier!(self, __rlshift__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<")),
)
}
pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "<<="))
})
self.call_or_unsupported(a, b, identifier!(self, __ilshift__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __lshift__),
identifier!(self, __rlshift__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<=")),
)
})
}
pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__rshift__", "__rrshift__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, ">>"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __rshift__),
identifier!(self, __rrshift__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>")),
)
}
pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, ">>="))
})
self.call_or_unsupported(a, b, identifier!(self, __irshift__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __rshift__),
identifier!(self, __rrshift__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>=")),
)
})
}
pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__xor__", "__rxor__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "^"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __xor__),
identifier!(self, __rxor__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^")),
)
}
pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "^="))
})
self.call_or_unsupported(a, b, identifier!(self, __ixor__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __xor__),
identifier!(self, __rxor__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^=")),
)
})
}
pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__or__", "__ror__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "|"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __or__),
identifier!(self, __ror__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|")),
)
}
pub fn _ior(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "|="))
})
self.call_or_unsupported(a, b, identifier!(self, __ior__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __or__),
identifier!(self, __ror__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|=")),
)
})
}
pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult {
self.call_or_reflection(a, b, "__and__", "__rand__", |vm, a, b| {
Err(vm.new_unsupported_binop_error(a, b, "&"))
})
self.call_or_reflection(
a,
b,
identifier!(self, __and__),
identifier!(self, __rand__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&")),
)
}
pub fn _iand(&self, a: &PyObject, b: &PyObject) -> 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_binop_error(a, b, "&="))
})
self.call_or_unsupported(a, b, identifier!(self, __iand__), |vm, a, b| {
vm.call_or_reflection(
a,
b,
identifier!(self, __and__),
identifier!(self, __rand__),
|vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&=")),
)
})
}
pub fn _abs(&self, a: &PyObject) -> PyResult<PyObjectRef> {
self.get_special_method(a.to_owned(), "__abs__")?
self.get_special_method(a.to_owned(), identifier!(self, __abs__))?
.map_err(|_| self.new_unsupported_unary_error(a, "abs()"))?
.invoke((), self)
}
pub fn _pos(&self, a: &PyObject) -> PyResult {
self.get_special_method(a.to_owned(), "__pos__")?
self.get_special_method(a.to_owned(), identifier!(self, __pos__))?
.map_err(|_| self.new_unsupported_unary_error(a, "unary +"))?
.invoke((), self)
}
pub fn _neg(&self, a: &PyObject) -> PyResult {
self.get_special_method(a.to_owned(), "__neg__")?
self.get_special_method(a.to_owned(), identifier!(self, __neg__))?
.map_err(|_| self.new_unsupported_unary_error(a, "unary -"))?
.invoke((), self)
}
pub fn _invert(&self, a: &PyObject) -> PyResult {
self.get_special_method(a.to_owned(), "__invert__")?
self.get_special_method(a.to_owned(), identifier!(self, __invert__))?
.map_err(|_| self.new_unsupported_unary_error(a, "unary ~"))?
.invoke((), self)
}
@@ -431,7 +541,7 @@ impl VirtualMachine {
}
pub fn _contains(&self, haystack: PyObjectRef, needle: PyObjectRef) -> PyResult {
match PyMethod::get_special(haystack, "__contains__", self)? {
match PyMethod::get_special(haystack, identifier!(self, __contains__), self)? {
Ok(method) => method.invoke((needle,), self),
Err(haystack) => self
._membership_iter_search(haystack, needle)