forked from Rust-related/RustPython
Add "incognito" compilation
This commit is contained in:
@@ -46,6 +46,7 @@ pub struct CodeObject {
|
||||
pub source_path: String,
|
||||
pub first_line_number: usize,
|
||||
pub obj_name: String, // Name of the object that created this code object
|
||||
pub incognito: bool,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@@ -393,6 +394,7 @@ impl CodeObject {
|
||||
source_path,
|
||||
first_line_number,
|
||||
obj_name,
|
||||
incognito: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,26 @@ struct Compiler<O: OutputStream = BasicOutputStream> {
|
||||
current_source_location: ast::Location,
|
||||
current_qualified_path: Option<String>,
|
||||
ctx: CompileContext,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompileOpts {
|
||||
/// How optimized the bytecode output should be; any optimize > 0 does
|
||||
/// not emit assert statements
|
||||
pub optimize: u8,
|
||||
/// Whether introspection on defined functions/other items should be allowed;
|
||||
/// e.g. when true, `func.__globals__` throws an error (where func is a function defined
|
||||
/// in an incognito context)
|
||||
pub incognito: bool,
|
||||
}
|
||||
impl Default for CompileOpts {
|
||||
fn default() -> Self {
|
||||
CompileOpts {
|
||||
optimize: 0,
|
||||
incognito: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -60,20 +79,20 @@ pub fn compile(
|
||||
source: &str,
|
||||
mode: Mode,
|
||||
source_path: String,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
) -> CompileResult<CodeObject> {
|
||||
match mode {
|
||||
Mode::Exec => {
|
||||
let ast = parser::parse_program(source)?;
|
||||
compile_program(ast, source_path, optimize)
|
||||
compile_program(ast, source_path.clone(), opts)
|
||||
}
|
||||
Mode::Eval => {
|
||||
let statement = parser::parse_statement(source)?;
|
||||
compile_statement_eval(statement, source_path, optimize)
|
||||
compile_statement_eval(statement, source_path.clone(), opts)
|
||||
}
|
||||
Mode::Single => {
|
||||
let ast = parser::parse_program(source)?;
|
||||
compile_program_single(ast, source_path, optimize)
|
||||
compile_program_single(ast, source_path.clone(), opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,10 +100,10 @@ pub fn compile(
|
||||
/// A helper function for the shared code of the different compile functions
|
||||
fn with_compiler(
|
||||
source_path: String,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
f: impl FnOnce(&mut Compiler) -> CompileResult<()>,
|
||||
) -> CompileResult<CodeObject> {
|
||||
let mut compiler = Compiler::new(optimize);
|
||||
let mut compiler = Compiler::new(opts);
|
||||
compiler.source_path = Some(source_path);
|
||||
compiler.push_new_code_object("<module>".to_owned());
|
||||
f(&mut compiler)?;
|
||||
@@ -97,9 +116,9 @@ fn with_compiler(
|
||||
pub fn compile_program(
|
||||
ast: ast::Program,
|
||||
source_path: String,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
) -> CompileResult<CodeObject> {
|
||||
with_compiler(source_path, optimize, |compiler| {
|
||||
with_compiler(source_path, opts, |compiler| {
|
||||
let symbol_table = make_symbol_table(&ast)?;
|
||||
compiler.compile_program(&ast, symbol_table)
|
||||
})
|
||||
@@ -109,9 +128,9 @@ pub fn compile_program(
|
||||
pub fn compile_statement_eval(
|
||||
statement: Vec<ast::Statement>,
|
||||
source_path: String,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
) -> CompileResult<CodeObject> {
|
||||
with_compiler(source_path, optimize, |compiler| {
|
||||
with_compiler(source_path, opts, |compiler| {
|
||||
let symbol_table = statements_to_symbol_table(&statement)?;
|
||||
compiler.compile_statement_eval(&statement, symbol_table)
|
||||
})
|
||||
@@ -121,9 +140,9 @@ pub fn compile_statement_eval(
|
||||
pub fn compile_program_single(
|
||||
ast: ast::Program,
|
||||
source_path: String,
|
||||
optimize: u8,
|
||||
opts: CompileOpts,
|
||||
) -> CompileResult<CodeObject> {
|
||||
with_compiler(source_path, optimize, |compiler| {
|
||||
with_compiler(source_path, opts, |compiler| {
|
||||
let symbol_table = make_symbol_table(&ast)?;
|
||||
compiler.compile_program_single(&ast, symbol_table)
|
||||
})
|
||||
@@ -134,12 +153,12 @@ where
|
||||
O: OutputStream,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Compiler::new(0)
|
||||
Compiler::new(CompileOpts::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: OutputStream> Compiler<O> {
|
||||
fn new(optimize: u8) -> Self {
|
||||
fn new(opts: CompileOpts) -> Self {
|
||||
Compiler {
|
||||
output_stack: Vec::new(),
|
||||
symbol_table_stack: Vec::new(),
|
||||
@@ -151,7 +170,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
in_loop: false,
|
||||
func: FunctionContext::NoFunction,
|
||||
},
|
||||
optimize,
|
||||
opts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +186,8 @@ impl<O: OutputStream> Compiler<O> {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_output(&mut self, code: CodeObject) {
|
||||
fn push_output(&mut self, mut code: CodeObject) {
|
||||
code.incognito = self.opts.incognito;
|
||||
self.output_stack.push(code.into());
|
||||
}
|
||||
|
||||
@@ -518,7 +538,7 @@ impl<O: OutputStream> Compiler<O> {
|
||||
} => self.compile_class_def(name, body, bases, keywords, decorator_list)?,
|
||||
Assert { test, msg } => {
|
||||
// if some flag, ignore all assert statements!
|
||||
if self.optimize == 0 {
|
||||
if self.opts.optimize == 0 {
|
||||
let end_label = self.new_label();
|
||||
self.compile_jump_if(test, true, end_label)?;
|
||||
self.emit(Instruction::LoadName {
|
||||
|
||||
@@ -49,7 +49,7 @@ impl CompilationSource {
|
||||
module_name: String,
|
||||
origin: F,
|
||||
) -> Result<CodeObject, Diagnostic> {
|
||||
compile::compile(source, mode, module_name, 0).map_err(|err| {
|
||||
compile::compile(source, mode, module_name, Default::default()).map_err(|err| {
|
||||
Diagnostic::spans_error(
|
||||
self.span,
|
||||
format!("Python compile error from {}: {}", origin(), err),
|
||||
|
||||
@@ -62,9 +62,14 @@ fn main() {
|
||||
let optimize = matches.occurrences_of("optimize") as u8;
|
||||
let scripts = matches.values_of_os("scripts").unwrap();
|
||||
|
||||
let opts = compile::CompileOpts {
|
||||
optimize,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for script in scripts.map(Path::new) {
|
||||
if script.exists() && script.is_file() {
|
||||
let res = display_script(script, mode, optimize, expand_codeobjects);
|
||||
let res = display_script(script, mode, opts.clone(), expand_codeobjects);
|
||||
if let Err(e) = res {
|
||||
error!("Error while compiling {:?}: {}", script, e);
|
||||
}
|
||||
@@ -77,11 +82,11 @@ fn main() {
|
||||
fn display_script(
|
||||
path: &Path,
|
||||
mode: compile::Mode,
|
||||
optimize: u8,
|
||||
opts: compile::CompileOpts,
|
||||
expand_codeobjects: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let source = fs::read_to_string(path)?;
|
||||
let code = compile::compile(&source, mode, path.to_string_lossy().into_owned(), optimize)?;
|
||||
let code = compile::compile(&source, mode, path.to_string_lossy().into_owned(), opts)?;
|
||||
println!("{}:", path.display());
|
||||
if expand_codeobjects {
|
||||
println!("{}", code.display_expand_codeobjects());
|
||||
|
||||
@@ -13,6 +13,7 @@ assert foo.__doc__ == "test"
|
||||
assert foo.__name__ == "foo"
|
||||
assert foo.__qualname__ == "foo"
|
||||
assert foo.__module__ == "function"
|
||||
assert foo.__globals__ is globals()
|
||||
|
||||
def my_func(a,):
|
||||
return a+2
|
||||
|
||||
@@ -74,13 +74,8 @@ pub fn import_file(
|
||||
file_path: String,
|
||||
content: String,
|
||||
) -> PyResult {
|
||||
let code_obj = compile::compile(
|
||||
&content,
|
||||
compile::Mode::Exec,
|
||||
file_path,
|
||||
vm.settings.optimize,
|
||||
)
|
||||
.map_err(|err| vm.new_syntax_error(&err))?;
|
||||
let code_obj = compile::compile(&content, compile::Mode::Exec, file_path, vm.compile_opts())
|
||||
.map_err(|err| vm.new_syntax_error(&err))?;
|
||||
import_codeobj(vm, module_name, code_obj, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,21 @@ impl FrameRef {
|
||||
}
|
||||
|
||||
#[pyproperty]
|
||||
fn f_globals(self) -> PyDictRef {
|
||||
self.scope.globals.clone()
|
||||
fn f_globals(self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
|
||||
if self.code.incognito {
|
||||
Err(vm.new_type_error("Can't get f_globals on an incognito frame".to_owned()))
|
||||
} else {
|
||||
Ok(self.scope.globals.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyproperty]
|
||||
fn f_locals(self) -> PyDictRef {
|
||||
self.scope.get_locals()
|
||||
fn f_locals(self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
|
||||
if self.code.incognito {
|
||||
Err(vm.new_type_error("Can't get f_locals on an incognito frame".to_owned()))
|
||||
} else {
|
||||
Ok(self.scope.get_locals())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyproperty]
|
||||
|
||||
@@ -278,6 +278,15 @@ impl PyFunction {
|
||||
fn kwdefaults(&self) -> Option<PyDictRef> {
|
||||
self.kw_only_defaults.clone()
|
||||
}
|
||||
|
||||
#[pyproperty(magic)]
|
||||
fn globals(&self, vm: &VirtualMachine) -> PyResult<PyDictRef> {
|
||||
if self.code.incognito {
|
||||
Err(vm.new_type_error("Can't get __globals__ on an incognito function".to_owned()))
|
||||
} else {
|
||||
Ok(self.scope.globals.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
|
||||
28
vm/src/vm.rs
28
vm/src/vm.rs
@@ -17,7 +17,10 @@ use num_bigint::BigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use once_cell::sync::Lazy;
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
use rustpython_compiler::{compile, error::CompileError};
|
||||
use rustpython_compiler::{
|
||||
compile::{self, CompileOpts},
|
||||
error::CompileError,
|
||||
};
|
||||
|
||||
use crate::builtins::{self, to_ascii};
|
||||
use crate::bytecode;
|
||||
@@ -1000,6 +1003,16 @@ impl VirtualMachine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a basic CompileOpts instance with options accurate to the vm. Used
|
||||
/// as the CompileOpts for `vm.compile()`.
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
pub fn compile_opts(&self) -> CompileOpts {
|
||||
CompileOpts {
|
||||
optimize: self.settings.optimize,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
pub fn compile(
|
||||
&self,
|
||||
@@ -1007,7 +1020,18 @@ impl VirtualMachine {
|
||||
mode: compile::Mode,
|
||||
source_path: String,
|
||||
) -> Result<PyCodeRef, CompileError> {
|
||||
compile::compile(source, mode, source_path, self.settings.optimize)
|
||||
self.compile_with_opts(source, mode, source_path, self.compile_opts())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
pub fn compile_with_opts(
|
||||
&self,
|
||||
source: &str,
|
||||
mode: compile::Mode,
|
||||
source_path: String,
|
||||
opts: CompileOpts,
|
||||
) -> Result<PyCodeRef, CompileError> {
|
||||
compile::compile(source, mode, source_path, opts)
|
||||
.map(|codeobj| PyCode::new(codeobj).into_ref(self))
|
||||
.map_err(|mut compile_error| {
|
||||
compile_error.update_statement_info(source.trim_end().to_owned());
|
||||
|
||||
@@ -263,8 +263,13 @@ impl WASMVirtualMachine {
|
||||
strict_private: Option<bool>,
|
||||
) -> Result<(), JsValue> {
|
||||
self.with(|StoredVirtualMachine { ref vm, .. }| {
|
||||
let strict_private = strict_private.unwrap_or(false);
|
||||
let opts = compile::CompileOpts {
|
||||
incognito: strict_private,
|
||||
..vm.compile_opts()
|
||||
};
|
||||
let code = vm
|
||||
.compile(source, Mode::Exec, name.clone())
|
||||
.compile_with_opts(source, Mode::Exec, name.clone(), opts)
|
||||
.map_err(convert::syntax_err)?;
|
||||
let attrs = vm.ctx.new_dict();
|
||||
attrs
|
||||
@@ -282,7 +287,7 @@ impl WASMVirtualMachine {
|
||||
vm.run_code_obj(code, Scope::new(None, attrs.clone(), vm))
|
||||
.to_js(vm)?;
|
||||
|
||||
let module_attrs = if strict_private.unwrap_or(false) {
|
||||
let module_attrs = if strict_private {
|
||||
let all = attrs
|
||||
.get_item_option("__all__", vm)
|
||||
.to_js(vm)?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
def test_inject_private(wdriver):
|
||||
assert wdriver.execute_script(
|
||||
wdriver.execute_script(
|
||||
"""
|
||||
const vm = rp.vmStore.init("vm")
|
||||
vm.injectModule(
|
||||
@@ -11,8 +11,17 @@ def get_thing(): return __thing()
|
||||
{ __thing: () => 1 },
|
||||
true
|
||||
)
|
||||
return vm.execSingle(
|
||||
`import mod; mod.get_thing() == 1 and "__thing" not in dir(mod)`
|
||||
vm.execSingle(
|
||||
`
|
||||
import mod
|
||||
assert mod.get_thing() == 1
|
||||
assert "__thing" not in dir(mod)
|
||||
try:
|
||||
globs = mod.get_thing.__globals__
|
||||
except TypeError: pass
|
||||
else:
|
||||
assert False, "incognito function.__globals__ didn't error"
|
||||
`
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user