|
1 | 1 | #[cfg(feature = "flame")] |
2 | 2 | use crate::bytecode::InstructionMetadata; |
3 | 3 | use crate::{ |
4 | | - AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, PyStackRef, TryFromObject, |
5 | | - VirtualMachine, |
| 4 | + AsObject, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, PyStackRef, |
| 5 | + TryFromObject, VirtualMachine, |
6 | 6 | builtins::{ |
7 | 7 | PyBaseException, PyBaseExceptionRef, PyCode, PyCoroutine, PyDict, PyDictRef, PyGenerator, |
8 | 8 | PyInterpolation, PyList, PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback, |
@@ -447,7 +447,14 @@ impl Py<Frame> { |
447 | 447 | locals: &self.locals, |
448 | 448 | globals: &self.globals, |
449 | 449 | builtins: &self.builtins, |
450 | | - builtins_dict: self.builtins.downcast_ref_if_exact::<PyDict>(vm), |
| 450 | + builtins_dict: if self.globals.class().is(vm.ctx.types.dict_type) { |
| 451 | + self.builtins |
| 452 | + .downcast_ref_if_exact::<PyDict>(vm) |
| 453 | + // SAFETY: downcast_ref_if_exact already verified exact type |
| 454 | + .map(|d| unsafe { PyExact::ref_unchecked(d) }) |
| 455 | + } else { |
| 456 | + None |
| 457 | + }, |
451 | 458 | lasti: &self.lasti, |
452 | 459 | object: self, |
453 | 460 | state: &mut state, |
@@ -530,9 +537,11 @@ struct ExecutingFrame<'a> { |
530 | 537 | locals: &'a ArgMapping, |
531 | 538 | globals: &'a PyDictRef, |
532 | 539 | builtins: &'a PyObjectRef, |
533 | | - /// Cached downcast of builtins to PyDict. builtins never changes during |
534 | | - /// frame execution, so we avoid repeating the downcast on every LOAD_GLOBAL. |
535 | | - builtins_dict: Option<&'a Py<PyDict>>, |
| 540 | + /// Cached downcast of builtins to PyDict for fast LOAD_GLOBAL. |
| 541 | + /// Only set when both globals and builtins are exact dict types (not |
| 542 | + /// subclasses), so that `__missing__` / `__getitem__` overrides are |
| 543 | + /// not bypassed. |
| 544 | + builtins_dict: Option<&'a PyExact<PyDict>>, |
536 | 545 | object: &'a Py<Frame>, |
537 | 546 | lasti: &'a PyAtomic<u32>, |
538 | 547 | state: &'a mut FrameState, |
@@ -3028,8 +3037,10 @@ impl ExecutingFrame<'_> { |
3028 | 3037 | #[inline] |
3029 | 3038 | fn load_global_or_builtin(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult { |
3030 | 3039 | if let Some(builtins_dict) = self.builtins_dict { |
3031 | | - // Fast path: both globals (PyDictRef) and builtins are exact dicts |
3032 | | - self.globals |
| 3040 | + // Fast path: both globals and builtins are exact dicts |
| 3041 | + // SAFETY: builtins_dict is only set when globals is also exact dict |
| 3042 | + let globals_exact = unsafe { PyExact::ref_unchecked(self.globals.as_ref()) }; |
| 3043 | + globals_exact |
3033 | 3044 | .get_chain_exact(builtins_dict, name, vm)? |
3034 | 3045 | .ok_or_else(|| { |
3035 | 3046 | vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned()) |
@@ -3720,13 +3731,10 @@ impl ExecutingFrame<'_> { |
3720 | 3731 |
|
3721 | 3732 | // FOR_ITER_RANGE: bypass generic iterator protocol for range iterators |
3722 | 3733 | if let Some(range_iter) = top.downcast_ref_if_exact::<PyRangeIterator>(vm) { |
3723 | | - let index = range_iter.index.fetch_add(1); |
3724 | | - if index < range_iter.length { |
3725 | | - let value = range_iter.start + (index as isize) * range_iter.step; |
| 3734 | + if let Some(value) = range_iter.next_fast() { |
3726 | 3735 | self.push_value(vm.ctx.new_int(value).into()); |
3727 | 3736 | return Ok(true); |
3728 | 3737 | } |
3729 | | - // Exhausted |
3730 | 3738 | if vm.use_tracing.get() && !vm.is_none(&self.object.trace.lock()) { |
3731 | 3739 | let stop_exc = vm.new_stop_iteration(None); |
3732 | 3740 | self.fire_exception_trace(&stop_exc, vm)?; |
@@ -4085,17 +4093,17 @@ impl ExecutingFrame<'_> { |
4085 | 4093 | self.push_value(vm.ctx.new_bool(result).into()); |
4086 | 4094 | return Ok(None); |
4087 | 4095 | } |
4088 | | - // COMPARE_OP_FLOAT |
| 4096 | + // COMPARE_OP_FLOAT: leaf type, cannot recurse — skip rich_compare dispatch. |
| 4097 | + // Falls through on NaN (partial_cmp returns None) for correct != semantics. |
4089 | 4098 | if let (Some(a_f), Some(b_f)) = ( |
4090 | 4099 | a.downcast_ref_if_exact::<PyFloat>(vm), |
4091 | 4100 | b.downcast_ref_if_exact::<PyFloat>(vm), |
4092 | 4101 | ) { |
4093 | | - let result = a_f |
4094 | | - .to_f64() |
4095 | | - .partial_cmp(&b_f.to_f64()) |
4096 | | - .is_some_and(|ord| cmp_op.eval_ord(ord)); |
4097 | | - self.push_value(vm.ctx.new_bool(result).into()); |
4098 | | - return Ok(None); |
| 4102 | + if let Some(ord) = a_f.to_f64().partial_cmp(&b_f.to_f64()) { |
| 4103 | + let result = cmp_op.eval_ord(ord); |
| 4104 | + self.push_value(vm.ctx.new_bool(result).into()); |
| 4105 | + return Ok(None); |
| 4106 | + } |
4099 | 4107 | } |
4100 | 4108 |
|
4101 | 4109 | let value = a.rich_compare(b, cmp_op, vm)?; |
|
0 commit comments