@@ -75,13 +75,20 @@ impl VirtualMachine {
7575 // Consider to use enum to distinguish `path`
7676 // https://github.com/RustPython/RustPython/pull/6276#discussion_r2529849479
7777
78- // TODO: check .pyc here
79- let pyc = false ;
78+ let pyc = maybe_pyc_file ( path) ;
8079 if pyc {
81- todo ! ( "running pyc is not implemented yet" ) ;
80+ // pyc file execution
81+ set_main_loader ( & module_dict, path, "SourcelessFileLoader" , self ) ?;
82+ let loader = module_dict. get_item ( "__loader__" , self ) ?;
83+ let get_code = loader. get_attr ( "get_code" , self ) ?;
84+ let code_obj = get_code. call ( ( identifier ! ( self , __main__) . to_owned ( ) , ) , self ) ?;
85+ let code = code_obj
86+ . downcast :: < PyCode > ( )
87+ . map_err ( |_| self . new_runtime_error ( "Bad code object in .pyc file" . to_owned ( ) ) ) ?;
88+ self . run_code_obj ( code, scope) ?;
8289 } else {
8390 if path != "<stdin>" {
84- set_main_loader ( & module_dict, path, self ) ?;
91+ set_main_loader ( & module_dict, path, "SourceFileLoader" , self ) ?;
8592 }
8693 // TODO: replace to something equivalent to py_run_file
8794 match std:: fs:: read_to_string ( path) {
@@ -125,16 +132,55 @@ impl VirtualMachine {
125132 }
126133}
127134
128- fn set_main_loader ( module_dict : & PyDictRef , filename : & str , vm : & VirtualMachine ) -> PyResult < ( ) > {
135+ fn set_main_loader (
136+ module_dict : & PyDictRef ,
137+ filename : & str ,
138+ loader_name : & str ,
139+ vm : & VirtualMachine ,
140+ ) -> PyResult < ( ) > {
129141 vm. import ( "importlib.machinery" , 0 ) ?;
130142 let sys_modules = vm. sys_module . get_attr ( identifier ! ( vm, modules) , vm) ?;
131143 let machinery = sys_modules. get_item ( "importlib.machinery" , vm) ?;
132- let loader_class = machinery. get_attr ( "SourceFileLoader" , vm) ?;
144+ let loader_name = vm. ctx . new_str ( loader_name) ;
145+ let loader_class = machinery. get_attr ( & loader_name, vm) ?;
133146 let loader = loader_class. call ( ( identifier ! ( vm, __main__) . to_owned ( ) , filename) , vm) ?;
134147 module_dict. set_item ( "__loader__" , loader, vm) ?;
135148 Ok ( ( ) )
136149}
137150
151+ /// Check whether a file is maybe a pyc file.
152+ ///
153+ /// Detection is performed by:
154+ /// 1. Checking if the filename ends with ".pyc"
155+ /// 2. If not, reading the first 2 bytes and comparing with the magic number
156+ fn maybe_pyc_file ( path : & str ) -> bool {
157+ // 1. Check if filename ends with ".pyc"
158+ if path. ends_with ( ".pyc" ) {
159+ return true ;
160+ }
161+ let magic_number = crate :: version:: get_magic_number ( ) ;
162+ maybe_pyc_file_with_magic ( path, & magic_number) . unwrap_or ( false )
163+ }
164+
165+ fn maybe_pyc_file_with_magic ( path : & str , magic_number : & [ u8 ] ) -> std:: io:: Result < bool > {
166+ // 2. For non-.pyc extension, check magic number
167+ let path_obj = std:: path:: Path :: new ( path) ;
168+ if !path_obj. is_file ( ) {
169+ return Ok ( false ) ;
170+ }
171+
172+ let mut file = std:: fs:: File :: open ( path) ?;
173+ let mut buf = [ 0u8 ; 2 ] ;
174+
175+ use std:: io:: Read ;
176+ if file. read ( & mut buf) ? != 2 || magic_number. len ( ) < 2 {
177+ return Ok ( false ) ;
178+ }
179+
180+ // Compare with first 2 bytes of magic number (half magic)
181+ Ok ( buf == magic_number[ ..2 ] )
182+ }
183+
138184fn get_importer ( path : & str , vm : & VirtualMachine ) -> PyResult < Option < PyObjectRef > > {
139185 let path_importer_cache = vm. sys_module . get_attr ( "path_importer_cache" , vm) ?;
140186 let path_importer_cache = PyDictRef :: try_from_object ( vm, path_importer_cache) ?;
0 commit comments