Vec from any iterable (dict yields keys, str yields one-char strs, bytes yields ints). `include_range = false` lets callers reject Range. */
(&mut self, o: Val, include_range: bool)
| 307 | |
| 308 | /* Vec<Val> from any iterable (dict yields keys, str yields one-char strs, bytes yields ints). `include_range = false` lets callers reject Range. */ |
| 309 | pub(in crate::modules::vm) fn extract_iter(&mut self, o: Val, include_range: bool) -> Result<Vec<Val>, VmErr> { |
| 310 | if !o.is_heap() { |
| 311 | return Err(self.not_iterable(o)); |
| 312 | } |
| 313 | // Snapshot the variant out so the &self borrow ends before any allocation. |
| 314 | let snapshot = match self.heap.get(o) { |
| 315 | HeapObj::List(v) => Some(v.borrow().clone()), |
| 316 | HeapObj::Tuple(v) => Some(v.clone()), |
| 317 | HeapObj::Set(v) => Some(v.borrow().iter().cloned().collect()), |
| 318 | HeapObj::FrozenSet(v) => Some(v.iter().cloned().collect()), |
| 319 | HeapObj::Range(s, e, st) if include_range => { |
| 320 | let (mut cur, end, step) = (*s, *e, *st); |
| 321 | // Materialised length is user-controlled; cap it against the heap budget. |
| 322 | let span = (end as i128 - cur as i128).unsigned_abs(); |
| 323 | let count = if step == 0 { 0 } else { span / (step as i128).unsigned_abs() }; |
| 324 | if count > self.heap.limit() as u128 { return Err(cold_heap()); } |
| 325 | let mut out = Vec::new(); |
| 326 | if step > 0 { |
| 327 | while cur < end { |
| 328 | out.push(range_int(&mut self.heap, cur)?); |
| 329 | match cur.checked_add(step) { Some(n) => cur = n, None => break } |
| 330 | } |
| 331 | } else { |
| 332 | while cur > end { |
| 333 | out.push(range_int(&mut self.heap, cur)?); |
| 334 | match cur.checked_add(step) { Some(n) => cur = n, None => break } |
| 335 | } |
| 336 | } |
| 337 | Some(out) |
| 338 | } |
| 339 | HeapObj::Dict(d) => Some(d.borrow().keys().collect()), |
| 340 | HeapObj::Bytes(b) => Some(b.iter().map(|&x| Val::int(x as i64)).collect()), |
| 341 | HeapObj::Str(_) => None, // handled below, needs heap allocation |
| 342 | _ => return Err(self.not_iterable(o)), |
| 343 | }; |
| 344 | if let Some(v) = snapshot { |
| 345 | // Cost scales with element count; charge it so repeated materialisation stays bounded. |
| 346 | self.charge_steps(v.len())?; |
| 347 | return Ok(v); |
| 348 | } |
| 349 | // Str path materialises one-char heap strings via the existing helper. |
| 350 | if let HeapObj::Str(s) = self.heap.get(o) { |
| 351 | let s = s.clone(); |
| 352 | return self.str_to_char_vals(&s); |
| 353 | } |
| 354 | unreachable!() |
| 355 | } |
| 356 | |
| 357 | /* Flatten any iterable to a fresh `Vec<Val>`, shared input path for iter/map/filter so all three accept the same set of sources. */ |
| 358 | pub(crate) fn iter_to_vec_general(&mut self, o: Val) -> Result<Vec<Val>, VmErr> { |
no test coverage detected