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