@@ -53,6 +53,8 @@ struct FrameState { |
53 | 53 | // We need 1 stack per frame |
54 | 54 | /// The main data frame of the stack machine |
55 | 55 | stack: BoxVec<Option<PyObjectRef>>, |
| 56 | + /// Cell and free variable references (cellvars + freevars). |
| 57 | + cells_frees: Box<[PyCellRef]>, |
56 | 58 | /// index of last instruction ran |
57 | 59 | #[cfg(feature = "threading")] |
58 | 60 | lasti: u32, |
@@ -93,7 +95,6 @@ pub struct Frame { |
93 | 95 | pub func_obj: Option<PyObjectRef>, |
94 | 96 |
|
95 | 97 | pub fastlocals: PyMutex<Box<[Option<PyObjectRef>]>>, |
96 | | - pub(crate) cells_frees: Box<[PyCellRef]>, |
97 | 98 | pub locals: ArgMapping, |
98 | 99 | pub globals: PyDictRef, |
99 | 100 | pub builtins: PyObjectRef, |
@@ -135,6 +136,7 @@ impl PyPayload for Frame { |
135 | 136 | unsafe impl Traverse for FrameState { |
136 | 137 | fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) { |
137 | 138 | self.stack.traverse(tracer_fn); |
| 139 | + self.cells_frees.traverse(tracer_fn); |
138 | 140 | } |
139 | 141 | } |
140 | 142 |
|
@@ -143,7 +145,6 @@ unsafe impl Traverse for Frame { |
143 | 145 | self.code.traverse(tracer_fn); |
144 | 146 | self.func_obj.traverse(tracer_fn); |
145 | 147 | self.fastlocals.traverse(tracer_fn); |
146 | | - self.cells_frees.traverse(tracer_fn); |
147 | 148 | self.locals.traverse(tracer_fn); |
148 | 149 | self.globals.traverse(tracer_fn); |
149 | 150 | self.builtins.traverse(tracer_fn); |
@@ -193,13 +194,13 @@ impl Frame { |
193 | 194 |
|
194 | 195 | let state = FrameState { |
195 | 196 | stack: BoxVec::new(code.max_stackdepth as usize), |
| 197 | + cells_frees, |
196 | 198 | #[cfg(feature = "threading")] |
197 | 199 | lasti: 0, |
198 | 200 | }; |
199 | 201 |
|
200 | 202 | Self { |
201 | 203 | fastlocals: PyMutex::new(fastlocals_vec.into_boxed_slice()), |
202 | | - cells_frees, |
203 | 204 | locals: scope.locals, |
204 | 205 | globals: scope.globals, |
205 | 206 | builtins, |
@@ -218,21 +219,39 @@ impl Frame { |
218 | 219 | } |
219 | 220 | } |
220 | 221 |
|
221 | | - /// Clear the evaluation stack. Used by frame.clear() to break reference cycles. |
222 | | - pub(crate) fn clear_value_stack(&self) { |
223 | | - self.state.lock().stack.clear(); |
| 222 | + /// Clear the evaluation stack and drop all cell/free variable references. |
| 223 | + pub(crate) fn clear_stack_and_cells(&self) { |
| 224 | + let mut state = self.state.lock(); |
| 225 | + state.stack.clear(); |
| 226 | + let _old = core::mem::take(&mut state.cells_frees); |
224 | 227 | } |
225 | 228 |
|
226 | 229 | /// Clear locals and stack after generator/coroutine close. |
227 | 230 | /// Releases references held by the frame, matching _PyFrame_ClearLocals. |
228 | 231 | pub(crate) fn clear_locals_and_stack(&self) { |
229 | | - self.state.lock().stack.clear(); |
| 232 | + self.clear_stack_and_cells(); |
230 | 233 | let mut fastlocals = self.fastlocals.lock(); |
231 | 234 | for slot in fastlocals.iter_mut() { |
232 | 235 | *slot = None; |
233 | 236 | } |
234 | 237 | } |
235 | 238 |
|
| 239 | + /// Get cell contents by cell index. Reads through fastlocals (no state lock needed). |
| 240 | + pub(crate) fn get_cell_contents(&self, cell_idx: usize) -> Option<PyObjectRef> { |
| 241 | + let nlocals = self.code.varnames.len(); |
| 242 | + let fastlocals = self.fastlocals.lock(); |
| 243 | + fastlocals |
| 244 | + .get(nlocals + cell_idx) |
| 245 | + .and_then(|slot| slot.as_ref()) |
| 246 | + .and_then(|obj| obj.downcast_ref::<PyCell>()) |
| 247 | + .and_then(|cell| cell.get()) |
| 248 | + } |
| 249 | + |
| 250 | + /// Set cell contents by cell index. Only safe to call before frame execution starts. |
| 251 | + pub(crate) fn set_cell_contents(&self, cell_idx: usize, value: Option<PyObjectRef>) { |
| 252 | + self.state.lock().cells_frees[cell_idx].set(value); |
| 253 | + } |
| 254 | + |
236 | 255 | /// Store a borrowed back-reference to the owning generator/coroutine. |
237 | 256 | /// The caller must ensure the generator outlives the frame. |
238 | 257 | pub fn set_generator(&self, generator: &PyObject) { |
@@ -306,23 +325,26 @@ impl Frame { |
306 | 325 | } |
307 | 326 | } |
308 | 327 | if !code.cellvars.is_empty() || !code.freevars.is_empty() { |
309 | | - let map_to_dict = |keys: &[&PyStrInterned], values: &[PyCellRef]| { |
310 | | - for (&k, v) in zip(keys, values) { |
311 | | - if let Some(value) = v.get() { |
312 | | - locals.mapping().ass_subscript(k, Some(value), vm)?; |
313 | | - } else { |
314 | | - match locals.mapping().ass_subscript(k, None, vm) { |
315 | | - Ok(()) => {} |
316 | | - Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {} |
317 | | - Err(e) => return Err(e), |
| 328 | + let state = self.state.lock(); |
| 329 | + if !state.cells_frees.is_empty() { |
| 330 | + let map_to_dict = |keys: &[&PyStrInterned], values: &[PyCellRef]| { |
| 331 | + for (&k, v) in zip(keys, values) { |
| 332 | + if let Some(value) = v.get() { |
| 333 | + locals.mapping().ass_subscript(k, Some(value), vm)?; |
| 334 | + } else { |
| 335 | + match locals.mapping().ass_subscript(k, None, vm) { |
| 336 | + Ok(()) => {} |
| 337 | + Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {} |
| 338 | + Err(e) => return Err(e), |
| 339 | + } |
318 | 340 | } |
319 | 341 | } |
| 342 | + Ok(()) |
| 343 | + }; |
| 344 | + map_to_dict(&code.cellvars, &state.cells_frees)?; |
| 345 | + if code.flags.contains(bytecode::CodeFlags::OPTIMIZED) { |
| 346 | + map_to_dict(&code.freevars, &state.cells_frees[code.cellvars.len()..])?; |
320 | 347 | } |
321 | | - Ok(()) |
322 | | - }; |
323 | | - map_to_dict(&code.cellvars, &self.cells_frees)?; |
324 | | - if code.flags.contains(bytecode::CodeFlags::OPTIMIZED) { |
325 | | - map_to_dict(&code.freevars, &self.cells_frees[code.cellvars.len()..])?; |
326 | 348 | } |
327 | 349 | } |
328 | 350 | Ok(locals.clone()) |
@@ -336,7 +358,6 @@ impl Py<Frame> { |
336 | 358 | let exec = ExecutingFrame { |
337 | 359 | code: &self.code, |
338 | 360 | fastlocals: &self.fastlocals, |
339 | | - cells_frees: &self.cells_frees, |
340 | 361 | locals: &self.locals, |
341 | 362 | globals: &self.globals, |
342 | 363 | builtins: &self.builtins, |
@@ -382,7 +403,6 @@ impl Py<Frame> { |
382 | 403 | let exec = ExecutingFrame { |
383 | 404 | code: &self.code, |
384 | 405 | fastlocals: &self.fastlocals, |
385 | | - cells_frees: &self.cells_frees, |
386 | 406 | locals: &self.locals, |
387 | 407 | globals: &self.globals, |
388 | 408 | builtins: &self.builtins, |
@@ -417,7 +437,6 @@ impl Py<Frame> { |
417 | 437 | struct ExecutingFrame<'a> { |
418 | 438 | code: &'a PyRef<PyCode>, |
419 | 439 | fastlocals: &'a PyMutex<Box<[Option<PyObjectRef>]>>, |
420 | | - cells_frees: &'a [PyCellRef], |
421 | 440 | locals: &'a ArgMapping, |
422 | 441 | globals: &'a PyDictRef, |
423 | 442 | builtins: &'a PyObjectRef, |
@@ -1021,7 +1040,7 @@ impl ExecutingFrame<'_> { |
1021 | 1040 | } |
1022 | 1041 | Instruction::DeleteAttr { idx } => self.delete_attr(vm, idx.get(arg)), |
1023 | 1042 | Instruction::DeleteDeref(i) => { |
1024 | | - self.cells_frees[i.get(arg) as usize].set(None); |
| 1043 | + self.state.cells_frees[i.get(arg) as usize].set(None); |
1025 | 1044 | Ok(None) |
1026 | 1045 | } |
1027 | 1046 | Instruction::DeleteFast(idx) => { |
@@ -1446,7 +1465,7 @@ impl ExecutingFrame<'_> { |
1446 | 1465 | }; |
1447 | 1466 | self.push_value(match value { |
1448 | 1467 | Some(v) => v, |
1449 | | - None => self.cells_frees[i] |
| 1468 | + None => self.state.cells_frees[i] |
1450 | 1469 | .get() |
1451 | 1470 | .ok_or_else(|| self.unbound_cell_exception(i, vm))?, |
1452 | 1471 | }); |
@@ -1503,7 +1522,7 @@ impl ExecutingFrame<'_> { |
1503 | 1522 | } |
1504 | 1523 | Instruction::LoadDeref(i) => { |
1505 | 1524 | let idx = i.get(arg) as usize; |
1506 | | - let x = self.cells_frees[idx] |
| 1525 | + let x = self.state.cells_frees[idx] |
1507 | 1526 | .get() |
1508 | 1527 | .ok_or_else(|| self.unbound_cell_exception(idx, vm))?; |
1509 | 1528 | self.push_value(x); |
@@ -2093,7 +2112,7 @@ impl ExecutingFrame<'_> { |
2093 | 2112 | Instruction::StoreAttr { idx } => self.store_attr(vm, idx.get(arg)), |
2094 | 2113 | Instruction::StoreDeref(i) => { |
2095 | 2114 | let value = self.pop_value(); |
2096 | | - self.cells_frees[i.get(arg) as usize].set(Some(value)); |
| 2115 | + self.state.cells_frees[i.get(arg) as usize].set(Some(value)); |
2097 | 2116 | Ok(None) |
2098 | 2117 | } |
2099 | 2118 | Instruction::StoreFast(idx) => { |
|