Merge pull request #1684 from youknowone/refactor-things

Refactor small things
This commit is contained in:
Jeong YunWon
2020-01-15 14:13:02 +09:00
committed by GitHub
9 changed files with 246 additions and 249 deletions

View File

@@ -145,7 +145,7 @@ mod tests {
#[test]
fn test_parse_empty() {
let parse_ast = parse_program(&String::from(""));
let parse_ast = parse_program("");
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
}

View File

@@ -951,11 +951,11 @@ pub fn builtin_build_class_(
let cells = vm.ctx.new_dict();
let scope = function
.scope
.scope()
.new_child_scope_with_locals(cells.clone())
.new_child_scope_with_locals(namespace.clone());
vm.invoke_python_function_with_scope(&function, vec![].into(), &scope)?;
function.invoke_with_scope(vec![].into(), &scope, vm)?;
let class = vm.call_method(
metaclass.as_object(),

View File

@@ -369,29 +369,16 @@ impl DictKey for &str {
}
impl DictKey for &String {
fn do_hash(self, _vm: &VirtualMachine) -> PyResult<HashValue> {
// follow a similar route as the hashing of PyStringRef
let raw_hash = pyhash::hash_value(self).to_bigint().unwrap();
let raw_hash = pyhash::hash_bigint(&raw_hash);
let mut hasher = DefaultHasher::new();
raw_hash.hash(&mut hasher);
Ok(hasher.finish() as HashValue)
fn do_hash(self, vm: &VirtualMachine) -> PyResult<HashValue> {
self.as_str().do_hash(vm)
}
fn do_is(self, _other: &PyObjectRef) -> bool {
// No matter who the other pyobject is, we are never the same thing, since
// we are a str, not a pyobject.
false
fn do_is(self, other: &PyObjectRef) -> bool {
self.as_str().do_is(other)
}
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
if let Some(py_str_value) = other_key.payload::<PyString>() {
Ok(py_str_value.as_str() == self)
} else {
// Fall back to PyString implementation.
let s = vm.new_str(self.to_string());
s.do_eq(vm, other_key)
}
self.as_str().do_eq(vm, other_key)
}
}

View File

@@ -29,10 +29,16 @@ use crate::vm::VirtualMachine;
#[pyclass]
#[derive(Clone, Debug)]
pub struct PyClassMethod {
pub callable: PyObjectRef,
callable: PyObjectRef,
}
pub type PyClassMethodRef = PyRef<PyClassMethod>;
impl PyClassMethod {
pub fn new(value: PyObjectRef) -> Self {
Self { callable: value }
}
}
impl PyValue for PyClassMethod {
const HAVE_DICT: bool = true;
@@ -73,7 +79,7 @@ impl PyClassMethod {
}
}
pub fn init(context: &PyContext) {
pub(crate) fn init(context: &PyContext) {
PyClassMethod::extend_class(context, &context.types.classmethod_type);
extend_class!(context, context.types.classmethod_type, {
"__get__" => context.new_method(PyClassMethod::get),

View File

@@ -3,21 +3,28 @@ use super::objdict::PyDictRef;
use super::objstr::PyStringRef;
use super::objtuple::PyTupleRef;
use super::objtype::PyClassRef;
use crate::bytecode;
use crate::descriptor::PyBuiltinDescriptor;
use crate::frame::Frame;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
use crate::obj::objcoroutine::PyCoroutine;
use crate::obj::objgenerator::PyGenerator;
use crate::pyobject::{
IdProtocol, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TypeProtocol,
};
use crate::scope::Scope;
use crate::vm::VirtualMachine;
pub type PyFunctionRef = PyRef<PyFunction>;
#[pyclass]
#[derive(Debug)]
pub struct PyFunction {
// TODO: these shouldn't be public
pub code: PyCodeRef,
pub scope: Scope,
pub defaults: Option<PyTupleRef>,
pub kw_only_defaults: Option<PyDictRef>,
code: PyCodeRef,
scope: Scope,
defaults: Option<PyTupleRef>,
kw_only_defaults: Option<PyDictRef>,
}
impl PyBuiltinDescriptor for PyFunction {
@@ -49,6 +56,182 @@ impl PyFunction {
kw_only_defaults,
}
}
pub fn scope(&self) -> &Scope {
&self.scope
}
fn fill_locals_from_args(
&self,
code_object: &bytecode::CodeObject,
locals: &PyDictRef,
func_args: PyFuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
let nargs = func_args.args.len();
let nexpected_args = code_object.arg_names.len();
// This parses the arguments from args and kwargs into
// the proper variables keeping into account default values
// and starargs and kwargs.
// See also: PyEval_EvalCodeWithName in cpython:
// https://github.com/python/cpython/blob/master/Python/ceval.c#L3681
let n = if nargs > nexpected_args {
nexpected_args
} else {
nargs
};
// Copy positional arguments into local variables
for i in 0..n {
let arg_name = &code_object.arg_names[i];
let arg = &func_args.args[i];
locals.set_item(arg_name, arg.clone(), vm)?;
}
// Pack other positional arguments in to *args:
match code_object.varargs {
bytecode::Varargs::Named(ref vararg_name) => {
let mut last_args = vec![];
for i in n..nargs {
let arg = &func_args.args[i];
last_args.push(arg.clone());
}
let vararg_value = vm.ctx.new_tuple(last_args);
locals.set_item(vararg_name, vararg_value, vm)?;
}
bytecode::Varargs::Unnamed | bytecode::Varargs::None => {
// Check the number of positional arguments
if nargs > nexpected_args {
return Err(vm.new_type_error(format!(
"Expected {} arguments (got: {})",
nexpected_args, nargs
)));
}
}
}
// Do we support `**kwargs` ?
let kwargs = match code_object.varkeywords {
bytecode::Varargs::Named(ref kwargs_name) => {
let d = vm.ctx.new_dict();
locals.set_item(kwargs_name, d.as_object().clone(), vm)?;
Some(d)
}
bytecode::Varargs::Unnamed => Some(vm.ctx.new_dict()),
bytecode::Varargs::None => None,
};
// Handle keyword arguments
for (name, value) in func_args.kwargs {
// Check if we have a parameter with this name:
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
{
if locals.contains_key(&name, vm) {
return Err(
vm.new_type_error(format!("Got multiple values for argument '{}'", name))
);
}
locals.set_item(&name, value, vm)?;
} else if let Some(d) = &kwargs {
d.set_item(&name, value, vm)?;
} else {
return Err(
vm.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
);
}
}
// Add missing positional arguments, if we have fewer positional arguments than the
// function definition calls for
if nargs < nexpected_args {
let num_defaults_available = self.defaults.as_ref().map_or(0, |d| d.as_slice().len());
// Given the number of defaults available, check all the arguments for which we
// _don't_ have defaults; if any are missing, raise an exception
let required_args = nexpected_args - num_defaults_available;
let mut missing = vec![];
for i in 0..required_args {
let variable_name = &code_object.arg_names[i];
if !locals.contains_key(variable_name, vm) {
missing.push(variable_name)
}
}
if !missing.is_empty() {
return Err(vm.new_type_error(format!(
"Missing {} required positional arguments: {:?}",
missing.len(),
missing
)));
}
if let Some(defaults) = &self.defaults {
let defaults = defaults.as_slice();
// We have sufficient defaults, so iterate over the corresponding names and use
// the default if we don't already have a value
for (default_index, i) in (required_args..nexpected_args).enumerate() {
let arg_name = &code_object.arg_names[i];
if !locals.contains_key(arg_name, vm) {
locals.set_item(arg_name, defaults[default_index].clone(), vm)?;
}
}
}
};
// Check if kw only arguments are all present:
for arg_name in &code_object.kwonlyarg_names {
if !locals.contains_key(arg_name, vm) {
if let Some(kw_only_defaults) = &self.kw_only_defaults {
if let Some(default) = kw_only_defaults.get_item_option(arg_name, vm)? {
locals.set_item(arg_name, default, vm)?;
continue;
}
}
// No default value and not specified.
return Err(
vm.new_type_error(format!("Missing required kw only argument: '{}'", arg_name))
);
}
}
Ok(())
}
pub fn invoke_with_scope(
&self,
func_args: PyFuncArgs,
scope: &Scope,
vm: &VirtualMachine,
) -> PyResult {
let code = &self.code;
let scope = if self.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
scope.new_child_scope(&vm.ctx)
} else {
scope.clone()
};
self.fill_locals_from_args(&code, &scope.get_locals(), func_args, vm)?;
// Construct frame:
let frame = Frame::new(code.clone(), scope).into_ref(vm);
// If we have a generator, create a new generator
if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) {
Ok(PyGenerator::new(frame, vm).into_object())
} else if code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) {
Ok(PyCoroutine::new(frame, vm).into_object())
} else {
vm.run_frame_full(frame)
}
}
pub fn invoke(&self, func_args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
self.invoke_with_scope(func_args, &self.scope, vm)
}
}
impl PyValue for PyFunction {
@@ -57,20 +240,25 @@ impl PyValue for PyFunction {
}
}
impl PyFunctionRef {
fn call(func: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm.invoke(&func, args)
#[pyimpl]
impl PyFunction {
#[pymethod(name = "__call__")]
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm.invoke(&zelf, args)
}
fn code(self, _vm: &VirtualMachine) -> PyCodeRef {
#[pyproperty(name = "__code__")]
fn code(&self, _vm: &VirtualMachine) -> PyCodeRef {
self.code.clone()
}
fn defaults(self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
#[pyproperty(name = "__defaults__")]
fn defaults(&self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
self.defaults.clone()
}
fn kwdefaults(self, _vm: &VirtualMachine) -> Option<PyDictRef> {
#[pyproperty(name = "__kwdefaults__")]
fn kwdefaults(&self, _vm: &VirtualMachine) -> Option<PyDictRef> {
self.kw_only_defaults.clone()
}
}
@@ -100,13 +288,10 @@ impl PyValue for PyBoundMethod {
pub fn init(context: &PyContext) {
let function_type = &context.types.function_type;
PyFunction::extend_class(context, function_type);
extend_class!(context, function_type, {
"__get__" => context.new_method(PyFunction::get),
(slot descr_get) => PyFunction::get,
"__call__" => context.new_method(PyFunctionRef::call),
"__code__" => context.new_property(PyFunctionRef::code),
"__defaults__" => context.new_property(PyFunctionRef::defaults),
"__kwdefaults__" => context.new_property(PyFunctionRef::kwdefaults),
});
let method_type = &context.types.bound_method_type;

View File

@@ -29,7 +29,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
}
#[pyimpl]
impl PyStaticMethodRef {
impl PyStaticMethod {
#[pyslot]
fn tp_new(
cls: PyClassRef,
@@ -44,7 +44,7 @@ impl PyStaticMethodRef {
}
pub fn init(context: &PyContext) {
PyStaticMethodRef::extend_class(context, &context.types.staticmethod_type);
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);
extend_class!(context, context.types.staticmethod_type, {
"__get__" => context.new_method(PyStaticMethod::get),
(slot descr_get) => PyStaticMethod::get,

View File

@@ -7,8 +7,9 @@ use super::objtype::PyClassRef;
use crate::function::OptionalArg;
use crate::pyhash;
use crate::pyobject::{
IntoPyObject, PyArithmaticValue::*, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef,
PyRef, PyResult, PyValue,
IntoPyObject,
PyArithmaticValue::{self, *},
PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
};
use crate::sequence::{self, SimpleSeq};
use crate::vm::{ReprGuard, VirtualMachine};
@@ -111,17 +112,17 @@ impl PyTuple {
}
#[pymethod(name = "__add__")]
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyArithmaticValue<PyTuple> {
if let Some(other) = other.payload_if_subclass::<PyTuple>(vm) {
let elements = self
let elements: Vec<_> = self
.elements
.iter()
.chain(other.as_slice().iter())
.cloned()
.collect();
Ok(vm.ctx.new_tuple(elements))
Implemented(elements.into())
} else {
Ok(vm.ctx.not_implemented())
NotImplemented
}
}
@@ -190,15 +191,15 @@ impl PyTuple {
}
#[pymethod(name = "__mul__")]
fn mul(&self, counter: isize, vm: &VirtualMachine) -> PyObjectRef {
let new_elements = sequence::seq_mul(&self.elements, counter)
fn mul(&self, counter: isize, _vm: &VirtualMachine) -> PyTuple {
let new_elements: Vec<_> = sequence::seq_mul(&self.elements, counter)
.cloned()
.collect();
vm.ctx.new_tuple(new_elements)
new_elements.into()
}
#[pymethod(name = "__rmul__")]
fn rmul(&self, counter: isize, vm: &VirtualMachine) -> PyObjectRef {
fn rmul(&self, counter: isize, vm: &VirtualMachine) -> PyTuple {
self.mul(counter, vm)
}

View File

@@ -492,9 +492,7 @@ impl PyContext {
F: IntoPyNativeFunc<T, R, VM>,
{
PyObject::new(
PyClassMethod {
callable: self.new_method(f),
},
PyClassMethod::new(self.new_method(f)),
self.classmethod_type(),
None,
)
@@ -692,12 +690,6 @@ where
}
}
impl<T> IntoPyObject for PyRef<T> {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.obj)
}
}
impl<'a, T: PyValue> From<&'a PyRef<T>> for &'a PyObjectRef {
fn from(obj: &'a PyRef<T>) -> Self {
obj.as_object()
@@ -747,12 +739,6 @@ impl TryFromObject for PyCallable {
}
}
impl IntoPyObject for PyCallable {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.into_object())
}
}
pub trait IdProtocol {
fn get_id(&self) -> usize;
fn is<T>(&self, other: &T) -> bool
@@ -1019,6 +1005,18 @@ pub trait IntoPyObject {
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult;
}
impl<T> IntoPyObject for PyRef<T> {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.obj)
}
}
impl IntoPyObject for PyCallable {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.into_object())
}
}
impl IntoPyObject for PyObjectRef {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self)

View File

@@ -28,16 +28,14 @@ use crate::import;
use crate::obj::objbool;
use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod};
use crate::obj::objcode::{PyCode, PyCodeRef};
use crate::obj::objcoroutine::PyCoroutine;
use crate::obj::objdict::PyDictRef;
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
use crate::obj::objgenerator::PyGenerator;
use crate::obj::objint::PyInt;
use crate::obj::objiter;
use crate::obj::objlist::PyList;
use crate::obj::objmodule::{self, PyModule};
use crate::obj::objstr::{PyString, PyStringRef};
use crate::obj::objtuple::{PyTuple, PyTupleRef};
use crate::obj::objtuple::PyTuple;
use crate::obj::objtype::{self, PyClassRef};
use crate::pyhash;
use crate::pyobject::{
@@ -661,9 +659,9 @@ impl VirtualMachine {
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
vm_trace!("Invoke: {:?} {:?}", func_ref, args);
if let Some(py_func) = func_ref.payload() {
if let Some(py_func) = func_ref.payload::<PyFunction>() {
self.trace_event(TraceEvent::Call)?;
let res = self.invoke_python_function(py_func, args);
let res = py_func.invoke(args, self);
self.trace_event(TraceEvent::Return)?;
res
} else if let Some(PyBoundMethod {
@@ -725,184 +723,6 @@ impl VirtualMachine {
Ok(())
}
pub fn invoke_python_function(&self, func: &PyFunction, func_args: PyFuncArgs) -> PyResult {
self.invoke_python_function_with_scope(func, func_args, &func.scope)
}
pub fn invoke_python_function_with_scope(
&self,
func: &PyFunction,
func_args: PyFuncArgs,
scope: &Scope,
) -> PyResult {
let code = &func.code;
let scope = if func.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
scope.new_child_scope(&self.ctx)
} else {
scope.clone()
};
self.fill_locals_from_args(
&code,
&scope.get_locals(),
func_args,
&func.defaults,
&func.kw_only_defaults,
)?;
// Construct frame:
let frame = Frame::new(code.clone(), scope).into_ref(self);
// If we have a generator, create a new generator
if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) {
Ok(PyGenerator::new(frame, self).into_object())
} else if code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) {
Ok(PyCoroutine::new(frame, self).into_object())
} else {
self.run_frame_full(frame)
}
}
fn fill_locals_from_args(
&self,
code_object: &bytecode::CodeObject,
locals: &PyDictRef,
func_args: PyFuncArgs,
defaults: &Option<PyTupleRef>,
kw_only_defaults: &Option<PyDictRef>,
) -> PyResult<()> {
let nargs = func_args.args.len();
let nexpected_args = code_object.arg_names.len();
// This parses the arguments from args and kwargs into
// the proper variables keeping into account default values
// and starargs and kwargs.
// See also: PyEval_EvalCodeWithName in cpython:
// https://github.com/python/cpython/blob/master/Python/ceval.c#L3681
let n = if nargs > nexpected_args {
nexpected_args
} else {
nargs
};
// Copy positional arguments into local variables
for i in 0..n {
let arg_name = &code_object.arg_names[i];
let arg = &func_args.args[i];
locals.set_item(arg_name, arg.clone(), self)?;
}
// Pack other positional arguments in to *args:
match code_object.varargs {
bytecode::Varargs::Named(ref vararg_name) => {
let mut last_args = vec![];
for i in n..nargs {
let arg = &func_args.args[i];
last_args.push(arg.clone());
}
let vararg_value = self.ctx.new_tuple(last_args);
locals.set_item(vararg_name, vararg_value, self)?;
}
bytecode::Varargs::Unnamed | bytecode::Varargs::None => {
// Check the number of positional arguments
if nargs > nexpected_args {
return Err(self.new_type_error(format!(
"Expected {} arguments (got: {})",
nexpected_args, nargs
)));
}
}
}
// Do we support `**kwargs` ?
let kwargs = match code_object.varkeywords {
bytecode::Varargs::Named(ref kwargs_name) => {
let d = self.ctx.new_dict();
locals.set_item(kwargs_name, d.as_object().clone(), self)?;
Some(d)
}
bytecode::Varargs::Unnamed => Some(self.ctx.new_dict()),
bytecode::Varargs::None => None,
};
// Handle keyword arguments
for (name, value) in func_args.kwargs {
// Check if we have a parameter with this name:
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
{
if locals.contains_key(&name, self) {
return Err(
self.new_type_error(format!("Got multiple values for argument '{}'", name))
);
}
locals.set_item(&name, value, self)?;
} else if let Some(d) = &kwargs {
d.set_item(&name, value, self)?;
} else {
return Err(
self.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
);
}
}
// Add missing positional arguments, if we have fewer positional arguments than the
// function definition calls for
if nargs < nexpected_args {
let num_defaults_available = defaults.as_ref().map_or(0, |d| d.as_slice().len());
// Given the number of defaults available, check all the arguments for which we
// _don't_ have defaults; if any are missing, raise an exception
let required_args = nexpected_args - num_defaults_available;
let mut missing = vec![];
for i in 0..required_args {
let variable_name = &code_object.arg_names[i];
if !locals.contains_key(variable_name, self) {
missing.push(variable_name)
}
}
if !missing.is_empty() {
return Err(self.new_type_error(format!(
"Missing {} required positional arguments: {:?}",
missing.len(),
missing
)));
}
if let Some(defaults) = defaults {
let defaults = defaults.as_slice();
// We have sufficient defaults, so iterate over the corresponding names and use
// the default if we don't already have a value
for (default_index, i) in (required_args..nexpected_args).enumerate() {
let arg_name = &code_object.arg_names[i];
if !locals.contains_key(arg_name, self) {
locals.set_item(arg_name, defaults[default_index].clone(), self)?;
}
}
}
};
// Check if kw only arguments are all present:
for arg_name in &code_object.kwonlyarg_names {
if !locals.contains_key(arg_name, self) {
if let Some(kw_only_defaults) = kw_only_defaults {
if let Some(default) = kw_only_defaults.get_item_option(arg_name, self)? {
locals.set_item(arg_name, default, self)?;
continue;
}
}
// No default value and not specified.
return Err(self
.new_type_error(format!("Missing required kw only argument: '{}'", arg_name)));
}
}
Ok(())
}
pub fn extract_elements<T: TryFromObject>(&self, value: &PyObjectRef) -> PyResult<Vec<T>> {
// Extract elements from item, if possible:
let cls = value.class();