mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-17 01:51:39 +09:00
merge upstream
This commit is contained in:
@@ -169,6 +169,7 @@ pub enum Instruction {
|
||||
},
|
||||
Unpack,
|
||||
FormatValue {
|
||||
conversion: Option<ast::ConversionFlag>,
|
||||
spec: String,
|
||||
},
|
||||
}
|
||||
@@ -361,7 +362,10 @@ impl Instruction {
|
||||
UnpackSequence { size } => w!(UnpackSequence, size),
|
||||
UnpackEx { before, after } => w!(UnpackEx, before, after),
|
||||
Unpack => w!(Unpack),
|
||||
FormatValue { spec } => w!(FormatValue, spec),
|
||||
FormatValue {
|
||||
conversion: _,
|
||||
spec,
|
||||
} => w!(FormatValue, spec), // TODO: write conversion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1352,9 +1352,16 @@ impl Compiler {
|
||||
},
|
||||
});
|
||||
}
|
||||
ast::StringGroup::FormattedValue { value, spec } => {
|
||||
ast::StringGroup::FormattedValue {
|
||||
value,
|
||||
conversion,
|
||||
spec,
|
||||
} => {
|
||||
self.compile_expression(value)?;
|
||||
self.emit(Instruction::FormatValue { spec: spec.clone() });
|
||||
self.emit(Instruction::FormatValue {
|
||||
conversion: *conversion,
|
||||
spec: spec.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -654,8 +654,15 @@ impl Frame {
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
bytecode::Instruction::FormatValue { spec } => {
|
||||
let value = self.pop_value();
|
||||
bytecode::Instruction::FormatValue { conversion, spec } => {
|
||||
use ast::ConversionFlag::*;
|
||||
let value = match conversion {
|
||||
Some(Str) => vm.to_str(&self.pop_value())?,
|
||||
Some(Repr) => vm.to_repr(&self.pop_value())?,
|
||||
Some(Ascii) => self.pop_value(), // TODO
|
||||
None => self.pop_value(),
|
||||
};
|
||||
|
||||
let spec = vm.new_str(spec.clone());
|
||||
let formatted = vm.call_method(&value, "__format__", vec![spec])?;
|
||||
self.push_value(formatted);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
use super::objtype;
|
||||
use crate::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
IntoPyObject, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_traits::Zero;
|
||||
|
||||
impl IntoPyObject for bool {
|
||||
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
|
||||
Ok(ctx.new_bool(self))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObjectRef> {
|
||||
let result = match obj.borrow().payload {
|
||||
PyObjectPayload::Integer { ref value } => !value.is_zero(),
|
||||
|
||||
@@ -3,8 +3,8 @@ use super::objstr;
|
||||
use super::objtype;
|
||||
use crate::format::FormatSpec;
|
||||
use crate::pyobject::{
|
||||
FromPyObjectRef, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
FromPyObjectRef, IntoPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
|
||||
PyResult, TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
@@ -15,6 +15,22 @@ use std::hash::{Hash, Hasher};
|
||||
// This proxy allows for easy switching between types.
|
||||
type IntType = BigInt;
|
||||
|
||||
pub type PyInt = BigInt;
|
||||
|
||||
impl IntoPyObject for PyInt {
|
||||
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
|
||||
Ok(ctx.new_int(self))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: macro to impl for all primitive ints
|
||||
|
||||
impl IntoPyObject for usize {
|
||||
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
|
||||
Ok(ctx.new_int(self))
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use super::objint;
|
||||
use super::objtype;
|
||||
use crate::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
@@ -18,6 +19,18 @@ 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 RangeType {
|
||||
#[inline]
|
||||
pub fn try_len(&self) -> Option<usize> {
|
||||
@@ -345,22 +358,12 @@ fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.ctx.new_bool(len > 0))
|
||||
}
|
||||
|
||||
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))
|
||||
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))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
}
|
||||
|
||||
fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
@@ -3,7 +3,8 @@ use super::objsequence::PySliceableSequence;
|
||||
use super::objtype;
|
||||
use crate::format::{FormatParseError, FormatPart, FormatString};
|
||||
use crate::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
FromPyObject, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_traits::ToPrimitive;
|
||||
@@ -17,6 +18,16 @@ extern crate unicode_segmentation;
|
||||
|
||||
use self::unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
impl FromPyObject for String {
|
||||
fn typ(ctx: &PyContext) -> Option<PyObjectRef> {
|
||||
Some(ctx.str_type())
|
||||
}
|
||||
|
||||
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self> {
|
||||
Ok(get_value(&obj))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let str_type = &context.str_type;
|
||||
context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add));
|
||||
@@ -474,15 +485,8 @@ fn str_rstrip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.ctx.new_str(value))
|
||||
}
|
||||
|
||||
fn str_endswith(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.ends_with(pat.as_str())))
|
||||
fn str_endswith(_vm: &mut VirtualMachine, zelf: String, suffix: String) -> bool {
|
||||
zelf.ends_with(&suffix)
|
||||
}
|
||||
|
||||
fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
@@ -29,6 +29,7 @@ use crate::obj::objsuper;
|
||||
use crate::obj::objtuple;
|
||||
use crate::obj::objtype;
|
||||
use crate::obj::objzip;
|
||||
use crate::stdlib::socket::Socket;
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_bigint::BigInt;
|
||||
use num_bigint::ToBigInt;
|
||||
@@ -72,7 +73,7 @@ pub type PyObjectWeakRef = Weak<RefCell<PyObject>>;
|
||||
/// Use this type for function which return a python object or and exception.
|
||||
/// Both the python object and the python exception are `PyObjectRef` types
|
||||
/// since exceptions are also python objects.
|
||||
pub type PyResult = Result<PyObjectRef, PyObjectRef>; // A valid value, or an exception
|
||||
pub type PyResult<T = PyObjectRef> = Result<T, PyObjectRef>; // A valid value, or an exception
|
||||
|
||||
/// For attributes we do not use a dict, but a hashmap. This is probably
|
||||
/// faster, unordered, and only supports strings as keys.
|
||||
@@ -553,13 +554,13 @@ impl PyContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_rustfunc<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
||||
&self,
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
pub fn new_rustfunc<F, T, R>(&self, factory: F) -> PyObjectRef
|
||||
where
|
||||
F: PyNativeFuncFactory<T, R>,
|
||||
{
|
||||
PyObject::new(
|
||||
PyObjectPayload::RustFunction {
|
||||
function: Box::new(function),
|
||||
function: factory.create(self),
|
||||
},
|
||||
self.builtin_function_or_method_type(),
|
||||
)
|
||||
@@ -945,6 +946,203 @@ impl PyFuncArgs {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn from_pyobject(obj: PyObjectRef) -> PyResult<Self> {
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoPyObject {
|
||||
fn into_pyobject(self, ctx: &PyContext) -> PyResult;
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyObjectRef {
|
||||
fn into_pyobject(self, _ctx: &PyContext) -> PyResult {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyObject for PyResult {
|
||||
fn into_pyobject(self, _ctx: &PyContext) -> PyResult {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FromPyFuncArgs: Sized {
|
||||
fn required_params(ctx: &PyContext) -> Vec<Parameter>;
|
||||
|
||||
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self>;
|
||||
}
|
||||
|
||||
macro_rules! tuple_from_py_func_args {
|
||||
($($T:ident),+) => {
|
||||
impl<$($T),+> FromPyFuncArgs for ($($T,)+)
|
||||
where
|
||||
$($T: FromPyFuncArgs),+
|
||||
{
|
||||
fn required_params(ctx: &PyContext) -> Vec<Parameter> {
|
||||
vec![$($T::required_params(ctx),)+].into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self> {
|
||||
Ok(($($T::from_py_func_args(args)?,)+))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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),
|
||||
}]
|
||||
}
|
||||
|
||||
fn from_py_func_args(args: &mut PyFuncArgs) -> PyResult<Self> {
|
||||
Self::from_pyobject(args.shift())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
where
|
||||
F: Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult + 'static,
|
||||
{
|
||||
fn create(self, _ctx: &PyContext) -> PyNativeFunc {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuple_py_native_func_factory {
|
||||
($($T:ident),+) => {
|
||||
impl<F, $($T,)+ R> PyNativeFuncFactory<($($T,)+), R> for F
|
||||
where
|
||||
F: Fn(&mut VirtualMachine, $($T),+) -> R + 'static,
|
||||
$($T: FromPyFuncArgs,)+
|
||||
R: IntoPyObject,
|
||||
{
|
||||
fn create(self, ctx: &PyContext) -> PyNativeFunc {
|
||||
let parameters = vec![$($T::required_params(ctx)),+]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let signature = Signature::new(parameters);
|
||||
|
||||
Box::new(move |vm, mut args| {
|
||||
signature.check(vm, &mut args)?;
|
||||
|
||||
(self)(vm, $($T::from_py_func_args(&mut args)?,)+)
|
||||
.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::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)?;
|
||||
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::*;
|
||||
|
||||
/// 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
|
||||
@@ -1045,6 +1243,9 @@ pub enum PyObjectPayload {
|
||||
RustFunction {
|
||||
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
|
||||
},
|
||||
Socket {
|
||||
socket: Socket,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for PyObjectPayload {
|
||||
@@ -1082,6 +1283,7 @@ impl fmt::Debug for PyObjectPayload {
|
||||
PyObjectPayload::Instance { .. } => write!(f, "instance"),
|
||||
PyObjectPayload::RustFunction { .. } => write!(f, "rust function"),
|
||||
PyObjectPayload::Frame { .. } => write!(f, "frame"),
|
||||
PyObjectPayload::Socket { .. } => write!(f, "socket"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ mod math;
|
||||
mod pystruct;
|
||||
mod random;
|
||||
mod re;
|
||||
pub mod socket;
|
||||
mod string;
|
||||
mod time_module;
|
||||
mod tokenize;
|
||||
@@ -25,24 +26,25 @@ pub type StdlibInitFunc = Box<dyn Fn(&PyContext) -> PyObjectRef>;
|
||||
pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
|
||||
let mut modules = HashMap::new();
|
||||
modules.insert("ast".to_string(), Box::new(ast::mk_module) as StdlibInitFunc);
|
||||
modules.insert("dis".to_string(), Box::new(dis::mk_module) as StdlibInitFunc);
|
||||
modules.insert("json".to_string(), Box::new(json::mk_module) as StdlibInitFunc);
|
||||
modules.insert("keyword".to_string(), Box::new(keyword::mk_module) as StdlibInitFunc);
|
||||
modules.insert("math".to_string(), Box::new(math::mk_module) as StdlibInitFunc);
|
||||
modules.insert("re".to_string(), Box::new(re::mk_module) as StdlibInitFunc);
|
||||
modules.insert("random".to_string(), Box::new(random::mk_module) as StdlibInitFunc);
|
||||
modules.insert("string".to_string(), Box::new(string::mk_module) as StdlibInitFunc);
|
||||
modules.insert("struct".to_string(), Box::new(pystruct::mk_module) as StdlibInitFunc);
|
||||
modules.insert("time".to_string(), Box::new(time_module::mk_module) as StdlibInitFunc);
|
||||
modules.insert( "tokenize".to_string(), Box::new(tokenize::mk_module) as StdlibInitFunc);
|
||||
modules.insert("types".to_string(), Box::new(types::mk_module) as StdlibInitFunc);
|
||||
modules.insert("_weakref".to_string(), Box::new(weakref::mk_module) as StdlibInitFunc);
|
||||
modules.insert("dis".to_string(), Box::new(dis::mk_module));
|
||||
modules.insert("json".to_string(), Box::new(json::mk_module));
|
||||
modules.insert("keyword".to_string(), Box::new(keyword::mk_module));
|
||||
modules.insert("math".to_string(), Box::new(math::mk_module));
|
||||
modules.insert("re".to_string(), Box::new(re::mk_module));
|
||||
modules.insert("random".to_string(), Box::new(random::mk_module));
|
||||
modules.insert("string".to_string(), Box::new(string::mk_module));
|
||||
modules.insert("struct".to_string(), Box::new(pystruct::mk_module));
|
||||
modules.insert("time".to_string(), Box::new(time_module::mk_module));
|
||||
modules.insert("tokenize".to_string(), Box::new(tokenize::mk_module));
|
||||
modules.insert("types".to_string(), Box::new(types::mk_module));
|
||||
modules.insert("_weakref".to_string(), Box::new(weakref::mk_module));
|
||||
|
||||
// disable some modules on WASM
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
modules.insert("io".to_string(), Box::new(io::mk_module) as StdlibInitFunc);
|
||||
modules.insert("os".to_string(), Box::new(os::mk_module) as StdlibInitFunc);
|
||||
modules.insert("io".to_string(), Box::new(io::mk_module));
|
||||
modules.insert("os".to_string(), Box::new(os::mk_module));
|
||||
modules.insert("socket".to_string(), Box::new(socket::mk_module));
|
||||
}
|
||||
|
||||
modules
|
||||
|
||||
315
vm/src/stdlib/socket.rs
Normal file
315
vm/src/stdlib/socket.rs
Normal file
@@ -0,0 +1,315 @@
|
||||
use crate::obj::objbytes;
|
||||
use crate::obj::objint;
|
||||
use crate::obj::objsequence::get_elements;
|
||||
use crate::obj::objstr;
|
||||
use crate::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
use num_traits::ToPrimitive;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum AddressFamily {
|
||||
AfUnix = 1,
|
||||
AfInet = 2,
|
||||
AfInet6 = 3,
|
||||
}
|
||||
|
||||
impl AddressFamily {
|
||||
fn from_i32(value: i32) -> AddressFamily {
|
||||
match value {
|
||||
1 => AddressFamily::AfUnix,
|
||||
2 => AddressFamily::AfInet,
|
||||
3 => AddressFamily::AfInet6,
|
||||
_ => panic!("Unknown value: {}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum SocketKind {
|
||||
SockStream = 1,
|
||||
SockDgram = 2,
|
||||
}
|
||||
|
||||
impl SocketKind {
|
||||
fn from_i32(value: i32) -> SocketKind {
|
||||
match value {
|
||||
1 => SocketKind::SockStream,
|
||||
2 => SocketKind::SockDgram,
|
||||
_ => panic!("Unknown value: {}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Connection {
|
||||
TcpListener(TcpListener),
|
||||
TcpStream(TcpStream),
|
||||
UdpSocket(UdpSocket),
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
fn accept(&mut self) -> io::Result<(TcpStream, SocketAddr)> {
|
||||
match self {
|
||||
Connection::TcpListener(con) => con.accept(),
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Connection {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Connection::TcpStream(con) => con.read(buf),
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Connection {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Connection::TcpStream(con) => con.write(buf),
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "oh no!")),
|
||||
}
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Socket {
|
||||
address_family: AddressFamily,
|
||||
sk: SocketKind,
|
||||
con: Option<Connection>,
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
fn new(address_family: AddressFamily, sk: SocketKind) -> Socket {
|
||||
Socket {
|
||||
address_family,
|
||||
sk: sk,
|
||||
con: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(cls, None),
|
||||
(family_int, Some(vm.ctx.int_type())),
|
||||
(kind_int, Some(vm.ctx.int_type()))
|
||||
]
|
||||
);
|
||||
|
||||
let address_family = AddressFamily::from_i32(objint::get_value(family_int).to_i32().unwrap());
|
||||
let kind = SocketKind::from_i32(objint::get_value(kind_int).to_i32().unwrap());
|
||||
|
||||
let socket = Socket::new(address_family, kind);
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Socket { socket },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn socket_connect(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))]
|
||||
);
|
||||
|
||||
let elements = get_elements(address);
|
||||
let host = objstr::get_value(&elements[0]);
|
||||
let port = objint::get_value(&elements[1]);
|
||||
|
||||
let address_string = format!("{}:{}", host, port.to_string());
|
||||
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_bind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, None), (address, Some(vm.ctx.tuple_type()))]
|
||||
);
|
||||
|
||||
let elements = get_elements(address);
|
||||
let host = objstr::get_value(&elements[0]);
|
||||
let port = objint::get_value(&elements[1]);
|
||||
|
||||
let address_string = format!("{}:{}", host, port.to_string());
|
||||
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_listen(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
fn socket_accept(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, None)]);
|
||||
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => {
|
||||
let ret = match socket.con {
|
||||
Some(ref mut v) => v.accept(),
|
||||
None => return Err(vm.new_type_error("".to_string())),
|
||||
};
|
||||
|
||||
let tcp_stream = match ret {
|
||||
Ok((socket, _addr)) => socket,
|
||||
_ => return Err(vm.new_type_error("".to_string())),
|
||||
};
|
||||
|
||||
let socket = Socket {
|
||||
address_family: socket.address_family.clone(),
|
||||
sk: socket.sk.clone(),
|
||||
con: Some(Connection::TcpStream(tcp_stream)),
|
||||
};
|
||||
|
||||
let sock_obj = PyObject::new(PyObjectPayload::Socket { socket }, mut_obj.typ());
|
||||
|
||||
let elements = vec![sock_obj, vm.get_none()];
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Sequence { elements },
|
||||
vm.ctx.tuple_type(),
|
||||
))
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_recv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, None), (bufsize, Some(vm.ctx.int_type()))]
|
||||
);
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => {
|
||||
let mut buffer = Vec::new();
|
||||
let _temp = match socket.con {
|
||||
Some(ref mut v) => v.read_to_end(&mut buffer).unwrap(),
|
||||
None => 0,
|
||||
};
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Bytes { value: buffer },
|
||||
vm.ctx.bytes_type(),
|
||||
))
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, None), (bytes, Some(vm.ctx.bytes_type()))]
|
||||
);
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => {
|
||||
match socket.con {
|
||||
Some(ref mut v) => v.write(&objbytes::get_value(&bytes)).unwrap(),
|
||||
None => return Err(vm.new_type_error("".to_string())),
|
||||
};
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn socket_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, None)]);
|
||||
let mut mut_obj = zelf.borrow_mut();
|
||||
|
||||
match mut_obj.payload {
|
||||
PyObjectPayload::Socket { ref mut socket } => match socket.address_family {
|
||||
AddressFamily::AfInet => match socket.sk {
|
||||
SocketKind::SockStream => {
|
||||
socket.con = None;
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
},
|
||||
_ => Err(vm.new_type_error("".to_string())),
|
||||
},
|
||||
_ => 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));
|
||||
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"AF_INET",
|
||||
ctx.new_int(AddressFamily::AfInet as i32),
|
||||
);
|
||||
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"SOCK_STREAM",
|
||||
ctx.new_int(SocketKind::SockStream 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));
|
||||
socket
|
||||
};
|
||||
ctx.set_attr(&py_mod, "socket", socket.clone());
|
||||
|
||||
py_mod
|
||||
}
|
||||
Reference in New Issue
Block a user