Merge pull request #2365 from RustPython/coolreader18/optimize-bytecode-format

Optimize the size of Instruction
This commit is contained in:
Noah
2020-12-16 09:01:27 -06:00
committed by GitHub
22 changed files with 739 additions and 691 deletions

44
Cargo.lock generated
View File

@@ -755,26 +755,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fehler"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5729fe49ba028cd550747b6e62cd3d841beccab5390aa398538c31a2d983635"
dependencies = [
"fehler-macros",
]
[[package]]
name = "fehler-macros"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccb5acb1045ebbfa222e2c50679e392a71dd77030b78fb0189f2d9c5974400f9"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fixedbitset"
version = "0.2.0"
@@ -1129,16 +1109,13 @@ dependencies = [
]
[[package]]
name = "lz-fear"
version = "0.1.1"
name = "lz4_flex"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06aad1ce45e4ccf7a8d7d43e0c3ad38dc5d2255174a5f29a3c39d961fbc6181d"
checksum = "2e7bea5a3a7bb3c040adc89eadadee33c3d39371b20dd980c952dd2642867b99"
dependencies = [
"bitflags",
"byteorder",
"fehler",
"thiserror",
"twox-hash",
"quick-error",
]
[[package]]
@@ -1769,7 +1746,7 @@ dependencies = [
"bitflags",
"bstr",
"itertools",
"lz-fear",
"lz4_flex",
"num-bigint",
"num-complex",
"serde",
@@ -1814,6 +1791,7 @@ dependencies = [
"itertools",
"log",
"num-complex",
"num-traits",
"rustpython-ast",
"rustpython-bytecode",
"rustpython-parser",
@@ -2394,16 +2372,6 @@ dependencies = [
"serde",
]
[[package]]
name = "twox-hash"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59"
dependencies = [
"cfg-if 0.1.10",
"static_assertions",
]
[[package]]
name = "typenum"
version = "1.12.0"

View File

@@ -11,7 +11,7 @@ license = "MIT"
[dependencies]
bincode = "1.1"
bitflags = "1.1"
lz-fear = "0.1"
lz4_flex = "0.4"
num-bigint = { version = "0.3", features = ["serde"] }
num-complex = { version = "0.3", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }

View File

@@ -117,15 +117,12 @@ pub struct CodeObject<C: Constant = ConstantData> {
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct CodeFlags: u16 {
const HAS_DEFAULTS = 0x01;
const HAS_KW_ONLY_DEFAULTS = 0x02;
const HAS_ANNOTATIONS = 0x04;
const NEW_LOCALS = 0x08;
const IS_GENERATOR = 0x10;
const IS_COROUTINE = 0x20;
const HAS_VARARGS = 0x40;
const HAS_VARKEYWORDS = 0x80;
const IS_OPTIMIZED = 0x0100;
const NEW_LOCALS = 0x01;
const IS_GENERATOR = 0x02;
const IS_COROUTINE = 0x04;
const HAS_VARARGS = 0x08;
const HAS_VARKEYWORDS = 0x10;
const IS_OPTIMIZED = 0x20;
}
}
@@ -145,8 +142,8 @@ impl CodeFlags {
#[derive(Serialize, Debug, Deserialize, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
// XXX: if you add a new instruction that stores a Label, make sure to add it in
// compile::CodeInfo::finalize_code and CodeObject::label_targets
pub struct Label(pub usize);
// Instruction::label_arg{,_mut}
pub struct Label(pub u32);
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
@@ -156,6 +153,8 @@ impl fmt::Display for Label {
/// Transforms a value prior to formatting it.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ConversionFlag {
/// No conversion
None,
/// Converts by calling `str(<value>)`.
Str,
/// Converts by calling `ascii(<value>)`.
@@ -164,16 +163,22 @@ pub enum ConversionFlag {
Repr,
}
pub type NameIdx = usize;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum RaiseKind {
Reraise,
Raise,
RaiseCause,
}
pub type NameIdx = u32;
/// A Single bytecode instruction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Instruction {
Import {
name_idx: Option<NameIdx>,
symbols_idx: Vec<NameIdx>,
level: usize,
ImportName {
idx: NameIdx,
},
ImportNameless,
ImportStar,
ImportFrom {
idx: NameIdx,
@@ -203,14 +208,16 @@ pub enum Instruction {
},
LoadConst {
/// index into constants vec
idx: usize,
idx: u32,
},
UnaryOperation {
op: UnaryOperator,
},
BinaryOperation {
op: BinaryOperator,
inplace: bool,
},
BinaryOperationInplace {
op: BinaryOperator,
},
LoadAttr {
idx: NameIdx,
@@ -220,11 +227,13 @@ pub enum Instruction {
},
Pop,
Rotate {
amount: usize,
amount: u32,
},
Duplicate,
GetIter,
Continue,
Continue {
target: Label,
},
Break,
Jump {
target: Label,
@@ -247,9 +256,15 @@ pub enum Instruction {
JumpIfFalseOrPop {
target: Label,
},
MakeFunction,
CallFunction {
typ: CallType,
MakeFunction(MakeFunctionFlags),
CallFunctionPositional {
nargs: u32,
},
CallFunctionKeyword {
nargs: u32,
},
CallFunctionEx {
has_kwargs: bool,
},
ForIter {
target: Label,
@@ -259,7 +274,6 @@ pub enum Instruction {
YieldFrom,
SetupAnnotation,
SetupLoop {
start: Label,
end: Label,
},
@@ -292,56 +306,57 @@ pub enum Instruction {
WithCleanupFinish,
PopBlock,
Raise {
argc: usize,
kind: RaiseKind,
},
BuildString {
size: usize,
size: u32,
},
BuildTuple {
size: usize,
unpack: bool,
size: u32,
},
BuildList {
size: usize,
unpack: bool,
size: u32,
},
BuildSet {
size: usize,
unpack: bool,
size: u32,
},
BuildMap {
size: usize,
unpack: bool,
for_call: bool,
size: u32,
},
BuildSlice {
size: usize,
/// whether build a slice with a third step argument
step: bool,
},
ListAppend {
i: usize,
i: u32,
},
SetAdd {
i: usize,
i: u32,
},
MapAdd {
i: usize,
i: u32,
},
PrintExpr,
LoadBuildClass,
UnpackSequence {
size: usize,
size: u32,
},
UnpackEx {
before: usize,
after: usize,
before: u8,
after: u8,
},
FormatValue {
conversion: Option<ConversionFlag>,
conversion: ConversionFlag,
},
PopException,
Reverse {
amount: usize,
amount: u32,
},
GetAwaitable,
BeforeAsyncWith,
@@ -355,17 +370,20 @@ pub enum Instruction {
/// required to support named expressions of Python 3.8 in dict comprehension
/// today (including Py3.9) only required in dict comprehension.
MapAddRev {
i: usize,
i: u32,
},
}
use self::Instruction::*;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CallType {
Positional(usize),
Keyword(usize),
Ex(bool),
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct MakeFunctionFlags: u8 {
const CLOSURE = 0x01;
const ANNOTATIONS = 0x02;
const KW_ONLY_DEFAULTS = 0x04;
const DEFAULTS = 0x08;
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -452,7 +470,7 @@ impl<C: Constant> BorrowedConstant<'_, C> {
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum ComparisonOperator {
Greater,
GreaterOrEqual,
@@ -467,7 +485,7 @@ pub enum ComparisonOperator {
ExceptionMatch,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum BinaryOperator {
Power,
Multiply,
@@ -484,7 +502,7 @@ pub enum BinaryOperator {
Or,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum UnaryOperator {
Not,
Invert,
@@ -588,39 +606,8 @@ impl<C: Constant> CodeObject<C> {
pub fn label_targets(&self) -> BTreeSet<Label> {
let mut label_targets = BTreeSet::new();
for instruction in &*self.instructions {
match instruction {
Jump { target: l }
| JumpIfTrue { target: l }
| JumpIfFalse { target: l }
| JumpIfTrueOrPop { target: l }
| JumpIfFalseOrPop { target: l }
| ForIter { target: l }
| SetupFinally { handler: l }
| SetupExcept { handler: l }
| SetupWith { end: l }
| SetupAsyncWith { end: l } => {
label_targets.insert(*l);
}
SetupLoop { start, end } => {
label_targets.insert(*start);
label_targets.insert(*end);
}
#[rustfmt::skip]
Import { .. } | ImportStar | ImportFrom { .. } | LoadFast(_) | LoadNameAny(_)
| LoadGlobal(_) | LoadDeref(_) | LoadClassDeref(_) | StoreFast(_) | StoreLocal(_)
| StoreGlobal(_) | StoreDeref(_) | DeleteFast(_) | DeleteLocal(_) | DeleteGlobal(_)
| DeleteDeref(_) | LoadClosure(_) | Subscript | StoreSubscript | DeleteSubscript
| StoreAttr { .. } | DeleteAttr { .. } | LoadConst { .. } | UnaryOperation { .. }
| BinaryOperation { .. } | LoadAttr { .. } | CompareOperation { .. } | Pop
| Rotate { .. } | Duplicate | GetIter | Continue | Break | MakeFunction
| CallFunction { .. } | ReturnValue | YieldValue | YieldFrom | SetupAnnotation
| EnterFinally | EndFinally | WithCleanupStart | WithCleanupFinish | PopBlock
| Raise { .. } | BuildString { .. } | BuildTuple { .. } | BuildList { .. }
| BuildSet { .. } | BuildMap { .. } | BuildSlice { .. } | ListAppend { .. }
| SetAdd { .. } | MapAdd { .. } | PrintExpr | LoadBuildClass | UnpackSequence { .. }
| UnpackEx { .. } | FormatValue { .. } | PopException | Reverse { .. }
| GetAwaitable | BeforeAsyncWith | GetAIter | GetANext | MapAddRev { .. } => {}
if let Some(l) = instruction.label_arg() {
label_targets.insert(*l);
}
}
label_targets
@@ -635,7 +622,7 @@ impl<C: Constant> CodeObject<C> {
let label_targets = self.label_targets();
for (offset, instruction) in self.instructions.iter().enumerate() {
let arrow = if label_targets.contains(&Label(offset)) {
let arrow = if label_targets.contains(&Label(offset as u32)) {
">>"
} else {
" "
@@ -736,18 +723,21 @@ impl<C: Constant> CodeObject<C> {
impl CodeObject<ConstantData> {
/// Load a code object from bytes
pub fn from_bytes(data: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
let reader = lz_fear::framed::LZ4FrameReader::new(data)?;
Ok(bincode::deserialize_from(reader.into_read())?)
// TODO: PR to lz4_flex to make it not panic
if data.len() < 4 {
return Err(
std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "bad bytecode").into(),
);
}
let raw_bincode = lz4_flex::decompress_size_prepended(data)?;
let data = bincode::deserialize(&raw_bincode)?;
Ok(data)
}
/// Serialize this bytecode to bytes.
pub fn to_bytes(&self) -> Vec<u8> {
let data = bincode::serialize(&self).expect("CodeObject is not serializable");
let mut out = Vec::new();
lz_fear::framed::CompressionSettings::default()
.compress_with_size_unchecked(data.as_slice(), &mut out, data.len() as u64)
.unwrap();
out
lz4_flex::compress_prepend_size(&data)
}
}
@@ -765,6 +755,46 @@ impl<C: Constant> fmt::Display for CodeObject<C> {
}
impl Instruction {
/// Gets the label stored inside this instruction, if it exists
#[inline]
pub fn label_arg(&self) -> Option<&Label> {
match self {
Jump { target: l }
| JumpIfTrue { target: l }
| JumpIfFalse { target: l }
| JumpIfTrueOrPop { target: l }
| JumpIfFalseOrPop { target: l }
| ForIter { target: l }
| SetupFinally { handler: l }
| SetupExcept { handler: l }
| SetupWith { end: l }
| SetupAsyncWith { end: l }
| SetupLoop { end: l }
| Continue { target: l } => Some(l),
_ => None,
}
}
/// Gets a mutable reference to the label stored inside this instruction, if it exists
#[inline]
pub fn label_arg_mut(&mut self) -> Option<&mut Label> {
match self {
Jump { target: l }
| JumpIfTrue { target: l }
| JumpIfFalse { target: l }
| JumpIfTrueOrPop { target: l }
| JumpIfFalseOrPop { target: l }
| ForIter { target: l }
| SetupFinally { handler: l }
| SetupExcept { handler: l }
| SetupWith { end: l }
| SetupAsyncWith { end: l }
| SetupLoop { end: l }
| Continue { target: l } => Some(l),
_ => None,
}
}
#[allow(clippy::too_many_arguments)]
fn fmt_dis<C: Constant>(
&self,
@@ -799,53 +829,41 @@ impl Instruction {
};
}
let cellname = |i: usize| {
let varname = |i: u32| varnames[i as usize].as_ref();
let name = |i: u32| names[i as usize].as_ref();
let cellname = |i: u32| {
cellvars
.get(i)
.unwrap_or_else(|| &freevars[i - cellvars.len()])
.get(i as usize)
.unwrap_or_else(|| &freevars[i as usize - cellvars.len()])
.as_ref()
};
match self {
Import {
name_idx,
symbols_idx,
level,
} => w!(
Import,
format!("{:?}", name_idx.map(|idx| names[idx].as_ref())),
format!(
"({:?})",
symbols_idx
.iter()
.map(|&idx| names[idx].as_ref())
.format(", ")
),
level
),
ImportName { idx } => w!(ImportName, name(*idx)),
ImportNameless => w!(ImportNameless),
ImportStar => w!(ImportStar),
ImportFrom { idx } => w!(ImportFrom, names[*idx].as_ref()),
LoadFast(idx) => w!(LoadFast, *idx, varnames[*idx].as_ref()),
LoadNameAny(idx) => w!(LoadNameAny, *idx, names[*idx].as_ref()),
LoadGlobal(idx) => w!(LoadGlobal, *idx, names[*idx].as_ref()),
ImportFrom { idx } => w!(ImportFrom, name(*idx)),
LoadFast(idx) => w!(LoadFast, *idx, varname(*idx)),
LoadNameAny(idx) => w!(LoadNameAny, *idx, name(*idx)),
LoadGlobal(idx) => w!(LoadGlobal, *idx, name(*idx)),
LoadDeref(idx) => w!(LoadDeref, *idx, cellname(*idx)),
LoadClassDeref(idx) => w!(LoadClassDeref, *idx, cellname(*idx)),
StoreFast(idx) => w!(StoreFast, *idx, varnames[*idx].as_ref()),
StoreLocal(idx) => w!(StoreLocal, *idx, names[*idx].as_ref()),
StoreGlobal(idx) => w!(StoreGlobal, *idx, names[*idx].as_ref()),
StoreFast(idx) => w!(StoreFast, *idx, varname(*idx)),
StoreLocal(idx) => w!(StoreLocal, *idx, name(*idx)),
StoreGlobal(idx) => w!(StoreGlobal, *idx, name(*idx)),
StoreDeref(idx) => w!(StoreDeref, *idx, cellname(*idx)),
DeleteFast(idx) => w!(DeleteFast, *idx, varnames[*idx].as_ref()),
DeleteLocal(idx) => w!(DeleteLocal, *idx, names[*idx].as_ref()),
DeleteGlobal(idx) => w!(DeleteGlobal, *idx, names[*idx].as_ref()),
DeleteFast(idx) => w!(DeleteFast, *idx, varname(*idx)),
DeleteLocal(idx) => w!(DeleteLocal, *idx, name(*idx)),
DeleteGlobal(idx) => w!(DeleteGlobal, *idx, name(*idx)),
DeleteDeref(idx) => w!(DeleteDeref, *idx, cellname(*idx)),
LoadClosure(i) => w!(LoadClosure, *i, cellname(*i)),
Subscript => w!(Subscript),
StoreSubscript => w!(StoreSubscript),
DeleteSubscript => w!(DeleteSubscript),
StoreAttr { idx } => w!(StoreAttr, names[*idx].as_ref()),
DeleteAttr { idx } => w!(DeleteAttr, names[*idx].as_ref()),
StoreAttr { idx } => w!(StoreAttr, name(*idx)),
DeleteAttr { idx } => w!(DeleteAttr, name(*idx)),
LoadConst { idx } => {
let value = &constants[*idx];
let value = &constants[*idx as usize];
match value.borrow_constant() {
BorrowedConstant::Code { code } if expand_codeobjects => {
writeln!(f, "{:20} ({:?}):", "LoadConst", code)?;
@@ -859,29 +877,34 @@ impl Instruction {
}
}
}
UnaryOperation { op } => w!(UnaryOperation, format!("{:?}", op)),
BinaryOperation { op, inplace } => w!(BinaryOperation, format!("{:?}", op), inplace),
LoadAttr { idx } => w!(LoadAttr, names[*idx].as_ref()),
CompareOperation { op } => w!(CompareOperation, format!("{:?}", op)),
UnaryOperation { op } => w!(UnaryOperation, format_args!("{:?}", op)),
BinaryOperation { op } => w!(BinaryOperation, format_args!("{:?}", op)),
BinaryOperationInplace { op } => {
w!(BinaryOperationInplace, format_args!("{:?}", op))
}
LoadAttr { idx } => w!(LoadAttr, name(*idx)),
CompareOperation { op } => w!(CompareOperation, format_args!("{:?}", op)),
Pop => w!(Pop),
Rotate { amount } => w!(Rotate, amount),
Duplicate => w!(Duplicate),
GetIter => w!(GetIter),
Continue => w!(Continue),
Continue { target } => w!(Continue, target),
Break => w!(Break),
Jump { target } => w!(Jump, target),
JumpIfTrue { target } => w!(JumpIfTrue, target),
JumpIfFalse { target } => w!(JumpIfFalse, target),
JumpIfTrueOrPop { target } => w!(JumpIfTrueOrPop, target),
JumpIfFalseOrPop { target } => w!(JumpIfFalseOrPop, target),
MakeFunction => w!(MakeFunction),
CallFunction { typ } => w!(CallFunction, format!("{:?}", typ)),
MakeFunction(flags) => w!(MakeFunction, format_args!("{:?}", flags)),
CallFunctionPositional { nargs } => w!(CallFunctionPositional, nargs),
CallFunctionKeyword { nargs } => w!(CallFunctionKeyword, nargs),
CallFunctionEx { has_kwargs } => w!(CallFunctionEx, has_kwargs),
ForIter { target } => w!(ForIter, target),
ReturnValue => w!(ReturnValue),
YieldValue => w!(YieldValue),
YieldFrom => w!(YieldFrom),
SetupAnnotation => w!(SetupAnnotation),
SetupLoop { start, end } => w!(SetupLoop, start, end),
SetupLoop { end } => w!(SetupLoop, end),
SetupExcept { handler } => w!(SetupExcept, handler),
SetupFinally { handler } => w!(SetupFinally, handler),
EnterFinally => w!(EnterFinally),
@@ -892,7 +915,7 @@ impl Instruction {
BeforeAsyncWith => w!(BeforeAsyncWith),
SetupAsyncWith { end } => w!(SetupAsyncWith, end),
PopBlock => w!(PopBlock),
Raise { argc } => w!(Raise, argc),
Raise { kind } => w!(Raise, format_args!("{:?}", kind)),
BuildString { size } => w!(BuildString, size),
BuildTuple { size, unpack } => w!(BuildTuple, size, unpack),
BuildList { size, unpack } => w!(BuildList, size, unpack),
@@ -902,7 +925,7 @@ impl Instruction {
unpack,
for_call,
} => w!(BuildMap, size, unpack, for_call),
BuildSlice { size } => w!(BuildSlice, size),
BuildSlice { step } => w!(BuildSlice, step),
ListAppend { i } => w!(ListAppend, i),
SetAdd { i } => w!(SetAdd, i),
MapAddRev { i } => w!(MapAddRev, i),
@@ -910,7 +933,7 @@ impl Instruction {
LoadBuildClass => w!(LoadBuildClass),
UnpackSequence { size } => w!(UnpackSequence, size),
UnpackEx { before, after } => w!(UnpackEx, before, after),
FormatValue { .. } => w!(FormatValue), // TODO: write conversion
FormatValue { conversion } => w!(FormatValue, format_args!("{:?}", conversion)),
PopException => w!(PopException),
Reverse { amount } => w!(Reverse, amount),
GetAwaitable => w!(GetAwaitable),

View File

@@ -13,6 +13,7 @@ itertools = "0.9"
rustpython-bytecode = { path = "../bytecode", version = "0.1.1" }
rustpython-ast = { path = "../ast" }
num-complex = { version = "0.3", features = ["serde"] }
num-traits = "0.2"
log = "0.4"
arrayvec = "0.5"

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,7 @@ pub enum CompileErrorType {
InvalidFuturePlacement,
InvalidFutureFeature(String),
FunctionImportStar,
TooManyStarUnpack,
}
impl fmt::Display for CompileErrorType {
@@ -70,6 +71,9 @@ impl fmt::Display for CompileErrorType {
CompileErrorType::FunctionImportStar => {
write!(f, "import * only allowed at module level")
}
CompileErrorType::TooManyStarUnpack => {
write!(f, "too many expressions in star-unpacking assignment")
}
}
}
}

View File

@@ -1,5 +1,4 @@
//! Compile a Python AST or source code into bytecode consumable by RustPython or
//! (eventually) CPython.
//! Compile a Python AST or source code into bytecode consumable by RustPython.
#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/master/logo.png")]
#![doc(html_root_url = "https://docs.rs/rustpython-compiler/")]

View File

@@ -54,7 +54,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let params = compiler.builder.func.dfg.block_params(entry_block).to_vec();
for (i, (ty, val)) in arg_types.iter().zip(params).enumerate() {
compiler
.store_variable(i, JitValue::new(val, ty.clone()))
.store_variable(i as u32, JitValue::new(val, ty.clone()))
.unwrap();
}
compiler
@@ -66,8 +66,8 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
val: JitValue,
) -> Result<(), JitCompileError> {
let builder = &mut self.builder;
let local = self.variables[idx].get_or_insert_with(|| {
let var = Variable::new(idx);
let local = self.variables[idx as usize].get_or_insert_with(|| {
let var = Variable::new(idx as usize);
let local = Local {
var,
ty: val.ty.clone(),
@@ -119,7 +119,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let label_targets = bytecode.label_targets();
for (offset, instruction) in bytecode.instructions.iter().enumerate() {
let label = Label(offset);
let label = Label(offset as u32);
if label_targets.contains(&label) {
let block = self.get_or_create_block(label);
@@ -219,7 +219,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
Ok(())
}
Instruction::LoadFast(idx) => {
let local = self.variables[*idx]
let local = self.variables[*idx as usize]
.as_ref()
.ok_or(JitCompileError::BadBytecode)?;
self.stack.push(JitValue {
@@ -232,7 +232,9 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let val = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
self.store_variable(*idx, val)
}
Instruction::LoadConst { idx } => self.load_const(constants[*idx].borrow_constant()),
Instruction::LoadConst { idx } => {
self.load_const(constants[*idx as usize].borrow_constant())
}
Instruction::ReturnValue => {
let val = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
if let Some(ref ty) = self.sig.ret {
@@ -320,7 +322,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
_ => Err(JitCompileError::NotSupported),
}
}
Instruction::BinaryOperation { op, .. } => {
Instruction::BinaryOperation { op } | Instruction::BinaryOperationInplace { op } => {
// the rhs is popped off first
let b = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;

View File

@@ -78,13 +78,15 @@ impl StackMachine {
names: &[String],
) -> bool {
match instruction {
Instruction::LoadConst { idx } => self.stack.push(constants[idx].clone().into()),
Instruction::LoadNameAny(idx) => {
self.stack.push(StackValue::String(names[idx].clone()))
Instruction::LoadConst { idx } => {
self.stack.push(constants[idx as usize].clone().into())
}
Instruction::LoadNameAny(idx) => self
.stack
.push(StackValue::String(names[idx as usize].clone())),
Instruction::StoreLocal(idx) => {
self.locals
.insert(names[idx].clone(), self.stack.pop().unwrap());
.insert(names[idx as usize].clone(), self.stack.pop().unwrap());
}
Instruction::StoreAttr { .. } => {
// Do nothing except throw away the stack values
@@ -104,7 +106,7 @@ impl StackMachine {
}
self.stack.push(StackValue::Map(map));
}
Instruction::MakeFunction => {
Instruction::MakeFunction(_flags) => {
let name = if let Some(StackValue::String(name)) = self.stack.pop() {
name
} else {
@@ -134,7 +136,7 @@ impl StackMachine {
let mut values = Vec::new();
// Pop all values from stack:
values.extend(self.stack.drain(self.stack.len() - amount..));
values.extend(self.stack.drain(self.stack.len() - amount as usize..));
// Push top of stack back first:
self.stack.push(values.pop().unwrap());

View File

@@ -510,7 +510,7 @@ fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> {
vm.get_attribute(vm.sys_module.clone(), "modules")?
.set_item("__main__", main_module, vm)?;
let site_result = vm.import("site", &[], 0);
let site_result = vm.import("site", None, 0);
if site_result.is_err() {
warn!(
@@ -559,7 +559,7 @@ fn run_command(vm: &VirtualMachine, scope: Scope, source: String) -> PyResult<()
fn run_module(vm: &VirtualMachine, module: &str) -> PyResult<()> {
debug!("Running module {}", module);
let runpy = vm.import("runpy", &[], 0)?;
let runpy = vm.import("runpy", None, 0)?;
let run_module_as_main = vm.get_attribute(runpy, "_run_module_as_main")?;
vm.invoke(&run_module_as_main, (module,))?;
Ok(())

View File

@@ -73,7 +73,7 @@ impl FrameRef {
}
#[pyproperty]
fn f_lasti(self) -> usize {
fn f_lasti(self) -> u32 {
self.lasti()
}

View File

@@ -82,7 +82,7 @@ impl PyModule {
#[pymethod(magic)]
fn repr(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
let importlib = vm.import("_frozen_importlib", &[], 0)?;
let importlib = vm.import("_frozen_importlib", None, 0)?;
let module_repr = vm.get_attribute(importlib, "_module_repr")?;
vm.invoke(&module_repr, (zelf,))
}

View File

@@ -314,11 +314,11 @@ pub fn init(context: &PyContext) {
fn common_reduce(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult {
if proto >= 2 {
let reducelib = vm.import("__reducelib", &[], 0)?;
let reducelib = vm.import("__reducelib", None, 0)?;
let reduce_2 = vm.get_attribute(reducelib, "reduce_2")?;
vm.invoke(&reduce_2, (obj,))
} else {
let copyreg = vm.import("copyreg", &[], 0)?;
let copyreg = vm.import("copyreg", None, 0)?;
let reduce_ex = vm.get_attribute(copyreg, "_reduce_ex")?;
vm.invoke(&reduce_ex, (obj, proto))
}

View File

@@ -95,12 +95,14 @@ impl fmt::Display for PyStr {
}
impl TryIntoRef<PyStr> for String {
#[inline]
fn try_into_ref(self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
Ok(PyStr::from(self).into_ref(vm))
}
}
impl TryIntoRef<PyStr> for &str {
#[inline]
fn try_into_ref(self, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
Ok(PyStr::from(self).into_ref(vm))
}

View File

@@ -8,7 +8,7 @@ use crate::vm::VirtualMachine;
pub struct PyTraceback {
pub next: Option<PyTracebackRef>, // TODO: Make mutable
pub frame: FrameRef,
pub lasti: usize,
pub lasti: u32,
pub lineno: usize,
}
@@ -22,7 +22,7 @@ impl PyValue for PyTraceback {
#[pyimpl]
impl PyTraceback {
pub fn new(next: Option<PyRef<Self>>, frame: FrameRef, lasti: usize, lineno: usize) -> Self {
pub fn new(next: Option<PyRef<Self>>, frame: FrameRef, lasti: u32, lineno: usize) -> Self {
PyTraceback {
next,
frame,
@@ -37,7 +37,7 @@ impl PyTraceback {
}
#[pyproperty(name = "tb_lasti")]
fn lasti(&self) -> usize {
fn lasti(&self) -> u32 {
self.lasti
}

View File

@@ -317,6 +317,7 @@ impl<T: TransmuteFromObject> TryFromObject for PyTupleTyped<T> {
impl<'a, T: TransmuteFromObject + 'a> BorrowValue<'a> for PyTupleTyped<T> {
type Borrowed = &'a [T];
#[inline]
fn borrow_value(&'a self) -> Self::Borrowed {
unsafe { &*(self.tuple.borrow_value() as *const [PyObjectRef] as *const [T]) }
}
@@ -327,3 +328,17 @@ impl<T: TransmuteFromObject + fmt::Debug> fmt::Debug for PyTupleTyped<T> {
self.borrow_value().fmt(f)
}
}
impl<T: TransmuteFromObject> From<PyTupleTyped<T>> for PyTupleRef {
#[inline]
fn from(tup: PyTupleTyped<T>) -> Self {
tup.tuple
}
}
impl<T: TransmuteFromObject> IntoPyObject for PyTupleTyped<T> {
#[inline]
fn into_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
self.tuple.into_object()
}
}

View File

@@ -1,5 +1,5 @@
use std::fmt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::{AtomicU32, Ordering};
use indexmap::IndexMap;
use itertools::Itertools;
@@ -42,7 +42,6 @@ struct Block {
#[derive(Clone, Debug)]
enum BlockType {
Loop {
start: bytecode::Label,
end: bytecode::Label,
},
TryExcept {
@@ -77,7 +76,7 @@ enum UnwindReason {
Break,
/// We are unwinding blocks since we hit a continue statements.
Continue,
Continue { target: bytecode::Label },
}
#[derive(Debug)]
@@ -100,7 +99,7 @@ pub struct Frame {
pub builtins: PyDictRef,
/// index of last instruction ran
pub lasti: AtomicUsize,
pub lasti: AtomicU32,
/// tracer function for this frame (usually is None)
pub trace: PyMutex<PyObjectRef>,
state: PyMutex<FrameState>,
@@ -187,7 +186,7 @@ impl Frame {
globals: scope.globals,
builtins,
code,
lasti: AtomicUsize::new(0),
lasti: AtomicU32::new(0),
state: PyMutex::new(FrameState {
stack: Vec::new(),
blocks: Vec::new(),
@@ -284,14 +283,14 @@ impl FrameRef {
}
pub fn current_location(&self) -> bytecode::Location {
self.code.locations[self.lasti.load(Ordering::Relaxed) - 1]
self.code.locations[self.lasti.load(Ordering::Relaxed) as usize - 1]
}
pub fn yield_from_target(&self) -> Option<PyObjectRef> {
self.with_exec(|exec| exec.yield_from_target())
}
pub fn lasti(&self) -> usize {
pub fn lasti(&self) -> u32 {
self.lasti.load(Ordering::Relaxed)
}
}
@@ -306,7 +305,7 @@ struct ExecutingFrame<'a> {
globals: &'a PyDictRef,
builtins: &'a PyDictRef,
object: &'a FrameRef,
lasti: &'a AtomicUsize,
lasti: &'a AtomicU32,
state: &'a mut FrameState,
}
@@ -326,8 +325,7 @@ impl ExecutingFrame<'_> {
flame_guard!(format!("Frame::run({})", self.code.obj_name));
// Execute until return or exception:
loop {
let idx = self.lasti.fetch_add(1, Ordering::Relaxed);
let loc = self.code.locations[idx];
let idx = self.lasti.fetch_add(1, Ordering::Relaxed) as usize;
let instr = &self.code.instructions[idx];
let result = self.execute_instruction(instr, vm);
match result {
@@ -341,6 +339,8 @@ impl ExecutingFrame<'_> {
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
// 3. Unwind block stack till appropriate handler is found.
let loc = self.code.locations[idx];
let next = exception.traceback();
let new_traceback =
@@ -365,7 +365,9 @@ impl ExecutingFrame<'_> {
}
fn yield_from_target(&self) -> Option<PyObjectRef> {
if let Some(bytecode::Instruction::YieldFrom) = self.code.instructions.get(self.lasti()) {
if let Some(bytecode::Instruction::YieldFrom) =
self.code.instructions.get(self.lasti() as usize)
{
Some(self.last_value())
} else {
None
@@ -442,23 +444,23 @@ impl ExecutingFrame<'_> {
match instruction {
bytecode::Instruction::LoadConst { idx } => {
self.push_value(self.code.constants[*idx].0.clone());
self.push_value(self.code.constants[*idx as usize].0.clone());
Ok(None)
}
bytecode::Instruction::Import {
name_idx,
symbols_idx,
level,
} => self.import(vm, *name_idx, symbols_idx, *level),
bytecode::Instruction::ImportName { idx } => {
self.import(vm, Some(self.code.names[*idx as usize].clone()))
}
bytecode::Instruction::ImportNameless => self.import(vm, None),
bytecode::Instruction::ImportStar => self.import_star(vm),
bytecode::Instruction::ImportFrom { idx } => self.import_from(vm, *idx),
bytecode::Instruction::LoadFast(idx) => {
let x = self.fastlocals.lock()[*idx].clone().ok_or_else(|| {
let idx = *idx as usize;
let x = self.fastlocals.lock()[idx].clone().ok_or_else(|| {
vm.new_exception_msg(
vm.ctx.exceptions.unbound_local_error.clone(),
format!(
"local variable '{}' referenced before assignment",
self.code.varnames[*idx]
self.code.varnames[idx]
),
)
})?;
@@ -466,7 +468,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadNameAny(idx) => {
let name = &self.code.names[*idx];
let name = &self.code.names[*idx as usize];
let x = self.locals.get_item_option(name.clone(), vm)?;
let x = match x {
Some(x) => x,
@@ -476,13 +478,13 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadGlobal(idx) => {
let name = &self.code.names[*idx];
let name = &self.code.names[*idx as usize];
let x = self.load_global_or_builtin(name, vm)?;
self.push_value(x);
Ok(None)
}
bytecode::Instruction::LoadDeref(i) => {
let i = *i;
let i = *i as usize;
let x = self.cells_frees[i]
.get()
.ok_or_else(|| self.unbound_cell_exception(i, vm))?;
@@ -490,7 +492,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::LoadClassDeref(i) => {
let i = *i;
let i = *i as usize;
let name = self.code.freevars[i - self.code.cellvars.len()].clone();
let value = if let Some(value) = self.locals.get_item_option(name, vm)? {
value
@@ -504,32 +506,32 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::StoreFast(idx) => {
let value = self.pop_value();
self.fastlocals.lock()[*idx] = Some(value);
self.fastlocals.lock()[*idx as usize] = Some(value);
Ok(None)
}
bytecode::Instruction::StoreLocal(idx) => {
let value = self.pop_value();
self.locals
.set_item(self.code.names[*idx].clone(), value, vm)?;
.set_item(self.code.names[*idx as usize].clone(), value, vm)?;
Ok(None)
}
bytecode::Instruction::StoreGlobal(idx) => {
let value = self.pop_value();
self.globals
.set_item(self.code.names[*idx].clone(), value, vm)?;
.set_item(self.code.names[*idx as usize].clone(), value, vm)?;
Ok(None)
}
bytecode::Instruction::StoreDeref(i) => {
let value = self.pop_value();
self.cells_frees[*i].set(Some(value));
self.cells_frees[*i as usize].set(Some(value));
Ok(None)
}
bytecode::Instruction::DeleteFast(idx) => {
self.fastlocals.lock()[*idx] = None;
self.fastlocals.lock()[*idx as usize] = None;
Ok(None)
}
bytecode::Instruction::DeleteLocal(idx) => {
let name = &self.code.names[*idx];
let name = &self.code.names[*idx as usize];
match self.locals.del_item(name.clone(), vm) {
Ok(()) => {}
Err(e) if e.isinstance(&vm.ctx.exceptions.key_error) => {
@@ -540,7 +542,7 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::DeleteGlobal(idx) => {
let name = &self.code.names[*idx];
let name = &self.code.names[*idx as usize];
match self.globals.del_item(name.clone(), vm) {
Ok(()) => {}
Err(e) if e.isinstance(&vm.ctx.exceptions.key_error) => {
@@ -551,11 +553,11 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::DeleteDeref(i) => {
self.cells_frees[*i].set(None);
self.cells_frees[*i as usize].set(None);
Ok(None)
}
bytecode::Instruction::LoadClosure(i) => {
let value = self.cells_frees[*i].clone();
let value = self.cells_frees[*i as usize].clone();
self.push_value(value.into_object());
Ok(None)
}
@@ -577,31 +579,40 @@ impl ExecutingFrame<'_> {
bytecode::Instruction::Rotate { amount } => self.execute_rotate(*amount),
bytecode::Instruction::BuildString { size } => {
let s = self
.pop_multiple(*size)
.into_iter()
.map(|pyobj| pystr::clone_value(&pyobj))
.pop_multiple(*size as usize)
.as_slice()
.iter()
.map(|pyobj| pystr::borrow_value(&pyobj))
.collect::<String>();
let str_obj = vm.ctx.new_str(s);
self.push_value(str_obj);
Ok(None)
}
bytecode::Instruction::BuildList { size, unpack } => {
let elements = self.get_elements(vm, *size, *unpack)?;
let elements = self.get_elements(vm, *size as usize, *unpack)?;
let list_obj = vm.ctx.new_list(elements);
self.push_value(list_obj);
Ok(None)
}
bytecode::Instruction::BuildSet { size, unpack } => {
let elements = self.get_elements(vm, *size, *unpack)?;
let set = vm.ctx.new_set();
for item in elements {
set.add(item, vm)?;
{
let elements = self.pop_multiple(*size as usize);
if *unpack {
for element in elements {
vm.map_iterable_object(&element, |x| set.add(x, vm))??;
}
} else {
for element in elements {
set.add(element, vm)?;
}
}
}
self.push_value(set.into_object());
Ok(None)
}
bytecode::Instruction::BuildTuple { size, unpack } => {
let elements = self.get_elements(vm, *size, *unpack)?;
let elements = self.get_elements(vm, *size as usize, *unpack)?;
let list_obj = vm.ctx.new_tuple(elements);
self.push_value(list_obj);
Ok(None)
@@ -611,7 +622,7 @@ impl ExecutingFrame<'_> {
unpack,
for_call,
} => self.execute_build_map(vm, *size, *unpack, *for_call),
bytecode::Instruction::BuildSlice { size } => self.execute_build_slice(vm, *size),
bytecode::Instruction::BuildSlice { step } => self.execute_build_slice(vm, *step),
bytecode::Instruction::ListAppend { i } => {
let list_obj = self.nth_value(*i);
let item = self.pop_value();
@@ -639,8 +650,9 @@ impl ExecutingFrame<'_> {
PyDictRef::try_from_object(vm, dict_obj)?.set_item(key, value, vm)?;
Ok(None)
}
bytecode::Instruction::BinaryOperation { ref op, inplace } => {
self.execute_binop(vm, op, *inplace)
bytecode::Instruction::BinaryOperation { op } => self.execute_binop(vm, *op),
bytecode::Instruction::BinaryOperationInplace { op } => {
self.execute_binop_inplace(vm, *op)
}
bytecode::Instruction::LoadAttr { idx } => self.load_attr(vm, *idx),
bytecode::Instruction::StoreAttr { idx } => self.store_attr(vm, *idx),
@@ -668,11 +680,8 @@ impl ExecutingFrame<'_> {
}
Ok(None)
}
bytecode::Instruction::SetupLoop { start, end } => {
self.push_block(BlockType::Loop {
start: *start,
end: *end,
});
bytecode::Instruction::SetupLoop { end } => {
self.push_block(BlockType::Loop { end: *end });
Ok(None)
}
bytecode::Instruction::SetupExcept { handler } => {
@@ -812,8 +821,16 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::ForIter { target } => self.execute_for_iter(vm, *target),
bytecode::Instruction::MakeFunction => self.execute_make_function(vm),
bytecode::Instruction::CallFunction { typ } => self.execute_call_function(vm, typ),
bytecode::Instruction::MakeFunction(flags) => self.execute_make_function(vm, *flags),
bytecode::Instruction::CallFunctionPositional { nargs } => {
self.execute_call_function_positional(vm, *nargs)
}
bytecode::Instruction::CallFunctionKeyword { nargs } => {
self.execute_call_function_keyword(vm, *nargs)
}
bytecode::Instruction::CallFunctionEx { has_kwargs } => {
self.execute_call_function_ex(vm, *has_kwargs)
}
bytecode::Instruction::Jump { target } => {
self.jump(*target);
Ok(None)
@@ -858,10 +875,12 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::Raise { argc } => self.execute_raise(vm, *argc),
bytecode::Instruction::Raise { kind } => self.execute_raise(vm, *kind),
bytecode::Instruction::Break => self.unwind_blocks(vm, UnwindReason::Break),
bytecode::Instruction::Continue => self.unwind_blocks(vm, UnwindReason::Continue),
bytecode::Instruction::Continue { target } => {
self.unwind_blocks(vm, UnwindReason::Continue { target: *target })
}
bytecode::Instruction::PrintExpr => {
let expr = self.pop_value();
@@ -888,7 +907,7 @@ impl ExecutingFrame<'_> {
e
}
})?;
let msg = match elements.len().cmp(size) {
let msg = match elements.len().cmp(&(*size as usize)) {
std::cmp::Ordering::Equal => {
for element in elements.into_iter().rev() {
self.push_value(element);
@@ -914,12 +933,13 @@ impl ExecutingFrame<'_> {
self.execute_unpack_ex(vm, *before, *after)
}
bytecode::Instruction::FormatValue { conversion } => {
use bytecode::ConversionFlag::*;
use bytecode::ConversionFlag;
let value = self.pop_value();
let value = match conversion {
Some(Str) => vm.to_str(&self.pop_value())?.into_object(),
Some(Repr) => vm.to_repr(&self.pop_value())?.into_object(),
Some(Ascii) => vm.ctx.new_str(builtins::ascii(self.pop_value(), vm)?),
None => self.pop_value(),
ConversionFlag::Str => vm.to_str(&value)?.into_object(),
ConversionFlag::Repr => vm.to_repr(&value)?.into_object(),
ConversionFlag::Ascii => vm.ctx.new_str(builtins::ascii(value, vm)?),
ConversionFlag::None => value,
};
let spec = self.pop_value();
@@ -938,7 +958,7 @@ impl ExecutingFrame<'_> {
}
bytecode::Instruction::Reverse { amount } => {
let stack_len = self.state.stack.len();
self.state.stack[stack_len - amount..stack_len].reverse();
self.state.stack[stack_len - *amount as usize..stack_len].reverse();
Ok(None)
}
}
@@ -966,27 +986,17 @@ impl ExecutingFrame<'_> {
}
Ok(result)
} else {
Ok(elements)
Ok(elements.collect())
}
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn import(
&mut self,
vm: &VirtualMachine,
module: Option<bytecode::NameIdx>,
symbols: &[bytecode::NameIdx],
level: usize,
) -> FrameResult {
let module = match module {
Some(idx) => self.code.names[idx].borrow_value(),
None => "",
};
let from_list = symbols
.iter()
.map(|&idx| self.code.names[idx].clone())
.collect::<Vec<_>>();
let module = vm.import(&module, &from_list, level)?;
fn import(&mut self, vm: &VirtualMachine, module: Option<PyStrRef>) -> FrameResult {
let module = module.unwrap_or_else(|| PyStr::from("").into_ref(vm));
let from_list = <Option<PyTupleTyped<PyStrRef>>>::try_from_object(vm, self.pop_value())?;
let level = usize::try_from_object(vm, self.pop_value())?;
let module = vm.import(module, from_list, level)?;
self.push_value(module);
Ok(None)
@@ -995,7 +1005,7 @@ impl ExecutingFrame<'_> {
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn import_from(&mut self, vm: &VirtualMachine, idx: bytecode::NameIdx) -> FrameResult {
let module = self.last_value();
let name = &self.code.names[idx];
let name = &self.code.names[idx as usize];
// Load attribute, and transform any error into import error.
let obj = vm.get_attribute(module, name.clone()).map_err(|_| {
vm.new_import_error(format!("cannot import name '{}'", name), name.clone())
@@ -1040,14 +1050,14 @@ impl ExecutingFrame<'_> {
// First unwind all existing blocks on the block stack:
while let Some(block) = self.current_block() {
match block.typ {
BlockType::Loop { start, end } => match &reason {
BlockType::Loop { end } => match &reason {
UnwindReason::Break => {
self.pop_block();
self.jump(end);
return Ok(None);
}
UnwindReason::Continue => {
self.jump(start);
UnwindReason::Continue { target } => {
self.jump(*target);
return Ok(None);
}
_ => {
@@ -1094,13 +1104,13 @@ impl ExecutingFrame<'_> {
match reason {
UnwindReason::Raising { exception } => Err(exception),
UnwindReason::Returning { value } => Ok(Some(ExecutionResult::Return(value))),
UnwindReason::Break | UnwindReason::Continue => {
UnwindReason::Break | UnwindReason::Continue { .. } => {
panic!("Internal error: break or continue must occur within a loop block.")
} // UnwindReason::NoWorries => Ok(None),
}
}
fn execute_rotate(&mut self, amount: usize) -> FrameResult {
fn execute_rotate(&mut self, amount: u32) -> FrameResult {
// Shuffles top of stack amount down
if amount < 2 {
panic!("Can only rotate two or more values");
@@ -1150,10 +1160,11 @@ impl ExecutingFrame<'_> {
fn execute_build_map(
&mut self,
vm: &VirtualMachine,
size: usize,
size: u32,
unpack: bool,
for_call: bool,
) -> FrameResult {
let size = size as usize;
let map_obj = vm.ctx.new_dict();
if unpack {
for obj in self.pop_multiple(size) {
@@ -1176,7 +1187,7 @@ impl ExecutingFrame<'_> {
}
}
} else {
for (key, value) in self.pop_multiple(2 * size).into_iter().tuples() {
for (key, value) in self.pop_multiple(2 * size).tuples() {
map_obj.set_item(key, value, vm).unwrap();
}
}
@@ -1185,14 +1196,8 @@ impl ExecutingFrame<'_> {
Ok(None)
}
fn execute_build_slice(&mut self, vm: &VirtualMachine, size: usize) -> FrameResult {
assert!(size == 2 || size == 3);
let step = if size == 3 {
Some(self.pop_value())
} else {
None
};
fn execute_build_slice(&mut self, vm: &VirtualMachine, step: bool) -> FrameResult {
let step = if step { Some(self.pop_value()) } else { None };
let stop = self.pop_value();
let start = self.pop_value();
@@ -1206,68 +1211,67 @@ impl ExecutingFrame<'_> {
Ok(None)
}
fn execute_call_function(
&mut self,
vm: &VirtualMachine,
typ: &bytecode::CallType,
) -> FrameResult {
let args = match typ {
bytecode::CallType::Positional(count) => {
let args: Vec<PyObjectRef> = self.pop_multiple(*count);
FuncArgs {
args,
kwargs: IndexMap::new(),
}
}
bytecode::CallType::Keyword(count) => {
let kwarg_names = self.pop_value();
let args: Vec<PyObjectRef> = self.pop_multiple(*count);
let kwarg_names = vm
.extract_elements(&kwarg_names)?
.iter()
.map(|pyobj| pystr::clone_value(pyobj))
.collect();
FuncArgs::with_kwargs_names(args, kwarg_names)
}
bytecode::CallType::Ex(has_kwargs) => {
let kwargs = if *has_kwargs {
let kw_dict: PyDictRef = self.pop_value().downcast().map_err(|_| {
// TODO: check collections.abc.Mapping
vm.new_type_error("Kwargs must be a dict.".to_owned())
})?;
let mut kwargs = IndexMap::new();
for (key, value) in kw_dict.into_iter() {
let key = key.payload_if_subclass::<pystr::PyStr>(vm).ok_or_else(|| {
vm.new_type_error("keywords must be strings".to_owned())
})?;
kwargs.insert(key.borrow_value().to_owned(), value);
}
kwargs
} else {
IndexMap::new()
};
let args = self.pop_value();
let args = vm.extract_elements(&args)?;
FuncArgs { args, kwargs }
}
fn execute_call_function_positional(&mut self, vm: &VirtualMachine, nargs: u32) -> FrameResult {
let args = FuncArgs {
args: self.pop_multiple(nargs as usize).collect(),
kwargs: IndexMap::new(),
};
// Call function:
// eprintln!(
// "calling from {} {:?}",
// self.code.obj_name,
// self.code.locations[self.lasti.load(Ordering::Relaxed)]
// );
let func_ref = self.pop_value();
let value = vm.invoke(&func_ref, args)?;
self.push_value(value);
Ok(None)
}
fn execute_raise(&mut self, vm: &VirtualMachine, argc: usize) -> FrameResult {
let cause = match argc {
2 => {
fn execute_call_function_keyword(&mut self, vm: &VirtualMachine, nargs: u32) -> FrameResult {
let kwarg_names = self
.pop_value()
.downcast::<PyTuple>()
.expect("kwarg names should be tuple of strings");
let args = self.pop_multiple(nargs as usize);
let kwarg_names = kwarg_names
.borrow_value()
.iter()
.map(|pyobj| pystr::clone_value(pyobj));
let args = FuncArgs::with_kwargs_names(args, kwarg_names);
let func_ref = self.pop_value();
let value = vm.invoke(&func_ref, args)?;
self.push_value(value);
Ok(None)
}
fn execute_call_function_ex(&mut self, vm: &VirtualMachine, has_kwargs: bool) -> FrameResult {
let kwargs = if has_kwargs {
let kw_dict: PyDictRef = self.pop_value().downcast().map_err(|_| {
// TODO: check collections.abc.Mapping
vm.new_type_error("Kwargs must be a dict.".to_owned())
})?;
let mut kwargs = IndexMap::new();
for (key, value) in kw_dict.into_iter() {
let key = key
.payload_if_subclass::<pystr::PyStr>(vm)
.ok_or_else(|| vm.new_type_error("keywords must be strings".to_owned()))?;
kwargs.insert(key.borrow_value().to_owned(), value);
}
kwargs
} else {
IndexMap::new()
};
let args = self.pop_value();
let args = vm.extract_elements(&args)?;
let args = FuncArgs { args, kwargs };
let func_ref = self.pop_value();
let value = vm.invoke(&func_ref, args)?;
self.push_value(value);
Ok(None)
}
fn execute_raise(&mut self, vm: &VirtualMachine, kind: bytecode::RaiseKind) -> FrameResult {
let cause = match kind {
bytecode::RaiseKind::RaiseCause => {
let val = self.pop_value();
Some(if vm.is_none(&val) {
// if the cause arg is none, we clear the cause
@@ -1278,19 +1282,19 @@ impl ExecutingFrame<'_> {
})
}
// if there's no cause arg, we keep the cause as is
_ => None,
bytecode::RaiseKind::Raise | bytecode::RaiseKind::Reraise => None,
};
let exception = match argc {
0 => vm
let exception = match kind {
bytecode::RaiseKind::RaiseCause | bytecode::RaiseKind::Raise => {
ExceptionCtor::try_from_object(vm, self.pop_value())?.instantiate(vm)?
}
bytecode::RaiseKind::Reraise => vm
.current_exception()
.ok_or_else(|| vm.new_runtime_error("No active exception to reraise".to_owned()))?,
1 | 2 => ExceptionCtor::try_from_object(vm, self.pop_value())?.instantiate(vm)?,
3 => panic!("Not implemented!"),
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
};
let context = match argc {
0 => None, // We have already got the exception,
_ => vm.current_exception(),
let context = match kind {
bytecode::RaiseKind::RaiseCause | bytecode::RaiseKind::Raise => vm.current_exception(),
bytecode::RaiseKind::Reraise => None, // We have already got the current exception
};
info!(
"Exception raised: {:?} with cause: {:?} and context: {:?}",
@@ -1343,12 +1347,8 @@ impl ExecutingFrame<'_> {
}
}
fn execute_unpack_ex(
&mut self,
vm: &VirtualMachine,
before: usize,
after: usize,
) -> FrameResult {
fn execute_unpack_ex(&mut self, vm: &VirtualMachine, before: u8, after: u8) -> FrameResult {
let (before, after) = (before as usize, after as usize);
let value = self.pop_value();
let elements = vm.extract_elements::<PyObjectRef>(&value)?;
let min_expected = before + after;
@@ -1412,7 +1412,11 @@ impl ExecutingFrame<'_> {
}
}
}
fn execute_make_function(&mut self, vm: &VirtualMachine) -> FrameResult {
fn execute_make_function(
&mut self,
vm: &VirtualMachine,
flags: bytecode::MakeFunctionFlags,
) -> FrameResult {
let qualified_name = self
.pop_value()
.downcast::<PyStr>()
@@ -1422,21 +1426,19 @@ impl ExecutingFrame<'_> {
.downcast()
.expect("Second to top value on the stack must be a code object");
let flags = code_obj.flags;
let closure = if code_obj.freevars.is_empty() {
None
} else {
let closure = if flags.contains(bytecode::MakeFunctionFlags::CLOSURE) {
Some(PyTupleTyped::try_from_object(vm, self.pop_value()).unwrap())
} else {
None
};
let annotations = if flags.contains(bytecode::CodeFlags::HAS_ANNOTATIONS) {
let annotations = if flags.contains(bytecode::MakeFunctionFlags::ANNOTATIONS) {
self.pop_value()
} else {
vm.ctx.new_dict().into_object()
};
let kw_only_defaults = if flags.contains(bytecode::CodeFlags::HAS_KW_ONLY_DEFAULTS) {
let kw_only_defaults = if flags.contains(bytecode::MakeFunctionFlags::KW_ONLY_DEFAULTS) {
Some(
self.pop_value()
.downcast::<PyDict>()
@@ -1446,7 +1448,7 @@ impl ExecutingFrame<'_> {
None
};
let defaults = if flags.contains(bytecode::CodeFlags::HAS_DEFAULTS) {
let defaults = if flags.contains(bytecode::MakeFunctionFlags::DEFAULTS) {
Some(
self.pop_value()
.downcast::<PyTuple>()
@@ -1486,47 +1488,50 @@ impl ExecutingFrame<'_> {
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn execute_binop(
fn execute_binop(&mut self, vm: &VirtualMachine, op: bytecode::BinaryOperator) -> FrameResult {
let b_ref = &self.pop_value();
let a_ref = &self.pop_value();
let value = match op {
bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref),
bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref),
bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref),
bytecode::BinaryOperator::MatrixMultiply => vm._matmul(a_ref, b_ref),
bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref),
bytecode::BinaryOperator::Divide => vm._truediv(a_ref, b_ref),
bytecode::BinaryOperator::FloorDivide => vm._floordiv(a_ref, b_ref),
bytecode::BinaryOperator::Modulo => vm._mod(a_ref, b_ref),
bytecode::BinaryOperator::Lshift => vm._lshift(a_ref, b_ref),
bytecode::BinaryOperator::Rshift => vm._rshift(a_ref, b_ref),
bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref),
bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref),
bytecode::BinaryOperator::And => vm._and(a_ref, b_ref),
}?;
self.push_value(value);
Ok(None)
}
fn execute_binop_inplace(
&mut self,
vm: &VirtualMachine,
op: &bytecode::BinaryOperator,
inplace: bool,
op: bytecode::BinaryOperator,
) -> FrameResult {
let b_ref = &self.pop_value();
let a_ref = &self.pop_value();
let value = if inplace {
match *op {
bytecode::BinaryOperator::Subtract => vm._isub(a_ref, b_ref),
bytecode::BinaryOperator::Add => vm._iadd(a_ref, b_ref),
bytecode::BinaryOperator::Multiply => vm._imul(a_ref, b_ref),
bytecode::BinaryOperator::MatrixMultiply => vm._imatmul(a_ref, b_ref),
bytecode::BinaryOperator::Power => vm._ipow(a_ref, b_ref),
bytecode::BinaryOperator::Divide => vm._itruediv(a_ref, b_ref),
bytecode::BinaryOperator::FloorDivide => vm._ifloordiv(a_ref, b_ref),
bytecode::BinaryOperator::Modulo => vm._imod(a_ref, b_ref),
bytecode::BinaryOperator::Lshift => vm._ilshift(a_ref, b_ref),
bytecode::BinaryOperator::Rshift => vm._irshift(a_ref, b_ref),
bytecode::BinaryOperator::Xor => vm._ixor(a_ref, b_ref),
bytecode::BinaryOperator::Or => vm._ior(a_ref, b_ref),
bytecode::BinaryOperator::And => vm._iand(a_ref, b_ref),
}?
} else {
match *op {
bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref),
bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref),
bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref),
bytecode::BinaryOperator::MatrixMultiply => vm._matmul(a_ref, b_ref),
bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref),
bytecode::BinaryOperator::Divide => vm._truediv(a_ref, b_ref),
bytecode::BinaryOperator::FloorDivide => vm._floordiv(a_ref, b_ref),
bytecode::BinaryOperator::Modulo => vm._mod(a_ref, b_ref),
bytecode::BinaryOperator::Lshift => vm._lshift(a_ref, b_ref),
bytecode::BinaryOperator::Rshift => vm._rshift(a_ref, b_ref),
bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref),
bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref),
bytecode::BinaryOperator::And => vm._and(a_ref, b_ref),
}?
};
let value = match op {
bytecode::BinaryOperator::Subtract => vm._isub(a_ref, b_ref),
bytecode::BinaryOperator::Add => vm._iadd(a_ref, b_ref),
bytecode::BinaryOperator::Multiply => vm._imul(a_ref, b_ref),
bytecode::BinaryOperator::MatrixMultiply => vm._imatmul(a_ref, b_ref),
bytecode::BinaryOperator::Power => vm._ipow(a_ref, b_ref),
bytecode::BinaryOperator::Divide => vm._itruediv(a_ref, b_ref),
bytecode::BinaryOperator::FloorDivide => vm._ifloordiv(a_ref, b_ref),
bytecode::BinaryOperator::Modulo => vm._imod(a_ref, b_ref),
bytecode::BinaryOperator::Lshift => vm._ilshift(a_ref, b_ref),
bytecode::BinaryOperator::Rshift => vm._irshift(a_ref, b_ref),
bytecode::BinaryOperator::Xor => vm._ixor(a_ref, b_ref),
bytecode::BinaryOperator::Or => vm._ior(a_ref, b_ref),
bytecode::BinaryOperator::And => vm._iand(a_ref, b_ref),
}?;
self.push_value(value);
Ok(None)
@@ -1610,7 +1615,7 @@ impl ExecutingFrame<'_> {
}
fn load_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone();
let attr_name = self.code.names[attr as usize].clone();
let parent = self.pop_value();
let obj = vm.get_attribute(parent, attr_name)?;
self.push_value(obj);
@@ -1618,7 +1623,7 @@ impl ExecutingFrame<'_> {
}
fn store_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone();
let attr_name = self.code.names[attr as usize].clone();
let parent = self.pop_value();
let value = self.pop_value();
vm.set_attr(&parent, attr_name, value)?;
@@ -1626,13 +1631,13 @@ impl ExecutingFrame<'_> {
}
fn delete_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone().into_object();
let attr_name = self.code.names[attr as usize].clone().into_object();
let parent = self.pop_value();
vm.del_attr(&parent, attr_name)?;
Ok(None)
}
fn lasti(&self) -> usize {
fn lasti(&self) -> u32 {
// it's okay to make this Relaxed, because we know that we only
// mutate lasti if the mutex is held, and any other thread that
// wants to guarantee the value of this will use a Lock anyway
@@ -1667,20 +1672,17 @@ impl ExecutingFrame<'_> {
.expect("Tried to pop value but there was nothing on the stack")
}
fn pop_multiple(&mut self, count: usize) -> Vec<PyObjectRef> {
fn pop_multiple(&mut self, count: usize) -> std::vec::Drain<PyObjectRef> {
let stack_len = self.state.stack.len();
self.state
.stack
.drain(stack_len - count..stack_len)
.collect()
self.state.stack.drain(stack_len - count..stack_len)
}
fn last_value(&self) -> PyObjectRef {
self.state.stack.last().unwrap().clone()
}
fn nth_value(&self, depth: usize) -> PyObjectRef {
self.state.stack[self.state.stack.len() - depth - 1].clone()
fn nth_value(&self, depth: u32) -> PyObjectRef {
self.state.stack[self.state.stack.len() - depth as usize - 1].clone()
}
}

View File

@@ -8,6 +8,7 @@ use crate::pyobject::{
};
use crate::vm::VirtualMachine;
use indexmap::IndexMap;
use itertools::Itertools;
use result_like::impl_option_like;
use std::collections::HashMap;
use std::marker::PhantomData;
@@ -98,15 +99,24 @@ impl FuncArgs {
Self { args, kwargs }
}
pub fn with_kwargs_names(mut args: Vec<PyObjectRef>, kwarg_names: Vec<String>) -> Self {
pub fn with_kwargs_names<A, KW>(mut args: A, kwarg_names: KW) -> Self
where
A: ExactSizeIterator<Item = PyObjectRef>,
KW: ExactSizeIterator<Item = String>,
{
// last `kwarg_names.len()` elements of args in order of appearance in the call signature
let kwarg_values = args.drain((args.len() - kwarg_names.len())..);
let total_argc = args.len();
let kwargc = kwarg_names.len();
let posargc = total_argc - kwargc;
let mut kwargs = IndexMap::new();
for (name, value) in kwarg_names.iter().zip(kwarg_values) {
kwargs.insert(name.clone(), value);
let posargs = args.by_ref().take(posargc).collect();
let kwargs = kwarg_names.zip_eq(args).collect::<IndexMap<_, _>>();
FuncArgs {
args: posargs,
kwargs,
}
FuncArgs { args, kwargs }
}
pub fn prepend_arg(&mut self, item: PyObjectRef) {

View File

@@ -36,7 +36,7 @@ pub(crate) fn init_importlib(
let install_external = vm.get_attribute(importlib, "_install_external_importers")?;
vm.invoke(&install_external, ())?;
// Set pyc magic number to commit hash. Should be changed when bytecode will be more stable.
let importlib_external = vm.import("_frozen_importlib_external", &[], 0)?;
let importlib_external = vm.import("_frozen_importlib_external", None, 0)?;
let mut magic = get_git_revision().into_bytes();
magic.truncate(4);
if magic.len() != 4 {
@@ -44,7 +44,7 @@ pub(crate) fn init_importlib(
}
vm.set_attr(&importlib_external, "MAGIC_NUMBER", vm.ctx.new_bytes(magic))?;
let zipimport_res = (|| -> PyResult<()> {
let zipimport = vm.import("zipimport", &[], 0)?;
let zipimport = vm.import("zipimport", None, 0)?;
let zipimporter = vm.get_attribute(zipimport, "zipimporter")?;
let path_hooks = vm.get_attribute(vm.sys_module.clone(), "path_hooks")?;
let path_hooks = list::PyListRef::try_from_object(vm, path_hooks)?;

View File

@@ -763,6 +763,7 @@ pub trait TryIntoRef<T: PyObjectPayload> {
}
impl<T: PyObjectPayload> TryIntoRef<T> for PyRef<T> {
#[inline]
fn try_into_ref(self, _vm: &VirtualMachine) -> PyResult<PyRef<T>> {
Ok(self)
}

View File

@@ -317,7 +317,7 @@ impl Drop for PyObjectRef {
Ok(v) => println!("{}", v.to_string()),
Err(_) => println!("{}", del_method.class().name),
}
let tb_module = vm.import("traceback", &[], 0).unwrap();
let tb_module = vm.import("traceback", None, 0).unwrap();
// TODO: set exc traceback
let print_stack = vm.get_attribute(tb_module, "print_stack").unwrap();
vm.invoke(&print_stack, ()).unwrap();

View File

@@ -22,7 +22,7 @@ use crate::builtins::object;
use crate::builtins::pybool;
use crate::builtins::pystr::{PyStr, PyStrRef};
use crate::builtins::pytype::PyTypeRef;
use crate::builtins::tuple::PyTuple;
use crate::builtins::tuple::{PyTuple, PyTupleTyped};
use crate::common::{hash::HashSecret, lock::PyMutex, rc::PyRc};
#[cfg(feature = "rustpython-compiler")]
use crate::compile::{self, CompileError, CompileErrorType, CompileOpts};
@@ -511,7 +511,7 @@ impl VirtualMachine {
pub fn try_class(&self, module: &str, class: &str) -> PyResult<PyTypeRef> {
let class = self
.get_attribute(self.import(module, &[], 0)?, class)?
.get_attribute(self.import(module, None, 0)?, class)?
.downcast()
.expect("not a class");
Ok(class)
@@ -519,7 +519,7 @@ impl VirtualMachine {
pub fn class(&self, module: &str, class: &str) -> PyTypeRef {
let module = self
.import(module, &[], 0)
.import(module, None, 0)
.unwrap_or_else(|_| panic!("unable to import {}", module));
let class = self
.get_attribute(module.clone(), class)
@@ -844,16 +844,35 @@ impl VirtualMachine {
})
}
pub fn import(&self, module: &str, from_list: &[PyStrRef], level: usize) -> PyResult {
#[inline]
pub fn import(
&self,
module: impl TryIntoRef<PyStr>,
from_list: Option<PyTupleTyped<PyStrRef>>,
level: usize,
) -> PyResult {
self._import_inner(module.try_into_ref(self)?, from_list, level)
}
fn _import_inner(
&self,
module: PyStrRef,
from_list: Option<PyTupleTyped<PyStrRef>>,
level: usize,
) -> PyResult {
// if the import inputs seem weird, e.g a package import or something, rather than just
// a straight `import ident`
let weird = module.contains('.') || level != 0 || !from_list.is_empty();
let weird = module.borrow_value().contains('.')
|| level != 0
|| from_list
.as_ref()
.map_or(false, |x| !x.borrow_value().is_empty());
let cached_module = if weird {
None
} else {
let sys_modules = self.get_attribute(self.sys_module.clone(), "modules")?;
sys_modules.get_item(module, self).ok()
sys_modules.get_item(module.clone(), self).ok()
};
match cached_module {
@@ -871,7 +890,7 @@ impl VirtualMachine {
let import_func = self
.get_attribute(self.builtins.clone(), "__import__")
.map_err(|_| {
self.new_import_error("__import__ not found".to_owned(), module)
self.new_import_error("__import__ not found".to_owned(), module.clone())
})?;
let (locals, globals) = if let Some(frame) = self.current_frame() {
@@ -879,9 +898,10 @@ impl VirtualMachine {
} else {
(None, None)
};
let from_list = self
.ctx
.new_tuple(from_list.iter().map(|x| x.as_object().clone()).collect());
let from_list = match from_list {
Some(tup) => tup.into_pyobject(self),
None => self.ctx.new_tuple(vec![]),
};
self.invoke(&import_func, (module, globals, locals, from_list, level))
.map_err(|exc| import::remove_importlib_frames(self, &exc))
}
@@ -1287,7 +1307,7 @@ impl VirtualMachine {
encoding: Option<PyStrRef>,
errors: Option<PyStrRef>,
) -> PyResult {
let codecsmodule = self.import("_codecs", &[], 0)?;
let codecsmodule = self.import("_codecs", None, 0)?;
let func = self.get_attribute(codecsmodule, func)?;
let mut args = vec![obj, encoding.into_pyobject(self)];
if let Some(errors) = errors {