1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4use indexmap::IndexMap;
5use parking_lot::{Mutex, RwLock};
6
7use crate::ast::PerlTypeName;
8use crate::error::PerlError;
9use crate::value::PerlValue;
10
11#[derive(Debug, Clone)]
13pub struct AtomicArray(pub Arc<Mutex<Vec<PerlValue>>>);
14
15#[derive(Debug, Clone)]
17pub struct AtomicHash(pub Arc<Mutex<IndexMap<String, PerlValue>>>);
18
19type ScopeCaptureWithAtomics = (
20 Vec<(String, PerlValue)>,
21 Vec<(String, AtomicArray)>,
22 Vec<(String, AtomicHash)>,
23);
24
25#[inline]
29fn capture_skip_bootstrap_array(name: &str) -> bool {
30 matches!(
31 name,
32 "INC" | "ARGV" | "_" | "-" | "+" | "^CAPTURE" | "^CAPTURE_ALL"
33 )
34}
35
36#[inline]
38fn capture_skip_bootstrap_hash(name: &str) -> bool {
39 matches!(name, "INC" | "ENV" | "SIG" | "^HOOK")
40}
41
42#[derive(Clone, Debug)]
44enum LocalRestore {
45 Scalar(String, PerlValue),
46 Array(String, Vec<PerlValue>),
47 Hash(String, IndexMap<String, PerlValue>),
48 HashElement(String, String, Option<PerlValue>),
50 ArrayElement(String, i64, PerlValue),
52}
53
54#[derive(Debug, Clone)]
59struct Frame {
60 scalars: Vec<(String, PerlValue)>,
61 arrays: Vec<(String, Vec<PerlValue>)>,
62 sub_underscore: Option<Vec<PerlValue>>,
65 hashes: Vec<(String, IndexMap<String, PerlValue>)>,
66 scalar_slots: Vec<PerlValue>,
70 scalar_slot_names: Vec<Option<String>>,
73 local_restores: Vec<LocalRestore>,
75 frozen_scalars: HashSet<String>,
77 frozen_arrays: HashSet<String>,
78 frozen_hashes: HashSet<String>,
79 typed_scalars: HashMap<String, PerlTypeName>,
81 atomic_arrays: Vec<(String, AtomicArray)>,
83 atomic_hashes: Vec<(String, AtomicHash)>,
85 defers: Vec<PerlValue>,
87}
88
89impl Frame {
90 #[inline]
93 fn clear_all_bindings(&mut self) {
94 self.scalars.clear();
95 self.arrays.clear();
96 self.sub_underscore = None;
97 self.hashes.clear();
98 self.scalar_slots.clear();
99 self.scalar_slot_names.clear();
100 self.local_restores.clear();
101 self.frozen_scalars.clear();
102 self.frozen_arrays.clear();
103 self.frozen_hashes.clear();
104 self.typed_scalars.clear();
105 self.atomic_arrays.clear();
106 self.defers.clear();
107 self.atomic_hashes.clear();
108 }
109
110 #[inline]
114 fn owns_scalar_slot_index(&self, idx: usize) -> bool {
115 self.scalar_slot_names.get(idx).is_some_and(|n| n.is_some())
116 }
117
118 #[inline]
119 fn new() -> Self {
120 Self {
121 scalars: Vec::new(),
122 arrays: Vec::new(),
123 sub_underscore: None,
124 hashes: Vec::new(),
125 scalar_slots: Vec::new(),
126 scalar_slot_names: Vec::new(),
127 frozen_scalars: HashSet::new(),
128 frozen_arrays: HashSet::new(),
129 frozen_hashes: HashSet::new(),
130 typed_scalars: HashMap::new(),
131 atomic_arrays: Vec::new(),
132 atomic_hashes: Vec::new(),
133 local_restores: Vec::new(),
134 defers: Vec::new(),
135 }
136 }
137
138 #[inline]
139 fn get_scalar(&self, name: &str) -> Option<&PerlValue> {
140 if let Some(v) = self.get_scalar_from_slot(name) {
141 return Some(v);
142 }
143 self.scalars.iter().find(|(k, _)| k == name).map(|(_, v)| v)
144 }
145
146 #[inline]
149 fn get_scalar_from_slot(&self, name: &str) -> Option<&PerlValue> {
150 for (i, sn) in self.scalar_slot_names.iter().enumerate() {
151 if let Some(ref n) = sn {
152 if n == name {
153 return self.scalar_slots.get(i);
154 }
155 }
156 }
157 None
158 }
159
160 #[inline]
161 fn has_scalar(&self, name: &str) -> bool {
162 if self
163 .scalar_slot_names
164 .iter()
165 .any(|sn| sn.as_deref() == Some(name))
166 {
167 return true;
168 }
169 self.scalars.iter().any(|(k, _)| k == name)
170 }
171
172 #[inline]
173 fn set_scalar(&mut self, name: &str, val: PerlValue) {
174 for (i, sn) in self.scalar_slot_names.iter().enumerate() {
175 if let Some(ref n) = sn {
176 if n == name {
177 if i < self.scalar_slots.len() {
178 self.scalar_slots[i] = val;
179 }
180 return;
181 }
182 }
183 }
184 if let Some(entry) = self.scalars.iter_mut().find(|(k, _)| k == name) {
185 entry.1 = val;
186 } else {
187 self.scalars.push((name.to_string(), val));
188 }
189 }
190
191 #[inline]
192 fn get_array(&self, name: &str) -> Option<&Vec<PerlValue>> {
193 if name == "_" {
194 if let Some(ref v) = self.sub_underscore {
195 return Some(v);
196 }
197 }
198 self.arrays.iter().find(|(k, _)| k == name).map(|(_, v)| v)
199 }
200
201 #[inline]
202 fn has_array(&self, name: &str) -> bool {
203 if name == "_" && self.sub_underscore.is_some() {
204 return true;
205 }
206 self.arrays.iter().any(|(k, _)| k == name)
207 }
208
209 #[inline]
210 fn get_array_mut(&mut self, name: &str) -> Option<&mut Vec<PerlValue>> {
211 if name == "_" {
212 return self.sub_underscore.as_mut();
213 }
214 self.arrays
215 .iter_mut()
216 .find(|(k, _)| k == name)
217 .map(|(_, v)| v)
218 }
219
220 #[inline]
221 fn set_array(&mut self, name: &str, val: Vec<PerlValue>) {
222 if name == "_" {
223 if let Some(pos) = self.arrays.iter().position(|(k, _)| k == name) {
224 self.arrays.swap_remove(pos);
225 }
226 self.sub_underscore = Some(val);
227 return;
228 }
229 if let Some(entry) = self.arrays.iter_mut().find(|(k, _)| k == name) {
230 entry.1 = val;
231 } else {
232 self.arrays.push((name.to_string(), val));
233 }
234 }
235
236 #[inline]
237 fn get_hash(&self, name: &str) -> Option<&IndexMap<String, PerlValue>> {
238 self.hashes.iter().find(|(k, _)| k == name).map(|(_, v)| v)
239 }
240
241 #[inline]
242 fn has_hash(&self, name: &str) -> bool {
243 self.hashes.iter().any(|(k, _)| k == name)
244 }
245
246 #[inline]
247 fn get_hash_mut(&mut self, name: &str) -> Option<&mut IndexMap<String, PerlValue>> {
248 self.hashes
249 .iter_mut()
250 .find(|(k, _)| k == name)
251 .map(|(_, v)| v)
252 }
253
254 #[inline]
255 fn set_hash(&mut self, name: &str, val: IndexMap<String, PerlValue>) {
256 if let Some(entry) = self.hashes.iter_mut().find(|(k, _)| k == name) {
257 entry.1 = val;
258 } else {
259 self.hashes.push((name.to_string(), val));
260 }
261 }
262}
263
264#[derive(Debug, Clone)]
267pub struct Scope {
268 frames: Vec<Frame>,
269 frame_pool: Vec<Frame>,
271 parallel_guard: bool,
276}
277
278impl Default for Scope {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284impl Scope {
285 pub fn new() -> Self {
286 let mut s = Self {
287 frames: Vec::with_capacity(32),
288 frame_pool: Vec::with_capacity(32),
289 parallel_guard: false,
290 };
291 s.frames.push(Frame::new());
292 s
293 }
294
295 #[inline]
297 pub fn set_parallel_guard(&mut self, enabled: bool) {
298 self.parallel_guard = enabled;
299 }
300
301 #[inline]
302 pub fn parallel_guard(&self) -> bool {
303 self.parallel_guard
304 }
305
306 #[inline]
307 fn parallel_skip_special_name(name: &str) -> bool {
308 name.contains("::")
309 }
310
311 #[inline]
313 fn parallel_allowed_topic_scalar(name: &str) -> bool {
314 matches!(name, "_" | "a" | "b")
315 }
316
317 #[inline]
319 fn parallel_allowed_internal_array(name: &str) -> bool {
320 matches!(name, "-" | "+" | "^CAPTURE" | "^CAPTURE_ALL")
321 }
322
323 #[inline]
325 fn parallel_allowed_internal_hash(name: &str) -> bool {
326 matches!(name, "+" | "-" | "ENV" | "INC")
327 }
328
329 fn check_parallel_scalar_write(&self, name: &str) -> Result<(), PerlError> {
330 if !self.parallel_guard || Self::parallel_skip_special_name(name) {
331 return Ok(());
332 }
333 if Self::parallel_allowed_topic_scalar(name) {
334 return Ok(());
335 }
336 if crate::special_vars::is_regex_match_scalar_name(name) {
337 return Ok(());
338 }
339 let inner = self.frames.len().saturating_sub(1);
340 for (i, frame) in self.frames.iter().enumerate().rev() {
341 if frame.has_scalar(name) {
342 if let Some(v) = frame.get_scalar(name) {
343 if v.as_atomic_arc().is_some() {
344 return Ok(());
345 }
346 }
347 if i != inner {
348 return Err(PerlError::runtime(
349 format!(
350 "cannot assign to captured non-mysync variable `${}` in a parallel block",
351 name
352 ),
353 0,
354 ));
355 }
356 return Ok(());
357 }
358 }
359 Err(PerlError::runtime(
360 format!(
361 "cannot assign to undeclared variable `${}` in a parallel block",
362 name
363 ),
364 0,
365 ))
366 }
367
368 #[inline]
369 pub fn depth(&self) -> usize {
370 self.frames.len()
371 }
372
373 #[inline]
376 pub fn pop_to_depth(&mut self, target_depth: usize) {
377 while self.frames.len() > target_depth && self.frames.len() > 1 {
378 self.pop_frame();
379 }
380 }
381
382 #[inline]
383 pub fn push_frame(&mut self) {
384 if let Some(mut frame) = self.frame_pool.pop() {
385 frame.clear_all_bindings();
386 self.frames.push(frame);
387 } else {
388 self.frames.push(Frame::new());
389 }
390 }
391
392 #[inline]
397 pub fn get_scalar_slot(&self, slot: u8) -> PerlValue {
398 let idx = slot as usize;
399 for frame in self.frames.iter().rev() {
400 if idx < frame.scalar_slots.len() && frame.owns_scalar_slot_index(idx) {
401 return frame.scalar_slots[idx].clone();
402 }
403 }
404 PerlValue::UNDEF
405 }
406
407 #[inline]
409 pub fn set_scalar_slot(&mut self, slot: u8, val: PerlValue) {
410 let idx = slot as usize;
411 let len = self.frames.len();
412 for i in (0..len).rev() {
413 if idx < self.frames[i].scalar_slots.len() && self.frames[i].owns_scalar_slot_index(idx)
414 {
415 self.frames[i].scalar_slots[idx] = val;
416 return;
417 }
418 }
419 let top = self.frames.last_mut().unwrap();
420 top.scalar_slots.resize(idx + 1, PerlValue::UNDEF);
421 if idx >= top.scalar_slot_names.len() {
422 top.scalar_slot_names.resize(idx + 1, None);
423 }
424 top.scalar_slot_names[idx] = Some(String::new());
425 top.scalar_slots[idx] = val;
426 }
427
428 #[inline]
432 pub fn set_scalar_slot_checked(
433 &mut self,
434 slot: u8,
435 val: PerlValue,
436 slot_name: Option<&str>,
437 ) -> Result<(), PerlError> {
438 if self.parallel_guard {
439 let idx = slot as usize;
440 let len = self.frames.len();
441 let top_has = idx < self.frames[len - 1].scalar_slots.len()
442 && self.frames[len - 1].owns_scalar_slot_index(idx);
443 if !top_has {
444 let name_owned: String = {
445 let mut found = String::new();
446 for i in (0..len).rev() {
447 if let Some(Some(n)) = self.frames[i].scalar_slot_names.get(idx) {
448 found = n.clone();
449 break;
450 }
451 }
452 if found.is_empty() {
453 if let Some(sn) = slot_name {
454 found = sn.to_string();
455 }
456 }
457 found
458 };
459 let name = name_owned.as_str();
460 if !name.is_empty() && !Self::parallel_allowed_topic_scalar(name) {
461 let inner = len.saturating_sub(1);
462 for (fi, frame) in self.frames.iter().enumerate().rev() {
463 if frame.has_scalar(name)
464 || (idx < frame.scalar_slots.len() && frame.owns_scalar_slot_index(idx))
465 {
466 if fi != inner {
467 return Err(PerlError::runtime(
468 format!(
469 "cannot assign to captured outer lexical `${}` inside a parallel block (use `mysync`)",
470 name
471 ),
472 0,
473 ));
474 }
475 break;
476 }
477 }
478 }
479 }
480 }
481 self.set_scalar_slot(slot, val);
482 Ok(())
483 }
484
485 #[inline]
489 pub fn declare_scalar_slot(&mut self, slot: u8, val: PerlValue, name: Option<&str>) {
490 let idx = slot as usize;
491 let frame = self.frames.last_mut().unwrap();
492 if idx >= frame.scalar_slots.len() {
493 frame.scalar_slots.resize(idx + 1, PerlValue::UNDEF);
494 }
495 frame.scalar_slots[idx] = val;
496 if idx >= frame.scalar_slot_names.len() {
497 frame.scalar_slot_names.resize(idx + 1, None);
498 }
499 match name {
500 Some(n) => frame.scalar_slot_names[idx] = Some(n.to_string()),
501 None => frame.scalar_slot_names[idx] = Some(String::new()),
503 }
504 }
505
506 #[inline]
517 pub fn scalar_slot_concat_repeat_inplace(&mut self, slot: u8, rhs: &str, n: usize) -> bool {
518 let idx = slot as usize;
519 let len = self.frames.len();
520 let fi = {
521 let mut found = len - 1;
522 if idx >= self.frames[found].scalar_slots.len()
523 || !self.frames[found].owns_scalar_slot_index(idx)
524 {
525 for i in (0..len - 1).rev() {
526 if idx < self.frames[i].scalar_slots.len()
527 && self.frames[i].owns_scalar_slot_index(idx)
528 {
529 found = i;
530 break;
531 }
532 }
533 }
534 found
535 };
536 let frame = &mut self.frames[fi];
537 if idx >= frame.scalar_slots.len() {
538 frame.scalar_slots.resize(idx + 1, PerlValue::UNDEF);
539 }
540 frame.scalar_slots[idx].try_concat_repeat_inplace(rhs, n)
541 }
542
543 #[inline]
548 pub fn scalar_slot_concat_repeat_slow(&mut self, slot: u8, rhs: &str, n: usize) {
549 let pv = PerlValue::string(rhs.to_owned());
550 for _ in 0..n {
551 let _ = self.scalar_slot_concat_inplace(slot, &pv);
552 }
553 }
554
555 #[inline]
556 pub fn scalar_slot_concat_inplace(&mut self, slot: u8, rhs: &PerlValue) -> PerlValue {
557 let idx = slot as usize;
558 let len = self.frames.len();
559 let fi = {
560 let mut found = len - 1;
561 if idx >= self.frames[found].scalar_slots.len()
562 || !self.frames[found].owns_scalar_slot_index(idx)
563 {
564 for i in (0..len - 1).rev() {
565 if idx < self.frames[i].scalar_slots.len()
566 && self.frames[i].owns_scalar_slot_index(idx)
567 {
568 found = i;
569 break;
570 }
571 }
572 }
573 found
574 };
575 let frame = &mut self.frames[fi];
576 if idx >= frame.scalar_slots.len() {
577 frame.scalar_slots.resize(idx + 1, PerlValue::UNDEF);
578 }
579 if frame.scalar_slots[idx].try_concat_append_inplace(rhs) {
587 return frame.scalar_slots[idx].shallow_clone();
588 }
589 let new_val = std::mem::replace(&mut frame.scalar_slots[idx], PerlValue::UNDEF)
590 .concat_append_owned(rhs);
591 let handle = new_val.shallow_clone();
592 frame.scalar_slots[idx] = new_val;
593 handle
594 }
595
596 #[inline]
597 pub(crate) fn can_pop_frame(&self) -> bool {
598 self.frames.len() > 1
599 }
600
601 #[inline]
602 pub fn pop_frame(&mut self) {
603 if self.frames.len() > 1 {
604 let mut frame = self.frames.pop().expect("pop_frame");
605 let saved_guard = self.parallel_guard;
608 self.parallel_guard = false;
609 for entry in frame.local_restores.drain(..).rev() {
610 match entry {
611 LocalRestore::Scalar(name, old) => {
612 let _ = self.set_scalar(&name, old);
613 }
614 LocalRestore::Array(name, old) => {
615 let _ = self.set_array(&name, old);
616 }
617 LocalRestore::Hash(name, old) => {
618 let _ = self.set_hash(&name, old);
619 }
620 LocalRestore::HashElement(name, key, old) => match old {
621 Some(v) => {
622 let _ = self.set_hash_element(&name, &key, v);
623 }
624 None => {
625 let _ = self.delete_hash_element(&name, &key);
626 }
627 },
628 LocalRestore::ArrayElement(name, index, old) => {
629 let _ = self.set_array_element(&name, index, old);
630 }
631 }
632 }
633 self.parallel_guard = saved_guard;
634 frame.clear_all_bindings();
635 if self.frame_pool.len() < 64 {
637 self.frame_pool.push(frame);
638 }
639 }
640 }
641
642 pub fn local_set_scalar(&mut self, name: &str, val: PerlValue) -> Result<(), PerlError> {
644 let old = self.get_scalar(name);
645 if let Some(frame) = self.frames.last_mut() {
646 frame
647 .local_restores
648 .push(LocalRestore::Scalar(name.to_string(), old));
649 }
650 self.set_scalar(name, val)
651 }
652
653 pub fn local_set_array(&mut self, name: &str, val: Vec<PerlValue>) -> Result<(), PerlError> {
655 if self.find_atomic_array(name).is_some() {
656 return Err(PerlError::runtime(
657 "local cannot be used on mysync arrays",
658 0,
659 ));
660 }
661 let old = self.get_array(name);
662 if let Some(frame) = self.frames.last_mut() {
663 frame
664 .local_restores
665 .push(LocalRestore::Array(name.to_string(), old));
666 }
667 self.set_array(name, val)?;
668 Ok(())
669 }
670
671 pub fn local_set_hash(
673 &mut self,
674 name: &str,
675 val: IndexMap<String, PerlValue>,
676 ) -> Result<(), PerlError> {
677 if self.find_atomic_hash(name).is_some() {
678 return Err(PerlError::runtime(
679 "local cannot be used on mysync hashes",
680 0,
681 ));
682 }
683 let old = self.get_hash(name);
684 if let Some(frame) = self.frames.last_mut() {
685 frame
686 .local_restores
687 .push(LocalRestore::Hash(name.to_string(), old));
688 }
689 self.set_hash(name, val)?;
690 Ok(())
691 }
692
693 pub fn local_set_hash_element(
695 &mut self,
696 name: &str,
697 key: &str,
698 val: PerlValue,
699 ) -> Result<(), PerlError> {
700 if self.find_atomic_hash(name).is_some() {
701 return Err(PerlError::runtime(
702 "local cannot be used on mysync hash elements",
703 0,
704 ));
705 }
706 let old = if self.exists_hash_element(name, key) {
707 Some(self.get_hash_element(name, key))
708 } else {
709 None
710 };
711 if let Some(frame) = self.frames.last_mut() {
712 frame.local_restores.push(LocalRestore::HashElement(
713 name.to_string(),
714 key.to_string(),
715 old,
716 ));
717 }
718 self.set_hash_element(name, key, val)?;
719 Ok(())
720 }
721
722 pub fn local_set_array_element(
725 &mut self,
726 name: &str,
727 index: i64,
728 val: PerlValue,
729 ) -> Result<(), PerlError> {
730 if self.find_atomic_array(name).is_some() {
731 return Err(PerlError::runtime(
732 "local cannot be used on mysync array elements",
733 0,
734 ));
735 }
736 let old = self.get_array_element(name, index);
737 if let Some(frame) = self.frames.last_mut() {
738 frame
739 .local_restores
740 .push(LocalRestore::ArrayElement(name.to_string(), index, old));
741 }
742 self.set_array_element(name, index, val)?;
743 Ok(())
744 }
745
746 #[inline]
749 pub fn declare_scalar(&mut self, name: &str, val: PerlValue) {
750 let _ = self.declare_scalar_frozen(name, val, false, None);
751 }
752
753 pub fn declare_scalar_frozen(
756 &mut self,
757 name: &str,
758 val: PerlValue,
759 frozen: bool,
760 ty: Option<PerlTypeName>,
761 ) -> Result<(), PerlError> {
762 if let Some(ref t) = ty {
763 t.check_value(&val)
764 .map_err(|msg| PerlError::type_error(format!("`${}`: {}", name, msg), 0))?;
765 }
766 if let Some(frame) = self.frames.last_mut() {
767 frame.set_scalar(name, val);
768 if frozen {
769 frame.frozen_scalars.insert(name.to_string());
770 }
771 if let Some(t) = ty {
772 frame.typed_scalars.insert(name.to_string(), t);
773 }
774 }
775 Ok(())
776 }
777
778 pub fn is_scalar_frozen(&self, name: &str) -> bool {
780 for frame in self.frames.iter().rev() {
781 if frame.has_scalar(name) {
782 return frame.frozen_scalars.contains(name);
783 }
784 }
785 false
786 }
787
788 pub fn is_array_frozen(&self, name: &str) -> bool {
790 for frame in self.frames.iter().rev() {
791 if frame.has_array(name) {
792 return frame.frozen_arrays.contains(name);
793 }
794 }
795 false
796 }
797
798 pub fn is_hash_frozen(&self, name: &str) -> bool {
800 for frame in self.frames.iter().rev() {
801 if frame.has_hash(name) {
802 return frame.frozen_hashes.contains(name);
803 }
804 }
805 false
806 }
807
808 pub fn check_frozen(&self, sigil: &str, name: &str) -> Option<&'static str> {
810 match sigil {
811 "$" => {
812 if self.is_scalar_frozen(name) {
813 Some("scalar")
814 } else {
815 None
816 }
817 }
818 "@" => {
819 if self.is_array_frozen(name) {
820 Some("array")
821 } else {
822 None
823 }
824 }
825 "%" => {
826 if self.is_hash_frozen(name) {
827 Some("hash")
828 } else {
829 None
830 }
831 }
832 _ => None,
833 }
834 }
835
836 #[inline]
837 pub fn get_scalar(&self, name: &str) -> PerlValue {
838 for frame in self.frames.iter().rev() {
839 if let Some(val) = frame.get_scalar(name) {
840 if let Some(arc) = val.as_atomic_arc() {
842 return arc.lock().clone();
843 }
844 if let Some(arc) = val.as_scalar_ref() {
846 return arc.read().clone();
847 }
848 return val.clone();
849 }
850 }
851 PerlValue::UNDEF
852 }
853
854 #[inline]
856 pub fn scalar_binding_exists(&self, name: &str) -> bool {
857 for frame in self.frames.iter().rev() {
858 if frame.has_scalar(name) {
859 return true;
860 }
861 }
862 false
863 }
864
865 pub fn all_scalar_names(&self) -> Vec<String> {
867 let mut names = Vec::new();
868 for frame in &self.frames {
869 for (name, _) in &frame.scalars {
870 if !names.contains(name) {
871 names.push(name.clone());
872 }
873 }
874 for name in frame.scalar_slot_names.iter().flatten() {
875 if !names.contains(name) {
876 names.push(name.clone());
877 }
878 }
879 }
880 names
881 }
882
883 #[inline]
885 pub fn array_binding_exists(&self, name: &str) -> bool {
886 if self.find_atomic_array(name).is_some() {
887 return true;
888 }
889 for frame in self.frames.iter().rev() {
890 if frame.has_array(name) {
891 return true;
892 }
893 }
894 false
895 }
896
897 #[inline]
899 pub fn hash_binding_exists(&self, name: &str) -> bool {
900 if self.find_atomic_hash(name).is_some() {
901 return true;
902 }
903 for frame in self.frames.iter().rev() {
904 if frame.has_hash(name) {
905 return true;
906 }
907 }
908 false
909 }
910
911 #[inline]
914 pub fn get_scalar_raw(&self, name: &str) -> PerlValue {
915 for frame in self.frames.iter().rev() {
916 if let Some(val) = frame.get_scalar(name) {
917 return val.clone();
918 }
919 }
920 PerlValue::UNDEF
921 }
922
923 pub fn atomic_mutate(
927 &mut self,
928 name: &str,
929 f: impl FnOnce(&PerlValue) -> PerlValue,
930 ) -> PerlValue {
931 for frame in self.frames.iter().rev() {
932 if let Some(v) = frame.get_scalar(name) {
933 if let Some(arc) = v.as_atomic_arc() {
934 let mut guard = arc.lock();
935 let old = guard.clone();
936 let new_val = f(&guard);
937 *guard = new_val.clone();
938 crate::parallel_trace::emit_scalar_mutation(name, &old, &new_val);
939 return new_val;
940 }
941 }
942 }
943 let old = self.get_scalar(name);
945 let new_val = f(&old);
946 let _ = self.set_scalar(name, new_val.clone());
947 new_val
948 }
949
950 pub fn atomic_mutate_post(
952 &mut self,
953 name: &str,
954 f: impl FnOnce(&PerlValue) -> PerlValue,
955 ) -> PerlValue {
956 for frame in self.frames.iter().rev() {
957 if let Some(v) = frame.get_scalar(name) {
958 if let Some(arc) = v.as_atomic_arc() {
959 let mut guard = arc.lock();
960 let old = guard.clone();
961 let new_val = f(&old);
962 *guard = new_val.clone();
963 crate::parallel_trace::emit_scalar_mutation(name, &old, &new_val);
964 return old;
965 }
966 }
967 }
968 let old = self.get_scalar(name);
970 let _ = self.set_scalar(name, f(&old));
971 old
972 }
973
974 #[inline]
981 pub fn scalar_concat_inplace(
982 &mut self,
983 name: &str,
984 rhs: &PerlValue,
985 ) -> Result<PerlValue, PerlError> {
986 self.check_parallel_scalar_write(name)?;
987 for frame in self.frames.iter_mut().rev() {
988 if let Some(entry) = frame.scalars.iter_mut().find(|(k, _)| k == name) {
989 if let Some(atomic_arc) = entry.1.as_atomic_arc() {
992 let mut guard = atomic_arc.lock();
993 let inner = std::mem::replace(&mut *guard, PerlValue::UNDEF);
994 let new_val = inner.concat_append_owned(rhs);
995 *guard = new_val.shallow_clone();
996 return Ok(new_val);
997 }
998 if entry.1.try_concat_append_inplace(rhs) {
1001 return Ok(entry.1.shallow_clone());
1002 }
1003 let new_val =
1006 std::mem::replace(&mut entry.1, PerlValue::UNDEF).concat_append_owned(rhs);
1007 entry.1 = new_val.shallow_clone();
1008 return Ok(new_val);
1009 }
1010 }
1011 let val = PerlValue::UNDEF.concat_append_owned(rhs);
1013 self.frames[0].set_scalar(name, val.shallow_clone());
1014 Ok(val)
1015 }
1016
1017 #[inline]
1018 pub fn set_scalar(&mut self, name: &str, val: PerlValue) -> Result<(), PerlError> {
1019 self.check_parallel_scalar_write(name)?;
1020 for frame in self.frames.iter_mut().rev() {
1021 if let Some(v) = frame.get_scalar(name) {
1023 if let Some(arc) = v.as_atomic_arc() {
1024 let mut guard = arc.lock();
1025 let old = guard.clone();
1026 *guard = val.clone();
1027 crate::parallel_trace::emit_scalar_mutation(name, &old, &val);
1028 return Ok(());
1029 }
1030 if let Some(arc) = v.as_scalar_ref() {
1032 *arc.write() = val;
1033 return Ok(());
1034 }
1035 }
1036 if frame.has_scalar(name) {
1037 if let Some(ty) = frame.typed_scalars.get(name) {
1038 ty.check_value(&val)
1039 .map_err(|msg| PerlError::type_error(format!("`${}`: {}", name, msg), 0))?;
1040 }
1041 frame.set_scalar(name, val);
1042 return Ok(());
1043 }
1044 }
1045 self.frames[0].set_scalar(name, val);
1046 Ok(())
1047 }
1048
1049 #[inline]
1056 pub fn set_topic(&mut self, val: PerlValue) {
1057 let old_3lt = self.get_scalar("_<<<");
1061 let old_2lt = self.get_scalar("_<<");
1062 let old_1lt = self.get_scalar("_<");
1063 let old_topic = self.get_scalar("_");
1064
1065 self.declare_scalar("_", val.clone());
1067 self.declare_scalar("_0", val);
1068 if !old_topic.is_undef() {
1070 self.declare_scalar("_<", old_topic);
1071 }
1072 if !old_1lt.is_undef() {
1073 self.declare_scalar("_<<", old_1lt);
1074 }
1075 if !old_2lt.is_undef() {
1076 self.declare_scalar("_<<<", old_2lt);
1077 }
1078 if !old_3lt.is_undef() {
1079 self.declare_scalar("_<<<<", old_3lt);
1080 }
1081 }
1082
1083 #[inline]
1086 pub fn set_closure_args(&mut self, args: &[PerlValue]) {
1087 if let Some(first) = args.first() {
1088 self.set_topic(first.clone());
1090 }
1091 for (i, val) in args.iter().enumerate() {
1092 self.declare_scalar(&format!("_{}", i), val.clone());
1093 }
1094 }
1095
1096 #[inline]
1098 pub fn push_defer(&mut self, coderef: PerlValue) {
1099 if let Some(frame) = self.frames.last_mut() {
1100 frame.defers.push(coderef);
1101 }
1102 }
1103
1104 #[inline]
1107 pub fn take_defers(&mut self) -> Vec<PerlValue> {
1108 if let Some(frame) = self.frames.last_mut() {
1109 let mut defers = std::mem::take(&mut frame.defers);
1110 defers.reverse();
1111 defers
1112 } else {
1113 Vec::new()
1114 }
1115 }
1116
1117 pub fn declare_atomic_array(&mut self, name: &str, val: Vec<PerlValue>) {
1120 if let Some(frame) = self.frames.last_mut() {
1121 frame
1122 .atomic_arrays
1123 .push((name.to_string(), AtomicArray(Arc::new(Mutex::new(val)))));
1124 }
1125 }
1126
1127 pub fn declare_atomic_hash(&mut self, name: &str, val: IndexMap<String, PerlValue>) {
1128 if let Some(frame) = self.frames.last_mut() {
1129 frame
1130 .atomic_hashes
1131 .push((name.to_string(), AtomicHash(Arc::new(Mutex::new(val)))));
1132 }
1133 }
1134
1135 fn find_atomic_array(&self, name: &str) -> Option<&AtomicArray> {
1137 for frame in self.frames.iter().rev() {
1138 if let Some(aa) = frame.atomic_arrays.iter().find(|(k, _)| k == name) {
1139 return Some(&aa.1);
1140 }
1141 }
1142 None
1143 }
1144
1145 fn find_atomic_hash(&self, name: &str) -> Option<&AtomicHash> {
1147 for frame in self.frames.iter().rev() {
1148 if let Some(ah) = frame.atomic_hashes.iter().find(|(k, _)| k == name) {
1149 return Some(&ah.1);
1150 }
1151 }
1152 None
1153 }
1154
1155 #[inline]
1160 pub fn take_sub_underscore(&mut self) -> Option<Vec<PerlValue>> {
1161 self.frames.last_mut()?.sub_underscore.take()
1162 }
1163
1164 pub fn declare_array(&mut self, name: &str, val: Vec<PerlValue>) {
1165 self.declare_array_frozen(name, val, false);
1166 }
1167
1168 pub fn declare_array_frozen(&mut self, name: &str, val: Vec<PerlValue>, frozen: bool) {
1169 let idx = if name.contains("::") {
1172 0
1173 } else {
1174 self.frames.len().saturating_sub(1)
1175 };
1176 if let Some(frame) = self.frames.get_mut(idx) {
1177 frame.set_array(name, val);
1178 if frozen {
1179 frame.frozen_arrays.insert(name.to_string());
1180 } else {
1181 frame.frozen_arrays.remove(name);
1183 }
1184 }
1185 }
1186
1187 pub fn get_array(&self, name: &str) -> Vec<PerlValue> {
1188 if let Some(aa) = self.find_atomic_array(name) {
1190 return aa.0.lock().clone();
1191 }
1192 if name.contains("::") {
1193 if let Some(f) = self.frames.first() {
1194 if let Some(val) = f.get_array(name) {
1195 return val.clone();
1196 }
1197 }
1198 return Vec::new();
1199 }
1200 for frame in self.frames.iter().rev() {
1201 if let Some(val) = frame.get_array(name) {
1202 return val.clone();
1203 }
1204 }
1205 Vec::new()
1206 }
1207
1208 #[inline]
1211 pub fn get_array_borrow(&self, name: &str) -> Option<&[PerlValue]> {
1212 if self.find_atomic_array(name).is_some() {
1213 return None;
1214 }
1215 if name.contains("::") {
1216 return self
1217 .frames
1218 .first()
1219 .and_then(|f| f.get_array(name))
1220 .map(|v| v.as_slice());
1221 }
1222 for frame in self.frames.iter().rev() {
1223 if let Some(val) = frame.get_array(name) {
1224 return Some(val.as_slice());
1225 }
1226 }
1227 None
1228 }
1229
1230 fn resolve_array_frame_idx(&self, name: &str) -> Option<usize> {
1231 if name.contains("::") {
1232 return Some(0);
1233 }
1234 (0..self.frames.len())
1235 .rev()
1236 .find(|&i| self.frames[i].has_array(name))
1237 }
1238
1239 fn check_parallel_array_write(&self, name: &str) -> Result<(), PerlError> {
1240 if !self.parallel_guard
1241 || Self::parallel_skip_special_name(name)
1242 || Self::parallel_allowed_internal_array(name)
1243 {
1244 return Ok(());
1245 }
1246 let inner = self.frames.len().saturating_sub(1);
1247 match self.resolve_array_frame_idx(name) {
1248 None => Err(PerlError::runtime(
1249 format!(
1250 "cannot modify undeclared array `@{}` in a parallel block",
1251 name
1252 ),
1253 0,
1254 )),
1255 Some(idx) if idx != inner => Err(PerlError::runtime(
1256 format!(
1257 "cannot modify captured non-mysync array `@{}` in a parallel block",
1258 name
1259 ),
1260 0,
1261 )),
1262 Some(_) => Ok(()),
1263 }
1264 }
1265
1266 pub fn get_array_mut(&mut self, name: &str) -> Result<&mut Vec<PerlValue>, PerlError> {
1267 if self.find_atomic_array(name).is_some() {
1270 return Err(PerlError::runtime(
1271 "get_array_mut: use atomic path for mysync arrays",
1272 0,
1273 ));
1274 }
1275 self.check_parallel_array_write(name)?;
1276 let idx = self.resolve_array_frame_idx(name).unwrap_or_default();
1277 let frame = &mut self.frames[idx];
1278 if frame.get_array_mut(name).is_none() {
1279 frame.arrays.push((name.to_string(), Vec::new()));
1280 }
1281 Ok(frame.get_array_mut(name).unwrap())
1282 }
1283
1284 pub fn push_to_array(&mut self, name: &str, val: PerlValue) -> Result<(), PerlError> {
1286 if let Some(aa) = self.find_atomic_array(name) {
1287 aa.0.lock().push(val);
1288 return Ok(());
1289 }
1290 self.get_array_mut(name)?.push(val);
1291 Ok(())
1292 }
1293
1294 pub fn push_int_range_to_array(
1298 &mut self,
1299 name: &str,
1300 start: i64,
1301 end: i64,
1302 ) -> Result<(), PerlError> {
1303 if end <= start {
1304 return Ok(());
1305 }
1306 let count = (end - start) as usize;
1307 if let Some(aa) = self.find_atomic_array(name) {
1308 let mut g = aa.0.lock();
1309 g.reserve(count);
1310 for i in start..end {
1311 g.push(PerlValue::integer(i));
1312 }
1313 return Ok(());
1314 }
1315 let arr = self.get_array_mut(name)?;
1316 arr.reserve(count);
1317 for i in start..end {
1318 arr.push(PerlValue::integer(i));
1319 }
1320 Ok(())
1321 }
1322
1323 pub fn pop_from_array(&mut self, name: &str) -> Result<PerlValue, PerlError> {
1325 if let Some(aa) = self.find_atomic_array(name) {
1326 return Ok(aa.0.lock().pop().unwrap_or(PerlValue::UNDEF));
1327 }
1328 Ok(self.get_array_mut(name)?.pop().unwrap_or(PerlValue::UNDEF))
1329 }
1330
1331 pub fn shift_from_array(&mut self, name: &str) -> Result<PerlValue, PerlError> {
1333 if let Some(aa) = self.find_atomic_array(name) {
1334 let mut guard = aa.0.lock();
1335 return Ok(if guard.is_empty() {
1336 PerlValue::UNDEF
1337 } else {
1338 guard.remove(0)
1339 });
1340 }
1341 let arr = self.get_array_mut(name)?;
1342 Ok(if arr.is_empty() {
1343 PerlValue::UNDEF
1344 } else {
1345 arr.remove(0)
1346 })
1347 }
1348
1349 pub fn array_len(&self, name: &str) -> usize {
1351 if let Some(aa) = self.find_atomic_array(name) {
1352 return aa.0.lock().len();
1353 }
1354 if name.contains("::") {
1355 return self
1356 .frames
1357 .first()
1358 .and_then(|f| f.get_array(name))
1359 .map(|a| a.len())
1360 .unwrap_or(0);
1361 }
1362 for frame in self.frames.iter().rev() {
1363 if let Some(arr) = frame.get_array(name) {
1364 return arr.len();
1365 }
1366 }
1367 0
1368 }
1369
1370 pub fn set_array(&mut self, name: &str, val: Vec<PerlValue>) -> Result<(), PerlError> {
1371 if let Some(aa) = self.find_atomic_array(name) {
1372 *aa.0.lock() = val;
1373 return Ok(());
1374 }
1375 self.check_parallel_array_write(name)?;
1376 for frame in self.frames.iter_mut().rev() {
1377 if frame.has_array(name) {
1378 frame.set_array(name, val);
1379 return Ok(());
1380 }
1381 }
1382 self.frames[0].set_array(name, val);
1383 Ok(())
1384 }
1385
1386 #[inline]
1388 pub fn get_array_element(&self, name: &str, index: i64) -> PerlValue {
1389 if let Some(aa) = self.find_atomic_array(name) {
1390 let arr = aa.0.lock();
1391 let idx = if index < 0 {
1392 (arr.len() as i64 + index) as usize
1393 } else {
1394 index as usize
1395 };
1396 return arr.get(idx).cloned().unwrap_or(PerlValue::UNDEF);
1397 }
1398 for frame in self.frames.iter().rev() {
1399 if let Some(arr) = frame.get_array(name) {
1400 let idx = if index < 0 {
1401 (arr.len() as i64 + index) as usize
1402 } else {
1403 index as usize
1404 };
1405 return arr.get(idx).cloned().unwrap_or(PerlValue::UNDEF);
1406 }
1407 }
1408 PerlValue::UNDEF
1409 }
1410
1411 pub fn set_array_element(
1412 &mut self,
1413 name: &str,
1414 index: i64,
1415 val: PerlValue,
1416 ) -> Result<(), PerlError> {
1417 if let Some(aa) = self.find_atomic_array(name) {
1418 let mut arr = aa.0.lock();
1419 let idx = if index < 0 {
1420 (arr.len() as i64 + index).max(0) as usize
1421 } else {
1422 index as usize
1423 };
1424 if idx >= arr.len() {
1425 arr.resize(idx + 1, PerlValue::UNDEF);
1426 }
1427 arr[idx] = val;
1428 return Ok(());
1429 }
1430 let arr = self.get_array_mut(name)?;
1431 let idx = if index < 0 {
1432 let len = arr.len() as i64;
1433 (len + index).max(0) as usize
1434 } else {
1435 index as usize
1436 };
1437 if idx >= arr.len() {
1438 arr.resize(idx + 1, PerlValue::UNDEF);
1439 }
1440 arr[idx] = val;
1441 Ok(())
1442 }
1443
1444 pub fn exists_array_element(&self, name: &str, index: i64) -> bool {
1446 if let Some(aa) = self.find_atomic_array(name) {
1447 let arr = aa.0.lock();
1448 let idx = if index < 0 {
1449 (arr.len() as i64 + index) as usize
1450 } else {
1451 index as usize
1452 };
1453 return idx < arr.len();
1454 }
1455 for frame in self.frames.iter().rev() {
1456 if let Some(arr) = frame.get_array(name) {
1457 let idx = if index < 0 {
1458 (arr.len() as i64 + index) as usize
1459 } else {
1460 index as usize
1461 };
1462 return idx < arr.len();
1463 }
1464 }
1465 false
1466 }
1467
1468 pub fn delete_array_element(&mut self, name: &str, index: i64) -> Result<PerlValue, PerlError> {
1470 if let Some(aa) = self.find_atomic_array(name) {
1471 let mut arr = aa.0.lock();
1472 let idx = if index < 0 {
1473 (arr.len() as i64 + index) as usize
1474 } else {
1475 index as usize
1476 };
1477 if idx >= arr.len() {
1478 return Ok(PerlValue::UNDEF);
1479 }
1480 let old = arr.get(idx).cloned().unwrap_or(PerlValue::UNDEF);
1481 arr[idx] = PerlValue::UNDEF;
1482 return Ok(old);
1483 }
1484 let arr = self.get_array_mut(name)?;
1485 let idx = if index < 0 {
1486 (arr.len() as i64 + index) as usize
1487 } else {
1488 index as usize
1489 };
1490 if idx >= arr.len() {
1491 return Ok(PerlValue::UNDEF);
1492 }
1493 let old = arr.get(idx).cloned().unwrap_or(PerlValue::UNDEF);
1494 arr[idx] = PerlValue::UNDEF;
1495 Ok(old)
1496 }
1497
1498 #[inline]
1501 pub fn declare_hash(&mut self, name: &str, val: IndexMap<String, PerlValue>) {
1502 self.declare_hash_frozen(name, val, false);
1503 }
1504
1505 pub fn declare_hash_frozen(
1506 &mut self,
1507 name: &str,
1508 val: IndexMap<String, PerlValue>,
1509 frozen: bool,
1510 ) {
1511 if let Some(frame) = self.frames.last_mut() {
1512 frame.set_hash(name, val);
1513 if frozen {
1514 frame.frozen_hashes.insert(name.to_string());
1515 }
1516 }
1517 }
1518
1519 pub fn declare_hash_global(&mut self, name: &str, val: IndexMap<String, PerlValue>) {
1521 if let Some(frame) = self.frames.first_mut() {
1522 frame.set_hash(name, val);
1523 }
1524 }
1525
1526 pub fn declare_hash_global_frozen(&mut self, name: &str, val: IndexMap<String, PerlValue>) {
1528 if let Some(frame) = self.frames.first_mut() {
1529 frame.set_hash(name, val);
1530 frame.frozen_hashes.insert(name.to_string());
1531 }
1532 }
1533
1534 pub fn has_lexical_hash(&self, name: &str) -> bool {
1536 self.frames.iter().skip(1).any(|f| f.has_hash(name))
1537 }
1538
1539 pub fn any_frame_has_hash(&self, name: &str) -> bool {
1541 self.frames.iter().any(|f| f.has_hash(name))
1542 }
1543
1544 pub fn get_hash(&self, name: &str) -> IndexMap<String, PerlValue> {
1545 if let Some(ah) = self.find_atomic_hash(name) {
1546 return ah.0.lock().clone();
1547 }
1548 for frame in self.frames.iter().rev() {
1549 if let Some(val) = frame.get_hash(name) {
1550 return val.clone();
1551 }
1552 }
1553 IndexMap::new()
1554 }
1555
1556 fn resolve_hash_frame_idx(&self, name: &str) -> Option<usize> {
1557 if name.contains("::") {
1558 return Some(0);
1559 }
1560 (0..self.frames.len())
1561 .rev()
1562 .find(|&i| self.frames[i].has_hash(name))
1563 }
1564
1565 fn check_parallel_hash_write(&self, name: &str) -> Result<(), PerlError> {
1566 if !self.parallel_guard
1567 || Self::parallel_skip_special_name(name)
1568 || Self::parallel_allowed_internal_hash(name)
1569 {
1570 return Ok(());
1571 }
1572 let inner = self.frames.len().saturating_sub(1);
1573 match self.resolve_hash_frame_idx(name) {
1574 None => Err(PerlError::runtime(
1575 format!(
1576 "cannot modify undeclared hash `%{}` in a parallel block",
1577 name
1578 ),
1579 0,
1580 )),
1581 Some(idx) if idx != inner => Err(PerlError::runtime(
1582 format!(
1583 "cannot modify captured non-mysync hash `%{}` in a parallel block",
1584 name
1585 ),
1586 0,
1587 )),
1588 Some(_) => Ok(()),
1589 }
1590 }
1591
1592 pub fn get_hash_mut(
1593 &mut self,
1594 name: &str,
1595 ) -> Result<&mut IndexMap<String, PerlValue>, PerlError> {
1596 if self.find_atomic_hash(name).is_some() {
1597 return Err(PerlError::runtime(
1598 "get_hash_mut: use atomic path for mysync hashes",
1599 0,
1600 ));
1601 }
1602 self.check_parallel_hash_write(name)?;
1603 let idx = self.resolve_hash_frame_idx(name).unwrap_or_default();
1604 let frame = &mut self.frames[idx];
1605 if frame.get_hash_mut(name).is_none() {
1606 frame.hashes.push((name.to_string(), IndexMap::new()));
1607 }
1608 Ok(frame.get_hash_mut(name).unwrap())
1609 }
1610
1611 pub fn set_hash(
1612 &mut self,
1613 name: &str,
1614 val: IndexMap<String, PerlValue>,
1615 ) -> Result<(), PerlError> {
1616 if let Some(ah) = self.find_atomic_hash(name) {
1617 *ah.0.lock() = val;
1618 return Ok(());
1619 }
1620 self.check_parallel_hash_write(name)?;
1621 for frame in self.frames.iter_mut().rev() {
1622 if frame.has_hash(name) {
1623 frame.set_hash(name, val);
1624 return Ok(());
1625 }
1626 }
1627 self.frames[0].set_hash(name, val);
1628 Ok(())
1629 }
1630
1631 #[inline]
1632 pub fn get_hash_element(&self, name: &str, key: &str) -> PerlValue {
1633 if let Some(ah) = self.find_atomic_hash(name) {
1634 return ah.0.lock().get(key).cloned().unwrap_or(PerlValue::UNDEF);
1635 }
1636 for frame in self.frames.iter().rev() {
1637 if let Some(hash) = frame.get_hash(name) {
1638 return hash.get(key).cloned().unwrap_or(PerlValue::UNDEF);
1639 }
1640 }
1641 PerlValue::UNDEF
1642 }
1643
1644 pub fn atomic_hash_mutate(
1647 &mut self,
1648 name: &str,
1649 key: &str,
1650 f: impl FnOnce(&PerlValue) -> PerlValue,
1651 ) -> Result<PerlValue, PerlError> {
1652 if let Some(ah) = self.find_atomic_hash(name) {
1653 let mut guard = ah.0.lock();
1654 let old = guard.get(key).cloned().unwrap_or(PerlValue::UNDEF);
1655 let new_val = f(&old);
1656 guard.insert(key.to_string(), new_val.clone());
1657 return Ok(new_val);
1658 }
1659 let old = self.get_hash_element(name, key);
1661 let new_val = f(&old);
1662 self.set_hash_element(name, key, new_val.clone())?;
1663 Ok(new_val)
1664 }
1665
1666 pub fn atomic_array_mutate(
1668 &mut self,
1669 name: &str,
1670 index: i64,
1671 f: impl FnOnce(&PerlValue) -> PerlValue,
1672 ) -> Result<PerlValue, PerlError> {
1673 if let Some(aa) = self.find_atomic_array(name) {
1674 let mut guard = aa.0.lock();
1675 let idx = if index < 0 {
1676 (guard.len() as i64 + index).max(0) as usize
1677 } else {
1678 index as usize
1679 };
1680 if idx >= guard.len() {
1681 guard.resize(idx + 1, PerlValue::UNDEF);
1682 }
1683 let old = guard[idx].clone();
1684 let new_val = f(&old);
1685 guard[idx] = new_val.clone();
1686 return Ok(new_val);
1687 }
1688 let old = self.get_array_element(name, index);
1690 let new_val = f(&old);
1691 self.set_array_element(name, index, new_val.clone())?;
1692 Ok(new_val)
1693 }
1694
1695 pub fn set_hash_element(
1696 &mut self,
1697 name: &str,
1698 key: &str,
1699 val: PerlValue,
1700 ) -> Result<(), PerlError> {
1701 if name == "SIG" {
1704 crate::perl_signal::install(key);
1705 }
1706 if let Some(ah) = self.find_atomic_hash(name) {
1707 ah.0.lock().insert(key.to_string(), val);
1708 return Ok(());
1709 }
1710 let hash = self.get_hash_mut(name)?;
1711 hash.insert(key.to_string(), val);
1712 Ok(())
1713 }
1714
1715 pub fn set_hash_int_times_range(
1719 &mut self,
1720 name: &str,
1721 start: i64,
1722 end: i64,
1723 k: i64,
1724 ) -> Result<(), PerlError> {
1725 if end <= start {
1726 return Ok(());
1727 }
1728 let count = (end - start) as usize;
1729 if let Some(ah) = self.find_atomic_hash(name) {
1730 let mut g = ah.0.lock();
1731 g.reserve(count);
1732 let mut buf = itoa::Buffer::new();
1733 for i in start..end {
1734 let key = buf.format(i).to_owned();
1735 g.insert(key, PerlValue::integer(i.wrapping_mul(k)));
1736 }
1737 return Ok(());
1738 }
1739 let hash = self.get_hash_mut(name)?;
1740 hash.reserve(count);
1741 let mut buf = itoa::Buffer::new();
1742 for i in start..end {
1743 let key = buf.format(i).to_owned();
1744 hash.insert(key, PerlValue::integer(i.wrapping_mul(k)));
1745 }
1746 Ok(())
1747 }
1748
1749 pub fn delete_hash_element(&mut self, name: &str, key: &str) -> Result<PerlValue, PerlError> {
1750 if let Some(ah) = self.find_atomic_hash(name) {
1751 return Ok(ah.0.lock().shift_remove(key).unwrap_or(PerlValue::UNDEF));
1752 }
1753 let hash = self.get_hash_mut(name)?;
1754 Ok(hash.shift_remove(key).unwrap_or(PerlValue::UNDEF))
1755 }
1756
1757 #[inline]
1758 pub fn exists_hash_element(&self, name: &str, key: &str) -> bool {
1759 if let Some(ah) = self.find_atomic_hash(name) {
1760 return ah.0.lock().contains_key(key);
1761 }
1762 for frame in self.frames.iter().rev() {
1763 if let Some(hash) = frame.get_hash(name) {
1764 return hash.contains_key(key);
1765 }
1766 }
1767 false
1768 }
1769
1770 #[inline]
1775 pub fn for_each_hash_value(&self, name: &str, mut visit: impl FnMut(&PerlValue)) {
1776 if let Some(ah) = self.find_atomic_hash(name) {
1777 let g = ah.0.lock();
1778 for v in g.values() {
1779 visit(v);
1780 }
1781 return;
1782 }
1783 for frame in self.frames.iter().rev() {
1784 if let Some(hash) = frame.get_hash(name) {
1785 for v in hash.values() {
1786 visit(v);
1787 }
1788 return;
1789 }
1790 }
1791 }
1792
1793 pub fn repl_binding_names(&self) -> Vec<String> {
1795 let mut seen = HashSet::new();
1796 let mut out = Vec::new();
1797 for frame in &self.frames {
1798 for (name, _) in &frame.scalars {
1799 let s = format!("${}", name);
1800 if seen.insert(s.clone()) {
1801 out.push(s);
1802 }
1803 }
1804 for (name, _) in &frame.arrays {
1805 let s = format!("@{}", name);
1806 if seen.insert(s.clone()) {
1807 out.push(s);
1808 }
1809 }
1810 for (name, _) in &frame.hashes {
1811 let s = format!("%{}", name);
1812 if seen.insert(s.clone()) {
1813 out.push(s);
1814 }
1815 }
1816 for (name, _) in &frame.atomic_arrays {
1817 let s = format!("@{}", name);
1818 if seen.insert(s.clone()) {
1819 out.push(s);
1820 }
1821 }
1822 for (name, _) in &frame.atomic_hashes {
1823 let s = format!("%{}", name);
1824 if seen.insert(s.clone()) {
1825 out.push(s);
1826 }
1827 }
1828 }
1829 out.sort();
1830 out
1831 }
1832
1833 pub fn capture(&mut self) -> Vec<(String, PerlValue)> {
1834 let mut captured = Vec::new();
1835 for frame in &mut self.frames {
1836 for (k, v) in &mut frame.scalars {
1837 if v.as_scalar_ref().is_some() {
1843 captured.push((format!("${}", k), v.clone()));
1844 } else if v.is_simple_scalar() {
1845 let wrapped = PerlValue::scalar_ref(Arc::new(RwLock::new(v.clone())));
1846 *v = wrapped.clone();
1849 captured.push((format!("${}", k), wrapped));
1850 } else {
1851 captured.push((format!("${}", k), v.clone()));
1852 }
1853 }
1854 for (i, v) in frame.scalar_slots.iter().enumerate() {
1855 if let Some(Some(name)) = frame.scalar_slot_names.get(i) {
1856 let wrapped = if v.as_scalar_ref().is_some() {
1859 v.clone()
1860 } else {
1861 PerlValue::scalar_ref(Arc::new(RwLock::new(v.clone())))
1862 };
1863 captured.push((format!("$slot:{}:{}", i, name), wrapped));
1864 }
1865 }
1866 for (k, v) in &frame.arrays {
1867 if capture_skip_bootstrap_array(k) {
1868 continue;
1869 }
1870 if frame.frozen_arrays.contains(k) {
1871 captured.push((format!("@frozen:{}", k), PerlValue::array(v.clone())));
1872 } else {
1873 captured.push((format!("@{}", k), PerlValue::array(v.clone())));
1874 }
1875 }
1876 for (k, v) in &frame.hashes {
1877 if capture_skip_bootstrap_hash(k) {
1878 continue;
1879 }
1880 if frame.frozen_hashes.contains(k) {
1881 captured.push((format!("%frozen:{}", k), PerlValue::hash(v.clone())));
1882 } else {
1883 captured.push((format!("%{}", k), PerlValue::hash(v.clone())));
1884 }
1885 }
1886 for (k, _aa) in &frame.atomic_arrays {
1887 captured.push((
1888 format!("@sync_{}", k),
1889 PerlValue::atomic(Arc::new(Mutex::new(PerlValue::string(String::new())))),
1890 ));
1891 }
1892 for (k, _ah) in &frame.atomic_hashes {
1893 captured.push((
1894 format!("%sync_{}", k),
1895 PerlValue::atomic(Arc::new(Mutex::new(PerlValue::string(String::new())))),
1896 ));
1897 }
1898 }
1899 captured
1900 }
1901
1902 pub fn capture_with_atomics(&self) -> ScopeCaptureWithAtomics {
1904 let mut scalars = Vec::new();
1905 let mut arrays = Vec::new();
1906 let mut hashes = Vec::new();
1907 for frame in &self.frames {
1908 for (k, v) in &frame.scalars {
1909 scalars.push((format!("${}", k), v.clone()));
1910 }
1911 for (i, v) in frame.scalar_slots.iter().enumerate() {
1912 if let Some(Some(name)) = frame.scalar_slot_names.get(i) {
1913 scalars.push((format!("$slot:{}:{}", i, name), v.clone()));
1914 }
1915 }
1916 for (k, v) in &frame.arrays {
1917 if capture_skip_bootstrap_array(k) {
1918 continue;
1919 }
1920 if frame.frozen_arrays.contains(k) {
1921 scalars.push((format!("@frozen:{}", k), PerlValue::array(v.clone())));
1922 } else {
1923 scalars.push((format!("@{}", k), PerlValue::array(v.clone())));
1924 }
1925 }
1926 for (k, v) in &frame.hashes {
1927 if capture_skip_bootstrap_hash(k) {
1928 continue;
1929 }
1930 if frame.frozen_hashes.contains(k) {
1931 scalars.push((format!("%frozen:{}", k), PerlValue::hash(v.clone())));
1932 } else {
1933 scalars.push((format!("%{}", k), PerlValue::hash(v.clone())));
1934 }
1935 }
1936 for (k, aa) in &frame.atomic_arrays {
1937 arrays.push((k.clone(), aa.clone()));
1938 }
1939 for (k, ah) in &frame.atomic_hashes {
1940 hashes.push((k.clone(), ah.clone()));
1941 }
1942 }
1943 (scalars, arrays, hashes)
1944 }
1945
1946 pub fn restore_capture(&mut self, captured: &[(String, PerlValue)]) {
1947 for (name, val) in captured {
1948 if let Some(rest) = name.strip_prefix("$slot:") {
1949 if let Some(colon) = rest.find(':') {
1951 let idx: usize = rest[..colon].parse().unwrap_or(0);
1952 let sname = &rest[colon + 1..];
1953 self.declare_scalar_slot(idx as u8, val.clone(), Some(sname));
1954 self.declare_scalar(sname, val.clone());
1955 }
1956 } else if let Some(stripped) = name.strip_prefix('$') {
1957 self.declare_scalar(stripped, val.clone());
1958 } else if let Some(rest) = name.strip_prefix("@frozen:") {
1959 let arr = val.as_array_vec().unwrap_or_else(|| val.to_list());
1960 self.declare_array_frozen(rest, arr, true);
1961 } else if let Some(rest) = name.strip_prefix("%frozen:") {
1962 if let Some(h) = val.as_hash_map() {
1963 self.declare_hash_frozen(rest, h.clone(), true);
1964 }
1965 } else if let Some(rest) = name.strip_prefix('@') {
1966 if rest.starts_with("sync_") {
1967 continue;
1968 }
1969 let arr = val.as_array_vec().unwrap_or_else(|| val.to_list());
1970 self.declare_array(rest, arr);
1971 } else if let Some(rest) = name.strip_prefix('%') {
1972 if rest.starts_with("sync_") {
1973 continue;
1974 }
1975 if let Some(h) = val.as_hash_map() {
1976 self.declare_hash(rest, h.clone());
1977 }
1978 }
1979 }
1980 }
1981
1982 pub fn restore_atomics(
1984 &mut self,
1985 arrays: &[(String, AtomicArray)],
1986 hashes: &[(String, AtomicHash)],
1987 ) {
1988 if let Some(frame) = self.frames.last_mut() {
1989 for (name, aa) in arrays {
1990 frame.atomic_arrays.push((name.clone(), aa.clone()));
1991 }
1992 for (name, ah) in hashes {
1993 frame.atomic_hashes.push((name.clone(), ah.clone()));
1994 }
1995 }
1996 }
1997}
1998
1999#[cfg(test)]
2000mod tests {
2001 use super::*;
2002 use crate::value::PerlValue;
2003
2004 #[test]
2005 fn missing_scalar_is_undef() {
2006 let s = Scope::new();
2007 assert!(s.get_scalar("not_declared").is_undef());
2008 }
2009
2010 #[test]
2011 fn inner_frame_shadows_outer_scalar() {
2012 let mut s = Scope::new();
2013 s.declare_scalar("a", PerlValue::integer(1));
2014 s.push_frame();
2015 s.declare_scalar("a", PerlValue::integer(2));
2016 assert_eq!(s.get_scalar("a").to_int(), 2);
2017 s.pop_frame();
2018 assert_eq!(s.get_scalar("a").to_int(), 1);
2019 }
2020
2021 #[test]
2022 fn set_scalar_updates_innermost_binding() {
2023 let mut s = Scope::new();
2024 s.declare_scalar("a", PerlValue::integer(1));
2025 s.push_frame();
2026 s.declare_scalar("a", PerlValue::integer(2));
2027 let _ = s.set_scalar("a", PerlValue::integer(99));
2028 assert_eq!(s.get_scalar("a").to_int(), 99);
2029 s.pop_frame();
2030 assert_eq!(s.get_scalar("a").to_int(), 1);
2031 }
2032
2033 #[test]
2034 fn array_negative_index_reads_from_end() {
2035 let mut s = Scope::new();
2036 s.declare_array(
2037 "a",
2038 vec![
2039 PerlValue::integer(10),
2040 PerlValue::integer(20),
2041 PerlValue::integer(30),
2042 ],
2043 );
2044 assert_eq!(s.get_array_element("a", -1).to_int(), 30);
2045 }
2046
2047 #[test]
2048 fn set_array_element_extends_array_with_undef_gaps() {
2049 let mut s = Scope::new();
2050 s.declare_array("a", vec![]);
2051 s.set_array_element("a", 2, PerlValue::integer(7)).unwrap();
2052 assert_eq!(s.get_array_element("a", 2).to_int(), 7);
2053 assert!(s.get_array_element("a", 0).is_undef());
2054 }
2055
2056 #[test]
2057 fn capture_restore_roundtrip_scalar() {
2058 let mut s = Scope::new();
2059 s.declare_scalar("n", PerlValue::integer(42));
2060 let cap = s.capture();
2061 let mut t = Scope::new();
2062 t.restore_capture(&cap);
2063 assert_eq!(t.get_scalar("n").to_int(), 42);
2064 }
2065
2066 #[test]
2067 fn capture_restore_roundtrip_lexical_array_and_hash() {
2068 let mut s = Scope::new();
2069 s.declare_array("a", vec![PerlValue::integer(1), PerlValue::integer(2)]);
2070 let mut m = IndexMap::new();
2071 m.insert("k".to_string(), PerlValue::integer(99));
2072 s.declare_hash("h", m);
2073 let cap = s.capture();
2074 let mut t = Scope::new();
2075 t.restore_capture(&cap);
2076 assert_eq!(t.get_array_element("a", 1).to_int(), 2);
2077 assert_eq!(t.get_hash_element("h", "k").to_int(), 99);
2078 }
2079
2080 #[test]
2081 fn hash_get_set_delete_exists() {
2082 let mut s = Scope::new();
2083 let mut m = IndexMap::new();
2084 m.insert("k".to_string(), PerlValue::integer(1));
2085 s.declare_hash("h", m);
2086 assert_eq!(s.get_hash_element("h", "k").to_int(), 1);
2087 assert!(s.exists_hash_element("h", "k"));
2088 s.set_hash_element("h", "k", PerlValue::integer(99))
2089 .unwrap();
2090 assert_eq!(s.get_hash_element("h", "k").to_int(), 99);
2091 let del = s.delete_hash_element("h", "k").unwrap();
2092 assert_eq!(del.to_int(), 99);
2093 assert!(!s.exists_hash_element("h", "k"));
2094 }
2095
2096 #[test]
2097 fn inner_frame_shadows_outer_hash_name() {
2098 let mut s = Scope::new();
2099 let mut outer = IndexMap::new();
2100 outer.insert("k".to_string(), PerlValue::integer(1));
2101 s.declare_hash("h", outer);
2102 s.push_frame();
2103 let mut inner = IndexMap::new();
2104 inner.insert("k".to_string(), PerlValue::integer(2));
2105 s.declare_hash("h", inner);
2106 assert_eq!(s.get_hash_element("h", "k").to_int(), 2);
2107 s.pop_frame();
2108 assert_eq!(s.get_hash_element("h", "k").to_int(), 1);
2109 }
2110
2111 #[test]
2112 fn inner_frame_shadows_outer_array_name() {
2113 let mut s = Scope::new();
2114 s.declare_array("a", vec![PerlValue::integer(1)]);
2115 s.push_frame();
2116 s.declare_array("a", vec![PerlValue::integer(2), PerlValue::integer(3)]);
2117 assert_eq!(s.get_array_element("a", 1).to_int(), 3);
2118 s.pop_frame();
2119 assert_eq!(s.get_array_element("a", 0).to_int(), 1);
2120 }
2121
2122 #[test]
2123 fn pop_frame_never_removes_global_frame() {
2124 let mut s = Scope::new();
2125 s.declare_scalar("x", PerlValue::integer(1));
2126 s.pop_frame();
2127 s.pop_frame();
2128 assert_eq!(s.get_scalar("x").to_int(), 1);
2129 }
2130
2131 #[test]
2132 fn empty_array_declared_has_zero_length() {
2133 let mut s = Scope::new();
2134 s.declare_array("a", vec![]);
2135 assert_eq!(s.get_array("a").len(), 0);
2136 }
2137
2138 #[test]
2139 fn depth_increments_with_push_frame() {
2140 let mut s = Scope::new();
2141 let d0 = s.depth();
2142 s.push_frame();
2143 assert_eq!(s.depth(), d0 + 1);
2144 s.pop_frame();
2145 assert_eq!(s.depth(), d0);
2146 }
2147
2148 #[test]
2149 fn pop_to_depth_unwinds_to_target() {
2150 let mut s = Scope::new();
2151 s.push_frame();
2152 s.push_frame();
2153 let target = s.depth() - 1;
2154 s.pop_to_depth(target);
2155 assert_eq!(s.depth(), target);
2156 }
2157
2158 #[test]
2159 fn array_len_and_push_pop_roundtrip() {
2160 let mut s = Scope::new();
2161 s.declare_array("a", vec![]);
2162 assert_eq!(s.array_len("a"), 0);
2163 s.push_to_array("a", PerlValue::integer(1)).unwrap();
2164 s.push_to_array("a", PerlValue::integer(2)).unwrap();
2165 assert_eq!(s.array_len("a"), 2);
2166 assert_eq!(s.pop_from_array("a").unwrap().to_int(), 2);
2167 assert_eq!(s.pop_from_array("a").unwrap().to_int(), 1);
2168 assert!(s.pop_from_array("a").unwrap().is_undef());
2169 }
2170
2171 #[test]
2172 fn shift_from_array_drops_front() {
2173 let mut s = Scope::new();
2174 s.declare_array("a", vec![PerlValue::integer(1), PerlValue::integer(2)]);
2175 assert_eq!(s.shift_from_array("a").unwrap().to_int(), 1);
2176 assert_eq!(s.array_len("a"), 1);
2177 }
2178
2179 #[test]
2180 fn atomic_mutate_increments_wrapped_scalar() {
2181 use parking_lot::Mutex;
2182 use std::sync::Arc;
2183 let mut s = Scope::new();
2184 s.declare_scalar(
2185 "n",
2186 PerlValue::atomic(Arc::new(Mutex::new(PerlValue::integer(10)))),
2187 );
2188 let v = s.atomic_mutate("n", |old| PerlValue::integer(old.to_int() + 5));
2189 assert_eq!(v.to_int(), 15);
2190 assert_eq!(s.get_scalar("n").to_int(), 15);
2191 }
2192
2193 #[test]
2194 fn atomic_mutate_post_returns_old_value() {
2195 use parking_lot::Mutex;
2196 use std::sync::Arc;
2197 let mut s = Scope::new();
2198 s.declare_scalar(
2199 "n",
2200 PerlValue::atomic(Arc::new(Mutex::new(PerlValue::integer(7)))),
2201 );
2202 let old = s.atomic_mutate_post("n", |v| PerlValue::integer(v.to_int() + 1));
2203 assert_eq!(old.to_int(), 7);
2204 assert_eq!(s.get_scalar("n").to_int(), 8);
2205 }
2206
2207 #[test]
2208 fn get_scalar_raw_keeps_atomic_wrapper() {
2209 use parking_lot::Mutex;
2210 use std::sync::Arc;
2211 let mut s = Scope::new();
2212 s.declare_scalar(
2213 "n",
2214 PerlValue::atomic(Arc::new(Mutex::new(PerlValue::integer(3)))),
2215 );
2216 assert!(s.get_scalar_raw("n").is_atomic());
2217 assert!(!s.get_scalar("n").is_atomic());
2218 }
2219
2220 #[test]
2221 fn missing_array_element_is_undef() {
2222 let mut s = Scope::new();
2223 s.declare_array("a", vec![PerlValue::integer(1)]);
2224 assert!(s.get_array_element("a", 99).is_undef());
2225 }
2226
2227 #[test]
2228 fn restore_atomics_puts_atomic_containers_in_frame() {
2229 use indexmap::IndexMap;
2230 use parking_lot::Mutex;
2231 use std::sync::Arc;
2232 let mut s = Scope::new();
2233 let aa = AtomicArray(Arc::new(Mutex::new(vec![PerlValue::integer(1)])));
2234 let ah = AtomicHash(Arc::new(Mutex::new(IndexMap::new())));
2235 s.restore_atomics(&[("ax".into(), aa.clone())], &[("hx".into(), ah.clone())]);
2236 assert_eq!(s.get_array_element("ax", 0).to_int(), 1);
2237 assert_eq!(s.array_len("ax"), 1);
2238 s.set_hash_element("hx", "k", PerlValue::integer(2))
2239 .unwrap();
2240 assert_eq!(s.get_hash_element("hx", "k").to_int(), 2);
2241 }
2242}