1use crate::{
4 Py, PyObjectRef, PyResult, VirtualMachine,
5 builtins::PyCode,
6 compiler::{self},
7 scope::Scope,
8};
9
10impl VirtualMachine {
11 pub fn run_simple_string(&self, source: &str) -> PyResult {
15 let scope = self.new_scope_with_builtins();
16 self.run_string(scope, source, "<string>")
17 }
18
19 pub fn run_string(&self, scope: Scope, source: &str, source_path: &str) -> PyResult {
23 let code_obj = self
24 .compile(source, compiler::Mode::Exec, source_path)
25 .map_err(|err| err.into_pyexception(self, Some(source)))?;
26 let _ = self.register_code_in_linecache(&code_obj, source);
28 self.run_code_obj(code_obj, scope)
29 }
30
31 pub fn register_code_in_linecache(&self, code: &Py<PyCode>, source: &str) -> PyResult<()> {
34 let linecache = self.import("linecache", 0)?;
35 let register = linecache.get_attr("_register_code", self)?;
36 let source_str = self.ctx.new_str(source);
37 let filename = self.ctx.new_str(code.source_path().as_str());
38 register.call(
39 (PyObjectRef::from(code.to_owned()), source_str, filename),
40 self,
41 )?;
42 Ok(())
43 }
44
45 #[deprecated(note = "use run_string instead")]
46 pub fn run_code_string(&self, scope: Scope, source: &str, source_path: &str) -> PyResult {
47 self.run_string(scope, source, source_path)
48 }
49
50 pub fn run_block_expr(&self, scope: Scope, source: &str) -> PyResult {
51 let code_obj = self
52 .compile(source, compiler::Mode::BlockExpr, "<embedded>")
53 .map_err(|err| err.into_pyexception(self, Some(source)))?;
54 self.run_code_obj(code_obj, scope)
55 }
56}
57
58#[cfg(feature = "host_env")]
59mod file_run {
60 use crate::{
61 AsObject, Py, PyResult, VirtualMachine,
62 builtins::{PyBaseExceptionRef, PyCode, PyDict},
63 compiler::{self},
64 scope::Scope,
65 };
66
67 impl VirtualMachine {
68 #[doc(hidden)]
75 pub fn run_any_file(&self, scope: Scope, path: &str) -> PyResult<()> {
76 let path = if path.is_empty() { "???" } else { path };
77 self.run_simple_file(scope, path)
78 }
79
80 fn run_simple_file(&self, scope: Scope, path: &str) -> PyResult<()> {
85 self.with_simple_run(path, |module_dict| {
86 self.run_simple_file_inner(module_dict, scope, path)
87 })
88 }
89
90 fn run_simple_file_inner(
91 &self,
92 module_dict: &Py<PyDict>,
93 scope: Scope,
94 path: &str,
95 ) -> PyResult<()> {
96 let pyc = maybe_pyc_file(path);
97 if pyc {
98 set_main_loader(module_dict, path, "SourcelessFileLoader", self)?;
100 let loader = module_dict.get_item("__loader__", self)?;
101 let get_code = loader.get_attr("get_code", self)?;
102 let code_obj = get_code.call((identifier!(self, __main__).to_owned(),), self)?;
103 let code = code_obj
104 .downcast::<PyCode>()
105 .map_err(|_| self.new_runtime_error("Bad code object in .pyc file"))?;
106 self.run_code_obj(code, scope)?;
107 } else {
108 if path != "<stdin>" {
109 set_main_loader(module_dict, path, "SourceFileLoader", self)?;
110 }
111 match crate::host_env::fs::read(path) {
112 Ok(source_bytes) => {
113 if let Some(null_at) = source_bytes.iter().position(|&b| b == 0) {
114 return Err(null_byte_syntax_error(self, path, &source_bytes, null_at));
115 }
116 #[cfg(feature = "parser")]
117 let source = self.decode_source_bytes(&source_bytes, path, false)?;
119 #[cfg(not(feature = "parser"))]
120 let source = String::from_utf8(source_bytes)
121 .map_err(|err| self.new_os_error(err.to_string()))?;
122 let code_obj = self
123 .compile(&source, compiler::Mode::Exec, path)
124 .map_err(|err| err.into_pyexception(self, Some(&source)))?;
125 self.run_code_obj(code_obj, scope)?;
126 }
127 Err(err) => {
128 return Err(self.new_os_error(err.to_string()));
129 }
130 }
131 }
132 Ok(())
133 }
134
135 pub fn run_script(&self, scope: Scope, path: &str) -> PyResult<()> {
137 self.run_any_file(scope, path)
138 }
139 }
140
141 fn null_byte_syntax_error(
142 vm: &VirtualMachine,
143 path: &str,
144 source_bytes: &[u8],
145 null_at: usize,
146 ) -> PyBaseExceptionRef {
147 let before = &source_bytes[..null_at];
148 let lineno = before.iter().filter(|&&b| b == b'\n').count() + 1;
149 let line_start = before
150 .iter()
151 .rposition(|&b| b == b'\n')
152 .map_or(0, |i| i + 1);
153 let line = String::from_utf8_lossy(&source_bytes[line_start..null_at]);
154 let syntax_error = vm.new_exception_msg(
155 vm.ctx.exceptions.syntax_error.to_owned(),
156 "source code cannot contain null bytes".into(),
157 );
158 let obj = syntax_error.as_object();
159 obj.set_attr("filename", vm.ctx.new_str(path), vm).unwrap();
160 obj.set_attr("lineno", vm.ctx.new_int(lineno), vm).unwrap();
161 obj.set_attr("text", vm.ctx.new_str(format!("{line}\n")), vm)
162 .unwrap();
163 syntax_error
164 }
165
166 fn set_main_loader(
167 module_dict: &Py<PyDict>,
168 filename: &str,
169 loader_name: &str,
170 vm: &VirtualMachine,
171 ) -> PyResult<()> {
172 vm.import("importlib.machinery", 0)?;
173 let sys_modules = vm.sys_module.get_attr(identifier!(vm, modules), vm)?;
174 let machinery = sys_modules.get_item("importlib.machinery", vm)?;
175 let loader_name = vm.ctx.new_str(loader_name);
176 let loader_class = machinery.get_attr(&loader_name, vm)?;
177 let loader = loader_class.call((identifier!(vm, __main__).to_owned(), filename), vm)?;
178 module_dict.set_item("__loader__", loader, vm)?;
179 Ok(())
180 }
181
182 fn maybe_pyc_file(path: &str) -> bool {
188 if path.ends_with(".pyc") {
189 return true;
190 }
191 maybe_pyc_file_with_magic(path).unwrap_or(false)
192 }
193
194 fn maybe_pyc_file_with_magic(path: &str) -> std::io::Result<bool> {
195 let path_obj = std::path::Path::new(path);
196 if !path_obj.is_file() {
197 return Ok(false);
198 }
199
200 let mut file = crate::host_env::fs::open(path)?;
201 let mut buf = [0u8; 2];
202
203 use std::io::{Read, Seek, SeekFrom};
204 let n = file.read(&mut buf)?;
205 let _ = file.seek(SeekFrom::Start(0));
208 if n != 2 {
209 return Ok(false);
210 }
211
212 Ok(crate::import::check_pyc_magic_number_bytes(&buf))
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use crate::object::AsObject;
222 use rustpython_vm::Interpreter;
223
224 fn interpreter() -> Interpreter {
225 Interpreter::without_stdlib(Default::default())
226 }
227
228 #[test]
229 fn block_expr_return_const() {
230 interpreter().enter(|vm| {
231 let scope = vm.new_scope_with_builtins();
232 let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "1"));
233 let value = vm.unwrap_pyresult(value.try_int(vm));
234 let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm));
235 assert_eq!(value, 1);
236 })
237 }
238
239 #[test]
240 fn block_expr_return_nonconst() {
241 interpreter().enter(|vm| {
242 let scope = vm.new_scope_with_builtins();
243 vm.unwrap_pyresult(scope.globals.set_item("x", vm.new_pyobj(3), vm));
244 let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "2 + x"));
245 let value = vm.unwrap_pyresult(value.try_int(vm));
246 let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm));
247 assert_eq!(value, 5);
248 })
249 }
250
251 #[test]
252 fn block_expr_return_function_def() {
253 interpreter().enter(|vm| {
254 let scope = vm.new_scope_with_builtins();
255 let value =
256 vm.unwrap_pyresult(vm.run_block_expr(scope.clone(), "def f():\n return 7"));
257 vm.unwrap_pyresult(scope.globals.set_item("returned", value, vm));
258 let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "returned is f"));
259 assert!(value.is(&vm.ctx.true_value));
260 })
261 }
262
263 #[test]
264 fn block_expr_return_class_def() {
265 interpreter().enter(|vm| {
266 let scope = vm.new_scope_with_builtins();
267 let value =
268 vm.unwrap_pyresult(vm.run_block_expr(scope.clone(), "class C:\n value = 11"));
269 vm.unwrap_pyresult(scope.globals.set_item("returned", value, vm));
270 let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "returned is C"));
271 assert!(value.is(&vm.ctx.true_value));
272 })
273 }
274}