Implement string interning for variable names

This commit is contained in:
Noah
2020-11-08 20:46:24 -06:00
parent b30ca99a09
commit 2f232ecbe3
19 changed files with 428 additions and 314 deletions

View File

@@ -32,6 +32,7 @@ impl Location {
}
pub trait Constant: Sized {
type Name: AsRef<str>;
fn borrow_constant(&self) -> BorrowedConstant<Self>;
fn into_data(self) -> ConstantData {
self.borrow_constant().into_data()
@@ -41,6 +42,7 @@ pub trait Constant: Sized {
}
}
impl Constant for ConstantData {
type Name = String;
fn borrow_constant(&self) -> BorrowedConstant<Self> {
use BorrowedConstant::*;
match self {
@@ -69,6 +71,10 @@ pub trait ConstantBag: Sized {
fn make_constant_borrowed<C: Constant>(&self, constant: BorrowedConstant<C>) -> Self::Constant {
self.make_constant(constant.into_data())
}
fn make_name(&self, name: String) -> <Self::Constant as Constant>::Name;
fn make_name_ref(&self, name: &str) -> <Self::Constant as Constant>::Name {
self.make_name(name.to_owned())
}
}
#[derive(Clone)]
@@ -78,8 +84,8 @@ impl ConstantBag for BasicBag {
fn make_constant(&self, constant: ConstantData) -> Self::Constant {
constant
}
fn make_constant_borrowed<C: Constant>(&self, constant: BorrowedConstant<C>) -> Self::Constant {
constant.into_data()
fn make_name(&self, name: String) -> <Self::Constant as Constant>::Name {
name
}
}
@@ -93,14 +99,17 @@ pub struct CodeObject<C: Constant = ConstantData> {
pub locations: Vec<Location>,
pub flags: CodeFlags,
pub posonlyarg_count: usize, // Number of positional-only arguments
pub arg_names: Vec<String>, // Names of positional arguments
pub varargs_name: Option<String>, // *args or *
pub kwonlyarg_names: Vec<String>,
pub varkeywords_name: Option<String>, // **kwargs or **
pub arg_count: usize,
pub kwonlyarg_count: usize,
pub source_path: String,
pub first_line_number: usize,
pub obj_name: String, // Name of the object that created this code object
pub constants: Vec<C>,
#[serde(bound(
deserialize = "C::Name: serde::Deserialize<'de>",
serialize = "C::Name: serde::Serialize"
))]
pub names: Vec<C::Name>,
}
bitflags! {
@@ -172,37 +181,39 @@ pub enum ConversionFlag {
Repr,
}
pub type NameIdx = usize;
/// A Single bytecode instruction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Instruction {
Import {
name: Option<String>,
symbols: Vec<String>,
name_idx: Option<NameIdx>,
symbols_idx: Vec<NameIdx>,
level: usize,
},
ImportStar,
ImportFrom {
name: String,
idx: NameIdx,
},
LoadName {
name: String,
idx: NameIdx,
scope: NameScope,
},
StoreName {
name: String,
idx: NameIdx,
scope: NameScope,
},
DeleteName {
name: String,
idx: NameIdx,
},
Subscript,
StoreSubscript,
DeleteSubscript,
StoreAttr {
name: String,
idx: NameIdx,
},
DeleteAttr {
name: String,
idx: NameIdx,
},
LoadConst {
/// index into constants vec
@@ -216,7 +227,7 @@ pub enum Instruction {
inplace: bool,
},
LoadAttr {
name: String,
idx: NameIdx,
},
CompareOperation {
op: ComparisonOperator,
@@ -503,15 +514,37 @@ pub enum BlockType {
}
*/
pub struct Arguments<'a, N: AsRef<str>> {
pub posonlyargs: &'a [N],
pub args: &'a [N],
pub vararg: Option<&'a N>,
pub kwonlyargs: &'a [N],
pub varkwarg: Option<&'a N>,
}
impl<N: AsRef<str>> fmt::Debug for Arguments<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
macro_rules! fmt_slice {
($x:expr) => {
format_args!("[{}]", $x.iter().map(AsRef::as_ref).format(", "))
};
}
f.debug_struct("Arguments")
.field("posonlyargs", &fmt_slice!(self.posonlyargs))
.field("args", &fmt_slice!(self.posonlyargs))
.field("vararg", &self.vararg.map(N::as_ref))
.field("kwonlyargs", &fmt_slice!(self.kwonlyargs))
.field("varkwarg", &self.varkwarg.map(N::as_ref))
.finish()
}
}
impl<C: Constant> CodeObject<C> {
#[allow(clippy::too_many_arguments)]
pub fn new(
flags: CodeFlags,
posonlyarg_count: usize,
arg_names: Vec<String>,
varargs_name: Option<String>,
kwonlyarg_names: Vec<String>,
varkeywords_name: Option<String>,
arg_count: usize,
kwonlyarg_count: usize,
source_path: String,
first_line_number: usize,
obj_name: String,
@@ -522,40 +555,45 @@ impl<C: Constant> CodeObject<C> {
locations: Vec::new(),
flags,
posonlyarg_count,
arg_names,
varargs_name,
kwonlyarg_names,
varkeywords_name,
arg_count,
kwonlyarg_count,
source_path,
first_line_number,
obj_name,
constants: Vec::new(),
names: Vec::new(),
}
}
pub fn varnames(&self) -> impl Iterator<Item = &str> + '_ {
self.arg_names
.iter()
.map(String::as_str)
.chain(self.kwonlyarg_names.iter().map(String::as_str))
.chain(self.varargs_name.as_deref())
.chain(self.varkeywords_name.as_deref())
.chain(
self.instructions
.iter()
.filter_map(|i| match i {
Instruction::LoadName {
name,
scope: NameScope::Local,
}
| Instruction::StoreName {
name,
scope: NameScope::Local,
} => Some(name.as_str()),
_ => None,
})
.unique(),
)
// like inspect.getargs
pub fn arg_names(&self) -> Arguments<C::Name> {
let nargs = self.arg_count;
let nkwargs = self.kwonlyarg_count;
let mut varargspos = nargs + nkwargs;
let posonlyargs = &self.names[..self.posonlyarg_count];
let args = &self.names[..nargs];
let kwonlyargs = &self.names[nargs..varargspos];
let vararg = if self.flags.contains(CodeFlags::HAS_VARARGS) {
let vararg = &self.names[varargspos];
varargspos += 1;
Some(vararg)
} else {
None
};
let varkwarg = if self.flags.contains(CodeFlags::HAS_VARKEYWORDS) {
Some(&self.names[varargspos])
} else {
None
};
Arguments {
posonlyargs,
args,
vararg,
kwonlyargs,
varkwarg,
}
}
fn display_inner(
@@ -579,6 +617,7 @@ impl<C: Constant> CodeObject<C> {
f,
&self.label_map,
&self.constants,
&self.names,
expand_codeobjects,
level,
)?;
@@ -603,16 +642,19 @@ impl<C: Constant> CodeObject<C> {
.into_iter()
.map(|x| x.map_constant(bag))
.collect(),
names: self
.names
.into_iter()
.map(|x| bag.make_name_ref(x.as_ref()))
.collect(),
instructions: self.instructions,
label_map: self.label_map,
locations: self.locations,
flags: self.flags,
posonlyarg_count: self.posonlyarg_count,
arg_names: self.arg_names,
varargs_name: self.varargs_name,
kwonlyarg_names: self.kwonlyarg_names,
varkeywords_name: self.varkeywords_name,
arg_count: self.arg_count,
kwonlyarg_count: self.kwonlyarg_count,
source_path: self.source_path,
first_line_number: self.first_line_number,
obj_name: self.obj_name,
@@ -626,16 +668,19 @@ impl<C: Constant> CodeObject<C> {
.iter()
.map(|x| bag.make_constant_borrowed(x.borrow_constant()))
.collect(),
names: self
.names
.iter()
.map(|x| bag.make_name_ref(x.as_ref()))
.collect(),
instructions: self.instructions.clone(),
label_map: self.label_map.clone(),
locations: self.locations.clone(),
flags: self.flags,
posonlyarg_count: self.posonlyarg_count,
arg_names: self.arg_names.clone(),
varargs_name: self.varargs_name.clone(),
kwonlyarg_names: self.kwonlyarg_names.clone(),
varkeywords_name: self.varkeywords_name.clone(),
arg_count: self.arg_count,
kwonlyarg_count: self.kwonlyarg_count,
source_path: self.source_path.clone(),
first_line_number: self.first_line_number,
obj_name: self.obj_name.clone(),
@@ -677,6 +722,7 @@ impl Instruction {
f: &mut fmt::Formatter,
label_map: &BTreeMap<Label, usize>,
constants: &[C],
names: &[C::Name],
expand_codeobjects: bool,
level: usize,
) -> fmt::Result {
@@ -704,25 +750,31 @@ impl Instruction {
match self {
Import {
name,
symbols,
name_idx,
symbols_idx,
level,
} => w!(
Import,
format!("{:?}", name),
format!("{:?}", symbols),
format!("{:?}", name_idx.map(|idx| names[idx].as_ref())),
format!(
"({:?})",
symbols_idx
.iter()
.map(|&idx| names[idx].as_ref())
.format(", ")
),
level
),
ImportStar => w!(ImportStar),
ImportFrom { name } => w!(ImportFrom, name),
LoadName { name, scope } => w!(LoadName, name, format!("{:?}", scope)),
StoreName { name, scope } => w!(StoreName, name, format!("{:?}", scope)),
DeleteName { name } => w!(DeleteName, name),
ImportFrom { idx } => w!(ImportFrom, names[*idx].as_ref()),
LoadName { idx, scope } => w!(LoadName, names[*idx].as_ref(), format!("{:?}", scope)),
StoreName { idx, scope } => w!(StoreName, names[*idx].as_ref(), format!("{:?}", scope)),
DeleteName { idx } => w!(DeleteName, names[*idx].as_ref()),
Subscript => w!(Subscript),
StoreSubscript => w!(StoreSubscript),
DeleteSubscript => w!(DeleteSubscript),
StoreAttr { name } => w!(StoreAttr, name),
DeleteAttr { name } => w!(DeleteAttr, name),
StoreAttr { idx } => w!(StoreAttr, names[*idx].as_ref()),
DeleteAttr { idx } => w!(DeleteAttr, names[*idx].as_ref()),
LoadConst { idx } => {
let value = &constants[*idx];
match value.borrow_constant() {
@@ -740,7 +792,7 @@ impl Instruction {
}
UnaryOperation { op } => w!(UnaryOperation, format!("{:?}", op)),
BinaryOperation { op, inplace } => w!(BinaryOperation, format!("{:?}", op), inplace),
LoadAttr { name } => w!(LoadAttr, name),
LoadAttr { idx } => w!(LoadAttr, names[*idx].as_ref()),
CompareOperation { op } => w!(CompareOperation, format!("{:?}", op)),
Pop => w!(Pop),
Rotate { amount } => w!(Rotate, amount),
@@ -818,6 +870,10 @@ impl<C: Constant> fmt::Debug for CodeObject<C> {
#[derive(Serialize, Deserialize)]
pub struct FrozenModule<C: Constant = ConstantData> {
#[serde(bound(
deserialize = "C: serde::Deserialize<'de>, C::Name: serde::Deserialize<'de>",
serialize = "C: serde::Serialize, C::Name: serde::Serialize"
))]
pub code: CodeObject<C>,
pub package: bool,
}

View File

@@ -10,6 +10,7 @@ pub use crate::mode::Mode;
use crate::symboltable::{
make_symbol_table, statements_to_symbol_table, Symbol, SymbolScope, SymbolTable,
};
use indexmap::IndexSet;
use itertools::Itertools;
use num_complex::Complex64;
use rustpython_ast as ast;
@@ -19,7 +20,7 @@ type CompileResult<T> = Result<T, CompileError>;
/// Main structure holding the state of compilation.
struct Compiler {
output_stack: Vec<CodeObject>,
output_stack: Vec<(CodeObject, IndexSet<String>)>,
symbol_table_stack: Vec<SymbolTable>,
nxt_label: usize,
source_path: String,
@@ -150,7 +151,7 @@ impl Compiler {
}
fn push_output(&mut self, code: CodeObject) {
self.output_stack.push(code);
self.output_stack.push((code, IndexSet::new()));
}
fn push_new_code_object(&mut self, obj_name: String) {
@@ -158,10 +159,8 @@ impl Compiler {
self.push_output(CodeObject::new(
Default::default(),
0,
Vec::new(),
None,
Vec::new(),
None,
0,
0,
self.source_path.clone(),
line_number,
obj_name,
@@ -169,7 +168,20 @@ impl Compiler {
}
fn pop_code_object(&mut self) -> CodeObject {
self.output_stack.pop().unwrap()
let (mut code, names) = self.output_stack.pop().unwrap();
code.names.extend(names);
code
}
// could take impl Into<Cow<str>>, but everything is borrowed from ast structs; we never
// actually have a `String` to pass
fn name(&mut self, name: &str) -> bytecode::NameIdx {
let cache = &mut self.output_stack.last_mut().expect("nothing on stack").1;
if let Some(x) = cache.get_index_of(name) {
x
} else {
cache.insert_full(name.to_owned()).0
}
}
fn compile_program(
@@ -183,8 +195,9 @@ impl Compiler {
let (statements, doc) = get_doc(&program.statements);
if let Some(value) = doc {
self.emit_constant(bytecode::ConstantData::Str { value });
let doc = self.name("__doc__");
self.emit(Instruction::StoreName {
name: "__doc__".to_owned(),
idx: doc,
scope: bytecode::NameScope::Global,
});
}
@@ -279,18 +292,14 @@ impl Compiler {
fn load_name(&mut self, name: &str) {
let scope = self.scope_for_name(name);
self.emit(Instruction::LoadName {
name: name.to_owned(),
scope,
});
let idx = self.name(name);
self.emit(Instruction::LoadName { idx, scope });
}
fn store_name(&mut self, name: &str) {
let scope = self.scope_for_name(name);
self.emit(Instruction::StoreName {
name: name.to_owned(),
scope,
});
let idx = self.name(name);
self.emit(Instruction::StoreName { idx, scope });
}
fn compile_statement(&mut self, statement: &ast::Statement) -> CompileResult<()> {
@@ -312,16 +321,16 @@ impl Compiler {
Import { names } => {
// import a, b, c as d
for name in names {
let name_idx = Some(self.name(&name.symbol));
self.emit(Instruction::Import {
name: Some(name.symbol.clone()),
symbols: vec![],
name_idx,
symbols_idx: vec![],
level: 0,
});
if let Some(alias) = &name.alias {
for part in name.symbol.split('.').skip(1) {
self.emit(Instruction::LoadAttr {
name: part.to_owned(),
});
let idx = self.name(part);
self.emit(Instruction::LoadAttr { idx });
}
self.store_name(alias);
} else {
@@ -336,31 +345,33 @@ impl Compiler {
} => {
let import_star = names.iter().any(|n| n.symbol == "*");
let module_idx = module.as_ref().map(|s| self.name(s));
if import_star {
let star = self.name("*");
// from .... import *
self.emit(Instruction::Import {
name: module.clone(),
symbols: vec!["*".to_owned()],
name_idx: module_idx,
symbols_idx: vec![star],
level: *level,
});
self.emit(Instruction::ImportStar);
} else {
// from mod import a, b as c
// First, determine the fromlist (for import lib):
let from_list = names.iter().map(|n| n.symbol.clone()).collect();
let from_list = names.iter().map(|n| self.name(&n.symbol)).collect();
// Load module once:
self.emit(Instruction::Import {
name: module.clone(),
symbols: from_list,
name_idx: module_idx,
symbols_idx: from_list,
level: *level,
});
for name in names {
let idx = self.name(&name.symbol);
// import symbol from module:
self.emit(Instruction::ImportFrom {
name: name.symbol.to_owned(),
});
self.emit(Instruction::ImportFrom { idx });
// Store module under proper name:
if let Some(alias) = &name.alias {
@@ -513,8 +524,9 @@ impl Compiler {
if self.opts.optimize == 0 {
let end_label = self.new_label();
self.compile_jump_if(test, true, end_label)?;
let assertion_error = self.name("AssertionError");
self.emit(Instruction::LoadName {
name: String::from("AssertionError"),
idx: assertion_error,
scope: bytecode::NameScope::Global,
});
match msg {
@@ -612,15 +624,13 @@ impl Compiler {
fn compile_delete(&mut self, expression: &ast::Expression) -> CompileResult<()> {
match &expression.node {
ast::ExpressionType::Identifier { name } => {
self.emit(Instruction::DeleteName {
name: name.to_owned(),
});
let idx = self.name(name);
self.emit(Instruction::DeleteName { idx });
}
ast::ExpressionType::Attribute { value, name } => {
self.compile_expression(value)?;
self.emit(Instruction::DeleteAttr {
name: name.to_owned(),
});
let idx = self.name(name);
self.emit(Instruction::DeleteAttr { idx });
}
ast::ExpressionType::Subscript { a, b } => {
self.compile_expression(a)?;
@@ -677,33 +687,35 @@ impl Compiler {
flags |= bytecode::CodeFlags::HAS_KW_ONLY_DEFAULTS;
}
let mut compile_varargs = |va: &ast::Varargs, flag| match va {
ast::Varargs::None => None,
ast::Varargs::Unnamed => {
flags |= flag;
None
}
ast::Varargs::Named(name) => {
flags |= flag;
Some(name.arg.clone())
}
};
let varargs_name = compile_varargs(&args.vararg, bytecode::CodeFlags::HAS_VARARGS);
let varkeywords_name = compile_varargs(&args.kwarg, bytecode::CodeFlags::HAS_VARKEYWORDS);
let line_number = self.get_source_line_number();
self.push_output(CodeObject::new(
flags,
args.posonlyargs_count,
args.args.iter().map(|a| a.arg.clone()).collect(),
varargs_name,
args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(),
varkeywords_name,
args.args.len(),
args.kwonlyargs.len(),
self.source_path.clone(),
line_number,
name.to_owned(),
));
for name in &args.args {
self.name(&name.arg);
}
for name in &args.kwonlyargs {
self.name(&name.arg);
}
let mut compile_varargs = |va: &ast::Varargs, flag| match va {
ast::Varargs::None | ast::Varargs::Unnamed => {}
ast::Varargs::Named(name) => {
self.current_code().flags |= flag;
self.name(&name.arg);
}
};
compile_varargs(&args.vararg, bytecode::CodeFlags::HAS_VARARGS);
compile_varargs(&args.kwarg, bytecode::CodeFlags::HAS_VARKEYWORDS);
self.enter_scope();
Ok(())
@@ -948,9 +960,8 @@ impl Compiler {
self.emit(Instruction::Duplicate);
self.load_docstring(doc_str);
self.emit(Instruction::Rotate { amount: 2 });
self.emit(Instruction::StoreAttr {
name: "__doc__".to_owned(),
});
let doc = self.name("__doc__");
self.emit(Instruction::StoreAttr { idx: doc });
self.apply_decorators(decorator_list);
self.store_name(name);
@@ -1041,10 +1052,8 @@ impl Compiler {
self.push_output(CodeObject::new(
Default::default(),
0,
vec![],
None,
vec![],
None,
0,
0,
self.source_path.clone(),
line_number,
name.to_owned(),
@@ -1053,24 +1062,28 @@ impl Compiler {
let (new_body, doc_str) = get_doc(body);
let dunder_name = self.name("__name__");
self.emit(Instruction::LoadName {
name: "__name__".to_owned(),
idx: dunder_name,
scope: bytecode::NameScope::Global,
});
let dunder_module = self.name("__module__");
self.emit(Instruction::StoreName {
name: "__module__".to_owned(),
idx: dunder_module,
scope: bytecode::NameScope::Free,
});
self.emit_constant(bytecode::ConstantData::Str {
value: qualified_name.clone(),
});
let qualname = self.name("__qualname__");
self.emit(Instruction::StoreName {
name: "__qualname__".to_owned(),
idx: qualname,
scope: bytecode::NameScope::Free,
});
self.load_docstring(doc_str);
let doc = self.name("__doc__");
self.emit(Instruction::StoreName {
name: "__doc__".to_owned(),
idx: doc,
scope: bytecode::NameScope::Free,
});
// setup annotations
@@ -1222,8 +1235,9 @@ impl Compiler {
self.set_label(check_asynciter_label);
self.emit(Instruction::Duplicate);
let stopasynciter = self.name("StopAsyncIteration");
self.emit(Instruction::LoadName {
name: "StopAsyncIteration".to_owned(),
idx: stopasynciter,
scope: bytecode::NameScope::Global,
});
self.emit(Instruction::CompareOperation {
@@ -1363,8 +1377,9 @@ impl Compiler {
if let ast::ExpressionType::Identifier { name } = &target.node {
// Store as dict entry in __annotations__ dict:
let annotations = self.name("__annotations__");
self.emit(Instruction::LoadName {
name: String::from("__annotations__"),
idx: annotations,
scope: bytecode::NameScope::Local,
});
self.emit_constant(bytecode::ConstantData::Str {
@@ -1390,9 +1405,8 @@ impl Compiler {
}
ast::ExpressionType::Attribute { value, name } => {
self.compile_expression(value)?;
self.emit(Instruction::StoreAttr {
name: name.to_owned(),
});
let idx = self.name(name);
self.emit(Instruction::StoreAttr { idx });
}
ast::ExpressionType::List { elements } | ast::ExpressionType::Tuple { elements } => {
let mut seen_star = false;
@@ -1658,9 +1672,8 @@ impl Compiler {
}
Attribute { value, name } => {
self.compile_expression(value)?;
self.emit(Instruction::LoadAttr {
name: name.to_owned(),
});
let idx = self.name(name);
self.emit(Instruction::LoadAttr { idx });
}
Compare { vals, ops } => {
self.compile_chained_comparison(vals, ops)?;
@@ -1966,14 +1979,13 @@ impl Compiler {
self.push_output(CodeObject::new(
Default::default(),
1,
vec![".0".to_owned()],
None,
vec![],
None,
1,
0,
self.source_path.clone(),
line_number,
name.clone(),
));
let arg0 = self.name(".0");
self.enter_scope();
// Create empty object of proper type:
@@ -2009,7 +2021,7 @@ impl Compiler {
if loop_labels.is_empty() {
// Load iterator onto stack (passed as first argument):
self.emit(Instruction::LoadName {
name: String::from(".0"),
idx: arg0,
scope: bytecode::NameScope::Local,
});
} else {
@@ -2228,9 +2240,11 @@ impl Compiler {
}
fn current_code(&mut self) -> &mut CodeObject {
self.output_stack
&mut self
.output_stack
.last_mut()
.expect("No OutputStream on stack")
.0
}
// Generate a new label

View File

@@ -11,7 +11,7 @@ edition = "2018"
proc-macro = true
[dependencies]
syn = { version = "1.0", features = ["full"] }
syn = { version = "1.0", features = ["full", "extra-traits"] }
syn-ext = { version = "0.2.1", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"

View File

@@ -34,12 +34,17 @@ pub struct FunctionCompiler<'a, 'b> {
}
impl<'a, 'b> FunctionCompiler<'a, 'b> {
pub fn new(
pub fn new<I, S>(
builder: &'a mut FunctionBuilder<'b>,
arg_names: &[String],
arg_names: I,
arg_types: &[JitType],
entry_block: Block,
) -> FunctionCompiler<'a, 'b> {
) -> FunctionCompiler<'a, 'b>
where
I: IntoIterator<Item = S>,
I::IntoIter: ExactSizeIterator,
S: AsRef<str>,
{
let mut compiler = FunctionCompiler {
builder,
stack: Vec::new(),
@@ -51,11 +56,12 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
},
};
let params = compiler.builder.func.dfg.block_params(entry_block).to_vec();
let arg_names = arg_names.into_iter();
debug_assert_eq!(arg_names.len(), arg_types.len());
debug_assert_eq!(arg_names.len(), params.len());
for ((name, ty), val) in arg_names.iter().zip(arg_types).zip(params) {
for ((name, ty), val) in arg_names.zip(arg_types).zip(params) {
compiler
.store_variable(name.clone(), JitValue::new(val, ty.clone()))
.store_variable(name.as_ref().to_owned(), JitValue::new(val, ty.clone()))
.unwrap();
}
compiler
@@ -131,7 +137,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
continue;
}
self.add_instruction(&instruction, &bytecode.constants)?;
self.add_instruction(&instruction, &bytecode.constants, &bytecode.names)?;
}
Ok(())
@@ -177,6 +183,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
&mut self,
instruction: &Instruction,
constants: &[C],
names: &[C::Name],
) -> Result<(), JitCompileError> {
match instruction {
Instruction::JumpIfFalse { target } => {
@@ -212,12 +219,12 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
Ok(())
}
Instruction::LoadName {
name,
idx,
scope: NameScope::Local,
} => {
let local = self
.variables
.get(name)
.get(names[*idx].as_ref())
.ok_or(JitCompileError::BadBytecode)?;
self.stack.push(JitValue {
val: self.builder.use_var(local.var),
@@ -226,11 +233,11 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
Ok(())
}
Instruction::StoreName {
name,
idx,
scope: NameScope::Local,
} => {
let val = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
self.store_variable(name.clone(), val)
self.store_variable(names[*idx].as_ref().to_owned(), val)
}
Instruction::LoadConst { idx } => self.load_const(constants[*idx].borrow_constant()),
Instruction::ReturnValue => {

View File

@@ -65,9 +65,8 @@ impl Jit {
builder.switch_to_block(entry_block);
let sig = {
let mut arg_names = bytecode.arg_names.clone();
arg_names.extend(bytecode.kwonlyarg_names.iter().cloned());
let mut compiler = FunctionCompiler::new(&mut builder, &arg_names, args, entry_block);
let arg_names = &bytecode.names[..bytecode.arg_count + bytecode.kwonlyarg_count];
let mut compiler = FunctionCompiler::new(&mut builder, arg_names, args, entry_block);
compiler.compile(bytecode)?;

View File

@@ -13,7 +13,7 @@ pub struct Function {
impl Function {
pub fn compile(self) -> CompiledCode {
let mut arg_types = Vec::new();
for arg in self.code.arg_names.iter() {
for arg in self.code.arg_names().args {
let arg_type = match self.annotations.get(arg) {
Some(StackValue::String(annotation)) => match annotation.as_str() {
"int" => JitType::Int,
@@ -65,7 +65,7 @@ impl StackMachine {
pub fn run(&mut self, code: CodeObject) {
for instruction in code.instructions {
if self.process_instruction(instruction, &code.constants) {
if self.process_instruction(instruction, &code.constants, &code.names) {
break;
}
}
@@ -75,15 +75,17 @@ impl StackMachine {
&mut self,
instruction: Instruction,
constants: &[ConstantData],
names: &[String],
) -> bool {
match instruction {
Instruction::LoadConst { idx } => self.stack.push(constants[idx].clone().into()),
Instruction::LoadName {
name,
idx,
scope: NameScope::Free,
} => self.stack.push(StackValue::String(name)),
Instruction::StoreName { name, .. } => {
self.locals.insert(name, self.stack.pop().unwrap());
} => self.stack.push(StackValue::String(names[idx].clone())),
Instruction::StoreName { idx, .. } => {
self.locals
.insert(names[idx].clone(), self.stack.pop().unwrap());
}
Instruction::StoreAttr { .. } => {
// Do nothing except throw away the stack values

View File

@@ -1,6 +1,6 @@
use rustpython_vm::builtins::PyStrRef;
use rustpython_vm::pyobject::{BorrowValue, PyIterable, PyResult, TryFromObject};
use rustpython_vm::scope::{NameProtocol, Scope};
use rustpython_vm::scope::Scope;
use rustpython_vm::VirtualMachine;
pub struct ShellHelper<'vm> {
@@ -74,7 +74,7 @@ impl<'vm> ShellHelper<'vm> {
// last: the last word, could be empty if it ends with a dot
// parents: the words before the dot
let mut current = self.scope.load_global(self.vm, first)?;
let mut current = self.scope.load_global(self.vm, first.as_str())?;
for attr in parents {
current = self.vm.get_attribute(current.clone(), attr.as_str()).ok()?;

View File

@@ -5,7 +5,7 @@
use std::fmt;
use std::ops::Deref;
use super::pytype::PyTypeRef;
use super::{PyStrRef, PyTypeRef};
use crate::bytecode::{self, BorrowedConstant, Constant, ConstantBag};
use crate::pyobject::{
BorrowValue, IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
@@ -66,6 +66,7 @@ fn borrow_obj_constant(obj: &PyObjectRef) -> BorrowedConstant<PyConstant> {
}
impl Constant for PyConstant {
type Name = PyStrRef;
fn borrow_constant(&self) -> BorrowedConstant<Self> {
borrow_obj_constant(&self.0)
}
@@ -85,7 +86,10 @@ impl ConstantBag for PyObjBag<'_> {
bytecode::ConstantData::Integer { value } => ctx.new_int(value),
bytecode::ConstantData::Float { value } => ctx.new_float(value),
bytecode::ConstantData::Complex { value } => ctx.new_complex(value),
bytecode::ConstantData::Str { value } => vm.intern_string(value).into_object(),
bytecode::ConstantData::Str { value } if value.len() <= 20 => {
vm.intern_string(value).into_object()
}
bytecode::ConstantData::Str { value } => vm.ctx.new_str(value),
bytecode::ConstantData::Bytes { value } => ctx.new_bytes(value.to_vec()),
bytecode::ConstantData::Boolean { value } => ctx.new_bool(value),
bytecode::ConstantData::Code { code } => {
@@ -110,7 +114,10 @@ impl ConstantBag for PyObjBag<'_> {
bytecode::BorrowedConstant::Integer { value } => ctx.new_bigint(value),
bytecode::BorrowedConstant::Float { value } => ctx.new_float(value),
bytecode::BorrowedConstant::Complex { value } => ctx.new_complex(value),
bytecode::BorrowedConstant::Str { value } => vm.intern_string(value).into_object(),
bytecode::BorrowedConstant::Str { value } if value.len() <= 20 => {
vm.intern_string(value).into_object()
}
bytecode::BorrowedConstant::Str { value } => vm.ctx.new_str(value),
bytecode::BorrowedConstant::Bytes { value } => ctx.new_bytes(value.to_vec()),
bytecode::BorrowedConstant::Boolean { value } => ctx.new_bool(value),
bytecode::BorrowedConstant::Code { code } => {
@@ -128,6 +135,12 @@ impl ConstantBag for PyObjBag<'_> {
};
PyConstant(obj)
}
fn make_name(&self, name: String) -> PyStrRef {
self.0.intern_string(name)
}
fn make_name_ref(&self, name: &str) -> PyStrRef {
self.0.intern_string(name)
}
}
pub type PyCodeRef = PyRef<PyCode>;
@@ -208,7 +221,7 @@ impl PyCodeRef {
#[pyproperty]
fn co_argcount(self) -> usize {
self.code.arg_names.len()
self.code.arg_count
}
#[pyproperty]
@@ -223,7 +236,7 @@ impl PyCodeRef {
#[pyproperty]
fn co_kwonlyargcount(self) -> usize {
self.code.kwonlyarg_names.len()
self.code.kwonlyarg_count
}
#[pyproperty]
@@ -244,7 +257,12 @@ impl PyCodeRef {
#[pyproperty]
fn co_varnames(self, vm: &VirtualMachine) -> PyObjectRef {
let varnames = self.code.varnames().map(|s| vm.ctx.new_str(s)).collect();
let varnames = self
.code
.names
.iter()
.map(|s| s.clone().into_object())
.collect();
vm.ctx.new_tuple(varnames)
}
}

View File

@@ -1,7 +1,7 @@
#[cfg(feature = "jit")]
mod jitfunc;
use super::code::{CodeObject, PyCodeRef};
use super::code::PyCodeRef;
use super::dict::PyDictRef;
use super::pystr::PyStrRef;
use super::pytype::PyTypeRef;
@@ -63,14 +63,13 @@ impl PyFunction {
fn fill_locals_from_args(
&self,
code_object: &CodeObject,
locals: &PyDictRef,
func_args: FuncArgs,
vm: &VirtualMachine,
) -> PyResult<()> {
let nargs = func_args.args.len();
let nexpected_args = code_object.arg_names.len();
let posonly_args = &code_object.arg_names[..code_object.posonlyarg_count];
let nexpected_args = self.code.arg_count;
let arg_names = self.code.arg_names();
// This parses the arguments from args and kwargs into
// the proper variables keeping into account default values
@@ -84,23 +83,17 @@ impl PyFunction {
nargs
};
let mut args_iter = func_args.args.into_iter();
// Copy positional arguments into local variables
for i in 0..n {
let arg_name = &code_object.arg_names[i];
let arg = &func_args.args[i];
locals.set_item(arg_name.as_str(), arg.clone(), vm)?;
for (arg, arg_name) in args_iter.by_ref().take(n).zip(arg_names.args) {
locals.set_item(arg_name.clone(), arg, vm)?;
}
// Pack other positional arguments in to *args:
if let Some(ref vararg_name) = code_object.varargs_name {
let mut last_args = vec![];
for i in n..nargs {
let arg = &func_args.args[i];
last_args.push(arg.clone());
}
let vararg_value = vm.ctx.new_tuple(last_args);
locals.set_item(vararg_name.as_str(), vararg_value, vm)?;
if let Some(vararg_name) = arg_names.vararg {
let vararg_value = vm.ctx.new_tuple(args_iter.collect());
locals.set_item(vararg_name.clone(), vararg_value, vm)?;
} else {
// Check the number of positional arguments
if nargs > nexpected_args {
@@ -112,27 +105,25 @@ impl PyFunction {
}
// Do we support `**kwargs` ?
let kwargs = if code_object
.flags
.contains(bytecode::CodeFlags::HAS_VARKEYWORDS)
{
let kwargs = if let Some(varkwarg) = arg_names.varkwarg {
let d = vm.ctx.new_dict();
if let Some(ref kwargs_name) = code_object.varkeywords_name {
locals.set_item(kwargs_name.as_str(), d.as_object().clone(), vm)?;
}
locals.set_item(varkwarg.clone(), d.as_object().clone(), vm)?;
Some(d)
} else {
None
};
let contains_arg =
|names: &[PyStrRef], name: &str| names.iter().any(|s| s.borrow_value() == name);
let mut posonly_passed_as_kwarg = Vec::new();
// Handle keyword arguments
for (name, value) in func_args.kwargs {
// Check if we have a parameter with this name:
let dict = if code_object.arg_names.contains(&name)
|| code_object.kwonlyarg_names.contains(&name)
let dict = if contains_arg(arg_names.args, &name)
|| contains_arg(arg_names.kwonlyargs, &name)
{
if posonly_args.contains(&name) {
if contains_arg(arg_names.posonlyargs, &name) {
posonly_passed_as_kwarg.push(name);
continue;
} else if locals.contains_key(&name, vm) {
@@ -146,12 +137,12 @@ impl PyFunction {
vm.new_type_error(format!("Got an unexpected keyword argument '{}'", name))
})?
};
dict.set_item(name.as_str(), value, vm)?;
dict.set_item(vm.intern_string(name).into_object(), value, vm)?;
}
if !posonly_passed_as_kwarg.is_empty() {
return Err(vm.new_type_error(format!(
"{}() got some positional-only arguments passed as keyword arguments: '{}'",
&code_object.obj_name,
&self.code.obj_name,
posonly_passed_as_kwarg.into_iter().format(", "),
)));
}
@@ -167,8 +158,8 @@ impl PyFunction {
let required_args = nexpected_args - num_defaults_available;
let mut missing = vec![];
for i in 0..required_args {
let variable_name = &code_object.arg_names[i];
if !locals.contains_key(variable_name, vm) {
let variable_name = &arg_names.args[i];
if !locals.contains_key(variable_name.clone(), vm) {
missing.push(variable_name)
}
}
@@ -184,22 +175,20 @@ impl PyFunction {
// We have sufficient defaults, so iterate over the corresponding names and use
// the default if we don't already have a value
for (default_index, i) in (required_args..nexpected_args).enumerate() {
let arg_name = &code_object.arg_names[i];
if !locals.contains_key(arg_name, vm) {
locals.set_item(arg_name.as_str(), defaults[default_index].clone(), vm)?;
let arg_name = &arg_names.args[i];
if !locals.contains_key(arg_name.clone(), vm) {
locals.set_item(arg_name.clone(), defaults[default_index].clone(), vm)?;
}
}
}
};
// Check if kw only arguments are all present:
for arg_name in &code_object.kwonlyarg_names {
if !locals.contains_key(arg_name, vm) {
for arg_name in arg_names.kwonlyargs {
if !locals.contains_key(arg_name.clone(), vm) {
if let Some(kw_only_defaults) = &self.kw_only_defaults {
if let Some(default) =
kw_only_defaults.get_item_option(arg_name.as_str(), vm)?
{
locals.set_item(arg_name.as_str(), default, vm)?;
if let Some(default) = kw_only_defaults.get_item_option(arg_name.clone(), vm)? {
locals.set_item(arg_name.clone(), default, vm)?;
continue;
}
}
@@ -242,7 +231,7 @@ impl PyFunction {
scope.clone()
};
self.fill_locals_from_args(&code, &scope.get_locals(), func_args, vm)?;
self.fill_locals_from_args(&scope.get_locals(), func_args, vm)?;
// Construct frame:
let frame = Frame::new(code.clone(), scope, vm).into_ref(vm);

View File

@@ -1,6 +1,7 @@
use crate::builtins::dict::PyDictRef;
use crate::builtins::function::{PyFunction, PyFunctionRef};
use crate::builtins::{float, int, pybool};
use crate::builtins::{float, int, pybool, PyStrRef};
use crate::bytecode::CodeFlags;
use crate::exceptions::PyBaseExceptionRef;
use crate::function::FuncArgs;
use crate::pyobject::{
@@ -9,7 +10,6 @@ use crate::pyobject::{
};
use crate::VirtualMachine;
use num_traits::ToPrimitive;
use rustpython_bytecode::bytecode::CodeFlags;
use rustpython_jit::{AbiValue, Args, CompiledCode, JitArgumentError, JitType};
#[derive(Debug, thiserror::Error)]
@@ -68,6 +68,8 @@ fn get_jit_arg_type(dict: &PyDictRef, name: &str, vm: &VirtualMachine) -> PyResu
}
pub fn get_jit_arg_types(func: &PyFunctionRef, vm: &VirtualMachine) -> PyResult<Vec<JitType>> {
let arg_names = func.code.arg_names();
if func
.code
.flags
@@ -79,7 +81,7 @@ pub fn get_jit_arg_types(func: &PyFunctionRef, vm: &VirtualMachine) -> PyResult<
));
}
if func.code.arg_names.is_empty() && func.code.kwonlyarg_names.is_empty() {
if arg_names.args.is_empty() && arg_names.kwonlyargs.is_empty() {
return Ok(Vec::new());
}
@@ -92,12 +94,12 @@ pub fn get_jit_arg_types(func: &PyFunctionRef, vm: &VirtualMachine) -> PyResult<
} else if let Ok(dict) = PyDictRef::try_from_object(vm, annotations) {
let mut arg_types = Vec::new();
for arg in &func.code.arg_names {
arg_types.push(get_jit_arg_type(&dict, arg, vm)?);
for arg in arg_names.args {
arg_types.push(get_jit_arg_type(&dict, arg.borrow_value(), vm)?);
}
for arg in &func.code.kwonlyarg_names {
arg_types.push(get_jit_arg_type(&dict, arg, vm)?);
for arg in arg_names.kwonlyargs {
arg_types.push(get_jit_arg_type(&dict, arg.borrow_value(), vm)?);
}
Ok(arg_types)
@@ -136,8 +138,9 @@ pub(crate) fn get_jit_args<'a>(
) -> Result<Args<'a>, ArgsError> {
let mut jit_args = jitted_code.args_builder();
let nargs = func_args.args.len();
let arg_names = func.code.arg_names();
if nargs > func.code.arg_names.len() || nargs < func.code.posonlyarg_count {
if nargs > func.code.arg_count || nargs < func.code.posonlyarg_count {
return Err(ArgsError::WrongNumberOfArgs);
}
@@ -148,14 +151,15 @@ pub(crate) fn get_jit_args<'a>(
// Handle keyword arguments
for (name, value) in &func_args.kwargs {
if let Some(arg_idx) = func.code.arg_names.iter().position(|arg| arg == name) {
let arg_pos =
|args: &[PyStrRef], name: &str| args.iter().position(|arg| arg.borrow_value() == name);
if let Some(arg_idx) = arg_pos(arg_names.args, name) {
if jit_args.is_set(arg_idx) {
return Err(ArgsError::ArgPassedMultipleTimes);
}
jit_args.set(arg_idx, get_jit_value(vm, &value)?)?;
} else if let Some(kwarg_idx) = func.code.kwonlyarg_names.iter().position(|arg| arg == name)
{
let arg_idx = kwarg_idx + func.code.arg_names.len();
} else if let Some(kwarg_idx) = arg_pos(arg_names.kwonlyargs, name) {
let arg_idx = kwarg_idx + func.code.arg_count;
if jit_args.is_set(arg_idx) {
return Err(ArgsError::ArgPassedMultipleTimes);
}
@@ -169,7 +173,7 @@ pub(crate) fn get_jit_args<'a>(
if let Some(defaults) = &func.defaults {
let defaults = defaults.borrow_value();
for (i, default) in defaults.iter().enumerate() {
let arg_idx = i + func.code.arg_names.len() - defaults.len();
let arg_idx = i + func.code.arg_count - defaults.len();
if !jit_args.is_set(arg_idx) {
jit_args.set(arg_idx, get_jit_value(vm, default)?)?;
}
@@ -178,11 +182,11 @@ pub(crate) fn get_jit_args<'a>(
// fill in keyword only defaults
if let Some(kw_only_defaults) = &func.kw_only_defaults {
for (i, name) in func.code.kwonlyarg_names.iter().enumerate() {
let arg_idx = i + func.code.arg_names.len();
for (i, name) in arg_names.kwonlyargs.iter().enumerate() {
let arg_idx = i + func.code.arg_count;
if !jit_args.is_set(arg_idx) {
let default = kw_only_defaults
.get_item(name.as_str(), vm)
.get_item(name.clone(), vm)
.map_err(|_| ArgsError::NotAllArgsPassed)
.and_then(|obj| get_jit_value(vm, &obj))?;
jit_args.set(arg_idx, default)?;

View File

@@ -70,6 +70,11 @@ where
s.as_ref().to_owned().into()
}
}
impl AsRef<str> for PyStrRef {
fn as_ref(&self) -> &str {
&self.value
}
}
impl From<String> for PyStr {
fn from(s: String) -> PyStr {
@@ -1452,6 +1457,7 @@ impl<'s> AnyStr<'s, char> for str {
}
}
#[derive(Clone)]
#[repr(transparent)]
pub struct PyStrExact(PyStrRef);
impl PyStrExact {
@@ -1482,3 +1488,8 @@ impl crate::dictdatatype::DictKey for PyStrExact {
self.0.key_eq(vm, other_key)
}
}
impl TryIntoRef<PyStr> for PyStrExact {
fn try_into_ref(self, _vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
Ok(self.0)
}
}

View File

@@ -13,7 +13,6 @@ use crate::pyobject::{
BorrowValue, IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject, TypeProtocol,
};
use crate::scope::NameProtocol;
use crate::slots::{SlotDescriptor, SlotGetattro};
use crate::vm::VirtualMachine;
@@ -87,10 +86,10 @@ impl PySuper {
obj
} else {
let frame = vm.current_frame().expect("no current frame for super()");
if let Some(first_arg) = frame.code.arg_names.get(0) {
if let Some(first_arg) = frame.code.arg_names().args.get(0) {
let locals = frame.scope.get_locals();
locals
.get_item_option(first_arg.as_str(), vm)?
.get_item_option(first_arg.clone(), vm)?
.ok_or_else(|| {
vm.new_type_error(format!("super argument {} was not supplied", first_arg))
})?

View File

@@ -26,7 +26,7 @@ use crate::pyobject::{
BorrowValue, IdProtocol, ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
};
use crate::scope::{NameProtocol, Scope};
use crate::scope::Scope;
use crate::slots::PyComparisonOp;
use crate::vm::VirtualMachine;
@@ -258,7 +258,6 @@ impl ExecutingFrame<'_> {
let idx = self.lasti.fetch_add(1, Ordering::Relaxed);
let loc = self.code.locations[idx];
let instr = &self.code.instructions[idx];
vm.check_signals()?;
let result = self.execute_instruction(instr, vm);
match result {
Ok(None) => {}
@@ -338,6 +337,8 @@ impl ExecutingFrame<'_> {
instruction: &bytecode::Instruction,
vm: &VirtualMachine,
) -> FrameResult {
vm.check_signals()?;
flame_guard!(format!("Frame::execute_instruction({:?})", instruction));
#[cfg(feature = "vm-tracing-logging")]
@@ -359,17 +360,15 @@ impl ExecutingFrame<'_> {
Ok(None)
}
bytecode::Instruction::Import {
ref name,
ref symbols,
ref level,
} => self.import(vm, name, symbols, *level),
name_idx,
symbols_idx,
level,
} => self.import(vm, *name_idx, symbols_idx, *level),
bytecode::Instruction::ImportStar => self.import_star(vm),
bytecode::Instruction::ImportFrom { ref name } => self.import_from(vm, name),
bytecode::Instruction::LoadName { ref name, scope } => self.load_name(vm, name, *scope),
bytecode::Instruction::StoreName { ref name, scope } => {
self.store_name(vm, name, *scope)
}
bytecode::Instruction::DeleteName { ref name } => self.delete_name(vm, name),
bytecode::Instruction::ImportFrom { idx } => self.import_from(vm, *idx),
bytecode::Instruction::LoadName { idx, scope } => self.load_name(vm, *idx, *scope),
bytecode::Instruction::StoreName { idx, scope } => self.store_name(vm, *idx, *scope),
bytecode::Instruction::DeleteName { idx } => self.delete_name(vm, *idx),
bytecode::Instruction::Subscript => self.execute_subscript(vm),
bytecode::Instruction::StoreSubscript => self.execute_store_subscript(vm),
bytecode::Instruction::DeleteSubscript => self.execute_delete_subscript(vm),
@@ -453,9 +452,9 @@ impl ExecutingFrame<'_> {
bytecode::Instruction::BinaryOperation { ref op, inplace } => {
self.execute_binop(vm, op, *inplace)
}
bytecode::Instruction::LoadAttr { ref name } => self.load_attr(vm, name),
bytecode::Instruction::StoreAttr { ref name } => self.store_attr(vm, name),
bytecode::Instruction::DeleteAttr { ref name } => self.delete_attr(vm, name),
bytecode::Instruction::LoadAttr { idx } => self.load_attr(vm, *idx),
bytecode::Instruction::StoreAttr { idx } => self.store_attr(vm, *idx),
bytecode::Instruction::DeleteAttr { idx } => self.delete_attr(vm, *idx),
bytecode::Instruction::UnaryOperation { ref op } => self.execute_unop(vm, op),
bytecode::Instruction::CompareOperation { ref op } => self.execute_compare(vm, op),
bytecode::Instruction::ReturnValue => {
@@ -778,24 +777,32 @@ impl ExecutingFrame<'_> {
fn import(
&mut self,
vm: &VirtualMachine,
module: &Option<String>,
symbols: &[String],
module: Option<bytecode::NameIdx>,
symbols: &[bytecode::NameIdx],
level: usize,
) -> FrameResult {
let module = module.clone().unwrap_or_default();
let module = vm.import(&module, symbols, level)?;
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)?;
self.push_value(module);
Ok(None)
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn import_from(&mut self, vm: &VirtualMachine, name: &str) -> FrameResult {
fn import_from(&mut self, vm: &VirtualMachine, idx: bytecode::NameIdx) -> FrameResult {
let module = self.last_value();
let name = &self.code.names[idx];
// Load attribute, and transform any error into import error.
let obj = vm
.get_attribute(module, name)
.map_err(|_| vm.new_import_error(format!("cannot import name '{}'", name), name))?;
let obj = vm.get_attribute(module, name.clone()).map_err(|_| {
vm.new_import_error(format!("cannot import name '{}'", name), name.clone())
})?;
self.push_value(obj);
Ok(None)
}
@@ -900,9 +907,10 @@ impl ExecutingFrame<'_> {
fn store_name(
&mut self,
vm: &VirtualMachine,
name: &str,
idx: bytecode::NameIdx,
name_scope: bytecode::NameScope,
) -> FrameResult {
let name = self.code.names[idx].clone();
let obj = self.pop_value();
match name_scope {
bytecode::NameScope::Global => {
@@ -921,8 +929,9 @@ impl ExecutingFrame<'_> {
Ok(None)
}
fn delete_name(&self, vm: &VirtualMachine, name: &str) -> FrameResult {
match self.scope.delete_name(vm, name) {
fn delete_name(&self, vm: &VirtualMachine, idx: bytecode::NameIdx) -> FrameResult {
let name = &self.code.names[idx];
match self.scope.delete_name(vm, name.clone()) {
Ok(_) => Ok(None),
Err(_) => Err(vm.new_name_error(format!("name '{}' is not defined", name))),
}
@@ -933,14 +942,15 @@ impl ExecutingFrame<'_> {
fn load_name(
&mut self,
vm: &VirtualMachine,
name: &str,
idx: bytecode::NameIdx,
name_scope: bytecode::NameScope,
) -> FrameResult {
let name = &self.code.names[idx];
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_local(&vm, name),
bytecode::NameScope::Free => self.scope.load_name(&vm, name),
bytecode::NameScope::Global => self.scope.load_global(vm, name.clone()),
bytecode::NameScope::NonLocal => self.scope.load_cell(vm, name.clone()),
bytecode::NameScope::Local => self.scope.load_local(vm, name.clone()),
bytecode::NameScope::Free => self.scope.load_name(vm, name.clone()),
};
let value = optional_value
@@ -1441,24 +1451,26 @@ impl ExecutingFrame<'_> {
Ok(None)
}
fn load_attr(&mut self, vm: &VirtualMachine, attr_name: &str) -> FrameResult {
fn load_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone();
let parent = self.pop_value();
let obj = vm.get_attribute(parent, attr_name)?;
self.push_value(obj);
Ok(None)
}
fn store_attr(&mut self, vm: &VirtualMachine, attr_name: &str) -> FrameResult {
fn store_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone();
let parent = self.pop_value();
let value = self.pop_value();
vm.set_attr(&parent, vm.ctx.new_str(attr_name), value)?;
vm.set_attr(&parent, attr_name, value)?;
Ok(None)
}
fn delete_attr(&mut self, vm: &VirtualMachine, attr_name: &str) -> FrameResult {
fn delete_attr(&mut self, vm: &VirtualMachine, attr: bytecode::NameIdx) -> FrameResult {
let attr_name = self.code.names[attr].clone().into_object();
let parent = self.pop_value();
let name = vm.ctx.new_str(attr_name);
vm.del_attr(&parent, name)?;
vm.del_attr(&parent, attr_name)?;
Ok(None)
}

View File

@@ -1,8 +1,8 @@
use std::fmt;
use crate::builtins::dict::PyDictRef;
use crate::pyobject::{ItemProtocol, PyContext, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
use crate::builtins::{PyDictRef, PyStr, PyStrRef};
use crate::pyobject::{IntoPyObject, ItemProtocol, PyContext, PyObjectRef, PyResult, TryIntoRef};
use crate::VirtualMachine;
/*
* So a scope is a linked list of scopes.
@@ -69,24 +69,11 @@ impl Scope {
pub fn new_child_scope(&self, ctx: &PyContext) -> Scope {
self.new_child_scope_with_locals(ctx.new_dict())
}
}
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>;
fn store_global(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
}
impl NameProtocol for Scope {
#[cfg_attr(feature = "flame-it", flame("Scope"))]
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
pub fn load_name(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
for dict in self.locals.iter() {
if let Some(value) = dict.get_item_option(name, vm).unwrap() {
if let Some(value) = dict.get_item_option(name.clone(), vm).unwrap() {
return Some(value);
}
}
@@ -97,23 +84,28 @@ impl NameProtocol for Scope {
#[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> {
pub fn load_local(&self, vm: &VirtualMachine, name: impl PyName) -> 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> {
pub fn load_cell(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
for dict in self.locals.iter().skip(1) {
if let Some(value) = dict.get_item_option(name, vm).unwrap() {
if let Some(value) = dict.get_item_option(name.clone(), vm).unwrap() {
return Some(value);
}
}
None
}
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {
pub fn store_cell(&self, vm: &VirtualMachine, name: impl PyName, value: PyObjectRef) {
// find the innermost outer scope that contains the symbol name
if let Some(locals) = self.locals.iter().rev().find(|l| l.contains_key(name, vm)) {
if let Some(locals) = self
.locals
.iter()
.rev()
.find(|l| l.contains_key(name.clone(), vm))
{
// add to the symbol
locals.set_item(name, value, vm).unwrap();
} else {
@@ -131,25 +123,37 @@ impl NameProtocol for Scope {
}
}
fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) {
pub fn store_name(&self, vm: &VirtualMachine, key: impl PyName, value: PyObjectRef) {
self.get_locals().set_item(key, value, vm).unwrap();
}
fn delete_name(&self, vm: &VirtualMachine, key: &str) -> PyResult {
pub fn delete_name(&self, vm: &VirtualMachine, key: impl PyName) -> PyResult {
self.get_locals().del_item(key, vm)
}
#[cfg_attr(feature = "flame-it", flame("Scope"))]
/// Load a global name.
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
if let Some(value) = self.globals.get_item_option(name, vm).unwrap() {
pub fn load_global(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
if let Some(value) = self.globals.get_item_option(name.clone(), vm).unwrap() {
Some(value)
} else {
vm.get_attribute(vm.builtins.clone(), name).ok()
}
}
fn store_global(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {
pub fn store_global(&self, vm: &VirtualMachine, name: impl PyName, value: PyObjectRef) {
self.globals.set_item(name, value, vm).unwrap();
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for &str {}
impl Sealed for super::PyStrRef {}
}
pub trait PyName:
sealed::Sealed + crate::dictdatatype::DictKey + Clone + IntoPyObject + TryIntoRef<PyStr>
{
}
impl PyName for &str {}
impl PyName for PyStrRef {}

View File

@@ -77,10 +77,9 @@ fn _imp_exec_builtin(_mod: PyModuleRef) -> i32 {
}
fn _imp_get_frozen_object(name: PyStrRef, vm: &VirtualMachine) -> PyResult<PyCode> {
let name = name.borrow_value();
vm.state
.frozen
.get(name)
.get(name.borrow_value())
.map(|frozen| {
let mut frozen = frozen.code.clone();
frozen.source_path = format!("frozen {}", name);
@@ -94,10 +93,9 @@ fn _imp_init_frozen(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
}
fn _imp_is_frozen_package(name: PyStrRef, vm: &VirtualMachine) -> PyResult<bool> {
let name = name.borrow_value();
vm.state
.frozen
.get(name)
.get(name.borrow_value())
.map(|frozen| frozen.package)
.ok_or_else(|| vm.new_import_error(format!("No such frozen object named {}", name), name))
}

View File

@@ -700,10 +700,14 @@ impl VirtualMachine {
syntax_error
}
pub fn new_import_error(&self, msg: String, name: &str) -> PyBaseExceptionRef {
pub fn new_import_error(
&self,
msg: String,
name: impl TryIntoRef<PyStr>,
) -> PyBaseExceptionRef {
let import_error = self.ctx.exceptions.import_error.clone();
let exc = self.new_exception_msg(import_error, msg);
self.set_attr(exc.as_object(), "name", self.new_pyobj(name))
self.set_attr(exc.as_object(), "name", name.try_into_ref(self).unwrap())
.unwrap();
exc
}
@@ -819,7 +823,7 @@ impl VirtualMachine {
})
}
pub fn import(&self, module: &str, from_list: &[String], level: usize) -> PyResult {
pub fn import(&self, module: &str, from_list: &[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();
@@ -859,7 +863,7 @@ impl VirtualMachine {
};
let from_list = self
.ctx
.new_tuple(from_list.iter().map(|name| self.new_pyobj(name)).collect());
.new_tuple(from_list.iter().map(|x| x.as_object().clone()).collect());
self.invoke(&import_func, (module, globals, locals, from_list, level))
.map_err(|exc| import::remove_importlib_frames(self, &exc))
}
@@ -1642,7 +1646,7 @@ impl VirtualMachine {
}
pub fn intern_string<S: Internable>(&self, s: S) -> PyStrRef {
let (s, _) = self
let (s, ()) = self
.ctx
.string_cache
.setdefault_entry(self, s, || ())

View File

@@ -4,7 +4,6 @@ use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use rustpython_vm::builtins::{PyDictRef, PyStrRef, PyTypeRef};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::function::OptionalArg;
use rustpython_vm::import::import_file;
use rustpython_vm::pyobject::{
@@ -276,6 +275,6 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
}
pub fn setup_browser_module(vm: &mut VirtualMachine) {
state.add_native_module("_browser".to_owned(), Box::new(make_module));
vm.add_native_module("_browser".to_owned(), Box::new(make_module));
vm.add_frozen(py_freeze!(file = "src/browser.py", module_name = "browser"));
}

View File

@@ -7,7 +7,6 @@ use wasm_bindgen::{closure::Closure, prelude::*, JsCast};
use wasm_bindgen_futures::{future_to_promise, JsFuture};
use rustpython_vm::builtins::{PyFloatRef, PyStrRef, PyTypeRef};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::exceptions::PyBaseExceptionRef;
use rustpython_vm::function::{Args, OptionalArg};
use rustpython_vm::pyobject::{

View File

@@ -5,10 +5,9 @@ use std::rc::{Rc, Weak};
use js_sys::{Object, TypeError};
use wasm_bindgen::prelude::*;
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::compile::{self, Mode};
use rustpython_vm::pyobject::{ItemProtocol, PyObjectRef, PyObjectWeak, PyValue};
use rustpython_vm::scope::{NameProtocol, Scope};
use rustpython_vm::scope::Scope;
use rustpython_vm::{InitParameter, Interpreter, PySettings, VirtualMachine};
use crate::browser_module::setup_browser_module;
@@ -204,7 +203,7 @@ impl WASMVirtualMachine {
pub fn add_to_scope(&self, name: String, value: JsValue) -> Result<(), JsValue> {
self.with_vm(move |vm, StoredVirtualMachine { ref scope, .. }| {
let value = convert::js_to_py(vm, value);
scope.borrow_mut().store_name(&vm, &name, value);
scope.borrow_mut().store_name(&vm, name.as_str(), value);
})
}