1use std::collections::{HashMap, VecDeque};
2use std::io::{self, Write as IoWrite};
3use std::sync::Arc;
4
5use indexmap::IndexMap;
6use parking_lot::RwLock;
7use rayon::prelude::*;
8
9
10use crate::ast::{BinOp, Block, Expr, MatchArm, PerlTypeName, Sigil, SubSigParam};
11use crate::bytecode::{BuiltinId, Chunk, Op, RuntimeSubDecl, SpliceExprEntry};
12use crate::compiler::scalar_compound_op_from_byte;
13use crate::error::{ErrorKind, StrykeError, StrykeResult};
14use crate::perl_fs::read_file_text_perl_compat;
15use crate::pmap_progress::{FanProgress, PmapProgress};
16use crate::sort_fast::{sort_magic_cmp, SortBlockFast};
17use crate::value::{
18 perl_list_range_expand, perl_shl_i64, perl_shr_i64, PerlBarrier, PerlHeap, PipelineInner,
19 PipelineOp, StrykeAsyncTask, StrykeSub, StrykeValue,
20};
21use crate::vm_helper::{
22 fold_preduce_init_step, merge_preduce_init_partials, preduce_init_fold_identity, Flow,
23 FlowOrError, VMHelper, WantarrayCtx,
24};
25use parking_lot::Mutex;
26use std::sync::Barrier;
27
28static PEEK_UNDEF: StrykeValue = StrykeValue::UNDEF;
30
31struct ParallelBlockVmShared {
33 ops: Arc<Vec<Op>>,
34 names: Arc<Vec<String>>,
35 constants: Arc<Vec<StrykeValue>>,
36 lines: Arc<Vec<usize>>,
37 sub_entries: Vec<(u16, usize, bool)>,
38 static_sub_calls: Vec<(usize, bool, u16)>,
39 blocks: Vec<Block>,
40 code_ref_sigs: Vec<Vec<SubSigParam>>,
41 block_bytecode_ranges: Vec<Option<(usize, usize)>>,
42 map_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
43 grep_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
44 regex_flip_flop_rhs_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
45 given_entries: Vec<(Expr, Block)>,
46 given_topic_bytecode_ranges: Vec<Option<(usize, usize)>>,
47 eval_timeout_entries: Vec<(Expr, Block)>,
48 eval_timeout_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
49 algebraic_match_entries: Vec<(Expr, Vec<MatchArm>)>,
50 algebraic_match_subject_bytecode_ranges: Vec<Option<(usize, usize)>>,
51 par_lines_entries: Vec<(Expr, Expr, Option<Expr>)>,
52 par_walk_entries: Vec<(Expr, Expr, Option<Expr>)>,
53 pwatch_entries: Vec<(Expr, Expr)>,
54 substr_four_arg_entries: Vec<(Expr, Expr, Option<Expr>, Expr)>,
55 keys_expr_entries: Vec<Expr>,
56 keys_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
57 map_expr_entries: Vec<Expr>,
58 grep_expr_entries: Vec<Expr>,
59 regex_flip_flop_rhs_expr_entries: Vec<Expr>,
60 values_expr_entries: Vec<Expr>,
61 values_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
62 delete_expr_entries: Vec<Expr>,
63 exists_expr_entries: Vec<Expr>,
64 push_expr_entries: Vec<(Expr, Vec<Expr>)>,
65 pop_expr_entries: Vec<Expr>,
66 shift_expr_entries: Vec<Expr>,
67 unshift_expr_entries: Vec<(Expr, Vec<Expr>)>,
68 splice_expr_entries: Vec<SpliceExprEntry>,
69 lvalues: Vec<Expr>,
70 ast_eval_exprs: Vec<Expr>,
71 format_decls: Vec<(String, Vec<String>)>,
72 use_overload_entries: Vec<Vec<(String, String)>>,
73 runtime_sub_decls: Arc<Vec<RuntimeSubDecl>>,
74 runtime_advice_decls: Arc<Vec<crate::bytecode::RuntimeAdviceDecl>>,
75 jit_sub_invoke_threshold: u32,
76 op_len_plus_one: usize,
77 static_sub_closure_subs: Vec<Option<Arc<StrykeSub>>>,
78 sub_entry_by_name: HashMap<u16, (usize, bool)>,
79}
80
81impl ParallelBlockVmShared {
82 fn from_vm(vm: &VM<'_>) -> Self {
83 let n = vm.ops.len().saturating_add(1);
84 Self {
85 ops: Arc::clone(&vm.ops),
86 names: Arc::clone(&vm.names),
87 constants: Arc::clone(&vm.constants),
88 lines: Arc::clone(&vm.lines),
89 sub_entries: vm.sub_entries.clone(),
90 static_sub_calls: vm.static_sub_calls.clone(),
91 blocks: vm.blocks.clone(),
92 code_ref_sigs: vm.code_ref_sigs.clone(),
93 block_bytecode_ranges: vm.block_bytecode_ranges.clone(),
94 map_expr_bytecode_ranges: vm.map_expr_bytecode_ranges.clone(),
95 grep_expr_bytecode_ranges: vm.grep_expr_bytecode_ranges.clone(),
96 regex_flip_flop_rhs_expr_bytecode_ranges: vm
97 .regex_flip_flop_rhs_expr_bytecode_ranges
98 .clone(),
99 given_entries: vm.given_entries.clone(),
100 given_topic_bytecode_ranges: vm.given_topic_bytecode_ranges.clone(),
101 eval_timeout_entries: vm.eval_timeout_entries.clone(),
102 eval_timeout_expr_bytecode_ranges: vm.eval_timeout_expr_bytecode_ranges.clone(),
103 algebraic_match_entries: vm.algebraic_match_entries.clone(),
104 algebraic_match_subject_bytecode_ranges: vm
105 .algebraic_match_subject_bytecode_ranges
106 .clone(),
107 par_lines_entries: vm.par_lines_entries.clone(),
108 par_walk_entries: vm.par_walk_entries.clone(),
109 pwatch_entries: vm.pwatch_entries.clone(),
110 substr_four_arg_entries: vm.substr_four_arg_entries.clone(),
111 keys_expr_entries: vm.keys_expr_entries.clone(),
112 keys_expr_bytecode_ranges: vm.keys_expr_bytecode_ranges.clone(),
113 map_expr_entries: vm.map_expr_entries.clone(),
114 grep_expr_entries: vm.grep_expr_entries.clone(),
115 regex_flip_flop_rhs_expr_entries: vm.regex_flip_flop_rhs_expr_entries.clone(),
116 values_expr_entries: vm.values_expr_entries.clone(),
117 values_expr_bytecode_ranges: vm.values_expr_bytecode_ranges.clone(),
118 delete_expr_entries: vm.delete_expr_entries.clone(),
119 exists_expr_entries: vm.exists_expr_entries.clone(),
120 push_expr_entries: vm.push_expr_entries.clone(),
121 pop_expr_entries: vm.pop_expr_entries.clone(),
122 shift_expr_entries: vm.shift_expr_entries.clone(),
123 unshift_expr_entries: vm.unshift_expr_entries.clone(),
124 splice_expr_entries: vm.splice_expr_entries.clone(),
125 lvalues: vm.lvalues.clone(),
126 ast_eval_exprs: vm.ast_eval_exprs.clone(),
127 format_decls: vm.format_decls.clone(),
128 use_overload_entries: vm.use_overload_entries.clone(),
129 runtime_sub_decls: Arc::clone(&vm.runtime_sub_decls),
130 runtime_advice_decls: Arc::clone(&vm.runtime_advice_decls),
131 jit_sub_invoke_threshold: vm.jit_sub_invoke_threshold,
132 op_len_plus_one: n,
133 static_sub_closure_subs: vm.static_sub_closure_subs.clone(),
134 sub_entry_by_name: vm.sub_entry_by_name.clone(),
135 }
136 }
137
138 fn worker_vm<'a>(&self, interp: &'a mut VMHelper) -> VM<'a> {
139 let n = self.op_len_plus_one;
140 VM {
141 names: Arc::clone(&self.names),
142 constants: Arc::clone(&self.constants),
143 ops: Arc::clone(&self.ops),
144 lines: Arc::clone(&self.lines),
145 sub_entries: self.sub_entries.clone(),
146 static_sub_calls: self.static_sub_calls.clone(),
147 blocks: self.blocks.clone(),
148 code_ref_sigs: self.code_ref_sigs.clone(),
149 block_bytecode_ranges: self.block_bytecode_ranges.clone(),
150 map_expr_bytecode_ranges: self.map_expr_bytecode_ranges.clone(),
151 grep_expr_bytecode_ranges: self.grep_expr_bytecode_ranges.clone(),
152 regex_flip_flop_rhs_expr_bytecode_ranges: self
153 .regex_flip_flop_rhs_expr_bytecode_ranges
154 .clone(),
155 given_entries: self.given_entries.clone(),
156 given_topic_bytecode_ranges: self.given_topic_bytecode_ranges.clone(),
157 eval_timeout_entries: self.eval_timeout_entries.clone(),
158 eval_timeout_expr_bytecode_ranges: self.eval_timeout_expr_bytecode_ranges.clone(),
159 algebraic_match_entries: self.algebraic_match_entries.clone(),
160 algebraic_match_subject_bytecode_ranges: self
161 .algebraic_match_subject_bytecode_ranges
162 .clone(),
163 par_lines_entries: self.par_lines_entries.clone(),
164 par_walk_entries: self.par_walk_entries.clone(),
165 pwatch_entries: self.pwatch_entries.clone(),
166 substr_four_arg_entries: self.substr_four_arg_entries.clone(),
167 keys_expr_entries: self.keys_expr_entries.clone(),
168 keys_expr_bytecode_ranges: self.keys_expr_bytecode_ranges.clone(),
169 map_expr_entries: self.map_expr_entries.clone(),
170 grep_expr_entries: self.grep_expr_entries.clone(),
171 regex_flip_flop_rhs_expr_entries: self.regex_flip_flop_rhs_expr_entries.clone(),
172 values_expr_entries: self.values_expr_entries.clone(),
173 values_expr_bytecode_ranges: self.values_expr_bytecode_ranges.clone(),
174 delete_expr_entries: self.delete_expr_entries.clone(),
175 exists_expr_entries: self.exists_expr_entries.clone(),
176 push_expr_entries: self.push_expr_entries.clone(),
177 pop_expr_entries: self.pop_expr_entries.clone(),
178 shift_expr_entries: self.shift_expr_entries.clone(),
179 unshift_expr_entries: self.unshift_expr_entries.clone(),
180 splice_expr_entries: self.splice_expr_entries.clone(),
181 lvalues: self.lvalues.clone(),
182 ast_eval_exprs: self.ast_eval_exprs.clone(),
183 format_decls: self.format_decls.clone(),
184 use_overload_entries: self.use_overload_entries.clone(),
185 runtime_sub_decls: Arc::clone(&self.runtime_sub_decls),
186 runtime_advice_decls: Arc::clone(&self.runtime_advice_decls),
187 ip: 0,
188 stack: Vec::with_capacity(256),
189 call_stack: Vec::with_capacity(32),
190 wantarray_stack: Vec::with_capacity(8),
191 interp,
192 jit_enabled: false,
193 sub_jit_skip_linear: vec![false; n],
194 sub_jit_skip_block: vec![false; n],
195 sub_fusevm_meta: vec![None; n],
196 uscore_name_idx: None,
197 sub_entry_at_ip: {
198 let mut v = vec![false; n];
199 for (_, e, _) in &self.sub_entries {
200 if *e < v.len() {
201 v[*e] = true;
202 }
203 }
204 v
205 },
206 sub_entry_invoke_count: vec![0; n],
207 jit_sub_invoke_threshold: self.jit_sub_invoke_threshold,
208 jit_buf_slot: Vec::new(),
209 jit_buf_plain: Vec::new(),
210 jit_buf_arg: Vec::new(),
211 jit_trampoline_out: None,
212 jit_trampoline_depth: 0,
213 halt: false,
214 try_stack: Vec::new(),
215 pending_catch_error: None,
216 exit_main_dispatch: false,
217 exit_main_dispatch_value: None,
218 static_sub_closure_subs: self.static_sub_closure_subs.clone(),
219 sub_entry_by_name: self.sub_entry_by_name.clone(),
220 block_region_mode: false,
221 block_region_end: 0,
222 block_region_return: None,
223 }
224 }
225}
226
227#[inline]
228fn vm_interp_result(r: Result<StrykeValue, FlowOrError>, line: usize) -> StrykeResult<StrykeValue> {
229 match r {
230 Ok(v) => Ok(v),
231 Err(FlowOrError::Error(e)) => Err(e),
232 Err(FlowOrError::Flow(_)) => Err(StrykeError::runtime(
233 "unexpected control flow in tree-assisted opcode",
234 line,
235 )),
236 }
237}
238
239#[derive(Debug, Clone, PartialEq)]
242pub(crate) enum TryState {
243 Trying,
245 Catching,
247 Finalizing,
249}
250
251#[derive(Debug, Clone)]
252pub(crate) struct TryFrame {
253 pub(crate) try_push_op_idx: usize,
254 pub(crate) state: TryState,
255 pub(crate) deferred_error: Option<StrykeError>,
258}
259
260#[derive(Debug)]
262struct CallFrame {
263 return_ip: usize,
264 stack_base: usize,
265 scope_depth: usize,
266 saved_wantarray: WantarrayCtx,
267 jit_trampoline_return: bool,
269 block_region: bool,
272 sub_profiler_start: Option<std::time::Instant>,
274}
275
276#[derive(Clone, Copy, PartialEq, Eq)]
281enum FusevmDispatch {
282 Int,
284 Float,
286 IntStr,
288 StrInt,
290 LitStrInt,
292 LitStrSprintf,
294 Str,
296 ValUnary,
298}
299
300#[derive(Clone)]
308struct FusevmSubElig {
309 dispatch: FusevmDispatch,
310 str_handle_slot: Option<u8>,
312 #[allow(dead_code)]
315 bypass_type_gate: bool,
316 cached_chunk: std::cell::OnceCell<std::sync::Arc<fusevm::Chunk>>,
324}
325
326fn compute_fusevm_elig(seg: &[Op], seg_ip: usize) -> Option<FusevmSubElig> {
334 use crate::fusevm_bridge as fb;
335 if fb::segment_is_fusevm_eligible(seg, seg_ip) {
336 return Some(FusevmSubElig {
337 dispatch: FusevmDispatch::Int,
338 str_handle_slot: None,
339 bypass_type_gate: false,
340 cached_chunk: std::cell::OnceCell::new(),
341 });
342 }
343 if fb::segment_is_string_compare_eligible(seg, seg_ip)
344 || fb::segment_is_string_concat_eligible(seg, seg_ip)
345 || fb::segment_is_string_unary_eligible(seg, seg_ip)
346 || fb::segment_is_string_binary_int_eligible(seg, seg_ip)
347 || fb::segment_is_string_bearing_int_result_eligible(seg, seg_ip)
348 {
349 return Some(FusevmSubElig {
350 dispatch: FusevmDispatch::Str,
351 str_handle_slot: None,
352 bypass_type_gate: false,
353 cached_chunk: std::cell::OnceCell::new(),
354 });
355 }
356 if fb::segment_is_any_value_unary_int_eligible(seg, seg_ip)
357 || fb::segment_is_any_value_unary_str_eligible(seg, seg_ip)
358 {
359 return Some(FusevmSubElig {
360 dispatch: FusevmDispatch::ValUnary,
361 str_handle_slot: None,
362 bypass_type_gate: true,
363 cached_chunk: std::cell::OnceCell::new(),
364 });
365 }
366 if fb::segment_is_fusevm_float_eligible(seg, seg_ip) {
367 return Some(FusevmSubElig {
368 dispatch: FusevmDispatch::Float,
369 str_handle_slot: None,
370 bypass_type_gate: false,
371 cached_chunk: std::cell::OnceCell::new(),
372 });
373 }
374 if fb::segment_is_int_to_string_eligible(seg, seg_ip) {
375 return Some(FusevmSubElig {
376 dispatch: FusevmDispatch::IntStr,
377 str_handle_slot: None,
378 bypass_type_gate: false,
379 cached_chunk: std::cell::OnceCell::new(),
380 });
381 }
382 if let Some(str_slot) = fb::string_handle_slot(seg, seg_ip) {
383 return Some(FusevmSubElig {
384 dispatch: FusevmDispatch::StrInt,
385 str_handle_slot: Some(str_slot),
386 bypass_type_gate: false,
387 cached_chunk: std::cell::OnceCell::new(),
388 });
389 }
390 if fb::segment_is_literal_string_int_to_string_eligible(seg, seg_ip) {
391 return Some(FusevmSubElig {
392 dispatch: FusevmDispatch::LitStrInt,
393 str_handle_slot: None,
394 bypass_type_gate: false,
395 cached_chunk: std::cell::OnceCell::new(),
396 });
397 }
398 if fb::segment_is_literal_string_anyval_sprintf_eligible(seg, seg_ip) {
399 return Some(FusevmSubElig {
400 dispatch: FusevmDispatch::LitStrSprintf,
401 str_handle_slot: None,
402 bypass_type_gate: true,
403 cached_chunk: std::cell::OnceCell::new(),
404 });
405 }
406 None
407}
408
409pub struct VM<'a> {
410 names: Arc<Vec<String>>,
412 constants: Arc<Vec<StrykeValue>>,
414 ops: Arc<Vec<Op>>,
416 lines: Arc<Vec<usize>>,
418 sub_entries: Vec<(u16, usize, bool)>,
420 static_sub_calls: Vec<(usize, bool, u16)>,
422 blocks: Vec<Block>,
424 code_ref_sigs: Vec<Vec<SubSigParam>>,
426 block_bytecode_ranges: Vec<Option<(usize, usize)>>,
428 map_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
430 grep_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
432 given_entries: Vec<(Expr, Block)>,
434 given_topic_bytecode_ranges: Vec<Option<(usize, usize)>>,
436 eval_timeout_entries: Vec<(Expr, Block)>,
438 eval_timeout_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
440 algebraic_match_entries: Vec<(Expr, Vec<MatchArm>)>,
442 algebraic_match_subject_bytecode_ranges: Vec<Option<(usize, usize)>>,
444 par_lines_entries: Vec<(Expr, Expr, Option<Expr>)>,
446 par_walk_entries: Vec<(Expr, Expr, Option<Expr>)>,
448 pwatch_entries: Vec<(Expr, Expr)>,
450 substr_four_arg_entries: Vec<(Expr, Expr, Option<Expr>, Expr)>,
452 keys_expr_entries: Vec<Expr>,
454 keys_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
456 map_expr_entries: Vec<Expr>,
458 grep_expr_entries: Vec<Expr>,
460 regex_flip_flop_rhs_expr_entries: Vec<Expr>,
462 regex_flip_flop_rhs_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
464 values_expr_entries: Vec<Expr>,
466 values_expr_bytecode_ranges: Vec<Option<(usize, usize)>>,
468 delete_expr_entries: Vec<Expr>,
470 exists_expr_entries: Vec<Expr>,
472 push_expr_entries: Vec<(Expr, Vec<Expr>)>,
474 pop_expr_entries: Vec<Expr>,
476 shift_expr_entries: Vec<Expr>,
478 unshift_expr_entries: Vec<(Expr, Vec<Expr>)>,
480 splice_expr_entries: Vec<SpliceExprEntry>,
482 lvalues: Vec<Expr>,
484 ast_eval_exprs: Vec<Expr>,
486 format_decls: Vec<(String, Vec<String>)>,
488 use_overload_entries: Vec<Vec<(String, String)>>,
490 runtime_sub_decls: Arc<Vec<RuntimeSubDecl>>,
492 runtime_advice_decls: Arc<Vec<crate::bytecode::RuntimeAdviceDecl>>,
494 pub(crate) ip: usize,
495 stack: Vec<StrykeValue>,
497 call_stack: Vec<CallFrame>,
499 wantarray_stack: Vec<WantarrayCtx>,
501 interp: &'a mut VMHelper,
503 jit_enabled: bool,
506 sub_jit_skip_linear: Vec<bool>,
509 sub_jit_skip_block: Vec<bool>,
511 sub_fusevm_meta: Vec<Option<Option<FusevmSubElig>>>,
519 uscore_name_idx: Option<Option<u16>>,
523 sub_entry_at_ip: Vec<bool>,
525 sub_entry_invoke_count: Vec<u32>,
527 jit_sub_invoke_threshold: u32,
529 jit_buf_slot: Vec<i64>,
531 jit_buf_plain: Vec<i64>,
533 jit_buf_arg: Vec<i64>,
535 jit_trampoline_out: Option<StrykeValue>,
537 jit_trampoline_depth: u32,
539 halt: bool,
541 try_stack: Vec<TryFrame>,
543 pub(crate) pending_catch_error: Option<StrykeValue>,
547 exit_main_dispatch: bool,
549 exit_main_dispatch_value: Option<StrykeValue>,
551 static_sub_closure_subs: Vec<Option<Arc<StrykeSub>>>,
553 sub_entry_by_name: HashMap<u16, (usize, bool)>,
555 block_region_mode: bool,
557 block_region_end: usize,
559 block_region_return: Option<StrykeValue>,
561}
562
563impl<'a> VM<'a> {
564 pub fn new(chunk: &Chunk, interp: &'a mut VMHelper) -> Self {
566 let static_sub_closure_subs: Vec<Option<Arc<StrykeSub>>> = chunk
567 .static_sub_calls
568 .iter()
569 .map(|(_, _, name_idx)| {
570 let nm = chunk.names[*name_idx as usize].as_str();
571 interp.subs.get(nm).cloned()
572 })
573 .collect();
574 let mut sub_entry_by_name = HashMap::with_capacity(chunk.sub_entries.len());
575 for &(n, ip, sa) in &chunk.sub_entries {
576 sub_entry_by_name.entry(n).or_insert((ip, sa));
577 }
578 Self {
579 names: Arc::new(chunk.names.clone()),
580 constants: Arc::new(chunk.constants.clone()),
581 ops: Arc::new(chunk.ops.clone()),
582 lines: Arc::new(chunk.lines.clone()),
583 sub_entries: chunk.sub_entries.clone(),
584 static_sub_calls: chunk.static_sub_calls.clone(),
585 blocks: chunk.blocks.clone(),
586 code_ref_sigs: chunk.code_ref_sigs.clone(),
587 block_bytecode_ranges: chunk.block_bytecode_ranges.clone(),
588 map_expr_bytecode_ranges: chunk.map_expr_bytecode_ranges.clone(),
589 grep_expr_bytecode_ranges: chunk.grep_expr_bytecode_ranges.clone(),
590 regex_flip_flop_rhs_expr_bytecode_ranges: chunk
591 .regex_flip_flop_rhs_expr_bytecode_ranges
592 .clone(),
593 given_entries: chunk.given_entries.clone(),
594 given_topic_bytecode_ranges: chunk.given_topic_bytecode_ranges.clone(),
595 eval_timeout_entries: chunk.eval_timeout_entries.clone(),
596 eval_timeout_expr_bytecode_ranges: chunk.eval_timeout_expr_bytecode_ranges.clone(),
597 algebraic_match_entries: chunk.algebraic_match_entries.clone(),
598 algebraic_match_subject_bytecode_ranges: chunk
599 .algebraic_match_subject_bytecode_ranges
600 .clone(),
601 par_lines_entries: chunk.par_lines_entries.clone(),
602 par_walk_entries: chunk.par_walk_entries.clone(),
603 pwatch_entries: chunk.pwatch_entries.clone(),
604 substr_four_arg_entries: chunk.substr_four_arg_entries.clone(),
605 keys_expr_entries: chunk.keys_expr_entries.clone(),
606 keys_expr_bytecode_ranges: chunk.keys_expr_bytecode_ranges.clone(),
607 map_expr_entries: chunk.map_expr_entries.clone(),
608 grep_expr_entries: chunk.grep_expr_entries.clone(),
609 regex_flip_flop_rhs_expr_entries: chunk.regex_flip_flop_rhs_expr_entries.clone(),
610 values_expr_entries: chunk.values_expr_entries.clone(),
611 values_expr_bytecode_ranges: chunk.values_expr_bytecode_ranges.clone(),
612 delete_expr_entries: chunk.delete_expr_entries.clone(),
613 exists_expr_entries: chunk.exists_expr_entries.clone(),
614 push_expr_entries: chunk.push_expr_entries.clone(),
615 pop_expr_entries: chunk.pop_expr_entries.clone(),
616 shift_expr_entries: chunk.shift_expr_entries.clone(),
617 unshift_expr_entries: chunk.unshift_expr_entries.clone(),
618 splice_expr_entries: chunk.splice_expr_entries.clone(),
619 lvalues: chunk.lvalues.clone(),
620 ast_eval_exprs: chunk.ast_eval_exprs.clone(),
621 format_decls: chunk.format_decls.clone(),
622 use_overload_entries: chunk.use_overload_entries.clone(),
623 runtime_sub_decls: Arc::new(chunk.runtime_sub_decls.clone()),
624 runtime_advice_decls: Arc::new(chunk.runtime_advice_decls.clone()),
625 ip: 0,
626 stack: Vec::with_capacity(256),
627 call_stack: Vec::with_capacity(32),
628 wantarray_stack: Vec::with_capacity(8),
629 interp,
630 jit_enabled: true,
631 sub_jit_skip_linear: vec![false; chunk.ops.len().saturating_add(1)],
632 sub_jit_skip_block: vec![false; chunk.ops.len().saturating_add(1)],
633 sub_fusevm_meta: vec![None; chunk.ops.len().saturating_add(1)],
634 uscore_name_idx: None,
635 sub_entry_at_ip: {
636 let mut v = vec![false; chunk.ops.len().saturating_add(1)];
637 for (_, e, _) in &chunk.sub_entries {
638 if *e < v.len() {
639 v[*e] = true;
640 }
641 }
642 v
643 },
644 sub_entry_invoke_count: vec![0; chunk.ops.len().saturating_add(1)],
645 jit_sub_invoke_threshold: std::env::var("STRYKE_JIT_SUB_INVOKES")
646 .ok()
647 .and_then(|s| s.parse().ok())
648 .unwrap_or(50),
649 jit_buf_slot: Vec::new(),
650 jit_buf_plain: Vec::new(),
651 jit_buf_arg: Vec::new(),
652 jit_trampoline_out: None,
653 jit_trampoline_depth: 0,
654 halt: false,
655 try_stack: Vec::new(),
656 pending_catch_error: None,
657 exit_main_dispatch: false,
658 exit_main_dispatch_value: None,
659 static_sub_closure_subs,
660 sub_entry_by_name,
661 block_region_mode: false,
662 block_region_end: 0,
663 block_region_return: None,
664 }
665 }
666
667 fn unwind_stale_block_region_frame(&mut self) {
670 if let Some(frame) = self.call_stack.pop() {
671 if frame.block_region {
672 self.interp.wantarray_kind = frame.saved_wantarray;
673 self.stack.truncate(frame.stack_base);
674 self.interp.pop_scope_to_depth(frame.scope_depth);
675 } else {
676 self.call_stack.push(frame);
677 }
678 }
679 }
680
681 fn run_block_region(
688 &mut self,
689 start: usize,
690 end: usize,
691 op_count: &mut u64,
692 ) -> StrykeResult<StrykeValue> {
693 if let Some(v) = self.try_fusevm_block_region(start, end)? {
703 *op_count = op_count.saturating_add(1);
706 return Ok(v);
707 }
708
709 let resume_ip = self.ip;
710 let saved_mode = self.block_region_mode;
711 let saved_end = self.block_region_end;
712 let saved_ret = self.block_region_return.take();
713
714 let scope_depth_before = self.interp.scope.depth();
715 let saved_wa = self.interp.wantarray_kind;
716
717 self.call_stack.push(CallFrame {
718 return_ip: 0,
719 stack_base: self.stack.len(),
720 scope_depth: scope_depth_before,
721 saved_wantarray: saved_wa,
722 jit_trampoline_return: false,
723 block_region: true,
724 sub_profiler_start: None,
725 });
726 self.interp.scope_push_hook();
727 self.interp.wantarray_kind = WantarrayCtx::Scalar;
728 self.ip = start;
729 self.block_region_mode = true;
730 self.block_region_end = end;
731 self.block_region_return = None;
732
733 let r = self.run_main_dispatch_loop(StrykeValue::UNDEF, op_count, false);
734 let out = self.block_region_return.take();
735
736 self.block_region_return = saved_ret;
737 self.block_region_mode = saved_mode;
738 self.block_region_end = saved_end;
739 self.ip = resume_ip;
740
741 match r {
742 Ok(_) => {
743 if let Some(val) = out {
744 Ok(val)
745 } else {
746 self.unwind_stale_block_region_frame();
747 Err(StrykeError::runtime(
748 "block bytecode region did not finish with BlockReturnValue",
749 self.line(),
750 ))
751 }
752 }
753 Err(e) => {
754 self.unwind_stale_block_region_frame();
755 Err(e)
756 }
757 }
758 }
759
760 #[inline]
761 fn extend_map_outputs(dst: &mut Vec<StrykeValue>, val: StrykeValue, peel_array_ref: bool) {
762 dst.extend(val.map_flatten_outputs(peel_array_ref));
763 }
764
765 fn map_with_block_common(
766 &mut self,
767 list: Vec<StrykeValue>,
768 block_idx: u16,
769 peel_array_ref: bool,
770 op_count: &mut u64,
771 ) -> StrykeResult<()> {
772 if list.len() == 1 {
773 if let Some(p) = list[0].as_pipeline() {
774 if peel_array_ref {
775 return Err(StrykeError::runtime(
776 "flat_map onto a pipeline value is not supported in this form — use a pipeline ->map stage",
777 self.line(),
778 ));
779 }
780 let idx = block_idx as usize;
781 let sub = self.interp.anon_coderef_from_block(&self.blocks[idx]);
782 let line = self.line();
783 self.interp.pipeline_push(&p, PipelineOp::Map(sub), line)?;
784 self.push(StrykeValue::pipeline(Arc::clone(&p)));
785 return Ok(());
786 }
787 }
788 let idx = block_idx as usize;
789 let block_tail_is_list_sensitive = self
796 .blocks
797 .get(idx)
798 .and_then(|b| b.last())
799 .map(|stmt| match &stmt.kind {
800 crate::ast::StmtKind::Expression(expr) => {
801 crate::compiler::expr_tail_is_list_sensitive(expr)
802 }
803 _ => true,
804 })
805 .unwrap_or(true);
806 if !block_tail_is_list_sensitive {
807 if let Some(&(start, end)) =
808 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
809 {
810 let saved_chain = self.interp.scope.save_topic_chain();
817 let mut result = Vec::new();
818 for item in list {
819 self.interp.scope.set_topic(item);
820 let val = self.run_block_region(start, end, op_count)?;
821 Self::extend_map_outputs(&mut result, val, peel_array_ref);
822 }
823 self.interp.scope.restore_topic_chain(saved_chain);
824 self.push(StrykeValue::array(result));
825 return Ok(());
826 }
827 }
828 let block = self.blocks[idx].clone();
829 let saved_chain = self.interp.scope.save_topic_chain();
830 let mut result = Vec::new();
831 for item in list {
832 self.interp.scope.set_topic(item);
833 match self.interp.exec_block_with_tail(&block, WantarrayCtx::List) {
834 Ok(val) => Self::extend_map_outputs(&mut result, val, peel_array_ref),
835 Err(FlowOrError::Error(e)) => {
836 self.interp.scope.restore_topic_chain(saved_chain);
837 return Err(e);
838 }
839 Err(_) => {}
840 }
841 }
842 self.interp.scope.restore_topic_chain(saved_chain);
843 self.push(StrykeValue::array(result));
844 Ok(())
845 }
846
847 fn map_with_expr_common(
848 &mut self,
849 list: Vec<StrykeValue>,
850 expr_idx: u16,
851 peel_array_ref: bool,
852 op_count: &mut u64,
853 ) -> StrykeResult<()> {
854 let idx = expr_idx as usize;
855 let dispatch_coderef = !crate::compat_mode();
856 if let Some(&(start, end)) = self
861 .map_expr_bytecode_ranges
862 .get(idx)
863 .and_then(|r| r.as_ref())
864 {
865 let mut result = Vec::new();
866 for item in list {
867 self.interp.scope.set_topic_local(item.clone());
868 let val = self.run_block_region(start, end, op_count)?;
869 let val = self.maybe_call_coderef_with_item(val, &item, dispatch_coderef)?;
870 Self::extend_map_outputs(&mut result, val, peel_array_ref);
871 }
872 self.push(StrykeValue::array(result));
873 } else {
874 let e = self.map_expr_entries[idx].clone();
875 let mut result = Vec::new();
876 for item in list {
877 self.interp.scope.set_topic_local(item.clone());
878 let val = vm_interp_result(
879 self.interp.eval_expr_ctx(&e, WantarrayCtx::List),
880 self.line(),
881 )?;
882 let val = self.maybe_call_coderef_with_item(val, &item, dispatch_coderef)?;
883 Self::extend_map_outputs(&mut result, val, peel_array_ref);
884 }
885 self.push(StrykeValue::array(result));
886 }
887 Ok(())
888 }
889
890 fn maybe_call_coderef_with_item(
896 &mut self,
897 val: StrykeValue,
898 item: &StrykeValue,
899 dispatch: bool,
900 ) -> StrykeResult<StrykeValue> {
901 if !dispatch {
902 return Ok(val);
903 }
904 if let Some(sub) = val.as_code_ref() {
905 let sub = sub.clone();
906 let line = self.line();
907 return vm_interp_result(
908 self.interp
909 .call_sub(&sub, vec![item.clone()], WantarrayCtx::Scalar, line),
910 line,
911 );
912 }
913 Ok(val)
914 }
915
916 fn chunk_by_with_block_common(
918 &mut self,
919 list: Vec<StrykeValue>,
920 block_idx: u16,
921 op_count: &mut u64,
922 ) -> StrykeResult<()> {
923 if list.is_empty() {
924 self.push(StrykeValue::array(vec![]));
925 return Ok(());
926 }
927 let idx = block_idx as usize;
928 let mut chunks: Vec<StrykeValue> = Vec::new();
929 let mut run: Vec<StrykeValue> = Vec::new();
930 let mut prev_key: Option<StrykeValue> = None;
931
932 let eval_key =
933 |vm: &mut VM, item: StrykeValue, op_count: &mut u64| -> StrykeResult<StrykeValue> {
934 vm.interp.scope.set_topic(item);
935 if let Some(&(start, end)) =
936 vm.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
937 {
938 vm.run_block_region(start, end, op_count)
939 } else {
940 let block = vm.blocks[idx].clone();
941 match vm.interp.exec_block(&block) {
942 Ok(val) => Ok(val),
943 Err(FlowOrError::Error(e)) => Err(e),
944 Err(FlowOrError::Flow(Flow::Return(v))) => Ok(v),
945 Err(_) => Ok(StrykeValue::UNDEF),
946 }
947 }
948 };
949
950 for item in list {
951 let key = eval_key(self, item.clone(), op_count)?;
952 match &prev_key {
953 None => {
954 run.push(item);
955 prev_key = Some(key);
956 }
957 Some(pk) => {
958 if key.str_eq(pk) {
959 run.push(item);
960 } else {
961 chunks.push(StrykeValue::array_ref(Arc::new(RwLock::new(
962 std::mem::take(&mut run),
963 ))));
964 run.push(item);
965 prev_key = Some(key);
966 }
967 }
968 }
969 }
970 if !run.is_empty() {
971 chunks.push(StrykeValue::array_ref(Arc::new(RwLock::new(run))));
972 }
973 self.push(StrykeValue::array(chunks));
974 Ok(())
975 }
976
977 fn chunk_by_with_expr_common(
978 &mut self,
979 list: Vec<StrykeValue>,
980 expr_idx: u16,
981 op_count: &mut u64,
982 ) -> StrykeResult<()> {
983 if list.is_empty() {
984 self.push(StrykeValue::array(vec![]));
985 return Ok(());
986 }
987 let idx = expr_idx as usize;
988 let mut chunks: Vec<StrykeValue> = Vec::new();
989 let mut run: Vec<StrykeValue> = Vec::new();
990 let mut prev_key: Option<StrykeValue> = None;
991 for item in list {
992 self.interp.scope.set_topic(item.clone());
993 let key = if let Some(&(start, end)) = self
994 .map_expr_bytecode_ranges
995 .get(idx)
996 .and_then(|r| r.as_ref())
997 {
998 self.run_block_region(start, end, op_count)?
999 } else {
1000 let e = &self.map_expr_entries[idx];
1001 vm_interp_result(
1002 self.interp.eval_expr_ctx(e, WantarrayCtx::Scalar),
1003 self.line(),
1004 )?
1005 };
1006 match &prev_key {
1007 None => {
1008 run.push(item);
1009 prev_key = Some(key);
1010 }
1011 Some(pk) => {
1012 if key.str_eq(pk) {
1013 run.push(item);
1014 } else {
1015 chunks.push(StrykeValue::array_ref(Arc::new(RwLock::new(
1016 std::mem::take(&mut run),
1017 ))));
1018 run.push(item);
1019 prev_key = Some(key);
1020 }
1021 }
1022 }
1023 }
1024 if !run.is_empty() {
1025 chunks.push(StrykeValue::array_ref(Arc::new(RwLock::new(run))));
1026 }
1027 self.push(StrykeValue::array(chunks));
1028 Ok(())
1029 }
1030
1031 #[inline]
1032 fn sub_jit_skip_linear_test(&self, ip: usize) -> bool {
1033 self.sub_jit_skip_linear.get(ip).copied().unwrap_or(false)
1034 }
1035
1036 #[inline]
1037 fn sub_jit_skip_linear_mark(&mut self, ip: usize) {
1038 if ip >= self.sub_jit_skip_linear.len() {
1039 self.sub_jit_skip_linear.resize(ip + 1, false);
1040 }
1041 self.sub_jit_skip_linear[ip] = true;
1042 }
1043
1044 #[inline]
1045 fn sub_jit_skip_block_test(&self, ip: usize) -> bool {
1046 self.sub_jit_skip_block.get(ip).copied().unwrap_or(false)
1047 }
1048
1049 #[inline]
1050 fn sub_jit_skip_block_mark(&mut self, ip: usize) {
1051 if ip >= self.sub_jit_skip_block.len() {
1052 self.sub_jit_skip_block.resize(ip + 1, false);
1053 }
1054 self.sub_jit_skip_block[ip] = true;
1055 }
1056
1057 pub fn set_jit_enabled(&mut self, enabled: bool) {
1060 self.jit_enabled = enabled;
1061 }
1062
1063 #[inline]
1064 fn push(&mut self, val: StrykeValue) {
1065 self.stack.push(val);
1066 }
1067
1068 #[inline]
1069 fn pop(&mut self) -> StrykeValue {
1070 self.stack.pop().unwrap_or(StrykeValue::UNDEF)
1071 }
1072
1073 fn resolve_binding_ref(&self, val: StrykeValue) -> StrykeValue {
1078 if let Some(name) = val.as_array_binding_name() {
1079 let data = self.interp.scope.get_array(&name);
1080 return StrykeValue::array_ref(Arc::new(RwLock::new(data)));
1081 }
1082 if let Some(name) = val.as_hash_binding_name() {
1083 let data = self.interp.scope.get_hash(&name);
1084 return StrykeValue::hash_ref(Arc::new(RwLock::new(data)));
1085 }
1086 if let Some(name) = val.as_scalar_binding_name() {
1087 let data = self.interp.scope.get_scalar(&name);
1088 return StrykeValue::scalar_ref(Arc::new(RwLock::new(data)));
1089 }
1090 val
1091 }
1092
1093 fn pop_flattened_array_slice_specs(&mut self, n: usize) -> Vec<i64> {
1098 let mut chunks: Vec<Vec<i64>> = Vec::with_capacity(n);
1099 for _ in 0..n {
1100 let spec = self.pop();
1101 let mut flat = Vec::new();
1102 if let Some(av) = spec.as_array_vec() {
1103 for pv in av.iter() {
1104 flat.push(pv.to_int());
1105 }
1106 } else {
1107 flat.push(spec.to_int());
1108 }
1109 chunks.push(flat);
1110 }
1111 chunks.reverse();
1112 chunks.into_iter().flatten().collect()
1113 }
1114
1115 fn pop_call_operands_flattened(&mut self, argc: usize) -> Vec<StrykeValue> {
1120 let mut slots = Vec::with_capacity(argc);
1121 for _ in 0..argc {
1122 slots.push(self.pop());
1123 }
1124 slots.reverse();
1125 let mut out = Vec::new();
1126 for v in slots {
1127 if let Some(items) = v.as_array_vec() {
1128 out.extend(items);
1129 } else if let Some(h) = v.as_hash_map() {
1130 for (k, val) in h {
1131 out.push(StrykeValue::string(k));
1132 out.push(val);
1133 }
1134 } else {
1135 out.push(v);
1136 }
1137 }
1138 out
1139 }
1140
1141 fn pop_call_operands_preserved(&mut self, argc: usize) -> Vec<StrykeValue> {
1144 let mut slots = Vec::with_capacity(argc);
1145 for _ in 0..argc {
1146 slots.push(self.pop());
1147 }
1148 slots.reverse();
1149 slots
1150 }
1151
1152 #[inline]
1153 fn call_preserve_operand_arrays(name: &str) -> bool {
1154 let name = name.strip_prefix("CORE::").unwrap_or(name);
1156 matches!(
1157 name,
1158 "zip"
1159 | "zip_longest"
1160 | "zip_shortest"
1161 | "mesh"
1162 | "mesh_longest"
1163 | "mesh_shortest"
1164 | "take"
1165 | "head"
1166 | "tail"
1167 | "drop"
1168 | "len"
1172 | "cnt"
1173 | "count"
1174 | "list_count"
1175 | "list_size"
1176 )
1177 }
1178
1179 fn flatten_array_slice_specs_ordered_values(
1180 &self,
1181 specs: &[StrykeValue],
1182 ) -> Result<Vec<i64>, StrykeError> {
1183 let mut out = Vec::new();
1184 for spec in specs {
1185 if let Some(av) = spec.as_array_vec() {
1186 for pv in av.iter() {
1187 out.push(pv.to_int());
1188 }
1189 } else {
1190 out.push(spec.to_int());
1191 }
1192 }
1193 Ok(out)
1194 }
1195
1196 fn flatten_hash_slice_key_slots(key_vals: &[StrykeValue]) -> Vec<String> {
1198 let mut ks = Vec::new();
1199 for kv in key_vals {
1200 if let Some(vv) = kv.as_array_vec() {
1201 ks.extend(vv.iter().map(|x| x.to_string()));
1202 } else {
1203 ks.push(kv.to_string());
1204 }
1205 }
1206 ks
1207 }
1208
1209 #[inline]
1210 fn peek(&self) -> &StrykeValue {
1211 self.stack.last().unwrap_or(&PEEK_UNDEF)
1212 }
1213
1214 #[inline]
1215 fn constant(&self, idx: u16) -> &StrykeValue {
1216 &self.constants[idx as usize]
1217 }
1218
1219 fn line(&self) -> usize {
1220 self.lines
1221 .get(self.ip.saturating_sub(1))
1222 .copied()
1223 .unwrap_or(0)
1224 }
1225
1226 fn try_fusevm_subroutine(&mut self) -> Result<bool, StrykeError> {
1236 let ip = self.ip;
1237 debug_assert!(self.sub_entry_at_ip.get(ip).copied().unwrap_or(false));
1238 let ops: &Vec<Op> = &self.ops;
1239 let ops = ops as *const Vec<Op>;
1240 let ops = unsafe { &*ops };
1241 let Some((full_seg, term)) = crate::jit::sub_entry_segment(ops, ip) else {
1242 return Ok(false);
1243 };
1244 if !matches!(term, crate::jit::SubTerminator::Value) {
1245 return Ok(false);
1246 }
1247
1248 let uscore = match self.uscore_name_idx {
1255 Some(cached) => cached,
1256 None => {
1257 let v = self.names.iter().position(|n| n == "_").and_then(|p| u16::try_from(p).ok());
1258 self.uscore_name_idx = Some(v);
1259 v
1260 }
1261 };
1262 let (seg, seg_ip, arg_binds): (&[Op], usize, Vec<(u8, usize)>) = match uscore
1263 .and_then(|u| crate::jit::recognize_args_unpack_prologue(full_seg, u))
1264 {
1265 Some((plen, binds)) => (&full_seg[plen..], ip + plen, binds),
1266 None => (full_seg, ip, Vec::new()),
1267 };
1268
1269 let base_slot = crate::jit::linear_slot_ops_max_index_seq(seg)
1276 .map(|m| m as usize + 1)
1277 .unwrap_or(0);
1278 let plain_remap: Option<(Vec<Op>, Vec<(u8, u16)>)> = if base_slot <= u8::MAX as usize {
1279 crate::jit::plain_scalar_read_names(seg).and_then(|pnames| {
1280 if base_slot + pnames.len() <= u8::MAX as usize + 1 {
1281 Some(crate::jit::remap_plain_reads_to_slots(
1282 seg,
1283 &pnames,
1284 base_slot as u8,
1285 ))
1286 } else {
1287 None
1288 }
1289 })
1290 } else {
1291 None
1292 };
1293 let (seg, plain_binds): (&[Op], &[(u8, u16)]) = match &plain_remap {
1294 Some((normalized, binds)) => (normalized.as_slice(), binds.as_slice()),
1295 None => (seg, &[][..]),
1296 };
1297
1298 let cache_ip = ip;
1307 if cache_ip >= self.sub_fusevm_meta.len() {
1308 self.sub_fusevm_meta.resize(cache_ip + 1, None);
1309 }
1310 match &self.sub_fusevm_meta[cache_ip] {
1316 Some(None) => return Ok(false),
1317 Some(Some(_)) => {}
1318 None => {
1319 let computed = compute_fusevm_elig(seg, seg_ip);
1320 self.sub_fusevm_meta[cache_ip] = Some(computed);
1321 if matches!(&self.sub_fusevm_meta[cache_ip], Some(None)) {
1322 return Ok(false);
1323 }
1324 }
1325 }
1326 let (dispatch, str_handle_slot) = {
1328 let e = self.sub_fusevm_meta[cache_ip]
1329 .as_ref()
1330 .and_then(|x| x.as_ref())
1331 .expect("populated above");
1332 (e.dispatch, e.str_handle_slot)
1333 };
1334 let str_ok = matches!(dispatch, FusevmDispatch::Str);
1335 let lit_str_sprintf_ok = matches!(dispatch, FusevmDispatch::LitStrSprintf);
1336 let val_unary_ok = matches!(dispatch, FusevmDispatch::ValUnary);
1337
1338 let arg_of_slot = |slot: u8| -> Option<usize> {
1342 arg_binds.iter().find(|(s, _)| *s == slot).map(|(_, a)| *a)
1343 };
1344 let argv: Vec<StrykeValue> = if arg_binds.is_empty() {
1347 Vec::new()
1348 } else {
1349 self.interp.scope.get_array("_")
1350 };
1351
1352 let plain_vals: Vec<(u8, StrykeValue)> = plain_binds
1356 .iter()
1357 .map(|(slot, name_idx)| {
1358 let nm = self.names[*name_idx as usize].clone();
1359 (*slot, self.interp.scope.get_scalar(&nm))
1360 })
1361 .collect();
1362 let plain_of_slot = |slot: u8| -> Option<&StrykeValue> {
1363 plain_vals.iter().find(|(s, _)| *s == slot).map(|(_, v)| v)
1364 };
1365
1366 let mut slot_n = 0usize;
1376 if let Some(max) = crate::jit::linear_slot_ops_max_index_seq(seg) {
1377 let n = max as usize + 1;
1378 self.jit_buf_slot.resize(n, 0);
1379 let str_slot_kinds: Option<Vec<bool>> = if str_ok {
1386 crate::fusevm_bridge::string_bearing_int_result_slot_kinds(seg, seg_ip)
1387 } else {
1388 None
1389 };
1390 let wants_string = |i: u8| -> bool {
1400 match str_handle_slot {
1401 Some(str_slot) => i == str_slot,
1402 None => match &str_slot_kinds {
1403 Some(kinds) => kinds.get(i as usize).copied().unwrap_or(false),
1404 None => str_ok || val_unary_ok || lit_str_sprintf_ok,
1405 },
1406 }
1407 };
1408 let bypass_type_gate = val_unary_ok || lit_str_sprintf_ok;
1413 for i in 0..=max {
1414 let bound = arg_of_slot(i);
1415 self.jit_buf_slot[i as usize] = if let Some(pv) = plain_of_slot(i) {
1416 if wants_string(i) {
1417 if !bypass_type_gate && !pv.is_string_like() {
1418 return Ok(false);
1419 }
1420 pv.raw_bits() as i64
1421 } else {
1422 match pv.as_integer() {
1423 Some(v) => v,
1424 None => return Ok(false),
1425 }
1426 }
1427 } else if wants_string(i) {
1428 let v = match bound {
1440 Some(a) => match argv.get(a) {
1441 Some(vr) => vr.shallow_clone(),
1442 None => StrykeValue::UNDEF,
1443 },
1444 None => self.interp.scope.get_scalar_slot(i),
1445 };
1446 if !bypass_type_gate && !v.is_string_like() {
1447 return Ok(false);
1448 }
1449 v.raw_bits() as i64
1450 } else if let Some(a) = bound {
1451 match argv.get(a).and_then(|v| v.as_integer()) {
1452 Some(v) => v,
1453 None => return Ok(false),
1454 }
1455 } else if crate::jit::slot_undef_prefill_ok_seq(seg, i) {
1456 0
1457 } else {
1458 match self.interp.scope.get_scalar_slot(i).as_integer() {
1459 Some(v) => v,
1460 None => return Ok(false),
1461 }
1462 };
1463 }
1464 slot_n = n;
1465 }
1466
1467 crate::fusevm_bridge::set_utf8_pragma(self.interp.utf8_pragma);
1472 let cached_chunk_arc: Option<std::sync::Arc<fusevm::Chunk>> = self
1477 .sub_fusevm_meta
1478 .get(cache_ip)
1479 .and_then(|x| x.as_ref())
1480 .and_then(|x| x.as_ref())
1481 .and_then(|e| e.cached_chunk.get().cloned());
1482 let (result_opt, fresh_chunk) = crate::fusevm_bridge::run_linear_segment_cached(
1483 seg,
1484 seg_ip,
1485 &mut self.jit_buf_slot[..slot_n],
1486 term,
1487 &self.constants,
1488 cached_chunk_arc.as_ref(),
1489 );
1490 if let Some(fresh) = fresh_chunk {
1491 if let Some(e) = self
1492 .sub_fusevm_meta
1493 .get(cache_ip)
1494 .and_then(|x| x.as_ref())
1495 .and_then(|x| x.as_ref())
1496 {
1497 let _ = e.cached_chunk.set(fresh);
1498 }
1499 }
1500 let Some(v) = result_opt else {
1501 return Ok(false);
1502 };
1503
1504 if let Some(frame) = self.call_stack.pop() {
1512 self.interp.wantarray_kind = frame.saved_wantarray;
1513 self.stack.truncate(frame.stack_base);
1514 self.interp.pop_scope_to_depth(frame.scope_depth);
1515 if frame.jit_trampoline_return {
1516 self.jit_trampoline_out = Some(v);
1517 } else {
1518 self.push(v);
1519 self.ip = frame.return_ip;
1520 }
1521 }
1522 Ok(true)
1523 }
1524
1525 fn try_fusevm_block_region(
1548 &mut self,
1549 start: usize,
1550 end: usize,
1551 ) -> Result<Option<StrykeValue>, StrykeError> {
1552 let ops: &Vec<Op> = &self.ops;
1553 let ops = ops as *const Vec<Op>;
1554 let ops = unsafe { &*ops };
1555
1556 if start >= end || end > ops.len() {
1561 return Ok(None);
1562 }
1563 if !matches!(ops.get(end - 1), Some(Op::BlockReturnValue)) {
1564 return Ok(None);
1565 }
1566 let full_seg = &ops[start..end - 1];
1567 if full_seg.is_empty() {
1568 return Ok(None);
1569 }
1570
1571 for op in full_seg {
1602 match op {
1603 Op::SetScalar(_)
1604 | Op::SetScalarPlain(_)
1605 | Op::SetScalarKeep(_)
1606 | Op::SetScalarKeepPlain(_)
1607 | Op::PreInc(_)
1608 | Op::PreDec(_)
1609 | Op::PostInc(_)
1610 | Op::PostDec(_)
1611 | Op::ScalarCompoundAssign { .. }
1612 | Op::SetScalarSlot(_)
1613 | Op::SetScalarSlotKeep(_)
1614 | Op::PreIncSlot(_)
1615 | Op::PreIncSlotVoid(_)
1616 | Op::PostIncSlot(_)
1617 | Op::PreDecSlot(_)
1618 | Op::PostDecSlot(_)
1619 | Op::AddAssignSlotSlot(_, _)
1620 | Op::AddAssignSlotSlotVoid(_, _) => return Ok(None),
1621 Op::StrEq | Op::StrNe => return Ok(None),
1634 _ => {}
1635 }
1636 }
1637
1638 let base_slot = crate::jit::linear_slot_ops_max_index_seq(full_seg)
1642 .map(|m| m as usize + 1)
1643 .unwrap_or(0);
1644 let plain_remap: Option<(Vec<Op>, Vec<(u8, u16)>)> = if base_slot <= u8::MAX as usize {
1645 crate::jit::plain_scalar_read_names(full_seg).and_then(|pnames| {
1646 if base_slot + pnames.len() <= u8::MAX as usize + 1 {
1647 Some(crate::jit::remap_plain_reads_to_slots(
1648 full_seg,
1649 &pnames,
1650 base_slot as u8,
1651 ))
1652 } else {
1653 None
1654 }
1655 })
1656 } else {
1657 None
1658 };
1659 let (seg, plain_binds): (&[Op], &[(u8, u16)]) = match &plain_remap {
1660 Some((normalized, binds)) => (normalized.as_slice(), binds.as_slice()),
1661 None => (full_seg, &[][..]),
1662 };
1663 let seg_ip = start;
1664
1665 let cache_ip = start;
1668 if cache_ip >= self.sub_fusevm_meta.len() {
1669 self.sub_fusevm_meta.resize(cache_ip + 1, None);
1670 }
1671 match &self.sub_fusevm_meta[cache_ip] {
1672 Some(None) => return Ok(None),
1673 Some(Some(_)) => {}
1674 None => {
1675 let computed = compute_fusevm_elig(seg, seg_ip);
1676 self.sub_fusevm_meta[cache_ip] = Some(computed);
1677 if matches!(&self.sub_fusevm_meta[cache_ip], Some(None)) {
1678 return Ok(None);
1679 }
1680 }
1681 }
1682 let (dispatch, str_handle_slot) = {
1683 let e = self.sub_fusevm_meta[cache_ip]
1684 .as_ref()
1685 .and_then(|x| x.as_ref())
1686 .expect("populated above");
1687 (e.dispatch, e.str_handle_slot)
1688 };
1689 let str_ok = matches!(dispatch, FusevmDispatch::Str);
1690 let lit_str_sprintf_ok = matches!(dispatch, FusevmDispatch::LitStrSprintf);
1691 let val_unary_ok = matches!(dispatch, FusevmDispatch::ValUnary);
1692
1693 let plain_vals: Vec<(u8, StrykeValue)> = plain_binds
1697 .iter()
1698 .map(|(slot, name_idx)| {
1699 let nm = self.names[*name_idx as usize].clone();
1700 (*slot, self.interp.scope.get_scalar(&nm))
1701 })
1702 .collect();
1703 let plain_of_slot = |slot: u8| -> Option<&StrykeValue> {
1704 plain_vals.iter().find(|(s, _)| *s == slot).map(|(_, v)| v)
1705 };
1706
1707 let mut slot_n = 0usize;
1711 if let Some(max) = crate::jit::linear_slot_ops_max_index_seq(seg) {
1712 let n = max as usize + 1;
1713 self.jit_buf_slot.resize(n, 0);
1714 let str_slot_kinds: Option<Vec<bool>> = if str_ok {
1715 crate::fusevm_bridge::string_bearing_int_result_slot_kinds(seg, seg_ip)
1716 } else {
1717 None
1718 };
1719 let wants_string = |i: u8| -> bool {
1720 match str_handle_slot {
1721 Some(str_slot) => i == str_slot,
1722 None => match &str_slot_kinds {
1723 Some(kinds) => kinds.get(i as usize).copied().unwrap_or(false),
1724 None => str_ok || val_unary_ok || lit_str_sprintf_ok,
1725 },
1726 }
1727 };
1728 let bypass_type_gate = true;
1745 for i in 0..=max {
1746 self.jit_buf_slot[i as usize] = if let Some(pv) = plain_of_slot(i) {
1747 if wants_string(i) {
1748 if !bypass_type_gate && !pv.is_string_like() {
1749 return Ok(None);
1750 }
1751 pv.raw_bits() as i64
1752 } else {
1753 match pv.as_integer() {
1754 Some(v) => v,
1755 None => return Ok(None),
1756 }
1757 }
1758 } else if wants_string(i) {
1759 let v = self.interp.scope.get_scalar_slot(i);
1760 if !bypass_type_gate && !v.is_string_like() {
1761 return Ok(None);
1762 }
1763 v.raw_bits() as i64
1764 } else if crate::jit::slot_undef_prefill_ok_seq(seg, i) {
1765 0
1766 } else {
1767 match self.interp.scope.get_scalar_slot(i).as_integer() {
1768 Some(v) => v,
1769 None => return Ok(None),
1770 }
1771 };
1772 }
1773 let _ = (val_unary_ok, lit_str_sprintf_ok);
1776 slot_n = n;
1777 }
1778
1779 crate::fusevm_bridge::set_utf8_pragma(self.interp.utf8_pragma);
1780 let cached_chunk_arc: Option<std::sync::Arc<fusevm::Chunk>> = self
1781 .sub_fusevm_meta
1782 .get(cache_ip)
1783 .and_then(|x| x.as_ref())
1784 .and_then(|x| x.as_ref())
1785 .and_then(|e| e.cached_chunk.get().cloned());
1786 let (result_opt, fresh_chunk) = crate::fusevm_bridge::run_linear_segment_cached(
1787 seg,
1788 seg_ip,
1789 &mut self.jit_buf_slot[..slot_n],
1790 crate::jit::SubTerminator::Value,
1791 &self.constants,
1792 cached_chunk_arc.as_ref(),
1793 );
1794 if let Some(fresh) = fresh_chunk {
1795 if let Some(e) = self
1796 .sub_fusevm_meta
1797 .get(cache_ip)
1798 .and_then(|x| x.as_ref())
1799 .and_then(|x| x.as_ref())
1800 {
1801 let _ = e.cached_chunk.set(fresh);
1802 }
1803 }
1804 Ok(result_opt)
1805 }
1806
1807 fn try_jit_subroutine_linear(&mut self) -> Result<bool, StrykeError> {
1810 let ip = self.ip;
1811 debug_assert!(self.sub_entry_at_ip.get(ip).copied().unwrap_or(false));
1812 if self.sub_jit_skip_linear_test(ip) {
1813 return Ok(false);
1814 }
1815 let ops: &Vec<Op> = &self.ops;
1816 let ops = ops as *const Vec<Op>;
1817 let ops = unsafe { &*ops };
1818 let constants: &Vec<StrykeValue> = &self.constants;
1819 let constants = constants as *const Vec<StrykeValue>;
1820 let constants = unsafe { &*constants };
1821 let names: &Vec<String> = &self.names;
1822 let names = names as *const Vec<String>;
1823 let names = unsafe { &*names };
1824 let Some((seg, _)) = crate::jit::sub_entry_segment(ops, ip) else {
1825 return Ok(false);
1826 };
1827 if crate::jit::segment_blocks_subroutine_linear_jit(seg, &self.sub_entries) {
1830 self.sub_jit_skip_linear_mark(ip);
1831 return Ok(false);
1832 }
1833 let mut slot_len: Option<usize> = None;
1834 if let Some(max) = crate::jit::linear_slot_ops_max_index_seq(seg) {
1835 let n = max as usize + 1;
1836 self.jit_buf_slot.resize(n, 0);
1837 let mut ok = true;
1838 for i in 0..=max {
1839 let pv = self.interp.scope.get_scalar_slot(i);
1840 self.jit_buf_slot[i as usize] = match pv.as_integer() {
1841 Some(v) => v,
1842 None if pv.is_undef() && crate::jit::slot_undef_prefill_ok_seq(seg, i) => 0,
1843 None => {
1844 ok = false;
1845 break;
1846 }
1847 };
1848 }
1849 if ok {
1850 slot_len = Some(n);
1851 }
1852 }
1853 let mut plain_len: Option<usize> = None;
1854 if let Some(max) = crate::jit::linear_plain_ops_max_index_seq(seg) {
1855 if (max as usize) < names.len() {
1856 let n = max as usize + 1;
1857 self.jit_buf_plain.resize(n, 0);
1858 let mut ok = true;
1859 for i in 0..=max {
1860 let nm = names[i as usize].as_str();
1861 match self.interp.scope.get_scalar(nm).as_integer() {
1862 Some(v) => self.jit_buf_plain[i as usize] = v,
1863 None => {
1864 ok = false;
1865 break;
1866 }
1867 }
1868 }
1869 if ok {
1870 plain_len = Some(n);
1871 }
1872 }
1873 }
1874 let mut arg_len: Option<usize> = None;
1875 if let Some(max) = crate::jit::linear_arg_ops_max_index_seq(seg) {
1876 if let Some(frame) = self.call_stack.last() {
1877 let base = frame.stack_base;
1878 let n = max as usize + 1;
1879 self.jit_buf_arg.resize(n, 0);
1880 let mut ok = true;
1881 for i in 0..=max {
1882 let pos = base + i as usize;
1883 let pv = self.stack.get(pos).cloned().unwrap_or(StrykeValue::UNDEF);
1884 match pv.as_integer() {
1885 Some(v) => self.jit_buf_arg[i as usize] = v,
1886 None => {
1887 ok = false;
1888 break;
1889 }
1890 }
1891 }
1892 if ok {
1893 arg_len = Some(n);
1894 }
1895 }
1896 }
1897 let vm_ptr = self as *mut VM<'_> as *mut std::ffi::c_void;
1898 let slot_buf = slot_len.map(|n| &mut self.jit_buf_slot[..n]);
1899 let plain_buf = plain_len.map(|n| &mut self.jit_buf_plain[..n]);
1900 let arg_buf = arg_len.map(|n| &self.jit_buf_arg[..n]);
1901 let Some(v) = crate::jit::try_run_linear_sub(
1902 ops,
1903 ip,
1904 slot_buf,
1905 plain_buf,
1906 arg_buf,
1907 constants,
1908 &self.sub_entries,
1909 vm_ptr,
1910 ) else {
1911 return Ok(false);
1912 };
1913 if let Some(n) = slot_len {
1914 let buf = &self.jit_buf_slot[..n];
1915 for idx in crate::jit::linear_slot_ops_written_indices_seq(seg) {
1916 self.interp
1917 .scope
1918 .set_scalar_slot(idx, StrykeValue::integer(buf[idx as usize]));
1919 }
1920 }
1921 if let Some(n) = plain_len {
1922 let buf = &self.jit_buf_plain[..n];
1923 for idx in crate::jit::linear_plain_ops_written_indices_seq(seg) {
1924 let name = names[idx as usize].as_str();
1925 self.interp
1926 .scope
1927 .set_scalar(name, StrykeValue::integer(buf[idx as usize]))
1928 .map_err(|e| e.at_line(self.line()))?;
1929 }
1930 }
1931 if let Some(frame) = self.call_stack.pop() {
1932 self.interp.wantarray_kind = frame.saved_wantarray;
1933 self.stack.truncate(frame.stack_base);
1934 self.interp.pop_scope_to_depth(frame.scope_depth);
1935 if frame.jit_trampoline_return {
1936 self.jit_trampoline_out = Some(v);
1937 } else {
1938 self.push(v);
1939 self.ip = frame.return_ip;
1940 }
1941 }
1942 Ok(true)
1943 }
1944
1945 fn try_jit_subroutine_block(&mut self) -> Result<bool, StrykeError> {
1947 let ip = self.ip;
1948 debug_assert!(self.sub_entry_at_ip.get(ip).copied().unwrap_or(false));
1949 if self.sub_jit_skip_block_test(ip) {
1950 return Ok(false);
1951 }
1952 let vm_ptr = self as *mut VM<'_> as *mut std::ffi::c_void;
1953 let ops: &Vec<Op> = &self.ops;
1954 let constants: &Vec<StrykeValue> = &self.constants;
1955 let names: &Vec<String> = &self.names;
1956 let Some((full_body, term)) = crate::jit::sub_full_body(ops, ip) else {
1957 return Ok(false);
1958 };
1959 if crate::jit::sub_body_blocks_subroutine_block_jit(full_body) {
1960 self.sub_jit_skip_block_mark(ip);
1961 return Ok(false);
1962 }
1963 let Some(validated) =
1964 crate::jit::block_jit_validate_sub(full_body, constants, term, &self.sub_entries)
1965 else {
1966 self.sub_jit_skip_block_mark(ip);
1967 return Ok(false);
1968 };
1969 let block_buf_mode = validated.buffer_mode();
1970
1971 let mut b_slot_len: Option<usize> = None;
1972 if let Some(max) = crate::jit::block_slot_ops_max_index(full_body) {
1973 let n = max as usize + 1;
1974 self.jit_buf_slot.resize(n, 0);
1975 let mut ok = true;
1976 for i in 0..=max {
1977 let pv = self.interp.scope.get_scalar_slot(i);
1978 self.jit_buf_slot[i as usize] = match block_buf_mode {
1979 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => pv.raw_bits() as i64,
1980 crate::jit::BlockJitBufferMode::I64AsInteger => match pv.as_integer() {
1981 Some(v) => v,
1982 None if pv.is_undef()
1983 && crate::jit::block_slot_undef_prefill_ok(full_body, i) =>
1984 {
1985 0
1986 }
1987 None => {
1988 ok = false;
1989 break;
1990 }
1991 },
1992 };
1993 }
1994 if ok {
1995 b_slot_len = Some(n);
1996 }
1997 }
1998
1999 let mut b_plain_len: Option<usize> = None;
2000 if let Some(max) = crate::jit::block_plain_ops_max_index(full_body) {
2001 if (max as usize) < names.len() {
2002 let n = max as usize + 1;
2003 self.jit_buf_plain.resize(n, 0);
2004 let mut ok = true;
2005 for i in 0..=max {
2006 let nm = names[i as usize].as_str();
2007 let pv = self.interp.scope.get_scalar(nm);
2008 self.jit_buf_plain[i as usize] = match block_buf_mode {
2009 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2010 pv.raw_bits() as i64
2011 }
2012 crate::jit::BlockJitBufferMode::I64AsInteger => match pv.as_integer() {
2013 Some(v) => v,
2014 None => {
2015 ok = false;
2016 break;
2017 }
2018 },
2019 };
2020 }
2021 if ok {
2022 b_plain_len = Some(n);
2023 }
2024 }
2025 }
2026
2027 let mut b_arg_len: Option<usize> = None;
2028 if let Some(max) = crate::jit::block_arg_ops_max_index(full_body) {
2029 if let Some(frame) = self.call_stack.last() {
2030 let base = frame.stack_base;
2031 let n = max as usize + 1;
2032 self.jit_buf_arg.resize(n, 0);
2033 let mut ok = true;
2034 for i in 0..=max {
2035 let pos = base + i as usize;
2036 let pv = self.stack.get(pos).cloned().unwrap_or(StrykeValue::UNDEF);
2037 self.jit_buf_arg[i as usize] = match block_buf_mode {
2038 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2039 pv.raw_bits() as i64
2040 }
2041 crate::jit::BlockJitBufferMode::I64AsInteger => match pv.as_integer() {
2042 Some(v) => v,
2043 None => {
2044 ok = false;
2045 break;
2046 }
2047 },
2048 };
2049 }
2050 if ok {
2051 b_arg_len = Some(n);
2052 }
2053 }
2054 }
2055
2056 let block_slot_buf = b_slot_len.map(|n| &mut self.jit_buf_slot[..n]);
2057 let block_plain_buf = b_plain_len.map(|n| &mut self.jit_buf_plain[..n]);
2058 let block_arg_buf = b_arg_len.map(|n| &self.jit_buf_arg[..n]);
2059
2060 let Some((v, buf_mode)) = crate::jit::try_run_block_ops(
2061 full_body,
2062 block_slot_buf,
2063 block_plain_buf,
2064 block_arg_buf,
2065 constants,
2066 Some(validated),
2067 vm_ptr,
2068 &self.sub_entries,
2069 ) else {
2070 self.sub_jit_skip_block_mark(ip);
2071 return Ok(false);
2072 };
2073
2074 if let Some(n) = b_slot_len {
2075 let buf = &self.jit_buf_slot[..n];
2076 for idx in crate::jit::block_slot_ops_written_indices(full_body) {
2077 let bits = buf[idx as usize] as u64;
2078 let pv = match buf_mode {
2079 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2080 StrykeValue::from_raw_bits(bits)
2081 }
2082 crate::jit::BlockJitBufferMode::I64AsInteger => {
2083 StrykeValue::integer(buf[idx as usize])
2084 }
2085 };
2086 self.interp.scope.set_scalar_slot(idx, pv);
2087 }
2088 }
2089 if let Some(n) = b_plain_len {
2090 let buf = &self.jit_buf_plain[..n];
2091 for idx in crate::jit::block_plain_ops_written_indices(full_body) {
2092 let name = names[idx as usize].as_str();
2093 let bits = buf[idx as usize] as u64;
2094 let pv = match buf_mode {
2095 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2096 StrykeValue::from_raw_bits(bits)
2097 }
2098 crate::jit::BlockJitBufferMode::I64AsInteger => {
2099 StrykeValue::integer(buf[idx as usize])
2100 }
2101 };
2102 self.interp
2103 .scope
2104 .set_scalar(name, pv)
2105 .map_err(|e| e.at_line(self.line()))?;
2106 }
2107 }
2108 if let Some(frame) = self.call_stack.pop() {
2109 self.interp.wantarray_kind = frame.saved_wantarray;
2110 self.stack.truncate(frame.stack_base);
2111 self.interp.pop_scope_to_depth(frame.scope_depth);
2112 if frame.jit_trampoline_return {
2113 self.jit_trampoline_out = Some(v);
2114 } else {
2115 self.push(v);
2116 self.ip = frame.return_ip;
2117 }
2118 }
2119 Ok(true)
2120 }
2121
2122 fn run_method_op(
2123 &mut self,
2124 name_idx: u16,
2125 argc: u8,
2126 wa: u8,
2127 super_call: bool,
2128 ) -> StrykeResult<()> {
2129 let method_owned = self.names[name_idx as usize].clone();
2130 let argc = argc as usize;
2131 let want = WantarrayCtx::from_byte(wa);
2132 let mut args = Vec::with_capacity(argc);
2133 for _ in 0..argc {
2134 args.push(self.pop());
2135 }
2136 args.reverse();
2137 let obj = self.pop();
2138 let method = method_owned.as_str();
2139 if let Some(r) = crate::pchannel::dispatch_method(&obj, method, &args, self.line()) {
2140 self.push(r?);
2141 return Ok(());
2142 }
2143 if let Some(r) = self
2144 .interp
2145 .try_native_method(&obj, method, &args, self.line())
2146 {
2147 self.push(r?);
2148 return Ok(());
2149 }
2150 let class = if let Some(b) = obj.as_blessed_ref() {
2151 b.class.clone()
2152 } else if let Some(s) = obj.as_str() {
2153 s
2154 } else {
2155 return Err(StrykeError::runtime(
2156 "Can't call method on non-object",
2157 self.line(),
2158 ));
2159 };
2160 if method == "VERSION" && !super_call {
2161 if let Some(ver) = self.interp.package_version_scalar(class.as_str())? {
2162 self.push(ver);
2163 return Ok(());
2164 }
2165 }
2166 if !super_call {
2168 match method {
2169 "isa" => {
2170 let target = args.first().map(|v| v.to_string()).unwrap_or_default();
2171 let mro = self.interp.mro_linearize(&class);
2172 let result = mro.iter().any(|c| c == &target);
2173 self.push(StrykeValue::integer(if result { 1 } else { 0 }));
2174 return Ok(());
2175 }
2176 "can" => {
2177 let target_method = args.first().map(|v| v.to_string()).unwrap_or_default();
2178 let found = self
2179 .interp
2180 .resolve_method_full_name(&class, &target_method, false)
2181 .and_then(|fq| self.interp.subs.get(&fq))
2182 .is_some();
2183 if found {
2184 self.push(StrykeValue::code_ref(std::sync::Arc::new(
2185 crate::value::StrykeSub {
2186 name: target_method,
2187 params: vec![],
2188 body: vec![],
2189 closure_env: None,
2190 prototype: None,
2191 fib_like: None,
2192 },
2193 )));
2194 } else {
2195 self.push(StrykeValue::UNDEF);
2196 }
2197 return Ok(());
2198 }
2199 "DOES" => {
2200 let target = args.first().map(|v| v.to_string()).unwrap_or_default();
2201 let mro = self.interp.mro_linearize(&class);
2202 let result = mro.iter().any(|c| c == &target);
2203 self.push(StrykeValue::integer(if result { 1 } else { 0 }));
2204 return Ok(());
2205 }
2206 _ => {}
2207 }
2208 }
2209 let mut all_args = vec![obj];
2210 all_args.extend(args);
2211 let full_name = match self
2212 .interp
2213 .resolve_method_full_name(&class, method, super_call)
2214 {
2215 Some(f) => f,
2216 None => {
2217 return Err(StrykeError::runtime(
2218 format!(
2219 "Can't locate method \"{}\" via inheritance (invocant \"{}\")",
2220 method, class
2221 ),
2222 self.line(),
2223 ));
2224 }
2225 };
2226 if let Some(sub) = self.interp.subs.get(&full_name).cloned() {
2227 let saved_wa = self.interp.wantarray_kind;
2228 self.interp.wantarray_kind = want;
2229 self.interp.scope_push_hook();
2230 self.interp.scope.declare_array("_", all_args);
2231 if let Some(ref env) = sub.closure_env {
2232 self.interp.scope.restore_capture(env);
2233 }
2234 let line = self.line();
2235 let argv = self.interp.scope.take_sub_underscore().unwrap_or_default();
2236 self.interp
2237 .apply_sub_signature(sub.as_ref(), &argv, line)
2238 .map_err(|e| e.at_line(line))?;
2239 self.interp.scope.declare_array("_", argv);
2240 let result = self.interp.exec_block_no_scope(&sub.body);
2241 self.interp.wantarray_kind = saved_wa;
2242 self.interp.scope_pop_hook();
2243 match result {
2244 Ok(v) => self.push(v),
2245 Err(crate::vm_helper::FlowOrError::Flow(crate::vm_helper::Flow::Return(v))) => {
2246 self.push(v)
2247 }
2248 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
2249 Err(_) => self.push(StrykeValue::UNDEF),
2250 }
2251 } else if method == "new" && !super_call {
2252 if class == "Set" {
2253 self.push(crate::value::set_from_elements(
2254 all_args.into_iter().skip(1),
2255 ));
2256 } else if let Some(def) = self.interp.struct_defs.get(&class).cloned() {
2257 let line = self.line();
2258 let mut provided = Vec::new();
2259 let mut i = 1;
2260 while i + 1 < all_args.len() {
2261 let k = all_args[i].to_string();
2262 let v = all_args[i + 1].clone();
2263 provided.push((k, v));
2264 i += 2;
2265 }
2266 let mut defaults = Vec::with_capacity(def.fields.len());
2267 for field in &def.fields {
2268 if let Some(ref expr) = field.default {
2269 let val = self.interp.eval_expr(expr).map_err(|e| match e {
2270 crate::vm_helper::FlowOrError::Error(stryke) => stryke,
2271 _ => StrykeError::runtime("default evaluation flow", line),
2272 })?;
2273 defaults.push(Some(val));
2274 } else {
2275 defaults.push(None);
2276 }
2277 }
2278 let v =
2279 crate::native_data::struct_new_with_defaults(&def, &provided, &defaults, line)?;
2280 self.push(v);
2281 } else if let Some(def) = self.interp.class_defs.get(&class).cloned() {
2282 let line = self.line();
2291 let user_args: Vec<StrykeValue> = all_args.into_iter().skip(1).collect();
2292 let v =
2293 self.interp
2294 .class_construct(&def, user_args, line)
2295 .map_err(|e| match e {
2296 crate::vm_helper::FlowOrError::Error(stryke) => stryke,
2297 _ => StrykeError::runtime("class_construct flow", line),
2298 })?;
2299 self.push(v);
2300 } else {
2301 let mut map = IndexMap::new();
2302 let mut i = 1;
2303 while i + 1 < all_args.len() {
2304 map.insert(all_args[i].to_string(), all_args[i + 1].clone());
2305 i += 2;
2306 }
2307 self.push(StrykeValue::blessed(Arc::new(
2308 crate::value::BlessedRef::new_blessed(class, StrykeValue::hash(map)),
2309 )));
2310 }
2311 } else if let Some(result) =
2312 self.interp
2313 .try_autoload_call(&full_name, all_args, self.line(), want, Some(&class))
2314 {
2315 match result {
2316 Ok(v) => self.push(v),
2317 Err(crate::vm_helper::FlowOrError::Flow(crate::vm_helper::Flow::Return(v))) => {
2318 self.push(v)
2319 }
2320 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
2321 Err(_) => self.push(StrykeValue::UNDEF),
2322 }
2323 } else {
2324 return Err(StrykeError::runtime(
2325 format!(
2326 "Can't locate method \"{}\" in package \"{}\"",
2327 method, class
2328 ),
2329 self.line(),
2330 ));
2331 }
2332 Ok(())
2333 }
2334
2335 fn run_fan_block(
2336 &mut self,
2337 block_idx: u16,
2338 n: usize,
2339 line: usize,
2340 progress: bool,
2341 ) -> StrykeResult<()> {
2342 let block = self.blocks[block_idx as usize].clone();
2343 let subs = self.interp.subs.clone();
2344 let (scope_capture, atomic_arrays, atomic_hashes) =
2345 self.interp.scope.capture_with_atomics();
2346 let lex_scalars = self.interp.english_lexical_scalars_clone();
2351 let our_scalars = self.interp.our_lexical_scalars_clone();
2352 let fan_progress = FanProgress::new(progress, n);
2353 let first_err: Arc<Mutex<Option<StrykeError>>> = Arc::new(Mutex::new(None));
2354 (0..n).into_par_iter().for_each(|i| {
2355 if first_err.lock().is_some() {
2356 return;
2357 }
2358 fan_progress.start_worker(i);
2359 let mut local_interp = VMHelper::new();
2360 local_interp.subs = subs.clone();
2361 local_interp.suppress_stdout = progress;
2362 local_interp.scope.restore_capture(&scope_capture);
2363 local_interp
2364 .scope
2365 .restore_atomics(&atomic_arrays, &atomic_hashes);
2366 local_interp.set_english_lexical_scalars(lex_scalars.clone());
2367 local_interp.set_our_lexical_scalars(our_scalars.clone());
2368 local_interp.enable_parallel_guard();
2369 local_interp.scope.set_topic(StrykeValue::integer(i as i64));
2370 crate::parallel_trace::fan_worker_set_index(Some(i as i64));
2371 local_interp.scope_push_hook();
2372 match local_interp.exec_block_no_scope(&block) {
2373 Ok(_) => {}
2374 Err(e) => {
2375 let stryke = match e {
2376 FlowOrError::Error(stryke) => stryke,
2377 FlowOrError::Flow(_) => StrykeError::runtime(
2378 "return/last/next/redo not supported inside fan block",
2379 line,
2380 ),
2381 };
2382 let mut g = first_err.lock();
2383 if g.is_none() {
2384 *g = Some(stryke);
2385 }
2386 }
2387 }
2388 local_interp.scope_pop_hook();
2389 crate::parallel_trace::fan_worker_set_index(None);
2390 fan_progress.finish_worker(i);
2391 });
2392 fan_progress.finish();
2393 if let Some(e) = first_err.lock().take() {
2394 return Err(e);
2395 }
2396 self.push(StrykeValue::UNDEF);
2397 Ok(())
2398 }
2399
2400 fn run_fan_cap_block(
2401 &mut self,
2402 block_idx: u16,
2403 n: usize,
2404 line: usize,
2405 progress: bool,
2406 ) -> StrykeResult<()> {
2407 let block = self.blocks[block_idx as usize].clone();
2408 let subs = self.interp.subs.clone();
2409 let (scope_capture, atomic_arrays, atomic_hashes) =
2410 self.interp.scope.capture_with_atomics();
2411 let lex_scalars = self.interp.english_lexical_scalars_clone();
2413 let our_scalars = self.interp.our_lexical_scalars_clone();
2414 let fan_progress = FanProgress::new(progress, n);
2415 let pairs: Vec<(usize, Result<StrykeValue, FlowOrError>)> = (0..n)
2416 .into_par_iter()
2417 .map(|i| {
2418 fan_progress.start_worker(i);
2419 let mut local_interp = VMHelper::new();
2420 local_interp.subs = subs.clone();
2421 local_interp.suppress_stdout = progress;
2422 local_interp.scope.restore_capture(&scope_capture);
2423 local_interp
2424 .scope
2425 .restore_atomics(&atomic_arrays, &atomic_hashes);
2426 local_interp.set_english_lexical_scalars(lex_scalars.clone());
2427 local_interp.set_our_lexical_scalars(our_scalars.clone());
2428 local_interp.enable_parallel_guard();
2429 local_interp.scope.set_topic(StrykeValue::integer(i as i64));
2430 crate::parallel_trace::fan_worker_set_index(Some(i as i64));
2431 local_interp.scope_push_hook();
2432 let res = local_interp.exec_block_no_scope(&block);
2433 local_interp.scope_pop_hook();
2434 crate::parallel_trace::fan_worker_set_index(None);
2435 fan_progress.finish_worker(i);
2436 (i, res)
2437 })
2438 .collect();
2439 fan_progress.finish();
2440 let mut pairs = pairs;
2441 pairs.sort_by_key(|(i, _)| *i);
2442 let mut out = Vec::with_capacity(n);
2443 for (_, r) in pairs {
2444 match r {
2445 Ok(v) => out.push(v),
2446 Err(e) => {
2447 let stryke = match e {
2448 FlowOrError::Error(stryke) => stryke,
2449 FlowOrError::Flow(_) => StrykeError::runtime(
2450 "return/last/next/redo not supported inside fan_cap block",
2451 line,
2452 ),
2453 };
2454 return Err(stryke);
2455 }
2456 }
2457 }
2458 self.push(StrykeValue::array(out));
2459 Ok(())
2460 }
2461
2462 fn require_scalar_mutable(&self, name: &str) -> StrykeResult<()> {
2463 if self.interp.scope.is_scalar_frozen(name) {
2464 return Err(StrykeError::syntax(
2465 format!("cannot assign to frozen variable `${}`", name),
2466 self.line(),
2467 ));
2468 }
2469 Ok(())
2470 }
2471
2472 fn require_array_mutable(&self, name: &str) -> StrykeResult<()> {
2473 if self.interp.scope.is_array_frozen(name) {
2474 return Err(StrykeError::syntax(
2475 format!("cannot modify frozen array `@{}`", name),
2476 self.line(),
2477 ));
2478 }
2479 Ok(())
2480 }
2481
2482 fn require_hash_mutable(&self, name: &str) -> StrykeResult<()> {
2483 if self.interp.scope.is_hash_frozen(name) || Self::is_reflection_hash(name) {
2484 return Err(StrykeError::syntax(
2485 format!("cannot modify frozen hash `%{}`", name),
2486 self.line(),
2487 ));
2488 }
2489 Ok(())
2490 }
2491
2492 fn is_reflection_hash(name: &str) -> bool {
2494 matches!(name, "b" | "pc" | "e" | "a" | "d" | "c" | "p" | "all")
2495 || name.starts_with("stryke::")
2496 }
2497
2498 pub fn execute(&mut self) -> StrykeResult<StrykeValue> {
2503 let ops_ref: &Vec<Op> = &self.ops;
2504 let ops = ops_ref as *const Vec<Op>;
2505 let ops = unsafe { &*ops };
2507 let names_ref: &Vec<String> = &self.names;
2508 let names = names_ref as *const Vec<String>;
2509 let names = unsafe { &*names };
2511 let constants_ref: &Vec<StrykeValue> = &self.constants;
2512 let constants = constants_ref as *const Vec<StrykeValue>;
2513 let constants = unsafe { &*constants };
2515 let mut last = StrykeValue::UNDEF;
2516 let mut op_count: u64 = 0;
2518
2519 crate::perl_signal::poll(self.interp)?;
2521 if self.jit_enabled {
2522 let mut top_slot_len: Option<usize> = None;
2523 if let Some(max) = crate::jit::linear_slot_ops_max_index(ops) {
2524 let n = max as usize + 1;
2525 self.jit_buf_slot.resize(n, 0);
2526 let mut ok = true;
2527 for i in 0..=max {
2528 let pv = self.interp.scope.get_scalar_slot(i);
2529 self.jit_buf_slot[i as usize] = match pv.as_integer() {
2530 Some(v) => v,
2531 None if pv.is_undef() && crate::jit::slot_undef_prefill_ok(ops, i) => 0,
2532 None => {
2533 ok = false;
2534 break;
2535 }
2536 };
2537 }
2538 if ok {
2539 top_slot_len = Some(n);
2540 }
2541 }
2542
2543 let mut top_plain_len: Option<usize> = None;
2544 if let Some(max) = crate::jit::linear_plain_ops_max_index(ops) {
2545 if (max as usize) < names.len() {
2546 let n = max as usize + 1;
2547 self.jit_buf_plain.resize(n, 0);
2548 let mut ok = true;
2549 for i in 0..=max {
2550 let nm = names[i as usize].as_str();
2551 match self.interp.scope.get_scalar(nm).as_integer() {
2552 Some(v) => self.jit_buf_plain[i as usize] = v,
2553 None => {
2554 ok = false;
2555 break;
2556 }
2557 }
2558 }
2559 if ok {
2560 top_plain_len = Some(n);
2561 }
2562 }
2563 }
2564
2565 let mut top_arg_len: Option<usize> = None;
2566 if let Some(max) = crate::jit::linear_arg_ops_max_index(ops) {
2567 if let Some(frame) = self.call_stack.last() {
2568 let base = frame.stack_base;
2569 let n = max as usize + 1;
2570 self.jit_buf_arg.resize(n, 0);
2571 let mut ok = true;
2572 for i in 0..=max {
2573 let pos = base + i as usize;
2574 let pv = self.stack.get(pos).cloned().unwrap_or(StrykeValue::UNDEF);
2575 match pv.as_integer() {
2576 Some(v) => self.jit_buf_arg[i as usize] = v,
2577 None => {
2578 ok = false;
2579 break;
2580 }
2581 }
2582 }
2583 if ok {
2584 top_arg_len = Some(n);
2585 }
2586 }
2587 }
2588
2589 let slot_buf = top_slot_len.map(|n| &mut self.jit_buf_slot[..n]);
2590 let plain_buf = top_plain_len.map(|n| &mut self.jit_buf_plain[..n]);
2591 let arg_buf = top_arg_len.map(|n| &self.jit_buf_arg[..n]);
2592
2593 if let Some(v) =
2594 crate::jit::try_run_linear_ops(ops, slot_buf, plain_buf, arg_buf, constants)
2595 {
2596 if let Some(n) = top_slot_len {
2597 let buf = &self.jit_buf_slot[..n];
2598 for idx in crate::jit::linear_slot_ops_written_indices(ops) {
2599 self.interp
2600 .scope
2601 .set_scalar_slot(idx, StrykeValue::integer(buf[idx as usize]));
2602 }
2603 }
2604 if let Some(n) = top_plain_len {
2605 let buf = &self.jit_buf_plain[..n];
2606 for idx in crate::jit::linear_plain_ops_written_indices(ops) {
2607 let name = names[idx as usize].as_str();
2608 self.interp
2609 .scope
2610 .set_scalar(name, StrykeValue::integer(buf[idx as usize]))?;
2611 }
2612 }
2613 return Ok(v);
2614 }
2615
2616 if let Some(validated) =
2618 crate::jit::block_jit_validate(ops, constants, &self.sub_entries)
2619 {
2620 let block_buf_mode = validated.buffer_mode();
2621
2622 let mut top_b_slot_len: Option<usize> = None;
2623 if let Some(max) = crate::jit::block_slot_ops_max_index(ops) {
2624 let n = max as usize + 1;
2625 self.jit_buf_slot.resize(n, 0);
2626 let mut ok = true;
2627 for i in 0..=max {
2628 let pv = self.interp.scope.get_scalar_slot(i);
2629 self.jit_buf_slot[i as usize] = match block_buf_mode {
2630 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2631 pv.raw_bits() as i64
2632 }
2633 crate::jit::BlockJitBufferMode::I64AsInteger => match pv.as_integer() {
2634 Some(v) => v,
2635 None if pv.is_undef()
2636 && crate::jit::block_slot_undef_prefill_ok(ops, i) =>
2637 {
2638 0
2639 }
2640 None => {
2641 ok = false;
2642 break;
2643 }
2644 },
2645 };
2646 }
2647 if ok {
2648 top_b_slot_len = Some(n);
2649 }
2650 }
2651
2652 let mut top_b_plain_len: Option<usize> = None;
2653 if let Some(max) = crate::jit::block_plain_ops_max_index(ops) {
2654 if (max as usize) < names.len() {
2655 let n = max as usize + 1;
2656 self.jit_buf_plain.resize(n, 0);
2657 let mut ok = true;
2658 for i in 0..=max {
2659 let nm = names[i as usize].as_str();
2660 let pv = self.interp.scope.get_scalar(nm);
2661 self.jit_buf_plain[i as usize] = match block_buf_mode {
2662 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2663 pv.raw_bits() as i64
2664 }
2665 crate::jit::BlockJitBufferMode::I64AsInteger => {
2666 match pv.as_integer() {
2667 Some(v) => v,
2668 None => {
2669 ok = false;
2670 break;
2671 }
2672 }
2673 }
2674 };
2675 }
2676 if ok {
2677 top_b_plain_len = Some(n);
2678 }
2679 }
2680 }
2681
2682 let mut top_b_arg_len: Option<usize> = None;
2683 if let Some(max) = crate::jit::block_arg_ops_max_index(ops) {
2684 if let Some(frame) = self.call_stack.last() {
2685 let base = frame.stack_base;
2686 let n = max as usize + 1;
2687 self.jit_buf_arg.resize(n, 0);
2688 let mut ok = true;
2689 for i in 0..=max {
2690 let pos = base + i as usize;
2691 let pv = self.stack.get(pos).cloned().unwrap_or(StrykeValue::UNDEF);
2692 self.jit_buf_arg[i as usize] = match block_buf_mode {
2693 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2694 pv.raw_bits() as i64
2695 }
2696 crate::jit::BlockJitBufferMode::I64AsInteger => {
2697 match pv.as_integer() {
2698 Some(v) => v,
2699 None => {
2700 ok = false;
2701 break;
2702 }
2703 }
2704 }
2705 };
2706 }
2707 if ok {
2708 top_b_arg_len = Some(n);
2709 }
2710 }
2711 }
2712
2713 let vm_ptr = self as *mut VM<'_> as *mut std::ffi::c_void;
2714 let block_slot_buf = top_b_slot_len.map(|n| &mut self.jit_buf_slot[..n]);
2715 let block_plain_buf = top_b_plain_len.map(|n| &mut self.jit_buf_plain[..n]);
2716 let block_arg_buf = top_b_arg_len.map(|n| &self.jit_buf_arg[..n]);
2717
2718 if let Some((v, buf_mode)) = crate::jit::try_run_block_ops(
2719 ops,
2720 block_slot_buf,
2721 block_plain_buf,
2722 block_arg_buf,
2723 constants,
2724 Some(validated),
2725 vm_ptr,
2726 &self.sub_entries,
2727 ) {
2728 if let Some(n) = top_b_slot_len {
2729 let buf = &self.jit_buf_slot[..n];
2730 for idx in crate::jit::block_slot_ops_written_indices(ops) {
2731 let bits = buf[idx as usize] as u64;
2732 let pv = match buf_mode {
2733 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2734 StrykeValue::from_raw_bits(bits)
2735 }
2736 crate::jit::BlockJitBufferMode::I64AsInteger => {
2737 StrykeValue::integer(buf[idx as usize])
2738 }
2739 };
2740 self.interp.scope.set_scalar_slot(idx, pv);
2741 }
2742 }
2743 if let Some(n) = top_b_plain_len {
2744 let buf = &self.jit_buf_plain[..n];
2745 for idx in crate::jit::block_plain_ops_written_indices(ops) {
2746 let name = names[idx as usize].as_str();
2747 let bits = buf[idx as usize] as u64;
2748 let pv = match buf_mode {
2749 crate::jit::BlockJitBufferMode::I64AsStrykeValueBits => {
2750 StrykeValue::from_raw_bits(bits)
2751 }
2752 crate::jit::BlockJitBufferMode::I64AsInteger => {
2753 StrykeValue::integer(buf[idx as usize])
2754 }
2755 };
2756 self.interp.scope.set_scalar(name, pv)?;
2757 }
2758 }
2759 return Ok(v);
2760 }
2761 }
2762 }
2763
2764 last = self.run_main_dispatch_loop(last, &mut op_count, true)?;
2765
2766 Ok(last)
2767 }
2768
2769 fn try_recover_from_exception(&mut self, e: &StrykeError) -> StrykeResult<bool> {
2777 if matches!(e.kind, ErrorKind::Exit(_)) {
2778 return Ok(false);
2779 }
2780 loop {
2781 let Some(frame) = self.try_stack.last() else {
2782 return Ok(false);
2783 };
2784 let op_idx = frame.try_push_op_idx;
2785 let Op::TryPush {
2786 catch_ip,
2787 finally_ip,
2788 ..
2789 } = &self.ops[op_idx]
2790 else {
2791 return Ok(false);
2792 };
2793 let catch_ip = *catch_ip;
2794 let finally_ip = *finally_ip;
2795 match frame.state {
2796 TryState::Trying => {
2797 let val = e
2798 .die_value
2799 .clone()
2800 .unwrap_or_else(|| StrykeValue::string(e.to_string()));
2801 self.pending_catch_error = Some(val);
2802 if let Some(top) = self.try_stack.last_mut() {
2803 top.state = TryState::Catching;
2804 }
2805 self.ip = catch_ip;
2806 return Ok(true);
2807 }
2808 TryState::Catching => {
2809 if let Some(fin_ip) = finally_ip {
2810 if let Some(top) = self.try_stack.last_mut() {
2811 top.state = TryState::Finalizing;
2812 top.deferred_error = Some(e.clone());
2813 }
2814 self.ip = fin_ip;
2815 return Ok(true);
2816 }
2817 self.try_stack.pop();
2818 }
2819 TryState::Finalizing => {
2820 self.try_stack.pop();
2822 }
2823 }
2824 }
2825 }
2826
2827 #[inline]
2829 fn sub_for_closure_restore(&self, name: &str) -> Option<Arc<StrykeSub>> {
2830 self.interp.subs.get(name).cloned()
2831 }
2832
2833 #[cold]
2838 fn dispatch_with_advice(
2839 &mut self,
2840 name: &str,
2841 closure_sub_hint: Option<Arc<StrykeSub>>,
2842 argc: usize,
2843 want: WantarrayCtx,
2844 preserve_arrays: bool,
2845 ) -> StrykeResult<()> {
2846 use crate::ast::AdviceKind;
2847
2848 let line = self.line();
2849
2850 let args = if preserve_arrays {
2851 self.pop_call_operands_preserved(argc)
2852 } else {
2853 self.pop_call_operands_flattened(argc)
2854 };
2855
2856 let sub_opt = closure_sub_hint.or_else(|| self.interp.resolve_sub_by_name(name));
2857
2858 let matching: Vec<crate::aop::Intercept> = self
2859 .interp
2860 .intercepts
2861 .iter()
2862 .filter(|i| crate::aop::glob_match(&i.pattern, name))
2863 .cloned()
2864 .collect();
2865
2866 self.interp
2868 .scope
2869 .declare_scalar("INTERCEPT_NAME", StrykeValue::string(name.to_string()));
2870 self.interp
2871 .scope
2872 .declare_array("INTERCEPT_ARGS", args.clone());
2873
2874 self.interp.intercept_active_names.push(name.to_string());
2875
2876 for adv in matching
2881 .iter()
2882 .filter(|i| matches!(i.kind, AdviceKind::Before))
2883 {
2884 if let Err(e) = self.run_advice_body_bytecode(adv, line) {
2885 self.interp.intercept_active_names.pop();
2886 return Err(e);
2887 }
2888 }
2889
2890 let around = matching
2891 .iter()
2892 .find(|i| matches!(i.kind, AdviceKind::Around));
2893
2894 let t0 = std::time::Instant::now();
2895 let retval = if let Some(around) = around {
2896 self.interp
2897 .intercept_ctx_stack
2898 .push(crate::aop::InterceptCtx {
2899 name: name.to_string(),
2900 args: args.clone(),
2901 proceeded: false,
2902 retval: StrykeValue::UNDEF,
2903 });
2904 let exec_res = self.run_advice_body_bytecode(around, line);
2905 let _ctx = self.interp.intercept_ctx_stack.pop();
2906 match exec_res {
2911 Ok(v) => v,
2912 Err(e) => {
2913 self.interp.intercept_active_names.pop();
2914 return Err(e);
2915 }
2916 }
2917 } else if let Some(sub) = sub_opt {
2918 match self.interp.call_sub(&sub, args.clone(), want, line) {
2919 Ok(v) => v,
2920 Err(FlowOrError::Flow(Flow::Return(v))) => v,
2921 Err(FlowOrError::Flow(_)) => StrykeValue::UNDEF,
2922 Err(FlowOrError::Error(e)) => {
2923 self.interp.intercept_active_names.pop();
2924 return Err(e.at_line(line));
2925 }
2926 }
2927 } else {
2928 let saved_wa_call = self.interp.wantarray_kind;
2930 self.interp.wantarray_kind = want;
2931 let r = crate::builtins::try_builtin(self.interp, name, &args, line);
2932 self.interp.wantarray_kind = saved_wa_call;
2933 match r {
2934 Some(Ok(v)) => v,
2935 Some(Err(e)) => {
2936 self.interp.intercept_active_names.pop();
2937 return Err(e.at_line(line));
2938 }
2939 None => {
2940 self.interp.intercept_active_names.pop();
2941 return Err(StrykeError::runtime(
2942 format!("undefined sub `{}` (advice fallback)", name),
2943 line,
2944 ));
2945 }
2946 }
2947 };
2948 let elapsed = t0.elapsed();
2949
2950 self.interp.scope.declare_scalar(
2952 "INTERCEPT_MS",
2953 StrykeValue::float(elapsed.as_secs_f64() * 1000.0),
2954 );
2955 self.interp.scope.declare_scalar(
2956 "INTERCEPT_US",
2957 StrykeValue::integer(elapsed.as_micros() as i64),
2958 );
2959 self.interp
2960 .scope
2961 .declare_scalar("INTERCEPT_RESULT", retval.clone());
2962
2963 for adv in matching
2964 .iter()
2965 .filter(|i| matches!(i.kind, AdviceKind::After))
2966 {
2967 if let Err(e) = self.run_advice_body_bytecode(adv, line) {
2968 self.interp.intercept_active_names.pop();
2969 return Err(e);
2970 }
2971 }
2972
2973 self.interp.intercept_active_names.pop();
2974 self.push(retval);
2975 Ok(())
2976 }
2977
2978 #[inline]
2987 fn run_advice_body_bytecode(
2988 &mut self,
2989 adv: &crate::aop::Intercept,
2990 line: usize,
2991 ) -> StrykeResult<StrykeValue> {
2992 let idx = adv.body_block_idx as usize;
2993 let range = self
2994 .block_bytecode_ranges
2995 .get(idx)
2996 .copied()
2997 .flatten()
2998 .ok_or_else(|| {
2999 StrykeError::runtime(
3000 format!(
3001 "AOP {} advice body for `{}` could not be lowered to bytecode \
3002 (likely contains a construct unsupported by block lowering, \
3003 e.g. a literal `return`); rewrite the body without it",
3004 match adv.kind {
3005 crate::ast::AdviceKind::Before => "before",
3006 crate::ast::AdviceKind::After => "after",
3007 crate::ast::AdviceKind::Around => "around",
3008 },
3009 adv.pattern,
3010 ),
3011 line,
3012 )
3013 })?;
3014 let mut op_count: u64 = 0;
3015 self.run_block_region(range.0, range.1, &mut op_count)
3016 }
3017
3018 fn vm_dispatch_user_call(
3019 &mut self,
3020 name_idx: u16,
3021 entry_opt: Option<(usize, bool)>,
3022 argc_u8: u8,
3023 wa_byte: u8,
3024 closure_sub_hint: Option<Arc<StrykeSub>>,
3026 ) -> StrykeResult<()> {
3027 let name_owned = self.names[name_idx as usize].clone();
3028 let name = name_owned.as_str();
3029 let argc = argc_u8 as usize;
3030 let want = WantarrayCtx::from_byte(wa_byte);
3031
3032 if !self.interp.intercepts.is_empty()
3037 && !self.interp.intercept_active_names.iter().any(|n| n == name)
3038 && self
3039 .interp
3040 .intercepts
3041 .iter()
3042 .any(|i| crate::aop::glob_match(&i.pattern, name))
3043 {
3044 let preserve = Self::call_preserve_operand_arrays(name);
3045 return self.dispatch_with_advice(&name_owned, closure_sub_hint, argc, want, preserve);
3046 }
3047
3048 if let Some((entry_ip, stack_args)) = entry_opt {
3049 let saved_wa = self.interp.wantarray_kind;
3050 let sub_prof_t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3051 if let Some(p) = &mut self.interp.profiler {
3052 p.enter_sub(name);
3053 }
3054 self.interp.debugger_enter_sub(name);
3055
3056 let fib_sub: Option<Arc<StrykeSub>> = closure_sub_hint
3062 .clone()
3063 .or_else(|| self.sub_for_closure_restore(name));
3064 if let Some(ref sub_arc) = fib_sub {
3065 if let Some(pat) = sub_arc.fib_like.as_ref() {
3066 if argc == 1 {
3069 let top_idx = self.stack.len().saturating_sub(1);
3070 if let Some(n0) = self.stack.get(top_idx).and_then(|v| v.as_integer()) {
3071 let result = crate::fib_like_tail::eval_fib_like_recursive_add(n0, pat);
3072 self.stack.truncate(top_idx);
3074 self.push(StrykeValue::integer(result));
3075 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, sub_prof_t0) {
3076 p.exit_sub(t0.elapsed());
3077 }
3078 self.interp.debugger_leave_sub();
3079 self.interp.wantarray_kind = saved_wa;
3080 return Ok(());
3081 }
3082 }
3083 }
3084 }
3085
3086 if stack_args {
3087 let eff_argc = if argc == 0 {
3088 self.push(self.interp.scope.get_scalar("_").clone());
3089 1
3090 } else {
3091 argc
3092 };
3093 let stack_base = self.stack.len() - eff_argc;
3094 self.call_stack.push(CallFrame {
3095 return_ip: self.ip,
3096 stack_base,
3097 scope_depth: self.interp.scope.depth(),
3098 saved_wantarray: saved_wa,
3099 jit_trampoline_return: false,
3100 block_region: false,
3101 sub_profiler_start: sub_prof_t0,
3102 });
3103 self.interp.wantarray_kind = want;
3104 self.interp.scope_push_hook();
3105 let closure_sub = closure_sub_hint.or_else(|| self.sub_for_closure_restore(name));
3106 if let Some(ref sub) = closure_sub {
3107 if let Some(ref env) = sub.closure_env {
3108 self.interp.scope.restore_capture(env);
3109 }
3110 self.interp.current_sub_stack.push(sub.clone());
3111 }
3112 self.ip = entry_ip;
3113 } else {
3114 let args = if Self::call_preserve_operand_arrays(name) {
3115 self.pop_call_operands_preserved(argc)
3116 } else {
3117 self.pop_call_operands_flattened(argc)
3118 };
3119 let args = if argc == 0 {
3122 self.interp.with_topic_default_args(args)
3123 } else {
3124 args
3125 };
3126 self.call_stack.push(CallFrame {
3127 return_ip: self.ip,
3128 stack_base: self.stack.len(),
3129 scope_depth: self.interp.scope.depth(),
3130 saved_wantarray: saved_wa,
3131 jit_trampoline_return: false,
3132 block_region: false,
3133 sub_profiler_start: sub_prof_t0,
3134 });
3135 self.interp.wantarray_kind = want;
3136 self.interp.scope_push_hook();
3137 self.interp.scope.declare_array("_", args);
3138 let closure_sub = closure_sub_hint.or_else(|| self.sub_for_closure_restore(name));
3139 if let Some(ref sub) = closure_sub {
3140 if let Some(ref env) = sub.closure_env {
3141 self.interp.scope.restore_capture(env);
3142 }
3143 let line = self.line();
3144 let argv = self.interp.scope.take_sub_underscore().unwrap_or_default();
3145 self.interp
3146 .apply_sub_signature(sub.as_ref(), &argv, line)
3147 .map_err(|e| e.at_line(line))?;
3148 self.interp.scope.declare_array("_", argv.clone());
3149 self.interp.scope.set_closure_args(&argv);
3150 self.interp.current_sub_stack.push(sub.clone());
3151 }
3152 self.ip = entry_ip;
3153 }
3154 } else {
3155 let args = if Self::call_preserve_operand_arrays(name) {
3156 self.pop_call_operands_preserved(argc)
3157 } else {
3158 self.pop_call_operands_flattened(argc)
3159 };
3160
3161 let saved_wa_call = self.interp.wantarray_kind;
3162 self.interp.wantarray_kind = want;
3163 let is_bare_builtin = !crate::compat_mode()
3168 && !name.contains("::")
3169 && crate::builtins::is_callable_spelling(name);
3170 if let Some(r) = crate::builtins::try_builtin(self.interp, name, &args, self.line()) {
3171 self.interp.wantarray_kind = saved_wa_call;
3172 self.push(r?);
3173 } else {
3174 self.interp.wantarray_kind = saved_wa_call;
3175 let maybe_sub = if is_bare_builtin {
3176 None
3177 } else {
3178 self.interp.resolve_sub_by_name(name)
3179 };
3180 if let Some(sub) = maybe_sub {
3181 let t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3182 if let Some(p) = &mut self.interp.profiler {
3183 p.enter_sub(name);
3184 }
3185 self.interp.debugger_enter_sub(name);
3186 let args = if argc == 0 {
3188 self.interp.with_topic_default_args(args)
3189 } else {
3190 args
3191 };
3192 let saved_wa = self.interp.wantarray_kind;
3193 self.interp.wantarray_kind = want;
3194 self.interp.scope_push_hook();
3195 self.interp.scope.declare_array("_", args);
3196 if let Some(ref env) = sub.closure_env {
3197 self.interp.scope.restore_capture(env);
3198 }
3199 let argv = self.interp.scope.take_sub_underscore().unwrap_or_default();
3200 let line = self.line();
3201 self.interp
3202 .apply_sub_signature(&sub, &argv, line)
3203 .map_err(|e| e.at_line(line))?;
3204 let result = {
3205 self.interp.scope.declare_array("_", argv.clone());
3206 self.interp.scope.set_closure_args(&argv);
3207 self.interp
3208 .exec_block_no_scope_with_tail(&sub.body, WantarrayCtx::List)
3209 };
3210 self.interp.wantarray_kind = saved_wa;
3211 self.interp.scope_pop_hook();
3212 match result {
3213 Ok(v) => self.push(v),
3214 Err(crate::vm_helper::FlowOrError::Flow(
3215 crate::vm_helper::Flow::Return(v),
3216 )) => self.push(v),
3217 Err(crate::vm_helper::FlowOrError::Error(e)) => {
3218 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3219 p.exit_sub(t0.elapsed());
3220 }
3221 self.interp.debugger_leave_sub();
3222 return Err(e);
3223 }
3224 Err(_) => self.push(StrykeValue::UNDEF),
3225 }
3226 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3227 p.exit_sub(t0.elapsed());
3228 }
3229 self.interp.debugger_leave_sub();
3230 } else if !name.contains("::")
3231 && matches!(
3232 name,
3233 "uniq"
3234 | "distinct"
3235 | "uniqstr"
3236 | "uniqint"
3237 | "uniqnum"
3238 | "shuffle"
3239 | "sample"
3240 | "chunked"
3241 | "windowed"
3242 | "zip"
3243 | "zip_shortest"
3244 | "zip_longest"
3245 | "mesh"
3246 | "mesh_shortest"
3247 | "mesh_longest"
3248 | "any"
3249 | "all"
3250 | "none"
3251 | "notall"
3252 | "first"
3253 | "find_index"
3254 | "firstidx"
3255 | "first_index"
3256 | "reduce"
3257 | "reductions"
3258 | "sum"
3259 | "sum0"
3260 | "product"
3261 | "min"
3262 | "max"
3263 | "minstr"
3264 | "maxstr"
3265 | "mean"
3266 | "median"
3267 | "mode"
3268 | "stddev"
3269 | "variance"
3270 | "pairs"
3271 | "unpairs"
3272 | "pairkeys"
3273 | "pairvalues"
3274 | "pairgrep"
3275 | "pairmap"
3276 | "pairfirst"
3277 | "blessed"
3279 | "refaddr"
3280 | "reftype"
3281 | "looks_like_number"
3282 | "weaken"
3283 | "unweaken"
3284 | "isweak"
3285 | "set_subname"
3286 | "subname"
3287 | "unicode_to_native"
3288 )
3289 {
3290 let t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3291 if let Some(p) = &mut self.interp.profiler {
3292 p.enter_sub(name);
3293 }
3294 self.interp.debugger_enter_sub(name);
3295 let saved_wa = self.interp.wantarray_kind;
3296 self.interp.wantarray_kind = want;
3297 let out = self
3298 .interp
3299 .call_bare_list_builtin(name, args, self.line(), want);
3300 self.interp.wantarray_kind = saved_wa;
3301 match out {
3302 Ok(v) => self.push(v),
3303 Err(crate::vm_helper::FlowOrError::Flow(
3304 crate::vm_helper::Flow::Return(v),
3305 )) => self.push(v),
3306 Err(crate::vm_helper::FlowOrError::Error(e)) => {
3307 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3308 p.exit_sub(t0.elapsed());
3309 }
3310 self.interp.debugger_leave_sub();
3311 return Err(e);
3312 }
3313 Err(_) => self.push(StrykeValue::UNDEF),
3314 }
3315 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3316 p.exit_sub(t0.elapsed());
3317 }
3318 self.interp.debugger_leave_sub();
3319 } else if let Some(result) = self.interp.try_autoload_call(
3320 name,
3321 if argc == 0 {
3322 self.interp.with_topic_default_args(args.clone())
3323 } else {
3324 args.clone()
3325 },
3326 self.line(),
3327 want,
3328 None,
3329 ) {
3330 let t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3331 if let Some(p) = &mut self.interp.profiler {
3332 p.enter_sub(name);
3333 }
3334 self.interp.debugger_enter_sub(name);
3335 match result {
3336 Ok(v) => self.push(v),
3337 Err(crate::vm_helper::FlowOrError::Flow(
3338 crate::vm_helper::Flow::Return(v),
3339 )) => self.push(v),
3340 Err(crate::vm_helper::FlowOrError::Error(e)) => {
3341 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3342 p.exit_sub(t0.elapsed());
3343 }
3344 self.interp.debugger_leave_sub();
3345 return Err(e);
3346 }
3347 Err(_) => self.push(StrykeValue::UNDEF),
3348 }
3349 if let (Some(p), Some(t0)) = (&mut self.interp.profiler, t0) {
3350 p.exit_sub(t0.elapsed());
3351 }
3352 self.interp.debugger_leave_sub();
3353 } else if let Some(def) = self.interp.struct_defs.get(name).cloned() {
3354 let result = self.interp.struct_construct(&def, args, self.line());
3356 match result {
3357 Ok(v) => self.push(v),
3358 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
3359 _ => self.push(StrykeValue::UNDEF),
3360 }
3361 } else if let Some(def) = self.interp.class_defs.get(name).cloned() {
3362 let result = self.interp.class_construct(&def, args, self.line());
3364 match result {
3365 Ok(v) => self.push(v),
3366 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
3367 _ => self.push(StrykeValue::UNDEF),
3368 }
3369 } else if let Some((prefix, suffix)) = name.rsplit_once("::") {
3370 if let Some(def) = self.interp.enum_defs.get(prefix).cloned() {
3372 let result = self.interp.enum_construct(&def, suffix, args, self.line());
3373 match result {
3374 Ok(v) => self.push(v),
3375 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
3376 _ => self.push(StrykeValue::UNDEF),
3377 }
3378 } else if let Some(def) = self.interp.class_defs.get(prefix).cloned() {
3380 if let Some(m) = def.method(suffix) {
3381 if m.is_static {
3382 if let Some(ref body) = m.body {
3383 let params = m.params.clone();
3384 match self.interp.call_static_class_method(
3385 body,
3386 ¶ms,
3387 args.clone(),
3388 self.line(),
3389 ) {
3390 Ok(v) => self.push(v),
3391 Err(crate::vm_helper::FlowOrError::Error(e)) => {
3392 return Err(e)
3393 }
3394 Err(crate::vm_helper::FlowOrError::Flow(
3395 crate::vm_helper::Flow::Return(v),
3396 )) => self.push(v),
3397 _ => self.push(StrykeValue::UNDEF),
3398 }
3399 } else {
3400 self.push(StrykeValue::UNDEF);
3401 }
3402 } else {
3403 return Err(StrykeError::runtime(
3404 format!("method `{}` is not static", suffix),
3405 self.line(),
3406 ));
3407 }
3408 } else if def.static_fields.iter().any(|sf| sf.name == suffix) {
3409 let key = format!("{}::{}", prefix, suffix);
3411 match args.len() {
3412 0 => {
3413 let val = self.interp.scope.get_scalar(&key);
3414 self.push(val);
3415 }
3416 1 => {
3417 let _ = self.interp.scope.set_scalar(&key, args[0].clone());
3418 self.push(args[0].clone());
3419 }
3420 _ => {
3421 return Err(StrykeError::runtime(
3422 format!(
3423 "static field `{}::{}` takes 0 or 1 arguments",
3424 prefix, suffix
3425 ),
3426 self.line(),
3427 ));
3428 }
3429 }
3430 } else {
3431 return Err(StrykeError::runtime(
3432 self.interp.undefined_subroutine_call_message(name),
3433 self.line(),
3434 ));
3435 }
3436 } else {
3437 return Err(StrykeError::runtime(
3438 self.interp.undefined_subroutine_call_message(name),
3439 self.line(),
3440 ));
3441 }
3442 } else {
3443 return Err(StrykeError::runtime(
3444 self.interp.undefined_subroutine_call_message(name),
3445 self.line(),
3446 ));
3447 }
3448 }
3449 }
3450 Ok(())
3451 }
3452
3453 #[inline]
3454 fn push_binop_with_overload<F>(
3455 &mut self,
3456 op: BinOp,
3457 a: StrykeValue,
3458 b: StrykeValue,
3459 default: F,
3460 ) -> StrykeResult<()>
3461 where
3462 F: FnOnce(&StrykeValue, &StrykeValue) -> StrykeResult<StrykeValue>,
3463 {
3464 let line = self.line();
3465 if let Some(exec_res) = self.interp.try_overload_binop(op, &a, &b, line) {
3466 self.push(vm_interp_result(exec_res, line)?);
3467 } else {
3468 self.push(default(&a, &b)?);
3469 }
3470 Ok(())
3471 }
3472
3473 pub(crate) fn concat_stack_values(
3474 &mut self,
3475 a: StrykeValue,
3476 b: StrykeValue,
3477 ) -> StrykeResult<StrykeValue> {
3478 let line = self.line();
3479 if let Some(exec_res) = self.interp.try_overload_binop(BinOp::Concat, &a, &b, line) {
3480 vm_interp_result(exec_res, line)
3481 } else {
3482 let sa = match self.interp.stringify_value(a, line) {
3483 Ok(s) => s,
3484 Err(FlowOrError::Error(e)) => return Err(e),
3485 Err(FlowOrError::Flow(_)) => {
3486 return Err(StrykeError::runtime(
3487 "concat: unexpected control flow",
3488 line,
3489 ));
3490 }
3491 };
3492 let sb = match self.interp.stringify_value(b, line) {
3493 Ok(s) => s,
3494 Err(FlowOrError::Error(e)) => return Err(e),
3495 Err(FlowOrError::Flow(_)) => {
3496 return Err(StrykeError::runtime(
3497 "concat: unexpected control flow",
3498 line,
3499 ));
3500 }
3501 };
3502 let mut s = sa;
3503 s.push_str(&sb);
3504 Ok(StrykeValue::string(s))
3505 }
3506 }
3507
3508 fn run_main_dispatch_loop(
3509 &mut self,
3510 mut last: StrykeValue,
3511 op_count: &mut u64,
3512 init_dispatch: bool,
3513 ) -> StrykeResult<StrykeValue> {
3514 if init_dispatch {
3515 self.halt = false;
3516 self.exit_main_dispatch = false;
3517 self.exit_main_dispatch_value = None;
3518 }
3519 let ops_ref: &Vec<Op> = &self.ops;
3520 let ops = ops_ref as *const Vec<Op>;
3521 let ops = unsafe { &*ops };
3522 let names_ref: &Vec<String> = &self.names;
3523 let names = names_ref as *const Vec<String>;
3524 let names = unsafe { &*names };
3525 let constants_ref: &Vec<StrykeValue> = &self.constants;
3526 let constants = constants_ref as *const Vec<StrykeValue>;
3527 let constants = unsafe { &*constants };
3528 let len = ops.len();
3529 const MAX_OPS: u64 = 1_000_000_000;
3530 loop {
3531 if self.jit_trampoline_depth > 0 && self.jit_trampoline_out.is_some() {
3532 break;
3533 }
3534 if self.block_region_return.is_some() {
3535 break;
3536 }
3537 if self.block_region_mode && self.ip >= self.block_region_end {
3538 return Err(StrykeError::runtime(
3539 "block bytecode region fell through without BlockReturnValue",
3540 self.line(),
3541 ));
3542 }
3543 if self.ip >= len {
3544 break;
3545 }
3546
3547 if !self.block_region_mode
3548 && self.jit_enabled
3549 && self.sub_entry_at_ip.get(self.ip).copied().unwrap_or(false)
3550 {
3551 let sub_ip = self.ip;
3552 if sub_ip >= self.sub_entry_invoke_count.len() {
3553 self.sub_entry_invoke_count.resize(sub_ip + 1, 0);
3554 }
3555 let c = &mut self.sub_entry_invoke_count[sub_ip];
3556 if *c <= self.jit_sub_invoke_threshold {
3557 *c = c.saturating_add(1);
3558 }
3559 let should_try_jit = *c > self.jit_sub_invoke_threshold
3560 && (!self.sub_jit_skip_linear_test(sub_ip)
3561 || !self.sub_jit_skip_block_test(sub_ip));
3562 if should_try_jit {
3563 if self.try_fusevm_subroutine()? {
3567 continue;
3568 }
3569 if !self.sub_jit_skip_linear_test(sub_ip) && self.try_jit_subroutine_linear()? {
3570 continue;
3571 }
3572 if !self.sub_jit_skip_block_test(sub_ip) && self.try_jit_subroutine_block()? {
3573 continue;
3574 }
3575 }
3576 }
3577
3578 *op_count += 1;
3579 if (*op_count & 0x3FF) == 0 {
3582 crate::perl_signal::poll(self.interp)?;
3583 if *op_count > MAX_OPS {
3584 return Err(StrykeError::runtime(
3585 "VM execution limit exceeded (possible infinite loop)",
3586 self.line(),
3587 ));
3588 }
3589 }
3590
3591 let ip_before = self.ip;
3592 let line = self.lines.get(ip_before).copied().unwrap_or(0);
3593 let op = &ops[self.ip];
3594 self.ip += 1;
3595
3596 if let Some(ref mut dbg) = self.interp.debugger {
3598 if dbg.should_stop(line) {
3599 let call_stack = self.interp.debug_call_stack.clone();
3600 match dbg.prompt(line, &self.interp.scope, &call_stack) {
3601 crate::debugger::DebugAction::Quit => {
3602 return Err(StrykeError::runtime("debugger: quit", line));
3603 }
3604 crate::debugger::DebugAction::Continue => {}
3605 crate::debugger::DebugAction::Prompt => {}
3606 }
3607 }
3608 }
3609
3610 let op_prof_t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3611 let __op_res: StrykeResult<()> = (|| -> StrykeResult<()> {
3615 match op {
3616 Op::Nop => Ok(()),
3617 Op::LoadInt(n) => {
3619 self.push(StrykeValue::integer(*n));
3620 Ok(())
3621 }
3622 Op::LoadFloat(f) => {
3623 self.push(StrykeValue::float(*f));
3624 Ok(())
3625 }
3626 Op::LoadConst(idx) => {
3627 self.push(self.constant(*idx).clone());
3628 Ok(())
3629 }
3630 Op::LoadUndef => {
3631 self.push(StrykeValue::UNDEF);
3632 Ok(())
3633 }
3634 Op::RuntimeErrorConst(idx) => {
3635 let msg = self.constant(*idx).to_string();
3636 let line = self.line();
3637 Err(crate::error::StrykeError::runtime(msg, line))
3638 }
3639 Op::BarewordRvalue(name_idx) => {
3640 let name = names[*name_idx as usize].clone();
3641 let line = self.line();
3642 let out = vm_interp_result(
3643 self.interp.resolve_bareword_rvalue(
3644 &name,
3645 crate::vm_helper::WantarrayCtx::Scalar,
3646 line,
3647 ),
3648 line,
3649 )?;
3650 self.push(out);
3651 Ok(())
3652 }
3653
3654 Op::Pop => {
3656 let v = self.pop();
3657 if v.is_iterator() {
3659 let iter = v.into_iterator();
3660 while iter.next_item().is_some() {}
3661 }
3662 Ok(())
3663 }
3664 Op::Dup => {
3665 let v = self.peek().dup_stack();
3666 self.push(v);
3667 Ok(())
3668 }
3669 Op::Dup2 => {
3670 let b = self.pop();
3671 let a = self.pop();
3672 self.push(a.dup_stack());
3673 self.push(b.dup_stack());
3674 self.push(a);
3675 self.push(b);
3676 Ok(())
3677 }
3678 Op::Swap => {
3679 let top = self.pop();
3680 let below = self.pop();
3681 self.push(top);
3682 self.push(below);
3683 Ok(())
3684 }
3685 Op::Rot => {
3686 let c = self.pop();
3687 let b = self.pop();
3688 let a = self.pop();
3689 self.push(b);
3690 self.push(c);
3691 self.push(a);
3692 Ok(())
3693 }
3694 Op::ValueScalarContext => {
3695 let v = self.pop();
3696 self.push(v.scalar_context());
3697 Ok(())
3698 }
3699 Op::ListFirst => {
3700 let v = self.pop();
3701 let first = if let Some(arr) = v.as_array_vec() {
3702 arr.first().cloned().unwrap_or(StrykeValue::UNDEF)
3703 } else {
3704 v
3705 };
3706 self.push(first);
3707 Ok(())
3708 }
3709
3710 Op::GetScalar(idx) => {
3712 let n = names[*idx as usize].as_str();
3713 let val = self.interp.get_special_var(n);
3714 self.push(val);
3715 Ok(())
3716 }
3717 Op::GetScalarPlain(idx) => {
3718 let n = names[*idx as usize].as_str();
3719 let val = self.interp.scope.get_scalar(n);
3720 self.push(val);
3721 Ok(())
3722 }
3723 Op::SetScalar(idx) => {
3724 let val = self.pop();
3725 let n = names[*idx as usize].as_str();
3726 self.require_scalar_mutable(n)?;
3727 self.interp.maybe_invalidate_regex_capture_memo(n);
3728 self.interp
3729 .set_special_var(n, &val)
3730 .map_err(|e| e.at_line(self.line()))?;
3731 Ok(())
3732 }
3733 Op::SetScalarPlain(idx) => {
3734 let val = self.pop();
3735 let n = names[*idx as usize].as_str();
3736 self.require_scalar_mutable(n)?;
3737 self.interp.maybe_invalidate_regex_capture_memo(n);
3738 self.interp
3739 .scope
3740 .set_scalar(n, val)
3741 .map_err(|e| e.at_line(self.line()))?;
3742 Ok(())
3743 }
3744 Op::SetScalarKeep(idx) => {
3745 let val = self.peek().dup_stack();
3746 let n = names[*idx as usize].as_str();
3747 self.require_scalar_mutable(n)?;
3748 self.interp.maybe_invalidate_regex_capture_memo(n);
3749 self.interp
3750 .set_special_var(n, &val)
3751 .map_err(|e| e.at_line(self.line()))?;
3752 Ok(())
3753 }
3754 Op::SetScalarKeepPlain(idx) => {
3755 let val = self.peek().dup_stack();
3756 let n = names[*idx as usize].as_str();
3757 self.require_scalar_mutable(n)?;
3758 self.interp.maybe_invalidate_regex_capture_memo(n);
3759 self.interp
3760 .scope
3761 .set_scalar(n, val)
3762 .map_err(|e| e.at_line(self.line()))?;
3763 Ok(())
3764 }
3765 Op::DeclareScalar(idx) => {
3766 let val = self.pop();
3767 let n = names[*idx as usize].as_str();
3768 self.interp
3769 .scope
3770 .declare_scalar_frozen(n, val, false, None)
3771 .map_err(|e| e.at_line(self.line()))?;
3772 Ok(())
3773 }
3774 Op::DeclareScalarFrozen(idx) => {
3775 let val = self.pop();
3776 let n = names[*idx as usize].as_str();
3777 self.interp
3778 .scope
3779 .declare_scalar_frozen(n, val, true, None)
3780 .map_err(|e| e.at_line(self.line()))?;
3781 Ok(())
3782 }
3783 Op::DeclareScalarTyped(idx, tyb) => {
3784 let val = self.pop();
3785 let n = names[*idx as usize].as_str();
3786 let ty = PerlTypeName::from_byte(*tyb).ok_or_else(|| {
3787 StrykeError::runtime(
3788 format!("invalid typed scalar type byte {}", tyb),
3789 self.line(),
3790 )
3791 })?;
3792 self.interp
3793 .scope
3794 .declare_scalar_frozen(n, val, false, Some(ty))
3795 .map_err(|e| e.at_line(self.line()))?;
3796 Ok(())
3797 }
3798 Op::DeclareScalarTypedFrozen(idx, tyb) => {
3799 let val = self.pop();
3800 let n = names[*idx as usize].as_str();
3801 let ty = PerlTypeName::from_byte(*tyb).ok_or_else(|| {
3802 StrykeError::runtime(
3803 format!("invalid typed scalar type byte {}", tyb),
3804 self.line(),
3805 )
3806 })?;
3807 self.interp
3808 .scope
3809 .declare_scalar_frozen(n, val, true, Some(ty))
3810 .map_err(|e| e.at_line(self.line()))?;
3811 Ok(())
3812 }
3813 Op::DeclareScalarTypedUser(name_idx, type_idx, flag) => {
3814 let val = self.pop();
3815 let n = names[*name_idx as usize].as_str();
3816 let type_name = names[*type_idx as usize].clone();
3817 let is_enum = (flag & 0b01) != 0;
3818 let is_frozen = (flag & 0b10) != 0;
3819 let ty = if is_enum {
3820 PerlTypeName::Enum(type_name)
3821 } else {
3822 PerlTypeName::Struct(type_name)
3827 };
3828 self.interp
3829 .scope
3830 .declare_scalar_frozen(n, val, is_frozen, Some(ty))
3831 .map_err(|e| e.at_line(self.line()))?;
3832 Ok(())
3833 }
3834
3835 Op::DeclareStateScalar(idx) => {
3837 let init_val = self.pop();
3838 let n = names[*idx as usize].as_str();
3839 let state_key = format!("{}:{}", self.line(), n);
3841 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3842 prev.clone()
3843 } else {
3844 self.interp
3845 .state_vars
3846 .insert(state_key.clone(), init_val.clone());
3847 init_val
3848 };
3849 self.interp
3850 .scope
3851 .declare_scalar_frozen(n, val, false, None)
3852 .map_err(|e| e.at_line(self.line()))?;
3853 if let Some(frame) = self.interp.state_bindings_stack.last_mut() {
3855 frame.push((n.to_string(), state_key));
3856 }
3857 Ok(())
3858 }
3859 Op::DeclareStateArray(idx) => {
3860 let init_val = self.pop();
3861 let n = names[*idx as usize].as_str();
3862 let state_key = format!("{}:{}", self.line(), n);
3863 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3864 prev.clone()
3865 } else {
3866 self.interp
3867 .state_vars
3868 .insert(state_key.clone(), init_val.clone());
3869 init_val
3870 };
3871 self.interp.scope.declare_array(n, val.to_list());
3872 Ok(())
3873 }
3874 Op::DeclareStateHash(idx) => {
3875 let init_val = self.pop();
3876 let n = names[*idx as usize].as_str();
3877 let state_key = format!("{}:{}", self.line(), n);
3878 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3879 prev.clone()
3880 } else {
3881 self.interp
3882 .state_vars
3883 .insert(state_key.clone(), init_val.clone());
3884 init_val
3885 };
3886 let items = val.to_list();
3887 let mut map = IndexMap::new();
3888 let mut i = 0;
3889 while i + 1 < items.len() {
3890 map.insert(items[i].to_string(), items[i + 1].clone());
3891 i += 2;
3892 }
3893 self.interp.scope.declare_hash(n, map);
3894 Ok(())
3895 }
3896
3897 Op::GetArray(idx) => {
3899 let n = names[*idx as usize].as_str();
3900 let arr = self.interp.scope.get_array(n);
3901 self.push(StrykeValue::array(arr));
3902 Ok(())
3903 }
3904 Op::SetArray(idx) => {
3905 let val = self.pop();
3906 let n = names[*idx as usize].as_str();
3907 self.require_array_mutable(n)?;
3908 self.interp
3909 .scope
3910 .set_array(n, val.to_list())
3911 .map_err(|e| e.at_line(self.line()))?;
3912 Ok(())
3913 }
3914 Op::DeclareArray(idx) => {
3915 let val = self.pop();
3916 let n = names[*idx as usize].as_str();
3917 self.interp.scope.declare_array(n, val.to_list());
3918 Ok(())
3919 }
3920 Op::DeclareArrayFrozen(idx) => {
3921 let val = self.pop();
3922 let n = names[*idx as usize].as_str();
3923 self.interp
3924 .scope
3925 .declare_array_frozen(n, val.to_list(), true);
3926 Ok(())
3927 }
3928 Op::GetArrayElem(idx) => {
3929 let index = self.pop().to_int();
3930 let n = names[*idx as usize].as_str();
3931 if let Some(real) = n.strip_prefix("__topicstr__") {
3935 let s = self.interp.scope.get_scalar(real).to_string();
3936 let cnt = s.chars().count() as i64;
3937 let i = if index < 0 { index + cnt } else { index };
3938 let v = if i >= 0 && i < cnt {
3939 s.chars()
3940 .nth(i as usize)
3941 .map(|c| StrykeValue::string(c.to_string()))
3942 .unwrap_or(StrykeValue::UNDEF)
3943 } else {
3944 StrykeValue::UNDEF
3945 };
3946 self.push(v);
3947 return Ok(());
3948 }
3949 if !crate::compat_mode() && self.interp.scope.scalar_binding_exists(n) {
3958 let prefer_scalar = self.interp.scope.get_array(n).is_empty();
3959 if prefer_scalar {
3960 let s = self.interp.scope.get_scalar(n).to_string();
3961 if !s.is_empty() {
3962 let cnt = s.chars().count() as i64;
3963 let i = if index < 0 { index + cnt } else { index };
3964 let v = if i >= 0 && i < cnt {
3965 s.chars()
3966 .nth(i as usize)
3967 .map(|c| StrykeValue::string(c.to_string()))
3968 .unwrap_or(StrykeValue::UNDEF)
3969 } else {
3970 StrykeValue::UNDEF
3971 };
3972 self.push(v);
3973 return Ok(());
3974 }
3975 }
3976 }
3977 let val = self.interp.scope.get_array_element(n, index);
3978 self.push(val);
3979 Ok(())
3980 }
3981 Op::ExistsArrayElem(idx) => {
3982 let index = self.pop().to_int();
3983 let n = names[*idx as usize].as_str();
3984 let yes = self.interp.scope.exists_array_element(n, index);
3985 self.push(StrykeValue::integer(if yes { 1 } else { 0 }));
3986 Ok(())
3987 }
3988 Op::DeleteArrayElem(idx) => {
3989 let index = self.pop().to_int();
3990 let n = names[*idx as usize].as_str();
3991 self.require_array_mutable(n)?;
3992 let v = self
3993 .interp
3994 .scope
3995 .delete_array_element(n, index)
3996 .map_err(|e| e.at_line(self.line()))?;
3997 self.push(v);
3998 Ok(())
3999 }
4000 Op::SetArrayElem(idx) => {
4001 let index = self.pop().to_int();
4002 let val = self.pop();
4003 let n = names[*idx as usize].as_str();
4004 self.require_array_mutable(n)?;
4005 self.interp
4006 .scope
4007 .set_array_element(n, index, val)
4008 .map_err(|e| e.at_line(self.line()))?;
4009 Ok(())
4010 }
4011 Op::SetArrayElemKeep(idx) => {
4012 let index = self.pop().to_int();
4013 let val = self.pop();
4014 let val_keep = val.clone();
4015 let n = names[*idx as usize].as_str();
4016 self.require_array_mutable(n)?;
4017 let line = self.line();
4018 self.interp
4019 .scope
4020 .set_array_element(n, index, val)
4021 .map_err(|e| e.at_line(line))?;
4022 self.push(val_keep);
4023 Ok(())
4024 }
4025 Op::PushArray(idx) => {
4026 let val = self.pop();
4027 let n = names[*idx as usize].as_str();
4028 self.require_array_mutable(n)?;
4029 let line = self.line();
4030 if let Some(items) = val.as_array_vec() {
4031 for item in items {
4032 self.interp
4033 .scope
4034 .push_to_array(n, item)
4035 .map_err(|e| e.at_line(line))?;
4036 }
4037 } else {
4038 self.interp
4039 .scope
4040 .push_to_array(n, val)
4041 .map_err(|e| e.at_line(line))?;
4042 }
4043 Ok(())
4044 }
4045 Op::PopArray(idx) => {
4046 let n = names[*idx as usize].as_str();
4047 self.require_array_mutable(n)?;
4048 let line = self.line();
4049 let val = self
4050 .interp
4051 .scope
4052 .pop_from_array(n)
4053 .map_err(|e| e.at_line(line))?;
4054 self.push(val);
4055 Ok(())
4056 }
4057 Op::ShiftArray(idx) => {
4058 let n = names[*idx as usize].as_str();
4059 self.require_array_mutable(n)?;
4060 let line = self.line();
4061 let val = self
4062 .interp
4063 .scope
4064 .shift_from_array(n)
4065 .map_err(|e| e.at_line(line))?;
4066 self.push(val);
4067 Ok(())
4068 }
4069 Op::PushArrayDeref => {
4070 let val = self.pop();
4071 let r = self.pop();
4072 let line = self.line();
4073 vm_interp_result(
4074 self.interp
4075 .push_array_deref_value(r.clone(), val, line)
4076 .map(|_| StrykeValue::UNDEF),
4077 line,
4078 )?;
4079 self.push(r);
4080 Ok(())
4081 }
4082 Op::ArrayDerefLen => {
4083 let r = self.pop();
4084 let line = self.line();
4085 let n = match self.interp.array_deref_len(r, line) {
4086 Ok(n) => n,
4087 Err(FlowOrError::Error(e)) => return Err(e),
4088 Err(FlowOrError::Flow(_)) => {
4089 return Err(StrykeError::runtime(
4090 "unexpected flow in tree-assisted opcode",
4091 line,
4092 ));
4093 }
4094 };
4095 self.push(StrykeValue::integer(n));
4096 Ok(())
4097 }
4098 Op::PopArrayDeref => {
4099 let r = self.pop();
4100 let line = self.line();
4101 let v = vm_interp_result(self.interp.pop_array_deref(r, line), line)?;
4102 self.push(v);
4103 Ok(())
4104 }
4105 Op::ShiftArrayDeref => {
4106 let r = self.pop();
4107 let line = self.line();
4108 let v = vm_interp_result(self.interp.shift_array_deref(r, line), line)?;
4109 self.push(v);
4110 Ok(())
4111 }
4112 Op::UnshiftArrayDeref(n_extra) => {
4113 let n = *n_extra as usize;
4114 let mut vals: Vec<StrykeValue> = Vec::with_capacity(n);
4115 for _ in 0..n {
4116 vals.push(self.pop());
4117 }
4118 vals.reverse();
4119 let r = self.pop();
4120 let line = self.line();
4121 let len = match self.interp.unshift_array_deref_multi(r, vals, line) {
4122 Ok(n) => n,
4123 Err(FlowOrError::Error(e)) => return Err(e),
4124 Err(FlowOrError::Flow(_)) => {
4125 return Err(StrykeError::runtime(
4126 "unexpected flow in tree-assisted opcode",
4127 line,
4128 ));
4129 }
4130 };
4131 self.push(StrykeValue::integer(len));
4132 Ok(())
4133 }
4134 Op::SpliceArrayDeref(n_rep) => {
4135 let n = *n_rep as usize;
4136 let mut rep_vals: Vec<StrykeValue> = Vec::with_capacity(n);
4137 for _ in 0..n {
4138 rep_vals.push(self.pop());
4139 }
4140 rep_vals.reverse();
4141 let length_val = self.pop();
4142 let offset_val = self.pop();
4143 let aref = self.pop();
4144 let line = self.line();
4145 let v = vm_interp_result(
4146 self.interp
4147 .splice_array_deref(aref, offset_val, length_val, rep_vals, line),
4148 line,
4149 )?;
4150 self.push(v);
4151 Ok(())
4152 }
4153 Op::ArrayLen(idx) => {
4154 let len = self.interp.scope.array_len(&self.names[*idx as usize]);
4155 self.push(StrykeValue::integer(len as i64));
4156 Ok(())
4157 }
4158 Op::ArraySlicePart(idx) => {
4159 let spec = self.pop();
4160 let n = names[*idx as usize].as_str();
4161 let mut out = Vec::new();
4162 if let Some(indices) = spec.as_array_vec() {
4163 for pv in indices {
4164 out.push(self.interp.scope.get_array_element(n, pv.to_int()));
4165 }
4166 } else {
4167 out.push(self.interp.scope.get_array_element(n, spec.to_int()));
4168 }
4169 self.push(StrykeValue::array(out));
4170 Ok(())
4171 }
4172 Op::GetArrayFromIndex(idx, start) => {
4173 let n = names[*idx as usize].as_str();
4174 let arr = self.interp.scope.get_array(n);
4175 let start = *start as usize;
4176 let out: Vec<StrykeValue> = if start >= arr.len() {
4177 Vec::new()
4178 } else {
4179 arr[start..].to_vec()
4180 };
4181 self.push(StrykeValue::array(out));
4182 Ok(())
4183 }
4184 Op::ArrayConcatTwo => {
4185 let b = self.pop();
4186 let a = self.pop();
4187 let mut av = a.as_array_vec().unwrap_or_else(|| vec![a]);
4188 let bv = b.as_array_vec().unwrap_or_else(|| vec![b]);
4189 av.extend(bv);
4190 self.push(StrykeValue::array(av));
4191 Ok(())
4192 }
4193
4194 Op::GetHash(idx) => {
4196 let n = names[*idx as usize].as_str();
4197 self.interp.touch_env_hash(n);
4198 let h = self.interp.scope.get_hash(n);
4199 self.push(StrykeValue::hash(h));
4200 Ok(())
4201 }
4202 Op::SetHash(idx) => {
4203 let val = self.pop();
4204 let items = val.to_list();
4205 let mut map = IndexMap::new();
4206 let mut i = 0;
4207 while i + 1 < items.len() {
4208 map.insert(items[i].to_string(), items[i + 1].clone());
4209 i += 2;
4210 }
4211 let n = names[*idx as usize].as_str();
4212 self.require_hash_mutable(n)?;
4213 self.interp
4214 .scope
4215 .set_hash(n, map)
4216 .map_err(|e| e.at_line(self.line()))?;
4217 Ok(())
4218 }
4219 Op::DeclareHash(idx) => {
4220 let val = self.pop();
4221 let n = names[*idx as usize].as_str();
4222 if val.is_undef() && n.contains("::") {
4237 let existing = self.interp.scope.get_hash(n);
4238 self.interp.scope.declare_hash(n, existing);
4239 } else {
4240 let items = val.to_list();
4241 let mut map = IndexMap::new();
4242 let mut i = 0;
4243 while i + 1 < items.len() {
4244 map.insert(items[i].to_string(), items[i + 1].clone());
4245 i += 2;
4246 }
4247 self.interp.scope.declare_hash(n, map);
4248 }
4249 Ok(())
4250 }
4251 Op::DeclareHashFrozen(idx) => {
4252 let val = self.pop();
4253 let items = val.to_list();
4254 let mut map = IndexMap::new();
4255 let mut i = 0;
4256 while i + 1 < items.len() {
4257 map.insert(items[i].to_string(), items[i + 1].clone());
4258 i += 2;
4259 }
4260 let n = names[*idx as usize].as_str();
4261 self.interp.scope.declare_hash_frozen(n, map, true);
4262 Ok(())
4263 }
4264 Op::LocalDeclareScalar(idx) => {
4265 let val = self.pop();
4266 let n = names[*idx as usize].as_str();
4267 if VMHelper::is_special_scalar_name_for_set(n) {
4272 let old = self.interp.get_special_var(n);
4273 if let Some(frame) = self.interp.special_var_restore_frames.last_mut() {
4274 frame.push((n.to_string(), old));
4275 }
4276 let line = self.line();
4277 self.interp
4278 .set_special_var(n, &val)
4279 .map_err(|e| e.at_line(line))?;
4280 }
4281 self.interp
4282 .scope
4283 .local_set_scalar(n, val.clone())
4284 .map_err(|e| e.at_line(self.line()))?;
4285 self.push(val);
4286 Ok(())
4287 }
4288 Op::LocalDeclareArray(idx) => {
4289 let val = self.pop();
4290 let n = names[*idx as usize].as_str();
4291 self.interp
4292 .scope
4293 .local_set_array(n, val.to_list())
4294 .map_err(|e| e.at_line(self.line()))?;
4295 self.push(val);
4296 Ok(())
4297 }
4298 Op::LocalDeclareHash(idx) => {
4299 let val = self.pop();
4300 let items = val.to_list();
4301 let mut map = IndexMap::new();
4302 let mut i = 0;
4303 while i + 1 < items.len() {
4304 map.insert(items[i].to_string(), items[i + 1].clone());
4305 i += 2;
4306 }
4307 let n = names[*idx as usize].as_str();
4308 self.interp.touch_env_hash(n);
4309 self.interp
4310 .scope
4311 .local_set_hash(n, map)
4312 .map_err(|e| e.at_line(self.line()))?;
4313 self.push(val);
4314 Ok(())
4315 }
4316 Op::LocalDeclareHashElement(idx) => {
4317 let key = self.pop().to_string();
4318 let val = self.pop();
4319 let n = names[*idx as usize].as_str();
4320 self.interp.touch_env_hash(n);
4321 self.interp
4322 .scope
4323 .local_set_hash_element(n, key.as_str(), val.clone())
4324 .map_err(|e| e.at_line(self.line()))?;
4325 self.push(val);
4326 Ok(())
4327 }
4328 Op::LocalDeclareArrayElement(idx) => {
4329 let index = self.pop().to_int();
4330 let val = self.pop();
4331 let n = names[*idx as usize].as_str();
4332 self.require_array_mutable(n)?;
4333 self.interp
4334 .scope
4335 .local_set_array_element(n, index, val.clone())
4336 .map_err(|e| e.at_line(self.line()))?;
4337 self.push(val);
4338 Ok(())
4339 }
4340 Op::LocalDeclareTypeglob(lhs_i, rhs_opt) => {
4341 let lhs = names[*lhs_i as usize].as_str();
4342 let rhs = rhs_opt.map(|i| names[i as usize].as_str());
4343 let line = self.line();
4344 self.interp
4345 .local_declare_typeglob(lhs, rhs, line)
4346 .map_err(|e| e.at_line(line))?;
4347 Ok(())
4348 }
4349 Op::LocalDeclareTypeglobDynamic(rhs_opt) => {
4350 let lhs = self.pop().to_string();
4351 let rhs = rhs_opt.map(|i| names[i as usize].as_str());
4352 let line = self.line();
4353 self.interp
4354 .local_declare_typeglob(lhs.as_str(), rhs, line)
4355 .map_err(|e| e.at_line(line))?;
4356 Ok(())
4357 }
4358 Op::GetHashElem(idx) => {
4359 let key = self.pop().to_string();
4360 let n = names[*idx as usize].as_str();
4361 self.interp.touch_env_hash(n);
4362 let val = self.interp.scope.get_hash_element(n, &key);
4363 self.push(val);
4364 Ok(())
4365 }
4366 Op::SetHashElem(idx) => {
4367 let key = self.pop().to_string();
4368 let val = self.pop();
4369 let n = names[*idx as usize].as_str();
4370 self.require_hash_mutable(n)?;
4371 self.interp.touch_env_hash(n);
4372 self.interp
4373 .scope
4374 .set_hash_element(n, &key, val)
4375 .map_err(|e| e.at_line(self.line()))?;
4376 Ok(())
4377 }
4378 Op::SetHashElemKeep(idx) => {
4379 let key = self.pop().to_string();
4380 let val = self.pop();
4381 let val_keep = val.clone();
4382 let n = names[*idx as usize].as_str();
4383 self.require_hash_mutable(n)?;
4384 self.interp.touch_env_hash(n);
4385 let line = self.line();
4386 self.interp
4387 .scope
4388 .set_hash_element(n, &key, val)
4389 .map_err(|e| e.at_line(line))?;
4390 self.push(val_keep);
4391 Ok(())
4392 }
4393 Op::DeleteHashElem(idx) => {
4394 let key = self.pop().to_string();
4395 let n = names[*idx as usize].as_str();
4396 self.require_hash_mutable(n)?;
4397 self.interp.touch_env_hash(n);
4398 if let Some(obj) = self.interp.tied_hashes.get(n).cloned() {
4399 let class = obj
4400 .as_blessed_ref()
4401 .map(|b| b.class.clone())
4402 .unwrap_or_default();
4403 let full = format!("{}::DELETE", class);
4404 if let Some(sub) = self.interp.subs.get(&full).cloned() {
4405 let line = self.line();
4406 let v = vm_interp_result(
4407 self.interp.call_sub(
4408 &sub,
4409 vec![obj, StrykeValue::string(key)],
4410 WantarrayCtx::Scalar,
4411 line,
4412 ),
4413 line,
4414 )?;
4415 self.push(v);
4416 return Ok(());
4417 }
4418 }
4419 let val = self
4420 .interp
4421 .scope
4422 .delete_hash_element(n, &key)
4423 .map_err(|e| e.at_line(self.line()))?;
4424 self.push(val);
4425 Ok(())
4426 }
4427 Op::ExistsHashElem(idx) => {
4428 let key = self.pop().to_string();
4429 let n = names[*idx as usize].as_str();
4430 self.interp.touch_env_hash(n);
4431 if let Some(obj) = self.interp.tied_hashes.get(n).cloned() {
4432 let class = obj
4433 .as_blessed_ref()
4434 .map(|b| b.class.clone())
4435 .unwrap_or_default();
4436 let full = format!("{}::EXISTS", class);
4437 if let Some(sub) = self.interp.subs.get(&full).cloned() {
4438 let line = self.line();
4439 let v = vm_interp_result(
4440 self.interp.call_sub(
4441 &sub,
4442 vec![obj, StrykeValue::string(key)],
4443 WantarrayCtx::Scalar,
4444 line,
4445 ),
4446 line,
4447 )?;
4448 self.push(v);
4449 return Ok(());
4450 }
4451 }
4452 let exists = self.interp.scope.exists_hash_element(n, &key);
4453 self.push(StrykeValue::integer(if exists { 1 } else { 0 }));
4454 Ok(())
4455 }
4456 Op::ExistsArrowHashElem => {
4457 let key = self.pop().to_string();
4458 let container = self.pop();
4459 let line = self.line();
4460 let yes = vm_interp_result(
4461 self.interp
4462 .exists_arrow_hash_element(container, &key, line)
4463 .map(|b| StrykeValue::integer(if b { 1 } else { 0 }))
4464 .map_err(FlowOrError::Error),
4465 line,
4466 )?;
4467 self.push(yes);
4468 Ok(())
4469 }
4470 Op::DeleteArrowHashElem => {
4471 let key = self.pop().to_string();
4472 let container = self.pop();
4473 let line = self.line();
4474 let v = vm_interp_result(
4475 self.interp
4476 .delete_arrow_hash_element(container, &key, line)
4477 .map_err(FlowOrError::Error),
4478 line,
4479 )?;
4480 self.push(v);
4481 Ok(())
4482 }
4483 Op::ExistsArrowArrayElem => {
4484 let idx = self.pop().to_int();
4485 let container = self.pop();
4486 let line = self.line();
4487 let yes = vm_interp_result(
4488 self.interp
4489 .exists_arrow_array_element(container, idx, line)
4490 .map(|b| StrykeValue::integer(if b { 1 } else { 0 }))
4491 .map_err(FlowOrError::Error),
4492 line,
4493 )?;
4494 self.push(yes);
4495 Ok(())
4496 }
4497 Op::DeleteArrowArrayElem => {
4498 let idx = self.pop().to_int();
4499 let container = self.pop();
4500 let line = self.line();
4501 let v = vm_interp_result(
4502 self.interp
4503 .delete_arrow_array_element(container, idx, line)
4504 .map_err(FlowOrError::Error),
4505 line,
4506 )?;
4507 self.push(v);
4508 Ok(())
4509 }
4510 Op::HashKeys(idx) => {
4511 let n = names[*idx as usize].as_str();
4512 self.interp.touch_env_hash(n);
4513 let h = self.interp.scope.get_hash(n);
4514 let keys: Vec<StrykeValue> =
4515 h.keys().map(|k| StrykeValue::string(k.clone())).collect();
4516 self.push(StrykeValue::array(keys));
4517 Ok(())
4518 }
4519 Op::HashKeysScalar(idx) => {
4520 let n = names[*idx as usize].as_str();
4521 self.interp.touch_env_hash(n);
4522 let h = self.interp.scope.get_hash(n);
4523 self.push(StrykeValue::integer(h.len() as i64));
4524 Ok(())
4525 }
4526 Op::HashValues(idx) => {
4527 let n = names[*idx as usize].as_str();
4528 self.interp.touch_env_hash(n);
4529 let h = self.interp.scope.get_hash(n);
4530 let vals: Vec<StrykeValue> = h.values().cloned().collect();
4531 self.push(StrykeValue::array(vals));
4532 Ok(())
4533 }
4534 Op::HashValuesScalar(idx) => {
4535 let n = names[*idx as usize].as_str();
4536 self.interp.touch_env_hash(n);
4537 let h = self.interp.scope.get_hash(n);
4538 self.push(StrykeValue::integer(h.len() as i64));
4539 Ok(())
4540 }
4541 Op::KeysFromValue => {
4542 let val = self.pop();
4543 let line = self.line();
4544 let v = vm_interp_result(VMHelper::keys_from_value(val, line), line)?;
4545 self.push(v);
4546 Ok(())
4547 }
4548 Op::KeysFromValueScalar => {
4549 let val = self.pop();
4550 let line = self.line();
4551 let v = vm_interp_result(VMHelper::keys_from_value(val, line), line)?;
4552 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
4553 self.push(StrykeValue::integer(n));
4554 Ok(())
4555 }
4556 Op::ValuesFromValue => {
4557 let val = self.pop();
4558 let line = self.line();
4559 let v = vm_interp_result(VMHelper::values_from_value(val, line), line)?;
4560 self.push(v);
4561 Ok(())
4562 }
4563 Op::ValuesFromValueScalar => {
4564 let val = self.pop();
4565 let line = self.line();
4566 let v = vm_interp_result(VMHelper::values_from_value(val, line), line)?;
4567 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
4568 self.push(StrykeValue::integer(n));
4569 Ok(())
4570 }
4571
4572 Op::Add => {
4574 let b = self.pop();
4575 let a = self.pop();
4576 self.push_binop_with_overload(BinOp::Add, a, b, |a, b| {
4577 if let Some(s) = crate::sketches::try_sketch_binop(
4578 crate::sketches::SketchOp::Add,
4579 a,
4580 b,
4581 ) {
4582 return Ok(s);
4583 }
4584 Ok(crate::value::compat_add(a, b))
4585 })
4586 }
4587 Op::Sub => {
4588 let b = self.pop();
4589 let a = self.pop();
4590 self.push_binop_with_overload(BinOp::Sub, a, b, |a, b| {
4591 if let Some(s) = crate::sketches::try_sketch_binop(
4592 crate::sketches::SketchOp::Sub,
4593 a,
4594 b,
4595 ) {
4596 return Ok(s);
4597 }
4598 Ok(crate::value::compat_sub(a, b))
4599 })
4600 }
4601 Op::Mul => {
4602 let b = self.pop();
4603 let a = self.pop();
4604 self.push_binop_with_overload(BinOp::Mul, a, b, |a, b| {
4605 Ok(crate::value::compat_mul(a, b))
4606 })
4607 }
4608 Op::Div => {
4609 let b = self.pop();
4610 let a = self.pop();
4611 let line = self.line();
4612 self.push_binop_with_overload(BinOp::Div, a, b, |a, b| {
4613 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
4614 if y == 0 {
4615 return Err(StrykeError::division_by_zero(
4616 "Illegal division by zero",
4617 line,
4618 ));
4619 }
4620 Ok(if x % y == 0 {
4621 StrykeValue::integer(x / y)
4622 } else {
4623 StrykeValue::float(x as f64 / y as f64)
4624 })
4625 } else {
4626 let d = b.to_number();
4627 if d == 0.0 {
4628 return Err(StrykeError::division_by_zero(
4629 "Illegal division by zero",
4630 line,
4631 ));
4632 }
4633 Ok(StrykeValue::float(a.to_number() / d))
4634 }
4635 })
4636 }
4637 Op::Mod => {
4638 let b = self.pop();
4639 let a = self.pop();
4640 let line = self.line();
4641 self.push_binop_with_overload(BinOp::Mod, a, b, |a, b| {
4642 let b = b.to_int();
4643 let a = a.to_int();
4644 if b == 0 {
4645 return Err(StrykeError::division_by_zero(
4646 "Illegal modulus zero",
4647 line,
4648 ));
4649 }
4650 Ok(StrykeValue::integer(crate::value::perl_mod_i64(a, b)))
4651 })
4652 }
4653 Op::Pow => {
4654 let b = self.pop();
4655 let a = self.pop();
4656 self.push_binop_with_overload(BinOp::Pow, a, b, |a, b| {
4657 Ok(crate::value::compat_pow(a, b))
4658 })
4659 }
4660 Op::Negate => {
4661 let a = self.pop();
4662 let line = self.line();
4663 if let Some(exec_res) =
4664 self.interp.try_overload_unary_dispatch("neg", &a, line)
4665 {
4666 self.push(vm_interp_result(exec_res, line)?);
4667 } else {
4668 self.push(if let Some(n) = a.as_integer() {
4669 StrykeValue::integer(-n)
4670 } else {
4671 StrykeValue::float(-a.to_number())
4672 });
4673 }
4674 Ok(())
4675 }
4676 Op::Inc => {
4677 let a = self.pop();
4678 self.push(if let Some(n) = a.as_integer() {
4679 StrykeValue::integer(n.wrapping_add(1))
4680 } else {
4681 StrykeValue::float(a.to_number() + 1.0)
4682 });
4683 Ok(())
4684 }
4685 Op::Dec => {
4686 let a = self.pop();
4687 self.push(if let Some(n) = a.as_integer() {
4688 StrykeValue::integer(n.wrapping_sub(1))
4689 } else {
4690 StrykeValue::float(a.to_number() - 1.0)
4691 });
4692 Ok(())
4693 }
4694
4695 Op::Concat => {
4697 let b = self.pop();
4698 let a = self.pop();
4699 let out = self.concat_stack_values(a, b)?;
4700 self.push(out);
4701 Ok(())
4702 }
4703 Op::ArrayStringifyListSep => {
4704 let raw = self.pop();
4705 let v = self.interp.peel_array_ref_for_list_join(raw);
4706 let sep = self.interp.list_separator.clone();
4707 let list = v.to_list();
4708 let joined = list
4709 .iter()
4710 .map(|x| x.to_string())
4711 .collect::<Vec<_>>()
4712 .join(&sep);
4713 self.push(StrykeValue::string(joined));
4714 Ok(())
4715 }
4716 Op::StringRepeat => {
4717 let n = self.pop().to_int();
4718 let val = self.pop();
4719 self.push(StrykeValue::string(val.repeat_value(n)));
4720 Ok(())
4721 }
4722 Op::ListRepeat => {
4723 let n = self.pop().to_int().max(0) as usize;
4724 let val = self.pop();
4725 let items: Vec<StrykeValue> =
4729 val.as_array_vec().unwrap_or_else(|| vec![val]);
4730 let mut out = Vec::with_capacity(items.len().saturating_mul(n));
4731 for _ in 0..n {
4732 out.extend(items.iter().cloned());
4733 }
4734 self.push(StrykeValue::array(out));
4735 Ok(())
4736 }
4737 Op::ProcessCaseEscapes => {
4738 let val = self.pop();
4739 let s = val.to_string();
4740 let processed = VMHelper::process_case_escapes(&s);
4741 self.push(StrykeValue::string(processed));
4742 Ok(())
4743 }
4744
4745 Op::NumEq => {
4747 let b = self.pop();
4748 let a = self.pop();
4749 self.push_binop_with_overload(BinOp::NumEq, a.clone(), b.clone(), |a, b| {
4750 if let (Some(sa), Some(sb)) = (a.as_struct_inst(), b.as_struct_inst()) {
4752 if sa.def.name != sb.def.name {
4753 return Ok(StrykeValue::integer(0));
4754 }
4755 let av = sa.get_values();
4756 let bv = sb.get_values();
4757 let eq = av.len() == bv.len()
4758 && av.iter().zip(bv.iter()).all(|(x, y)| x.struct_field_eq(y));
4759 Ok(StrykeValue::integer(if eq { 1 } else { 0 }))
4760 } else {
4761 if !crate::compat_mode() && both_non_numeric_strings(a, b) {
4762 let sa = a.to_string();
4763 let sb = b.to_string();
4764 return Ok(StrykeValue::integer(if sa == sb { 1 } else { 0 }));
4765 }
4766 Ok(int_cmp(a, b, |x, y| x == y, |x, y| x == y))
4767 }
4768 })
4769 }
4770 Op::NumNe => {
4771 let b = self.pop();
4772 let a = self.pop();
4773 self.push_binop_with_overload(BinOp::NumNe, a, b, |a, b| {
4774 if !crate::compat_mode() && both_non_numeric_strings(a, b) {
4780 let sa = a.to_string();
4781 let sb = b.to_string();
4782 return Ok(StrykeValue::integer(if sa != sb { 1 } else { 0 }));
4783 }
4784 Ok(int_cmp(a, b, |x, y| x != y, |x, y| x != y))
4785 })
4786 }
4787 Op::NumLt => {
4788 let b = self.pop();
4789 let a = self.pop();
4790 self.push_binop_with_overload(BinOp::NumLt, a, b, |a, b| {
4791 Ok(int_cmp(a, b, |x, y| x < y, |x, y| x < y))
4792 })
4793 }
4794 Op::NumGt => {
4795 let b = self.pop();
4796 let a = self.pop();
4797 self.push_binop_with_overload(BinOp::NumGt, a, b, |a, b| {
4798 Ok(int_cmp(a, b, |x, y| x > y, |x, y| x > y))
4799 })
4800 }
4801 Op::NumLe => {
4802 let b = self.pop();
4803 let a = self.pop();
4804 self.push_binop_with_overload(BinOp::NumLe, a, b, |a, b| {
4805 Ok(int_cmp(a, b, |x, y| x <= y, |x, y| x <= y))
4806 })
4807 }
4808 Op::NumGe => {
4809 let b = self.pop();
4810 let a = self.pop();
4811 self.push_binop_with_overload(BinOp::NumGe, a, b, |a, b| {
4812 Ok(int_cmp(a, b, |x, y| x >= y, |x, y| x >= y))
4813 })
4814 }
4815 Op::Spaceship => {
4816 let b = self.pop();
4817 let a = self.pop();
4818 self.push_binop_with_overload(BinOp::Spaceship, a, b, |a, b| {
4819 Ok(
4820 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
4821 StrykeValue::integer(if x < y {
4822 -1
4823 } else if x > y {
4824 1
4825 } else {
4826 0
4827 })
4828 } else {
4829 let x = a.to_number();
4830 let y = b.to_number();
4831 StrykeValue::integer(if x < y {
4832 -1
4833 } else if x > y {
4834 1
4835 } else {
4836 0
4837 })
4838 },
4839 )
4840 })
4841 }
4842
4843 Op::StrEq => {
4845 let b = self.pop();
4846 let a = self.pop();
4847 self.push_binop_with_overload(BinOp::StrEq, a, b, |a, b| {
4848 Ok(StrykeValue::integer(if a.str_eq(b) { 1 } else { 0 }))
4849 })
4850 }
4851 Op::StrNe => {
4852 let b = self.pop();
4853 let a = self.pop();
4854 self.push_binop_with_overload(BinOp::StrNe, a, b, |a, b| {
4855 Ok(StrykeValue::integer(if !a.str_eq(b) { 1 } else { 0 }))
4856 })
4857 }
4858 Op::StrLt => {
4859 let b = self.pop();
4860 let a = self.pop();
4861 self.push_binop_with_overload(BinOp::StrLt, a, b, |a, b| {
4862 Ok(StrykeValue::integer(
4863 if a.str_cmp(b) == std::cmp::Ordering::Less {
4864 1
4865 } else {
4866 0
4867 },
4868 ))
4869 })
4870 }
4871 Op::StrGt => {
4872 let b = self.pop();
4873 let a = self.pop();
4874 self.push_binop_with_overload(BinOp::StrGt, a, b, |a, b| {
4875 Ok(StrykeValue::integer(
4876 if a.str_cmp(b) == std::cmp::Ordering::Greater {
4877 1
4878 } else {
4879 0
4880 },
4881 ))
4882 })
4883 }
4884 Op::StrLe => {
4885 let b = self.pop();
4886 let a = self.pop();
4887 self.push_binop_with_overload(BinOp::StrLe, a, b, |a, b| {
4888 let o = a.str_cmp(b);
4889 Ok(StrykeValue::integer(
4890 if matches!(o, std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
4891 {
4892 1
4893 } else {
4894 0
4895 },
4896 ))
4897 })
4898 }
4899 Op::StrGe => {
4900 let b = self.pop();
4901 let a = self.pop();
4902 self.push_binop_with_overload(BinOp::StrGe, a, b, |a, b| {
4903 let o = a.str_cmp(b);
4904 Ok(StrykeValue::integer(
4905 if matches!(
4906 o,
4907 std::cmp::Ordering::Greater | std::cmp::Ordering::Equal
4908 ) {
4909 1
4910 } else {
4911 0
4912 },
4913 ))
4914 })
4915 }
4916 Op::StrCmp => {
4917 let b = self.pop();
4918 let a = self.pop();
4919 self.push_binop_with_overload(BinOp::StrCmp, a, b, |a, b| {
4920 let cmp = a.str_cmp(b);
4921 Ok(StrykeValue::integer(match cmp {
4922 std::cmp::Ordering::Less => -1,
4923 std::cmp::Ordering::Greater => 1,
4924 std::cmp::Ordering::Equal => 0,
4925 }))
4926 })
4927 }
4928
4929 Op::LogNot => {
4931 let a = self.pop();
4932 let line = self.line();
4933 if let Some(exec_res) =
4934 self.interp.try_overload_unary_dispatch("bool", &a, line)
4935 {
4936 let pv = vm_interp_result(exec_res, line)?;
4937 self.push(StrykeValue::integer(if pv.is_true() { 0 } else { 1 }));
4938 } else {
4939 self.push(StrykeValue::integer(if a.is_true() { 0 } else { 1 }));
4940 }
4941 Ok(())
4942 }
4943 Op::BitAnd => {
4944 let rv = self.pop();
4945 let lv = self.pop();
4946 if let Some(s) = crate::value::set_intersection(&lv, &rv) {
4947 self.push(s);
4948 } else if let Some(s) = crate::sketches::try_sketch_binop(
4949 crate::sketches::SketchOp::And,
4950 &lv,
4951 &rv,
4952 ) {
4953 self.push(s);
4954 } else {
4955 self.push(StrykeValue::integer(lv.to_int() & rv.to_int()));
4956 }
4957 Ok(())
4958 }
4959 Op::BitOr => {
4960 let rv = self.pop();
4961 let lv = self.pop();
4962 if let Some(s) = crate::value::set_union(&lv, &rv) {
4963 self.push(s);
4964 } else if let Some(s) = crate::sketches::try_sketch_binop(
4965 crate::sketches::SketchOp::Or,
4966 &lv,
4967 &rv,
4968 ) {
4969 self.push(s);
4970 } else {
4971 self.push(StrykeValue::integer(lv.to_int() | rv.to_int()));
4972 }
4973 Ok(())
4974 }
4975 Op::BitXor => {
4976 let rv = self.pop();
4977 let lv = self.pop();
4978 if let Some(s) = crate::sketches::try_sketch_binop(
4979 crate::sketches::SketchOp::Xor,
4980 &lv,
4981 &rv,
4982 ) {
4983 self.push(s);
4984 } else {
4985 self.push(StrykeValue::integer(lv.to_int() ^ rv.to_int()));
4986 }
4987 Ok(())
4988 }
4989 Op::BitNot => {
4990 let a = self.pop().to_int();
4991 self.push(StrykeValue::integer(!a));
4992 Ok(())
4993 }
4994 Op::Shl => {
4995 let b = self.pop().to_int();
4996 let a = self.pop().to_int();
4997 self.push(StrykeValue::integer(perl_shl_i64(a, b)));
4998 Ok(())
4999 }
5000 Op::Shr => {
5001 let b = self.pop().to_int();
5002 let a = self.pop().to_int();
5003 self.push(StrykeValue::integer(perl_shr_i64(a, b)));
5004 Ok(())
5005 }
5006
5007 Op::Jump(target) => {
5009 self.ip = *target;
5010 Ok(())
5011 }
5012 Op::JumpIfTrue(target) => {
5013 let val = self.pop();
5014 if val.is_true() {
5015 self.ip = *target;
5016 }
5017 Ok(())
5018 }
5019 Op::JumpIfFalse(target) => {
5020 let val = self.pop();
5021 if !val.is_true() {
5022 self.ip = *target;
5023 }
5024 Ok(())
5025 }
5026 Op::JumpIfFalseKeep(target) => {
5027 if !self.peek().is_true() {
5028 self.ip = *target;
5029 } else {
5030 self.pop();
5031 }
5032 Ok(())
5033 }
5034 Op::JumpIfTrueKeep(target) => {
5035 if self.peek().is_true() {
5036 self.ip = *target;
5037 } else {
5038 self.pop();
5039 }
5040 Ok(())
5041 }
5042 Op::JumpIfDefinedKeep(target) => {
5043 if !self.peek().is_undef() {
5044 self.ip = *target;
5045 } else {
5046 self.pop();
5047 }
5048 Ok(())
5049 }
5050
5051 Op::PreInc(idx) => {
5053 let n = names[*idx as usize].as_str();
5054 self.require_scalar_mutable(n)?;
5055 let en = self.interp.english_scalar_name(n);
5056 let new_val = self
5057 .interp
5058 .scope
5059 .atomic_mutate(en, |v| StrykeValue::integer(v.to_int() + 1))
5060 .map_err(|e| e.at_line(self.line()))?;
5061 self.push(new_val);
5062 Ok(())
5063 }
5064 Op::PreDec(idx) => {
5065 let n = names[*idx as usize].as_str();
5066 self.require_scalar_mutable(n)?;
5067 let en = self.interp.english_scalar_name(n);
5068 let new_val = self
5069 .interp
5070 .scope
5071 .atomic_mutate(en, |v| StrykeValue::integer(v.to_int() - 1))
5072 .map_err(|e| e.at_line(self.line()))?;
5073 self.push(new_val);
5074 Ok(())
5075 }
5076 Op::PostInc(idx) => {
5077 let n = names[*idx as usize].as_str();
5078 self.require_scalar_mutable(n)?;
5079 let en = self.interp.english_scalar_name(n);
5080 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5081 self.interp
5082 .scope
5083 .atomic_mutate_post(en, crate::vm_helper::perl_inc)
5084 .map_err(|e| e.at_line(self.line()))?;
5085 self.ip += 1;
5086 } else {
5087 let old = self
5088 .interp
5089 .scope
5090 .atomic_mutate_post(en, crate::vm_helper::perl_inc)
5091 .map_err(|e| e.at_line(self.line()))?;
5092 self.push(old);
5093 }
5094 Ok(())
5095 }
5096 Op::PostDec(idx) => {
5097 let n = names[*idx as usize].as_str();
5098 self.require_scalar_mutable(n)?;
5099 let en = self.interp.english_scalar_name(n);
5100 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5101 self.interp
5102 .scope
5103 .atomic_mutate_post(en, |v| StrykeValue::integer(v.to_int() - 1))
5104 .map_err(|e| e.at_line(self.line()))?;
5105 self.ip += 1;
5106 } else {
5107 let old = self
5108 .interp
5109 .scope
5110 .atomic_mutate_post(en, |v| StrykeValue::integer(v.to_int() - 1))
5111 .map_err(|e| e.at_line(self.line()))?;
5112 self.push(old);
5113 }
5114 Ok(())
5115 }
5116 Op::PreIncSlot(slot) => {
5117 let cur = self.interp.scope.get_scalar_slot(*slot);
5118 let new_val = crate::vm_helper::perl_inc(&cur);
5119 self.interp.scope.set_scalar_slot(*slot, new_val.clone());
5120 self.push(new_val);
5121 Ok(())
5122 }
5123 Op::PreIncSlotVoid(slot) => {
5124 let cur = self.interp.scope.get_scalar_slot(*slot);
5125 let new_val = crate::vm_helper::perl_inc(&cur);
5126 self.interp.scope.set_scalar_slot(*slot, new_val);
5127 Ok(())
5128 }
5129 Op::PreDecSlot(slot) => {
5130 let val = self.interp.scope.get_scalar_slot(*slot).to_int() - 1;
5131 let new_val = StrykeValue::integer(val);
5132 self.interp.scope.set_scalar_slot(*slot, new_val.clone());
5133 self.push(new_val);
5134 Ok(())
5135 }
5136 Op::PostIncSlot(slot) => {
5137 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5139 let cur = self.interp.scope.get_scalar_slot(*slot);
5140 let new_val = crate::vm_helper::perl_inc(&cur);
5141 self.interp.scope.set_scalar_slot(*slot, new_val);
5142 self.ip += 1; } else {
5144 let old = self.interp.scope.get_scalar_slot(*slot);
5145 let new_val = crate::vm_helper::perl_inc(&old);
5146 self.interp.scope.set_scalar_slot(*slot, new_val);
5147 self.push(old);
5148 }
5149 Ok(())
5150 }
5151 Op::PostDecSlot(slot) => {
5152 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5153 let val = self.interp.scope.get_scalar_slot(*slot).to_int() - 1;
5154 self.interp
5155 .scope
5156 .set_scalar_slot(*slot, StrykeValue::integer(val));
5157 self.ip += 1;
5158 } else {
5159 let old = self.interp.scope.get_scalar_slot(*slot);
5160 let new_val = StrykeValue::integer(old.to_int() - 1);
5161 self.interp.scope.set_scalar_slot(*slot, new_val);
5162 self.push(old);
5163 }
5164 Ok(())
5165 }
5166
5167 Op::Call(name_idx, argc, wa) => {
5169 let name = &self.names[*name_idx as usize];
5178 let entry_opt = if !crate::compat_mode()
5179 && !name.contains("::")
5180 && crate::builtins::is_callable_spelling(name)
5181 {
5182 None
5183 } else {
5184 self.find_sub_entry(*name_idx)
5185 };
5186 self.vm_dispatch_user_call(*name_idx, entry_opt, *argc, *wa, None)?;
5187 Ok(())
5188 }
5189 Op::CallStaticSubId(sid, name_idx, argc, wa) => {
5190 let t = self.static_sub_calls.get(*sid as usize).ok_or_else(|| {
5191 StrykeError::runtime("VM: invalid CallStaticSubId", self.line())
5192 })?;
5193 debug_assert_eq!(t.2, *name_idx);
5194 let closure_sub = self
5195 .static_sub_closure_subs
5196 .get(*sid as usize)
5197 .and_then(|x| x.clone());
5198 self.vm_dispatch_user_call(
5199 *name_idx,
5200 Some((t.0, t.1)),
5201 *argc,
5202 *wa,
5203 closure_sub,
5204 )?;
5205 Ok(())
5206 }
5207 Op::Return => {
5208 if let Some(frame) = self.call_stack.pop() {
5209 if frame.block_region {
5210 return Err(StrykeError::runtime(
5211 "Return in map/grep/sort block bytecode",
5212 self.line(),
5213 ));
5214 }
5215 if let Some(t0) = frame.sub_profiler_start {
5216 if let Some(p) = &mut self.interp.profiler {
5217 p.exit_sub(t0.elapsed());
5218 }
5219 }
5220 self.interp.debugger_leave_sub();
5221 self.interp.wantarray_kind = frame.saved_wantarray;
5222 self.stack.truncate(frame.stack_base);
5223 self.interp.pop_scope_to_depth(frame.scope_depth);
5224 self.interp.current_sub_stack.pop();
5225 if frame.jit_trampoline_return {
5226 self.jit_trampoline_out = Some(StrykeValue::UNDEF);
5227 } else {
5228 self.push(StrykeValue::UNDEF);
5229 self.ip = frame.return_ip;
5230 }
5231 } else {
5232 self.exit_main_dispatch = true;
5233 }
5234 Ok(())
5235 }
5236 Op::ReturnValue => {
5237 let val = self.pop();
5238 let val = self.resolve_binding_ref(val);
5244 let val = if matches!(self.interp.wantarray_kind, WantarrayCtx::Scalar) {
5251 if let Some(items) = val.as_array_vec() {
5252 items.last().cloned().unwrap_or(StrykeValue::UNDEF)
5253 } else {
5254 val
5255 }
5256 } else {
5257 val
5258 };
5259 if let Some(frame) = self.call_stack.pop() {
5260 if frame.block_region {
5261 return Err(StrykeError::runtime(
5262 "Return in map/grep/sort block bytecode",
5263 self.line(),
5264 ));
5265 }
5266 if let Some(t0) = frame.sub_profiler_start {
5267 if let Some(p) = &mut self.interp.profiler {
5268 p.exit_sub(t0.elapsed());
5269 }
5270 }
5271 self.interp.debugger_leave_sub();
5272 self.interp.wantarray_kind = frame.saved_wantarray;
5273 self.stack.truncate(frame.stack_base);
5274 self.interp.pop_scope_to_depth(frame.scope_depth);
5275 self.interp.current_sub_stack.pop();
5276 if frame.jit_trampoline_return {
5277 self.jit_trampoline_out = Some(val);
5278 } else {
5279 self.push(val);
5280 self.ip = frame.return_ip;
5281 }
5282 } else {
5283 self.exit_main_dispatch_value = Some(val);
5284 self.exit_main_dispatch = true;
5285 }
5286 Ok(())
5287 }
5288 Op::BlockReturnValue => {
5289 let val = self.pop();
5290 let val = self.resolve_binding_ref(val);
5291 if let Some(frame) = self.call_stack.pop() {
5292 if !frame.block_region {
5293 return Err(StrykeError::runtime(
5294 "BlockReturnValue without map/grep/sort block frame",
5295 self.line(),
5296 ));
5297 }
5298 self.interp.wantarray_kind = frame.saved_wantarray;
5299 self.stack.truncate(frame.stack_base);
5300 self.interp.pop_scope_to_depth(frame.scope_depth);
5301 self.block_region_return = Some(val);
5302 Ok(())
5303 } else {
5304 Err(StrykeError::runtime(
5305 "BlockReturnValue with empty call stack",
5306 self.line(),
5307 ))
5308 }
5309 }
5310 Op::BindSubClosure(name_idx) => {
5311 let n = names[*name_idx as usize].as_str();
5312 self.interp.rebind_sub_closure(n);
5313 Ok(())
5314 }
5315
5316 Op::PushFrame => {
5318 self.interp.scope_push_hook();
5319 Ok(())
5320 }
5321 Op::PopFrame => {
5322 self.interp.scope_pop_hook();
5323 Ok(())
5324 }
5325 Op::Print(handle_idx, argc) => {
5327 let argc = *argc as usize;
5328 let mut args = Vec::with_capacity(argc);
5329 for _ in 0..argc {
5330 args.push(self.pop());
5331 }
5332 args.reverse();
5333 let mut output = String::new();
5334 if args.is_empty() {
5335 let topic = self.interp.scope.get_scalar("_").clone();
5336 let s = match self.interp.stringify_value(topic, self.line()) {
5337 Ok(s) => s,
5338 Err(FlowOrError::Error(e)) => return Err(e),
5339 Err(FlowOrError::Flow(_)) => {
5340 return Err(StrykeError::runtime(
5341 "print: unexpected control flow",
5342 self.line(),
5343 ));
5344 }
5345 };
5346 output.push_str(&s);
5347 } else {
5348 for (i, arg) in args.iter().enumerate() {
5349 if i > 0 && !self.interp.ofs.is_empty() {
5350 output.push_str(&self.interp.ofs);
5351 }
5352 for item in arg.to_list() {
5353 let s = match self.interp.stringify_value(item, self.line()) {
5354 Ok(s) => s,
5355 Err(FlowOrError::Error(e)) => return Err(e),
5356 Err(FlowOrError::Flow(_)) => {
5357 return Err(StrykeError::runtime(
5358 "print: unexpected control flow",
5359 self.line(),
5360 ));
5361 }
5362 };
5363 output.push_str(&s);
5364 }
5365 }
5366 }
5367 output.push_str(&self.interp.ors);
5368 let handle_name = match handle_idx {
5369 Some(idx) => self.interp.resolve_io_handle_name(
5370 self.names
5371 .get(*idx as usize)
5372 .map_or("STDOUT", |s| s.as_str()),
5373 ),
5374 None => self
5375 .interp
5376 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5377 };
5378 self.interp.write_formatted_print(
5379 handle_name.as_str(),
5380 &output,
5381 self.line(),
5382 )?;
5383 self.push(StrykeValue::integer(1));
5384 Ok(())
5385 }
5386 Op::Printf(handle_idx, argc) => {
5387 let argc = *argc as usize;
5388 let mut args = Vec::with_capacity(argc);
5389 for _ in 0..argc {
5390 args.push(self.pop());
5391 }
5392 args.reverse();
5393 let (fmt, rest) = match args.split_first() {
5394 Some((f, r)) => (f.to_string(), r),
5395 None => {
5396 return Err(StrykeError::runtime(
5397 "printf requires a format string",
5398 self.line(),
5399 ));
5400 }
5401 };
5402 let mut flat = Vec::new();
5406 for a in rest {
5407 if let Some(items) = a.as_array_vec() {
5408 flat.extend(items);
5409 } else {
5410 flat.push(a.clone());
5411 }
5412 }
5413 let s = match self.interp.perl_sprintf_stringify(&fmt, &flat, self.line()) {
5414 Ok(s) => s,
5415 Err(FlowOrError::Error(e)) => return Err(e),
5416 Err(FlowOrError::Flow(_)) => {
5417 return Err(StrykeError::runtime(
5418 "printf: unexpected control flow",
5419 self.line(),
5420 ));
5421 }
5422 };
5423 let handle_name = match handle_idx {
5424 Some(idx) => self.interp.resolve_io_handle_name(
5425 self.names
5426 .get(*idx as usize)
5427 .map_or("STDOUT", |s| s.as_str()),
5428 ),
5429 None => self
5430 .interp
5431 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5432 };
5433 self.interp
5434 .write_formatted_print(handle_name.as_str(), &s, self.line())?;
5435 self.push(StrykeValue::integer(1));
5436 Ok(())
5437 }
5438 Op::Say(handle_idx, argc) => {
5439 if (self.interp.feature_bits & crate::vm_helper::FEAT_SAY) == 0 {
5440 return Err(StrykeError::runtime(
5441 "say() is disabled (enable with use feature 'say' or use feature ':5.10')",
5442 self.line(),
5443 ));
5444 }
5445 let argc = *argc as usize;
5446 let mut args = Vec::with_capacity(argc);
5447 for _ in 0..argc {
5448 args.push(self.pop());
5449 }
5450 args.reverse();
5451 let mut output = String::new();
5452 if args.is_empty() {
5453 let topic = self.interp.scope.get_scalar("_").clone();
5454 let s = match self.interp.stringify_value(topic, self.line()) {
5455 Ok(s) => s,
5456 Err(FlowOrError::Error(e)) => return Err(e),
5457 Err(FlowOrError::Flow(_)) => {
5458 return Err(StrykeError::runtime(
5459 "say: unexpected control flow",
5460 self.line(),
5461 ));
5462 }
5463 };
5464 output.push_str(&s);
5465 } else {
5466 for (i, arg) in args.iter().enumerate() {
5467 if i > 0 && !self.interp.ofs.is_empty() {
5468 output.push_str(&self.interp.ofs);
5469 }
5470 for item in arg.to_list() {
5471 let s = match self.interp.stringify_value(item, self.line()) {
5472 Ok(s) => s,
5473 Err(FlowOrError::Error(e)) => return Err(e),
5474 Err(FlowOrError::Flow(_)) => {
5475 return Err(StrykeError::runtime(
5476 "say: unexpected control flow",
5477 self.line(),
5478 ));
5479 }
5480 };
5481 output.push_str(&s);
5482 }
5483 }
5484 }
5485 output.push('\n');
5486 output.push_str(&self.interp.ors);
5487 let handle_name = match handle_idx {
5488 Some(idx) => self.interp.resolve_io_handle_name(
5489 self.names
5490 .get(*idx as usize)
5491 .map_or("STDOUT", |s| s.as_str()),
5492 ),
5493 None => self
5494 .interp
5495 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5496 };
5497 self.interp.write_formatted_print(
5498 handle_name.as_str(),
5499 &output,
5500 self.line(),
5501 )?;
5502 self.push(StrykeValue::integer(1));
5503 Ok(())
5504 }
5505
5506 Op::CallBuiltin(id, argc) => {
5508 let argc = *argc as usize;
5509 let mut args = Vec::with_capacity(argc);
5510 for _ in 0..argc {
5511 args.push(self.pop());
5512 }
5513 args.reverse();
5514 let result = self.exec_builtin(*id, args)?;
5515 self.push(result);
5516 Ok(())
5517 }
5518 Op::WantarrayPush(wa) => {
5519 self.wantarray_stack.push(self.interp.wantarray_kind);
5520 self.interp.wantarray_kind = WantarrayCtx::from_byte(*wa);
5521 Ok(())
5522 }
5523 Op::WantarrayPop => {
5524 self.interp.wantarray_kind =
5525 self.wantarray_stack.pop().unwrap_or(WantarrayCtx::Scalar);
5526 Ok(())
5527 }
5528
5529 Op::MakeArray(n) => {
5531 let n = *n as usize;
5532 let mut stack_vals = Vec::with_capacity(n);
5538 for _ in 0..n {
5539 stack_vals.push(self.pop());
5540 }
5541 stack_vals.reverse();
5542 let mut arr = Vec::new();
5543 for v in stack_vals {
5544 if let Some(items) = v.as_array_vec() {
5545 arr.extend(items);
5546 } else if let Some(map) = v.as_hash_map() {
5547 for (k, vv) in map {
5548 arr.push(StrykeValue::string(k));
5549 arr.push(vv);
5550 }
5551 } else {
5552 arr.push(v);
5553 }
5554 }
5555 self.push(StrykeValue::array(arr));
5556 Ok(())
5557 }
5558 Op::HashSliceDeref(n) => {
5559 let n = *n as usize;
5560 let mut key_vals = Vec::with_capacity(n);
5561 for _ in 0..n {
5562 key_vals.push(self.pop());
5563 }
5564 key_vals.reverse();
5565 let container = self.pop();
5566 let line = self.line();
5567 let out = vm_interp_result(
5568 self.interp
5569 .hash_slice_deref_values(&container, &key_vals, line),
5570 line,
5571 )?;
5572 self.push(out);
5573 Ok(())
5574 }
5575 Op::ArrowArraySlice(n) => {
5576 let n = *n as usize;
5577 let idxs = self.pop_flattened_array_slice_specs(n);
5578 let r = self.pop();
5579 let line = self.line();
5580 let out = vm_interp_result(
5581 self.interp.arrow_array_slice_values(r, &idxs, line),
5582 line,
5583 )?;
5584 self.push(out);
5585 Ok(())
5586 }
5587 Op::SetHashSliceDeref(n) => {
5588 let n = *n as usize;
5589 let mut key_vals = Vec::with_capacity(n);
5590 for _ in 0..n {
5591 key_vals.push(self.pop());
5592 }
5593 key_vals.reverse();
5594 let container = self.pop();
5595 let val = self.pop();
5596 let line = self.line();
5597 vm_interp_result(
5598 self.interp
5599 .assign_hash_slice_deref(container, key_vals, val, line),
5600 line,
5601 )?;
5602 Ok(())
5603 }
5604 Op::SetHashSlice(hash_idx, n) => {
5605 let n = *n as usize;
5606 let mut key_vals = Vec::with_capacity(n);
5607 for _ in 0..n {
5608 key_vals.push(self.pop());
5609 }
5610 key_vals.reverse();
5611 let name = names[*hash_idx as usize].as_str();
5612 self.require_hash_mutable(name)?;
5613 let val = self.pop();
5614 let line = self.line();
5615 vm_interp_result(
5616 self.interp
5617 .assign_named_hash_slice(name, key_vals, val, line),
5618 line,
5619 )?;
5620 Ok(())
5621 }
5622 Op::GetHashSlice(hash_idx, n) => {
5623 let n = *n as usize;
5624 let mut key_vals = Vec::with_capacity(n);
5625 for _ in 0..n {
5626 key_vals.push(self.pop());
5627 }
5628 key_vals.reverse();
5629 let name = names[*hash_idx as usize].as_str();
5630 let h = self.interp.scope.get_hash(name);
5631 let mut result = Vec::new();
5632 for kv in &key_vals {
5633 if let Some(vv) = kv.as_array_vec() {
5636 for v in vv {
5637 let k = v.to_string();
5638 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5639 }
5640 } else if let Some(r) = kv.as_array_ref() {
5641 for v in r.read().iter() {
5642 let k = v.to_string();
5643 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5644 }
5645 } else {
5646 let k = kv.to_string();
5647 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5648 }
5649 }
5650 self.push(StrykeValue::array(result));
5651 Ok(())
5652 }
5653 Op::HashSliceDerefCompound(op_byte, n) => {
5654 let n = *n as usize;
5655 let mut key_vals = Vec::with_capacity(n);
5656 for _ in 0..n {
5657 key_vals.push(self.pop());
5658 }
5659 key_vals.reverse();
5660 let container = self.pop();
5661 let rhs = self.pop();
5662 let line = self.line();
5663 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5664 .ok_or_else(|| {
5665 crate::error::StrykeError::runtime(
5666 "VM: HashSliceDerefCompound: bad op byte",
5667 line,
5668 )
5669 })?;
5670 let new_val = vm_interp_result(
5671 self.interp.compound_assign_hash_slice_deref(
5672 container, key_vals, op, rhs, line,
5673 ),
5674 line,
5675 )?;
5676 self.push(new_val);
5677 Ok(())
5678 }
5679 Op::HashSliceDerefIncDec(kind, n) => {
5680 let n = *n as usize;
5681 let mut key_vals = Vec::with_capacity(n);
5682 for _ in 0..n {
5683 key_vals.push(self.pop());
5684 }
5685 key_vals.reverse();
5686 let container = self.pop();
5687 let line = self.line();
5688 let out = vm_interp_result(
5689 self.interp
5690 .hash_slice_deref_inc_dec(container, key_vals, *kind, line),
5691 line,
5692 )?;
5693 self.push(out);
5694 Ok(())
5695 }
5696 Op::NamedHashSliceCompound(op_byte, hash_idx, n) => {
5697 let n = *n as usize;
5698 let mut key_vals = Vec::with_capacity(n);
5699 for _ in 0..n {
5700 key_vals.push(self.pop());
5701 }
5702 key_vals.reverse();
5703 let name = names[*hash_idx as usize].as_str();
5704 self.require_hash_mutable(name)?;
5705 let rhs = self.pop();
5706 let line = self.line();
5707 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5708 .ok_or_else(|| {
5709 crate::error::StrykeError::runtime(
5710 "VM: NamedHashSliceCompound: bad op byte",
5711 line,
5712 )
5713 })?;
5714 let new_val = vm_interp_result(
5715 self.interp
5716 .compound_assign_named_hash_slice(name, key_vals, op, rhs, line),
5717 line,
5718 )?;
5719 self.push(new_val);
5720 Ok(())
5721 }
5722 Op::NamedHashSliceIncDec(kind, hash_idx, n) => {
5723 let n = *n as usize;
5724 let mut key_vals = Vec::with_capacity(n);
5725 for _ in 0..n {
5726 key_vals.push(self.pop());
5727 }
5728 key_vals.reverse();
5729 let name = names[*hash_idx as usize].as_str();
5730 self.require_hash_mutable(name)?;
5731 let line = self.line();
5732 let out = vm_interp_result(
5733 self.interp
5734 .named_hash_slice_inc_dec(name, key_vals, *kind, line),
5735 line,
5736 )?;
5737 self.push(out);
5738 Ok(())
5739 }
5740 Op::NamedHashSlicePeekLast(hash_idx, n) => {
5741 let n = *n as usize;
5742 let line = self.line();
5743 let name = names[*hash_idx as usize].as_str();
5744 self.require_hash_mutable(name)?;
5745 let len = self.stack.len();
5746 if len < n {
5747 return Err(StrykeError::runtime(
5748 "VM: NamedHashSlicePeekLast: stack underflow",
5749 line,
5750 ));
5751 }
5752 let base = len - n;
5753 let key_vals: Vec<StrykeValue> = self.stack[base..base + n].to_vec();
5754 let ks = Self::flatten_hash_slice_key_slots(&key_vals);
5755 let last_k = ks.last().ok_or_else(|| {
5756 StrykeError::runtime("VM: NamedHashSlicePeekLast: empty key list", line)
5757 })?;
5758 self.interp.touch_env_hash(name);
5759 let cur = self.interp.scope.get_hash_element(name, last_k.as_str());
5760 self.push(cur);
5761 Ok(())
5762 }
5763 Op::NamedHashSliceDropKeysKeepCur(n) => {
5764 let n = *n as usize;
5765 let cur = self.pop();
5766 for _ in 0..n {
5767 self.pop();
5768 }
5769 self.push(cur);
5770 Ok(())
5771 }
5772 Op::SetNamedHashSliceLastKeep(hash_idx, n) => {
5773 let n = *n as usize;
5774 let line = self.line();
5775 let name = names[*hash_idx as usize].as_str();
5776 self.require_hash_mutable(name)?;
5777 let mut key_vals_rev = Vec::with_capacity(n);
5778 for _ in 0..n {
5779 key_vals_rev.push(self.pop());
5780 }
5781 key_vals_rev.reverse();
5782 let mut val = self.pop();
5783 if let Some(av) = val.as_array_vec() {
5784 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
5785 }
5786 let ks = Self::flatten_hash_slice_key_slots(&key_vals_rev);
5787 let last_k = ks.last().ok_or_else(|| {
5788 StrykeError::runtime(
5789 "VM: SetNamedHashSliceLastKeep: empty key list",
5790 line,
5791 )
5792 })?;
5793 let val_keep = val.clone();
5794 self.interp.touch_env_hash(name);
5795 vm_interp_result(
5796 self.interp
5797 .scope
5798 .set_hash_element(name, last_k.as_str(), val)
5799 .map(|()| StrykeValue::UNDEF)
5800 .map_err(|e| FlowOrError::Error(e.at_line(line))),
5801 line,
5802 )?;
5803 self.push(val_keep);
5804 Ok(())
5805 }
5806 Op::HashSliceDerefPeekLast(n) => {
5807 let n = *n as usize;
5808 let line = self.line();
5809 let len = self.stack.len();
5810 if len < n + 1 {
5811 return Err(StrykeError::runtime(
5812 "VM: HashSliceDerefPeekLast: stack underflow",
5813 line,
5814 ));
5815 }
5816 let base = len - n - 1;
5817 let container = self.stack[base].clone();
5818 let key_vals: Vec<StrykeValue> =
5819 self.stack[base + 1..base + 1 + n].to_vec();
5820 let list = vm_interp_result(
5821 self.interp
5822 .hash_slice_deref_values(&container, &key_vals, line),
5823 line,
5824 )?;
5825 let cur = list.to_list().last().cloned().unwrap_or(StrykeValue::UNDEF);
5826 self.push(cur);
5827 Ok(())
5828 }
5829 Op::HashSliceDerefRollValUnderKeys(n) => {
5830 let n = *n as usize;
5831 let val = self.pop();
5832 let mut keys_rev = Vec::with_capacity(n);
5833 for _ in 0..n {
5834 keys_rev.push(self.pop());
5835 }
5836 let container = self.pop();
5837 keys_rev.reverse();
5838 self.push(val);
5839 self.push(container);
5840 for k in keys_rev {
5841 self.push(k);
5842 }
5843 Ok(())
5844 }
5845 Op::HashSliceDerefSetLastKeep(n) => {
5846 let n = *n as usize;
5847 let line = self.line();
5848 let mut key_vals_rev = Vec::with_capacity(n);
5849 for _ in 0..n {
5850 key_vals_rev.push(self.pop());
5851 }
5852 key_vals_rev.reverse();
5853 let container = self.pop();
5854 let mut val = self.pop();
5855 if let Some(av) = val.as_array_vec() {
5856 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
5857 }
5858 let ks = Self::flatten_hash_slice_key_slots(&key_vals_rev);
5859 let last_k = ks.last().ok_or_else(|| {
5860 StrykeError::runtime(
5861 "VM: HashSliceDerefSetLastKeep: empty key list",
5862 line,
5863 )
5864 })?;
5865 let val_keep = val.clone();
5866 vm_interp_result(
5867 self.interp.assign_hash_slice_one_key(
5868 container,
5869 last_k.as_str(),
5870 val,
5871 line,
5872 ),
5873 line,
5874 )?;
5875 self.push(val_keep);
5876 Ok(())
5877 }
5878 Op::HashSliceDerefDropKeysKeepCur(n) => {
5879 let n = *n as usize;
5880 let cur = self.pop();
5881 for _ in 0..n {
5882 self.pop();
5883 }
5884 let _container = self.pop();
5885 self.push(cur);
5886 Ok(())
5887 }
5888 Op::SetArrowArraySlice(n) => {
5889 let n = *n as usize;
5890 let idxs = self.pop_flattened_array_slice_specs(n);
5891 let aref = self.pop();
5892 let val = self.pop();
5893 let line = self.line();
5894 vm_interp_result(
5895 self.interp.assign_arrow_array_slice(aref, idxs, val, line),
5896 line,
5897 )?;
5898 Ok(())
5899 }
5900 Op::ArrowArraySliceCompound(op_byte, n) => {
5901 let n = *n as usize;
5902 let idxs = self.pop_flattened_array_slice_specs(n);
5903 let aref = self.pop();
5904 let rhs = self.pop();
5905 let line = self.line();
5906 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5907 .ok_or_else(|| {
5908 crate::error::StrykeError::runtime(
5909 "VM: ArrowArraySliceCompound: bad op byte",
5910 line,
5911 )
5912 })?;
5913 let new_val = vm_interp_result(
5914 self.interp
5915 .compound_assign_arrow_array_slice(aref, idxs, op, rhs, line),
5916 line,
5917 )?;
5918 self.push(new_val);
5919 Ok(())
5920 }
5921 Op::ArrowArraySliceIncDec(kind, n) => {
5922 let n = *n as usize;
5923 let idxs = self.pop_flattened_array_slice_specs(n);
5924 let aref = self.pop();
5925 let line = self.line();
5926 let out = vm_interp_result(
5927 self.interp
5928 .arrow_array_slice_inc_dec(aref, idxs, *kind, line),
5929 line,
5930 )?;
5931 self.push(out);
5932 Ok(())
5933 }
5934 Op::ArrowArraySlicePeekLast(n) => {
5935 let n = *n as usize;
5936 let line = self.line();
5937 let len = self.stack.len();
5938 if len < n + 1 {
5939 return Err(StrykeError::runtime(
5940 "VM: ArrowArraySlicePeekLast: stack underflow",
5941 line,
5942 ));
5943 }
5944 let base = len - n - 1;
5945 let aref = self.stack[base].clone();
5946 let idxs =
5947 self.flatten_array_slice_specs_ordered_values(&self.stack[base + 1..])?;
5948 let last = *idxs.last().ok_or_else(|| {
5949 StrykeError::runtime(
5950 "VM: ArrowArraySlicePeekLast: empty index list",
5951 line,
5952 )
5953 })?;
5954 let cur = vm_interp_result(
5955 self.interp.read_arrow_array_element(aref, last, line),
5956 line,
5957 )?;
5958 self.push(cur);
5959 Ok(())
5960 }
5961 Op::ArrowArraySliceDropKeysKeepCur(n) => {
5962 let n = *n as usize;
5963 let cur = self.pop();
5964 let _idxs = self.pop_flattened_array_slice_specs(n);
5965 let _aref = self.pop();
5966 self.push(cur);
5967 Ok(())
5968 }
5969 Op::ArrowArraySliceRollValUnderSpecs(n) => {
5970 let n = *n as usize;
5971 let val = self.pop();
5972 let mut specs_rev = Vec::with_capacity(n);
5973 for _ in 0..n {
5974 specs_rev.push(self.pop());
5975 }
5976 let aref = self.pop();
5977 self.push(val);
5978 self.push(aref);
5979 for s in specs_rev.into_iter().rev() {
5980 self.push(s);
5981 }
5982 Ok(())
5983 }
5984 Op::SetArrowArraySliceLastKeep(n) => {
5985 let n = *n as usize;
5986 let line = self.line();
5987 let idxs = self.pop_flattened_array_slice_specs(n);
5988 let aref = self.pop();
5989 let mut val = self.pop();
5990 if let Some(av) = val.as_array_vec() {
5993 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
5994 }
5995 let last = *idxs.last().ok_or_else(|| {
5996 StrykeError::runtime(
5997 "VM: SetArrowArraySliceLastKeep: empty index list",
5998 line,
5999 )
6000 })?;
6001 let val_keep = val.clone();
6002 vm_interp_result(
6003 self.interp.assign_arrow_array_deref(aref, last, val, line),
6004 line,
6005 )?;
6006 self.push(val_keep);
6007 Ok(())
6008 }
6009 Op::NamedArraySliceIncDec(kind, arr_idx, n) => {
6010 let n = *n as usize;
6011 let idxs = self.pop_flattened_array_slice_specs(n);
6012 let name = names[*arr_idx as usize].as_str();
6013 self.require_array_mutable(name)?;
6014 let line = self.line();
6015 let out = vm_interp_result(
6016 self.interp
6017 .named_array_slice_inc_dec(name, idxs, *kind, line),
6018 line,
6019 )?;
6020 self.push(out);
6021 Ok(())
6022 }
6023 Op::NamedArraySliceCompound(op_byte, arr_idx, n) => {
6024 let n = *n as usize;
6025 let idxs = self.pop_flattened_array_slice_specs(n);
6026 let name = names[*arr_idx as usize].as_str();
6027 self.require_array_mutable(name)?;
6028 let rhs = self.pop();
6029 let line = self.line();
6030 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
6031 .ok_or_else(|| {
6032 crate::error::StrykeError::runtime(
6033 "VM: NamedArraySliceCompound: bad op byte",
6034 line,
6035 )
6036 })?;
6037 let new_val = vm_interp_result(
6038 self.interp
6039 .compound_assign_named_array_slice(name, idxs, op, rhs, line),
6040 line,
6041 )?;
6042 self.push(new_val);
6043 Ok(())
6044 }
6045 Op::NamedArraySlicePeekLast(arr_idx, n) => {
6046 let n = *n as usize;
6047 let line = self.line();
6048 let name = names[*arr_idx as usize].as_str();
6049 self.require_array_mutable(name)?;
6050 let len = self.stack.len();
6051 if len < n {
6052 return Err(StrykeError::runtime(
6053 "VM: NamedArraySlicePeekLast: stack underflow",
6054 line,
6055 ));
6056 }
6057 let base = len - n;
6058 let idxs =
6059 self.flatten_array_slice_specs_ordered_values(&self.stack[base..])?;
6060 let last = *idxs.last().ok_or_else(|| {
6061 StrykeError::runtime(
6062 "VM: NamedArraySlicePeekLast: empty index list",
6063 line,
6064 )
6065 })?;
6066 let cur = self.interp.scope.get_array_element(name, last);
6067 self.push(cur);
6068 Ok(())
6069 }
6070 Op::NamedArraySliceDropKeysKeepCur(n) => {
6071 let n = *n as usize;
6072 let cur = self.pop();
6073 let _idxs = self.pop_flattened_array_slice_specs(n);
6074 self.push(cur);
6075 Ok(())
6076 }
6077 Op::NamedArraySliceRollValUnderSpecs(n) => {
6078 let n = *n as usize;
6079 let val = self.pop();
6080 let mut specs_rev = Vec::with_capacity(n);
6081 for _ in 0..n {
6082 specs_rev.push(self.pop());
6083 }
6084 self.push(val);
6085 for s in specs_rev.into_iter().rev() {
6086 self.push(s);
6087 }
6088 Ok(())
6089 }
6090 Op::SetNamedArraySliceLastKeep(arr_idx, n) => {
6091 let n = *n as usize;
6092 let line = self.line();
6093 let idxs = self.pop_flattened_array_slice_specs(n);
6094 let name = names[*arr_idx as usize].as_str();
6095 self.require_array_mutable(name)?;
6096 let mut val = self.pop();
6097 if let Some(av) = val.as_array_vec() {
6098 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
6099 }
6100 let last = *idxs.last().ok_or_else(|| {
6101 StrykeError::runtime(
6102 "VM: SetNamedArraySliceLastKeep: empty index list",
6103 line,
6104 )
6105 })?;
6106 let val_keep = val.clone();
6107 vm_interp_result(
6108 self.interp
6109 .scope
6110 .set_array_element(name, last, val)
6111 .map(|()| StrykeValue::UNDEF)
6112 .map_err(|e| FlowOrError::Error(e.at_line(line))),
6113 line,
6114 )?;
6115 self.push(val_keep);
6116 Ok(())
6117 }
6118 Op::SetNamedArraySlice(arr_idx, n) => {
6119 let n = *n as usize;
6120 let idxs = self.pop_flattened_array_slice_specs(n);
6121 let name = names[*arr_idx as usize].as_str();
6122 self.require_array_mutable(name)?;
6123 let val = self.pop();
6124 let line = self.line();
6125 vm_interp_result(
6126 self.interp.assign_named_array_slice(name, idxs, val, line),
6127 line,
6128 )?;
6129 Ok(())
6130 }
6131 Op::MakeHash(n) => {
6132 let n = *n as usize;
6133 let mut items = Vec::with_capacity(n);
6134 for _ in 0..n {
6135 items.push(self.pop());
6136 }
6137 items.reverse();
6138 let mut map = IndexMap::new();
6139 let mut i = 0;
6140 while i + 1 < items.len() {
6141 map.insert(items[i].to_string(), items[i + 1].clone());
6142 i += 2;
6143 }
6144 self.push(StrykeValue::hash(map));
6145 Ok(())
6146 }
6147 Op::Range => {
6148 let to = self.pop();
6149 let from = self.pop();
6150 let arr = perl_list_range_expand(from, to);
6151 self.push(StrykeValue::array(arr));
6152 Ok(())
6153 }
6154 Op::RangeStep => {
6155 let step = self.pop();
6156 let to = self.pop();
6157 let from = self.pop();
6158 let arr = crate::value::perl_list_range_expand_stepped(from, to, step);
6159 self.push(StrykeValue::array(arr));
6160 Ok(())
6161 }
6162 Op::ArraySliceRange(arr_idx) => {
6163 let step = self.pop();
6164 let to = self.pop();
6165 let from = self.pop();
6166 let line = self.line();
6167 let name = names[*arr_idx as usize].as_str();
6168 if let Some(real) = name.strip_prefix("__topicstr__") {
6171 let s = self.interp.scope.get_scalar(real).to_string();
6172 let chars: Vec<char> = s.chars().collect();
6173 let n = chars.len() as i64;
6174 let step_i = if step.is_undef() { 1 } else { step.to_int() };
6175 let mut from_i = if from.is_undef() {
6179 if step_i >= 0 {
6180 0
6181 } else {
6182 n - 1
6183 }
6184 } else {
6185 from.to_int()
6186 };
6187 let mut to_i = if to.is_undef() {
6188 if step_i >= 0 {
6189 n - 1
6190 } else {
6191 0
6192 }
6193 } else {
6194 to.to_int()
6195 };
6196 if from_i < 0 {
6197 from_i += n
6198 }
6199 if to_i < 0 {
6200 to_i += n
6201 }
6202 let mut out = String::new();
6203 if step_i > 0 {
6204 let mut i = from_i;
6205 while i <= to_i && i < n {
6206 if i >= 0 {
6207 out.push(chars[i as usize]);
6208 }
6209 i += step_i;
6210 }
6211 } else if step_i < 0 {
6212 let mut i = from_i;
6213 while i >= to_i && i >= 0 {
6214 if i < n {
6215 out.push(chars[i as usize]);
6216 }
6217 i += step_i;
6218 }
6219 }
6220 self.push(StrykeValue::string(out));
6221 return Ok(());
6222 }
6223 let arr_len = self.interp.scope.array_len(name) as i64;
6224 if !crate::compat_mode()
6229 && arr_len == 0
6230 && self.interp.scope.scalar_binding_exists(name)
6231 {
6232 let s = self.interp.scope.get_scalar(name).to_string();
6233 if !s.is_empty() {
6234 let chars: Vec<char> = s.chars().collect();
6235 let n = chars.len() as i64;
6236 let step_i = if step.is_undef() { 1 } else { step.to_int() };
6237 let mut from_i = if from.is_undef() {
6241 if step_i >= 0 {
6242 0
6243 } else {
6244 n - 1
6245 }
6246 } else {
6247 from.to_int()
6248 };
6249 let mut to_i = if to.is_undef() {
6250 if step_i >= 0 {
6251 n - 1
6252 } else {
6253 0
6254 }
6255 } else {
6256 to.to_int()
6257 };
6258 if from_i < 0 {
6259 from_i += n
6260 }
6261 if to_i < 0 {
6262 to_i += n
6263 }
6264 let mut out = String::new();
6265 if step_i > 0 {
6266 let mut i = from_i;
6267 while i <= to_i && i < n {
6268 if i >= 0 {
6269 out.push(chars[i as usize]);
6270 }
6271 i += step_i;
6272 }
6273 } else if step_i < 0 {
6274 let mut i = from_i;
6275 while i >= to_i && i >= 0 {
6276 if i < n {
6277 out.push(chars[i as usize]);
6278 }
6279 i += step_i;
6280 }
6281 }
6282 self.push(StrykeValue::string(out));
6283 return Ok(());
6284 }
6285 }
6286 let indices = match crate::value::compute_array_slice_indices(
6287 arr_len, &from, &to, &step,
6288 ) {
6289 Ok(v) => v,
6290 Err(msg) => {
6291 return Err(StrykeError::runtime(msg, line));
6292 }
6293 };
6294 let mut out = Vec::with_capacity(indices.len());
6295 for i in indices {
6296 out.push(self.interp.scope.get_array_element(name, i));
6297 }
6298 self.push(StrykeValue::array(out));
6299 Ok(())
6300 }
6301 Op::HashSliceRange(hash_idx) => {
6302 let step = self.pop();
6303 let to = self.pop();
6304 let from = self.pop();
6305 let line = self.line();
6306 let name = names[*hash_idx as usize].as_str();
6307 let keys = match crate::value::compute_hash_slice_keys(&from, &to, &step) {
6308 Ok(v) => v,
6309 Err(msg) => {
6310 return Err(StrykeError::runtime(msg, line));
6311 }
6312 };
6313 let h = self.interp.scope.get_hash(name);
6314 let mut out = Vec::with_capacity(keys.len());
6315 for k in &keys {
6316 out.push(h.get(k).cloned().unwrap_or(StrykeValue::UNDEF));
6317 }
6318 self.push(StrykeValue::array(out));
6319 Ok(())
6320 }
6321 Op::ScalarFlipFlop(slot, exclusive) => {
6322 let to = self.pop().to_int();
6323 let from = self.pop().to_int();
6324 let line = self.line();
6325 let v = vm_interp_result(
6326 self.interp
6327 .scalar_flip_flop_eval(from, to, *slot as usize, *exclusive != 0)
6328 .map_err(Into::into),
6329 line,
6330 )?;
6331 self.push(v);
6332 Ok(())
6333 }
6334 Op::RegexFlipFlop(slot, exclusive, lp, lf, rp, rf) => {
6335 let line = self.line();
6336 let left_pat = constants[*lp as usize].as_str_or_empty();
6337 let left_flags = constants[*lf as usize].as_str_or_empty();
6338 let right_pat = constants[*rp as usize].as_str_or_empty();
6339 let right_flags = constants[*rf as usize].as_str_or_empty();
6340 let v = vm_interp_result(
6341 self.interp
6342 .regex_flip_flop_eval(
6343 left_pat.as_str(),
6344 left_flags.as_str(),
6345 right_pat.as_str(),
6346 right_flags.as_str(),
6347 *slot as usize,
6348 *exclusive != 0,
6349 line,
6350 )
6351 .map_err(Into::into),
6352 line,
6353 )?;
6354 self.push(v);
6355 Ok(())
6356 }
6357 Op::RegexEofFlipFlop(slot, exclusive, lp, lf) => {
6358 let line = self.line();
6359 let left_pat = constants[*lp as usize].as_str_or_empty();
6360 let left_flags = constants[*lf as usize].as_str_or_empty();
6361 let v = vm_interp_result(
6362 self.interp
6363 .regex_eof_flip_flop_eval(
6364 left_pat.as_str(),
6365 left_flags.as_str(),
6366 *slot as usize,
6367 *exclusive != 0,
6368 line,
6369 )
6370 .map_err(Into::into),
6371 line,
6372 )?;
6373 self.push(v);
6374 Ok(())
6375 }
6376 Op::RegexFlipFlopExprRhs(slot, exclusive, lp, lf, rhs_idx) => {
6377 let idx = *rhs_idx as usize;
6378 let line = self.line();
6379 let right_m = if let Some(&(start, end)) = self
6380 .regex_flip_flop_rhs_expr_bytecode_ranges
6381 .get(idx)
6382 .and_then(|r| r.as_ref())
6383 {
6384 let val = self.run_block_region(start, end, op_count)?;
6385 val.is_true()
6386 } else {
6387 let e = &self.regex_flip_flop_rhs_expr_entries[idx];
6388 match self.interp.eval_boolean_rvalue_condition(e) {
6389 Ok(b) => b,
6390 Err(FlowOrError::Error(err)) => return Err(err),
6391 Err(FlowOrError::Flow(_)) => {
6392 return Err(StrykeError::runtime(
6393 "unexpected flow in regex flip-flop RHS",
6394 line,
6395 ))
6396 }
6397 }
6398 };
6399 let left_pat = constants[*lp as usize].as_str_or_empty();
6400 let left_flags = constants[*lf as usize].as_str_or_empty();
6401 let v = vm_interp_result(
6402 self.interp
6403 .regex_flip_flop_eval_dynamic_right(
6404 left_pat.as_str(),
6405 left_flags.as_str(),
6406 *slot as usize,
6407 *exclusive != 0,
6408 line,
6409 right_m,
6410 )
6411 .map_err(Into::into),
6412 line,
6413 )?;
6414 self.push(v);
6415 Ok(())
6416 }
6417 Op::RegexFlipFlopDotLineRhs(slot, exclusive, lp, lf, line_cidx) => {
6418 let line = self.line();
6419 let rhs_line = constants[*line_cidx as usize].to_int();
6420 let left_pat = constants[*lp as usize].as_str_or_empty();
6421 let left_flags = constants[*lf as usize].as_str_or_empty();
6422 let v = vm_interp_result(
6423 self.interp
6424 .regex_flip_flop_eval_dot_line_rhs(
6425 left_pat.as_str(),
6426 left_flags.as_str(),
6427 *slot as usize,
6428 *exclusive != 0,
6429 line,
6430 rhs_line,
6431 )
6432 .map_err(Into::into),
6433 line,
6434 )?;
6435 self.push(v);
6436 Ok(())
6437 }
6438
6439 Op::RegexMatch(pat_idx, flags_idx, scalar_g, pos_key_idx) => {
6441 let val = self.pop();
6442 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6443 let flags = constants[*flags_idx as usize].as_str_or_empty();
6444 let line = self.line();
6445 if val.is_iterator() {
6446 let source = crate::map_stream::into_pull_iter(val);
6447 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6448 Ok(r) => r,
6449 Err(FlowOrError::Error(e)) => return Err(e),
6450 Err(FlowOrError::Flow(_)) => {
6451 return Err(StrykeError::runtime(
6452 "unexpected flow in regex compile",
6453 line,
6454 ));
6455 }
6456 };
6457 let global = flags.contains('g');
6458 if global {
6459 self.push(StrykeValue::iterator(std::sync::Arc::new(
6460 crate::map_stream::MatchGlobalStreamIterator::new(source, re),
6461 )));
6462 } else {
6463 self.push(StrykeValue::iterator(std::sync::Arc::new(
6464 crate::map_stream::MatchStreamIterator::new(source, re),
6465 )));
6466 }
6467 return Ok(());
6468 }
6469 let string = val.into_string();
6470 let pos_key_owned = if *pos_key_idx == u16::MAX {
6471 None
6472 } else {
6473 Some(constants[*pos_key_idx as usize].as_str_or_empty())
6474 };
6475 let pos_key: &str = pos_key_owned.as_deref().unwrap_or("_");
6476 match self
6477 .interp
6478 .regex_match_execute(string, &pattern, &flags, *scalar_g, pos_key, line)
6479 {
6480 Ok(v) => {
6481 self.push(v);
6482 Ok(())
6483 }
6484 Err(FlowOrError::Error(e)) => Err(e),
6485 Err(FlowOrError::Flow(_)) => {
6486 Err(StrykeError::runtime("unexpected flow in regex match", line))
6487 }
6488 }
6489 }
6490 Op::RegexSubst(pat_idx, repl_idx, flags_idx, lvalue_idx) => {
6491 let val = self.pop();
6492 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6493 let replacement = constants[*repl_idx as usize].as_str_or_empty();
6494 let flags = constants[*flags_idx as usize].as_str_or_empty();
6495 let line = self.line();
6496 if val.is_iterator() {
6497 let source = crate::map_stream::into_pull_iter(val);
6498 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6499 Ok(r) => r,
6500 Err(FlowOrError::Error(e)) => return Err(e),
6501 Err(FlowOrError::Flow(_)) => {
6502 return Err(StrykeError::runtime(
6503 "unexpected flow in regex compile",
6504 line,
6505 ));
6506 }
6507 };
6508 let global = flags.contains('g');
6509 self.push(StrykeValue::iterator(std::sync::Arc::new(
6510 crate::map_stream::SubstStreamIterator::new(
6511 source,
6512 re,
6513 crate::vm_helper::normalize_replacement_backrefs(&replacement),
6514 global,
6515 ),
6516 )));
6517 return Ok(());
6518 }
6519 let string = val.into_string();
6520 let target = &self.lvalues[*lvalue_idx as usize];
6521 match self.interp.regex_subst_execute(
6522 string,
6523 &pattern,
6524 &replacement,
6525 &flags,
6526 target,
6527 line,
6528 ) {
6529 Ok(v) => {
6530 self.push(v);
6531 Ok(())
6532 }
6533 Err(FlowOrError::Error(e)) => Err(e),
6534 Err(FlowOrError::Flow(_)) => {
6535 Err(StrykeError::runtime("unexpected flow in s///", line))
6536 }
6537 }
6538 }
6539 Op::RegexTransliterate(from_idx, to_idx, flags_idx, lvalue_idx) => {
6540 let val = self.pop();
6541 let from = constants[*from_idx as usize].as_str_or_empty();
6542 let to = constants[*to_idx as usize].as_str_or_empty();
6543 let flags = constants[*flags_idx as usize].as_str_or_empty();
6544 let line = self.line();
6545 if val.is_iterator() {
6546 let source = crate::map_stream::into_pull_iter(val);
6547 self.push(StrykeValue::iterator(std::sync::Arc::new(
6548 crate::map_stream::TransliterateStreamIterator::new(
6549 source, &from, &to, &flags,
6550 ),
6551 )));
6552 return Ok(());
6553 }
6554 let string = val.into_string();
6555 let target = &self.lvalues[*lvalue_idx as usize];
6556 match self
6557 .interp
6558 .regex_transliterate_execute(string, &from, &to, &flags, target, line)
6559 {
6560 Ok(v) => {
6561 self.push(v);
6562 Ok(())
6563 }
6564 Err(FlowOrError::Error(e)) => Err(e),
6565 Err(FlowOrError::Flow(_)) => {
6566 Err(StrykeError::runtime("unexpected flow in tr///", line))
6567 }
6568 }
6569 }
6570 Op::RegexMatchDyn(negate) => {
6571 let rhs = self.pop();
6572 let s = self.pop().into_string();
6573 let line = self.line();
6574 let exec = if let Some((pat, fl)) = rhs.regex_src_and_flags() {
6575 self.interp
6576 .regex_match_execute(s, &pat, &fl, false, "_", line)
6577 } else {
6578 let pattern = rhs.into_string();
6579 self.interp
6580 .regex_match_execute(s, &pattern, "", false, "_", line)
6581 };
6582 match exec {
6583 Ok(v) => {
6584 let matched = v.is_true();
6585 let out = if *negate { !matched } else { matched };
6586 self.push(StrykeValue::integer(if out { 1 } else { 0 }));
6587 }
6588 Err(FlowOrError::Error(e)) => return Err(e),
6589 Err(FlowOrError::Flow(_)) => {
6590 return Err(StrykeError::runtime("unexpected flow in =~", line));
6591 }
6592 }
6593 Ok(())
6594 }
6595 Op::RegexBoolToScalar => {
6596 let v = self.pop();
6597 self.push(if v.is_true() {
6598 StrykeValue::integer(1)
6599 } else {
6600 StrykeValue::string(String::new())
6601 });
6602 Ok(())
6603 }
6604 Op::SetRegexPos => {
6605 let key = self.pop().to_string();
6606 let val = self.pop();
6607 if val.is_undef() {
6608 self.interp.regex_pos.insert(key, None);
6609 } else {
6610 let u = val.to_int().max(0) as usize;
6611 self.interp.regex_pos.insert(key, Some(u));
6612 }
6613 Ok(())
6614 }
6615 Op::LoadRegex(pat_idx, flags_idx) => {
6616 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6617 let flags = constants[*flags_idx as usize].as_str_or_empty();
6618 let line = self.line();
6619 let pattern_owned = pattern.clone();
6620 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6621 Ok(r) => r,
6622 Err(FlowOrError::Error(e)) => return Err(e),
6623 Err(FlowOrError::Flow(_)) => {
6624 return Err(StrykeError::runtime(
6625 "unexpected flow in qr// compile",
6626 line,
6627 ));
6628 }
6629 };
6630 self.push(StrykeValue::regex(re, pattern_owned, flags.to_string()));
6631 Ok(())
6632 }
6633 Op::ConcatAppend(idx) => {
6634 let rhs = self.pop();
6635 let n = names[*idx as usize].as_str();
6636 let line = self.line();
6637 let result = self
6638 .interp
6639 .scope
6640 .scalar_concat_inplace(n, &rhs)
6641 .map_err(|e| e.at_line(line))?;
6642 self.push(result);
6643 Ok(())
6644 }
6645 Op::ConcatAppendSlot(slot) => {
6646 let rhs = self.pop();
6647 let result = self.interp.scope.scalar_slot_concat_inplace(*slot, &rhs);
6648 self.push(result);
6649 Ok(())
6650 }
6651 Op::ConcatAppendSlotVoid(slot) => {
6652 let rhs = self.pop();
6653 self.interp.scope.scalar_slot_concat_inplace(*slot, &rhs);
6654 Ok(())
6655 }
6656 Op::SlotLtIntJumpIfFalse(slot, limit, target) => {
6657 let val = self.interp.scope.get_scalar_slot(*slot);
6658 let lt = if let Some(i) = val.as_integer() {
6659 i < *limit as i64
6660 } else {
6661 val.to_number() < *limit as f64
6662 };
6663 if !lt {
6664 self.ip = *target;
6665 }
6666 Ok(())
6667 }
6668 Op::SlotIncLtIntJumpBack(slot, limit, body_target) => {
6669 let next_i = self
6675 .interp
6676 .scope
6677 .get_scalar_slot(*slot)
6678 .to_int()
6679 .wrapping_add(1);
6680 self.interp
6681 .scope
6682 .set_scalar_slot(*slot, StrykeValue::integer(next_i));
6683 if next_i < *limit as i64 {
6684 self.ip = *body_target;
6685 }
6686 Ok(())
6687 }
6688 Op::AccumSumLoop(sum_slot, i_slot, limit) => {
6689 let mut sum = self.interp.scope.get_scalar_slot(*sum_slot).to_int();
6694 let mut i = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6695 let limit = *limit as i64;
6696 while i < limit {
6697 sum = sum.wrapping_add(i);
6698 i = i.wrapping_add(1);
6699 }
6700 self.interp
6701 .scope
6702 .set_scalar_slot(*sum_slot, StrykeValue::integer(sum));
6703 self.interp
6704 .scope
6705 .set_scalar_slot(*i_slot, StrykeValue::integer(i));
6706 Ok(())
6707 }
6708 Op::AddHashElemPlainKeyToSlot(sum_slot, k_name_idx, h_name_idx) => {
6709 let k_name = names[*k_name_idx as usize].as_str();
6714 let h_name = names[*h_name_idx as usize].as_str();
6715 self.interp.touch_env_hash(h_name);
6716 let key = self.interp.scope.get_scalar(k_name).to_string();
6717 let elem = self.interp.scope.get_hash_element(h_name, &key);
6718 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6719 let new_v =
6720 if let (Some(a), Some(b)) = (cur.as_integer(), elem.as_integer()) {
6721 StrykeValue::integer(a.wrapping_add(b))
6722 } else {
6723 StrykeValue::float(cur.to_number() + elem.to_number())
6724 };
6725 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6726 Ok(())
6727 }
6728 Op::AddHashElemSlotKeyToSlot(sum_slot, k_slot, h_name_idx) => {
6729 let h_name = names[*h_name_idx as usize].as_str();
6732 self.interp.touch_env_hash(h_name);
6733 let key_val = self.interp.scope.get_scalar_slot(*k_slot);
6734 let key = key_val.to_string();
6735 let elem = self.interp.scope.get_hash_element(h_name, &key);
6736 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6737 let new_v =
6738 if let (Some(a), Some(b)) = (cur.as_integer(), elem.as_integer()) {
6739 StrykeValue::integer(a.wrapping_add(b))
6740 } else {
6741 StrykeValue::float(cur.to_number() + elem.to_number())
6742 };
6743 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6744 Ok(())
6745 }
6746 Op::SumHashValuesToSlot(sum_slot, h_name_idx) => {
6747 let h_name = names[*h_name_idx as usize].as_str();
6753 self.interp.touch_env_hash(h_name);
6754 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6755 let mut int_acc: i64 = cur.as_integer().unwrap_or(0);
6756 let mut float_acc: f64 = 0.0;
6757 let mut is_int = cur.as_integer().is_some();
6758 if !is_int {
6759 float_acc = cur.to_number();
6760 }
6761 self.interp.scope.for_each_hash_value(h_name, |v| {
6765 if is_int {
6766 if let Some(x) = v.as_integer() {
6767 int_acc = int_acc.wrapping_add(x);
6768 return;
6769 }
6770 float_acc = int_acc as f64;
6771 is_int = false;
6772 }
6773 float_acc += v.to_number();
6774 });
6775 let new_v = if is_int {
6776 StrykeValue::integer(int_acc)
6777 } else {
6778 StrykeValue::float(float_acc)
6779 };
6780 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6781 Ok(())
6782 }
6783 Op::SetHashIntTimesLoop(h_name_idx, i_slot, k, limit) => {
6784 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6789 let lim = *limit as i64;
6790 if i_cur < lim {
6791 let n = names[*h_name_idx as usize].as_str();
6792 self.require_hash_mutable(n)?;
6793 self.interp.touch_env_hash(n);
6794 let line = self.line();
6795 self.interp
6796 .scope
6797 .set_hash_int_times_range(n, i_cur, lim, *k as i64)
6798 .map_err(|e| e.at_line(line))?;
6799 }
6800 self.interp
6801 .scope
6802 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6803 Ok(())
6804 }
6805 Op::PushIntRangeToArrayLoop(arr_name_idx, i_slot, limit) => {
6806 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6811 let lim = *limit as i64;
6812 if i_cur < lim {
6813 let n = names[*arr_name_idx as usize].as_str();
6814 self.require_array_mutable(n)?;
6815 let line = self.line();
6816 self.interp
6817 .scope
6818 .push_int_range_to_array(n, i_cur, lim)
6819 .map_err(|e| e.at_line(line))?;
6820 }
6821 self.interp
6822 .scope
6823 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6824 Ok(())
6825 }
6826 Op::ConcatConstSlotLoop(const_idx, s_slot, i_slot, limit) => {
6827 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6834 let lim = *limit as i64;
6835 if i_cur < lim {
6836 let n_iters = (lim - i_cur) as usize;
6837 let rhs = constants[*const_idx as usize].as_str_or_empty();
6838 if !self
6839 .interp
6840 .scope
6841 .scalar_slot_concat_repeat_inplace(*s_slot, &rhs, n_iters)
6842 {
6843 self.interp
6844 .scope
6845 .scalar_slot_concat_repeat_slow(*s_slot, &rhs, n_iters);
6846 }
6847 }
6848 self.interp
6849 .scope
6850 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6851 Ok(())
6852 }
6853 Op::AddAssignSlotSlot(dst, src) => {
6854 let a = self.interp.scope.get_scalar_slot(*dst);
6855 let b = self.interp.scope.get_scalar_slot(*src);
6856 let result = crate::value::compat_add(&a, &b);
6857 self.interp.scope.set_scalar_slot(*dst, result.clone());
6858 self.push(result);
6859 Ok(())
6860 }
6861 Op::AddAssignSlotSlotVoid(dst, src) => {
6862 let a = self.interp.scope.get_scalar_slot(*dst);
6863 let b = self.interp.scope.get_scalar_slot(*src);
6864 let result = crate::value::compat_add(&a, &b);
6865 self.interp.scope.set_scalar_slot(*dst, result);
6866 Ok(())
6867 }
6868 Op::SubAssignSlotSlot(dst, src) => {
6869 let a = self.interp.scope.get_scalar_slot(*dst);
6870 let b = self.interp.scope.get_scalar_slot(*src);
6871 let result = crate::value::compat_sub(&a, &b);
6872 self.interp.scope.set_scalar_slot(*dst, result.clone());
6873 self.push(result);
6874 Ok(())
6875 }
6876 Op::MulAssignSlotSlot(dst, src) => {
6877 let a = self.interp.scope.get_scalar_slot(*dst);
6878 let b = self.interp.scope.get_scalar_slot(*src);
6879 let result = crate::value::compat_mul(&a, &b);
6880 self.interp.scope.set_scalar_slot(*dst, result.clone());
6881 self.push(result);
6882 Ok(())
6883 }
6884
6885 Op::GetScalarSlot(slot) => {
6887 let val = self.interp.scope.get_scalar_slot(*slot);
6888 self.push(val);
6889 Ok(())
6890 }
6891 Op::SetScalarSlot(slot) => {
6892 let val = self.pop();
6893 self.interp
6894 .scope
6895 .set_scalar_slot_checked(*slot, val, None)
6896 .map_err(|e| e.at_line(self.line()))?;
6897 Ok(())
6898 }
6899 Op::SetScalarSlotKeep(slot) => {
6900 let val = self.peek().dup_stack();
6901 self.interp
6902 .scope
6903 .set_scalar_slot_checked(*slot, val, None)
6904 .map_err(|e| e.at_line(self.line()))?;
6905 Ok(())
6906 }
6907 Op::DeclareScalarSlot(slot, name_idx) => {
6908 let val = self.pop();
6909 let name_opt = if *name_idx == u16::MAX {
6910 None
6911 } else {
6912 Some(names[*name_idx as usize].as_str())
6913 };
6914 self.interp.scope.declare_scalar_slot(*slot, val, name_opt);
6915 Ok(())
6916 }
6917 Op::GetArg(idx) => {
6918 let val = if let Some(frame) = self.call_stack.last() {
6920 let arg_pos = frame.stack_base + *idx as usize;
6921 self.stack
6922 .get(arg_pos)
6923 .cloned()
6924 .unwrap_or(StrykeValue::UNDEF)
6925 } else {
6926 StrykeValue::UNDEF
6927 };
6928 self.push(val);
6929 Ok(())
6930 }
6931
6932 Op::ReadIntoVar(name_idx) => {
6933 let length = self.pop().to_int() as usize;
6934 let fh_val = self.pop();
6935 let name = &names[*name_idx as usize];
6936 let line = self.line();
6937 let result = vm_interp_result(
6938 self.interp.builtin_read_into(fh_val, name, length, line),
6939 line,
6940 )?;
6941 self.push(result);
6942 Ok(())
6943 }
6944 Op::ChompInPlace(lvalue_idx) => {
6945 let val = self.pop();
6946 let target = &self.lvalues[*lvalue_idx as usize];
6947 let line = self.line();
6948 match self.interp.chomp_inplace_execute(val, target) {
6949 Ok(v) => self.push(v),
6950 Err(FlowOrError::Error(e)) => return Err(e),
6951 Err(FlowOrError::Flow(_)) => {
6952 return Err(StrykeError::runtime("unexpected flow in chomp", line));
6953 }
6954 }
6955 Ok(())
6956 }
6957 Op::ChopInPlace(lvalue_idx) => {
6958 let val = self.pop();
6959 let target = &self.lvalues[*lvalue_idx as usize];
6960 let line = self.line();
6961 match self.interp.chop_inplace_execute(val, target) {
6962 Ok(v) => self.push(v),
6963 Err(FlowOrError::Error(e)) => return Err(e),
6964 Err(FlowOrError::Flow(_)) => {
6965 return Err(StrykeError::runtime("unexpected flow in chop", line));
6966 }
6967 }
6968 Ok(())
6969 }
6970 Op::SubstrFourArg(idx) => {
6971 let (string_e, offset_e, length_e, rep_e) =
6972 &self.substr_four_arg_entries[*idx as usize];
6973 let v = vm_interp_result(
6974 self.interp.eval_substr_expr(
6975 string_e,
6976 offset_e,
6977 length_e.as_ref(),
6978 Some(rep_e),
6979 self.line(),
6980 ),
6981 self.line(),
6982 )?;
6983 self.push(v);
6984 Ok(())
6985 }
6986 Op::KeysExpr(idx) => {
6987 let i = *idx as usize;
6988 let line = self.line();
6989 let v = if let Some(&(start, end)) = self
6990 .keys_expr_bytecode_ranges
6991 .get(i)
6992 .and_then(|r| r.as_ref())
6993 {
6994 let val = self.run_block_region(start, end, op_count)?;
6995 vm_interp_result(VMHelper::keys_from_value(val, line), line)?
6996 } else {
6997 let e = &self.keys_expr_entries[i];
6998 vm_interp_result(self.interp.eval_keys_expr(e, line), line)?
6999 };
7000 self.push(v);
7001 Ok(())
7002 }
7003 Op::KeysExprScalar(idx) => {
7004 let i = *idx as usize;
7005 let line = self.line();
7006 let v = if let Some(&(start, end)) = self
7007 .keys_expr_bytecode_ranges
7008 .get(i)
7009 .and_then(|r| r.as_ref())
7010 {
7011 let val = self.run_block_region(start, end, op_count)?;
7012 vm_interp_result(VMHelper::keys_from_value(val, line), line)?
7013 } else {
7014 let e = &self.keys_expr_entries[i];
7015 vm_interp_result(self.interp.eval_keys_expr(e, line), line)?
7016 };
7017 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
7018 self.push(StrykeValue::integer(n));
7019 Ok(())
7020 }
7021 Op::ValuesExpr(idx) => {
7022 let i = *idx as usize;
7023 let line = self.line();
7024 let v = if let Some(&(start, end)) = self
7025 .values_expr_bytecode_ranges
7026 .get(i)
7027 .and_then(|r| r.as_ref())
7028 {
7029 let val = self.run_block_region(start, end, op_count)?;
7030 vm_interp_result(VMHelper::values_from_value(val, line), line)?
7031 } else {
7032 let e = &self.values_expr_entries[i];
7033 vm_interp_result(self.interp.eval_values_expr(e, line), line)?
7034 };
7035 self.push(v);
7036 Ok(())
7037 }
7038 Op::ValuesExprScalar(idx) => {
7039 let i = *idx as usize;
7040 let line = self.line();
7041 let v = if let Some(&(start, end)) = self
7042 .values_expr_bytecode_ranges
7043 .get(i)
7044 .and_then(|r| r.as_ref())
7045 {
7046 let val = self.run_block_region(start, end, op_count)?;
7047 vm_interp_result(VMHelper::values_from_value(val, line), line)?
7048 } else {
7049 let e = &self.values_expr_entries[i];
7050 vm_interp_result(self.interp.eval_values_expr(e, line), line)?
7051 };
7052 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
7053 self.push(StrykeValue::integer(n));
7054 Ok(())
7055 }
7056 Op::DeleteExpr(idx) => {
7057 let e = &self.delete_expr_entries[*idx as usize];
7058 let v = vm_interp_result(
7059 self.interp.eval_delete_operand(e, self.line()),
7060 self.line(),
7061 )?;
7062 self.push(v);
7063 Ok(())
7064 }
7065 Op::ExistsExpr(idx) => {
7066 let e = &self.exists_expr_entries[*idx as usize];
7067 let v = vm_interp_result(
7068 self.interp.eval_exists_operand(e, self.line()),
7069 self.line(),
7070 )?;
7071 self.push(v);
7072 Ok(())
7073 }
7074 Op::PushExpr(idx) => {
7075 let (array, values) = &self.push_expr_entries[*idx as usize];
7076 let v = vm_interp_result(
7077 self.interp
7078 .eval_push_expr(array, values.as_slice(), self.line()),
7079 self.line(),
7080 )?;
7081 self.push(v);
7082 Ok(())
7083 }
7084 Op::PopExpr(idx) => {
7085 let e = &self.pop_expr_entries[*idx as usize];
7086 let v = vm_interp_result(
7087 self.interp.eval_pop_expr(e, self.line()),
7088 self.line(),
7089 )?;
7090 self.push(v);
7091 Ok(())
7092 }
7093 Op::ShiftExpr(idx) => {
7094 let e = &self.shift_expr_entries[*idx as usize];
7095 let v = vm_interp_result(
7096 self.interp.eval_shift_expr(e, self.line()),
7097 self.line(),
7098 )?;
7099 self.push(v);
7100 Ok(())
7101 }
7102 Op::UnshiftExpr(idx) => {
7103 let (array, values) = &self.unshift_expr_entries[*idx as usize];
7104 let v = vm_interp_result(
7105 self.interp
7106 .eval_unshift_expr(array, values.as_slice(), self.line()),
7107 self.line(),
7108 )?;
7109 self.push(v);
7110 Ok(())
7111 }
7112 Op::SpliceExpr(idx) => {
7113 let (array, offset, length, replacement) =
7114 &self.splice_expr_entries[*idx as usize];
7115 let v = vm_interp_result(
7116 self.interp.eval_splice_expr(
7117 array,
7118 offset.as_ref(),
7119 length.as_ref(),
7120 replacement.as_slice(),
7121 self.interp.wantarray_kind,
7122 self.line(),
7123 ),
7124 self.line(),
7125 )?;
7126 self.push(v);
7127 Ok(())
7128 }
7129
7130 Op::MakeScalarRef => {
7132 let val = self.pop();
7133 self.push(StrykeValue::scalar_ref(Arc::new(RwLock::new(val))));
7134 Ok(())
7135 }
7136 Op::MakeScalarBindingRef(name_idx) => {
7137 let name = names[*name_idx as usize].clone();
7138 self.push(StrykeValue::scalar_binding_ref(name));
7139 Ok(())
7140 }
7141 Op::MakeArrayBindingRef(name_idx) => {
7142 let name = &names[*name_idx as usize];
7143 let arc = self.interp.scope.promote_array_to_shared(name);
7147 self.push(StrykeValue::array_ref(arc));
7148 Ok(())
7149 }
7150 Op::MakeHashBindingRef(name_idx) => {
7151 let name = &names[*name_idx as usize];
7152 self.interp.touch_env_hash(name);
7157 let arc = self.interp.scope.promote_hash_to_shared(name);
7158 self.push(StrykeValue::hash_ref(arc));
7159 Ok(())
7160 }
7161 Op::MakeArrayRefAlias => {
7162 let v = self.pop();
7163 let line = self.line();
7164 let out =
7165 vm_interp_result(self.interp.make_array_ref_alias(v, line), line)?;
7166 self.push(out);
7167 Ok(())
7168 }
7169 Op::MakeHashRefAlias => {
7170 let v = self.pop();
7171 let line = self.line();
7172 let out = vm_interp_result(self.interp.make_hash_ref_alias(v, line), line)?;
7173 self.push(out);
7174 Ok(())
7175 }
7176 Op::MakeArrayRef => {
7177 let val = self.pop();
7178 let val = self.interp.scope.resolve_container_binding_ref(val);
7179 let arr = if let Some(a) = val.as_array_vec() {
7180 a
7181 } else {
7182 vec![val]
7183 };
7184 self.push(StrykeValue::array_ref(Arc::new(RwLock::new(arr))));
7185 Ok(())
7186 }
7187 Op::MakeHashRef => {
7188 let val = self.pop();
7189 let map = if let Some(h) = val.as_hash_map() {
7190 h
7191 } else {
7192 let items = val.to_list();
7193 let mut m = IndexMap::new();
7194 let mut i = 0;
7195 while i + 1 < items.len() {
7196 m.insert(items[i].to_string(), items[i + 1].clone());
7197 i += 2;
7198 }
7199 m
7200 };
7201 self.push(StrykeValue::hash_ref(Arc::new(RwLock::new(map))));
7202 Ok(())
7203 }
7204 Op::MakeCodeRef(block_idx, sig_idx) => {
7205 let block = self.blocks[*block_idx as usize].clone();
7206 let params = self.code_ref_sigs[*sig_idx as usize].clone();
7207 let captured = self.interp.scope.capture();
7208 self.push(StrykeValue::code_ref(Arc::new(crate::value::StrykeSub {
7209 name: "__ANON__".to_string(),
7210 params,
7211 body: block,
7212 closure_env: Some(captured),
7213 prototype: None,
7214 fib_like: None,
7215 })));
7216 Ok(())
7217 }
7218 Op::LoadNamedSubRef(name_idx) => {
7219 let name = names[*name_idx as usize].as_str();
7220 let line = self.line();
7221 let sub = self.interp.resolve_sub_by_name(name).ok_or_else(|| {
7222 StrykeError::runtime(
7223 self.interp.undefined_subroutine_resolve_message(name),
7224 line,
7225 )
7226 })?;
7227 self.push(StrykeValue::code_ref(sub));
7228 Ok(())
7229 }
7230 Op::LoadDynamicSubRef => {
7231 let name = self.pop().to_string();
7232 let line = self.line();
7233 let sub = self.interp.resolve_sub_by_name(&name).ok_or_else(|| {
7234 StrykeError::runtime(
7235 self.interp.undefined_subroutine_resolve_message(&name),
7236 line,
7237 )
7238 })?;
7239 self.push(StrykeValue::code_ref(sub));
7240 Ok(())
7241 }
7242 Op::LoadDynamicTypeglob => {
7243 let name = self.pop().to_string();
7244 let n = self.interp.resolve_io_handle_name(&name);
7245 self.push(StrykeValue::string(n));
7246 Ok(())
7247 }
7248 Op::CopyTypeglobSlots(lhs_i, rhs_i) => {
7249 let lhs = self.names[*lhs_i as usize].as_str();
7250 let rhs = self.names[*rhs_i as usize].as_str();
7251 let line = self.line();
7252 self.interp
7253 .copy_typeglob_slots(lhs, rhs, line)
7254 .map_err(|e| e.at_line(line))?;
7255 Ok(())
7256 }
7257 Op::TypeglobAssignFromValue(name_idx) => {
7258 let val = self.pop();
7259 let name = self.names[*name_idx as usize].as_str();
7260 let line = self.line();
7261 vm_interp_result(
7262 self.interp.assign_typeglob_value(name, val.clone(), line),
7263 line,
7264 )?;
7265 self.push(val);
7266 Ok(())
7267 }
7268 Op::TypeglobAssignFromValueDynamic => {
7269 let val = self.pop();
7270 let name = self.pop().to_string();
7271 let line = self.line();
7272 vm_interp_result(
7273 self.interp.assign_typeglob_value(&name, val.clone(), line),
7274 line,
7275 )?;
7276 self.push(val);
7277 Ok(())
7278 }
7279 Op::CopyTypeglobSlotsDynamicLhs(rhs_i) => {
7280 let lhs = self.pop().to_string();
7281 let rhs = self.names[*rhs_i as usize].as_str();
7282 let line = self.line();
7283 self.interp
7284 .copy_typeglob_slots(&lhs, rhs, line)
7285 .map_err(|e| e.at_line(line))?;
7286 Ok(())
7287 }
7288 Op::SymbolicDeref(kind_byte) => {
7289 let v = self.pop();
7290 let kind = match *kind_byte {
7291 0 => Sigil::Scalar,
7292 1 => Sigil::Array,
7293 2 => Sigil::Hash,
7294 3 => Sigil::Typeglob,
7295 _ => {
7296 return Err(StrykeError::runtime(
7297 "VM: bad SymbolicDeref kind byte",
7298 self.line(),
7299 ));
7300 }
7301 };
7302 let line = self.line();
7303 let out =
7304 vm_interp_result(self.interp.symbolic_deref(v, kind, line), line)?;
7305 self.push(out);
7306 Ok(())
7307 }
7308
7309 Op::ArrowArray => {
7311 let idx = self.pop().to_int();
7312 let r = self.pop();
7313 let line = self.line();
7314 let v = vm_interp_result(
7315 self.interp.read_arrow_array_element(r, idx, line),
7316 line,
7317 )?;
7318 self.push(v);
7319 Ok(())
7320 }
7321 Op::ArrowHash => {
7322 let key = self.pop().to_string();
7323 let r = self.pop();
7324 let line = self.line();
7325 let v = vm_interp_result(
7326 self.interp.read_arrow_hash_element(r, key.as_str(), line),
7327 line,
7328 )?;
7329 self.push(v);
7330 Ok(())
7331 }
7332 Op::SetArrowHash => {
7333 let key = self.pop().to_string();
7334 let r = self.pop();
7335 let val = self.pop();
7336 let line = self.line();
7337 vm_interp_result(
7338 self.interp.assign_arrow_hash_deref(r, key, val, line),
7339 line,
7340 )?;
7341 Ok(())
7342 }
7343 Op::SetArrowArray => {
7344 let idx = self.pop().to_int();
7345 let r = self.pop();
7346 let val = self.pop();
7347 let line = self.line();
7348 vm_interp_result(
7349 self.interp.assign_arrow_array_deref(r, idx, val, line),
7350 line,
7351 )?;
7352 Ok(())
7353 }
7354 Op::SetArrowArrayKeep => {
7355 let idx = self.pop().to_int();
7356 let r = self.pop();
7357 let val = self.pop();
7358 let val_keep = val.clone();
7359 let line = self.line();
7360 vm_interp_result(
7361 self.interp.assign_arrow_array_deref(r, idx, val, line),
7362 line,
7363 )?;
7364 self.push(val_keep);
7365 Ok(())
7366 }
7367 Op::SetArrowHashKeep => {
7368 let key = self.pop().to_string();
7369 let r = self.pop();
7370 let val = self.pop();
7371 let val_keep = val.clone();
7372 let line = self.line();
7373 vm_interp_result(
7374 self.interp.assign_arrow_hash_deref(r, key, val, line),
7375 line,
7376 )?;
7377 self.push(val_keep);
7378 Ok(())
7379 }
7380 Op::ArrowArrayPostfix(b) => {
7381 let idx = self.pop().to_int();
7382 let r = self.pop();
7383 let line = self.line();
7384 let old = vm_interp_result(
7385 self.interp.arrow_array_postfix(r, idx, *b == 1, line),
7386 line,
7387 )?;
7388 self.push(old);
7389 Ok(())
7390 }
7391 Op::ArrowHashPostfix(b) => {
7392 let key = self.pop().to_string();
7393 let r = self.pop();
7394 let line = self.line();
7395 let old = vm_interp_result(
7396 self.interp.arrow_hash_postfix(r, key, *b == 1, line),
7397 line,
7398 )?;
7399 self.push(old);
7400 Ok(())
7401 }
7402 Op::SetSymbolicScalarRef => {
7403 let r = self.pop();
7404 let val = self.pop();
7405 let line = self.line();
7406 vm_interp_result(self.interp.assign_scalar_ref_deref(r, val, line), line)?;
7407 Ok(())
7408 }
7409 Op::SetSymbolicScalarRefKeep => {
7410 let r = self.pop();
7411 let val = self.pop();
7412 let val_keep = val.clone();
7413 let line = self.line();
7414 vm_interp_result(self.interp.assign_scalar_ref_deref(r, val, line), line)?;
7415 self.push(val_keep);
7416 Ok(())
7417 }
7418 Op::SetSymbolicArrayRef => {
7419 let r = self.pop();
7420 let val = self.pop();
7421 let line = self.line();
7422 vm_interp_result(
7423 self.interp.assign_symbolic_array_ref_deref(r, val, line),
7424 line,
7425 )?;
7426 Ok(())
7427 }
7428 Op::SetSymbolicHashRef => {
7429 let r = self.pop();
7430 let val = self.pop();
7431 let line = self.line();
7432 vm_interp_result(
7433 self.interp.assign_symbolic_hash_ref_deref(r, val, line),
7434 line,
7435 )?;
7436 Ok(())
7437 }
7438 Op::SetSymbolicTypeglobRef => {
7439 let r = self.pop();
7440 let val = self.pop();
7441 let line = self.line();
7442 vm_interp_result(
7443 self.interp.assign_symbolic_typeglob_ref_deref(r, val, line),
7444 line,
7445 )?;
7446 Ok(())
7447 }
7448 Op::SymbolicScalarRefPostfix(b) => {
7449 let r = self.pop();
7450 let line = self.line();
7451 let old = vm_interp_result(
7452 self.interp.symbolic_scalar_ref_postfix(r, *b == 1, line),
7453 line,
7454 )?;
7455 self.push(old);
7456 Ok(())
7457 }
7458 Op::ArrowCall(wa) => {
7459 let want = WantarrayCtx::from_byte(*wa);
7460 let args_val = self.pop();
7461 let r = self.pop();
7462 let r = if let Some(inner) = r.as_scalar_ref() {
7464 inner.read().clone()
7465 } else {
7466 r
7467 };
7468 let args = args_val.to_list();
7469 if let Some(sub) = r.as_code_ref() {
7470 if let Some(hof_result) =
7475 self.interp.try_hof_dispatch(&sub, &args, want, self.line())
7476 {
7477 let v = vm_interp_result(hof_result, self.line())?;
7478 self.push(v);
7479 return Ok(());
7480 }
7481 self.interp.current_sub_stack.push(sub.clone());
7482 let saved_wa = self.interp.wantarray_kind;
7483 self.interp.wantarray_kind = want;
7484 self.interp.scope_push_hook();
7485 self.interp.scope.declare_array("_", args.clone());
7486 if let Some(ref env) = sub.closure_env {
7487 self.interp.scope.restore_capture(env);
7488 }
7489 let line = self.line();
7490 let argv = self.interp.scope.take_sub_underscore().unwrap_or_default();
7491 self.interp
7492 .apply_sub_signature(sub.as_ref(), &argv, line)
7493 .map_err(|e| e.at_line(line))?;
7494 self.interp.scope.declare_array("_", argv.clone());
7495 self.interp.scope.set_closure_args(&argv);
7497 let result = self.interp.exec_block_no_scope(&sub.body);
7498 self.interp.wantarray_kind = saved_wa;
7499 self.interp.scope_pop_hook();
7500 self.interp.current_sub_stack.pop();
7501 match result {
7502 Ok(v) => self.push(v),
7503 Err(crate::vm_helper::FlowOrError::Flow(
7504 crate::vm_helper::Flow::Return(v),
7505 )) => self.push(v),
7506 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
7507 Err(_) => self.push(StrykeValue::UNDEF),
7508 }
7509 } else {
7510 return Err(StrykeError::runtime("Not a code reference", self.line()));
7511 }
7512 Ok(())
7513 }
7514 Op::IndirectCall(argc, wa, pass_flag) => {
7515 let want = WantarrayCtx::from_byte(*wa);
7516 let line = self.line();
7517 let arg_vals = if *pass_flag != 0 {
7518 self.interp.scope.get_array("_")
7519 } else {
7520 let n = *argc as usize;
7521 let mut args = Vec::with_capacity(n);
7522 for _ in 0..n {
7523 args.push(self.pop());
7524 }
7525 args.reverse();
7526 args
7527 };
7528 let target = self.pop();
7529 if let Some(sub) = target.as_code_ref() {
7531 if let Some(hof_result) =
7532 self.interp.try_hof_dispatch(&sub, &arg_vals, want, line)
7533 {
7534 let v = vm_interp_result(hof_result, line)?;
7535 self.push(v);
7536 return Ok(());
7537 }
7538 }
7539 let r = self
7540 .interp
7541 .dispatch_indirect_call(target, arg_vals, want, line);
7542 let v = vm_interp_result(r, line)?;
7543 self.push(v);
7544 Ok(())
7545 }
7546
7547 Op::MethodCall(name_idx, argc, wa) => {
7549 self.run_method_op(*name_idx, *argc, *wa, false)?;
7550 Ok(())
7551 }
7552 Op::MethodCallSuper(name_idx, argc, wa) => {
7553 self.run_method_op(*name_idx, *argc, *wa, true)?;
7554 Ok(())
7555 }
7556
7557 Op::FileTestOp(test) => {
7559 let path = self.pop().to_string();
7560 let op = *test as char;
7561 if matches!(op, 'M' | 'A' | 'C') {
7563 #[cfg(unix)]
7564 {
7565 let v = match crate::perl_fs::filetest_age_days(&path, op) {
7566 Some(days) => StrykeValue::float(days),
7567 None => StrykeValue::UNDEF,
7568 };
7569 self.push(v);
7570 return Ok(());
7571 }
7572 #[cfg(not(unix))]
7573 {
7574 self.push(StrykeValue::UNDEF);
7575 return Ok(());
7576 }
7577 }
7578 if op == 's' {
7580 let v = match std::fs::metadata(&path) {
7581 Ok(m) => StrykeValue::integer(m.len() as i64),
7582 Err(_) => StrykeValue::UNDEF,
7583 };
7584 self.push(v);
7585 return Ok(());
7586 }
7587 let result = match op {
7588 'e' => std::path::Path::new(&path).exists(),
7589 'f' => std::path::Path::new(&path).is_file(),
7590 'd' => std::path::Path::new(&path).is_dir(),
7591 'l' => std::path::Path::new(&path).is_symlink(),
7592 #[cfg(unix)]
7593 'r' => crate::perl_fs::filetest_effective_access(&path, 4),
7594 #[cfg(not(unix))]
7595 'r' => std::fs::metadata(&path).is_ok(),
7596 #[cfg(unix)]
7597 'w' => crate::perl_fs::filetest_effective_access(&path, 2),
7598 #[cfg(not(unix))]
7599 'w' => std::fs::metadata(&path).is_ok(),
7600 #[cfg(unix)]
7601 'x' => crate::perl_fs::filetest_effective_access(&path, 1),
7602 #[cfg(not(unix))]
7603 'x' => false,
7604 #[cfg(unix)]
7605 'o' => crate::perl_fs::filetest_owned_effective(&path),
7606 #[cfg(not(unix))]
7607 'o' => false,
7608 #[cfg(unix)]
7609 'R' => crate::perl_fs::filetest_real_access(&path, libc::R_OK),
7610 #[cfg(not(unix))]
7611 'R' => false,
7612 #[cfg(unix)]
7613 'W' => crate::perl_fs::filetest_real_access(&path, libc::W_OK),
7614 #[cfg(not(unix))]
7615 'W' => false,
7616 #[cfg(unix)]
7617 'X' => crate::perl_fs::filetest_real_access(&path, libc::X_OK),
7618 #[cfg(not(unix))]
7619 'X' => false,
7620 #[cfg(unix)]
7621 'O' => crate::perl_fs::filetest_owned_real(&path),
7622 #[cfg(not(unix))]
7623 'O' => false,
7624 'z' => std::fs::metadata(&path)
7625 .map(|m| m.len() == 0)
7626 .unwrap_or(true),
7627 't' => crate::perl_fs::filetest_is_tty(&path),
7628 #[cfg(unix)]
7629 'p' => crate::perl_fs::filetest_is_pipe(&path),
7630 #[cfg(not(unix))]
7631 'p' => false,
7632 #[cfg(unix)]
7633 'S' => crate::perl_fs::filetest_is_socket(&path),
7634 #[cfg(not(unix))]
7635 'S' => false,
7636 #[cfg(unix)]
7637 'b' => crate::perl_fs::filetest_is_block_device(&path),
7638 #[cfg(not(unix))]
7639 'b' => false,
7640 #[cfg(unix)]
7641 'c' => crate::perl_fs::filetest_is_char_device(&path),
7642 #[cfg(not(unix))]
7643 'c' => false,
7644 #[cfg(unix)]
7645 'u' => crate::perl_fs::filetest_is_setuid(&path),
7646 #[cfg(not(unix))]
7647 'u' => false,
7648 #[cfg(unix)]
7649 'g' => crate::perl_fs::filetest_is_setgid(&path),
7650 #[cfg(not(unix))]
7651 'g' => false,
7652 #[cfg(unix)]
7653 'k' => crate::perl_fs::filetest_is_sticky(&path),
7654 #[cfg(not(unix))]
7655 'k' => false,
7656 'T' => crate::perl_fs::filetest_is_text(&path),
7657 'B' => crate::perl_fs::filetest_is_binary(&path),
7658 _ => false,
7659 };
7660 self.push(StrykeValue::integer(if result { 1 } else { 0 }));
7661 Ok(())
7662 }
7663
7664 Op::MapIntMul(k) => {
7666 let list = self.pop().to_list();
7667 if list.len() == 1 {
7668 if let Some(p) = list[0].as_pipeline() {
7669 let line = self.line();
7670 let sub = VMHelper::pipeline_int_mul_sub(*k);
7671 self.interp.pipeline_push(&p, PipelineOp::Map(sub), line)?;
7672 self.push(StrykeValue::pipeline(Arc::clone(&p)));
7673 return Ok(());
7674 }
7675 }
7676 let mut result = Vec::with_capacity(list.len());
7677 for item in list {
7678 let n = item.to_int();
7679 result.push(StrykeValue::integer(n.wrapping_mul(*k)));
7680 }
7681 self.push(StrykeValue::array(result));
7682 Ok(())
7683 }
7684 Op::GrepIntModEq(m, r) => {
7685 let list = self.pop().to_list();
7686 let mut result = Vec::new();
7687 for item in list {
7688 let n = item.to_int();
7689 if n % m == *r {
7690 result.push(item);
7691 }
7692 }
7693 self.push(StrykeValue::array(result));
7694 Ok(())
7695 }
7696 Op::MapWithBlock(block_idx) => {
7697 let list = self.pop().to_list();
7698 self.map_with_block_common(list, *block_idx, false, op_count)
7699 }
7700 Op::FlatMapWithBlock(block_idx) => {
7701 let list = self.pop().to_list();
7702 self.map_with_block_common(list, *block_idx, true, op_count)
7703 }
7704 Op::MapWithExpr(expr_idx) => {
7705 let list = self.pop().to_list();
7706 self.map_with_expr_common(list, *expr_idx, false, op_count)
7707 }
7708 Op::FlatMapWithExpr(expr_idx) => {
7709 let list = self.pop().to_list();
7710 self.map_with_expr_common(list, *expr_idx, true, op_count)
7711 }
7712 Op::MapsWithBlock(block_idx) => {
7713 let val = self.pop();
7714 let block = self.blocks[*block_idx as usize].clone();
7715 let out =
7716 self.interp
7717 .map_stream_block_output(val, &block, false, self.line())?;
7718 self.push(out);
7719 Ok(())
7720 }
7721 Op::MapsFlatMapWithBlock(block_idx) => {
7722 let val = self.pop();
7723 let block = self.blocks[*block_idx as usize].clone();
7724 let out =
7725 self.interp
7726 .map_stream_block_output(val, &block, true, self.line())?;
7727 self.push(out);
7728 Ok(())
7729 }
7730 Op::MapsWithExpr(expr_idx) => {
7731 let val = self.pop();
7732 let idx = *expr_idx as usize;
7733 let expr = self.map_expr_entries[idx].clone();
7734 let out =
7735 self.interp
7736 .map_stream_expr_output(val, &expr, false, self.line())?;
7737 self.push(out);
7738 Ok(())
7739 }
7740 Op::MapsFlatMapWithExpr(expr_idx) => {
7741 let val = self.pop();
7742 let idx = *expr_idx as usize;
7743 let expr = self.map_expr_entries[idx].clone();
7744 let out =
7745 self.interp
7746 .map_stream_expr_output(val, &expr, true, self.line())?;
7747 self.push(out);
7748 Ok(())
7749 }
7750 Op::FilterWithBlock(block_idx) => {
7751 let val = self.pop();
7752 let block = self.blocks[*block_idx as usize].clone();
7753 let out =
7754 self.interp
7755 .filter_stream_block_output(val, &block, self.line())?;
7756 self.push(out);
7757 Ok(())
7758 }
7759 Op::FilterWithExpr(expr_idx) => {
7760 let val = self.pop();
7761 let idx = *expr_idx as usize;
7762 let expr = self.grep_expr_entries[idx].clone();
7763 let out = self
7764 .interp
7765 .filter_stream_expr_output(val, &expr, self.line())?;
7766 self.push(out);
7767 Ok(())
7768 }
7769 Op::ChunkByWithBlock(block_idx) => {
7770 let list = self.pop().to_list();
7771 self.chunk_by_with_block_common(list, *block_idx, op_count)
7772 }
7773 Op::ChunkByWithExpr(expr_idx) => {
7774 let list = self.pop().to_list();
7775 self.chunk_by_with_expr_common(list, *expr_idx, op_count)
7776 }
7777 Op::GrepWithBlock(block_idx) => {
7778 let list = self.pop().to_list();
7779 if list.len() == 1 {
7780 if let Some(p) = list[0].as_pipeline() {
7781 let idx = *block_idx as usize;
7782 let sub = self.interp.anon_coderef_from_block(&self.blocks[idx]);
7783 let line = self.line();
7784 self.interp
7785 .pipeline_push(&p, PipelineOp::Filter(sub), line)?;
7786 self.push(StrykeValue::pipeline(Arc::clone(&p)));
7787 return Ok(());
7788 }
7789 }
7790 let idx = *block_idx as usize;
7791 let saved_chain = self.interp.scope.save_topic_chain();
7796 if let Some(&(start, end)) =
7797 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7798 {
7799 let mut result = Vec::new();
7800 for item in list {
7801 self.interp.scope.set_topic(item.clone());
7802 let val = self.run_block_region(start, end, op_count)?;
7803 let keep = if let Some(re) = val.as_regex() {
7805 re.is_match(&item.to_string())
7806 } else {
7807 val.is_true()
7808 };
7809 if keep {
7810 result.push(item);
7811 }
7812 }
7813 self.interp.scope.restore_topic_chain(saved_chain);
7814 self.push(StrykeValue::array(result));
7815 Ok(())
7816 } else {
7817 let block = self.blocks[idx].clone();
7818 let mut result = Vec::new();
7819 for item in list {
7820 self.interp.scope.set_topic(item.clone());
7821 match self.interp.exec_block(&block) {
7822 Ok(val) => {
7823 let keep = if let Some(re) = val.as_regex() {
7824 re.is_match(&item.to_string())
7825 } else {
7826 val.is_true()
7827 };
7828 if keep {
7829 result.push(item);
7830 }
7831 }
7832 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7833 self.interp.scope.restore_topic_chain(saved_chain);
7834 return Err(e);
7835 }
7836 Err(_) => {}
7837 }
7838 }
7839 self.interp.scope.restore_topic_chain(saved_chain);
7840 self.push(StrykeValue::array(result));
7841 Ok(())
7842 }
7843 }
7844 Op::ForEachWithBlock(block_idx) => {
7845 let val = self.pop();
7846 let idx = *block_idx as usize;
7847 let saved_chain = self.interp.scope.save_topic_chain();
7851 if val.is_iterator() {
7853 let iter = val.into_iterator();
7854 let mut count = 0i64;
7855 if let Some(&(start, end)) =
7856 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7857 {
7858 while let Some(item) = iter.next_item() {
7859 count += 1;
7860 self.interp.scope.set_topic(item);
7861 if let Err(e) = self.run_block_region(start, end, op_count) {
7862 self.interp.scope.restore_topic_chain(saved_chain);
7863 return Err(e);
7864 }
7865 }
7866 } else {
7867 let block = self.blocks[idx].clone();
7868 while let Some(item) = iter.next_item() {
7869 count += 1;
7870 self.interp.scope.set_topic(item);
7871 match self.interp.exec_block(&block) {
7872 Ok(_) => {}
7873 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7874 self.interp.scope.restore_topic_chain(saved_chain);
7875 return Err(e);
7876 }
7877 Err(_) => {}
7878 }
7879 }
7880 }
7881 self.interp.scope.restore_topic_chain(saved_chain);
7882 self.push(StrykeValue::integer(count));
7883 return Ok(());
7884 }
7885 let list = val.to_list();
7886 let count = list.len() as i64;
7887 if let Some(&(start, end)) =
7888 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7889 {
7890 for item in list {
7891 self.interp.scope.set_topic(item);
7892 if let Err(e) = self.run_block_region(start, end, op_count) {
7893 self.interp.scope.restore_topic_chain(saved_chain);
7894 return Err(e);
7895 }
7896 }
7897 } else {
7898 let block = self.blocks[idx].clone();
7899 for item in list {
7900 self.interp.scope.set_topic(item);
7901 match self.interp.exec_block(&block) {
7902 Ok(_) => {}
7903 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7904 self.interp.scope.restore_topic_chain(saved_chain);
7905 return Err(e);
7906 }
7907 Err(_) => {}
7908 }
7909 }
7910 }
7911 self.interp.scope.restore_topic_chain(saved_chain);
7912 self.push(StrykeValue::integer(count));
7913 Ok(())
7914 }
7915 Op::GrepWithExpr(expr_idx) => {
7916 let list = self.pop().to_list();
7917 let idx = *expr_idx as usize;
7918 let dispatch_coderef = !crate::compat_mode();
7919 if let Some(&(start, end)) = self
7923 .grep_expr_bytecode_ranges
7924 .get(idx)
7925 .and_then(|r| r.as_ref())
7926 {
7927 let mut result = Vec::new();
7928 for item in list {
7929 self.interp.scope.set_topic_local(item.clone());
7930 let val = self.run_block_region(start, end, op_count)?;
7931 let val = self.maybe_call_coderef_with_item(
7932 val,
7933 &item,
7934 dispatch_coderef,
7935 )?;
7936 let keep = if let Some(re) = val.as_regex() {
7937 re.is_match(&item.to_string())
7938 } else {
7939 val.is_true()
7940 };
7941 if keep {
7942 result.push(item);
7943 }
7944 }
7945 self.push(StrykeValue::array(result));
7946 Ok(())
7947 } else {
7948 let e = self.grep_expr_entries[idx].clone();
7949 let mut result = Vec::new();
7950 for item in list {
7951 self.interp.scope.set_topic_local(item.clone());
7952 let val = vm_interp_result(self.interp.eval_expr(&e), self.line())?;
7953 let val = self.maybe_call_coderef_with_item(
7954 val,
7955 &item,
7956 dispatch_coderef,
7957 )?;
7958 let keep = if let Some(re) = val.as_regex() {
7959 re.is_match(&item.to_string())
7960 } else {
7961 val.is_true()
7962 };
7963 if keep {
7964 result.push(item);
7965 }
7966 }
7967 self.push(StrykeValue::array(result));
7968 Ok(())
7969 }
7970 }
7971 Op::SortWithBlock(block_idx) => {
7972 let mut items = self.pop().to_list();
7973 let idx = *block_idx as usize;
7974 let saved_topic = self.interp.scope.save_topic_chain();
7977 if let Some(&(start, end)) =
7978 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7979 {
7980 let mut sort_err: Option<StrykeError> = None;
7981 items.sort_by(|a, b| {
7982 if sort_err.is_some() {
7983 return std::cmp::Ordering::Equal;
7984 }
7985 self.interp.scope.set_sort_pair(a.clone(), b.clone());
7986 match self.run_block_region(start, end, op_count) {
7987 Ok(v) => {
7988 let n = v.to_int();
7989 if n < 0 {
7990 std::cmp::Ordering::Less
7991 } else if n > 0 {
7992 std::cmp::Ordering::Greater
7993 } else {
7994 std::cmp::Ordering::Equal
7995 }
7996 }
7997 Err(e) => {
7998 sort_err = Some(e);
7999 std::cmp::Ordering::Equal
8000 }
8001 }
8002 });
8003 self.interp.scope.restore_topic_chain(saved_topic);
8004 if let Some(e) = sort_err {
8005 return Err(e);
8006 }
8007 self.push(StrykeValue::array(items));
8008 Ok(())
8009 } else {
8010 let block = self.blocks[idx].clone();
8011 items.sort_by(|a, b| {
8012 self.interp.scope.set_sort_pair(a.clone(), b.clone());
8013 match self.interp.exec_block(&block) {
8014 Ok(v) => {
8015 let n = v.to_int();
8016 if n < 0 {
8017 std::cmp::Ordering::Less
8018 } else if n > 0 {
8019 std::cmp::Ordering::Greater
8020 } else {
8021 std::cmp::Ordering::Equal
8022 }
8023 }
8024 Err(_) => std::cmp::Ordering::Equal,
8025 }
8026 });
8027 self.interp.scope.restore_topic_chain(saved_topic);
8028 self.push(StrykeValue::array(items));
8029 Ok(())
8030 }
8031 }
8032 Op::SortWithBlockFast(tag) => {
8033 let mut items = self.pop().to_list();
8034 let mode = match *tag {
8035 0 => SortBlockFast::Numeric,
8036 1 => SortBlockFast::String,
8037 2 => SortBlockFast::NumericRev,
8038 3 => SortBlockFast::StringRev,
8039 _ => SortBlockFast::Numeric,
8040 };
8041 items.sort_by(|a, b| sort_magic_cmp(a, b, mode));
8042 self.push(StrykeValue::array(items));
8043 Ok(())
8044 }
8045 Op::SortNoBlock => {
8046 let mut items = self.pop().to_list();
8047 items.sort_by_key(|a| a.to_string());
8048 self.push(StrykeValue::array(items));
8049 Ok(())
8050 }
8051 Op::SortWithCodeComparator(wa) => {
8052 let want = WantarrayCtx::from_byte(*wa);
8053 let cmp_val = self.pop();
8054 let mut items = self.pop().to_list();
8055 let line = self.line();
8056 let Some(sub) = cmp_val.as_code_ref() else {
8057 return Err(StrykeError::runtime(
8058 "sort: comparator must be a code reference",
8059 line,
8060 ));
8061 };
8062 let interp = &mut self.interp;
8063 items.sort_by(|a, b| {
8064 interp.scope.set_sort_pair(a.clone(), b.clone());
8067 match interp.call_sub(
8068 sub.as_ref(),
8069 vec![a.clone(), b.clone()],
8070 want,
8071 line,
8072 ) {
8073 Ok(v) => {
8074 let n = v.to_int();
8075 if n < 0 {
8076 std::cmp::Ordering::Less
8077 } else if n > 0 {
8078 std::cmp::Ordering::Greater
8079 } else {
8080 std::cmp::Ordering::Equal
8081 }
8082 }
8083 Err(_) => std::cmp::Ordering::Equal,
8084 }
8085 });
8086 self.push(StrykeValue::array(items));
8087 Ok(())
8088 }
8089 Op::ReverseListOp => {
8090 let val = self.pop();
8091 if val.is_iterator() {
8092 self.push(StrykeValue::iterator(std::sync::Arc::new(
8093 crate::value::RevIterator::new(val.into_iterator()),
8094 )));
8095 } else {
8096 let mut items = val.to_list();
8097 items.reverse();
8098 self.push(StrykeValue::array(items));
8099 }
8100 Ok(())
8101 }
8102 Op::ReverseScalarOp => {
8103 let val = self.pop();
8104 let items = val.to_list();
8105 let s: String = items.iter().map(|v| v.to_string()).collect();
8106 self.push(StrykeValue::string(s.chars().rev().collect()));
8107 Ok(())
8108 }
8109 Op::RevListOp => {
8110 let val = self.pop();
8111 if val.is_iterator() {
8112 let mut items = val.to_list();
8115 items.reverse();
8116 self.push(StrykeValue::array(items));
8117 } else if let Some(s) = crate::value::set_payload(&val) {
8118 let mut out = crate::value::PerlSet::new();
8119 for (k, v) in s.iter().rev() {
8120 out.insert(k.clone(), v.clone());
8121 }
8122 self.push(StrykeValue::set(std::sync::Arc::new(out)));
8123 } else if let Some(ar) = val.as_array_ref() {
8124 let items: Vec<_> = ar.read().iter().rev().cloned().collect();
8125 self.push(StrykeValue::array_ref(std::sync::Arc::new(
8126 parking_lot::RwLock::new(items),
8127 )));
8128 } else if let Some(hr) = val.as_hash_ref() {
8129 let mut out: indexmap::IndexMap<String, StrykeValue> =
8130 indexmap::IndexMap::new();
8131 for (k, v) in hr.read().iter() {
8132 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8133 }
8134 self.push(StrykeValue::hash_ref(std::sync::Arc::new(
8135 parking_lot::RwLock::new(out),
8136 )));
8137 } else if let Some(hm) = val.as_hash_map() {
8138 let mut out: indexmap::IndexMap<String, StrykeValue> =
8139 indexmap::IndexMap::new();
8140 for (k, v) in hm.iter() {
8141 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8142 }
8143 self.push(StrykeValue::hash(out));
8144 } else if val.as_array_vec().is_some() {
8145 let mut items = val.to_list();
8146 items.reverse();
8147 self.push(StrykeValue::array(items));
8148 } else {
8149 let s = val.to_string();
8150 self.push(StrykeValue::string(s.chars().rev().collect()));
8151 }
8152 Ok(())
8153 }
8154 Op::RevScalarOp => {
8155 let val = self.pop();
8156 if let Some(s) = crate::value::set_payload(&val) {
8157 let mut out = crate::value::PerlSet::new();
8158 for (k, v) in s.iter().rev() {
8159 out.insert(k.clone(), v.clone());
8160 }
8161 self.push(StrykeValue::set(std::sync::Arc::new(out)));
8162 } else if let Some(ar) = val.as_array_ref() {
8163 let items: Vec<_> = ar.read().iter().rev().cloned().collect();
8164 self.push(StrykeValue::array_ref(std::sync::Arc::new(
8165 parking_lot::RwLock::new(items),
8166 )));
8167 } else if let Some(hr) = val.as_hash_ref() {
8168 let mut out: indexmap::IndexMap<String, StrykeValue> =
8169 indexmap::IndexMap::new();
8170 for (k, v) in hr.read().iter() {
8171 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8172 }
8173 self.push(StrykeValue::hash_ref(std::sync::Arc::new(
8174 parking_lot::RwLock::new(out),
8175 )));
8176 } else {
8177 let items = val.to_list();
8178 let s: String = items.iter().map(|v| v.to_string()).collect();
8179 self.push(StrykeValue::string(s.chars().rev().collect()));
8180 }
8181 Ok(())
8182 }
8183 Op::StackArrayLen => {
8184 let v = self.pop();
8185 self.push(StrykeValue::integer(v.to_list().len() as i64));
8186 Ok(())
8187 }
8188 Op::ListSliceToScalar => {
8189 let v = self.pop();
8190 let items = v.to_list();
8191 self.push(items.last().cloned().unwrap_or(StrykeValue::UNDEF));
8192 Ok(())
8193 }
8194
8195 Op::EvalBlock(block_idx, want) => {
8197 let block = self.blocks[*block_idx as usize].clone();
8198 let tail = crate::vm_helper::WantarrayCtx::from_byte(*want);
8199 self.interp.eval_nesting += 1;
8200 match self.interp.exec_block_with_tail(&block, tail) {
8203 Ok(v) => {
8204 self.interp.clear_eval_error();
8205 self.push(v);
8206 }
8207 Err(crate::vm_helper::FlowOrError::Error(e)) => {
8208 self.interp.set_eval_error_from_perl_error(&e);
8209 self.push(StrykeValue::UNDEF);
8210 }
8211 Err(_) => self.push(StrykeValue::UNDEF),
8212 }
8213 self.interp.eval_nesting -= 1;
8214 Ok(())
8215 }
8216 Op::TraceBlock(block_idx) => {
8217 let block = self.blocks[*block_idx as usize].clone();
8218 crate::parallel_trace::trace_enter();
8219 self.interp.eval_nesting += 1;
8220 match self.interp.exec_block(&block) {
8221 Ok(v) => {
8222 self.interp.clear_eval_error();
8223 self.push(v);
8224 }
8225 Err(FlowOrError::Error(e)) => {
8226 self.interp.set_eval_error_from_perl_error(&e);
8227 self.push(StrykeValue::UNDEF);
8228 }
8229 Err(_) => self.push(StrykeValue::UNDEF),
8230 }
8231 self.interp.eval_nesting -= 1;
8232 crate::parallel_trace::trace_leave();
8233 Ok(())
8234 }
8235 Op::TimerBlock(block_idx) => {
8236 let block = self.blocks[*block_idx as usize].clone();
8237 let start = std::time::Instant::now();
8238 self.interp.eval_nesting += 1;
8239 let _ = match self.interp.exec_block(&block) {
8240 Ok(v) => {
8241 self.interp.clear_eval_error();
8242 v
8243 }
8244 Err(FlowOrError::Error(e)) => {
8245 self.interp.set_eval_error_from_perl_error(&e);
8246 StrykeValue::UNDEF
8247 }
8248 Err(_) => StrykeValue::UNDEF,
8249 };
8250 self.interp.eval_nesting -= 1;
8251 let ms = start.elapsed().as_secs_f64() * 1000.0;
8252 self.push(StrykeValue::float(ms));
8253 Ok(())
8254 }
8255 Op::BenchBlock(block_idx) => {
8256 let n_i = self.pop().to_int();
8257 if n_i < 0 {
8258 return Err(StrykeError::runtime(
8259 "bench: iteration count must be non-negative",
8260 self.line(),
8261 ));
8262 }
8263 let n = n_i as usize;
8264 let block = self.blocks[*block_idx as usize].clone();
8265 let v = vm_interp_result(
8266 self.interp.run_bench_block(&block, n, self.line()),
8267 self.line(),
8268 )?;
8269 self.push(v);
8270 Ok(())
8271 }
8272 Op::Given(idx) => {
8273 let i = *idx as usize;
8274 let line = self.line();
8275 let v = if let Some(&(start, end)) = self
8276 .given_topic_bytecode_ranges
8277 .get(i)
8278 .and_then(|r| r.as_ref())
8279 {
8280 let topic_val = self.run_block_region(start, end, op_count)?;
8281 let body = &self.given_entries[i].1;
8282 vm_interp_result(
8283 self.interp.exec_given_with_topic_value(topic_val, body),
8284 line,
8285 )?
8286 } else {
8287 let (topic, body) = &self.given_entries[i];
8288 vm_interp_result(self.interp.exec_given(topic, body), line)?
8289 };
8290 self.push(v);
8291 Ok(())
8292 }
8293 Op::EvalTimeout(idx) => {
8294 let i = *idx as usize;
8295 let body = self.eval_timeout_entries[i].1.clone();
8296 let secs = if let Some(&(start, end)) = self
8297 .eval_timeout_expr_bytecode_ranges
8298 .get(i)
8299 .and_then(|r| r.as_ref())
8300 {
8301 self.run_block_region(start, end, op_count)?.to_number()
8302 } else {
8303 let timeout_expr = &self.eval_timeout_entries[i].0;
8304 vm_interp_result(self.interp.eval_expr(timeout_expr), self.line())?
8305 .to_number()
8306 };
8307 let v = vm_interp_result(
8308 self.interp.eval_timeout_block(&body, secs, self.line()),
8309 self.line(),
8310 )?;
8311 self.push(v);
8312 Ok(())
8313 }
8314 Op::AlgebraicMatch(idx) => {
8315 let i = *idx as usize;
8316 let line = self.line();
8317 let v = if let Some(&(start, end)) = self
8318 .algebraic_match_subject_bytecode_ranges
8319 .get(i)
8320 .and_then(|r| r.as_ref())
8321 {
8322 let subject_val = self.run_block_region(start, end, op_count)?;
8323 let arms = &self.algebraic_match_entries[i].1;
8324 vm_interp_result(
8325 self.interp.eval_algebraic_match_with_subject_value(
8326 subject_val,
8327 arms,
8328 line,
8329 ),
8330 self.line(),
8331 )?
8332 } else {
8333 let (subject, arms) = &self.algebraic_match_entries[i];
8334 vm_interp_result(
8335 self.interp.eval_algebraic_match(subject, arms, line),
8336 self.line(),
8337 )?
8338 };
8339 self.push(v);
8340 Ok(())
8341 }
8342 Op::ParLines(idx) => {
8343 let (path, callback, progress) = &self.par_lines_entries[*idx as usize];
8344 let v = vm_interp_result(
8345 self.interp.eval_par_lines_expr(
8346 path,
8347 callback,
8348 progress.as_ref(),
8349 self.line(),
8350 ),
8351 self.line(),
8352 )?;
8353 self.push(v);
8354 Ok(())
8355 }
8356 Op::ParWalk(idx) => {
8357 let (path, callback, progress) = &self.par_walk_entries[*idx as usize];
8358 let v = vm_interp_result(
8359 self.interp.eval_par_walk_expr(
8360 path,
8361 callback,
8362 progress.as_ref(),
8363 self.line(),
8364 ),
8365 self.line(),
8366 )?;
8367 self.push(v);
8368 Ok(())
8369 }
8370 Op::Pwatch(idx) => {
8371 let (path, callback) = &self.pwatch_entries[*idx as usize];
8372 let v = vm_interp_result(
8373 self.interp.eval_pwatch_expr(path, callback, self.line()),
8374 self.line(),
8375 )?;
8376 self.push(v);
8377 Ok(())
8378 }
8379
8380 Op::PMapWithBlock(block_idx) => {
8382 let list = self.pop().to_list();
8383 let progress_flag = self.pop().is_true();
8384 let idx = *block_idx as usize;
8385 let subs = self.interp.subs.clone();
8386 let (scope_capture, atomic_arrays, atomic_hashes) =
8387 self.interp.scope.capture_with_atomics();
8388 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8389 let n_workers = rayon::current_num_threads();
8390 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
8391 .map(|_| {
8392 let mut interp = VMHelper::new();
8393 interp.subs = subs.clone();
8394 interp.scope.restore_capture(&scope_capture);
8395 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
8396 interp.enable_parallel_guard();
8397 Mutex::new(interp)
8398 })
8399 .collect();
8400 if let Some(&(start, end)) =
8401 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8402 {
8403 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8404 let results: Vec<StrykeValue> = list
8405 .into_par_iter()
8406 .map(|item| {
8407 let tid =
8408 rayon::current_thread_index().unwrap_or(0) % pool.len();
8409 let mut local_interp = pool[tid].lock();
8410 local_interp.scope.set_topic(item);
8411 let mut vm = shared.worker_vm(&mut local_interp);
8412 let mut op_count = 0u64;
8413 let val = match vm.run_block_region(start, end, &mut op_count) {
8414 Ok(v) => v,
8415 Err(_) => StrykeValue::UNDEF,
8416 };
8417 pmap_progress.tick();
8418 val
8419 })
8420 .collect();
8421 pmap_progress.finish();
8422 self.push(StrykeValue::array(results));
8423 Ok(())
8424 } else {
8425 let block = self.blocks[idx].clone();
8426 let results: Vec<StrykeValue> = list
8427 .into_par_iter()
8428 .map(|item| {
8429 let tid =
8430 rayon::current_thread_index().unwrap_or(0) % pool.len();
8431 let mut local_interp = pool[tid].lock();
8432 local_interp.scope.set_topic(item);
8433 local_interp.scope_push_hook();
8434 let val = match local_interp.exec_block_no_scope(&block) {
8435 Ok(val) => val,
8436 Err(_) => StrykeValue::UNDEF,
8437 };
8438 local_interp.scope_pop_hook();
8439 pmap_progress.tick();
8440 val
8441 })
8442 .collect();
8443 pmap_progress.finish();
8444 self.push(StrykeValue::array(results));
8445 Ok(())
8446 }
8447 }
8448 Op::PFlatMapWithBlock(block_idx) => {
8449 let list = self.pop().to_list();
8450 let progress_flag = self.pop().is_true();
8451 let idx = *block_idx as usize;
8452 let subs = self.interp.subs.clone();
8453 let (scope_capture, atomic_arrays, atomic_hashes) =
8454 self.interp.scope.capture_with_atomics();
8455 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8456 let n_workers = rayon::current_num_threads();
8457 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
8458 .map(|_| {
8459 let mut interp = VMHelper::new();
8460 interp.subs = subs.clone();
8461 interp.scope.restore_capture(&scope_capture);
8462 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
8463 interp.enable_parallel_guard();
8464 Mutex::new(interp)
8465 })
8466 .collect();
8467 if let Some(&(start, end)) =
8468 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8469 {
8470 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8471 let mut indexed: Vec<(usize, Vec<StrykeValue>)> = list
8472 .into_par_iter()
8473 .enumerate()
8474 .map(|(i, item)| {
8475 let tid =
8476 rayon::current_thread_index().unwrap_or(0) % pool.len();
8477 let mut local_interp = pool[tid].lock();
8478 local_interp.scope.set_topic(item);
8479 let mut vm = shared.worker_vm(&mut local_interp);
8480 let mut op_count = 0u64;
8481 let val = match vm.run_block_region(start, end, &mut op_count) {
8482 Ok(v) => v,
8483 Err(_) => StrykeValue::UNDEF,
8484 };
8485 let out = val.map_flatten_outputs(true);
8486 pmap_progress.tick();
8487 (i, out)
8488 })
8489 .collect();
8490 pmap_progress.finish();
8491 indexed.sort_by_key(|(i, _)| *i);
8492 let results: Vec<StrykeValue> =
8493 indexed.into_iter().flat_map(|(_, v)| v).collect();
8494 self.push(StrykeValue::array(results));
8495 Ok(())
8496 } else {
8497 let block = self.blocks[idx].clone();
8498 let mut indexed: Vec<(usize, Vec<StrykeValue>)> = list
8499 .into_par_iter()
8500 .enumerate()
8501 .map(|(i, item)| {
8502 let tid =
8503 rayon::current_thread_index().unwrap_or(0) % pool.len();
8504 let mut local_interp = pool[tid].lock();
8505 local_interp.scope.set_topic(item);
8506 local_interp.scope_push_hook();
8507 let val = match local_interp.exec_block_no_scope(&block) {
8508 Ok(val) => val,
8509 Err(_) => StrykeValue::UNDEF,
8510 };
8511 local_interp.scope_pop_hook();
8512 let out = val.map_flatten_outputs(true);
8513 pmap_progress.tick();
8514 (i, out)
8515 })
8516 .collect();
8517 pmap_progress.finish();
8518 indexed.sort_by_key(|(i, _)| *i);
8519 let results: Vec<StrykeValue> =
8520 indexed.into_iter().flat_map(|(_, v)| v).collect();
8521 self.push(StrykeValue::array(results));
8522 Ok(())
8523 }
8524 }
8525 Op::PMapRemote { block_idx, flat } => {
8526 let cluster = self.pop();
8527 let list_pv = self.pop();
8528 let progress_flag = self.pop().is_true();
8529 let idx = *block_idx as usize;
8530 let block = self.blocks[idx].clone();
8531 let flat_outputs = *flat != 0;
8532 let v = vm_interp_result(
8533 self.interp.eval_pmap_remote(
8534 cluster,
8535 list_pv,
8536 progress_flag,
8537 &block,
8538 flat_outputs,
8539 self.line(),
8540 ),
8541 self.line(),
8542 )?;
8543 self.push(v);
8544 Ok(())
8545 }
8546 Op::Puniq => {
8547 let list = self.pop().to_list();
8548 let progress_flag = self.pop().is_true();
8549 let n_threads = self.interp.parallel_thread_count();
8550 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8551 let out = crate::par_list::puniq_run(list, n_threads, &pmap_progress);
8552 pmap_progress.finish();
8553 self.push(StrykeValue::array(out));
8554 Ok(())
8555 }
8556 Op::PFirstWithBlock(block_idx) => {
8557 let list = self.pop().to_list();
8558 let progress_flag = self.pop().is_true();
8559 let idx = *block_idx as usize;
8560 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8561 let subs = self.interp.subs.clone();
8562 let (scope_capture, atomic_arrays, atomic_hashes) =
8563 self.interp.scope.capture_with_atomics();
8564 let out = if let Some(&(start, end)) =
8565 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8566 {
8567 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8568 crate::par_list::pfirst_run(list, &pmap_progress, |item| {
8569 let mut local_interp = VMHelper::new();
8570 local_interp.subs = subs.clone();
8571 local_interp.scope.restore_capture(&scope_capture);
8572 local_interp
8573 .scope
8574 .restore_atomics(&atomic_arrays, &atomic_hashes);
8575 local_interp.enable_parallel_guard();
8576 local_interp.scope.set_topic(item);
8577 let mut vm = shared.worker_vm(&mut local_interp);
8578 let mut op_count = 0u64;
8579 match vm.run_block_region(start, end, &mut op_count) {
8580 Ok(v) => v.is_true(),
8581 Err(_) => false,
8582 }
8583 })
8584 } else {
8585 let block = self.blocks[idx].clone();
8586 crate::par_list::pfirst_run(list, &pmap_progress, |item| {
8587 let mut local_interp = VMHelper::new();
8588 local_interp.subs = subs.clone();
8589 local_interp.scope.restore_capture(&scope_capture);
8590 local_interp
8591 .scope
8592 .restore_atomics(&atomic_arrays, &atomic_hashes);
8593 local_interp.enable_parallel_guard();
8594 local_interp.scope.set_topic(item);
8595 local_interp.scope_push_hook();
8596 let ok = match local_interp.exec_block_no_scope(&block) {
8597 Ok(v) => v.is_true(),
8598 Err(_) => false,
8599 };
8600 local_interp.scope_pop_hook();
8601 ok
8602 })
8603 };
8604 pmap_progress.finish();
8605 self.push(out.unwrap_or(StrykeValue::UNDEF));
8606 Ok(())
8607 }
8608 Op::PAnyWithBlock(block_idx) => {
8609 let list = self.pop().to_list();
8610 let progress_flag = self.pop().is_true();
8611 let idx = *block_idx as usize;
8612 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8613 let subs = self.interp.subs.clone();
8614 let (scope_capture, atomic_arrays, atomic_hashes) =
8615 self.interp.scope.capture_with_atomics();
8616 let b = if let Some(&(start, end)) =
8617 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8618 {
8619 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8620 crate::par_list::pany_run(list, &pmap_progress, |item| {
8621 let mut local_interp = VMHelper::new();
8622 local_interp.subs = subs.clone();
8623 local_interp.scope.restore_capture(&scope_capture);
8624 local_interp
8625 .scope
8626 .restore_atomics(&atomic_arrays, &atomic_hashes);
8627 local_interp.enable_parallel_guard();
8628 local_interp.scope.set_topic(item);
8629 let mut vm = shared.worker_vm(&mut local_interp);
8630 let mut op_count = 0u64;
8631 match vm.run_block_region(start, end, &mut op_count) {
8632 Ok(v) => v.is_true(),
8633 Err(_) => false,
8634 }
8635 })
8636 } else {
8637 let block = self.blocks[idx].clone();
8638 crate::par_list::pany_run(list, &pmap_progress, |item| {
8639 let mut local_interp = VMHelper::new();
8640 local_interp.subs = subs.clone();
8641 local_interp.scope.restore_capture(&scope_capture);
8642 local_interp
8643 .scope
8644 .restore_atomics(&atomic_arrays, &atomic_hashes);
8645 local_interp.enable_parallel_guard();
8646 local_interp.scope.set_topic(item);
8647 local_interp.scope_push_hook();
8648 let ok = match local_interp.exec_block_no_scope(&block) {
8649 Ok(v) => v.is_true(),
8650 Err(_) => false,
8651 };
8652 local_interp.scope_pop_hook();
8653 ok
8654 })
8655 };
8656 pmap_progress.finish();
8657 self.push(StrykeValue::integer(if b { 1 } else { 0 }));
8658 Ok(())
8659 }
8660 Op::PMapChunkedWithBlock(block_idx) => {
8661 let list = self.pop().to_list();
8662 let chunk_n = self.pop().to_int().max(1) as usize;
8663 let progress_flag = self.pop().is_true();
8664 let idx = *block_idx as usize;
8665 let subs = self.interp.subs.clone();
8666 let (scope_capture, atomic_arrays, atomic_hashes) =
8667 self.interp.scope.capture_with_atomics();
8668 let indexed_chunks: Vec<(usize, Vec<StrykeValue>)> = list
8669 .chunks(chunk_n)
8670 .enumerate()
8671 .map(|(i, c)| (i, c.to_vec()))
8672 .collect();
8673 let n_chunks = indexed_chunks.len();
8674 let pmap_progress = PmapProgress::new(progress_flag, n_chunks);
8675 if let Some(&(start, end)) =
8676 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8677 {
8678 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8679 let mut chunk_results: Vec<(usize, Vec<StrykeValue>)> = indexed_chunks
8680 .into_par_iter()
8681 .map(|(chunk_idx, chunk)| {
8682 let mut local_interp = VMHelper::new();
8683 local_interp.subs = subs.clone();
8684 local_interp.scope.restore_capture(&scope_capture);
8685 local_interp
8686 .scope
8687 .restore_atomics(&atomic_arrays, &atomic_hashes);
8688 local_interp.enable_parallel_guard();
8689 let mut out = Vec::with_capacity(chunk.len());
8690 for item in chunk {
8691 local_interp.scope.set_topic(item);
8692 let mut vm = shared.worker_vm(&mut local_interp);
8693 let mut op_count = 0u64;
8694 let val =
8695 match vm.run_block_region(start, end, &mut op_count) {
8696 Ok(v) => v,
8697 Err(_) => StrykeValue::UNDEF,
8698 };
8699 out.push(val);
8700 }
8701 pmap_progress.tick();
8702 (chunk_idx, out)
8703 })
8704 .collect();
8705 pmap_progress.finish();
8706 chunk_results.sort_by_key(|(i, _)| *i);
8707 let results: Vec<StrykeValue> =
8708 chunk_results.into_iter().flat_map(|(_, v)| v).collect();
8709 self.push(StrykeValue::array(results));
8710 Ok(())
8711 } else {
8712 let block = self.blocks[idx].clone();
8713 let mut chunk_results: Vec<(usize, Vec<StrykeValue>)> = indexed_chunks
8714 .into_par_iter()
8715 .map(|(chunk_idx, chunk)| {
8716 let mut local_interp = VMHelper::new();
8717 local_interp.subs = subs.clone();
8718 local_interp.scope.restore_capture(&scope_capture);
8719 local_interp
8720 .scope
8721 .restore_atomics(&atomic_arrays, &atomic_hashes);
8722 local_interp.enable_parallel_guard();
8723 let mut out = Vec::with_capacity(chunk.len());
8724 for item in chunk {
8725 local_interp.scope.set_topic(item);
8726 local_interp.scope_push_hook();
8727 let val = match local_interp.exec_block_no_scope(&block) {
8728 Ok(val) => val,
8729 Err(_) => StrykeValue::UNDEF,
8730 };
8731 local_interp.scope_pop_hook();
8732 out.push(val);
8733 }
8734 pmap_progress.tick();
8735 (chunk_idx, out)
8736 })
8737 .collect();
8738 pmap_progress.finish();
8739 chunk_results.sort_by_key(|(i, _)| *i);
8740 let results: Vec<StrykeValue> =
8741 chunk_results.into_iter().flat_map(|(_, v)| v).collect();
8742 self.push(StrykeValue::array(results));
8743 Ok(())
8744 }
8745 }
8746 Op::ReduceWithBlock(block_idx) => {
8747 let list = self.pop().to_list();
8748 let idx = *block_idx as usize;
8749 let subs = self.interp.subs.clone();
8750 let scope_capture = self.interp.scope.capture();
8751 if list.is_empty() {
8752 self.push(StrykeValue::UNDEF);
8753 return Ok(());
8754 }
8755 if list.len() == 1 {
8756 self.push(list.into_iter().next().unwrap());
8757 return Ok(());
8758 }
8759 let mut items = list;
8760 let mut acc = items.remove(0);
8761 let rest = items;
8762 if let Some(&(start, end)) =
8763 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8764 {
8765 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8766 for b in rest {
8767 let mut local_interp = VMHelper::new();
8768 local_interp.subs = subs.clone();
8769 local_interp.scope.restore_capture(&scope_capture);
8770 local_interp.scope.set_sort_pair(acc.clone(), b.clone());
8771 let mut vm = shared.worker_vm(&mut local_interp);
8772 let mut op_count = 0u64;
8773 acc = match vm.run_block_region(start, end, &mut op_count) {
8774 Ok(v) => v,
8775 Err(_) => StrykeValue::UNDEF,
8776 };
8777 }
8778 } else {
8779 let block = self.blocks[idx].clone();
8780 for b in rest {
8781 let mut local_interp = VMHelper::new();
8782 local_interp.subs = subs.clone();
8783 local_interp.scope.restore_capture(&scope_capture);
8784 local_interp.scope.set_sort_pair(acc.clone(), b.clone());
8785 acc = match local_interp.exec_block(&block) {
8786 Ok(val) => val,
8787 Err(_) => StrykeValue::UNDEF,
8788 };
8789 }
8790 }
8791 self.push(acc);
8792 Ok(())
8793 }
8794 Op::PReduceWithBlock(block_idx) => {
8795 let list = self.pop().to_list();
8796 let progress_flag = self.pop().is_true();
8797 let idx = *block_idx as usize;
8798 let subs = self.interp.subs.clone();
8799 let scope_capture = self.interp.scope.capture();
8800 if list.is_empty() {
8801 self.push(StrykeValue::UNDEF);
8802 return Ok(());
8803 }
8804 if list.len() == 1 {
8805 self.push(list.into_iter().next().unwrap());
8806 return Ok(());
8807 }
8808 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8809 if let Some(&(start, end)) =
8810 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8811 {
8812 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8813 let result = list
8814 .into_par_iter()
8815 .map(|x| {
8816 pmap_progress.tick();
8817 x
8818 })
8819 .reduce_with(|a, b| {
8820 let mut local_interp = VMHelper::new();
8821 local_interp.subs = subs.clone();
8822 local_interp.scope.restore_capture(&scope_capture);
8823 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8824 let mut vm = shared.worker_vm(&mut local_interp);
8825 let mut op_count = 0u64;
8826 match vm.run_block_region(start, end, &mut op_count) {
8827 Ok(val) => val,
8828 Err(_) => StrykeValue::UNDEF,
8829 }
8830 });
8831 pmap_progress.finish();
8832 self.push(result.unwrap_or(StrykeValue::UNDEF));
8833 Ok(())
8834 } else {
8835 let block = self.blocks[idx].clone();
8836 let result = list
8837 .into_par_iter()
8838 .map(|x| {
8839 pmap_progress.tick();
8840 x
8841 })
8842 .reduce_with(|a, b| {
8843 let mut local_interp = VMHelper::new();
8844 local_interp.subs = subs.clone();
8845 local_interp.scope.restore_capture(&scope_capture);
8846 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8847 match local_interp.exec_block(&block) {
8848 Ok(val) => val,
8849 Err(_) => StrykeValue::UNDEF,
8850 }
8851 });
8852 pmap_progress.finish();
8853 self.push(result.unwrap_or(StrykeValue::UNDEF));
8854 Ok(())
8855 }
8856 }
8857 Op::PReduceInitWithBlock(block_idx) => {
8858 let init_val = self.pop();
8859 let list = self.pop().to_list();
8860 let progress_flag = self.pop().is_true();
8861 let idx = *block_idx as usize;
8862 let subs = self.interp.subs.clone();
8863 let scope_capture = self.interp.scope.capture();
8864 let cap: &[(String, StrykeValue)] = scope_capture.as_slice();
8865 let block = self.blocks[idx].clone();
8866 if list.is_empty() {
8867 self.push(init_val);
8868 return Ok(());
8869 }
8870 if list.len() == 1 {
8871 let v = fold_preduce_init_step(
8872 &subs,
8873 cap,
8874 &block,
8875 preduce_init_fold_identity(&init_val),
8876 list.into_iter().next().unwrap(),
8877 );
8878 self.push(v);
8879 return Ok(());
8880 }
8881 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8882 let result = list
8883 .into_par_iter()
8884 .fold(
8885 || preduce_init_fold_identity(&init_val),
8886 |acc, item| {
8887 pmap_progress.tick();
8888 fold_preduce_init_step(&subs, cap, &block, acc, item)
8889 },
8890 )
8891 .reduce(
8892 || preduce_init_fold_identity(&init_val),
8893 |a, b| merge_preduce_init_partials(a, b, &block, &subs, cap),
8894 );
8895 pmap_progress.finish();
8896 self.push(result);
8897 Ok(())
8898 }
8899 Op::PMapReduceWithBlocks(map_idx, reduce_idx) => {
8900 let list = self.pop().to_list();
8901 let progress_flag = self.pop().is_true();
8902 let map_i = *map_idx as usize;
8903 let reduce_i = *reduce_idx as usize;
8904 let subs = self.interp.subs.clone();
8905 let scope_capture = self.interp.scope.capture();
8906 if list.is_empty() {
8907 self.push(StrykeValue::UNDEF);
8908 return Ok(());
8909 }
8910 if list.len() == 1 {
8911 let mut local_interp = VMHelper::new();
8912 local_interp.subs = subs.clone();
8913 local_interp.scope.restore_capture(&scope_capture);
8914 local_interp
8915 .scope
8916 .set_topic(list.into_iter().next().unwrap());
8917 let map_block = self.blocks[map_i].clone();
8918 let v = match local_interp.exec_block_no_scope(&map_block) {
8919 Ok(v) => v,
8920 Err(_) => StrykeValue::UNDEF,
8921 };
8922 self.push(v);
8923 return Ok(());
8924 }
8925 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8926 let map_range = self
8927 .block_bytecode_ranges
8928 .get(map_i)
8929 .and_then(|r| r.as_ref())
8930 .copied();
8931 let reduce_range = self
8932 .block_bytecode_ranges
8933 .get(reduce_i)
8934 .and_then(|r| r.as_ref())
8935 .copied();
8936 if let (Some((map_start, map_end)), Some((reduce_start, reduce_end))) =
8937 (map_range, reduce_range)
8938 {
8939 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8940 let result = list
8941 .into_par_iter()
8942 .map(|item| {
8943 let mut local_interp = VMHelper::new();
8944 local_interp.subs = subs.clone();
8945 local_interp.scope.restore_capture(&scope_capture);
8946 local_interp.scope.set_topic(item);
8947 let mut vm = shared.worker_vm(&mut local_interp);
8948 let mut op_count = 0u64;
8949 let val = match vm.run_block_region(
8950 map_start,
8951 map_end,
8952 &mut op_count,
8953 ) {
8954 Ok(val) => val,
8955 Err(_) => StrykeValue::UNDEF,
8956 };
8957 pmap_progress.tick();
8958 val
8959 })
8960 .reduce_with(|a, b| {
8961 let mut local_interp = VMHelper::new();
8962 local_interp.subs = subs.clone();
8963 local_interp.scope.restore_capture(&scope_capture);
8964 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8965 let mut vm = shared.worker_vm(&mut local_interp);
8966 let mut op_count = 0u64;
8967 match vm.run_block_region(
8968 reduce_start,
8969 reduce_end,
8970 &mut op_count,
8971 ) {
8972 Ok(val) => val,
8973 Err(_) => StrykeValue::UNDEF,
8974 }
8975 });
8976 pmap_progress.finish();
8977 self.push(result.unwrap_or(StrykeValue::UNDEF));
8978 Ok(())
8979 } else {
8980 let map_block = self.blocks[map_i].clone();
8981 let reduce_block = self.blocks[reduce_i].clone();
8982 let result = list
8983 .into_par_iter()
8984 .map(|item| {
8985 let mut local_interp = VMHelper::new();
8986 local_interp.subs = subs.clone();
8987 local_interp.scope.restore_capture(&scope_capture);
8988 local_interp.scope.set_topic(item);
8989 let val = match local_interp.exec_block_no_scope(&map_block) {
8990 Ok(val) => val,
8991 Err(_) => StrykeValue::UNDEF,
8992 };
8993 pmap_progress.tick();
8994 val
8995 })
8996 .reduce_with(|a, b| {
8997 let mut local_interp = VMHelper::new();
8998 local_interp.subs = subs.clone();
8999 local_interp.scope.restore_capture(&scope_capture);
9000 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9001 match local_interp.exec_block_no_scope(&reduce_block) {
9002 Ok(val) => val,
9003 Err(_) => StrykeValue::UNDEF,
9004 }
9005 });
9006 pmap_progress.finish();
9007 self.push(result.unwrap_or(StrykeValue::UNDEF));
9008 Ok(())
9009 }
9010 }
9011 Op::PcacheWithBlock(block_idx) => {
9012 let list = self.pop().to_list();
9013 let progress_flag = self.pop().is_true();
9014 let idx = *block_idx as usize;
9015 let subs = self.interp.subs.clone();
9016 let scope_capture = self.interp.scope.capture();
9017 let block = self.blocks[idx].clone();
9018 let cache = &*crate::pcache::GLOBAL_PCACHE;
9019 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9020 if let Some(&(start, end)) =
9021 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9022 {
9023 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9024 let results: Vec<StrykeValue> = list
9025 .into_par_iter()
9026 .map(|item| {
9027 let k = crate::pcache::cache_key(&item);
9028 if let Some(v) = cache.get(&k) {
9029 pmap_progress.tick();
9030 return v.clone();
9031 }
9032 let mut local_interp = VMHelper::new();
9033 local_interp.subs = subs.clone();
9034 local_interp.scope.restore_capture(&scope_capture);
9035 local_interp.scope.set_topic(item.clone());
9036 let mut vm = shared.worker_vm(&mut local_interp);
9037 let mut op_count = 0u64;
9038 let val = match vm.run_block_region(start, end, &mut op_count) {
9039 Ok(v) => v,
9040 Err(_) => StrykeValue::UNDEF,
9041 };
9042 cache.insert(k, val.clone());
9043 pmap_progress.tick();
9044 val
9045 })
9046 .collect();
9047 pmap_progress.finish();
9048 self.push(StrykeValue::array(results));
9049 Ok(())
9050 } else {
9051 let results: Vec<StrykeValue> = list
9052 .into_par_iter()
9053 .map(|item| {
9054 let k = crate::pcache::cache_key(&item);
9055 if let Some(v) = cache.get(&k) {
9056 pmap_progress.tick();
9057 return v.clone();
9058 }
9059 let mut local_interp = VMHelper::new();
9060 local_interp.subs = subs.clone();
9061 local_interp.scope.restore_capture(&scope_capture);
9062 local_interp.scope.set_topic(item.clone());
9063 let val = match local_interp.exec_block_no_scope(&block) {
9064 Ok(v) => v,
9065 Err(_) => StrykeValue::UNDEF,
9066 };
9067 cache.insert(k, val.clone());
9068 pmap_progress.tick();
9069 val
9070 })
9071 .collect();
9072 pmap_progress.finish();
9073 self.push(StrykeValue::array(results));
9074 Ok(())
9075 }
9076 }
9077 Op::Pselect { n_rx, has_timeout } => {
9078 let timeout = if *has_timeout {
9079 let t = self.pop().to_number();
9080 Some(std::time::Duration::from_secs_f64(t.max(0.0)))
9081 } else {
9082 None
9083 };
9084 let mut rx_vals = Vec::with_capacity(*n_rx as usize);
9085 for _ in 0..*n_rx {
9086 rx_vals.push(self.pop());
9087 }
9088 rx_vals.reverse();
9089 let line = self.line();
9090 let v = crate::pchannel::pselect_recv_with_optional_timeout(
9091 &rx_vals, timeout, line,
9092 )?;
9093 self.push(v);
9094 Ok(())
9095 }
9096 Op::PGrepWithBlock(block_idx) => {
9097 let list = self.pop().to_list();
9098 let progress_flag = self.pop().is_true();
9099 let idx = *block_idx as usize;
9100 let subs = self.interp.subs.clone();
9101 let (scope_capture, atomic_arrays, atomic_hashes) =
9102 self.interp.scope.capture_with_atomics();
9103 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9104 let n_workers = rayon::current_num_threads();
9105 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
9106 .map(|_| {
9107 let mut interp = VMHelper::new();
9108 interp.subs = subs.clone();
9109 interp.scope.restore_capture(&scope_capture);
9110 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
9111 interp.enable_parallel_guard();
9112 Mutex::new(interp)
9113 })
9114 .collect();
9115 if let Some(&(start, end)) =
9116 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9117 {
9118 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9119 let results: Vec<StrykeValue> = list
9120 .into_par_iter()
9121 .filter_map(|item| {
9122 let tid =
9123 rayon::current_thread_index().unwrap_or(0) % pool.len();
9124 let mut local_interp = pool[tid].lock();
9125 local_interp.scope.set_topic(item.clone());
9126 let mut vm = shared.worker_vm(&mut local_interp);
9127 let mut op_count = 0u64;
9128 let keep = match vm.run_block_region(start, end, &mut op_count)
9129 {
9130 Ok(val) => val.is_true(),
9131 Err(_) => false,
9132 };
9133 pmap_progress.tick();
9134 if keep {
9135 Some(item)
9136 } else {
9137 None
9138 }
9139 })
9140 .collect();
9141 pmap_progress.finish();
9142 self.push(StrykeValue::array(results));
9143 Ok(())
9144 } else {
9145 let block = self.blocks[idx].clone();
9146 let results: Vec<StrykeValue> = list
9147 .into_par_iter()
9148 .filter_map(|item| {
9149 let tid =
9150 rayon::current_thread_index().unwrap_or(0) % pool.len();
9151 let mut local_interp = pool[tid].lock();
9152 local_interp.scope.set_topic(item.clone());
9153 local_interp.scope_push_hook();
9154 let keep = match local_interp.exec_block_no_scope(&block) {
9155 Ok(val) => val.is_true(),
9156 Err(_) => false,
9157 };
9158 local_interp.scope_pop_hook();
9159 pmap_progress.tick();
9160 if keep {
9161 Some(item)
9162 } else {
9163 None
9164 }
9165 })
9166 .collect();
9167 pmap_progress.finish();
9168 self.push(StrykeValue::array(results));
9169 Ok(())
9170 }
9171 }
9172 Op::PMapsWithBlock(block_idx) => {
9173 let val = self.pop();
9174 let block = self.blocks[*block_idx as usize].clone();
9175 let source = crate::map_stream::into_pull_iter(val);
9176 let sub = self.interp.anon_coderef_from_block(&block);
9177 let (capture, atomic_arrays, atomic_hashes) =
9178 self.interp.scope.capture_with_atomics();
9179 let out = StrykeValue::iterator(Arc::new(
9180 crate::map_stream::PMapStreamIterator::new(
9181 source,
9182 sub,
9183 self.interp.subs.clone(),
9184 capture,
9185 atomic_arrays,
9186 atomic_hashes,
9187 false,
9188 ),
9189 ));
9190 self.push(out);
9191 Ok(())
9192 }
9193 Op::PFlatMapsWithBlock(block_idx) => {
9194 let val = self.pop();
9195 let block = self.blocks[*block_idx as usize].clone();
9196 let source = crate::map_stream::into_pull_iter(val);
9197 let sub = self.interp.anon_coderef_from_block(&block);
9198 let (capture, atomic_arrays, atomic_hashes) =
9199 self.interp.scope.capture_with_atomics();
9200 let out = StrykeValue::iterator(Arc::new(
9201 crate::map_stream::PMapStreamIterator::new(
9202 source,
9203 sub,
9204 self.interp.subs.clone(),
9205 capture,
9206 atomic_arrays,
9207 atomic_hashes,
9208 true,
9209 ),
9210 ));
9211 self.push(out);
9212 Ok(())
9213 }
9214 Op::PGrepsWithBlock(block_idx) => {
9215 let val = self.pop();
9216 let block = self.blocks[*block_idx as usize].clone();
9217 let source = crate::map_stream::into_pull_iter(val);
9218 let sub = self.interp.anon_coderef_from_block(&block);
9219 let (capture, atomic_arrays, atomic_hashes) =
9220 self.interp.scope.capture_with_atomics();
9221 let out = StrykeValue::iterator(Arc::new(
9222 crate::map_stream::PGrepStreamIterator::new(
9223 source,
9224 sub,
9225 self.interp.subs.clone(),
9226 capture,
9227 atomic_arrays,
9228 atomic_hashes,
9229 ),
9230 ));
9231 self.push(out);
9232 Ok(())
9233 }
9234 Op::PForWithBlock(block_idx) => {
9235 let line = self.line();
9236 let list = self.pop().to_list();
9237 let progress_flag = self.pop().is_true();
9238 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9239 let idx = *block_idx as usize;
9240 let subs = self.interp.subs.clone();
9241 let (scope_capture, atomic_arrays, atomic_hashes) =
9242 self.interp.scope.capture_with_atomics();
9243 let first_err: Arc<Mutex<Option<StrykeError>>> = Arc::new(Mutex::new(None));
9244 let n_workers = rayon::current_num_threads();
9245 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
9246 .map(|_| {
9247 let mut interp = VMHelper::new();
9248 interp.subs = subs.clone();
9249 interp.scope.restore_capture(&scope_capture);
9250 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
9251 interp.enable_parallel_guard();
9252 Mutex::new(interp)
9253 })
9254 .collect();
9255 if let Some(&(start, end)) =
9256 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9257 {
9258 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9259 list.into_par_iter().for_each(|item| {
9260 if first_err.lock().is_some() {
9261 return;
9262 }
9263 let tid = rayon::current_thread_index().unwrap_or(0) % pool.len();
9264 let mut local_interp = pool[tid].lock();
9265 local_interp.scope.set_topic(item);
9266 let mut vm = shared.worker_vm(&mut local_interp);
9267 let mut op_count = 0u64;
9268 match vm.run_block_region(start, end, &mut op_count) {
9269 Ok(_) => {}
9270 Err(e) => {
9271 let mut g = first_err.lock();
9272 if g.is_none() {
9273 *g = Some(e);
9274 }
9275 }
9276 }
9277 pmap_progress.tick();
9278 });
9279 } else {
9280 let block = self.blocks[idx].clone();
9281 list.into_par_iter().for_each(|item| {
9282 if first_err.lock().is_some() {
9283 return;
9284 }
9285 let tid = rayon::current_thread_index().unwrap_or(0) % pool.len();
9286 let mut local_interp = pool[tid].lock();
9287 local_interp.scope.set_topic(item);
9288 local_interp.scope_push_hook();
9289 match local_interp.exec_block_no_scope(&block) {
9290 Ok(_) => {}
9291 Err(e) => {
9292 let stryke = match e {
9293 FlowOrError::Error(stryke) => stryke,
9294 FlowOrError::Flow(_) => StrykeError::runtime(
9295 "return/last/next/redo not supported inside pfor block",
9296 line,
9297 ),
9298 };
9299 let mut g = first_err.lock();
9300 if g.is_none() {
9301 *g = Some(stryke);
9302 }
9303 }
9304 }
9305 local_interp.scope_pop_hook();
9306 pmap_progress.tick();
9307 });
9308 }
9309 pmap_progress.finish();
9310 if let Some(e) = first_err.lock().take() {
9311 return Err(e);
9312 }
9313 self.push(StrykeValue::UNDEF);
9314 Ok(())
9315 }
9316 Op::PSortWithBlock(block_idx) => {
9317 let mut items = self.pop().to_list();
9318 let progress_flag = self.pop().is_true();
9319 let pmap_progress = PmapProgress::new(progress_flag, 2);
9320 pmap_progress.tick();
9321 let idx = *block_idx as usize;
9322 let subs = self.interp.subs.clone();
9323 let (scope_capture, atomic_arrays, atomic_hashes) =
9324 self.interp.scope.capture_with_atomics();
9325 if let Some(&(start, end)) =
9326 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9327 {
9328 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9329 items.par_sort_by(|a, b| {
9330 let mut local_interp = VMHelper::new();
9331 local_interp.subs = subs.clone();
9332 local_interp.scope.restore_capture(&scope_capture);
9333 local_interp
9334 .scope
9335 .restore_atomics(&atomic_arrays, &atomic_hashes);
9336 local_interp.enable_parallel_guard();
9337 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9338 local_interp.scope.set_closure_args(&[a.clone(), b.clone()]);
9345 let mut vm = shared.worker_vm(&mut local_interp);
9346 let mut op_count = 0u64;
9347 match vm.run_block_region(start, end, &mut op_count) {
9348 Ok(v) => {
9349 let n = v.to_int();
9350 if n < 0 {
9351 std::cmp::Ordering::Less
9352 } else if n > 0 {
9353 std::cmp::Ordering::Greater
9354 } else {
9355 std::cmp::Ordering::Equal
9356 }
9357 }
9358 Err(_) => std::cmp::Ordering::Equal,
9359 }
9360 });
9361 } else {
9362 let block = self.blocks[idx].clone();
9363 items.par_sort_by(|a, b| {
9364 let mut local_interp = VMHelper::new();
9365 local_interp.subs = subs.clone();
9366 local_interp.scope.restore_capture(&scope_capture);
9367 local_interp
9368 .scope
9369 .restore_atomics(&atomic_arrays, &atomic_hashes);
9370 local_interp.enable_parallel_guard();
9371 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9372 local_interp.scope.set_closure_args(&[a.clone(), b.clone()]);
9373 local_interp.scope_push_hook();
9374 let ord = match local_interp.exec_block_no_scope(&block) {
9375 Ok(v) => {
9376 let n = v.to_int();
9377 if n < 0 {
9378 std::cmp::Ordering::Less
9379 } else if n > 0 {
9380 std::cmp::Ordering::Greater
9381 } else {
9382 std::cmp::Ordering::Equal
9383 }
9384 }
9385 Err(_) => std::cmp::Ordering::Equal,
9386 };
9387 local_interp.scope_pop_hook();
9388 ord
9389 });
9390 }
9391 pmap_progress.tick();
9392 pmap_progress.finish();
9393 self.push(StrykeValue::array(items));
9394 Ok(())
9395 }
9396 Op::PSortWithBlockFast(tag) => {
9397 let mut items = self.pop().to_list();
9398 let progress_flag = self.pop().is_true();
9399 let pmap_progress = PmapProgress::new(progress_flag, 2);
9400 pmap_progress.tick();
9401 let mode = match *tag {
9402 0 => SortBlockFast::Numeric,
9403 1 => SortBlockFast::String,
9404 2 => SortBlockFast::NumericRev,
9405 3 => SortBlockFast::StringRev,
9406 _ => SortBlockFast::Numeric,
9407 };
9408 items.par_sort_by(|a, b| sort_magic_cmp(a, b, mode));
9409 pmap_progress.tick();
9410 pmap_progress.finish();
9411 self.push(StrykeValue::array(items));
9412 Ok(())
9413 }
9414 Op::PSortNoBlockParallel => {
9415 let mut items = self.pop().to_list();
9416 let progress_flag = self.pop().is_true();
9417 let pmap_progress = PmapProgress::new(progress_flag, 2);
9418 pmap_progress.tick();
9419 items.par_sort_by(|a, b| a.to_string().cmp(&b.to_string()));
9420 pmap_progress.tick();
9421 pmap_progress.finish();
9422 self.push(StrykeValue::array(items));
9423 Ok(())
9424 }
9425 Op::FanWithBlock(block_idx) => {
9426 let line = self.line();
9427 let n = self.pop().to_int().max(0) as usize;
9428 let progress_flag = self.pop().is_true();
9429 self.run_fan_block(*block_idx, n, line, progress_flag)?;
9430 Ok(())
9431 }
9432 Op::FanWithBlockAuto(block_idx) => {
9433 let line = self.line();
9434 let n = self.interp.parallel_thread_count();
9435 let progress_flag = self.pop().is_true();
9436 self.run_fan_block(*block_idx, n, line, progress_flag)?;
9437 Ok(())
9438 }
9439 Op::FanCapWithBlock(block_idx) => {
9440 let line = self.line();
9441 let n = self.pop().to_int().max(0) as usize;
9442 let progress_flag = self.pop().is_true();
9443 self.run_fan_cap_block(*block_idx, n, line, progress_flag)?;
9444 Ok(())
9445 }
9446 Op::FanCapWithBlockAuto(block_idx) => {
9447 let line = self.line();
9448 let n = self.interp.parallel_thread_count();
9449 let progress_flag = self.pop().is_true();
9450 self.run_fan_cap_block(*block_idx, n, line, progress_flag)?;
9451 Ok(())
9452 }
9453
9454 Op::AsyncBlock(block_idx) => {
9455 let block = self.blocks[*block_idx as usize].clone();
9456 let subs = self.interp.subs.clone();
9457 let (scope_capture, atomic_arrays, atomic_hashes) =
9458 self.interp.scope.capture_with_atomics();
9459 let result_slot: Arc<Mutex<Option<StrykeResult<StrykeValue>>>> =
9460 Arc::new(Mutex::new(None));
9461 let join_slot: Arc<Mutex<Option<std::thread::JoinHandle<()>>>> =
9462 Arc::new(Mutex::new(None));
9463 let rs = Arc::clone(&result_slot);
9464 let h = std::thread::spawn(move || {
9465 let mut local_interp = VMHelper::new();
9466 local_interp.subs = subs;
9467 local_interp.scope.restore_capture(&scope_capture);
9468 local_interp
9469 .scope
9470 .restore_atomics(&atomic_arrays, &atomic_hashes);
9471 local_interp.enable_parallel_guard();
9472 local_interp.scope_push_hook();
9473 let out = match local_interp.exec_block_no_scope(&block) {
9474 Ok(v) => Ok(v),
9475 Err(FlowOrError::Flow(Flow::Return(v))) => Ok(v),
9476 Err(FlowOrError::Error(e)) => Err(e),
9477 Err(_) => Ok(StrykeValue::UNDEF),
9478 };
9479 local_interp.scope_pop_hook();
9480 *rs.lock() = Some(out);
9481 });
9482 *join_slot.lock() = Some(h);
9483 self.push(StrykeValue::async_task(Arc::new(StrykeAsyncTask {
9484 result: result_slot,
9485 join: join_slot,
9486 })));
9487 Ok(())
9488 }
9489 Op::Await => {
9490 let v = self.pop();
9491 if let Some(t) = v.as_async_task() {
9492 let r = t.await_result();
9493 self.push(r?);
9494 } else {
9495 self.push(v);
9496 }
9497 Ok(())
9498 }
9499
9500 Op::LoadCurrentSub => {
9501 if let Some(sub) = self.interp.current_sub_stack.last().cloned() {
9502 self.push(StrykeValue::code_ref(sub));
9503 } else {
9504 self.push(StrykeValue::UNDEF);
9505 }
9506 Ok(())
9507 }
9508
9509 Op::DeferBlock => {
9510 let coderef = self.pop();
9511 self.interp.scope.push_defer(coderef);
9512 Ok(())
9513 }
9514
9515 Op::TryPush { .. } => {
9517 self.try_stack.push(TryFrame {
9518 try_push_op_idx: self.ip - 1,
9519 state: TryState::Trying,
9520 deferred_error: None,
9521 });
9522 Ok(())
9523 }
9524 Op::TryContinueNormal => {
9525 let frame = self.try_stack.last().ok_or_else(|| {
9526 StrykeError::runtime(
9527 "TryContinueNormal without active try",
9528 self.line(),
9529 )
9530 })?;
9531 let Op::TryPush {
9532 finally_ip,
9533 after_ip,
9534 ..
9535 } = &self.ops[frame.try_push_op_idx]
9536 else {
9537 return Err(StrykeError::runtime(
9538 "TryContinueNormal: corrupt try frame",
9539 self.line(),
9540 ));
9541 };
9542 if let Some(fin_ip) = *finally_ip {
9543 self.ip = fin_ip;
9544 Ok(())
9545 } else {
9546 self.try_stack.pop();
9547 self.ip = *after_ip;
9548 Ok(())
9549 }
9550 }
9551 Op::TryFinallyEnd => {
9552 let frame = self.try_stack.pop().ok_or_else(|| {
9553 StrykeError::runtime("TryFinallyEnd without active try", self.line())
9554 })?;
9555 if let Some(deferred) = frame.deferred_error {
9558 return Err(deferred);
9559 }
9560 let Op::TryPush { after_ip, .. } = &self.ops[frame.try_push_op_idx] else {
9561 return Err(StrykeError::runtime(
9562 "TryFinallyEnd: corrupt try frame",
9563 self.line(),
9564 ));
9565 };
9566 self.ip = *after_ip;
9567 Ok(())
9568 }
9569 Op::CatchReceive(idx) => {
9570 let val = self.pending_catch_error.take().ok_or_else(|| {
9571 StrykeError::runtime(
9572 "CatchReceive without pending exception",
9573 self.line(),
9574 )
9575 })?;
9576 let n = names[*idx as usize].as_str();
9577 self.interp.scope_pop_hook();
9578 self.interp.scope_push_hook();
9579 self.interp.scope.declare_scalar(n, val);
9580 self.interp.english_note_lexical_scalar(n);
9581 Ok(())
9582 }
9583
9584 Op::DeclareMySyncScalar(name_idx) => {
9585 let val = self.pop();
9586 let n = names[*name_idx as usize].as_str();
9587 let stored = if val.is_mysync_deque_or_heap() {
9588 val
9589 } else {
9590 StrykeValue::atomic(Arc::new(Mutex::new(val)))
9591 };
9592 self.interp.scope.declare_scalar(n, stored);
9593 Ok(())
9594 }
9595 Op::DeclareMySyncArray(name_idx) => {
9596 let val = self.pop();
9597 let n = names[*name_idx as usize].as_str();
9598 self.interp.scope.declare_atomic_array(n, val.to_list());
9599 Ok(())
9600 }
9601 Op::DeclareMySyncHash(name_idx) => {
9602 let val = self.pop();
9603 let n = names[*name_idx as usize].as_str();
9604 let items = val.to_list();
9605 let mut map = IndexMap::new();
9606 let mut i = 0usize;
9607 while i + 1 < items.len() {
9608 map.insert(items[i].to_string(), items[i + 1].clone());
9609 i += 2;
9610 }
9611 self.interp.scope.declare_atomic_hash(n, map);
9612 Ok(())
9613 }
9614 Op::DeclareOurSyncScalar(name_idx) => {
9615 let val = self.pop();
9616 let n = names[*name_idx as usize].as_str();
9617 let stored = if val.is_mysync_deque_or_heap() {
9618 val
9619 } else {
9620 StrykeValue::atomic(Arc::new(Mutex::new(val)))
9621 };
9622 self.interp.scope.declare_scalar(n, stored);
9623 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9628 self.interp.english_note_lexical_scalar_pub(&bare);
9629 self.interp.note_our_scalar_pub(&bare);
9630 Ok(())
9631 }
9632 Op::DeclareOurSyncArray(name_idx) => {
9633 let val = self.pop();
9634 let n = names[*name_idx as usize].as_str();
9635 self.interp.scope.declare_atomic_array(n, val.to_list());
9636 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9637 self.interp.english_note_lexical_scalar_pub(&bare);
9638 self.interp.note_our_scalar_pub(&bare);
9639 Ok(())
9640 }
9641 Op::DeclareOurSyncHash(name_idx) => {
9642 let val = self.pop();
9643 let n = names[*name_idx as usize].as_str();
9644 let items = val.to_list();
9645 let mut map = IndexMap::new();
9646 let mut i = 0usize;
9647 while i + 1 < items.len() {
9648 map.insert(items[i].to_string(), items[i + 1].clone());
9649 i += 2;
9650 }
9651 self.interp.scope.declare_atomic_hash(n, map);
9652 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9653 self.interp.english_note_lexical_scalar_pub(&bare);
9654 self.interp.note_our_scalar_pub(&bare);
9655 Ok(())
9656 }
9657 Op::RuntimeSubDecl(idx) => {
9658 let rs = &self.runtime_sub_decls[*idx as usize];
9659 let key = self.interp.qualify_sub_key(&rs.name);
9660 let captured = self.interp.scope.capture();
9661 let closure_env = if captured.is_empty() {
9662 None
9663 } else {
9664 Some(captured)
9665 };
9666 let mut sub = StrykeSub {
9667 name: rs.name.clone(),
9668 params: rs.params.clone(),
9669 body: rs.body.clone(),
9670 closure_env,
9671 prototype: rs.prototype.clone(),
9672 fib_like: None,
9673 };
9674 sub.fib_like = crate::fib_like_tail::detect_fib_like_recursive_add(&sub);
9675 self.interp.subs.insert(key, Arc::new(sub));
9676 Ok(())
9677 }
9678 Op::RegisterAdvice(idx) => {
9679 let rd = &self.runtime_advice_decls[*idx as usize];
9680 let id = self.interp.next_intercept_id;
9681 self.interp.next_intercept_id = id.saturating_add(1);
9682 self.interp.intercepts.push(crate::aop::Intercept {
9683 id,
9684 kind: rd.kind,
9685 pattern: rd.pattern.clone(),
9686 body: rd.body.clone(),
9687 body_block_idx: rd.body_block_idx,
9688 });
9689 Ok(())
9690 }
9691 Op::Tie {
9692 target_kind,
9693 name_idx,
9694 argc,
9695 } => {
9696 let argc = *argc as usize;
9697 let mut stack_vals = Vec::with_capacity(argc);
9698 for _ in 0..argc {
9699 stack_vals.push(self.pop());
9700 }
9701 stack_vals.reverse();
9702 let name = names[*name_idx as usize].as_str();
9703 let line = self.line();
9704 self.interp
9705 .tie_execute(*target_kind, name, stack_vals, line)
9706 .map_err(|e| e.at_line(line))?;
9707 Ok(())
9708 }
9709 Op::FormatDecl(idx) => {
9710 let (basename, lines) = &self.format_decls[*idx as usize];
9711 let line = self.line();
9712 self.interp
9713 .install_format_decl(basename.as_str(), lines, line)
9714 .map_err(|e| e.at_line(line))?;
9715 Ok(())
9716 }
9717 Op::UseOverload(idx) => {
9718 let pairs = &self.use_overload_entries[*idx as usize];
9719 self.interp.install_use_overload_pairs(pairs);
9720 Ok(())
9721 }
9722 Op::ScalarCompoundAssign { name_idx, op: op_b } => {
9723 let rhs = self.pop();
9724 let n = names[*name_idx as usize].as_str();
9725 let op = scalar_compound_op_from_byte(*op_b).ok_or_else(|| {
9726 StrykeError::runtime(
9727 "ScalarCompoundAssign: invalid op byte",
9728 self.line(),
9729 )
9730 })?;
9731 let en = self.interp.english_scalar_name(n);
9732 let val = self
9733 .interp
9734 .scalar_compound_assign_scalar_target(en, op, rhs)
9735 .map_err(|e| e.at_line(self.line()))?;
9736 self.push(val);
9737 Ok(())
9738 }
9739
9740 Op::SetGlobalPhase(phase) => {
9741 let s = match *phase {
9742 crate::bytecode::GP_START => "START",
9743 crate::bytecode::GP_UNITCHECK => "UNITCHECK",
9744 crate::bytecode::GP_CHECK => "CHECK",
9745 crate::bytecode::GP_INIT => "INIT",
9746 crate::bytecode::GP_RUN => "RUN",
9747 crate::bytecode::GP_END => "END",
9748 _ => {
9749 return Err(StrykeError::runtime(
9750 format!("SetGlobalPhase: invalid phase byte {}", phase),
9751 self.line(),
9752 ));
9753 }
9754 };
9755 self.interp.global_phase = s.to_string();
9756 Ok(())
9757 }
9758
9759 Op::Halt => {
9761 self.halt = true;
9762 Ok(())
9763 }
9764 Op::EvalAstExpr(idx) => {
9765 let expr = &self.ast_eval_exprs[*idx as usize];
9766 let val = match self.interp.eval_expr_ctx(expr, self.interp.wantarray_kind)
9767 {
9768 Ok(v) => v,
9769 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
9770 Err(crate::vm_helper::FlowOrError::Flow(f)) => {
9771 return Err(StrykeError::runtime(
9772 format!("unexpected flow control in EvalAstExpr: {:?}", f),
9773 self.line(),
9774 ));
9775 }
9776 };
9777 self.push(val);
9778 Ok(())
9779 }
9780 }
9781 })();
9782 if let (Some(prof), Some(t0)) = (&mut self.interp.profiler, op_prof_t0) {
9783 prof.on_line(&self.interp.file, line, t0.elapsed());
9784 }
9785 if let Err(e) = __op_res {
9786 if self.try_recover_from_exception(&e)? {
9787 continue;
9788 }
9789 return Err(e);
9790 }
9791 if crate::pending_destroy::pending_destroy_vm_sync_needed() {
9794 self.interp.drain_pending_destroys(line)?;
9795 }
9796 if self.exit_main_dispatch {
9797 if let Some(v) = self.exit_main_dispatch_value.take() {
9798 last = v;
9799 }
9800 break;
9801 }
9802 if self.halt {
9803 break;
9804 }
9805 }
9806
9807 if !self.stack.is_empty() {
9808 last = self.stack.last().cloned().unwrap_or(StrykeValue::UNDEF);
9809 if last.is_iterator() {
9812 let iter = last.clone().into_iterator();
9813 while iter.next_item().is_some() {}
9814 last = StrykeValue::UNDEF;
9815 }
9816 }
9817
9818 Ok(last)
9819 }
9820
9821 pub(crate) fn jit_trampoline_run_sub(
9823 &mut self,
9824 entry_ip: usize,
9825 want: WantarrayCtx,
9826 args: &[i64],
9827 ) -> StrykeResult<StrykeValue> {
9828 let saved_wa = self.interp.wantarray_kind;
9829 for a in args {
9830 self.push(StrykeValue::integer(*a));
9831 }
9832 let stack_base = self.stack.len() - args.len();
9833 let mut sub_prof_t0 = None;
9834 if let Some(nidx) = self.sub_entry_name_idx(entry_ip) {
9835 sub_prof_t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
9836 let nm_owned = self.names[nidx as usize].to_string();
9837 if let Some(p) = &mut self.interp.profiler {
9838 p.enter_sub(nm_owned.as_str());
9839 }
9840 self.interp.debugger_enter_sub(nm_owned.as_str());
9841 }
9842 self.call_stack.push(CallFrame {
9843 return_ip: 0,
9844 stack_base,
9845 scope_depth: self.interp.scope.depth(),
9846 saved_wantarray: saved_wa,
9847 jit_trampoline_return: true,
9848 block_region: false,
9849 sub_profiler_start: sub_prof_t0,
9850 });
9851 self.interp.wantarray_kind = want;
9852 self.interp.scope_push_hook();
9853 if let Some(nidx) = self.sub_entry_name_idx(entry_ip) {
9854 let nm = self.names[nidx as usize].as_str();
9855 if let Some(sub) = self.interp.subs.get(nm).cloned() {
9856 if let Some(ref env) = sub.closure_env {
9857 self.interp.scope.restore_capture(env);
9858 }
9859 }
9860 }
9861 self.ip = entry_ip;
9862 self.jit_trampoline_out = None;
9863 self.jit_trampoline_depth = self.jit_trampoline_depth.saturating_add(1);
9864 let mut op_count = 0u64;
9865 let last = StrykeValue::UNDEF;
9866 let r = self.run_main_dispatch_loop(last, &mut op_count, true);
9867 self.jit_trampoline_depth = self.jit_trampoline_depth.saturating_sub(1);
9868 r?;
9869 self.jit_trampoline_out.take().ok_or_else(|| {
9870 StrykeError::runtime("JIT trampoline: subroutine did not return", self.line())
9871 })
9872 }
9873
9874 #[inline]
9875 fn find_sub_entry(&self, name_idx: u16) -> Option<(usize, bool)> {
9876 self.sub_entry_by_name.get(&name_idx).copied()
9877 }
9878
9879 fn sub_entry_name_idx(&self, entry_ip: usize) -> Option<u16> {
9881 for &(n, ip, _) in &self.sub_entries {
9882 if ip == entry_ip {
9883 return Some(n);
9884 }
9885 }
9886 None
9887 }
9888
9889 fn exec_builtin(&mut self, id: u16, args: Vec<StrykeValue>) -> StrykeResult<StrykeValue> {
9890 let line = self.line();
9891 let bid = BuiltinId::from_u16(id);
9892 match bid {
9893 Some(BuiltinId::Length) => {
9894 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9895 Ok(StrykeValue::integer(val.length_value(self.interp.utf8_pragma)))
9896 }
9897 Some(BuiltinId::Defined) => {
9898 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9899 Ok(StrykeValue::integer(if val.is_undef() { 0 } else { 1 }))
9900 }
9901 Some(BuiltinId::Abs) => {
9902 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9903 Ok(StrykeValue::float(val.to_number().abs()))
9904 }
9905 Some(BuiltinId::Int) => {
9906 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9907 Ok(StrykeValue::integer(val.to_number() as i64))
9908 }
9909 Some(BuiltinId::Sqrt) => {
9910 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9911 Ok(StrykeValue::float(val.to_number().sqrt()))
9912 }
9913 Some(BuiltinId::Sin) => {
9914 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9915 Ok(StrykeValue::float(val.to_number().sin()))
9916 }
9917 Some(BuiltinId::Cos) => {
9918 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9919 Ok(StrykeValue::float(val.to_number().cos()))
9920 }
9921 Some(BuiltinId::Atan2) => {
9922 let mut it = args.into_iter();
9923 let y = it.next().unwrap_or(StrykeValue::UNDEF);
9924 let x = it.next().unwrap_or(StrykeValue::UNDEF);
9925 Ok(StrykeValue::float(y.to_number().atan2(x.to_number())))
9926 }
9927 Some(BuiltinId::Exp) => {
9928 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9929 Ok(StrykeValue::float(val.to_number().exp()))
9930 }
9931 Some(BuiltinId::Log) => {
9932 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9933 Ok(StrykeValue::float(val.to_number().ln()))
9934 }
9935 Some(BuiltinId::Rand) => {
9936 let upper = match args.len() {
9937 0 => 1.0,
9938 _ => args[0].to_number(),
9939 };
9940 Ok(StrykeValue::float(self.interp.perl_rand(upper)))
9941 }
9942 Some(BuiltinId::Srand) => {
9943 let seed = match args.len() {
9944 0 => None,
9945 _ => Some(args[0].to_number()),
9946 };
9947 Ok(StrykeValue::integer(self.interp.perl_srand(seed)))
9948 }
9949 Some(BuiltinId::Crypt) => {
9950 let mut it = args.into_iter();
9951 let p = it.next().unwrap_or(StrykeValue::UNDEF).to_string();
9952 let salt = it.next().unwrap_or(StrykeValue::UNDEF).to_string();
9953 Ok(StrykeValue::string(crate::crypt_util::perl_crypt(
9954 &p, &salt,
9955 )))
9956 }
9957 Some(BuiltinId::Fc) => {
9958 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9959 Ok(StrykeValue::string(s.fc_value()))
9960 }
9961 Some(BuiltinId::Quotemeta) => {
9962 let s = args
9963 .into_iter()
9964 .next()
9965 .map(|v| v.to_string())
9966 .unwrap_or_default();
9967 Ok(StrykeValue::string(crate::perl_regex::perl_quotemeta(&s)))
9968 }
9969 Some(BuiltinId::Tan) => Ok(StrykeValue::float(
9970 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().tan(),
9971 )),
9972 Some(BuiltinId::Asin) => Ok(StrykeValue::float(
9973 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().asin(),
9974 )),
9975 Some(BuiltinId::Acos) => Ok(StrykeValue::float(
9976 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().acos(),
9977 )),
9978 Some(BuiltinId::Atan) => Ok(StrykeValue::float(
9979 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().atan(),
9980 )),
9981 Some(BuiltinId::Sinh) => Ok(StrykeValue::float(
9982 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().sinh(),
9983 )),
9984 Some(BuiltinId::Cosh) => Ok(StrykeValue::float(
9985 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().cosh(),
9986 )),
9987 Some(BuiltinId::Tanh) => Ok(StrykeValue::float(
9988 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().tanh(),
9989 )),
9990 Some(BuiltinId::Log2) => Ok(StrykeValue::float(
9991 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().log2(),
9992 )),
9993 Some(BuiltinId::Log10) => Ok(StrykeValue::float(
9994 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().log10(),
9995 )),
9996 Some(BuiltinId::Ceil) => Ok(StrykeValue::integer(
9997 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().ceil() as i64,
9998 )),
9999 Some(BuiltinId::Floor) => Ok(StrykeValue::integer(
10000 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().floor() as i64,
10001 )),
10002 Some(BuiltinId::Round) => Ok(StrykeValue::integer(
10006 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().round() as i64,
10007 )),
10008 Some(BuiltinId::Pos) => {
10009 let key = if args.is_empty() {
10010 "_".to_string()
10011 } else {
10012 args[0].to_string()
10013 };
10014 Ok(self
10015 .interp
10016 .regex_pos
10017 .get(&key)
10018 .copied()
10019 .flatten()
10020 .map(|n| StrykeValue::integer(n as i64))
10021 .unwrap_or(StrykeValue::UNDEF))
10022 }
10023 Some(BuiltinId::Study) => {
10024 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10025 Ok(VMHelper::study_return_value(&s.to_string()))
10026 }
10027 Some(BuiltinId::Chr) => {
10028 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10029 Ok(StrykeValue::string(val.chr_value()))
10030 }
10031 Some(BuiltinId::Ord) => {
10032 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10033 Ok(StrykeValue::integer(val.ord_value()))
10034 }
10035 Some(BuiltinId::Hex) => {
10036 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10037 Ok(StrykeValue::integer(val.hex_value()))
10038 }
10039 Some(BuiltinId::Oct) => {
10040 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10041 Ok(StrykeValue::integer(val.oct_value()))
10042 }
10043 Some(BuiltinId::Uc) => {
10044 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10045 Ok(StrykeValue::string(s.uc_value()))
10046 }
10047 Some(BuiltinId::Lc) => {
10048 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10049 Ok(StrykeValue::string(s.lc_value()))
10050 }
10051 Some(BuiltinId::Ref) => {
10052 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10053 Ok(val.ref_type())
10054 }
10055 Some(BuiltinId::Scalar) => {
10056 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10057 Ok(val.scalar_context())
10058 }
10059 Some(BuiltinId::Join) => {
10060 let mut iter = args.into_iter();
10061 let sep = iter.next().unwrap_or(StrykeValue::UNDEF).to_string();
10062 let list = iter.next().unwrap_or(StrykeValue::UNDEF).to_list();
10063 let mut strs = Vec::with_capacity(list.len());
10064 for v in list {
10065 let s = match self.interp.stringify_value(v, line) {
10066 Ok(s) => s,
10067 Err(FlowOrError::Error(e)) => return Err(e),
10068 Err(FlowOrError::Flow(_)) => {
10069 return Err(StrykeError::runtime(
10070 "join: unexpected control flow",
10071 line,
10072 ));
10073 }
10074 };
10075 strs.push(s);
10076 }
10077 Ok(StrykeValue::string(strs.join(&sep)))
10078 }
10079 Some(BuiltinId::Split) => {
10080 let mut iter = args.into_iter();
10081 let pat_val = iter.next().unwrap_or(StrykeValue::string(" ".into()));
10082 let pat = pat_val
10088 .regex_src_and_flags()
10089 .map(|(s, _)| s)
10090 .unwrap_or_else(|| pat_val.to_string());
10091 let s = iter.next().unwrap_or(StrykeValue::UNDEF).to_string();
10092 if s.is_empty() {
10095 return Ok(StrykeValue::array(vec![]));
10096 }
10097 let lim_signed: Option<i64> = iter.next().map(|v| v.to_int());
10102
10103 let mut parts: Vec<String> = if pat.is_empty() {
10104 let chars: Vec<String> = s.chars().map(|c| c.to_string()).collect();
10108 match lim_signed {
10109 Some(l) if l > 0 => {
10116 let n = l as usize;
10117 if n < chars.len() {
10118 let mut head: Vec<String> =
10119 chars.iter().take(n.saturating_sub(1)).cloned().collect();
10120 let tail: String = s.chars().skip(n.saturating_sub(1)).collect();
10121 head.push(tail);
10122 head
10123 } else if n == chars.len() {
10124 chars
10125 } else {
10126 let mut v = chars;
10127 v.push(String::new());
10128 v
10129 }
10130 }
10131 Some(l) if l < 0 => {
10133 let mut v = chars;
10134 v.push(String::new());
10135 v
10136 }
10137 _ => chars,
10140 }
10141 } else {
10142 let re =
10143 regex::Regex::new(&pat).unwrap_or_else(|_| regex::Regex::new(" ").unwrap());
10144 match lim_signed {
10145 Some(l) if l > 0 => {
10146 re.splitn(&s, l as usize).map(|p| p.to_string()).collect()
10147 }
10148 _ => re.split(&s).map(|p| p.to_string()).collect(),
10149 }
10150 };
10151
10152 let strip_trailing = matches!(lim_signed, None | Some(0));
10156 if strip_trailing {
10157 while parts.last().is_some_and(|p| p.is_empty()) {
10158 parts.pop();
10159 }
10160 }
10161
10162 Ok(StrykeValue::array(
10163 parts.into_iter().map(StrykeValue::string).collect(),
10164 ))
10165 }
10166 Some(BuiltinId::Sprintf) => {
10167 let mut flat: Vec<StrykeValue> = Vec::with_capacity(args.len());
10170 for a in args.into_iter() {
10171 if let Some(items) = a.as_array_vec() {
10172 flat.extend(items);
10173 } else {
10174 flat.push(a);
10175 }
10176 }
10177 let args = flat;
10178 if args.is_empty() {
10179 return Ok(StrykeValue::string(String::new()));
10180 }
10181 let fmt = args[0].to_string();
10182 let rest = &args[1..];
10183 match self.interp.perl_sprintf_stringify(&fmt, rest, line) {
10184 Ok(s) => Ok(StrykeValue::string(s)),
10185 Err(FlowOrError::Error(e)) => Err(e),
10186 Err(FlowOrError::Flow(_)) => Err(StrykeError::runtime(
10187 "sprintf: unexpected control flow",
10188 line,
10189 )),
10190 }
10191 }
10192 Some(BuiltinId::Reverse) => {
10193 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10194 Ok(if let Some(mut a) = val.as_array_vec() {
10195 a.reverse();
10196 StrykeValue::array(a)
10197 } else if let Some(s) = val.as_str() {
10198 StrykeValue::string(s.chars().rev().collect())
10199 } else {
10200 StrykeValue::string(val.to_string().chars().rev().collect())
10201 })
10202 }
10203 Some(BuiltinId::Die) => {
10204 if args.len() == 1 {
10207 let v = &args[0];
10208 if v.as_hash_ref().is_some()
10209 || v.as_blessed_ref().is_some()
10210 || v.as_array_ref().is_some()
10211 || v.as_code_ref().is_some()
10212 {
10213 let msg = v.to_string();
10214 self.interp.fire_pseudosig_die(&msg, line)?;
10215 return Err(StrykeError::die_with_value(v.clone(), msg, line));
10216 }
10217 }
10218 let mut msg = String::new();
10219 for a in &args {
10220 msg.push_str(&a.to_string());
10221 }
10222 if msg.is_empty() {
10223 msg = "Died".to_string();
10224 }
10225 if !msg.ends_with('\n') {
10226 msg.push_str(&self.interp.die_warn_at_suffix(line));
10227 msg.push('\n');
10228 }
10229 self.interp.fire_pseudosig_die(&msg, line)?;
10230 Err(StrykeError::die(msg, line))
10231 }
10232 Some(BuiltinId::Warn) => {
10233 let mut msg = String::new();
10234 for a in &args {
10235 msg.push_str(&a.to_string());
10236 }
10237 if msg.is_empty() {
10238 msg = "Warning: something's wrong".to_string();
10239 }
10240 if !msg.ends_with('\n') {
10241 msg.push_str(&self.interp.die_warn_at_suffix(line));
10242 msg.push('\n');
10243 }
10244 self.interp.fire_pseudosig_warn(&msg, line)?;
10245 Ok(StrykeValue::integer(1))
10246 }
10247 Some(BuiltinId::Exit) => {
10248 let code = args
10249 .into_iter()
10250 .next()
10251 .map(|v| v.to_int() as i32)
10252 .unwrap_or(0);
10253 Err(StrykeError::new(
10254 ErrorKind::Exit(code),
10255 "",
10256 line,
10257 &self.interp.file,
10258 ))
10259 }
10260 Some(BuiltinId::System) => {
10261 let strs: Vec<String> = args.iter().map(|a| a.to_string()).collect();
10270 if strs.is_empty() {
10271 self.interp.child_exit_status = -1;
10272 return Ok(StrykeValue::integer(-1));
10273 }
10274 let status = if strs.len() == 1 {
10275 std::process::Command::new("sh")
10276 .arg("-c")
10277 .arg(&strs[0])
10278 .status()
10279 } else {
10280 std::process::Command::new(&strs[0])
10281 .args(&strs[1..])
10282 .status()
10283 };
10284 match status {
10285 Ok(s) => {
10286 self.interp.record_child_exit_status(s);
10287 Ok(StrykeValue::integer(self.interp.child_exit_status))
10288 }
10289 Err(e) => {
10290 self.interp.errno = e.to_string();
10291 self.interp.child_exit_status = -1;
10292 Ok(StrykeValue::integer(-1))
10293 }
10294 }
10295 }
10296 Some(BuiltinId::Ssh) => self.interp.ssh_builtin_execute(&args),
10297 Some(BuiltinId::Chomp) => {
10298 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10301 let s = val.to_string();
10302 Ok(StrykeValue::integer(if s.ends_with('\n') { 1 } else { 0 }))
10303 }
10304 Some(BuiltinId::Chop) => {
10305 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10306 let s = val.to_string();
10307 Ok(s.chars()
10308 .last()
10309 .map(|c| StrykeValue::string(c.to_string()))
10310 .unwrap_or(StrykeValue::UNDEF))
10311 }
10312 Some(BuiltinId::Substr) => {
10313 if args.len() < 3 {
10314 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10315 let off = args.get(1).map(|v| v.to_int()).unwrap_or(0);
10316 return Ok(StrykeValue::string(s.substr2_value(off)));
10317 }
10318 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10319 let off = args.get(1).map(|v| v.to_int()).unwrap_or(0);
10320 let len = args.get(2).map(|v| v.to_int()).unwrap_or(0);
10321 Ok(StrykeValue::string(s.substr3_value(off, len)))
10322 }
10323 Some(BuiltinId::Index) => {
10324 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10325 let sub = args.get(1).cloned().unwrap_or(StrykeValue::UNDEF);
10326 if args.len() < 3 {
10327 return Ok(StrykeValue::integer(s.index_value(&sub)));
10328 }
10329 let s = s.to_string();
10330 let sub = sub.to_string();
10331 let pos_raw = args.get(2).map(|v| v.to_int()).unwrap_or(0);
10334 let pos = if pos_raw < 0 {
10335 0usize
10336 } else {
10337 (pos_raw as usize).min(s.len())
10338 };
10339 Ok(StrykeValue::integer(
10340 s[pos..].find(&sub).map(|i| (i + pos) as i64).unwrap_or(-1),
10341 ))
10342 }
10343 Some(BuiltinId::Rindex) => {
10344 let sv = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10345 let subv = args.get(1).cloned().unwrap_or(StrykeValue::UNDEF);
10346 if args.len() < 3 {
10347 return Ok(StrykeValue::integer(sv.rindex_value(&subv)));
10348 }
10349 let s = sv.to_string();
10350 let sub = subv.to_string();
10351 let result = match args.get(2) {
10354 Some(v) => {
10355 let p = v.to_int();
10356 if p < 0 {
10357 -1
10358 } else {
10359 let end = (p as usize).saturating_add(sub.len()).min(s.len());
10360 s[..end].rfind(&sub).map(|i| i as i64).unwrap_or(-1)
10361 }
10362 }
10363 None => s.rfind(&sub).map(|i| i as i64).unwrap_or(-1),
10364 };
10365 Ok(StrykeValue::integer(result))
10366 }
10367 Some(BuiltinId::Ucfirst) => {
10368 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10369 Ok(StrykeValue::string(s.ucfirst_value()))
10370 }
10371 Some(BuiltinId::Lcfirst) => {
10372 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10373 Ok(StrykeValue::string(s.lcfirst_value()))
10374 }
10375 Some(BuiltinId::Splice) => self.interp.splice_builtin_execute(&args, line),
10376 Some(BuiltinId::Unshift) => self.interp.unshift_builtin_execute(&args, line),
10377 Some(BuiltinId::Printf) => {
10378 let mut flat: Vec<StrykeValue> = Vec::with_capacity(args.len());
10381 for a in args.into_iter() {
10382 if let Some(items) = a.as_array_vec() {
10383 flat.extend(items);
10384 } else {
10385 flat.push(a);
10386 }
10387 }
10388 let args = flat;
10389 let (fmt, rest): (String, &[StrykeValue]) = if args.is_empty() {
10390 let s = match self
10391 .interp
10392 .stringify_value(self.interp.scope.get_scalar("_").clone(), line)
10393 {
10394 Ok(s) => s,
10395 Err(FlowOrError::Error(e)) => return Err(e),
10396 Err(FlowOrError::Flow(_)) => {
10397 return Err(StrykeError::runtime(
10398 "printf: unexpected control flow",
10399 line,
10400 ));
10401 }
10402 };
10403 (s, &[])
10404 } else {
10405 (args[0].to_string(), &args[1..])
10406 };
10407 let out = match self.interp.perl_sprintf_stringify(&fmt, rest, line) {
10408 Ok(s) => s,
10409 Err(FlowOrError::Error(e)) => return Err(e),
10410 Err(FlowOrError::Flow(_)) => {
10411 return Err(StrykeError::runtime(
10412 "printf: unexpected control flow",
10413 line,
10414 ));
10415 }
10416 };
10417 print!("{}", out);
10418 if self.interp.output_autoflush {
10419 let _ = io::stdout().flush();
10420 }
10421 Ok(StrykeValue::integer(1))
10422 }
10423 Some(BuiltinId::Open) => {
10424 if args.len() < 2 {
10425 return Err(StrykeError::runtime(
10426 "open requires at least 2 arguments",
10427 line,
10428 ));
10429 }
10430 let handle_name = args[0].to_string();
10431 let mode_s = args[1].to_string();
10432 let file_opt = args.get(2).map(|v| v.to_string());
10433 self.interp
10434 .open_builtin_execute(handle_name, mode_s, file_opt, line)
10435 }
10436 Some(BuiltinId::Close) => {
10437 let name = args
10438 .into_iter()
10439 .next()
10440 .unwrap_or(StrykeValue::UNDEF)
10441 .to_string();
10442 self.interp.close_builtin_execute(name)
10443 }
10444 Some(BuiltinId::Eof) => self.interp.eof_builtin_execute(&args, line),
10445 Some(BuiltinId::ReadLine) => {
10446 let h = if args.is_empty() {
10447 None
10448 } else {
10449 Some(args[0].to_string())
10450 };
10451 self.interp.readline_builtin_execute(h.as_deref())
10452 }
10453 Some(BuiltinId::ReadLineList) => {
10454 let h = if args.is_empty() {
10455 None
10456 } else {
10457 Some(args[0].to_string())
10458 };
10459 self.interp.readline_builtin_execute_list(h.as_deref())
10460 }
10461 Some(BuiltinId::Exec) => {
10462 let cmd = args
10463 .iter()
10464 .map(|a| a.to_string())
10465 .collect::<Vec<_>>()
10466 .join(" ");
10467 let status = std::process::Command::new("sh")
10468 .arg("-c")
10469 .arg(&cmd)
10470 .status();
10471 std::process::exit(status.map(|s| s.code().unwrap_or(-1)).unwrap_or(-1));
10472 }
10473 Some(BuiltinId::Chdir) => {
10474 let path = args
10475 .into_iter()
10476 .next()
10477 .unwrap_or(StrykeValue::UNDEF)
10478 .to_string();
10479 if std::env::set_current_dir(&path).is_ok() {
10480 if let Ok(c) = std::env::current_dir() {
10481 self.interp.stryke_pwd = std::fs::canonicalize(&c).unwrap_or(c);
10482 }
10483 Ok(StrykeValue::integer(1))
10484 } else {
10485 Ok(StrykeValue::integer(0))
10486 }
10487 }
10488 Some(BuiltinId::Mkdir) => {
10489 let path = args.first().map(|v| v.to_string()).unwrap_or_default();
10490 let path = self.interp.resolve_stryke_path_string(&path);
10491 Ok(StrykeValue::integer(
10492 if std::fs::create_dir(&path).is_ok() {
10493 1
10494 } else {
10495 0
10496 },
10497 ))
10498 }
10499 Some(BuiltinId::Unlink) => {
10500 let mut count = 0i64;
10501 for a in &args {
10502 let p = self.interp.resolve_stryke_path_string(&a.to_string());
10503 if std::fs::remove_file(&p).is_ok() {
10504 count += 1;
10505 }
10506 }
10507 Ok(StrykeValue::integer(count))
10508 }
10509 Some(BuiltinId::Rmdir) => self.interp.builtin_rmdir_execute(&args, line),
10510 Some(BuiltinId::Utime) => self.interp.builtin_utime_execute(&args, line),
10511 Some(BuiltinId::Umask) => self.interp.builtin_umask_execute(&args, line),
10512 Some(BuiltinId::Getcwd) => self.interp.builtin_getcwd_execute(&args, line),
10513 Some(BuiltinId::Pipe) => self.interp.builtin_pipe_execute(&args, line),
10514 Some(BuiltinId::Rename) => {
10515 let old = self.interp.resolve_stryke_path_string(
10516 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10517 );
10518 let new = self.interp.resolve_stryke_path_string(
10519 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10520 );
10521 Ok(crate::perl_fs::rename_paths(&old, &new))
10522 }
10523 Some(BuiltinId::Chmod) => {
10524 if args.is_empty() {
10525 return Ok(StrykeValue::integer(0));
10526 }
10527 let mode = args[0].to_int();
10528 let paths: Vec<String> = args
10529 .iter()
10530 .skip(1)
10531 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10532 .collect();
10533 Ok(StrykeValue::integer(crate::perl_fs::chmod_paths(
10534 &paths, mode,
10535 )))
10536 }
10537 Some(BuiltinId::Chown) => {
10538 if args.len() < 3 {
10539 return Ok(StrykeValue::integer(0));
10540 }
10541 let uid = args[0].to_int();
10542 let gid = args[1].to_int();
10543 let paths: Vec<String> = args
10544 .iter()
10545 .skip(2)
10546 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10547 .collect();
10548 Ok(StrykeValue::integer(crate::perl_fs::chown_paths(
10549 &paths, uid, gid,
10550 )))
10551 }
10552 Some(BuiltinId::Stat) => {
10553 let path = self.interp.resolve_stryke_path_string(
10554 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10555 );
10556 Ok(crate::perl_fs::stat_path(&path, false))
10557 }
10558 Some(BuiltinId::Lstat) => {
10559 let path = self.interp.resolve_stryke_path_string(
10560 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10561 );
10562 Ok(crate::perl_fs::stat_path(&path, true))
10563 }
10564 Some(BuiltinId::Link) => {
10565 let old = self.interp.resolve_stryke_path_string(
10566 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10567 );
10568 let new = self.interp.resolve_stryke_path_string(
10569 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10570 );
10571 Ok(crate::perl_fs::link_hard(&old, &new))
10572 }
10573 Some(BuiltinId::Symlink) => {
10574 let old = args.first().map(|v| v.to_string()).unwrap_or_default();
10575 let new = self.interp.resolve_stryke_path_string(
10576 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10577 );
10578 Ok(crate::perl_fs::link_sym(&old, &new))
10579 }
10580 Some(BuiltinId::Readlink) => {
10581 let path = self.interp.resolve_stryke_path_string(
10582 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10583 );
10584 Ok(crate::perl_fs::read_link(&path))
10585 }
10586 Some(BuiltinId::Glob) => {
10587 let pats: Vec<String> = args.iter().map(|v| v.to_string()).collect();
10593 Ok(crate::perl_fs::glob_patterns(&pats))
10594 }
10595 Some(BuiltinId::Files) => {
10596 let dir = if args.is_empty() {
10597 self.interp.resolve_stryke_path_string(".")
10598 } else {
10599 self.interp.resolve_stryke_path_string(&args[0].to_string())
10600 };
10601 Ok(crate::perl_fs::list_files(&dir))
10602 }
10603 Some(BuiltinId::Filesf) => {
10604 let dir = if args.is_empty() {
10605 self.interp.resolve_stryke_path_string(".")
10606 } else {
10607 self.interp.resolve_stryke_path_string(&args[0].to_string())
10608 };
10609 Ok(crate::perl_fs::list_filesf(&dir))
10610 }
10611 Some(BuiltinId::FilesfRecursive) => {
10612 let dir = if args.is_empty() {
10613 self.interp.resolve_stryke_path_string(".")
10614 } else {
10615 self.interp.resolve_stryke_path_string(&args[0].to_string())
10616 };
10617 Ok(StrykeValue::iterator(std::sync::Arc::new(
10618 crate::value::FsWalkIterator::new(&dir, true),
10619 )))
10620 }
10621 Some(BuiltinId::Dirs) => {
10622 let dir = if args.is_empty() {
10623 self.interp.resolve_stryke_path_string(".")
10624 } else {
10625 self.interp.resolve_stryke_path_string(&args[0].to_string())
10626 };
10627 Ok(crate::perl_fs::list_dirs(&dir))
10628 }
10629 Some(BuiltinId::DirsRecursive) => {
10630 let dir = if args.is_empty() {
10631 self.interp.resolve_stryke_path_string(".")
10632 } else {
10633 self.interp.resolve_stryke_path_string(&args[0].to_string())
10634 };
10635 Ok(StrykeValue::iterator(std::sync::Arc::new(
10636 crate::value::FsWalkIterator::new(&dir, false),
10637 )))
10638 }
10639 Some(BuiltinId::SymLinks) => {
10640 let dir = if args.is_empty() {
10641 self.interp.resolve_stryke_path_string(".")
10642 } else {
10643 self.interp.resolve_stryke_path_string(&args[0].to_string())
10644 };
10645 Ok(crate::perl_fs::list_sym_links(&dir))
10646 }
10647 Some(BuiltinId::Sockets) => {
10648 let dir = if args.is_empty() {
10649 self.interp.resolve_stryke_path_string(".")
10650 } else {
10651 self.interp.resolve_stryke_path_string(&args[0].to_string())
10652 };
10653 Ok(crate::perl_fs::list_sockets(&dir))
10654 }
10655 Some(BuiltinId::Pipes) => {
10656 let dir = if args.is_empty() {
10657 self.interp.resolve_stryke_path_string(".")
10658 } else {
10659 self.interp.resolve_stryke_path_string(&args[0].to_string())
10660 };
10661 Ok(crate::perl_fs::list_pipes(&dir))
10662 }
10663 Some(BuiltinId::BlockDevices) => {
10664 let dir = if args.is_empty() {
10665 self.interp.resolve_stryke_path_string(".")
10666 } else {
10667 self.interp.resolve_stryke_path_string(&args[0].to_string())
10668 };
10669 Ok(crate::perl_fs::list_block_devices(&dir))
10670 }
10671 Some(BuiltinId::CharDevices) => {
10672 let dir = if args.is_empty() {
10673 self.interp.resolve_stryke_path_string(".")
10674 } else {
10675 self.interp.resolve_stryke_path_string(&args[0].to_string())
10676 };
10677 Ok(crate::perl_fs::list_char_devices(&dir))
10678 }
10679 Some(BuiltinId::Executables) => {
10680 let dir = if args.is_empty() {
10681 self.interp.resolve_stryke_path_string(".")
10682 } else {
10683 self.interp.resolve_stryke_path_string(&args[0].to_string())
10684 };
10685 Ok(crate::perl_fs::list_executables(&dir))
10686 }
10687 Some(BuiltinId::GlobPar) => {
10688 let pats: Vec<String> = args
10689 .iter()
10690 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10691 .collect();
10692 Ok(crate::perl_fs::glob_par_patterns(&pats))
10693 }
10694 Some(BuiltinId::GlobParProgress) => {
10695 let progress = args.last().map(|v| v.is_true()).unwrap_or(false);
10696 let pats: Vec<String> = args[..args.len().saturating_sub(1)]
10697 .iter()
10698 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10699 .collect();
10700 Ok(crate::perl_fs::glob_par_patterns_with_progress(
10701 &pats, progress,
10702 ))
10703 }
10704 Some(BuiltinId::ParSed) => self.interp.builtin_par_sed(&args, line, false),
10705 Some(BuiltinId::ParSedProgress) => self.interp.builtin_par_sed(&args, line, true),
10706 Some(BuiltinId::Opendir) => {
10707 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10708 let path = args.get(1).map(|v| v.to_string()).unwrap_or_default();
10709 Ok(self.interp.opendir_handle(&handle, &path))
10710 }
10711 Some(BuiltinId::Readdir) => {
10712 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10713 Ok(self.interp.readdir_handle(&handle))
10714 }
10715 Some(BuiltinId::ReaddirList) => {
10716 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10717 Ok(self.interp.readdir_handle_list(&handle))
10718 }
10719 Some(BuiltinId::Closedir) => {
10720 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10721 Ok(self.interp.closedir_handle(&handle))
10722 }
10723 Some(BuiltinId::Rewinddir) => {
10724 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10725 Ok(self.interp.rewinddir_handle(&handle))
10726 }
10727 Some(BuiltinId::Telldir) => {
10728 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10729 Ok(self.interp.telldir_handle(&handle))
10730 }
10731 Some(BuiltinId::Seekdir) => {
10732 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10733 let pos = args.get(1).map(|v| v.to_int().max(0) as usize).unwrap_or(0);
10734 Ok(self.interp.seekdir_handle(&handle, pos))
10735 }
10736 Some(BuiltinId::Slurp) => {
10737 let path = args
10738 .into_iter()
10739 .next()
10740 .unwrap_or(StrykeValue::UNDEF)
10741 .to_string();
10742 let path = self.interp.resolve_stryke_path_string(&path);
10743 crate::perl_fs::read_bytes_or_glob(&path)
10744 .map(StrykeValue::bytes)
10745 .map_err(|e| StrykeError::runtime(format!("slurp: {}", e), line))
10746 }
10747 Some(BuiltinId::Swallow) => {
10748 let path = args
10749 .into_iter()
10750 .next()
10751 .unwrap_or(StrykeValue::UNDEF)
10752 .to_string();
10753 let path = self.interp.resolve_stryke_path_string(&path);
10754 crate::perl_fs::swallow_to_hash(&path)
10755 .map_err(|e| StrykeError::runtime(format!("swallow: {}", e), line))
10756 }
10757 Some(BuiltinId::Ingest) => {
10758 let path = args
10759 .into_iter()
10760 .next()
10761 .unwrap_or(StrykeValue::UNDEF)
10762 .to_string();
10763 let path = self.interp.resolve_stryke_path_string(&path);
10764 crate::perl_fs::ingest_iterator(&path)
10765 .map_err(|e| StrykeError::runtime(format!("ingest: {}", e), line))
10766 }
10767 Some(BuiltinId::Burp) => {
10768 let v = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10769 crate::perl_fs::burp_hash_to_disk(&v)
10770 .map(StrykeValue::integer)
10771 .map_err(|e| StrykeError::runtime(format!("burp: {}", e), line))
10772 }
10773 Some(BuiltinId::God) => {
10774 let v = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10775 Ok(StrykeValue::string(crate::god::god_dump(&v)))
10776 }
10777 Some(BuiltinId::Capture) => {
10778 let cmd = args
10779 .into_iter()
10780 .next()
10781 .unwrap_or(StrykeValue::UNDEF)
10782 .to_string();
10783 crate::capture::run_capture(self.interp, &cmd, line)
10784 }
10785 Some(BuiltinId::Ppool) => {
10786 let n = args
10787 .first()
10788 .map(|v| v.to_int().max(0) as usize)
10789 .unwrap_or(1);
10790 crate::ppool::create_pool(n)
10791 }
10792 Some(BuiltinId::Wantarray) => Ok(match self.interp.wantarray_kind {
10793 crate::vm_helper::WantarrayCtx::Void => StrykeValue::UNDEF,
10794 crate::vm_helper::WantarrayCtx::Scalar => StrykeValue::integer(0),
10795 crate::vm_helper::WantarrayCtx::List => StrykeValue::integer(1),
10796 }),
10797 Some(BuiltinId::FetchUrl) => {
10798 let url = args
10799 .into_iter()
10800 .next()
10801 .unwrap_or(StrykeValue::UNDEF)
10802 .to_string();
10803 ureq::get(&url)
10804 .call()
10805 .map_err(|e| StrykeError::runtime(format!("fetch_url: {}", e), line))
10806 .and_then(|r| {
10807 r.into_string()
10808 .map(StrykeValue::string)
10809 .map_err(|e| StrykeError::runtime(format!("fetch_url: {}", e), line))
10810 })
10811 }
10812 Some(BuiltinId::Pchannel) => {
10813 if args.is_empty() {
10814 Ok(crate::pchannel::create_pair())
10815 } else if args.len() == 1 {
10816 let n = args[0].to_int().max(1) as usize;
10817 Ok(crate::pchannel::create_bounded_pair(n))
10818 } else {
10819 Err(StrykeError::runtime(
10820 "pchannel() takes 0 or 1 arguments (capacity)",
10821 line,
10822 ))
10823 }
10824 }
10825 Some(BuiltinId::Pselect) => crate::pchannel::pselect_recv(&args, line),
10826 Some(BuiltinId::DequeNew) => {
10827 if !args.is_empty() {
10828 return Err(StrykeError::runtime("deque() takes no arguments", line));
10829 }
10830 Ok(StrykeValue::deque(Arc::new(Mutex::new(VecDeque::new()))))
10831 }
10832 Some(BuiltinId::HeapNew) => {
10833 if args.len() != 1 {
10834 return Err(StrykeError::runtime(
10835 "heap() expects one comparator sub",
10836 line,
10837 ));
10838 }
10839 let a0 = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10840 if let Some(sub) = a0.as_code_ref() {
10841 Ok(StrykeValue::heap(Arc::new(Mutex::new(PerlHeap {
10842 items: Vec::new(),
10843 cmp: Arc::clone(&sub),
10844 }))))
10845 } else {
10846 Err(StrykeError::runtime(
10847 "heap() requires a code reference",
10848 line,
10849 ))
10850 }
10851 }
10852 Some(BuiltinId::BarrierNew) => {
10853 let n = args
10854 .first()
10855 .map(|v| v.to_int().max(1) as usize)
10856 .unwrap_or(1);
10857 Ok(StrykeValue::barrier(PerlBarrier(Arc::new(Barrier::new(n)))))
10858 }
10859 Some(BuiltinId::ClusterNew) => {
10860 let items = if args.len() == 1 {
10866 args[0].to_list()
10867 } else {
10868 args.clone()
10869 };
10870 let c = crate::value::RemoteCluster::from_list_args(&items)
10871 .map_err(|msg| StrykeError::runtime(msg, line))?;
10872 Ok(StrykeValue::remote_cluster(std::sync::Arc::new(c)))
10873 }
10874 Some(BuiltinId::Pipeline) => {
10875 let mut items = Vec::new();
10876 for v in args {
10877 if let Some(a) = v.as_array_vec() {
10878 items.extend(a);
10879 } else {
10880 items.push(v);
10881 }
10882 }
10883 Ok(StrykeValue::pipeline(Arc::new(Mutex::new(PipelineInner {
10884 source: items,
10885 ops: Vec::new(),
10886 has_scalar_terminal: false,
10887 par_stream: false,
10888 streaming: false,
10889 streaming_workers: 0,
10890 streaming_buffer: 256,
10891 }))))
10892 }
10893 Some(BuiltinId::ParPipeline) => {
10894 if crate::par_pipeline::is_named_par_pipeline_args(&args) {
10895 return crate::par_pipeline::run_par_pipeline(self.interp, &args, line);
10896 }
10897 let mut items = Vec::new();
10898 for v in args {
10899 if let Some(a) = v.as_array_vec() {
10900 items.extend(a);
10901 } else {
10902 items.push(v);
10903 }
10904 }
10905 Ok(StrykeValue::pipeline(Arc::new(Mutex::new(PipelineInner {
10906 source: items,
10907 ops: Vec::new(),
10908 has_scalar_terminal: false,
10909 par_stream: true,
10910 streaming: false,
10911 streaming_workers: 0,
10912 streaming_buffer: 256,
10913 }))))
10914 }
10915 Some(BuiltinId::ParPipelineStream) => {
10916 if crate::par_pipeline::is_named_par_pipeline_args(&args) {
10917 return crate::par_pipeline::run_par_pipeline_streaming(
10918 self.interp,
10919 &args,
10920 line,
10921 );
10922 }
10923 self.interp.builtin_par_pipeline_stream_new(&args, line)
10924 }
10925 Some(BuiltinId::Each) => {
10926 let _arg = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10927 Ok(StrykeValue::array(vec![]))
10928 }
10929 Some(BuiltinId::Readpipe) => {
10930 let cmd = args
10931 .into_iter()
10932 .next()
10933 .unwrap_or(StrykeValue::UNDEF)
10934 .to_string();
10935 crate::capture::run_readpipe(self.interp, &cmd, line)
10936 }
10937 Some(BuiltinId::ReadpipeList) => {
10938 let cmd = args
10939 .into_iter()
10940 .next()
10941 .unwrap_or(StrykeValue::UNDEF)
10942 .to_string();
10943 let v = crate::capture::run_readpipe(self.interp, &cmd, line)?;
10944 let s = v.to_string();
10945 if s.is_empty() {
10946 return Ok(StrykeValue::array(Vec::new()));
10947 }
10948 let mut lines = Vec::new();
10949 let mut buf = String::new();
10950 for c in s.chars() {
10951 buf.push(c);
10952 if c == '\n' {
10953 lines.push(StrykeValue::string(std::mem::take(&mut buf)));
10954 }
10955 }
10956 if !buf.is_empty() {
10957 lines.push(StrykeValue::string(buf));
10958 }
10959 Ok(StrykeValue::array(lines))
10960 }
10961 Some(BuiltinId::Eval) => {
10962 let arg = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10963 self.interp.eval_nesting += 1;
10964 let out = if let Some(sub) = arg.as_code_ref() {
10965 match self.interp.exec_block(&sub.body) {
10966 Ok(v) => {
10967 self.interp.clear_eval_error();
10968 Ok(v)
10969 }
10970 Err(crate::vm_helper::FlowOrError::Error(e)) => {
10971 self.interp.set_eval_error_from_perl_error(&e);
10972 Ok(StrykeValue::UNDEF)
10973 }
10974 Err(crate::vm_helper::FlowOrError::Flow(_)) => {
10975 self.interp.clear_eval_error();
10976 Ok(StrykeValue::UNDEF)
10977 }
10978 }
10979 } else {
10980 let code = arg.to_string();
10981 match crate::parse_and_run_string(&code, self.interp) {
10982 Ok(v) => {
10983 self.interp.clear_eval_error();
10984 Ok(v)
10985 }
10986 Err(e) => {
10987 self.interp.set_eval_error_from_perl_error(&e);
10988 Ok(StrykeValue::UNDEF)
10989 }
10990 }
10991 };
10992 self.interp.eval_nesting -= 1;
10993 out
10994 }
10995 Some(BuiltinId::Do) => {
10996 let filename = args
10997 .into_iter()
10998 .next()
10999 .unwrap_or(StrykeValue::UNDEF)
11000 .to_string();
11001 match read_file_text_perl_compat(&filename) {
11002 Ok(code) => {
11003 let code = crate::data_section::strip_perl_end_marker(&code);
11004 crate::parse_and_run_string_in_file(code, self.interp, &filename)
11005 .or(Ok(StrykeValue::UNDEF))
11006 }
11007 Err(_) => Ok(StrykeValue::UNDEF),
11008 }
11009 }
11010 Some(BuiltinId::Require) => {
11011 let name = args
11012 .into_iter()
11013 .next()
11014 .unwrap_or(StrykeValue::UNDEF)
11015 .to_string();
11016 self.interp.require_execute(&name, line)
11017 }
11018 Some(BuiltinId::Bless) => {
11019 let ref_val = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
11020 let class = args
11021 .get(1)
11022 .map(|v| v.to_string())
11023 .unwrap_or_else(|| self.interp.scope.get_scalar("__PACKAGE__").to_string());
11024 Ok(StrykeValue::blessed(Arc::new(
11025 crate::value::BlessedRef::new_blessed(class, ref_val),
11026 )))
11027 }
11028 Some(BuiltinId::Caller) => {
11029 let sub_name = self
11033 .interp
11034 .current_sub_stack
11035 .last()
11036 .map(|s| StrykeValue::string(s.name.clone()))
11037 .unwrap_or(StrykeValue::UNDEF);
11038 let pkg = self.interp.current_package();
11039 Ok(StrykeValue::array(vec![
11040 StrykeValue::string(pkg),
11041 StrykeValue::string(self.interp.file.clone()),
11042 StrykeValue::integer(line as i64),
11043 sub_name,
11044 ]))
11045 }
11046 Some(BuiltinId::PMap)
11048 | Some(BuiltinId::PGrep)
11049 | Some(BuiltinId::PFor)
11050 | Some(BuiltinId::PSort)
11051 | Some(BuiltinId::Fan)
11052 | Some(BuiltinId::MapBlock)
11053 | Some(BuiltinId::GrepBlock)
11054 | Some(BuiltinId::SortBlock)
11055 | Some(BuiltinId::Sort) => Ok(StrykeValue::UNDEF),
11056 _ => Err(StrykeError::runtime(
11057 format!("Unimplemented builtin {:?}", bid),
11058 line,
11059 )),
11060 }
11061 }
11062}
11063
11064#[inline]
11066fn both_non_numeric_strings(a: &StrykeValue, b: &StrykeValue) -> bool {
11072 if !a.is_string_like() || !b.is_string_like() {
11073 return false;
11074 }
11075 let sa = a.to_string();
11076 let sb = b.to_string();
11077 !looks_numeric(&sa) && !looks_numeric(&sb)
11078}
11079
11080#[inline]
11081fn looks_numeric(s: &str) -> bool {
11082 let t = s.trim();
11083 if t.is_empty() {
11084 return false;
11085 }
11086 t.parse::<f64>().is_ok()
11087}
11088
11089fn int_cmp(
11090 a: &StrykeValue,
11091 b: &StrykeValue,
11092 int_op: fn(&i64, &i64) -> bool,
11093 float_op: fn(f64, f64) -> bool,
11094) -> StrykeValue {
11095 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
11096 StrykeValue::integer(if int_op(&x, &y) { 1 } else { 0 })
11097 } else {
11098 StrykeValue::integer(if float_op(a.to_number(), b.to_number()) {
11099 1
11100 } else {
11101 0
11102 })
11103 }
11104}
11105
11106#[no_mangle]
11112pub unsafe extern "C" fn stryke_jit_concat_vm(vm: *mut std::ffi::c_void, a: i64, b: i64) -> i64 {
11113 let vm: &mut VM<'static> = unsafe { &mut *(vm as *mut VM<'static>) };
11114 let pa = StrykeValue::from_raw_bits(crate::jit::perl_value_bits_from_jit_string_operand(a));
11115 let pb = StrykeValue::from_raw_bits(crate::jit::perl_value_bits_from_jit_string_operand(b));
11116 match vm.concat_stack_values(pa, pb) {
11117 Ok(pv) => pv.raw_bits() as i64,
11118 Err(_) => StrykeValue::UNDEF.raw_bits() as i64,
11119 }
11120}
11121
11122#[no_mangle]
11130pub unsafe extern "C" fn stryke_jit_call_sub(
11131 vm: *mut std::ffi::c_void,
11132 sub_ip: i64,
11133 argc: i64,
11134 wa: i64,
11135 a0: i64,
11136 a1: i64,
11137 a2: i64,
11138 a3: i64,
11139 a4: i64,
11140 a5: i64,
11141 a6: i64,
11142 a7: i64,
11143) -> i64 {
11144 let vm: &mut VM<'static> = unsafe { &mut *(vm as *mut VM<'static>) };
11145 let want = WantarrayCtx::from_byte(wa as u8);
11146 if want != WantarrayCtx::Scalar {
11147 return StrykeValue::UNDEF.raw_bits() as i64;
11148 }
11149 let argc = argc.clamp(0, 8) as usize;
11150 let args = [a0, a1, a2, a3, a4, a5, a6, a7];
11151 let args = &args[..argc];
11152 match vm.jit_trampoline_run_sub(sub_ip as usize, want, args) {
11153 Ok(pv) => {
11154 if let Some(n) = pv.as_integer() {
11155 n
11156 } else {
11157 pv.raw_bits() as i64
11158 }
11159 }
11160 Err(_) => StrykeValue::UNDEF.raw_bits() as i64,
11161 }
11162}
11163
11164#[cfg(test)]
11165mod tests {
11166 use super::*;
11167 use crate::bytecode::{Chunk, Op};
11168 use crate::value::StrykeValue;
11169
11170 fn run_chunk(chunk: &Chunk) -> StrykeResult<StrykeValue> {
11171 let mut interp = VMHelper::new();
11172 let mut vm = VM::new(chunk, &mut interp);
11173 vm.execute()
11174 }
11175
11176 fn block_jit_sum_chunk(limit: i64) -> Chunk {
11178 let mut c = Chunk::new();
11179 let ni = c.intern_name("i");
11180 let ns = c.intern_name("sum");
11181 c.emit(Op::LoadInt(0), 1);
11182 c.emit(Op::DeclareScalarSlot(0, ni), 1);
11183 c.emit(Op::LoadInt(0), 1);
11184 c.emit(Op::DeclareScalarSlot(1, ns), 1);
11185 c.emit(Op::GetScalarSlot(0), 1);
11186 c.emit(Op::LoadInt(limit), 1);
11187 c.emit(Op::NumLt, 1);
11188 c.emit(Op::JumpIfFalse(15), 1);
11189 c.emit(Op::GetScalarSlot(1), 1);
11190 c.emit(Op::GetScalarSlot(0), 1);
11191 c.emit(Op::Add, 1);
11192 c.emit(Op::SetScalarSlot(1), 1);
11193 c.emit(Op::PostIncSlot(0), 1);
11194 c.emit(Op::Pop, 1);
11195 c.emit(Op::Jump(4), 1);
11196 c.emit(Op::GetScalarSlot(1), 1);
11197 c.emit(Op::Halt, 1);
11198 c
11199 }
11200
11201 #[test]
11202 fn jit_disabled_same_result_as_jit_block_loop() {
11203 let limit = 500i64;
11204 let chunk = block_jit_sum_chunk(limit);
11205 let expect = limit * (limit - 1) / 2;
11206
11207 let mut interp_on = VMHelper::new();
11208 let mut vm_on = VM::new(&chunk, &mut interp_on);
11209 assert_eq!(vm_on.execute().expect("vm").to_int(), expect);
11210
11211 let mut interp_off = VMHelper::new();
11212 let mut vm_off = VM::new(&chunk, &mut interp_off);
11213 vm_off.set_jit_enabled(false);
11214 assert_eq!(vm_off.execute().expect("vm").to_int(), expect);
11215 }
11216
11217 #[test]
11218 fn vm_add_two_integers() {
11219 let mut c = Chunk::new();
11220 c.emit(Op::LoadInt(2), 1);
11221 c.emit(Op::LoadInt(3), 1);
11222 c.emit(Op::Add, 1);
11223 c.emit(Op::Halt, 1);
11224 let v = run_chunk(&c).expect("vm");
11225 assert_eq!(v.to_int(), 5);
11226 }
11227
11228 #[test]
11229 fn vm_sub_mul_div() {
11230 let mut c = Chunk::new();
11231 c.emit(Op::LoadInt(10), 1);
11232 c.emit(Op::LoadInt(3), 1);
11233 c.emit(Op::Sub, 1);
11234 c.emit(Op::Halt, 1);
11235 assert_eq!(run_chunk(&c).expect("vm").to_int(), 7);
11236
11237 let mut c = Chunk::new();
11238 c.emit(Op::LoadInt(6), 1);
11239 c.emit(Op::LoadInt(7), 1);
11240 c.emit(Op::Mul, 1);
11241 c.emit(Op::Halt, 1);
11242 assert_eq!(run_chunk(&c).expect("vm").to_int(), 42);
11243
11244 let mut c = Chunk::new();
11245 c.emit(Op::LoadInt(20), 1);
11246 c.emit(Op::LoadInt(4), 1);
11247 c.emit(Op::Div, 1);
11248 c.emit(Op::Halt, 1);
11249 assert_eq!(run_chunk(&c).expect("vm").to_int(), 5);
11250 }
11251
11252 #[test]
11253 fn vm_mod_and_pow() {
11254 let mut c = Chunk::new();
11255 c.emit(Op::LoadInt(17), 1);
11256 c.emit(Op::LoadInt(5), 1);
11257 c.emit(Op::Mod, 1);
11258 c.emit(Op::Halt, 1);
11259 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11260
11261 let mut c = Chunk::new();
11262 c.emit(Op::LoadInt(2), 1);
11263 c.emit(Op::LoadInt(3), 1);
11264 c.emit(Op::Pow, 1);
11265 c.emit(Op::Halt, 1);
11266 assert_eq!(run_chunk(&c).expect("vm").to_int(), 8);
11267 }
11268
11269 #[test]
11270 fn vm_negate() {
11271 let mut c = Chunk::new();
11272 c.emit(Op::LoadInt(7), 1);
11273 c.emit(Op::Negate, 1);
11274 c.emit(Op::Halt, 1);
11275 assert_eq!(run_chunk(&c).expect("vm").to_int(), -7);
11276 }
11277
11278 #[test]
11279 fn vm_dup_and_pop() {
11280 let mut c = Chunk::new();
11281 c.emit(Op::LoadInt(1), 1);
11282 c.emit(Op::Dup, 1);
11283 c.emit(Op::Add, 1);
11284 c.emit(Op::Halt, 1);
11285 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11286
11287 let mut c = Chunk::new();
11288 c.emit(Op::LoadInt(1), 1);
11289 c.emit(Op::LoadInt(2), 1);
11290 c.emit(Op::Pop, 1);
11291 c.emit(Op::Halt, 1);
11292 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11293 }
11294
11295 #[test]
11296 fn vm_set_get_scalar() {
11297 let mut c = Chunk::new();
11298 let i = c.intern_name("v");
11299 c.emit(Op::LoadInt(99), 1);
11300 c.emit(Op::SetScalar(i), 1);
11301 c.emit(Op::GetScalar(i), 1);
11302 c.emit(Op::Halt, 1);
11303 assert_eq!(run_chunk(&c).expect("vm").to_int(), 99);
11304 }
11305
11306 #[test]
11307 fn vm_scalar_plain_roundtrip_and_keep() {
11308 let mut c = Chunk::new();
11309 let i = c.intern_name("plainvar");
11310 c.emit(Op::LoadInt(99), 1);
11311 c.emit(Op::SetScalarPlain(i), 1);
11312 c.emit(Op::GetScalarPlain(i), 1);
11313 c.emit(Op::Halt, 1);
11314 assert_eq!(run_chunk(&c).expect("vm").to_int(), 99);
11315
11316 let mut c = Chunk::new();
11317 let k = c.intern_name("keepme");
11318 c.emit(Op::LoadInt(5), 1);
11319 c.emit(Op::SetScalarKeepPlain(k), 1);
11320 c.emit(Op::Halt, 1);
11321 assert_eq!(run_chunk(&c).expect("vm").to_int(), 5);
11322 }
11323
11324 #[test]
11325 fn vm_get_scalar_plain_skips_special_global_zero() {
11326 let mut c = Chunk::new();
11327 let idx = c.intern_name("0");
11328 c.emit(Op::GetScalar(idx), 1);
11329 c.emit(Op::Halt, 1);
11330 assert_eq!(run_chunk(&c).expect("vm").to_string(), "stryke");
11331
11332 let mut c = Chunk::new();
11333 let idx = c.intern_name("0");
11334 c.emit(Op::GetScalarPlain(idx), 1);
11335 c.emit(Op::Halt, 1);
11336 assert!(run_chunk(&c).expect("vm").is_undef());
11337 }
11338
11339 #[test]
11340 fn vm_slot_pre_post_inc_dec() {
11341 let mut c = Chunk::new();
11342 c.emit(Op::LoadInt(10), 1);
11343 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11344 c.emit(Op::PostIncSlot(0), 1);
11345 c.emit(Op::Pop, 1);
11346 c.emit(Op::GetScalarSlot(0), 1);
11347 c.emit(Op::Halt, 1);
11348 assert_eq!(run_chunk(&c).expect("vm").to_int(), 11);
11349
11350 let mut c = Chunk::new();
11351 c.emit(Op::LoadInt(0), 1);
11352 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11353 c.emit(Op::PreIncSlot(0), 1);
11354 c.emit(Op::Halt, 1);
11355 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11356
11357 let mut c = Chunk::new();
11358 c.emit(Op::LoadInt(5), 1);
11359 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11360 c.emit(Op::PreDecSlot(0), 1);
11361 c.emit(Op::Halt, 1);
11362 assert_eq!(run_chunk(&c).expect("vm").to_int(), 4);
11363
11364 let mut c = Chunk::new();
11365 c.emit(Op::LoadInt(3), 1);
11366 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11367 c.emit(Op::PostDecSlot(0), 1);
11368 c.emit(Op::Pop, 1);
11369 c.emit(Op::GetScalarSlot(0), 1);
11370 c.emit(Op::Halt, 1);
11371 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11372 }
11373
11374 #[test]
11375 fn vm_str_eq_ne_heap_strings() {
11376 let mut c = Chunk::new();
11377 let a = c.add_constant(StrykeValue::string("same".into()));
11378 let b = c.add_constant(StrykeValue::string("same".into()));
11379 c.emit(Op::LoadConst(a), 1);
11380 c.emit(Op::LoadConst(b), 1);
11381 c.emit(Op::StrEq, 1);
11382 c.emit(Op::Halt, 1);
11383 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11384
11385 let mut c = Chunk::new();
11386 let a = c.add_constant(StrykeValue::string("a".into()));
11387 let b = c.add_constant(StrykeValue::string("b".into()));
11388 c.emit(Op::LoadConst(a), 1);
11389 c.emit(Op::LoadConst(b), 1);
11390 c.emit(Op::StrNe, 1);
11391 c.emit(Op::Halt, 1);
11392 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11393 }
11394
11395 #[test]
11396 fn vm_num_eq_ine() {
11397 let mut c = Chunk::new();
11398 c.emit(Op::LoadInt(1), 1);
11399 c.emit(Op::LoadInt(1), 1);
11400 c.emit(Op::NumEq, 1);
11401 c.emit(Op::Halt, 1);
11402 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11403
11404 let mut c = Chunk::new();
11405 c.emit(Op::LoadInt(1), 1);
11406 c.emit(Op::LoadInt(2), 1);
11407 c.emit(Op::NumNe, 1);
11408 c.emit(Op::Halt, 1);
11409 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11410 }
11411
11412 #[test]
11413 fn vm_num_ordering() {
11414 for (a, b, op, want) in [
11415 (1i64, 2i64, Op::NumLt, 1),
11416 (3i64, 2i64, Op::NumGt, 1),
11417 (2i64, 2i64, Op::NumLe, 1),
11418 (2i64, 2i64, Op::NumGe, 1),
11419 ] {
11420 let mut c = Chunk::new();
11421 c.emit(Op::LoadInt(a), 1);
11422 c.emit(Op::LoadInt(b), 1);
11423 c.emit(op, 1);
11424 c.emit(Op::Halt, 1);
11425 assert_eq!(run_chunk(&c).expect("vm").to_int(), want);
11426 }
11427 }
11428
11429 #[test]
11430 fn vm_concat_and_str_cmp() {
11431 let mut c = Chunk::new();
11432 let i1 = c.add_constant(StrykeValue::string("a".into()));
11433 let i2 = c.add_constant(StrykeValue::string("b".into()));
11434 c.emit(Op::LoadConst(i1), 1);
11435 c.emit(Op::LoadConst(i2), 1);
11436 c.emit(Op::Concat, 1);
11437 c.emit(Op::Halt, 1);
11438 assert_eq!(run_chunk(&c).expect("vm").to_string(), "ab");
11439
11440 let mut c = Chunk::new();
11441 let i1 = c.add_constant(StrykeValue::string("a".into()));
11442 let i2 = c.add_constant(StrykeValue::string("b".into()));
11443 c.emit(Op::LoadConst(i1), 1);
11444 c.emit(Op::LoadConst(i2), 1);
11445 c.emit(Op::StrCmp, 1);
11446 c.emit(Op::Halt, 1);
11447 let v = run_chunk(&c).expect("vm");
11448 assert!(v.to_int() < 0);
11449 }
11450
11451 #[test]
11452 fn vm_log_not() {
11453 let mut c = Chunk::new();
11454 c.emit(Op::LoadInt(0), 1);
11455 c.emit(Op::LogNot, 1);
11456 c.emit(Op::Halt, 1);
11457 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11458 }
11459
11460 #[test]
11461 fn vm_bit_and_or_xor_not() {
11462 let mut c = Chunk::new();
11463 c.emit(Op::LoadInt(0b1100), 1);
11464 c.emit(Op::LoadInt(0b1010), 1);
11465 c.emit(Op::BitAnd, 1);
11466 c.emit(Op::Halt, 1);
11467 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b1000);
11468
11469 let mut c = Chunk::new();
11470 c.emit(Op::LoadInt(0b1100), 1);
11471 c.emit(Op::LoadInt(0b1010), 1);
11472 c.emit(Op::BitOr, 1);
11473 c.emit(Op::Halt, 1);
11474 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b1110);
11475
11476 let mut c = Chunk::new();
11477 c.emit(Op::LoadInt(0b1100), 1);
11478 c.emit(Op::LoadInt(0b1010), 1);
11479 c.emit(Op::BitXor, 1);
11480 c.emit(Op::Halt, 1);
11481 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b0110);
11482
11483 let mut c = Chunk::new();
11484 c.emit(Op::LoadInt(0), 1);
11485 c.emit(Op::BitNot, 1);
11486 c.emit(Op::Halt, 1);
11487 assert!((run_chunk(&c).expect("vm").to_int() & 0xFF) != 0);
11488 }
11489
11490 #[test]
11491 fn vm_shl_shr() {
11492 let mut c = Chunk::new();
11493 c.emit(Op::LoadInt(1), 1);
11494 c.emit(Op::LoadInt(3), 1);
11495 c.emit(Op::Shl, 1);
11496 c.emit(Op::Halt, 1);
11497 assert_eq!(run_chunk(&c).expect("vm").to_int(), 8);
11498
11499 let mut c = Chunk::new();
11500 c.emit(Op::LoadInt(16), 1);
11501 c.emit(Op::LoadInt(2), 1);
11502 c.emit(Op::Shr, 1);
11503 c.emit(Op::Halt, 1);
11504 assert_eq!(run_chunk(&c).expect("vm").to_int(), 4);
11505 }
11506
11507 #[test]
11508 fn vm_load_undef_float_constant() {
11509 let mut c = Chunk::new();
11510 c.emit(Op::LoadUndef, 1);
11511 c.emit(Op::Halt, 1);
11512 assert!(run_chunk(&c).expect("vm").is_undef());
11513
11514 let mut c = Chunk::new();
11515 c.emit(Op::LoadFloat(2.5), 1);
11516 c.emit(Op::Halt, 1);
11517 assert!((run_chunk(&c).expect("vm").to_number() - 2.5).abs() < 1e-9);
11518 }
11519
11520 #[test]
11521 fn vm_jump_skips_ops() {
11522 let mut c = Chunk::new();
11523 let j = c.emit(Op::Jump(0), 1);
11524 c.emit(Op::LoadInt(1), 1);
11525 c.emit(Op::LoadInt(2), 1);
11526 c.emit(Op::Add, 1);
11527 c.patch_jump_here(j);
11528 c.emit(Op::LoadInt(40), 1);
11529 c.emit(Op::Halt, 1);
11530 assert_eq!(run_chunk(&c).expect("vm").to_int(), 40);
11531 }
11532
11533 #[test]
11534 fn vm_jump_if_false() {
11535 let mut c = Chunk::new();
11536 c.emit(Op::LoadInt(0), 1);
11537 let j = c.emit(Op::JumpIfFalse(0), 1);
11538 c.emit(Op::LoadInt(1), 1);
11539 c.emit(Op::Halt, 1);
11540 c.patch_jump_here(j);
11541 c.emit(Op::LoadInt(2), 1);
11542 c.emit(Op::Halt, 1);
11543 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11544 }
11545
11546 #[test]
11547 fn vm_call_builtin_defined() {
11548 let mut c = Chunk::new();
11549 c.emit(Op::LoadUndef, 1);
11550 c.emit(Op::CallBuiltin(BuiltinId::Defined as u16, 1), 1);
11551 c.emit(Op::Halt, 1);
11552 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0);
11553 }
11554
11555 #[test]
11556 fn vm_call_builtin_length_string() {
11557 let mut c = Chunk::new();
11558 let idx = c.add_constant(StrykeValue::string("abc".into()));
11559 c.emit(Op::LoadConst(idx), 1);
11560 c.emit(Op::CallBuiltin(BuiltinId::Length as u16, 1), 1);
11561 c.emit(Op::Halt, 1);
11562 assert_eq!(run_chunk(&c).expect("vm").to_int(), 3);
11563 }
11564
11565 #[test]
11566 fn vm_make_array_two() {
11567 let mut c = Chunk::new();
11568 c.emit(Op::LoadInt(1), 1);
11569 c.emit(Op::LoadInt(2), 1);
11570 c.emit(Op::MakeArray(2), 1);
11571 c.emit(Op::Halt, 1);
11572 let v = run_chunk(&c).expect("vm");
11573 let a = v.as_array_vec().expect("array");
11574 assert_eq!(a.len(), 2);
11575 assert_eq!(a[0].to_int(), 1);
11576 assert_eq!(a[1].to_int(), 2);
11577 }
11578
11579 #[test]
11580 fn vm_spaceship() {
11581 let mut c = Chunk::new();
11582 c.emit(Op::LoadInt(1), 1);
11583 c.emit(Op::LoadInt(2), 1);
11584 c.emit(Op::Spaceship, 1);
11585 c.emit(Op::Halt, 1);
11586 assert_eq!(run_chunk(&c).expect("vm").to_int(), -1);
11587 }
11588
11589 #[test]
11590 fn compiled_try_catch_catches_die_via_vm() {
11591 let program = crate::parse(
11592 r#"
11593 try {
11594 die "boom";
11595 } catch ($err) {
11596 42;
11597 }
11598 "#,
11599 )
11600 .expect("parse");
11601 let chunk = crate::compiler::Compiler::new()
11602 .compile_program(&program)
11603 .expect("compile");
11604 let tp = chunk
11605 .ops
11606 .iter()
11607 .position(|o| matches!(o, Op::TryPush { .. }))
11608 .expect("TryPush op");
11609 match &chunk.ops[tp] {
11610 Op::TryPush {
11611 catch_ip, after_ip, ..
11612 } => {
11613 assert_ne!(*catch_ip, 0, "catch_ip must be patched");
11614 assert_ne!(*after_ip, 0, "after_ip must be patched");
11615 }
11616 _ => unreachable!(),
11617 }
11618 let mut interp = VMHelper::new();
11619 let mut vm = VM::new(&chunk, &mut interp);
11620 vm.set_jit_enabled(false);
11621 let v = vm.execute().expect("vm should catch die");
11622 assert_eq!(v.to_int(), 42);
11623 }
11624}