Skip to content

WIP - Specify metaclass and create class with correct type. #184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool
}
Err(err) => {
// Enum rather than special string here.
let msg = match vm.get_attribute(err.clone(), "msg") {
let name = vm.new_str("msg".to_string());
let msg = match vm.get_attribute(err.clone(), name) {
Ok(value) => objstr::get_value(&value),
Err(_) => panic!("Expected msg attribute on exception object!"),
};
Expand Down
25 changes: 25 additions & 0 deletions tests/snippets/metaclasses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class MC(type):
classes = []
count = 0

def __new__(cls, name, bases, namespace):
MC.classes.append(name)
return type.__new__(cls, name, bases, namespace)

def __call__(cls):
MC.count += 1
return type.__call__(cls, MC.count)

class C(object, metaclass=MC):
def __new__(cls, count):
self = object.__new__(cls)
self.count = count
return self

class D(object, metaclass=MC):
pass

assert MC == type(C)
assert C == type(C())
assert MC.classes == ['C', 'D']
assert C().count == 2
53 changes: 27 additions & 26 deletions vm/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn dir_object(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyObjectRef {

fn builtin_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(x, None)]);
match vm.get_attribute(x.clone(), &"__abs__") {
match vm.get_method(x.clone(), "__abs__") {
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![], vec![])),
Err(..) => Err(vm.new_type_error("bad operand for abs".to_string())),
}
Expand Down Expand Up @@ -134,7 +134,7 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(x, None), (y, None)]);
match vm.get_attribute(x.clone(), &"__divmod__") {
match vm.get_method(x.clone(), "__divmod__") {
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])),
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for divmod".to_string())),
}
Expand Down Expand Up @@ -218,11 +218,7 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
args,
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
);
if let PyObjectKind::String { ref value } = attr.borrow().kind {
vm.get_attribute(obj.clone(), value)
} else {
panic!("argument checking failure: attr not string")
}
vm.get_attribute(obj.clone(), attr.clone())
}

// builtin_globals
Expand All @@ -233,15 +229,11 @@ fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
args,
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
);
if let PyObjectKind::String { ref value } = attr.borrow().kind {
let has_attr = match vm.get_attribute(obj.clone(), value) {
Ok(..) => true,
Err(..) => false,
};
Ok(vm.context().new_bool(has_attr))
} else {
panic!("argument checking failure: attr not string")
}
let has_attr = match vm.get_attribute(obj.clone(), attr.clone()) {
Ok(..) => true,
Err(..) => false,
};
Ok(vm.context().new_bool(has_attr))
}

fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Expand Down Expand Up @@ -308,7 +300,7 @@ fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
_ => {
let len_method_name = "__len__".to_string();
match vm.get_attribute(obj.clone(), &len_method_name) {
match vm.get_method(obj.clone(), &len_method_name) {
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
Err(..) => Err(vm.context().new_str(
format!(
Expand Down Expand Up @@ -444,7 +436,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
optional = [(mod_value, Some(vm.ctx.int_type()))]
);
let pow_method_name = "__pow__".to_string();
let result = match vm.get_attribute(x.clone(), &pow_method_name) {
let result = match vm.get_method(x.clone(), &pow_method_name) {
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])),
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for pow".to_string())),
};
Expand All @@ -454,7 +446,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
match mod_value {
Some(mod_value) => {
let mod_method_name = "__mod__".to_string();
match vm.get_attribute(
match vm.get_method(
result.expect("result not defined").clone(),
&mod_method_name,
) {
Expand Down Expand Up @@ -644,14 +636,10 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
let function = args.shift();
let name_arg = args.shift();
let name_arg_ref = name_arg.borrow();
let name = match name_arg_ref.kind {
PyObjectKind::String { ref value } => value,
_ => panic!("Class name must by a string!"),
};
let mut bases = args.args.clone();
let metaclass = args.get_kwarg("metaclass", vm.get_type());

bases.push(vm.context().object());
let metaclass = vm.get_type();
let namespace = vm.new_dict();
&vm.invoke(
function,
Expand All @@ -660,5 +648,18 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
kwargs: vec![],
},
);
objtype::new(metaclass, name, bases, namespace)

let bases = vm.context().new_tuple(bases);

// Special case: __new__ must be looked up on the metaclass, not the meta-metaclass as
// per vm.call(metaclass, "__new__", ...)
let new = metaclass.get_attr("__new__").unwrap();
let wrapped = vm.call_get_descriptor(new, metaclass)?;
vm.invoke(
wrapped,
PyFuncArgs {
args: vec![name_arg, bases, namespace],
kwargs: vec![],
},
)
}
33 changes: 29 additions & 4 deletions vm/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ impl Compiler {
name,
body,
bases,
keywords: _,
keywords,
decorator_list,
} => {
self.prepare_decorators(decorator_list)?;
Expand Down Expand Up @@ -483,9 +483,34 @@ impl Compiler {
for base in bases {
self.compile_expression(base)?;
}
self.emit(Instruction::CallFunction {
typ: CallType::Positional(2 + bases.len()),
});

if keywords.len() > 0 {
let mut kwarg_names = vec![];
for keyword in keywords {
if let Some(name) = &keyword.name {
kwarg_names.push(bytecode::Constant::String {
value: name.to_string(),
});
} else {
// This means **kwargs!
panic!("name must be set");
}
self.compile_expression(&keyword.value)?;
}

self.emit(Instruction::LoadConst {
value: bytecode::Constant::Tuple {
elements: kwarg_names,
},
});
self.emit(Instruction::CallFunction {
typ: CallType::Keyword(2 + keywords.len() + bases.len()),
});
} else {
self.emit(Instruction::CallFunction {
typ: CallType::Positional(2 + bases.len()),
});
}

self.apply_decorators(decorator_list);

Expand Down
6 changes: 3 additions & 3 deletions vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,8 @@ impl Frame {
&exception,
&vm.ctx.exceptions.base_exception_type
));
let traceback = vm
.get_attribute(exception.clone(), &"__traceback__".to_string())
.unwrap();
let traceback_name = vm.new_str("__traceback__".to_string());
let traceback = vm.get_attribute(exception.clone(), traceback_name).unwrap();
trace!("Adding to traceback: {:?} {:?}", traceback, lineno);
let pos = vm.ctx.new_tuple(vec![
vm.ctx.new_str(filename.clone()),
Expand Down Expand Up @@ -1007,6 +1006,7 @@ impl Frame {

fn load_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
let parent = self.pop_value();
let attr_name = vm.new_str(attr_name.to_string());
let obj = vm.get_attribute(parent, attr_name)?;
self.push_value(obj);
Ok(None)
Expand Down
2 changes: 1 addition & 1 deletion vm/src/obj/objbool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObje
PyObjectKind::String { ref value } => !value.is_empty(),
PyObjectKind::None { .. } => false,
_ => {
if let Ok(f) = objtype::get_attribute(vm, obj.clone(), &String::from("__bool__")) {
if let Ok(f) = vm.get_method(obj.clone(), "__bool__") {
let bool_res = vm.invoke(f, PyFuncArgs::default())?;
let v = match bool_res.borrow().kind {
PyObjectKind::Integer { ref value } => !value.is_zero(),
Expand Down
62 changes: 56 additions & 6 deletions vm/src/obj/objobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::super::pyobject::{
use super::super::vm::VirtualMachine;
use super::objbool;
use super::objdict;
use super::objstr;
use super::objtype;

pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
Expand All @@ -15,12 +16,6 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
Ok(obj)
}

pub fn call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
let instance = args.shift();
let function = objtype::get_attribute(vm, instance, &String::from("__call__"))?;
vm.invoke(function, args)
}

pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
(*object_type.borrow_mut()).kind = PyObjectKind::Class {
name: String::from("object"),
Expand Down Expand Up @@ -101,6 +96,10 @@ pub fn init(context: &PyContext) {
object.set_attr("__hash__", context.new_rustfunc(object_hash));
object.set_attr("__str__", context.new_rustfunc(object_str));
object.set_attr("__repr__", context.new_rustfunc(object_repr));
object.set_attr(
"__getattribute__",
context.new_rustfunc(object_getattribute),
);
}

fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
Expand All @@ -114,3 +113,54 @@ fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
_ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())),
}
}

fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [
(obj, Some(vm.ctx.object())),
(name_str, Some(vm.ctx.str_type()))
]
);
let name = objstr::get_value(&name_str);
trace!("object.__getattribute__({:?}, {:?})", obj, name);
let cls = obj.typ();

if let Some(attr) = cls.get_attr(&name) {
let attr_class = attr.typ();
if attr_class.has_attr("__set__") {
if let Some(descriptor) = attr_class.get_attr("__get__") {
return vm.invoke(
descriptor,
PyFuncArgs {
args: vec![attr, obj.clone(), cls],
kwargs: vec![],
},
);
}
}
}

if let Some(obj_attr) = obj.get_attr(&name) {
Ok(obj_attr)
} else if let Some(attr) = cls.get_attr(&name) {
vm.call_get_descriptor(attr, obj.clone())
} else {
if let Some(getter) = cls.get_attr("__getattr__") {
vm.invoke(
getter,
PyFuncArgs {
args: vec![cls, name_str.clone()],
kwargs: vec![],
},
)
} else {
let attribute_error = vm.context().exceptions.attribute_error.clone();
Err(vm.new_exception(
attribute_error,
format!("{:?} object has no attribute {}", cls, name),
))
}
}
}
Loading