Merge branch 'master' into store-rcs-in-stored-vm

This commit is contained in:
coolreader18
2019-03-05 17:18:43 -06:00
30 changed files with 1307 additions and 739 deletions

View File

@@ -97,7 +97,7 @@ $ pipenv install
$ pipenv run pytest -v
```
There also are some unit tests, you can run those will cargo:
There also are some unit tests, you can run those with cargo:
```shell
$ cargo test --all

View File

@@ -719,16 +719,13 @@ where
Some('+') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::PlusEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Plus, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::PlusEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Plus, tok_end)));
}
}
Some('*') => {
@@ -792,61 +789,49 @@ where
Some('%') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::PercentEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Percent, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::PercentEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Percent, tok_end)));
}
}
Some('|') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::VbarEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Vbar, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::VbarEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Vbar, tok_end)));
}
}
Some('^') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::CircumFlex, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::CircumFlex, tok_end)));
}
}
Some('&') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::AmperEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Amper, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::AmperEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::Amper, tok_end)));
}
}
Some('-') => {
@@ -872,16 +857,13 @@ where
Some('@') => {
let tok_start = self.get_pos();
self.next_char();
match self.chr0 {
Some('=') => {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::AtEqual, tok_end)));
}
_ => {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::At, tok_end)));
}
if let Some('=') = self.chr0 {
self.next_char();
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::AtEqual, tok_end)));
} else {
let tok_end = self.get_pos();
return Some(Ok((tok_start, Tok::At, tok_end)));
}
}
Some('!') => {

View File

@@ -1,6 +1,10 @@
import socket
from testutils import assertRaises
MESSAGE_A = b'aaaa'
MESSAGE_B= b'bbbbb'
# TCP
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(("127.0.0.1", 0))
@@ -8,18 +12,15 @@ listener.listen(1)
connector = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connector.connect(("127.0.0.1", listener.getsockname()[1]))
connection = listener.accept()[0]
message_a = b'aaaa'
message_b = b'bbbbb'
connector.send(message_a)
connection.send(message_b)
recv_a = connection.recv(len(message_a))
recv_b = connector.recv(len(message_b))
assert recv_a == message_a
assert recv_b == message_b
(connection, addr) = listener.accept()
assert addr == connector.getsockname()
connector.send(MESSAGE_A)
connection.send(MESSAGE_B)
recv_a = connection.recv(len(MESSAGE_A))
recv_b = connector.recv(len(MESSAGE_B))
assert recv_a == MESSAGE_A
assert recv_b == MESSAGE_B
connection.close()
connector.close()
listener.close()
@@ -35,3 +36,40 @@ with assertRaises(TypeError):
s.bind((888, 8888))
s.close()
# UDP
sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock1.bind(("127.0.0.1", 0))
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock2.sendto(MESSAGE_A, sock1.getsockname())
(recv_a, addr1) = sock1.recvfrom(len(MESSAGE_A))
assert recv_a == MESSAGE_A
sock2.sendto(MESSAGE_B, sock1.getsockname())
(recv_b, addr2) = sock1.recvfrom(len(MESSAGE_B))
assert recv_b == MESSAGE_B
assert addr1[0] == addr2[0]
assert addr1[1] == addr2[1]
sock2.close()
sock3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock3.bind(("127.0.0.1", 0))
sock3.sendto(MESSAGE_A, sock1.getsockname())
(recv_a, addr) = sock1.recvfrom(len(MESSAGE_A))
assert recv_a == MESSAGE_A
assert addr == sock3.getsockname()
sock1.connect(("127.0.0.1", sock3.getsockname()[1]))
sock3.connect(("127.0.0.1", sock1.getsockname()[1]))
sock1.send(MESSAGE_A)
sock3.send(MESSAGE_B)
recv_a = sock3.recv(len(MESSAGE_A))
recv_b = sock1.recv(len(MESSAGE_B))
assert recv_a == MESSAGE_A
assert recv_b == MESSAGE_B
sock1.close()
sock3.close()

View File

@@ -0,0 +1,42 @@
exec("def square(x):\n return x * x\n")
assert 16 == square(4)
d = {}
exec("def square(x):\n return x * x\n", {}, d)
assert 16 == d['square'](4)
exec("assert 2 == x", {}, {'x': 2})
exec("assert 2 == x", {'x': 2}, {})
exec("assert 4 == x", {'x': 2}, {'x': 4})
exec("assert max(1, 2) == 2", {}, {})
exec("assert max(1, 5, square(5)) == 25", None)
#
# These doesn't work yet:
#
# Local environment shouldn't replace global environment:
#
# exec("assert max(1, 5, square(5)) == 25", None, {})
#
# Closures aren't available if local scope is replaced:
#
# def g():
# seven = "seven"
# def f():
# try:
# exec("seven", None, {})
# except NameError:
# pass
# else:
# raise NameError("seven shouldn't be in scope")
# f()
# g()
try:
exec("", 1)
except TypeError:
pass
else:
raise TypeError("exec should fail unless globals is a dict or None")

View File

@@ -18,7 +18,6 @@ use crate::frame::{Scope, ScopeRef};
use crate::pyobject::{
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
};
use std::rc::Rc;
#[cfg(not(target_arch = "wasm32"))]
use crate::stdlib::io::io_open;
@@ -191,12 +190,11 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vm,
args,
required = [(source, None)],
optional = [
(_globals, Some(vm.ctx.dict_type())),
(locals, Some(vm.ctx.dict_type()))
]
optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))]
);
let scope = make_scope(vm, globals, locals)?;
// Determine code object:
let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) {
source.clone()
@@ -215,8 +213,6 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
return Err(vm.new_type_error("code argument must be str or code object".to_string()));
};
let scope = make_scope(vm, locals);
// Run the source:
vm.run_code_obj(code_obj.clone(), scope)
}
@@ -228,12 +224,11 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vm,
args,
required = [(source, None)],
optional = [
(_globals, Some(vm.ctx.dict_type())),
(locals, Some(vm.ctx.dict_type()))
]
optional = [(globals, None), (locals, Some(vm.ctx.dict_type()))]
);
let scope = make_scope(vm, globals, locals)?;
// Determine code object:
let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) {
let mode = compile::Mode::Exec;
@@ -252,26 +247,48 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
return Err(vm.new_type_error("source argument must be str or code object".to_string()));
};
let scope = make_scope(vm, locals);
// Run the code:
vm.run_code_obj(code_obj, scope)
}
fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> ScopeRef {
// handle optional global and locals
let locals = if let Some(locals) = locals {
locals.clone()
} else {
vm.new_dict()
fn make_scope(
vm: &mut VirtualMachine,
globals: Option<&PyObjectRef>,
locals: Option<&PyObjectRef>,
) -> PyResult<ScopeRef> {
let dict_type = vm.ctx.dict_type();
let globals = match globals {
Some(arg) => {
if arg.is(&vm.get_none()) {
None
} else {
if vm.isinstance(arg, &dict_type)? {
Some(arg)
} else {
let arg_typ = arg.typ();
let actual_type = vm.to_pystr(&arg_typ)?;
let expected_type_name = vm.to_pystr(&dict_type)?;
return Err(vm.new_type_error(format!(
"globals must be a {}, not {}",
expected_type_name, actual_type
)));
}
}
}
None => None,
};
// TODO: handle optional globals
// Construct new scope:
Rc::new(Scope {
locals,
parent: None,
})
let current_scope = vm.current_scope();
let parent = match globals {
Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))),
None => current_scope.parent.clone(),
};
let locals = match locals {
Some(dict) => dict.clone(),
None => current_scope.locals.clone(),
};
Ok(Scope::new(locals, parent))
}
fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -347,7 +364,7 @@ fn builtin_isinstance(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
required = [(obj, None), (typ, Some(vm.get_type()))]
);
let isinstance = objtype::real_isinstance(vm, obj, typ)?;
let isinstance = vm.isinstance(obj, typ)?;
Ok(vm.new_bool(isinstance))
}
@@ -358,7 +375,7 @@ fn builtin_issubclass(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
required = [(subclass, Some(vm.get_type())), (cls, Some(vm.get_type()))]
);
let issubclass = objtype::real_issubclass(vm, subclass, cls)?;
let issubclass = vm.issubclass(subclass, cls)?;
Ok(vm.context().new_bool(issubclass))
}
@@ -814,7 +831,7 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
let mut metaclass = args.get_kwarg("metaclass", vm.get_type());
for base in bases.clone() {
if objtype::real_issubclass(vm, &base.typ(), &metaclass)? {
if objtype::issubclass(&base.typ(), &metaclass) {
metaclass = base.typ();
} else if !objtype::issubclass(&metaclass, &base.typ()) {
return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string()));

View File

@@ -66,11 +66,7 @@ impl Dict {
}
}
pub fn contains(
&self,
vm: &mut VirtualMachine,
key: &PyObjectRef,
) -> PyResult<bool> {
pub fn contains(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult<bool> {
if let LookupResult::Existing(_index) = self.lookup(vm, key)? {
Ok(true)
} else {
@@ -93,11 +89,7 @@ impl Dict {
}
/// Delete a key
pub fn delete(
&mut self,
vm: &mut VirtualMachine,
key: &PyObjectRef,
) -> PyResult<()> {
pub fn delete(&mut self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult<()> {
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
self.entries[index] = None;
self.size -= 1;
@@ -126,11 +118,7 @@ impl Dict {
}
/// Lookup the index for the given key.
fn lookup(
&self,
vm: &mut VirtualMachine,
key: &PyObjectRef,
) -> PyResult<LookupResult> {
fn lookup(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult<LookupResult> {
let hash_value = calc_hash(vm, key)?;
let perturb = hash_value;
let mut hash_index: usize = hash_value;

View File

@@ -35,6 +35,12 @@ pub struct Scope {
}
pub type ScopeRef = Rc<Scope>;
impl Scope {
pub fn new(locals: PyObjectRef, parent: Option<ScopeRef>) -> ScopeRef {
Rc::new(Scope { locals, parent })
}
}
#[derive(Clone, Debug)]
struct Block {
/// The type of block.

83
vm/src/function.rs Normal file
View File

@@ -0,0 +1,83 @@
use std::marker::PhantomData;
use std::ops::Deref;
use crate::obj::objtype;
use crate::pyobject::{
IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TryFromObject, TypeProtocol,
};
use crate::vm::VirtualMachine;
// TODO: Move PyFuncArgs, FromArgs, etc. here
// TODO: `PyRef` probably actually belongs in the pyobject module.
/// A reference to the payload of a built-in object.
///
/// Note that a `PyRef<T>` can only deref to a shared / immutable reference.
/// It is the payload type's responsibility to handle (possibly concurrent)
/// mutability with locks or concurrent data structures if required.
///
/// A `PyRef<T>` can be directly returned from a built-in function to handle
/// situations (such as when implementing in-place methods such as `__iadd__`)
/// where a reference to the same object must be returned.
pub struct PyRef<T> {
// invariant: this obj must always have payload of type T
obj: PyObjectRef,
_payload: PhantomData<T>,
}
impl<T> PyRef<T>
where
T: PyObjectPayload2,
{
pub fn new(ctx: &PyContext, payload: T) -> Self {
PyRef {
obj: PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(payload),
},
T::required_type(ctx),
),
_payload: PhantomData,
}
}
}
impl<T> Deref for PyRef<T>
where
T: PyObjectPayload2,
{
type Target = T;
fn deref(&self) -> &T {
self.obj.payload().expect("unexpected payload for type")
}
}
impl<T> TryFromObject for PyRef<T>
where
T: PyObjectPayload2,
{
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if objtype::isinstance(&obj, &T::required_type(&vm.ctx)) {
Ok(PyRef {
obj,
_payload: PhantomData,
})
} else {
let expected_type = vm.to_pystr(&T::required_type(&vm.ctx))?;
let actual_type = vm.to_pystr(&obj.typ())?;
Err(vm.new_type_error(format!(
"Expected type {}, not {}",
expected_type, actual_type,
)))
}
}
}
impl<T> IntoPyObject for PyRef<T> {
fn into_pyobject(self, _ctx: &PyContext) -> PyResult {
Ok(self.obj)
}
}

View File

@@ -41,6 +41,7 @@ pub mod eval;
mod exceptions;
pub mod format;
pub mod frame;
pub mod function;
pub mod import;
pub mod obj;
pub mod pyobject;

View File

@@ -19,7 +19,7 @@ macro_rules! type_check {
if let Some(expected_type) = $arg_type {
let arg = &$args.args[$arg_count];
if !$crate::obj::objtype::real_isinstance($vm, arg, &expected_type)? {
if !$crate::obj::objtype::isinstance(arg, &expected_type) {
let arg_typ = arg.typ();
let expected_type_name = $vm.to_pystr(&expected_type)?;
let actual_type = $vm.to_pystr(&arg_typ)?;
@@ -124,3 +124,16 @@ macro_rules! py_module {
}
}
}
#[macro_export]
macro_rules! py_class {
( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:expr => $value:expr),* $(,)* }) => {
{
let py_class = $ctx.new_class($class_name, $class_base);
$(
$ctx.set_attr(&py_class, $name, $value);
)*
py_class
}
}
}

View File

@@ -1,3 +1,5 @@
use super::objfloat::PyFloat;
use super::objstr::PyString;
use super::objtype;
use crate::pyobject::{
IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
@@ -12,12 +14,16 @@ impl IntoPyObject for bool {
}
pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObjectRef> {
if let Some(s) = obj.payload::<PyString>() {
return Ok(!s.value.is_empty());
}
if let Some(value) = obj.payload::<PyFloat>() {
return Ok(*value != PyFloat::from(0.0));
}
let result = match obj.payload {
PyObjectPayload::Integer { ref value } => !value.is_zero(),
PyObjectPayload::Float { value } => value != 0.0,
PyObjectPayload::Sequence { ref elements } => !elements.borrow().is_empty(),
PyObjectPayload::Dict { ref elements } => !elements.borrow().is_empty(),
PyObjectPayload::String { ref value } => !value.is_empty(),
PyObjectPayload::None { .. } => false,
_ => {
if let Ok(f) = vm.get_method(obj.clone(), "__bool__") {

View File

@@ -1,17 +1,47 @@
//! Implementation of the python bytearray object.
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use crate::pyobject::{PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyResult, TypeProtocol};
use crate::pyobject::{
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TypeProtocol,
};
use super::objint;
use super::objbytes::get_mut_value;
use super::objbytes::get_value;
use super::objtype;
use crate::vm::VirtualMachine;
use num_traits::ToPrimitive;
#[derive(Debug)]
pub struct PyByteArray {
// TODO: shouldn't be public
pub value: RefCell<Vec<u8>>,
}
impl PyByteArray {
pub fn new(data: Vec<u8>) -> Self {
PyByteArray {
value: RefCell::new(data),
}
}
}
impl PyObjectPayload2 for PyByteArray {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.bytearray_type()
}
}
pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<u8>> + 'a {
obj.payload::<PyByteArray>().unwrap().value.borrow()
}
pub fn get_mut_value<'a>(obj: &'a PyObjectRef) -> impl DerefMut<Target = Vec<u8>> + 'a {
obj.payload::<PyByteArray>().unwrap().value.borrow_mut()
}
// Binary data support
/// Fill bytearray class methods dictionary.
@@ -143,8 +173,8 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vec![]
};
Ok(PyObject::new(
PyObjectPayload::Bytes {
value: RefCell::new(value),
PyObjectPayload::AnyRustValue {
value: Box::new(PyByteArray::new(value)),
},
cls.clone(),
))
@@ -290,13 +320,8 @@ fn bytearray_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn bytearray_clear(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
match zelf.payload {
PyObjectPayload::Bytes { ref value } => {
value.borrow_mut().clear();
Ok(vm.get_none())
}
_ => panic!("Bytearray has incorrect payload."),
}
get_mut_value(zelf).clear();
Ok(vm.get_none())
}
fn bytearray_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

View File

@@ -1,16 +1,41 @@
use std::cell::{Cell, RefCell};
use std::cell::Cell;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ops::DerefMut;
use super::objint;
use super::objtype;
use crate::pyobject::{
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TypeProtocol,
};
use crate::vm::VirtualMachine;
use num_traits::ToPrimitive;
#[derive(Debug)]
pub struct PyBytes {
value: Vec<u8>,
}
impl PyBytes {
pub fn new(data: Vec<u8>) -> Self {
PyBytes { value: data }
}
}
impl Deref for PyBytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.value
}
}
impl PyObjectPayload2 for PyBytes {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.bytes_type()
}
}
// Binary data support
// Fill bytes class methods:
@@ -71,8 +96,8 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
Ok(PyObject::new(
PyObjectPayload::Bytes {
value: RefCell::new(value),
PyObjectPayload::AnyRustValue {
value: Box::new(PyBytes::new(value)),
},
cls.clone(),
))
@@ -170,19 +195,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<u8>> + 'a {
if let PyObjectPayload::Bytes { ref value } = obj.payload {
value.borrow()
} else {
panic!("Inner error getting bytearray {:?}", obj);
}
}
pub fn get_mut_value<'a>(obj: &'a PyObjectRef) -> impl DerefMut<Target = Vec<u8>> + 'a {
if let PyObjectPayload::Bytes { ref value } = obj.payload {
value.borrow_mut()
} else {
panic!("Inner error getting bytearray {:?}", obj);
}
&obj.payload::<PyBytes>().unwrap().value
}
fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

View File

@@ -55,10 +55,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.new_str(repr))
}
fn member_code_obj(
vm: &mut VirtualMachine,
args: PyFuncArgs,
) -> PyResult<bytecode::CodeObject> {
fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult<bytecode::CodeObject> {
arg_check!(
vm,
args,

View File

@@ -2,12 +2,30 @@ use super::objfloat;
use super::objint;
use super::objtype;
use crate::pyobject::{
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TypeProtocol,
};
use crate::vm::VirtualMachine;
use num_complex::Complex64;
use num_traits::ToPrimitive;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PyComplex {
value: Complex64,
}
impl PyObjectPayload2 for PyComplex {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.complex_type()
}
}
impl From<Complex64> for PyComplex {
fn from(value: Complex64) -> Self {
PyComplex { value }
}
}
pub fn init(context: &PyContext) {
let complex_type = &context.complex_type;
@@ -45,11 +63,7 @@ pub fn init(context: &PyContext) {
}
pub fn get_value(obj: &PyObjectRef) -> Complex64 {
if let PyObjectPayload::Complex { value } = &obj.payload {
*value
} else {
panic!("Inner error getting complex");
}
obj.payload::<PyComplex>().unwrap().value
}
fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -77,7 +91,9 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let value = Complex64::new(real, imag);
Ok(PyObject::new(
PyObjectPayload::Complex { value },
PyObjectPayload::AnyRustValue {
value: Box::new(PyComplex { value }),
},
cls.clone(),
))
}

View File

@@ -3,12 +3,30 @@ use super::objint;
use super::objstr;
use super::objtype;
use crate::pyobject::{
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TypeProtocol,
};
use crate::vm::VirtualMachine;
use num_bigint::ToBigInt;
use num_traits::ToPrimitive;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PyFloat {
value: f64,
}
impl PyObjectPayload2 for PyFloat {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.float_type()
}
}
impl From<f64> for PyFloat {
fn from(value: f64) -> Self {
PyFloat { value }
}
}
fn float_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(float, Some(vm.ctx.float_type()))]);
let v = get_value(float);
@@ -50,16 +68,18 @@ fn float_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let type_name = objtype::get_type_name(&arg.typ());
return Err(vm.new_type_error(format!("can't convert {} to float", type_name)));
};
Ok(PyObject::new(PyObjectPayload::Float { value }, cls.clone()))
Ok(PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(PyFloat { value }),
},
cls.clone(),
))
}
// Retrieve inner float value:
pub fn get_value(obj: &PyObjectRef) -> f64 {
if let PyObjectPayload::Float { value } = &obj.payload {
*value
} else {
panic!("Inner error getting float: {}", obj);
}
obj.payload::<PyFloat>().unwrap().value
}
pub fn make_float(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult<f64> {

View File

@@ -4,7 +4,7 @@ use super::objtype;
use crate::format::FormatSpec;
use crate::pyobject::{
FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
PyResult, TypeProtocol,
PyResult, TryFromObject, TypeProtocol,
};
use crate::vm::VirtualMachine;
use num_bigint::{BigInt, ToBigInt};
@@ -31,6 +31,26 @@ impl IntoPyObject for usize {
}
}
impl TryFromObject for usize {
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
// FIXME: don't use get_value
match get_value(&obj).to_usize() {
Some(value) => Ok(value),
None => Err(vm.new_overflow_error("Int value cannot fit into Rust usize".to_string())),
}
}
}
impl TryFromObject for isize {
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
// FIXME: don't use get_value
match get_value(&obj).to_isize() {
Some(value) => Ok(value),
None => Err(vm.new_overflow_error("Int value cannot fit into Rust isize".to_string())),
}
}
}
fn int_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(int, Some(vm.ctx.int_type()))]);
let v = get_value(int);
@@ -63,11 +83,7 @@ fn int_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
// Casting function:
pub fn to_int(
vm: &mut VirtualMachine,
obj: &PyObjectRef,
base: u32,
) -> PyResult<IntType> {
pub fn to_int(vm: &mut VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<IntType> {
let val = if objtype::isinstance(obj, &vm.ctx.int_type()) {
get_value(obj)
} else if objtype::isinstance(obj, &vm.ctx.float_type()) {

View File

@@ -2,13 +2,16 @@
* Various types to support iteration.
*/
use super::objbool;
use crate::pyobject::{
PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
};
use crate::vm::VirtualMachine;
// use super::objstr;
use super::objtype; // Required for arg_check! to use isinstance
use super::objbool;
use super::objbytearray::PyByteArray;
use super::objbytes::PyBytes;
use super::objrange::PyRange;
use super::objtype;
/*
* This helper function is called at multiple places. First, it is called
@@ -129,38 +132,43 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
iterated_obj: ref iterated_obj_ref,
} = iter.payload
{
match iterated_obj_ref.payload {
PyObjectPayload::Sequence { ref elements } => {
if position.get() < elements.borrow().len() {
let obj_ref = elements.borrow()[position.get()].clone();
position.set(position.get() + 1);
Ok(obj_ref)
} else {
Err(new_stop_iteration(vm))
}
if let Some(range) = iterated_obj_ref.payload::<PyRange>() {
if let Some(int) = range.get(position.get()) {
position.set(position.get() + 1);
Ok(vm.ctx.new_int(int))
} else {
Err(new_stop_iteration(vm))
}
PyObjectPayload::Range { ref range } => {
if let Some(int) = range.get(position.get()) {
position.set(position.get() + 1);
Ok(vm.ctx.new_int(int))
} else {
Err(new_stop_iteration(vm))
}
} else if let Some(bytes) = iterated_obj_ref.payload::<PyBytes>() {
if position.get() < bytes.len() {
let obj_ref = vm.ctx.new_int(bytes[position.get()]);
position.set(position.get() + 1);
Ok(obj_ref)
} else {
Err(new_stop_iteration(vm))
}
PyObjectPayload::Bytes { ref value } => {
if position.get() < value.borrow().len() {
let obj_ref = vm.ctx.new_int(value.borrow()[position.get()]);
position.set(position.get() + 1);
Ok(obj_ref)
} else {
Err(new_stop_iteration(vm))
}
} else if let Some(bytes) = iterated_obj_ref.payload::<PyByteArray>() {
if position.get() < bytes.value.borrow().len() {
let obj_ref = vm.ctx.new_int(bytes.value.borrow()[position.get()]);
position.set(position.get() + 1);
Ok(obj_ref)
} else {
Err(new_stop_iteration(vm))
}
_ => {
panic!("NOT IMPL");
} else {
match iterated_obj_ref.payload {
PyObjectPayload::Sequence { ref elements } => {
if position.get() < elements.borrow().len() {
let obj_ref = elements.borrow()[position.get()].clone();
position.set(position.get() + 1);
Ok(obj_ref)
} else {
Err(new_stop_iteration(vm))
}
}
_ => {
panic!("NOT IMPL");
}
}
}
} else {

View File

@@ -4,7 +4,7 @@ use std::ops::Mul;
use super::objint;
use super::objtype;
use crate::pyobject::{
FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
TypeProtocol,
};
use crate::vm::VirtualMachine;
@@ -13,7 +13,7 @@ use num_integer::Integer;
use num_traits::{One, Signed, ToPrimitive, Zero};
#[derive(Debug, Clone)]
pub struct RangeType {
pub struct PyRange {
// Unfortunately Rust's built in range type doesn't support things like indexing
// or ranges where start > end so we need to roll our own.
pub start: BigInt,
@@ -21,19 +21,13 @@ pub struct RangeType {
pub step: BigInt,
}
type PyRange = RangeType;
impl FromPyObject for PyRange {
fn typ(ctx: &PyContext) -> Option<PyObjectRef> {
Some(ctx.range_type())
}
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self> {
Ok(get_value(&obj))
impl PyObjectPayload2 for PyRange {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.range_type()
}
}
impl RangeType {
impl PyRange {
#[inline]
pub fn try_len(&self) -> Option<usize> {
match self.step.sign() {
@@ -129,12 +123,12 @@ impl RangeType {
};
match self.step.sign() {
Sign::Plus => RangeType {
Sign::Plus => PyRange {
start,
end: &self.start - 1,
step: -&self.step,
},
Sign::Minus => RangeType {
Sign::Minus => PyRange {
start,
end: &self.start + 1,
step: -&self.step,
@@ -152,12 +146,8 @@ impl RangeType {
}
}
pub fn get_value(obj: &PyObjectRef) -> RangeType {
if let PyObjectPayload::Range { range } = &obj.payload {
range.clone()
} else {
panic!("Inner error getting range {:?}", obj);
}
pub fn get_value(obj: &PyObjectRef) -> PyRange {
obj.payload::<PyRange>().unwrap().clone()
}
pub fn init(context: &PyContext) {
@@ -236,8 +226,8 @@ fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Err(vm.new_value_error("range with 0 step size".to_string()))
} else {
Ok(PyObject::new(
PyObjectPayload::Range {
range: RangeType { start, end, step },
PyObjectPayload::AnyRustValue {
value: Box::new(PyRange { start, end, step }),
},
cls.clone(),
))
@@ -264,7 +254,12 @@ fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(PyObject::new(
PyObjectPayload::Iterator {
position: Cell::new(0),
iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()),
iterated_obj: PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(range),
},
vm.ctx.range_type(),
),
},
vm.ctx.iter_type(),
))
@@ -329,12 +324,12 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
Ok(PyObject::new(
PyObjectPayload::Range {
range: RangeType {
PyObjectPayload::AnyRustValue {
value: Box::new(PyRange {
start: new_start,
end: new_end,
step: new_step,
},
}),
},
vm.ctx.range_type(),
))
@@ -360,12 +355,22 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_bool(len > 0))
}
fn range_contains(vm: &mut VirtualMachine, zelf: PyRange, needle: PyObjectRef) -> bool {
if objtype::isinstance(&needle, &vm.ctx.int_type()) {
zelf.contains(&objint::get_value(&needle))
fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(zelf, Some(vm.ctx.range_type())), (needle, None)]
);
let range = get_value(zelf);
let result = if objtype::isinstance(needle, &vm.ctx.int_type()) {
range.contains(&objint::get_value(needle))
} else {
false
}
};
Ok(vm.ctx.new_bool(result))
}
fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

View File

@@ -2,8 +2,10 @@ use super::objint;
use super::objsequence::PySliceableSequence;
use super::objtype;
use crate::format::{FormatParseError, FormatPart, FormatString};
use crate::function::PyRef;
use crate::pyobject::{
FromPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
OptArg, PyContext, PyFuncArgs, PyIterable, PyObjectPayload, PyObjectPayload2, PyObjectRef,
PyResult, TypeProtocol,
};
use crate::vm::VirtualMachine;
use num_traits::ToPrimitive;
@@ -16,13 +18,71 @@ extern crate unicode_segmentation;
use self::unicode_segmentation::UnicodeSegmentation;
impl FromPyObject for String {
fn typ(ctx: &PyContext) -> Option<PyObjectRef> {
Some(ctx.str_type())
#[derive(Clone, Debug)]
pub struct PyString {
// TODO: shouldn't be public
pub value: String,
}
impl PyString {
pub fn endswith(
zelf: PyRef<Self>,
suffix: PyRef<Self>,
start: OptArg<usize>,
end: OptArg<usize>,
_vm: &mut VirtualMachine,
) -> bool {
let start = start.unwrap_or(0);
let end = end.unwrap_or(zelf.value.len());
zelf.value[start..end].ends_with(&suffix.value)
}
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self> {
Ok(get_value(&obj))
pub fn startswith(
zelf: PyRef<Self>,
prefix: PyRef<Self>,
start: OptArg<usize>,
end: OptArg<usize>,
_vm: &mut VirtualMachine,
) -> bool {
let start = start.unwrap_or(0);
let end = end.unwrap_or(zelf.value.len());
zelf.value[start..end].starts_with(&prefix.value)
}
fn upper(zelf: PyRef<Self>, _vm: &mut VirtualMachine) -> PyString {
PyString {
value: zelf.value.to_uppercase(),
}
}
fn lower(zelf: PyRef<Self>, _vm: &mut VirtualMachine) -> PyString {
PyString {
value: zelf.value.to_lowercase(),
}
}
fn join(
zelf: PyRef<Self>,
iterable: PyIterable<PyRef<Self>>,
vm: &mut VirtualMachine,
) -> PyResult<PyString> {
let mut joined = String::new();
for (idx, elem) in iterable.iter(vm)?.enumerate() {
let elem = elem?;
if idx != 0 {
joined.push_str(&zelf.value);
}
joined.push_str(&elem.value)
}
Ok(PyString { value: joined })
}
}
impl PyObjectPayload2 for PyString {
fn required_type(ctx: &PyContext) -> PyObjectRef {
ctx.str_type()
}
}
@@ -47,9 +107,9 @@ pub fn init(context: &PyContext) {
context.set_attr(&str_type, "__str__", context.new_rustfunc(str_str));
context.set_attr(&str_type, "__repr__", context.new_rustfunc(str_repr));
context.set_attr(&str_type, "format", context.new_rustfunc(str_format));
context.set_attr(&str_type, "lower", context.new_rustfunc(str_lower));
context.set_attr(&str_type, "lower", context.new_rustfunc(PyString::lower));
context.set_attr(&str_type, "casefold", context.new_rustfunc(str_casefold));
context.set_attr(&str_type, "upper", context.new_rustfunc(str_upper));
context.set_attr(&str_type, "upper", context.new_rustfunc(PyString::upper));
context.set_attr(
&str_type,
"capitalize",
@@ -60,11 +120,15 @@ pub fn init(context: &PyContext) {
context.set_attr(&str_type, "strip", context.new_rustfunc(str_strip));
context.set_attr(&str_type, "lstrip", context.new_rustfunc(str_lstrip));
context.set_attr(&str_type, "rstrip", context.new_rustfunc(str_rstrip));
context.set_attr(&str_type, "endswith", context.new_rustfunc(str_endswith));
context.set_attr(
&str_type,
"endswith",
context.new_rustfunc(PyString::endswith),
);
context.set_attr(
&str_type,
"startswith",
context.new_rustfunc(str_startswith),
context.new_rustfunc(PyString::startswith),
);
context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum));
context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric));
@@ -84,7 +148,7 @@ pub fn init(context: &PyContext) {
"splitlines",
context.new_rustfunc(str_splitlines),
);
context.set_attr(&str_type, "join", context.new_rustfunc(str_join));
context.set_attr(&str_type, "join", context.new_rustfunc(PyString::join));
context.set_attr(&str_type, "find", context.new_rustfunc(str_find));
context.set_attr(&str_type, "rfind", context.new_rustfunc(str_rfind));
context.set_attr(&str_type, "index", context.new_rustfunc(str_index));
@@ -113,19 +177,11 @@ pub fn init(context: &PyContext) {
}
pub fn get_value(obj: &PyObjectRef) -> String {
if let PyObjectPayload::String { value } = &obj.payload {
value.to_string()
} else {
panic!("Inner error getting str");
}
obj.payload::<PyString>().unwrap().value.clone()
}
pub fn borrow_value(obj: &PyObjectRef) -> &str {
if let PyObjectPayload::String { value } = &obj.payload {
value.as_str()
} else {
panic!("Inner error getting str");
}
&obj.payload::<PyString>().unwrap().value
}
fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -387,18 +443,6 @@ fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn str_upper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
let value = get_value(&s).to_uppercase();
Ok(vm.ctx.new_str(value))
}
fn str_lower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
let value = get_value(&s).to_lowercase();
Ok(vm.ctx.new_str(value))
}
fn str_capitalize(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
let value = get_value(&s);
@@ -477,10 +521,6 @@ fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_str(value))
}
fn str_endswith(_vm: &mut VirtualMachine, zelf: String, suffix: String) -> bool {
zelf.ends_with(&suffix)
}
fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
let value = get_value(&s);
@@ -560,22 +600,6 @@ fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_str(new_str))
}
fn str_join(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(s, Some(vm.ctx.str_type())), (iterable, None)]
);
let value = get_value(&s);
let elements: Vec<String> = vm
.extract_elements(iterable)?
.iter()
.map(|w| get_value(&w))
.collect();
let joined = elements.join(&value);
Ok(vm.ctx.new_str(joined))
}
fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -865,17 +889,6 @@ fn str_center(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_str(new_str))
}
fn str_startswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(s, Some(vm.ctx.str_type())), (pat, Some(vm.ctx.str_type()))]
);
let value = get_value(&s);
let pat = get_value(&pat);
Ok(vm.ctx.new_bool(value.starts_with(pat.as_str())))
}
fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,

View File

@@ -72,9 +72,7 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
// Check obj type:
if !(objtype::real_isinstance(vm, &py_obj, &py_type)?
|| objtype::real_issubclass(vm, &py_obj, &py_type)?)
{
if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) {
return Err(vm.new_type_error(
"super(type, obj): obj must be an instance or subtype of type".to_string(),
));

View File

@@ -1,4 +1,3 @@
use super::objbool;
use super::objdict;
use super::objstr;
use super::objtype; // Required for arg_check! to use isinstance
@@ -9,7 +8,6 @@ use crate::pyobject::{
use crate::vm::VirtualMachine;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
/*
* The magical type type
@@ -108,23 +106,6 @@ pub fn isinstance(obj: &PyObjectRef, cls: &PyObjectRef) -> bool {
mro.into_iter().any(|c| c.is(&cls))
}
/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via the
/// __instancecheck__ magic method.
pub fn real_isinstance(
vm: &mut VirtualMachine,
obj: &PyObjectRef,
cls: &PyObjectRef,
) -> PyResult<bool> {
// cpython first does an exact check on the type, although documentation doesn't state that
// https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408
if Rc::ptr_eq(&obj.typ(), cls) {
Ok(true)
} else {
let ret = vm.call_method(cls, "__instancecheck__", vec![obj.clone()])?;
objbool::boolval(vm, ret)
}
}
fn type_instance_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -142,17 +123,6 @@ pub fn issubclass(subclass: &PyObjectRef, cls: &PyObjectRef) -> bool {
mro.into_iter().any(|c| c.is(&cls))
}
/// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually via
/// the __subclasscheck__ magic method.
pub fn real_issubclass(
vm: &mut VirtualMachine,
subclass: &PyObjectRef,
cls: &PyObjectRef,
) -> PyResult<bool> {
let ret = vm.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?;
objbool::boolval(vm, ret)
}
fn type_subclass_check(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,

View File

@@ -1,3 +1,15 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::iter;
use std::ops::RangeInclusive;
use std::rc::{Rc, Weak};
use num_bigint::BigInt;
use num_bigint::ToBigInt;
use num_complex::Complex64;
use num_traits::{One, Zero};
use crate::bytecode;
use crate::exceptions;
use crate::frame::{Frame, Scope, ScopeRef};
@@ -5,11 +17,11 @@ use crate::obj::objbool;
use crate::obj::objbytearray;
use crate::obj::objbytes;
use crate::obj::objcode;
use crate::obj::objcomplex;
use crate::obj::objcomplex::{self, PyComplex};
use crate::obj::objdict;
use crate::obj::objenumerate;
use crate::obj::objfilter;
use crate::obj::objfloat;
use crate::obj::objfloat::{self, PyFloat};
use crate::obj::objframe;
use crate::obj::objfunction;
use crate::obj::objgenerator;
@@ -30,14 +42,6 @@ use crate::obj::objtuple;
use crate::obj::objtype;
use crate::obj::objzip;
use crate::vm::VirtualMachine;
use num_bigint::BigInt;
use num_bigint::ToBigInt;
use num_complex::Complex64;
use num_traits::{One, Zero};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::rc::{Rc, Weak};
/* Python objects and references.
@@ -457,22 +461,37 @@ impl PyContext {
)
}
pub fn new_float(&self, i: f64) -> PyObjectRef {
PyObject::new(PyObjectPayload::Float { value: i }, self.float_type())
pub fn new_float(&self, value: f64) -> PyObjectRef {
PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(PyFloat::from(value)),
},
self.float_type(),
)
}
pub fn new_complex(&self, i: Complex64) -> PyObjectRef {
PyObject::new(PyObjectPayload::Complex { value: i }, self.complex_type())
pub fn new_complex(&self, value: Complex64) -> PyObjectRef {
PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(PyComplex::from(value)),
},
self.complex_type(),
)
}
pub fn new_str(&self, s: String) -> PyObjectRef {
PyObject::new(PyObjectPayload::String { value: s }, self.str_type())
PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(objstr::PyString { value: s }),
},
self.str_type(),
)
}
pub fn new_bytes(&self, data: Vec<u8>) -> PyObjectRef {
PyObject::new(
PyObjectPayload::Bytes {
value: RefCell::new(data),
PyObjectPayload::AnyRustValue {
value: Box::new(objbytes::PyBytes::new(data)),
},
self.bytes_type(),
)
@@ -480,8 +499,8 @@ impl PyContext {
pub fn new_bytearray(&self, data: Vec<u8>) -> PyObjectRef {
PyObject::new(
PyObjectPayload::Bytes {
value: RefCell::new(data),
PyObjectPayload::AnyRustValue {
value: Box::new(objbytearray::PyByteArray::new(data)),
},
self.bytearray_type(),
)
@@ -552,28 +571,18 @@ impl PyContext {
)
}
pub fn new_rustfunc<F, T, R>(&self, factory: F) -> PyObjectRef
pub fn new_rustfunc<F, T, R>(&self, f: F) -> PyObjectRef
where
F: PyNativeFuncFactory<T, R>,
F: IntoPyNativeFunc<T, R>,
{
PyObject::new(
PyObjectPayload::RustFunction {
function: factory.create(self),
function: f.into_func(),
},
self.builtin_function_or_method_type(),
)
}
pub fn new_rustfunc_from_box(
&self,
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
) -> PyObjectRef {
PyObject::new(
PyObjectPayload::RustFunction { function },
self.builtin_function_or_method_type(),
)
}
pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef {
PyObject::new(
PyObjectPayload::Frame {
@@ -925,7 +934,7 @@ impl PyFuncArgs {
) -> Result<Option<PyObjectRef>, PyObjectRef> {
match self.get_optional_kwarg(key) {
Some(kwarg) => {
if objtype::real_isinstance(vm, &kwarg, &ty)? {
if objtype::isinstance(&kwarg, &ty) {
Ok(Some(kwarg))
} else {
let expected_ty_name = vm.to_pystr(&ty)?;
@@ -939,24 +948,304 @@ impl PyFuncArgs {
None => Ok(None),
}
}
}
pub trait FromPyObject: Sized {
fn typ(ctx: &PyContext) -> Option<PyObjectRef>;
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self>;
}
impl FromPyObject for PyObjectRef {
fn typ(_ctx: &PyContext) -> Option<PyObjectRef> {
None
/// Serializes these arguments into an iterator starting with the positional
/// arguments followed by keyword arguments.
fn into_iter(self) -> impl Iterator<Item = PyArg> {
self.args.into_iter().map(PyArg::Positional).chain(
self.kwargs
.into_iter()
.map(|(name, value)| PyArg::Keyword(name, value)),
)
}
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self> {
/// Binds these arguments to their respective values.
///
/// If there is an insufficient number of arguments, there are leftover
/// arguments after performing the binding, or if an argument is not of
/// the expected type, a TypeError is raised.
///
/// If the given `FromArgs` includes any conversions, exceptions raised
/// during the conversion will halt the binding and return the error.
fn bind<T: FromArgs>(self, vm: &mut VirtualMachine) -> PyResult<T> {
let given_args = self.args.len();
let mut args = self.into_iter().peekable();
let bound = match T::from_args(vm, &mut args) {
Ok(args) => args,
Err(ArgumentError::TooFewArgs) => {
return Err(vm.new_type_error(format!(
"Expected at least {} arguments ({} given)",
T::arity().start(),
given_args,
)));
}
Err(ArgumentError::Exception(ex)) => {
return Err(ex);
}
};
match args.next() {
None => Ok(bound),
Some(PyArg::Positional(_)) => Err(vm.new_type_error(format!(
"Expected at most {} arguments ({} given)",
T::arity().end(),
given_args,
))),
Some(PyArg::Keyword(name, _)) => {
Err(vm.new_type_error(format!("Unexpected keyword argument {}", name)))
}
}
}
}
/// Implemented by any type that can be accepted as a parameter to a built-in
/// function.
///
pub trait FromArgs: Sized {
/// The range of positional arguments permitted by the function signature.
///
/// Returns an empty range if not applicable.
fn arity() -> RangeInclusive<usize> {
0..=0
}
/// Extracts this item from the next argument(s).
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>,
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>;
}
/// An iterable Python object.
///
/// `PyIterable` implements `FromArgs` so that a built-in function can accept
/// an object that is required to conform to the Python iterator protocol.
///
/// PyIterable can optionally perform type checking and conversions on iterated
/// objects using a generic type parameter that implements `TryFromObject`.
pub struct PyIterable<T = PyObjectRef> {
method: PyObjectRef,
_item: std::marker::PhantomData<T>,
}
impl<T> PyIterable<T> {
/// Returns an iterator over this sequence of objects.
///
/// This operation may fail if an exception is raised while invoking the
/// `__iter__` method of the iterable object.
pub fn iter<'a>(&self, vm: &'a mut VirtualMachine) -> PyResult<PyIterator<'a, T>> {
let iter_obj = vm.invoke(
self.method.clone(),
PyFuncArgs {
args: vec![],
kwargs: vec![],
},
)?;
Ok(PyIterator {
vm,
obj: iter_obj,
_item: std::marker::PhantomData,
})
}
}
pub struct PyIterator<'a, T> {
vm: &'a mut VirtualMachine,
obj: PyObjectRef,
_item: std::marker::PhantomData<T>,
}
impl<'a, T> Iterator for PyIterator<'a, T>
where
T: TryFromObject,
{
type Item = PyResult<T>;
fn next(&mut self) -> Option<Self::Item> {
match self.vm.call_method(&self.obj, "__next__", vec![]) {
Ok(value) => Some(T::try_from_object(self.vm, value)),
Err(err) => {
let stop_ex = self.vm.ctx.exceptions.stop_iteration.clone();
if objtype::isinstance(&err, &stop_ex) {
None
} else {
Some(Err(err))
}
}
}
}
}
impl<T> TryFromObject for PyIterable<T>
where
T: TryFromObject,
{
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(PyIterable {
method: vm.get_method(obj, "__iter__")?,
_item: std::marker::PhantomData,
})
}
}
impl TryFromObject for PyObjectRef {
fn try_from_object(_vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(obj)
}
}
/// A map of keyword arguments to their values.
///
/// A built-in function with a `KwArgs` parameter is analagous to a Python
/// function with `*kwargs`. All remaining keyword arguments are extracted
/// (and hence the function will permit an arbitrary number of them).
///
/// `KwArgs` optionally accepts a generic type parameter to allow type checks
/// or conversions of each argument.
pub struct KwArgs<T = PyObjectRef>(HashMap<String, T>);
impl<T> FromArgs for KwArgs<T>
where
T: TryFromObject,
{
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>,
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>,
{
let mut kwargs = HashMap::new();
while let Some(PyArg::Keyword(name, value)) = args.next() {
kwargs.insert(name, T::try_from_object(vm, value)?);
}
Ok(KwArgs(kwargs))
}
}
/// A list of positional argument values.
///
/// A built-in function with a `Args` parameter is analagous to a Python
/// function with `*args`. All remaining positional arguments are extracted
/// (and hence the function will permit an arbitrary number of them).
///
/// `Args` optionally accepts a generic type parameter to allow type checks
/// or conversions of each argument.
pub struct Args<T>(Vec<T>);
impl<T> FromArgs for Args<T>
where
T: TryFromObject,
{
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>,
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>,
{
let mut varargs = Vec::new();
while let Some(PyArg::Positional(value)) = args.next() {
varargs.push(T::try_from_object(vm, value)?);
}
Ok(Args(varargs))
}
}
impl<T> FromArgs for T
where
T: TryFromObject,
{
fn arity() -> RangeInclusive<usize> {
1..=1
}
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>,
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>,
{
if let Some(PyArg::Positional(value)) = args.next() {
Ok(T::try_from_object(vm, value)?)
} else {
Err(ArgumentError::TooFewArgs)
}
}
}
pub struct OptArg<T>(Option<T>);
impl<T> std::ops::Deref for OptArg<T> {
type Target = Option<T>;
fn deref(&self) -> &Option<T> {
&self.0
}
}
impl<T> FromArgs for OptArg<T>
where
T: TryFromObject,
{
fn arity() -> RangeInclusive<usize> {
0..=1
}
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>,
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>,
{
Ok(OptArg(if let Some(PyArg::Positional(_)) = args.peek() {
let value = if let Some(PyArg::Positional(value)) = args.next() {
value
} else {
unreachable!()
};
Some(T::try_from_object(vm, value)?)
} else {
None
}))
}
}
pub enum PyArg {
Positional(PyObjectRef),
Keyword(String, PyObjectRef),
}
pub enum ArgumentError {
TooFewArgs,
Exception(PyObjectRef),
}
impl From<PyObjectRef> for ArgumentError {
fn from(ex: PyObjectRef) -> Self {
ArgumentError::Exception(ex)
}
}
/// Implemented by any type that can be created from a Python object.
///
/// Any type that implements `TryFromObject` is automatically `FromArgs`, and
/// so can be accepted as a argument to a built-in function.
pub trait TryFromObject: Sized {
/// Attempt to convert a Python object to a value of this type.
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
}
/// Implemented by any type that can be returned from a built-in Python function.
///
/// `IntoPyObject` has a blanket implementation for any built-in object payload,
/// and should be implemented by many primitive Rust types, allowing a built-in
/// function to simply return a `bool` or a `usize` for example.
pub trait IntoPyObject {
fn into_pyobject(self, ctx: &PyContext) -> PyResult;
}
@@ -967,206 +1256,170 @@ impl IntoPyObject for PyObjectRef {
}
}
impl IntoPyObject for PyResult {
fn into_pyobject(self, _ctx: &PyContext) -> PyResult {
self
impl<T> IntoPyObject for PyResult<T>
where
T: IntoPyObject,
{
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
self.and_then(|res| T::into_pyobject(res, ctx))
}
}
pub trait FromPyFuncArgs: Sized {
fn required_params(ctx: &PyContext) -> Vec<Parameter>;
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self>;
// This allows a built-in function to not return a value, mapping to
// Python's behavior of returning `None` in this situation.
impl IntoPyObject for () {
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
Ok(ctx.none())
}
}
// TODO: Allow a built-in function to return an `Option`, i.e.:
//
// impl<T: IntoPyObject> IntoPyObject for Option<T>
//
// Option::None should map to a Python `None`.
// Allows a built-in function to return any built-in object payload without
// explicitly implementing `IntoPyObject`.
impl<T> IntoPyObject for T
where
T: PyObjectPayload2 + Sized,
{
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
Ok(PyObject::new(
PyObjectPayload::AnyRustValue {
value: Box::new(self),
},
T::required_type(ctx),
))
}
}
// A tuple of types that each implement `FromArgs` represents a sequence of
// arguments that can be bound and passed to a built-in function.
//
// Technically, a tuple can contain tuples, which can contain tuples, and so on,
// so this actually represents a tree of values to be bound from arguments, but
// in practice this is only used for the top-level parameters.
macro_rules! tuple_from_py_func_args {
($($T:ident),+) => {
impl<$($T),+> FromPyFuncArgs for ($($T,)+)
impl<$($T),+> FromArgs for ($($T,)+)
where
$($T: FromPyFuncArgs),+
$($T: FromArgs),+
{
fn required_params(ctx: &PyContext) -> Vec<Parameter> {
vec![$($T::required_params(ctx),)+].into_iter().flatten().collect()
fn arity() -> RangeInclusive<usize> {
let mut min = 0;
let mut max = 0;
$(
let (start, end) = $T::arity().into_inner();
min += start;
max += end;
)+
min..=max
}
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self> {
Ok(($($T::from_py_func_args(args)?,)+))
fn from_args<I>(
vm: &mut VirtualMachine,
args: &mut iter::Peekable<I>
) -> Result<Self, ArgumentError>
where
I: Iterator<Item = PyArg>
{
Ok(($($T::from_args(vm, args)?,)+))
}
}
};
}
// Implement `FromArgs` for up to 5-tuples, allowing built-in functions to bind
// up to 5 top-level parameters (note that `Args`, `KwArgs`, nested tuples, etc.
// count as 1, so this should actually be more than enough).
tuple_from_py_func_args!(A);
tuple_from_py_func_args!(A, B);
tuple_from_py_func_args!(A, B, C);
tuple_from_py_func_args!(A, B, C, D);
tuple_from_py_func_args!(A, B, C, D, E);
impl<T> FromPyFuncArgs for T
where
T: FromPyObject,
{
fn required_params(ctx: &PyContext) -> Vec<Parameter> {
vec![Parameter {
kind: PositionalOnly,
typ: T::typ(ctx),
}]
}
/// A built-in Python function.
pub type PyNativeFunc = Box<dyn Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static>;
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self> {
Self::from_pyobject(args.shift())
}
/// Implemented by types that are or can generate built-in functions.
///
/// For example, any function that:
///
/// - Accepts a sequence of types that implement `FromArgs`, followed by a
/// `&mut VirtualMachine`
/// - Returns some type that implements `IntoPyObject`
///
/// will generate a `PyNativeFunc` that performs the appropriate type and arity
/// checking, any requested conversions, and then if successful call the function
/// with the bound values.
///
/// A bare `PyNativeFunc` also implements this trait, allowing the above to be
/// done manually, for rare situations that don't fit into this model.
pub trait IntoPyNativeFunc<T, R> {
fn into_func(self) -> PyNativeFunc;
}
pub type PyNativeFunc = Box<dyn Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>;
pub trait PyNativeFuncFactory<T, R> {
fn create(self, ctx: &PyContext) -> PyNativeFunc;
}
impl<F> PyNativeFuncFactory<PyFuncArgs, PyResult> for F
impl<F> IntoPyNativeFunc<PyFuncArgs, PyResult> for F
where
F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static,
{
fn create(self, _ctx: &PyContext) -> PyNativeFunc {
fn into_func(self) -> PyNativeFunc {
Box::new(self)
}
}
macro_rules! tuple_py_native_func_factory {
($($T:ident),+) => {
impl<F, $($T,)+ R> PyNativeFuncFactory<($($T,)+), R> for F
impl IntoPyNativeFunc<PyFuncArgs, PyResult> for PyNativeFunc {
fn into_func(self) -> PyNativeFunc {
self
}
}
// This is the "magic" that allows rust functions of varying signatures to
// generate native python functions.
//
// Note that this could be done without a macro - it is simply to avoid repetition.
macro_rules! into_py_native_func_tuple {
($(($n:tt, $T:ident)),+) => {
impl<F, $($T,)+ R> IntoPyNativeFunc<($($T,)+), R> for F
where
F: Fn(&mut VirtualMachine, $($T),+) -> R + 'static,
$($T: FromPyFuncArgs,)+
F: Fn($($T,)+ &mut VirtualMachine) -> R + 'static,
$($T: FromArgs,)+
($($T,)+): FromArgs,
R: IntoPyObject,
{
fn create(self, ctx: &PyContext) -> PyNativeFunc {
let parameters = vec![$($T::required_params(ctx)),+]
.into_iter()
.flatten()
.collect();
let signature = Signature::new(parameters);
fn into_func(self) -> PyNativeFunc {
Box::new(move |vm, args| {
let ($($n,)+) = args.bind::<($($T,)+)>(vm)?;
Box::new(move |vm, mut args| {
signature.check(vm, &args)?;
(self)(vm, $($T::from_py_func_args(&mut args)?,)+)
.into_pyobject(&vm.ctx)
(self)($($n,)+ vm).into_pyobject(&vm.ctx)
})
}
}
};
}
tuple_py_native_func_factory!(A);
tuple_py_native_func_factory!(A, B);
tuple_py_native_func_factory!(A, B, C);
tuple_py_native_func_factory!(A, B, C, D);
tuple_py_native_func_factory!(A, B, C, D, E);
#[derive(Debug)]
pub struct Signature {
positional_params: Vec<Parameter>,
keyword_params: HashMap<String, Parameter>,
}
impl Signature {
fn new(params: Vec<Parameter>) -> Self {
let mut positional_params = Vec::new();
let mut keyword_params = HashMap::new();
for param in params {
match param.kind {
PositionalOnly => {
positional_params.push(param);
}
KeywordOnly { ref name } => {
keyword_params.insert(name.clone(), param);
}
}
}
Self {
positional_params,
keyword_params,
}
}
fn arg_type(&self, pos: usize) -> Option<&PyObjectRef> {
self.positional_params[pos].typ.as_ref()
}
#[allow(unused)]
fn kwarg_type(&self, name: &str) -> Option<&PyObjectRef> {
self.keyword_params[name].typ.as_ref()
}
fn check(&self, vm: &mut VirtualMachine, args: &PyFuncArgs) -> PyResult<()> {
// TODO: check arity
for (pos, arg) in args.args.iter().enumerate() {
if let Some(expected_type) = self.arg_type(pos) {
if !objtype::real_isinstance(vm, arg, expected_type)? {
let arg_typ = arg.typ();
let expected_type_name = vm.to_pystr(&expected_type)?;
let actual_type = vm.to_pystr(&arg_typ)?;
return Err(vm.new_type_error(format!(
"argument of type {} is required for parameter {} (got: {})",
expected_type_name,
pos + 1,
actual_type
)));
}
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct Parameter {
typ: Option<PyObjectRef>,
kind: ParameterKind,
}
#[derive(Debug)]
pub enum ParameterKind {
PositionalOnly,
KeywordOnly { name: String },
}
use self::ParameterKind::*;
into_py_native_func_tuple!((a, A));
into_py_native_func_tuple!((a, A), (b, B));
into_py_native_func_tuple!((a, A), (b, B), (c, C));
into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D));
into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E));
/// Rather than determining the type of a python object, this enum is more
/// a holder for the rust payload of a python object. It is more a carrier
/// of rust data for a particular python object. Determine the python type
/// by using for example the `.typ()` method on a python object.
pub enum PyObjectPayload {
String {
value: String,
},
Integer {
value: BigInt,
},
Float {
value: f64,
},
Complex {
value: Complex64,
},
Bytes {
value: RefCell<Vec<u8>>,
},
Sequence {
elements: RefCell<Vec<PyObjectRef>>,
},
Dict {
elements: RefCell<objdict::DictContentType>,
},
Set {
elements: RefCell<HashMap<u64, PyObjectRef>>,
},
Iterator {
position: Cell<usize>,
iterated_obj: PyObjectRef,
@@ -1191,9 +1444,6 @@ pub enum PyObjectPayload {
stop: Option<BigInt>,
step: Option<BigInt>,
},
Range {
range: objrange::RangeType,
},
MemoryView {
obj: PyObjectRef,
},
@@ -1226,6 +1476,9 @@ pub enum PyObjectPayload {
dict: RefCell<PyAttributes>,
mro: Vec<PyObjectRef>,
},
Set {
elements: RefCell<HashMap<u64, PyObjectRef>>,
},
WeakRef {
referent: PyObjectWeakRef,
},
@@ -1233,7 +1486,7 @@ pub enum PyObjectPayload {
dict: RefCell<PyAttributes>,
},
RustFunction {
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
function: PyNativeFunc,
},
AnyRustValue {
value: Box<dyn std::any::Any>,
@@ -1243,17 +1496,12 @@ pub enum PyObjectPayload {
impl fmt::Debug for PyObjectPayload {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value),
PyObjectPayload::Integer { ref value } => write!(f, "int {}", value),
PyObjectPayload::Float { ref value } => write!(f, "float {}", value),
PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value),
PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value),
PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj),
PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"),
PyObjectPayload::Dict { .. } => write!(f, "dict"),
PyObjectPayload::Set { .. } => write!(f, "set"),
PyObjectPayload::WeakRef { .. } => write!(f, "weakref"),
PyObjectPayload::Range { .. } => write!(f, "range"),
PyObjectPayload::Iterator { .. } => write!(f, "iterator"),
PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"),
PyObjectPayload::FilterIterator { .. } => write!(f, "filter"),
@@ -1296,6 +1544,20 @@ impl PyObject {
pub fn into_ref(self) -> PyObjectRef {
Rc::new(self)
}
pub fn payload<T: PyObjectPayload2>(&self) -> Option<&T> {
if let PyObjectPayload::AnyRustValue { ref value } = self.payload {
value.downcast_ref()
} else {
None
}
}
}
// The intention is for this to replace `PyObjectPayload` once everything is
// converted to use `PyObjectPayload::AnyRustvalue`.
pub trait PyObjectPayload2: std::any::Any + fmt::Debug {
fn required_type(ctx: &PyContext) -> PyObjectRef;
}
#[cfg(test)]

View File

@@ -15,13 +15,13 @@ use num_traits::ToPrimitive;
//custom imports
use super::os;
use crate::obj::objbytearray::PyByteArray;
use crate::obj::objbytes;
use crate::obj::objint;
use crate::obj::objstr;
use crate::pyobject::{
AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef,
PyResult, TypeProtocol,
AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
};
use crate::import;
@@ -86,8 +86,8 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
.map_err(|_| vm.new_value_error("IO Error".to_string()))?;
//Copy bytes from the buffer vector into the results vector
if let PyObjectPayload::Bytes { ref value } = buffer.payload {
result.extend(value.borrow().iter().cloned());
if let Some(bytes) = buffer.payload::<PyByteArray>() {
result.extend_from_slice(&bytes.value.borrow());
};
let len = vm.get_method(buffer.clone(), &"__len__".to_string());
@@ -169,10 +169,10 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let handle = os::rust_file(raw_fd);
let mut f = handle.take(length);
if let PyObjectPayload::Bytes { ref value } = obj.payload {
if let Some(bytes) = obj.payload::<PyByteArray>() {
//TODO: Implement for MemoryView
let mut value_mut = value.borrow_mut();
let mut value_mut = bytes.value.borrow_mut();
value_mut.clear();
match f.read_to_end(&mut value_mut) {
Ok(_) => {}
@@ -200,9 +200,9 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
//to support windows - i.e. raw file_handles
let mut handle = os::rust_file(raw_fd);
match obj.payload {
PyObjectPayload::Bytes { ref value } => {
let value_mut = value.borrow();
match obj.payload::<PyByteArray>() {
Some(bytes) => {
let value_mut = bytes.value.borrow();
match handle.write(&value_mut[..]) {
Ok(len) => {
//reset raw fd on the FileIO object
@@ -215,7 +215,7 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())),
}
}
_ => Err(vm.new_value_error("Expected Bytes Object".to_string())),
None => Err(vm.new_value_error("Expected Bytes Object".to_string())),
}
}
@@ -346,101 +346,67 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None));
ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open));
//IOBase the abstract base class of the IO Module
let io_base = ctx.new_class("IOBase", ctx.object());
ctx.set_attr(&py_mod, "IOBase", io_base.clone());
// IOBase Subclasses
let raw_io_base = ctx.new_class("RawIOBase", ctx.object());
ctx.set_attr(&py_mod, "RawIOBase", raw_io_base.clone());
let buffered_io_base = {
let buffered_io_base = ctx.new_class("BufferedIOBase", io_base.clone());
ctx.set_attr(
&buffered_io_base,
"__init__",
ctx.new_rustfunc(buffered_io_base_init),
);
buffered_io_base
};
ctx.set_attr(&py_mod, "BufferedIOBase", buffered_io_base.clone());
let buffered_io_base = py_class!(ctx, "BufferedIOBase", io_base.clone(), {
"__init__" => ctx.new_rustfunc(buffered_io_base_init)
});
//TextIO Base has no public constructor
let text_io_base = {
let text_io_base = ctx.new_class("TextIOBase", io_base.clone());
ctx.set_attr(&text_io_base, "read", ctx.new_rustfunc(text_io_base_read));
text_io_base
};
ctx.set_attr(&py_mod, "TextIOBase", text_io_base.clone());
let text_io_base = py_class!(ctx, "TextIOBase", io_base.clone(), {
"read" => ctx.new_rustfunc(text_io_base_read)
});
// RawBaseIO Subclasses
let file_io = {
let file_io = ctx.new_class("FileIO", raw_io_base.clone());
ctx.set_attr(&file_io, "__init__", ctx.new_rustfunc(file_io_init));
ctx.set_attr(&file_io, "name", ctx.str_type());
ctx.set_attr(&file_io, "read", ctx.new_rustfunc(file_io_read));
ctx.set_attr(&file_io, "readinto", ctx.new_rustfunc(file_io_readinto));
ctx.set_attr(&file_io, "write", ctx.new_rustfunc(file_io_write));
file_io
};
ctx.set_attr(&py_mod, "FileIO", file_io.clone());
let file_io = py_class!(ctx, "FileIO", raw_io_base.clone(), {
"__init__" => ctx.new_rustfunc(file_io_init),
"name" => ctx.str_type(),
"read" => ctx.new_rustfunc(file_io_read),
"readinto" => ctx.new_rustfunc(file_io_readinto),
"write" => ctx.new_rustfunc(file_io_write)
});
// BufferedIOBase Subclasses
let buffered_reader = {
let buffered_reader = ctx.new_class("BufferedReader", buffered_io_base.clone());
ctx.set_attr(
&buffered_reader,
"read",
ctx.new_rustfunc(buffered_reader_read),
);
buffered_reader
};
ctx.set_attr(&py_mod, "BufferedReader", buffered_reader.clone());
let buffered_reader = py_class!(ctx, "BufferedReader", buffered_io_base.clone(), {
"read" => ctx.new_rustfunc(buffered_reader_read)
});
let buffered_writer = {
let buffered_writer = ctx.new_class("BufferedWriter", buffered_io_base.clone());
ctx.set_attr(
&buffered_writer,
"write",
ctx.new_rustfunc(buffered_writer_write),
);
buffered_writer
};
ctx.set_attr(&py_mod, "BufferedWriter", buffered_writer.clone());
let buffered_writer = py_class!(ctx, "BufferedWriter", buffered_io_base.clone(), {
"write" => ctx.new_rustfunc(buffered_writer_write)
});
//TextIOBase Subclass
let text_io_wrapper = {
let text_io_wrapper = ctx.new_class("TextIOWrapper", text_io_base.clone());
ctx.set_attr(
&text_io_wrapper,
"__init__",
ctx.new_rustfunc(text_io_wrapper_init),
);
text_io_wrapper
};
ctx.set_attr(&py_mod, "TextIOWrapper", text_io_wrapper.clone());
let text_io_wrapper = py_class!(ctx, "TextIOWrapper", text_io_base.clone(), {
"__init__" => ctx.new_rustfunc(text_io_wrapper_init)
});
//StringIO: in-memory text
let string_io = {
let string_io = ctx.new_class("StringIO", text_io_base.clone());
ctx.set_attr(&string_io, "__init__", ctx.new_rustfunc(string_io_init));
ctx.set_attr(&string_io, "getvalue", ctx.new_rustfunc(string_io_getvalue));
string_io
};
ctx.set_attr(&py_mod, "StringIO", string_io);
let string_io = py_class!(ctx, "StringIO", text_io_base.clone(), {
"__init__" => ctx.new_rustfunc(string_io_init),
"getvalue" => ctx.new_rustfunc(string_io_getvalue)
});
//BytesIO: in-memory bytes
let bytes_io = {
let bytes_io = ctx.new_class("BytesIO", buffered_io_base.clone());
ctx.set_attr(&bytes_io, "__init__", ctx.new_rustfunc(bytes_io_init));
ctx.set_attr(&bytes_io, "getvalue", ctx.new_rustfunc(bytes_io_getvalue));
bytes_io
};
ctx.set_attr(&py_mod, "BytesIO", bytes_io);
let bytes_io = py_class!(ctx, "BytesIO", buffered_io_base.clone(), {
"__init__" => ctx.new_rustfunc(bytes_io_init),
"getvalue" => ctx.new_rustfunc(bytes_io_getvalue)
});
py_mod
py_module!(ctx, "io", {
"open" => ctx.new_rustfunc(io_open),
"IOBase" => io_base.clone(),
"RawIOBase" => raw_io_base.clone(),
"BufferedIOBase" => buffered_io_base.clone(),
"TextIOBase" => text_io_base.clone(),
"FileIO" => file_io.clone(),
"BufferedReader" => buffered_reader.clone(),
"BufferedWriter" => buffered_writer.clone(),
"TextIOWrapper" => text_io_wrapper.clone(),
"StringIO" => string_io,
"BytesIO" => bytes_io,
})
}

View File

@@ -5,7 +5,11 @@ use serde::de::{DeserializeSeed, Visitor};
use serde::ser::{SerializeMap, SerializeSeq};
use serde_json;
use crate::obj::{objbool, objdict, objfloat, objint, objsequence, objstr, objtype};
use crate::obj::{
objbool, objdict, objfloat, objint, objsequence,
objstr::{self, PyString},
objtype,
};
use crate::pyobject::{
create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult,
TypeProtocol,
@@ -167,8 +171,8 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> {
// than wrapping the given object up and then unwrapping it to determine whether or
// not it is a string
while let Some((key_obj, value)) = access.next_entry_seed(self.clone(), self.clone())? {
let key = match key_obj.payload {
PyObjectPayload::String { ref value } => value.clone(),
let key: String = match key_obj.payload::<PyString>() {
Some(PyString { ref value }) => value.clone(),
_ => unimplemented!("map keys must be strings"),
};
dict.set_item(&self.vm.ctx, &key, value);

View File

@@ -46,31 +46,19 @@ fn get_int(vm: &mut VirtualMachine, arg: &PyObjectRef) -> PyResult<BigInt> {
objint::to_int(vm, arg, 10)
}
fn pack_i8(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_i8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_i8().unwrap();
data.write_i8(v).unwrap();
Ok(())
}
fn pack_u8(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_u8(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_u8().unwrap();
data.write_u8(v).unwrap();
Ok(())
}
fn pack_bool(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_bool(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
if objtype::isinstance(&arg, &vm.ctx.bool_type()) {
let v = if objbool::get_value(arg) { 1 } else { 0 };
data.write_u8(v).unwrap();
@@ -80,71 +68,43 @@ fn pack_bool(
}
}
fn pack_i16(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_i16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_i16().unwrap();
data.write_i16::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_u16(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_u16(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_u16().unwrap();
data.write_u16::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_i32(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_i32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_i32().unwrap();
data.write_i32::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_u32(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_u32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_u32().unwrap();
data.write_u32::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_i64(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_i64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_i64().unwrap();
data.write_i64::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_u64(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_u64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
let v = get_int(vm, arg)?.to_u64().unwrap();
data.write_u64::<LittleEndian>(v).unwrap();
Ok(())
}
fn pack_f32(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_f32(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
if objtype::isinstance(&arg, &vm.ctx.float_type()) {
let v = objfloat::get_value(arg) as f32;
data.write_f32::<LittleEndian>(v).unwrap();
@@ -154,11 +114,7 @@ fn pack_f32(
}
}
fn pack_f64(
vm: &mut VirtualMachine,
arg: &PyObjectRef,
data: &mut Write,
) -> PyResult<()> {
fn pack_f64(vm: &mut VirtualMachine, arg: &PyObjectRef, data: &mut Write) -> PyResult<()> {
if objtype::isinstance(&arg, &vm.ctx.float_type()) {
let v = objfloat::get_value(arg) as f64;
data.write_f64::<LittleEndian>(v).unwrap();

View File

@@ -50,13 +50,11 @@ fn re_search(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
let py_mod = ctx.new_module("re", ctx.new_scope(None));
let match_type = py_class!(ctx, "Match", ctx.object(), {});
let match_type = ctx.new_class("Match", ctx.object());
ctx.set_attr(&py_mod, "Match", match_type);
ctx.set_attr(&py_mod, "match", ctx.new_rustfunc(re_match));
ctx.set_attr(&py_mod, "search", ctx.new_rustfunc(re_search));
py_mod
py_module!(ctx, "re", {
"Match" => match_type,
"match" => ctx.new_rustfunc(re_match),
"search" => ctx.new_rustfunc(re_search)
})
}

View File

@@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::io;
use std::io::Read;
use std::io::Write;
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket};
use std::ops::DerefMut;
use crate::obj::objbytes;
@@ -53,7 +53,7 @@ impl SocketKind {
enum Connection {
TcpListener(TcpListener),
TcpStream(TcpStream),
// UdpSocket(UdpSocket),
UdpSocket(UdpSocket),
}
impl Connection {
@@ -67,6 +67,21 @@ impl Connection {
fn local_addr(&self) -> io::Result<SocketAddr> {
match self {
Connection::TcpListener(con) => con.local_addr(),
Connection::UdpSocket(con) => con.local_addr(),
Connection::TcpStream(con) => con.local_addr(),
}
}
fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
match self {
Connection::UdpSocket(con) => con.recv_from(buf),
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
}
}
fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> {
match self {
Connection::UdpSocket(con) => con.send_to(buf, addr),
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
}
}
@@ -76,6 +91,7 @@ impl Read for Connection {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Connection::TcpStream(con) => con.read(buf),
Connection::UdpSocket(con) => con.recv(buf),
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
}
}
@@ -85,6 +101,7 @@ impl Write for Connection {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
Connection::TcpStream(con) => con.write(buf),
Connection::UdpSocket(con) => con.send(buf),
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
}
}
@@ -153,12 +170,27 @@ fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let mut socket = get_socket(zelf);
if let Ok(stream) = TcpStream::connect(address_string) {
socket.con = Some(Connection::TcpStream(stream));
Ok(vm.get_none())
} else {
// TODO: Socket error
Err(vm.new_type_error("socket failed".to_string()))
match socket.socket_kind {
SocketKind::Stream => {
if let Ok(stream) = TcpStream::connect(address_string) {
socket.con = Some(Connection::TcpStream(stream));
Ok(vm.get_none())
} else {
// TODO: Socket error
Err(vm.new_type_error("socket failed".to_string()))
}
}
SocketKind::Dgram => {
if let Some(Connection::UdpSocket(con)) = &socket.con {
match con.connect(address_string) {
Ok(_) => Ok(vm.get_none()),
// TODO: Socket error
Err(_) => Err(vm.new_type_error("socket failed".to_string())),
}
} else {
Err(vm.new_type_error("".to_string()))
}
}
}
}
@@ -173,12 +205,25 @@ fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let mut socket = get_socket(zelf);
if let Ok(stream) = TcpListener::bind(address_string) {
socket.con = Some(Connection::TcpListener(stream));
Ok(vm.get_none())
} else {
// TODO: Socket error
Err(vm.new_type_error("socket failed".to_string()))
match socket.socket_kind {
SocketKind::Stream => {
if let Ok(stream) = TcpListener::bind(address_string) {
socket.con = Some(Connection::TcpListener(stream));
Ok(vm.get_none())
} else {
// TODO: Socket error
Err(vm.new_type_error("socket failed".to_string()))
}
}
SocketKind::Dgram => {
if let Ok(dgram) = UdpSocket::bind(address_string) {
socket.con = Some(Connection::UdpSocket(dgram));
Ok(vm.get_none())
} else {
// TODO: Socket error
Err(vm.new_type_error("socket failed".to_string()))
}
}
}
}
@@ -225,8 +270,8 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
None => return Err(vm.new_type_error("".to_string())),
};
let tcp_stream = match ret {
Ok((socket, _addr)) => socket,
let (tcp_stream, addr) = match ret {
Ok((socket, addr)) => (socket, addr),
_ => return Err(vm.new_type_error("".to_string())),
};
@@ -243,12 +288,9 @@ fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
zelf.typ(),
);
let elements = RefCell::new(vec![sock_obj, vm.get_none()]);
let addr_tuple = get_addr_tuple(vm, addr)?;
Ok(PyObject::new(
PyObjectPayload::Sequence { elements },
vm.ctx.tuple_type(),
))
Ok(vm.ctx.new_tuple(vec![sock_obj, addr_tuple]))
}
fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -267,6 +309,31 @@ fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_bytes(buffer))
}
fn socket_recvfrom(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))]
);
let mut socket = get_socket(zelf);
let mut buffer = vec![0u8; objint::get_value(bufsize).to_usize().unwrap()];
let ret = match socket.con {
Some(ref mut v) => v.recv_from(&mut buffer),
None => return Err(vm.new_type_error("".to_string())),
};
let addr = match ret {
Ok((_size, addr)) => addr,
_ => return Err(vm.new_type_error("".to_string())),
};
let addr_tuple = get_addr_tuple(vm, addr)?;
Ok(vm.ctx.new_tuple(vec![vm.ctx.new_bytes(buffer), addr_tuple]))
}
fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -282,6 +349,46 @@ fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.get_none())
}
fn socket_sendto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [
(zelf, None),
(bytes, Some(vm.ctx.bytes_type())),
(address, Some(vm.ctx.tuple_type()))
]
);
let address_string = get_address_string(vm, address)?;
let mut socket = get_socket(zelf);
match socket.socket_kind {
SocketKind::Dgram => {
match socket.con {
Some(ref mut v) => {
if let Ok(_) = v.send_to(&objbytes::get_value(&bytes), address_string) {
Ok(vm.get_none())
} else {
Err(vm.new_type_error("socket failed".to_string()))
}
}
None => {
// Doing implicit bind
if let Ok(dgram) = UdpSocket::bind("0.0.0.0:0") {
if let Ok(_) = dgram.send_to(&objbytes::get_value(&bytes), address_string) {
socket.con = Some(Connection::UdpSocket(dgram));
return Ok(vm.get_none());
}
}
Err(vm.new_type_error("socket failed".to_string()))
}
}
}
_ => Err(vm.new_not_implemented_error("".to_string())),
}
}
fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(zelf, None)]);
@@ -300,45 +407,37 @@ fn socket_getsockname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
match addr {
Ok(addr) => {
let port = vm.ctx.new_int(addr.port());
let ip = vm.ctx.new_str(addr.ip().to_string());
let elements = RefCell::new(vec![ip, port]);
Ok(PyObject::new(
PyObjectPayload::Sequence { elements },
vm.ctx.tuple_type(),
))
}
Ok(addr) => get_addr_tuple(vm, addr),
_ => Err(vm.new_type_error("".to_string())),
}
}
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
let py_mod = ctx.new_module(&"socket".to_string(), ctx.new_scope(None));
fn get_addr_tuple(vm: &mut VirtualMachine, addr: SocketAddr) -> PyResult {
let port = vm.ctx.new_int(addr.port());
let ip = vm.ctx.new_str(addr.ip().to_string());
ctx.set_attr(&py_mod, "AF_INET", ctx.new_int(AddressFamily::Inet as i32));
ctx.set_attr(
&py_mod,
"SOCK_STREAM",
ctx.new_int(SocketKind::Stream as i32),
);
let socket = {
let socket = ctx.new_class("socket", ctx.object());
ctx.set_attr(&socket, "__new__", ctx.new_rustfunc(socket_new));
ctx.set_attr(&socket, "connect", ctx.new_rustfunc(socket_connect));
ctx.set_attr(&socket, "recv", ctx.new_rustfunc(socket_recv));
ctx.set_attr(&socket, "send", ctx.new_rustfunc(socket_send));
ctx.set_attr(&socket, "bind", ctx.new_rustfunc(socket_bind));
ctx.set_attr(&socket, "accept", ctx.new_rustfunc(socket_accept));
ctx.set_attr(&socket, "listen", ctx.new_rustfunc(socket_listen));
ctx.set_attr(&socket, "close", ctx.new_rustfunc(socket_close));
ctx.set_attr(&socket, "getsockname", ctx.new_rustfunc(socket_getsockname));
socket
};
ctx.set_attr(&py_mod, "socket", socket.clone());
py_mod
Ok(vm.ctx.new_tuple(vec![ip, port]))
}
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
let socket = py_class!(ctx, "socket", ctx.object(), {
"__new__" => ctx.new_rustfunc(socket_new),
"connect" => ctx.new_rustfunc(socket_connect),
"recv" => ctx.new_rustfunc(socket_recv),
"send" => ctx.new_rustfunc(socket_send),
"bind" => ctx.new_rustfunc(socket_bind),
"accept" => ctx.new_rustfunc(socket_accept),
"listen" => ctx.new_rustfunc(socket_listen),
"close" => ctx.new_rustfunc(socket_close),
"getsockname" => ctx.new_rustfunc(socket_getsockname),
"sendto" => ctx.new_rustfunc(socket_sendto),
"recvfrom" => ctx.new_rustfunc(socket_recvfrom),
});
py_module!(ctx, "socket", {
"AF_INET" => ctx.new_int(AddressFamily::Inet as i32),
"SOCK_STREAM" => ctx.new_int(SocketKind::Stream as i32),
"SOCK_DGRAM" => ctx.new_int(SocketKind::Dgram as i32),
"socket" => socket.clone(),
})
}

View File

@@ -90,6 +90,12 @@ impl VirtualMachine {
result
}
pub fn current_scope(&self) -> &ScopeRef {
let current_frame = &self.frames[self.frames.len() - 1];
let frame = objframe::get_value(current_frame);
&frame.scope
}
/// Create a new python string object.
pub fn new_str(&self, s: String) -> PyObjectRef {
self.ctx.new_str(s)
@@ -218,7 +224,7 @@ impl VirtualMachine {
&self.ctx
}
pub fn get_builtin_scope(&mut self) -> ScopeRef {
pub fn get_builtin_scope(&self) -> ScopeRef {
let a2 = &*self.builtins;
match a2.payload {
PyObjectPayload::Module { ref scope, .. } => scope.clone(),
@@ -242,6 +248,26 @@ impl VirtualMachine {
self.call_method(obj, "__repr__", vec![])
}
/// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via
/// the __instancecheck__ magic method.
pub fn isinstance(&mut self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult<bool> {
// cpython first does an exact check on the type, although documentation doesn't state that
// https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408
if Rc::ptr_eq(&obj.typ(), cls) {
Ok(true)
} else {
let ret = self.call_method(cls, "__instancecheck__", vec![obj.clone()])?;
objbool::boolval(self, ret)
}
}
/// Determines if `subclass` is a subclass of `cls`, either directly, indirectly or virtually
/// via the __subclasscheck__ magic method.
pub fn issubclass(&mut self, subclass: &PyObjectRef, cls: &PyObjectRef) -> PyResult<bool> {
let ret = self.call_method(cls, "__subclasscheck__", vec![subclass.clone()])?;
objbool::boolval(self, ret)
}
pub fn call_get_descriptor(&mut self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
let attr_class = attr.typ();
if let Some(descriptor) = attr_class.get_attr("__get__") {

View File

@@ -301,7 +301,7 @@ impl WASMVirtualMachine {
};
scope
.locals
.set_item(&vm.ctx, "print", vm.ctx.new_rustfunc_from_box(print_fn));
.set_item(&vm.ctx, "print", vm.ctx.new_rustfunc(print_fn));
Ok(())
},
)?