Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions crates/vm/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type UnpackFunc = fn(&VirtualMachine, &[u8]) -> PyObjectRef;

static OVERFLOW_MSG: &str = "total struct size too long"; // not a const to reduce code size

#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Endianness {
Native,
Little,
Expand All @@ -40,21 +40,30 @@ impl Endianness {
Some(b'>' | b'!') => Self::Big,
_ => return Self::Native,
};
chars.next().unwrap();

// SAFETY:
// We just ensured with `chars.peek()` that this is safe
unsafe {
let _ = chars.next().unwrap_unchecked();
}
e
}
}

trait ByteOrder {
fn convert<I: PrimInt>(i: I) -> I;
}

enum BigEndian {}

impl ByteOrder for BigEndian {
fn convert<I: PrimInt>(i: I) -> I {
i.to_be()
}
}

enum LittleEndian {}

impl ByteOrder for LittleEndian {
fn convert<I: PrimInt>(i: I) -> I {
i.to_le()
Expand All @@ -66,7 +75,7 @@ type NativeEndian = cfg_select! {
target_endian = "little" => LittleEndian,
};

#[derive(Copy, Clone, num_enum::TryFromPrimitive)]
#[derive(Copy, Clone, num_enum::TryFromPrimitive, Eq, PartialEq)]
#[repr(u8)]
pub(crate) enum FormatType {
Pad = b'x',
Expand Down Expand Up @@ -105,6 +114,7 @@ impl FormatType {
fn info(self, e: Endianness) -> &'static FormatInfo {
use FormatType::*;
use mem::{align_of, size_of};

macro_rules! native_info {
($t:ty) => {{
&FormatInfo {
Expand All @@ -115,6 +125,7 @@ impl FormatType {
}
}};
}

macro_rules! nonnative_info {
($t:ty, $end:ty) => {{
&FormatInfo {
Expand All @@ -125,6 +136,7 @@ impl FormatType {
}
}};
}

macro_rules! match_nonnative {
($zelf:expr, $end:ty) => {{
match $zelf {
Expand Down Expand Up @@ -158,6 +170,7 @@ impl FormatType {
}
}};
}

match e {
Endianness::Native => match self {
Pad | Str | Pascal => &FormatInfo {
Expand Down Expand Up @@ -381,6 +394,7 @@ pub(crate) struct FormatInfo {
pub pack: Option<PackFunc>,
pub unpack: Option<UnpackFunc>,
}

impl fmt::Debug for FormatInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FormatInfo")
Expand Down
8 changes: 3 additions & 5 deletions crates/vm/src/builtins/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ impl PyObjectRef {
pub fn try_to_bool(self, vm: &VirtualMachine) -> PyResult<bool> {
if self.is(&vm.ctx.true_value) {
return Ok(true);
}
if self.is(&vm.ctx.false_value) {
} else if self.is(&vm.ctx.false_value) {
return Ok(false);
}

Expand Down Expand Up @@ -83,10 +82,9 @@ impl Constructor for PyBool {
fn slot_new(zelf: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let x: Self::Args = args.bind(vm)?;
if !zelf.fast_isinstance(vm.ctx.types.type_type) {
let actual_class = zelf.class();
let actual_type = &actual_class.name();
return Err(vm.new_type_error(format!(
"requires a 'type' object but received a '{actual_type}'"
"requires a 'type' object but received a '{}'",
zelf.class().name()
)));
}
let val = x.map_or(Ok(false), |val| val.try_to_bool(vm))?;
Expand Down
56 changes: 30 additions & 26 deletions crates/vm/src/builtins/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,40 +43,40 @@ pub(crate) mod stack_analysis {
}

impl Kind {
fn from_i64(v: i64) -> Option<Self> {
match v {
1 => Some(Self::Iterator),
2 => Some(Self::Except),
3 => Some(Self::Object),
4 => Some(Self::Null),
5 => Some(Self::Lasti),
_ => None,
}
const fn from_i64(v: i64) -> Option<Self> {
Some(match v {
1 => Self::Iterator,
2 => Self::Except,
3 => Self::Object,
4 => Self::Null,
5 => Self::Lasti,
_ => return None,
})
}
}

pub(crate) fn push_value(stack: i64, kind: i64) -> i64 {
pub(crate) const fn push_value(stack: i64, kind: i64) -> i64 {
if (stack as u64) >= WILL_OVERFLOW {
OVERFLOWED
} else {
(stack << BITS_PER_BLOCK) | kind
}
}

pub(crate) fn pop_value(stack: i64) -> i64 {
pub(crate) const fn pop_value(stack: i64) -> i64 {
stack >> BITS_PER_BLOCK
}

pub(crate) fn top_of_stack(stack: i64) -> i64 {
pub(crate) const fn top_of_stack(stack: i64) -> i64 {
stack & MASK
}

fn peek(stack: i64, n: u32) -> i64 {
const fn peek(stack: i64, n: u32) -> i64 {
debug_assert!(n >= 1);
(stack >> (BITS_PER_BLOCK * (n - 1))) & MASK
}

fn stack_swap(stack: i64, n: u32) -> i64 {
const fn stack_swap(stack: i64, n: u32) -> i64 {
debug_assert!(n >= 1);
let to_swap = peek(stack, n);
let top = top_of_stack(stack);
Expand All @@ -85,7 +85,7 @@ pub(crate) mod stack_analysis {
(replaced_low & !MASK) | to_swap
}

fn pop_to_level(mut stack: i64, level: u32) -> i64 {
const fn pop_to_level(mut stack: i64, level: u32) -> i64 {
if level == 0 {
return EMPTY_STACK;
}
Expand All @@ -97,20 +97,21 @@ pub(crate) mod stack_analysis {
stack
}

fn compatible_kind(from: i64, to: i64) -> bool {
#[must_use]
const fn compatible_kind(from: i64, to: i64) -> bool {
if to == 0 {
return false;
}
if to == Kind::Object as i64 {
return from != Kind::Null as i64;
}
if to == Kind::Null as i64 {
return true;
false
} else if to == Kind::Object as i64 {
from != Kind::Null as i64
} else if to == Kind::Null as i64 {
true
} else {
from == to
}
from == to
}

pub(crate) fn compatible_stack(from_stack: i64, to_stack: i64) -> bool {
#[must_use]
pub(crate) const fn compatible_stack(from_stack: i64, to_stack: i64) -> bool {
if from_stack < 0 || to_stack < 0 {
return false;
}
Expand All @@ -131,14 +132,17 @@ pub(crate) mod stack_analysis {
to == 0
}

pub(crate) fn explain_incompatible_stack(to_stack: i64) -> &'static str {
pub(crate) const fn explain_incompatible_stack(to_stack: i64) -> &'static str {
debug_assert!(to_stack != 0);

if to_stack == OVERFLOWED {
return "stack is too deep to analyze";
}

if to_stack == UNINITIALIZED {
return "can't jump into an exception handler, or code may be unreachable";
}

match Kind::from_i64(top_of_stack(to_stack)) {
Some(Kind::Except) => "can't jump into an 'except' block as there's no exception",
Some(Kind::Lasti) => "can't jump into a re-raising block as there's no location",
Expand Down
13 changes: 10 additions & 3 deletions crates/vm/src/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,26 @@ fn format_missing_args(
missing: &mut Vec<impl core::fmt::Display>,
) -> String {
let count = missing.len();

let last = if missing.len() > 1 {
missing.pop()
} else {
None
};

let (and, right): (&str, String) = if let Some(last) = last {
(
if missing.len() == 1 {
"' and '"
} else {
"', and '"
},
format!("{last}"),
last.to_string(),
)
} else {
("", String::new())
};

format!(
"{qualname}() missing {count} required {kind} argument{}: '{}{}{right}'",
if count == 1 { "" } else { "s" },
Expand Down Expand Up @@ -1268,12 +1271,12 @@ impl PyBoundMethod {
}

#[inline]
pub(crate) fn function_obj(&self) -> &PyObjectRef {
pub(crate) const fn function_obj(&self) -> &PyObjectRef {
&self.function
}

#[inline]
pub(crate) fn self_obj(&self) -> &PyObjectRef {
pub(crate) const fn self_obj(&self) -> &PyObjectRef {
&self.object
}

Expand Down Expand Up @@ -1398,6 +1401,7 @@ impl Representable for PyBoundMethod {
pub(crate) struct PyCell {
contents: PyMutex<Option<PyObjectRef>>,
}

pub(crate) type PyCellRef = PyRef<PyCell>;

impl PyPayload for PyCell {
Expand Down Expand Up @@ -1426,6 +1430,7 @@ impl PyCell {
pub(crate) fn get(&self) -> Option<PyObjectRef> {
self.contents.lock().clone()
}

pub(crate) fn set(&self, x: Option<PyObjectRef>) {
*self.contents.lock() = x;
}
Expand All @@ -1435,6 +1440,7 @@ impl PyCell {
self.get()
.ok_or_else(|| vm.new_value_error("Cell is empty"))
}

#[pygetset(setter)]
fn set_cell_contents(&self, x: PySetterValue) {
match x {
Expand Down Expand Up @@ -1488,6 +1494,7 @@ pub(crate) fn vectorcall_function(
args.truncate(nargs);
FuncArgs::from(args)
};

zelf.invoke(func_args, vm)
}

Expand Down
33 changes: 18 additions & 15 deletions crates/vm/src/builtins/super.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl Initializer for PySuper {
if frame.code.arg_count == 0 {
return Err(vm.new_runtime_error("super(): no arguments"));
}

// SAFETY: Frame is current and not concurrently mutated.
use rustpython_compiler_core::bytecode::CO_FAST_CELL;
let obj = unsafe { frame.fastlocals() }[0]
Expand Down Expand Up @@ -165,9 +166,9 @@ impl GetAttr for PySuper {
Some(o) => o.clone(),
None => return skip(zelf, name),
};

// We want __class__ to return the class of the super object
// (i.e. super, or a subclass), not the class of su->obj.

if name.as_bytes() == b"__class__" {
return skip(zelf, name);
}
Expand Down Expand Up @@ -280,21 +281,23 @@ pub(crate) fn init(context: &'static Context) {
let super_type = &context.types.super_type;
PySuper::extend_class(context, super_type);

let super_doc = "super() -> same as super(__class__, <first argument>)\n\
super(type) -> unbound super object\n\
super(type, obj) -> bound super object; requires isinstance(obj, type)\n\
super(type, type2) -> bound super object; requires issubclass(type2, type)\n\
Typical use to call a cooperative superclass method:\n\
class C(B):\n \
def meth(self, arg):\n \
super().meth(arg)\n\
This works for class methods too:\n\
class C(B):\n \
@classmethod\n \
def cmeth(cls, arg):\n \
super().cmeth(arg)\n";
const SUPER_DOC: &str = "\
super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super().meth(arg)
This works for class methods too:
class C(B):
@classmethod
def cmeth(cls, arg):
super().cmeth(arg)
";

extend_class!(context, super_type, {
"__doc__" => context.new_str(super_doc),
"__doc__" => context.new_str(SUPER_DOC),
});
}
Loading
Loading