@@ -72,7 +72,7 @@ pub struct Frame {
7272 pub ( crate ) cells_frees : Box < [ PyCellRef ] > ,
7373 pub locals : ArgMapping ,
7474 pub globals : PyDictRef ,
75- pub builtins : PyDictRef ,
75+ pub builtins : PyObjectRef ,
7676
7777 // on feature=threading, this is a duplicate of FrameState.lasti, but it's faster to do an
7878 // atomic store than it is to do a fetch_add, for every instruction executed
@@ -137,7 +137,7 @@ impl Frame {
137137 pub ( crate ) fn new (
138138 code : PyRef < PyCode > ,
139139 scope : Scope ,
140- builtins : PyDictRef ,
140+ builtins : PyObjectRef ,
141141 closure : & [ PyCellRef ] ,
142142 func_obj : Option < PyObjectRef > ,
143143 vm : & VirtualMachine ,
@@ -352,7 +352,7 @@ struct ExecutingFrame<'a> {
352352 cells_frees : & ' a [ PyCellRef ] ,
353353 locals : & ' a ArgMapping ,
354354 globals : & ' a PyDictRef ,
355- builtins : & ' a PyDictRef ,
355+ builtins : & ' a PyObjectRef ,
356356 object : & ' a Py < Frame > ,
357357 lasti : & ' a Lasti ,
358358 state : & ' a mut FrameState ,
@@ -1207,7 +1207,31 @@ impl ExecutingFrame<'_> {
12071207 Instruction :: LoadAttr { idx } => self . load_attr ( vm, idx. get ( arg) ) ,
12081208 Instruction :: LoadSuperAttr { arg : idx } => self . load_super_attr ( vm, idx. get ( arg) ) ,
12091209 Instruction :: LoadBuildClass => {
1210- self . push_value ( vm. builtins . get_attr ( identifier ! ( vm, __build_class__) , vm) ?) ;
1210+ let build_class =
1211+ if let Some ( builtins_dict) = self . builtins . downcast_ref :: < PyDict > ( ) {
1212+ builtins_dict
1213+ . get_item_opt ( identifier ! ( vm, __build_class__) , vm) ?
1214+ . ok_or_else ( || {
1215+ vm. new_name_error (
1216+ "__build_class__ not found" . to_owned ( ) ,
1217+ identifier ! ( vm, __build_class__) . to_owned ( ) ,
1218+ )
1219+ } ) ?
1220+ } else {
1221+ self . builtins
1222+ . get_item ( identifier ! ( vm, __build_class__) , vm)
1223+ . map_err ( |e| {
1224+ if e. fast_isinstance ( vm. ctx . exceptions . key_error ) {
1225+ vm. new_name_error (
1226+ "__build_class__ not found" . to_owned ( ) ,
1227+ identifier ! ( vm, __build_class__) . to_owned ( ) ,
1228+ )
1229+ } else {
1230+ e
1231+ }
1232+ } ) ?
1233+ } ;
1234+ self . push_value ( build_class) ;
12111235 Ok ( None )
12121236 }
12131237 Instruction :: LoadLocals => {
@@ -2124,11 +2148,26 @@ impl ExecutingFrame<'_> {
21242148
21252149 #[ inline]
21262150 fn load_global_or_builtin ( & self , name : & Py < PyStr > , vm : & VirtualMachine ) -> PyResult {
2127- self . globals
2128- . get_chain ( self . builtins , name, vm) ?
2129- . ok_or_else ( || {
2130- vm. new_name_error ( format ! ( "name '{name}' is not defined" ) , name. to_owned ( ) )
2151+ if let Some ( builtins_dict) = self . builtins . downcast_ref :: < PyDict > ( ) {
2152+ // Fast path: builtins is a dict
2153+ self . globals
2154+ . get_chain ( builtins_dict, name, vm) ?
2155+ . ok_or_else ( || {
2156+ vm. new_name_error ( format ! ( "name '{name}' is not defined" ) , name. to_owned ( ) )
2157+ } )
2158+ } else {
2159+ // Slow path: builtins is not a dict, use generic __getitem__
2160+ if let Some ( value) = self . globals . get_item_opt ( name, vm) ? {
2161+ return Ok ( value) ;
2162+ }
2163+ self . builtins . get_item ( name, vm) . map_err ( |e| {
2164+ if e. fast_isinstance ( vm. ctx . exceptions . key_error ) {
2165+ vm. new_name_error ( format ! ( "name '{name}' is not defined" ) , name. to_owned ( ) )
2166+ } else {
2167+ e
2168+ }
21312169 } )
2170+ }
21322171 }
21332172
21342173 #[ cfg_attr( feature = "flame-it" , flame( "Frame" ) ) ]
0 commit comments