Merge branch 'master' of https://github.com/RustPython/RustPython into extend-socket

This commit is contained in:
lynskylate
2019-08-25 15:01:21 +08:00
23 changed files with 6989 additions and 80 deletions

2472
Lib/datetime.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -59,10 +59,19 @@ bitflags! {
pub type Label = usize;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// An indication where the name must be accessed.
pub enum NameScope {
/// The name will be in the local scope.
Local,
/// The name will be located in scope surrounding the current scope.
NonLocal,
/// The name will be in global scope.
Global,
/// The name will be located in any scope between the current scope and the top scope.
Free,
}
/// Transforms a value prior to formatting it.

View File

@@ -282,8 +282,8 @@ impl<O: OutputStream> Compiler<O> {
match symbol.scope {
SymbolScope::Global => bytecode::NameScope::Global,
SymbolScope::Nonlocal => bytecode::NameScope::NonLocal,
SymbolScope::Unknown => bytecode::NameScope::Local,
SymbolScope::Local => bytecode::NameScope::Local,
SymbolScope::Unknown => bytecode::NameScope::Free,
SymbolScope::Local => bytecode::NameScope::Free,
}
}
@@ -500,7 +500,7 @@ impl<O: OutputStream> Compiler<O> {
self.compile_jump_if(test, true, end_label)?;
self.emit(Instruction::LoadName {
name: String::from("AssertionError"),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Global,
});
match msg {
Some(e) => {
@@ -736,7 +736,7 @@ impl<O: OutputStream> Compiler<O> {
// Check exception type:
self.emit(Instruction::LoadName {
name: String::from("isinstance"),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Global,
});
self.emit(Instruction::Rotate { amount: 2 });
self.compile_expression(exc_type)?;
@@ -931,11 +931,11 @@ impl<O: OutputStream> Compiler<O> {
self.emit(Instruction::LoadName {
name: "__name__".to_string(),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Free,
});
self.emit(Instruction::StoreName {
name: "__module__".to_string(),
scope: bytecode::NameScope::Local,
scope: bytecode::NameScope::Free,
});
self.compile_statements(new_body)?;
self.emit(Instruction::LoadConst {

View File

@@ -31,6 +31,9 @@ pub fn statements_to_symbol_table(
/// Captures all symbols in the current scope, and has a list of subscopes in this scope.
#[derive(Clone, Default)]
pub struct SymbolTable {
/// The name of this symbol table. Often the name of the class or function.
pub name: String,
/// A set of symbols present on this scope level.
pub symbols: IndexMap<String, Symbol>,
@@ -40,8 +43,9 @@ pub struct SymbolTable {
}
impl SymbolTable {
fn new() -> Self {
fn new(name: String) -> Self {
SymbolTable {
name,
symbols: Default::default(),
sub_tables: vec![],
}
@@ -209,20 +213,22 @@ impl SymbolTableAnalyzer {
if symbol.is_assigned || symbol.is_parameter {
symbol.scope = SymbolScope::Local;
} else {
// TODO: comment this out and make it work properly:
/*
let found_in_outer_scope = self
.tables
.iter()
.any(|t| t.symbols.contains_key(&symbol.name));
// Interesting stuff about the __class__ variable:
// https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
let found_in_outer_scope = (symbol.name == "__class__")
|| self
.tables
.iter()
.skip(1)
.any(|t| t.symbols.contains_key(&symbol.name));
if found_in_outer_scope {
// Symbol is in some outer scope.
symbol.is_free = true;
} else {
// Well, it must be a global then :)
// symbol.scope = SymbolScope::Global;
symbol.scope = SymbolScope::Global;
}
*/
}
}
}
@@ -257,8 +263,7 @@ enum ExpressionContext {
impl SymbolTableBuilder {
fn prepare(&mut self) {
let table = SymbolTable::new();
self.tables.push(table);
self.enter_block("top")
}
fn finish(&mut self) -> Result<SymbolTable, SymbolTableError> {
@@ -268,9 +273,9 @@ impl SymbolTableBuilder {
Ok(symbol_table)
}
fn enter_block(&mut self) {
fn enter_block(&mut self, name: &str) {
// let parent = Some(self.tables.last().unwrap().clone());
let table = SymbolTable::new();
let table = SymbolTable::new(name.to_string());
self.tables.push(table);
}
@@ -343,7 +348,7 @@ impl SymbolTableBuilder {
if let Some(expression) = returns {
self.scan_expression(expression, &ExpressionContext::Load)?;
}
self.enter_function(args)?;
self.enter_function(name, args)?;
self.scan_statements(body)?;
self.leave_block();
}
@@ -355,7 +360,7 @@ impl SymbolTableBuilder {
decorator_list,
} => {
self.register_name(name, SymbolUsage::Assigned)?;
self.enter_block();
self.enter_block(name);
self.scan_statements(body)?;
self.leave_block();
self.scan_expressions(bases, &ExpressionContext::Load)?;
@@ -607,7 +612,7 @@ impl SymbolTableBuilder {
}
}
Lambda { args, body } => {
self.enter_function(args)?;
self.enter_function("lambda", args)?;
self.scan_expression(body, &ExpressionContext::Load)?;
self.leave_block();
}
@@ -620,7 +625,7 @@ impl SymbolTableBuilder {
Ok(())
}
fn enter_function(&mut self, args: &ast::Parameters) -> SymbolTableResult {
fn enter_function(&mut self, name: &str, args: &ast::Parameters) -> SymbolTableResult {
// Evaluate eventual default parameters:
self.scan_expressions(&args.defaults, &ExpressionContext::Load)?;
for kw_default in &args.kw_defaults {
@@ -639,7 +644,7 @@ impl SymbolTableBuilder {
self.scan_parameter_annotation(name)?;
}
self.enter_block();
self.enter_block(name);
// Fill scope with parameter names:
self.scan_parameters(&args.args)?;

View File

@@ -73,4 +73,4 @@ print()
print('======== dis.dis ========')
print()
co = compile(source, filename, 'exec')
print(dis.dis(co))
dis.dis(co)

View File

@@ -0,0 +1,15 @@
from array import array
a1 = array("b", [0, 1, 2, 3])
assert a1.tobytes() == b"\x00\x01\x02\x03"
assert a1[2] == 2
assert list(a1) == [0, 1, 2, 3]
a1.reverse()
assert a1 == array("B", [3, 2, 1, 0])
a1.extend([4, 5, 6, 7])
assert a1 == array("h", [3, 2, 1, 0, 4, 5, 6, 7])

View File

@@ -97,14 +97,57 @@ assert 10 // -4 == -3
assert -10 // -4 == 2
assert int() == 0
assert int(1) == 1
assert int("101", 2) == 5
assert int("101", base=2) == 5
assert int(1) == 1
# implied base
assert int('1', base=0) == 1
assert int('123', base=0) == 123
assert int('0b101', base=0) == 5
assert int('0B101', base=0) == 5
assert int('0o100', base=0) == 64
assert int('0O100', base=0) == 64
assert int('0xFF', base=0) == 255
assert int('0XFF', base=0) == 255
with assertRaises(ValueError):
int('0xFF', base=10)
with assertRaises(ValueError):
int('0oFF', base=10)
with assertRaises(ValueError):
int('0bFF', base=10)
with assertRaises(ValueError):
int('0bFF', base=10)
with assertRaises(ValueError):
int(b"F\xc3\xb8\xc3\xb6\xbbB\xc3\xa5r")
with assertRaises(ValueError):
int(b"F\xc3\xb8\xc3\xb6\xbbB\xc3\xa5r")
# underscore
assert int('0xFF_FF_FF', base=16) == 16_777_215
with assertRaises(ValueError):
int("_123_")
with assertRaises(ValueError):
int("123_")
with assertRaises(ValueError):
int("_123")
with assertRaises(ValueError):
int("1__23")
# signed
assert int('-123') == -123
assert int('+0b101', base=2) == +5
# trailing spaces
assert int(' 1') == 1
assert int('1 ') == 1
assert int(' 1 ') == 1
assert int('10', base=0) == 10
# type byte, signed, implied base
assert int(b' -0XFF ', base=0) == -255
assert int.from_bytes(b'\x00\x10', 'big') == 16
assert int.from_bytes(b'\x00\x10', 'little') == 4096
assert int.from_bytes(b'\xfc\x00', 'big', signed=True) == -1024
@@ -179,4 +222,4 @@ assert (1).__round__(0) == 1
assert_raises(TypeError, lambda: (0).__round__(None))
assert_raises(TypeError, lambda: (1).__round__(None))
assert_raises(TypeError, lambda: (0).__round__(0.0))
assert_raises(TypeError, lambda: (1).__round__(0.0))
assert_raises(TypeError, lambda: (1).__round__(0.0))

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,17 @@ def assert_raises(exc_type, expr, msg=None):
assert False, failmsg
class assertRaises:
def assertRaises(expected, *args, **kw):
if not args:
assert not kw
return _assertRaises(expected)
else:
f, f_args = args[0], args[1:]
with _assertRaises(expected):
f(*f_args, **kw)
class _assertRaises:
def __init__(self, expected):
self.expected = expected
self.exception = None

View File

@@ -95,7 +95,6 @@ fn print_traceback_entry(vm: &VirtualMachine, tb_entry: &PyObjectRef) {
print_source_line(filename, lineno.parse().unwrap());
} else {
println!(" File ??");
return;
}
}

View File

@@ -907,6 +907,9 @@ impl Frame {
bytecode::NameScope::Local => {
self.scope.store_name(vm, name, obj);
}
bytecode::NameScope::Free => {
self.scope.store_name(vm, name, obj);
}
}
Ok(None)
}
@@ -928,7 +931,8 @@ impl Frame {
let optional_value = match name_scope {
bytecode::NameScope::Global => self.scope.load_global(vm, name),
bytecode::NameScope::NonLocal => self.scope.load_cell(vm, name),
bytecode::NameScope::Local => self.scope.load_name(&vm, name),
bytecode::NameScope::Local => self.scope.load_local(&vm, name),
bytecode::NameScope::Free => self.scope.load_name(&vm, name),
};
let value = match optional_value {

View File

@@ -273,3 +273,13 @@ macro_rules! flame_guard {
let _guard = ::flame::start_guard($name);
};
}
#[macro_export]
macro_rules! class_or_notimplemented {
($vm:expr, $t:ty, $obj:expr) => {
match $crate::pyobject::PyObject::downcast::<$t>($obj) {
Ok(pyref) => pyref,
Err(_) => return Ok($vm.ctx.not_implemented()),
}
};
}

View File

@@ -100,12 +100,12 @@ impl ByteInnerNewOptions {
if let OptionalArg::Present(eval) = self.val_option {
if let Ok(input) = eval.downcast::<PyString>() {
let inner = PyByteInner::from_string(&input.value, enc.as_str(), vm)?;
return Ok(inner);
Ok(inner)
} else {
return Err(vm.new_type_error("encoding without a string argument".to_string()));
Err(vm.new_type_error("encoding without a string argument".to_string()))
}
} else {
return Err(vm.new_type_error("encoding without a string argument".to_string()));
Err(vm.new_type_error("encoding without a string argument".to_string()))
}
// Only one argument
} else {

View File

@@ -40,6 +40,11 @@ impl IntoPyObject for f64 {
Ok(vm.ctx.new_float(self))
}
}
impl IntoPyObject for f32 {
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
Ok(vm.ctx.new_float(f64::from(self)))
}
}
impl TryFromObject for f64 {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
@@ -66,6 +71,18 @@ pub fn try_float(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f6
})
}
macro_rules! impl_try_from_object_float {
($($t:ty),*) => {
$(impl TryFromObject for $t {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
PyFloatRef::try_from_object(vm, obj).map(|f| f.to_f64() as $t)
}
})*
};
}
impl_try_from_object_float!(f32, f64);
fn inner_div(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<f64> {
if v2 != 0.0 {
Ok(v1 / v2)

View File

@@ -1,8 +1,9 @@
use std::fmt;
use std::str;
use num_bigint::{BigInt, Sign};
use num_integer::Integer;
use num_traits::{One, Pow, Signed, ToPrimitive, Zero};
use num_traits::{Num, One, Pow, Signed, ToPrimitive, Zero};
use crate::format::FormatSpec;
use crate::function::{KwArgs, OptionalArg, PyFuncArgs};
@@ -713,7 +714,9 @@ impl IntOptions {
fn get_int_value(self, vm: &VirtualMachine) -> PyResult<BigInt> {
if let OptionalArg::Present(val) = self.val_options {
let base = if let OptionalArg::Present(base) = self.base {
if !objtype::isinstance(&val, &vm.ctx.str_type()) {
if !(objtype::isinstance(&val, &vm.ctx.str_type())
|| objtype::isinstance(&val, &vm.ctx.bytes_type()))
{
return Err(vm.new_type_error(
"int() can't convert non-string with explicit base".to_string(),
));
@@ -736,21 +739,22 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul
}
// Casting function:
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, mut base: u32) -> PyResult<BigInt> {
if base == 0 {
base = 10
} else if base < 2 || base > 36 {
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
if base != 0 && (base < 2 || base > 36) {
return Err(vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_string()));
}
match_class!(obj.clone(),
s @ PyString => {
i32::from_str_radix(s.as_str().trim(), base)
.map(BigInt::from)
.map_err(|_|vm.new_value_error(format!(
"invalid literal for int() with base {}: '{}'",
base, s
)))
string @ PyString => {
let s = string.value.as_str().trim();
str_to_int(vm, s, base)
},
bytes @ PyBytes => {
let bytes = bytes.get_value();
let s = std::str::from_utf8(bytes)
.map(|s| s.trim())
.map_err(|e| vm.new_value_error(format!("utf8 decode error: {}", e)))?;
str_to_int(vm, s, base)
},
obj => {
let method = vm.get_method_or_type_error(obj.clone(), "__int__", || {
@@ -766,6 +770,76 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, mut base: u32) -> PyResult
)
}
fn str_to_int(vm: &VirtualMachine, literal: &str, mut base: u32) -> PyResult<BigInt> {
let mut buf = validate_literal(vm, literal, base)?;
let is_signed = buf.starts_with('+') || buf.starts_with('-');
let radix_range = if is_signed { 1..3 } else { 0..2 };
let radix_candidate = buf.get(radix_range.clone());
// try to find base
if let Some(radix_candidate) = radix_candidate {
if let Some(matched_radix) = detect_base(&radix_candidate) {
if base != 0 && base != matched_radix {
return Err(invalid_literal(vm, literal, base));
} else {
base = matched_radix;
}
buf.drain(radix_range);
}
}
// base still not found, try to use default
if base == 0 {
if buf.starts_with('0') {
return Err(invalid_literal(vm, literal, base));
}
base = 10;
}
BigInt::from_str_radix(&buf, base).map_err(|_err| invalid_literal(vm, literal, base))
}
fn validate_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyResult<String> {
if literal.starts_with('_') || literal.ends_with('_') {
return Err(invalid_literal(vm, literal, base));
}
let mut buf = String::with_capacity(literal.len());
let mut last_tok = None;
for c in literal.chars() {
if !(c.is_ascii_alphanumeric() || c == '_' || c == '+' || c == '-') {
return Err(invalid_literal(vm, literal, base));
}
if c == '_' && Some(c) == last_tok {
return Err(invalid_literal(vm, literal, base));
}
last_tok = Some(c);
buf.push(c);
}
Ok(buf)
}
fn detect_base(literal: &str) -> Option<u32> {
match literal {
"0x" | "0X" => Some(16),
"0o" | "0O" => Some(8),
"0b" | "0B" => Some(2),
_ => None,
}
}
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: u32) -> PyObjectRef {
vm.new_value_error(format!(
"invalid literal for int() with base {}: '{}'",
base, literal
))
}
// Retrieve inner int value:
pub fn get_value(obj: &PyObjectRef) -> &BigInt {
&get_py_int(obj).value

View File

@@ -471,7 +471,9 @@ pub fn is_valid_slice_arg(
match_class!(value,
i @ PyInt => Ok(Some(i.as_bigint().clone())),
_obj @ PyNone => Ok(None),
_=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));}
_=> {
Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()))
}
// TODO: check for an __index__ method
)
} else {

View File

@@ -349,21 +349,35 @@ impl PyString {
fn split(
&self,
pattern: OptionalArg<PyStringRef>,
num: OptionalArg<usize>,
num: OptionalArg<isize>,
vm: &VirtualMachine,
) -> PyObjectRef {
let value = &self.value;
let pattern = match pattern {
OptionalArg::Present(ref s) => &s.value,
OptionalArg::Missing => " ",
OptionalArg::Present(ref s) => Some(s.as_str()),
OptionalArg::Missing => None,
};
let num_splits = num.into_option().unwrap_or(-1);
let elements: Vec<_> = match (pattern, num_splits.is_negative()) {
(Some(pattern), true) => value
.split(pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(Some(pattern), false) => value
.splitn(num_splits as usize + 1, pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(None, true) => value
.split(|c: char| c.is_ascii_whitespace())
.filter(|s| !s.is_empty())
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(None, false) => value
.splitn(num_splits as usize + 1, |c: char| c.is_ascii_whitespace())
.filter(|s| !s.is_empty())
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
};
let num_splits = num
.into_option()
.unwrap_or_else(|| value.split(pattern).count());
let elements = value
.splitn(num_splits + 1, pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect();
vm.ctx.new_list(elements)
}
@@ -371,21 +385,35 @@ impl PyString {
fn rsplit(
&self,
pattern: OptionalArg<PyStringRef>,
num: OptionalArg<usize>,
num: OptionalArg<isize>,
vm: &VirtualMachine,
) -> PyObjectRef {
let value = &self.value;
let pattern = match pattern {
OptionalArg::Present(ref s) => &s.value,
OptionalArg::Missing => " ",
OptionalArg::Present(ref s) => Some(s.as_str()),
OptionalArg::Missing => None,
};
let num_splits = num.into_option().unwrap_or(-1);
let mut elements: Vec<_> = match (pattern, num_splits.is_negative()) {
(Some(pattern), true) => value
.rsplit(pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(Some(pattern), false) => value
.rsplitn(num_splits as usize + 1, pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(None, true) => value
.rsplit(|c: char| c.is_ascii_whitespace())
.filter(|s| !s.is_empty())
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
(None, false) => value
.rsplitn(num_splits as usize + 1, |c: char| c.is_ascii_whitespace())
.filter(|s| !s.is_empty())
.map(|o| vm.ctx.new_str(o.to_string()))
.collect(),
};
let num_splits = num
.into_option()
.unwrap_or_else(|| value.split(pattern).count());
let mut elements: Vec<_> = value
.rsplitn(num_splits + 1, pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect();
// Unlike Python rsplit, Rust rsplitn returns an iterator that
// starts from the end of the string.
elements.reverse();

View File

@@ -312,6 +312,8 @@ fn type_dict_setter(_instance: PyClassRef, _value: PyObjectRef, vm: &VirtualMach
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
flame_guard!(format!("class_get_attr({:?})", attr_name));
if let Some(item) = class.attributes.borrow().get(attr_name).cloned() {
return Some(item);
}

View File

@@ -123,6 +123,7 @@ pub trait NameProtocol {
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
fn delete_name(&self, vm: &VirtualMachine, name: &str) -> PyResult;
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
@@ -142,6 +143,12 @@ impl NameProtocol for Scope {
self.load_global(vm, name)
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
/// Load a local name. Only check the local dictionary for the given name.
fn load_local(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
self.get_locals().get_item_option(name, vm).unwrap()
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
for dict in self.locals.iter().skip(1) {
@@ -170,7 +177,17 @@ impl NameProtocol for Scope {
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
/// Load a global name.
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
// First, take a look in the outmost local scope (the scope at top level)
let last_local_dict = self.locals.iter().last();
if let Some(local_dict) = last_local_dict {
if let Some(value) = local_dict.get_item_option(name, vm).unwrap() {
return Some(value);
}
}
// Now, take a look at the globals or builtins.
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
Some(value)
} else {

392
vm/src/stdlib/array.rs Normal file
View File

@@ -0,0 +1,392 @@
use crate::function::OptionalArg;
use crate::obj::objbytes::PyBytesRef;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::obj::{objbool, objiter};
use crate::pyobject::{
IntoPyObject, PyClassImpl, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
};
use crate::VirtualMachine;
use std::cell::{Cell, RefCell};
use std::fmt;
struct ArrayTypeSpecifierError {
_priv: (),
}
impl fmt::Display for ArrayTypeSpecifierError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)"
)
}
}
macro_rules! def_array_enum {
($(($n:ident, $t:ident, $c:literal)),*$(,)?) => {
#[derive(Debug)]
enum ArrayContentType {
$($n(Vec<$t>),)*
}
#[allow(clippy::naive_bytecount, clippy::float_cmp)]
impl ArrayContentType {
fn from_char(c: char) -> Result<Self, ArrayTypeSpecifierError> {
match c {
$($c => Ok(ArrayContentType::$n(Vec::new())),)*
_ => Err(ArrayTypeSpecifierError { _priv: () }),
}
}
fn typecode(&self) -> char {
match self {
$(ArrayContentType::$n(_) => $c,)*
}
}
fn itemsize(&self) -> usize {
match self {
$(ArrayContentType::$n(_) => std::mem::size_of::<$t>(),)*
}
}
fn addr(&self) -> usize {
match self {
$(ArrayContentType::$n(v) => v.as_ptr() as usize,)*
}
}
fn len(&self) -> usize {
match self {
$(ArrayContentType::$n(v) => v.len(),)*
}
}
fn push(&mut self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
match self {
$(ArrayContentType::$n(v) => {
let val = $t::try_from_object(vm, obj)?;
v.push(val);
})*
}
Ok(())
}
fn pop(&mut self, i: usize, vm: &VirtualMachine) -> PyResult {
match self {
$(ArrayContentType::$n(v) => {
v.remove(i).into_pyobject(vm)
})*
}
}
fn insert(&mut self, i: usize, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
match self {
$(ArrayContentType::$n(v) => {
let val = $t::try_from_object(vm, obj)?;
v.insert(i, val);
})*
}
Ok(())
}
fn count(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
match self {
$(ArrayContentType::$n(v) => {
let val = $t::try_from_object(vm, obj)?;
Ok(v.iter().filter(|&&a| a == val).count())
})*
}
}
fn frombytes(&mut self, b: &[u8]) {
match self {
$(ArrayContentType::$n(v) => {
// safe because every configuration of bytes for the types we
// support are valid
let ptr = b.as_ptr() as *const $t;
let ptr_len = b.len() / std::mem::size_of::<$t>();
let slice = unsafe { std::slice::from_raw_parts(ptr, ptr_len) };
v.extend_from_slice(slice);
})*
}
}
fn tobytes(&self) -> Vec<u8> {
match self {
$(ArrayContentType::$n(v) => {
// safe because we're just reading memory as bytes
let ptr = v.as_ptr() as *const u8;
let ptr_len = v.len() * std::mem::size_of::<$t>();
let slice = unsafe { std::slice::from_raw_parts(ptr, ptr_len) };
slice.to_vec()
})*
}
}
fn index(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<usize>> {
match self {
$(ArrayContentType::$n(v) => {
let val = $t::try_from_object(vm, x)?;
Ok(v.iter().position(|&a| a == val))
})*
}
}
fn reverse(&mut self) {
match self {
$(ArrayContentType::$n(v) => v.reverse(),)*
}
}
fn getitem(&self, i: usize, vm: &VirtualMachine) -> Option<PyResult> {
match self {
$(ArrayContentType::$n(v) => v.get(i).map(|x| x.into_pyobject(vm)),)*
}
}
fn iter<'a>(&'a self, vm: &'a VirtualMachine) -> impl Iterator<Item = PyResult> + 'a {
let mut i = 0;
std::iter::from_fn(move || {
let ret = self.getitem(i, vm);
i += 1;
ret
})
}
}
};
}
def_array_enum!(
(SignedByte, i8, 'b'),
(UnsignedByte, u8, 'B'),
// TODO: support unicode char
(SignedShort, i16, 'h'),
(UnsignedShort, u16, 'H'),
(SignedInt, i16, 'i'),
(UnsignedInt, u16, 'I'),
(SignedLong, i32, 'l'),
(UnsignedLong, u32, 'L'),
(SignedLongLong, i64, 'q'),
(UnsignedLongLong, u64, 'Q'),
(Float, f32, 'f'),
(Double, f64, 'd'),
);
#[pyclass]
#[derive(Debug)]
pub struct PyArray {
array: RefCell<ArrayContentType>,
}
pub type PyArrayRef = PyRef<PyArray>;
impl PyValue for PyArray {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("array", "array")
}
}
#[pyimpl]
impl PyArray {
#[pymethod(name = "__new__")]
fn new(
cls: PyClassRef,
spec: PyStringRef,
init: OptionalArg<PyIterable>,
vm: &VirtualMachine,
) -> PyResult<PyArrayRef> {
let spec = match spec.as_str().len() {
1 => spec.as_str().chars().next().unwrap(),
_ => {
return Err(vm.new_type_error(
"array() argument 1 must be a unicode character, not str".to_owned(),
))
}
};
let array =
ArrayContentType::from_char(spec).map_err(|err| vm.new_value_error(err.to_string()))?;
let zelf = PyArray {
array: RefCell::new(array),
};
if let OptionalArg::Present(init) = init {
zelf.extend(init, vm)?;
}
zelf.into_ref_with_type(vm, cls)
}
#[pyproperty]
fn typecode(&self, _vm: &VirtualMachine) -> String {
self.array.borrow().typecode().to_string()
}
#[pyproperty]
fn itemsize(&self, _vm: &VirtualMachine) -> usize {
self.array.borrow().itemsize()
}
#[pymethod]
fn append(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
self.array.borrow_mut().push(x, vm)
}
#[pymethod]
fn buffer_info(&self, _vm: &VirtualMachine) -> (usize, usize) {
let array = self.array.borrow();
(array.addr(), array.len())
}
#[pymethod]
fn count(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
self.array.borrow().count(x, vm)
}
fn idx(&self, i: isize, vm: &VirtualMachine) -> PyResult<usize> {
let len = self.array.borrow().len();
if len == 0 {
return Err(vm.new_index_error("pop from empty array".to_owned()));
}
let i = if i.is_negative() {
len - i.abs() as usize
} else {
i as usize
};
if i > len - 1 {
return Err(vm.new_index_error("pop index out of range".to_owned()));
}
Ok(i)
}
#[pymethod]
fn extend(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
let mut array = self.array.borrow_mut();
for elem in iter.iter(vm)? {
array.push(elem?, vm)?;
}
Ok(())
}
#[pymethod]
fn frombytes(&self, b: PyBytesRef, vm: &VirtualMachine) -> PyResult<()> {
let b = b.get_value();
let itemsize = self.array.borrow().itemsize();
if b.len() % itemsize != 0 {
return Err(vm.new_value_error("bytes length not a multiple of item size".to_owned()));
}
if b.len() / itemsize > 0 {
self.array.borrow_mut().frombytes(&b);
}
Ok(())
}
#[pymethod]
fn index(&self, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
self.array
.borrow()
.index(x, vm)?
.ok_or_else(|| vm.new_value_error("x not in array".to_owned()))
}
#[pymethod]
fn insert(&self, i: isize, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let i = self.idx(i, vm)?;
self.array.borrow_mut().insert(i, x, vm)
}
#[pymethod]
fn pop(&self, i: OptionalArg<isize>, vm: &VirtualMachine) -> PyResult {
let i = self.idx(i.unwrap_or(-1), vm)?;
self.array.borrow_mut().pop(i, vm)
}
#[pymethod]
fn tobytes(&self, _vm: &VirtualMachine) -> Vec<u8> {
self.array.borrow().tobytes()
}
#[pymethod]
fn reverse(&self, _vm: &VirtualMachine) {
self.array.borrow_mut().reverse()
}
#[pymethod(name = "__getitem__")]
fn getitem(&self, i: isize, vm: &VirtualMachine) -> PyResult {
let i = self.idx(i, vm)?;
self.array
.borrow()
.getitem(i, vm)
.unwrap_or_else(|| Err(vm.new_index_error("array index out of range".to_owned())))
}
#[pymethod(name = "__eq__")]
fn eq(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let lhs = class_or_notimplemented!(vm, Self, lhs);
let rhs = class_or_notimplemented!(vm, Self, rhs);
let lhs = lhs.array.borrow();
let rhs = rhs.array.borrow();
if lhs.len() != rhs.len() {
Ok(vm.new_bool(false))
} else {
for (a, b) in lhs.iter(vm).zip(rhs.iter(vm)) {
let ne = objbool::boolval(vm, vm._ne(a?, b?)?)?;
if ne {
return Ok(vm.new_bool(false));
}
}
Ok(vm.new_bool(true))
}
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyArrayIter {
PyArrayIter {
position: Cell::new(0),
array: zelf,
}
}
}
#[pyclass]
#[derive(Debug)]
pub struct PyArrayIter {
position: Cell<usize>,
array: PyArrayRef,
}
impl PyValue for PyArrayIter {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("array", "arrayiterator")
}
}
#[pyimpl]
impl PyArrayIter {
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
if self.position.get() < self.array.array.borrow().len() {
let ret = self
.array
.array
.borrow()
.getitem(self.position.get(), vm)
.unwrap()?;
self.position.set(self.position.get() + 1);
Ok(ret)
} else {
Err(objiter::new_stop_iteration(vm))
}
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
py_module!(vm, "array", {
"array" => PyArray::make_class(&vm.ctx),
"arrayiterator" => PyArrayIter::make_class(&vm.ctx),
})
}

View File

@@ -1,3 +1,4 @@
mod array;
#[cfg(feature = "rustpython-parser")]
mod ast;
mod binascii;
@@ -54,7 +55,8 @@ pub type StdlibInitFunc = Box<dyn Fn(&VirtualMachine) -> PyObjectRef>;
pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
#[allow(unused_mut)]
let mut modules = hashmap! {
"binascii".to_string() => Box::new(binascii::make_module) as StdlibInitFunc,
"array".to_string() => Box::new(array::make_module) as StdlibInitFunc,
"binascii".to_string() => Box::new(binascii::make_module),
"dis".to_string() => Box::new(dis::make_module),
"_codecs".to_string() => Box::new(codecs::make_module),
"_collections".to_string() => Box::new(collections::make_module),

View File

@@ -34,7 +34,7 @@ fn symtable_symtable(
let symtable =
source_to_symtable(&source.value, mode).map_err(|err| vm.new_syntax_error(&err))?;
let py_symbol_table = to_py_symbol_table("top".to_string(), symtable);
let py_symbol_table = to_py_symbol_table(symtable);
Ok(py_symbol_table.into_ref(vm))
}
@@ -56,8 +56,8 @@ fn source_to_symtable(
Ok(symtable)
}
fn to_py_symbol_table(name: String, symtable: symboltable::SymbolTable) -> PySymbolTable {
PySymbolTable { name, symtable }
fn to_py_symbol_table(symtable: symboltable::SymbolTable) -> PySymbolTable {
PySymbolTable { symtable }
}
type PySymbolTableRef = PyRef<PySymbolTable>;
@@ -65,7 +65,6 @@ type PySymbolRef = PyRef<PySymbol>;
#[pyclass(name = "SymbolTable")]
struct PySymbolTable {
name: String,
symtable: symboltable::SymbolTable,
}
@@ -85,7 +84,7 @@ impl PyValue for PySymbolTable {
impl PySymbolTable {
#[pymethod(name = "get_name")]
fn get_name(&self, vm: &VirtualMachine) -> PyResult {
Ok(vm.ctx.new_str(self.name.clone()))
Ok(vm.ctx.new_str(self.symtable.name.clone()))
}
#[pymethod(name = "lookup")]
@@ -118,11 +117,7 @@ impl PySymbolTable {
.symtable
.sub_tables
.iter()
.map(|s| {
to_py_symbol_table("bla".to_string(), s.clone())
.into_ref(vm)
.into_object()
})
.map(|t| to_py_symbol_table(t.clone()).into_ref(vm).into_object())
.collect();
Ok(vm.ctx.new_list(children))
}

View File

@@ -534,6 +534,8 @@ impl VirtualMachine {
where
T: Into<PyFuncArgs>,
{
flame_guard!(format!("call_method({:?})", method_name));
// This is only used in the vm for magic methods, which use a greatly simplified attribute lookup.
let cls = obj.class();
match objtype::class_get_attr(&cls, method_name) {
@@ -552,7 +554,6 @@ impl VirtualMachine {
}
}
#[cfg_attr(feature = "flame-it", flame("VirtualMachine"))]
fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult {
vm_trace!("Invoke: {:?} {:?}", func_ref, args);