Skip to content

Commit c07e932

Browse files
authored
Merge pull request #1684 from youknowone/refactor-things
Refactor small things
2 parents d31d0c6 + bf0b4dc commit c07e932

File tree

9 files changed

+246
-249
lines changed

9 files changed

+246
-249
lines changed

parser/src/parser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ mod tests {
145145

146146
#[test]
147147
fn test_parse_empty() {
148-
let parse_ast = parse_program(&String::from(""));
148+
let parse_ast = parse_program("");
149149
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
150150
}
151151

vm/src/builtins.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -951,11 +951,11 @@ pub fn builtin_build_class_(
951951
let cells = vm.ctx.new_dict();
952952

953953
let scope = function
954-
.scope
954+
.scope()
955955
.new_child_scope_with_locals(cells.clone())
956956
.new_child_scope_with_locals(namespace.clone());
957957

958-
vm.invoke_python_function_with_scope(&function, vec![].into(), &scope)?;
958+
function.invoke_with_scope(vec![].into(), &scope, vm)?;
959959

960960
let class = vm.call_method(
961961
metaclass.as_object(),

vm/src/dictdatatype.rs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -369,29 +369,16 @@ impl DictKey for &str {
369369
}
370370

371371
impl DictKey for &String {
372-
fn do_hash(self, _vm: &VirtualMachine) -> PyResult<HashValue> {
373-
// follow a similar route as the hashing of PyStringRef
374-
let raw_hash = pyhash::hash_value(self).to_bigint().unwrap();
375-
let raw_hash = pyhash::hash_bigint(&raw_hash);
376-
let mut hasher = DefaultHasher::new();
377-
raw_hash.hash(&mut hasher);
378-
Ok(hasher.finish() as HashValue)
372+
fn do_hash(self, vm: &VirtualMachine) -> PyResult<HashValue> {
373+
self.as_str().do_hash(vm)
379374
}
380375

381-
fn do_is(self, _other: &PyObjectRef) -> bool {
382-
// No matter who the other pyobject is, we are never the same thing, since
383-
// we are a str, not a pyobject.
384-
false
376+
fn do_is(self, other: &PyObjectRef) -> bool {
377+
self.as_str().do_is(other)
385378
}
386379

387380
fn do_eq(self, vm: &VirtualMachine, other_key: &PyObjectRef) -> PyResult<bool> {
388-
if let Some(py_str_value) = other_key.payload::<PyString>() {
389-
Ok(py_str_value.as_str() == self)
390-
} else {
391-
// Fall back to PyString implementation.
392-
let s = vm.new_str(self.to_string());
393-
s.do_eq(vm, other_key)
394-
}
381+
self.as_str().do_eq(vm, other_key)
395382
}
396383
}
397384

vm/src/obj/objclassmethod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ use crate::vm::VirtualMachine;
2929
#[pyclass]
3030
#[derive(Clone, Debug)]
3131
pub struct PyClassMethod {
32-
pub callable: PyObjectRef,
32+
callable: PyObjectRef,
3333
}
3434
pub type PyClassMethodRef = PyRef<PyClassMethod>;
3535

36+
impl PyClassMethod {
37+
pub fn new(value: PyObjectRef) -> Self {
38+
Self { callable: value }
39+
}
40+
}
41+
3642
impl PyValue for PyClassMethod {
3743
const HAVE_DICT: bool = true;
3844

@@ -73,7 +79,7 @@ impl PyClassMethod {
7379
}
7480
}
7581

76-
pub fn init(context: &PyContext) {
82+
pub(crate) fn init(context: &PyContext) {
7783
PyClassMethod::extend_class(context, &context.types.classmethod_type);
7884
extend_class!(context, context.types.classmethod_type, {
7985
"__get__" => context.new_method(PyClassMethod::get),

vm/src/obj/objfunction.rs

Lines changed: 201 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,28 @@ use super::objdict::PyDictRef;
33
use super::objstr::PyStringRef;
44
use super::objtuple::PyTupleRef;
55
use super::objtype::PyClassRef;
6+
use crate::bytecode;
67
use crate::descriptor::PyBuiltinDescriptor;
8+
use crate::frame::Frame;
79
use crate::function::{OptionalArg, PyFuncArgs};
8-
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
10+
use crate::obj::objcoroutine::PyCoroutine;
11+
use crate::obj::objgenerator::PyGenerator;
12+
use crate::pyobject::{
13+
IdProtocol, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
14+
TypeProtocol,
15+
};
916
use crate::scope::Scope;
1017
use crate::vm::VirtualMachine;
1118

1219
pub type PyFunctionRef = PyRef<PyFunction>;
1320

21+
#[pyclass]
1422
#[derive(Debug)]
1523
pub struct PyFunction {
16-
// TODO: these shouldn't be public
17-
pub code: PyCodeRef,
18-
pub scope: Scope,
19-
pub defaults: Option<PyTupleRef>,
20-
pub kw_only_defaults: Option<PyDictRef>,
24+
code: PyCodeRef,
25+
scope: Scope,
26+
defaults: Option<PyTupleRef>,
27+
kw_only_defaults: Option<PyDictRef>,
2128
}
2229

2330
impl PyBuiltinDescriptor for PyFunction {
@@ -49,6 +56,182 @@ impl PyFunction {
4956
kw_only_defaults,
5057
}
5158
}
59+
60+
pub fn scope(&self) -> &Scope {
61+
&self.scope
62+
}
63+
64+
fn fill_locals_from_args(
65+
&self,
66+
code_object: &bytecode::CodeObject,
67+
locals: &PyDictRef,
68+
func_args: PyFuncArgs,
69+
vm: &VirtualMachine,
70+
) -> PyResult<()> {
71+
let nargs = func_args.args.len();
72+
let nexpected_args = code_object.arg_names.len();
73+
74+
// This parses the arguments from args and kwargs into
75+
// the proper variables keeping into account default values
76+
// and starargs and kwargs.
77+
// See also: PyEval_EvalCodeWithName in cpython:
78+
// https://github.com/python/cpython/blob/master/Python/ceval.c#L3681
79+
80+
let n = if nargs > nexpected_args {
81+
nexpected_args
82+
} else {
83+
nargs
84+
};
85+
86+
// Copy positional arguments into local variables
87+
for i in 0..n {
88+
let arg_name = &code_object.arg_names[i];
89+
let arg = &func_args.args[i];
90+
locals.set_item(arg_name, arg.clone(), vm)?;
91+
}
92+
93+
// Pack other positional arguments in to *args:
94+
match code_object.varargs {
95+
bytecode::Varargs::Named(ref vararg_name) => {
96+
let mut last_args = vec![];
97+
for i in n..nargs {
98+
let arg = &func_args.args[i];
99+
last_args.push(arg.clone());
100+
}
101+
let vararg_value = vm.ctx.new_tuple(last_args);
102+
103+
locals.set_item(vararg_name, vararg_value, vm)?;
104+
}
105+
bytecode::Varargs::Unnamed | bytecode::Varargs::None => {
106+
// Check the number of positional arguments
107+
if nargs > nexpected_args {
108+
return Err(vm.new_type_error(format!(
109+
"Expected {} arguments (got: {})",
110+
nexpected_args, nargs
111+
)));
112+
}
113+
}
114+
}
115+
116+
// Do we support `**kwargs` ?
117+
let kwargs = match code_object.varkeywords {
118+
bytecode::Varargs::Named(ref kwargs_name) => {
119+
let d = vm.ctx.new_dict();
120+
locals.set_item(kwargs_name, d.as_object().clone(), vm)?;
121+
Some(d)
122+
}
123+
bytecode::Varargs::Unnamed => Some(vm.ctx.new_dict()),
124+
bytecode::Varargs::None => None,
125+
};
126+
127+
// Handle keyword arguments
128+
for (name, value) in func_args.kwargs {
129+
// Check if we have a parameter with this name:
130+
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
131+
{
132+
if locals.contains_key(&name, vm) {
133+
return Err(
134+
vm.new_type_error(format!("Got multiple values for argument '{}'", name))
135+
);
136+
}
137+
138+
locals.set_item(&name, value, vm)?;
139+
} else if let Some(d) = &kwargs {
140+
d.set_item(&name, value, vm)?;
141+
} else {
142+
return Err(
143+
vm.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
144+
);
145+
}
146+
}
147+
148+
// Add missing positional arguments, if we have fewer positional arguments than the
149+
// function definition calls for
150+
if nargs < nexpected_args {
151+
let num_defaults_available = self.defaults.as_ref().map_or(0, |d| d.as_slice().len());
152+
153+
// Given the number of defaults available, check all the arguments for which we
154+
// _don't_ have defaults; if any are missing, raise an exception
155+
let required_args = nexpected_args - num_defaults_available;
156+
let mut missing = vec![];
157+
for i in 0..required_args {
158+
let variable_name = &code_object.arg_names[i];
159+
if !locals.contains_key(variable_name, vm) {
160+
missing.push(variable_name)
161+
}
162+
}
163+
if !missing.is_empty() {
164+
return Err(vm.new_type_error(format!(
165+
"Missing {} required positional arguments: {:?}",
166+
missing.len(),
167+
missing
168+
)));
169+
}
170+
if let Some(defaults) = &self.defaults {
171+
let defaults = defaults.as_slice();
172+
// We have sufficient defaults, so iterate over the corresponding names and use
173+
// the default if we don't already have a value
174+
for (default_index, i) in (required_args..nexpected_args).enumerate() {
175+
let arg_name = &code_object.arg_names[i];
176+
if !locals.contains_key(arg_name, vm) {
177+
locals.set_item(arg_name, defaults[default_index].clone(), vm)?;
178+
}
179+
}
180+
}
181+
};
182+
183+
// Check if kw only arguments are all present:
184+
for arg_name in &code_object.kwonlyarg_names {
185+
if !locals.contains_key(arg_name, vm) {
186+
if let Some(kw_only_defaults) = &self.kw_only_defaults {
187+
if let Some(default) = kw_only_defaults.get_item_option(arg_name, vm)? {
188+
locals.set_item(arg_name, default, vm)?;
189+
continue;
190+
}
191+
}
192+
193+
// No default value and not specified.
194+
return Err(
195+
vm.new_type_error(format!("Missing required kw only argument: '{}'", arg_name))
196+
);
197+
}
198+
}
199+
200+
Ok(())
201+
}
202+
203+
pub fn invoke_with_scope(
204+
&self,
205+
func_args: PyFuncArgs,
206+
scope: &Scope,
207+
vm: &VirtualMachine,
208+
) -> PyResult {
209+
let code = &self.code;
210+
211+
let scope = if self.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
212+
scope.new_child_scope(&vm.ctx)
213+
} else {
214+
scope.clone()
215+
};
216+
217+
self.fill_locals_from_args(&code, &scope.get_locals(), func_args, vm)?;
218+
219+
// Construct frame:
220+
let frame = Frame::new(code.clone(), scope).into_ref(vm);
221+
222+
// If we have a generator, create a new generator
223+
if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) {
224+
Ok(PyGenerator::new(frame, vm).into_object())
225+
} else if code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) {
226+
Ok(PyCoroutine::new(frame, vm).into_object())
227+
} else {
228+
vm.run_frame_full(frame)
229+
}
230+
}
231+
232+
pub fn invoke(&self, func_args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
233+
self.invoke_with_scope(func_args, &self.scope, vm)
234+
}
52235
}
53236

54237
impl PyValue for PyFunction {
@@ -57,20 +240,25 @@ impl PyValue for PyFunction {
57240
}
58241
}
59242

60-
impl PyFunctionRef {
61-
fn call(func: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
62-
vm.invoke(&func, args)
243+
#[pyimpl]
244+
impl PyFunction {
245+
#[pymethod(name = "__call__")]
246+
fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
247+
vm.invoke(&zelf, args)
63248
}
64249

65-
fn code(self, _vm: &VirtualMachine) -> PyCodeRef {
250+
#[pyproperty(name = "__code__")]
251+
fn code(&self, _vm: &VirtualMachine) -> PyCodeRef {
66252
self.code.clone()
67253
}
68254

69-
fn defaults(self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
255+
#[pyproperty(name = "__defaults__")]
256+
fn defaults(&self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
70257
self.defaults.clone()
71258
}
72259

73-
fn kwdefaults(self, _vm: &VirtualMachine) -> Option<PyDictRef> {
260+
#[pyproperty(name = "__kwdefaults__")]
261+
fn kwdefaults(&self, _vm: &VirtualMachine) -> Option<PyDictRef> {
74262
self.kw_only_defaults.clone()
75263
}
76264
}
@@ -100,13 +288,10 @@ impl PyValue for PyBoundMethod {
100288

101289
pub fn init(context: &PyContext) {
102290
let function_type = &context.types.function_type;
291+
PyFunction::extend_class(context, function_type);
103292
extend_class!(context, function_type, {
104293
"__get__" => context.new_method(PyFunction::get),
105294
(slot descr_get) => PyFunction::get,
106-
"__call__" => context.new_method(PyFunctionRef::call),
107-
"__code__" => context.new_property(PyFunctionRef::code),
108-
"__defaults__" => context.new_property(PyFunctionRef::defaults),
109-
"__kwdefaults__" => context.new_property(PyFunctionRef::kwdefaults),
110295
});
111296

112297
let method_type = &context.types.bound_method_type;

vm/src/obj/objstaticmethod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
2929
}
3030

3131
#[pyimpl]
32-
impl PyStaticMethodRef {
32+
impl PyStaticMethod {
3333
#[pyslot]
3434
fn tp_new(
3535
cls: PyClassRef,
@@ -44,7 +44,7 @@ impl PyStaticMethodRef {
4444
}
4545

4646
pub fn init(context: &PyContext) {
47-
PyStaticMethodRef::extend_class(context, &context.types.staticmethod_type);
47+
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);
4848
extend_class!(context, context.types.staticmethod_type, {
4949
"__get__" => context.new_method(PyStaticMethod::get),
5050
(slot descr_get) => PyStaticMethod::get,

0 commit comments

Comments
 (0)