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 #[inline]
3478 fn push_binop_with_overload<F>(
3479 &mut self,
3480 op: BinOp,
3481 a: StrykeValue,
3482 b: StrykeValue,
3483 default: F,
3484 ) -> StrykeResult<()>
3485 where
3486 F: FnOnce(&StrykeValue, &StrykeValue) -> StrykeResult<StrykeValue>,
3487 {
3488 let line = self.line();
3489 if let Some(exec_res) = self.interp.try_overload_binop(op, &a, &b, line) {
3490 self.push(vm_interp_result(exec_res, line)?);
3491 } else {
3492 self.push(default(&a, &b)?);
3493 }
3494 Ok(())
3495 }
3496
3497 pub(crate) fn concat_stack_values(
3498 &mut self,
3499 a: StrykeValue,
3500 b: StrykeValue,
3501 ) -> StrykeResult<StrykeValue> {
3502 let line = self.line();
3503 if let Some(exec_res) = self.interp.try_overload_binop(BinOp::Concat, &a, &b, line) {
3504 vm_interp_result(exec_res, line)
3505 } else {
3506 let sa = match self.interp.stringify_value(a, line) {
3507 Ok(s) => s,
3508 Err(FlowOrError::Error(e)) => return Err(e),
3509 Err(FlowOrError::Flow(_)) => {
3510 return Err(StrykeError::runtime(
3511 "concat: unexpected control flow",
3512 line,
3513 ));
3514 }
3515 };
3516 let sb = match self.interp.stringify_value(b, line) {
3517 Ok(s) => s,
3518 Err(FlowOrError::Error(e)) => return Err(e),
3519 Err(FlowOrError::Flow(_)) => {
3520 return Err(StrykeError::runtime(
3521 "concat: unexpected control flow",
3522 line,
3523 ));
3524 }
3525 };
3526 let mut s = sa;
3527 s.push_str(&sb);
3528 Ok(StrykeValue::string(s))
3529 }
3530 }
3531
3532 fn run_main_dispatch_loop(
3533 &mut self,
3534 mut last: StrykeValue,
3535 op_count: &mut u64,
3536 init_dispatch: bool,
3537 ) -> StrykeResult<StrykeValue> {
3538 if init_dispatch {
3539 self.halt = false;
3540 self.exit_main_dispatch = false;
3541 self.exit_main_dispatch_value = None;
3542 }
3543 let ops_ref: &Vec<Op> = &self.ops;
3544 let ops = ops_ref as *const Vec<Op>;
3545 let ops = unsafe { &*ops };
3546 let names_ref: &Vec<String> = &self.names;
3547 let names = names_ref as *const Vec<String>;
3548 let names = unsafe { &*names };
3549 let constants_ref: &Vec<StrykeValue> = &self.constants;
3550 let constants = constants_ref as *const Vec<StrykeValue>;
3551 let constants = unsafe { &*constants };
3552 let len = ops.len();
3553 const MAX_OPS: u64 = 1_000_000_000;
3554 loop {
3555 if self.jit_trampoline_depth > 0 && self.jit_trampoline_out.is_some() {
3556 break;
3557 }
3558 if self.block_region_return.is_some() {
3559 break;
3560 }
3561 if self.block_region_mode && self.ip >= self.block_region_end {
3562 return Err(StrykeError::runtime(
3563 "block bytecode region fell through without BlockReturnValue",
3564 self.line(),
3565 ));
3566 }
3567 if self.ip >= len {
3568 break;
3569 }
3570
3571 if !self.block_region_mode
3572 && self.jit_enabled
3573 && self.sub_entry_at_ip.get(self.ip).copied().unwrap_or(false)
3574 {
3575 let sub_ip = self.ip;
3576 if sub_ip >= self.sub_entry_invoke_count.len() {
3577 self.sub_entry_invoke_count.resize(sub_ip + 1, 0);
3578 }
3579 let c = &mut self.sub_entry_invoke_count[sub_ip];
3580 if *c <= self.jit_sub_invoke_threshold {
3581 *c = c.saturating_add(1);
3582 }
3583 let should_try_jit = *c > self.jit_sub_invoke_threshold
3584 && (!self.sub_jit_skip_linear_test(sub_ip)
3585 || !self.sub_jit_skip_block_test(sub_ip));
3586 if should_try_jit {
3587 if self.try_fusevm_subroutine()? {
3591 continue;
3592 }
3593 if !self.sub_jit_skip_linear_test(sub_ip) && self.try_jit_subroutine_linear()? {
3594 continue;
3595 }
3596 if !self.sub_jit_skip_block_test(sub_ip) && self.try_jit_subroutine_block()? {
3597 continue;
3598 }
3599 }
3600 }
3601
3602 *op_count += 1;
3603 if (*op_count & 0x3FF) == 0 {
3606 crate::perl_signal::poll(self.interp)?;
3607 if *op_count > MAX_OPS {
3608 return Err(StrykeError::runtime(
3609 "VM execution limit exceeded (possible infinite loop)",
3610 self.line(),
3611 ));
3612 }
3613 }
3614
3615 let ip_before = self.ip;
3616 let line = self.lines.get(ip_before).copied().unwrap_or(0);
3617 let op = &ops[self.ip];
3618 self.ip += 1;
3619
3620 if let Some(ref mut dbg) = self.interp.debugger {
3622 if dbg.should_stop(line) {
3623 let call_stack = self.interp.debug_call_stack.clone();
3624 match dbg.prompt(line, &self.interp.scope, &call_stack) {
3625 crate::debugger::DebugAction::Quit => {
3626 return Err(StrykeError::runtime("debugger: quit", line));
3627 }
3628 crate::debugger::DebugAction::Continue => {}
3629 crate::debugger::DebugAction::Prompt => {}
3630 }
3631 }
3632 }
3633
3634 let op_prof_t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
3635 let __op_res: StrykeResult<()> = (|| -> StrykeResult<()> {
3639 match op {
3640 Op::Nop => Ok(()),
3641 Op::LoadInt(n) => {
3643 self.push(StrykeValue::integer(*n));
3644 Ok(())
3645 }
3646 Op::LoadFloat(f) => {
3647 self.push(StrykeValue::float(*f));
3648 Ok(())
3649 }
3650 Op::LoadConst(idx) => {
3651 self.push(self.constant(*idx).clone());
3652 Ok(())
3653 }
3654 Op::LoadUndef => {
3655 self.push(StrykeValue::UNDEF);
3656 Ok(())
3657 }
3658 Op::RuntimeErrorConst(idx) => {
3659 let msg = self.constant(*idx).to_string();
3660 let line = self.line();
3661 Err(crate::error::StrykeError::runtime(msg, line))
3662 }
3663 Op::BarewordRvalue(name_idx) => {
3664 let name = names[*name_idx as usize].clone();
3665 let line = self.line();
3666 let out = vm_interp_result(
3667 self.interp.resolve_bareword_rvalue(
3668 &name,
3669 crate::vm_helper::WantarrayCtx::Scalar,
3670 line,
3671 ),
3672 line,
3673 )?;
3674 self.push(out);
3675 Ok(())
3676 }
3677
3678 Op::Pop => {
3680 let v = self.pop();
3681 if v.is_iterator() {
3683 let iter = v.into_iterator();
3684 while iter.next_item().is_some() {}
3685 }
3686 Ok(())
3687 }
3688 Op::Dup => {
3689 let v = self.peek().dup_stack();
3690 self.push(v);
3691 Ok(())
3692 }
3693 Op::Dup2 => {
3694 let b = self.pop();
3695 let a = self.pop();
3696 self.push(a.dup_stack());
3697 self.push(b.dup_stack());
3698 self.push(a);
3699 self.push(b);
3700 Ok(())
3701 }
3702 Op::Swap => {
3703 let top = self.pop();
3704 let below = self.pop();
3705 self.push(top);
3706 self.push(below);
3707 Ok(())
3708 }
3709 Op::Rot => {
3710 let c = self.pop();
3711 let b = self.pop();
3712 let a = self.pop();
3713 self.push(b);
3714 self.push(c);
3715 self.push(a);
3716 Ok(())
3717 }
3718 Op::ValueScalarContext => {
3719 let v = self.pop();
3720 self.push(v.scalar_context());
3721 Ok(())
3722 }
3723 Op::ListFirst => {
3724 let v = self.pop();
3725 let first = if let Some(arr) = v.as_array_vec() {
3726 arr.first().cloned().unwrap_or(StrykeValue::UNDEF)
3727 } else {
3728 v
3729 };
3730 self.push(first);
3731 Ok(())
3732 }
3733
3734 Op::GetScalar(idx) => {
3736 let n = names[*idx as usize].as_str();
3737 let val = self.interp.get_special_var(n);
3738 self.push(val);
3739 Ok(())
3740 }
3741 Op::GetScalarPlain(idx) => {
3742 let n = names[*idx as usize].as_str();
3743 let val = self.interp.scope.get_scalar(n);
3744 self.push(val);
3745 Ok(())
3746 }
3747 Op::SetScalar(idx) => {
3748 let val = self.pop();
3749 let n = names[*idx as usize].as_str();
3750 self.require_scalar_mutable(n)?;
3751 self.interp.maybe_invalidate_regex_capture_memo(n);
3752 self.interp
3753 .set_special_var(n, &val)
3754 .map_err(|e| e.at_line(self.line()))?;
3755 Ok(())
3756 }
3757 Op::SetScalarPlain(idx) => {
3758 let val = self.pop();
3759 let n = names[*idx as usize].as_str();
3760 self.require_scalar_mutable(n)?;
3761 self.interp.maybe_invalidate_regex_capture_memo(n);
3762 self.interp
3763 .scope
3764 .set_scalar(n, val)
3765 .map_err(|e| e.at_line(self.line()))?;
3766 Ok(())
3767 }
3768 Op::SetScalarKeep(idx) => {
3769 let val = self.peek().dup_stack();
3770 let n = names[*idx as usize].as_str();
3771 self.require_scalar_mutable(n)?;
3772 self.interp.maybe_invalidate_regex_capture_memo(n);
3773 self.interp
3774 .set_special_var(n, &val)
3775 .map_err(|e| e.at_line(self.line()))?;
3776 Ok(())
3777 }
3778 Op::SetScalarKeepPlain(idx) => {
3779 let val = self.peek().dup_stack();
3780 let n = names[*idx as usize].as_str();
3781 self.require_scalar_mutable(n)?;
3782 self.interp.maybe_invalidate_regex_capture_memo(n);
3783 self.interp
3784 .scope
3785 .set_scalar(n, val)
3786 .map_err(|e| e.at_line(self.line()))?;
3787 Ok(())
3788 }
3789 Op::DeclareScalar(idx) => {
3790 let val = self.pop();
3791 let n = names[*idx as usize].as_str();
3792 self.interp
3793 .scope
3794 .declare_scalar_frozen(n, val, false, None)
3795 .map_err(|e| e.at_line(self.line()))?;
3796 Ok(())
3797 }
3798 Op::DeclareScalarFrozen(idx) => {
3799 let val = self.pop();
3800 let n = names[*idx as usize].as_str();
3801 self.interp
3802 .scope
3803 .declare_scalar_frozen(n, val, true, None)
3804 .map_err(|e| e.at_line(self.line()))?;
3805 Ok(())
3806 }
3807 Op::DeclareScalarTyped(idx, tyb) => {
3808 let val = self.pop();
3809 let n = names[*idx as usize].as_str();
3810 let ty = PerlTypeName::from_byte(*tyb).ok_or_else(|| {
3811 StrykeError::runtime(
3812 format!("invalid typed scalar type byte {}", tyb),
3813 self.line(),
3814 )
3815 })?;
3816 self.interp
3817 .scope
3818 .declare_scalar_frozen(n, val, false, Some(ty))
3819 .map_err(|e| e.at_line(self.line()))?;
3820 Ok(())
3821 }
3822 Op::DeclareScalarTypedFrozen(idx, tyb) => {
3823 let val = self.pop();
3824 let n = names[*idx as usize].as_str();
3825 let ty = PerlTypeName::from_byte(*tyb).ok_or_else(|| {
3826 StrykeError::runtime(
3827 format!("invalid typed scalar type byte {}", tyb),
3828 self.line(),
3829 )
3830 })?;
3831 self.interp
3832 .scope
3833 .declare_scalar_frozen(n, val, true, Some(ty))
3834 .map_err(|e| e.at_line(self.line()))?;
3835 Ok(())
3836 }
3837 Op::DeclareScalarTypedUser(name_idx, type_idx, flag) => {
3838 let val = self.pop();
3839 let n = names[*name_idx as usize].as_str();
3840 let type_name = names[*type_idx as usize].clone();
3841 let is_enum = (flag & 0b01) != 0;
3842 let is_frozen = (flag & 0b10) != 0;
3843 let ty = if is_enum {
3844 PerlTypeName::Enum(type_name)
3845 } else {
3846 PerlTypeName::Struct(type_name)
3851 };
3852 self.interp
3853 .scope
3854 .declare_scalar_frozen(n, val, is_frozen, Some(ty))
3855 .map_err(|e| e.at_line(self.line()))?;
3856 Ok(())
3857 }
3858
3859 Op::DeclareStateScalar(idx) => {
3861 let init_val = self.pop();
3862 let n = names[*idx as usize].as_str();
3863 let state_key = format!("{}:{}", self.line(), n);
3865 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3866 prev.clone()
3867 } else {
3868 self.interp
3869 .state_vars
3870 .insert(state_key.clone(), init_val.clone());
3871 init_val
3872 };
3873 self.interp
3874 .scope
3875 .declare_scalar_frozen(n, val, false, None)
3876 .map_err(|e| e.at_line(self.line()))?;
3877 if let Some(frame) = self.interp.state_bindings_stack.last_mut() {
3879 frame.push((n.to_string(), state_key));
3880 }
3881 Ok(())
3882 }
3883 Op::DeclareStateArray(idx) => {
3884 let init_val = self.pop();
3885 let n = names[*idx as usize].as_str();
3886 let state_key = format!("{}:{}", self.line(), n);
3887 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3888 prev.clone()
3889 } else {
3890 self.interp
3891 .state_vars
3892 .insert(state_key.clone(), init_val.clone());
3893 init_val
3894 };
3895 self.interp.scope.declare_array(n, val.to_list());
3896 Ok(())
3897 }
3898 Op::DeclareStateHash(idx) => {
3899 let init_val = self.pop();
3900 let n = names[*idx as usize].as_str();
3901 let state_key = format!("{}:{}", self.line(), n);
3902 let val = if let Some(prev) = self.interp.state_vars.get(&state_key) {
3903 prev.clone()
3904 } else {
3905 self.interp
3906 .state_vars
3907 .insert(state_key.clone(), init_val.clone());
3908 init_val
3909 };
3910 let items = val.to_list();
3911 let mut map = IndexMap::new();
3912 let mut i = 0;
3913 while i + 1 < items.len() {
3914 map.insert(items[i].to_string(), items[i + 1].clone());
3915 i += 2;
3916 }
3917 self.interp.scope.declare_hash(n, map);
3918 Ok(())
3919 }
3920
3921 Op::GetArray(idx) => {
3923 let n = names[*idx as usize].as_str();
3924 let arr = self.interp.scope.get_array(n);
3925 self.push(StrykeValue::array(arr));
3926 Ok(())
3927 }
3928 Op::SetArray(idx) => {
3929 let val = self.pop();
3930 let n = names[*idx as usize].as_str();
3931 self.require_array_mutable(n)?;
3932 self.interp
3933 .scope
3934 .set_array(n, val.to_list())
3935 .map_err(|e| e.at_line(self.line()))?;
3936 Ok(())
3937 }
3938 Op::DeclareArray(idx) => {
3939 let val = self.pop();
3940 let n = names[*idx as usize].as_str();
3941 self.interp.scope.declare_array(n, val.to_list());
3942 Ok(())
3943 }
3944 Op::DeclareArrayFrozen(idx) => {
3945 let val = self.pop();
3946 let n = names[*idx as usize].as_str();
3947 self.interp
3948 .scope
3949 .declare_array_frozen(n, val.to_list(), true);
3950 Ok(())
3951 }
3952 Op::GetArrayElem(idx) => {
3953 let index = self.pop().to_int();
3954 let n = names[*idx as usize].as_str();
3955 if let Some(real) = n.strip_prefix("__topicstr__") {
3959 let s = self.interp.scope.get_scalar(real).to_string();
3960 let cnt = s.chars().count() as i64;
3961 let i = if index < 0 { index + cnt } else { index };
3962 let v = if i >= 0 && i < cnt {
3963 s.chars()
3964 .nth(i as usize)
3965 .map(|c| StrykeValue::string(c.to_string()))
3966 .unwrap_or(StrykeValue::UNDEF)
3967 } else {
3968 StrykeValue::UNDEF
3969 };
3970 self.push(v);
3971 return Ok(());
3972 }
3973 if !crate::compat_mode() && self.interp.scope.scalar_binding_exists(n) {
3982 let prefer_scalar = self.interp.scope.get_array(n).is_empty();
3983 if prefer_scalar {
3984 let s = self.interp.scope.get_scalar(n).to_string();
3985 if !s.is_empty() {
3986 let cnt = s.chars().count() as i64;
3987 let i = if index < 0 { index + cnt } else { index };
3988 let v = if i >= 0 && i < cnt {
3989 s.chars()
3990 .nth(i as usize)
3991 .map(|c| StrykeValue::string(c.to_string()))
3992 .unwrap_or(StrykeValue::UNDEF)
3993 } else {
3994 StrykeValue::UNDEF
3995 };
3996 self.push(v);
3997 return Ok(());
3998 }
3999 }
4000 }
4001 let val = self.interp.scope.get_array_element(n, index);
4002 self.push(val);
4003 Ok(())
4004 }
4005 Op::ExistsArrayElem(idx) => {
4006 let index = self.pop().to_int();
4007 let n = names[*idx as usize].as_str();
4008 let yes = self.interp.scope.exists_array_element(n, index);
4009 self.push(StrykeValue::integer(if yes { 1 } else { 0 }));
4010 Ok(())
4011 }
4012 Op::DeleteArrayElem(idx) => {
4013 let index = self.pop().to_int();
4014 let n = names[*idx as usize].as_str();
4015 self.require_array_mutable(n)?;
4016 let v = self
4017 .interp
4018 .scope
4019 .delete_array_element(n, index)
4020 .map_err(|e| e.at_line(self.line()))?;
4021 self.push(v);
4022 Ok(())
4023 }
4024 Op::SetArrayElem(idx) => {
4025 let index = self.pop().to_int();
4026 let val = self.pop();
4027 let n = names[*idx as usize].as_str();
4028 self.require_array_mutable(n)?;
4029 self.interp
4030 .scope
4031 .set_array_element(n, index, val)
4032 .map_err(|e| e.at_line(self.line()))?;
4033 Ok(())
4034 }
4035 Op::SetArrayElemKeep(idx) => {
4036 let index = self.pop().to_int();
4037 let val = self.pop();
4038 let val_keep = val.clone();
4039 let n = names[*idx as usize].as_str();
4040 self.require_array_mutable(n)?;
4041 let line = self.line();
4042 self.interp
4043 .scope
4044 .set_array_element(n, index, val)
4045 .map_err(|e| e.at_line(line))?;
4046 self.push(val_keep);
4047 Ok(())
4048 }
4049 Op::PushArray(idx) => {
4050 let val = self.pop();
4051 let n = names[*idx as usize].as_str();
4052 self.require_array_mutable(n)?;
4053 let line = self.line();
4054 if let Some(items) = val.as_array_vec() {
4055 for item in items {
4056 self.interp
4057 .scope
4058 .push_to_array(n, item)
4059 .map_err(|e| e.at_line(line))?;
4060 }
4061 } else {
4062 self.interp
4063 .scope
4064 .push_to_array(n, val)
4065 .map_err(|e| e.at_line(line))?;
4066 }
4067 Ok(())
4068 }
4069 Op::PopArray(idx) => {
4070 let n = names[*idx as usize].as_str();
4071 self.require_array_mutable(n)?;
4072 let line = self.line();
4073 let val = self
4074 .interp
4075 .scope
4076 .pop_from_array(n)
4077 .map_err(|e| e.at_line(line))?;
4078 self.push(val);
4079 Ok(())
4080 }
4081 Op::ShiftArray(idx) => {
4082 let n = names[*idx as usize].as_str();
4083 self.require_array_mutable(n)?;
4084 let line = self.line();
4085 let val = self
4086 .interp
4087 .scope
4088 .shift_from_array(n)
4089 .map_err(|e| e.at_line(line))?;
4090 self.push(val);
4091 Ok(())
4092 }
4093 Op::PushArrayDeref => {
4094 let val = self.pop();
4095 let r = self.pop();
4096 let line = self.line();
4097 vm_interp_result(
4098 self.interp
4099 .push_array_deref_value(r.clone(), val, line)
4100 .map(|_| StrykeValue::UNDEF),
4101 line,
4102 )?;
4103 self.push(r);
4104 Ok(())
4105 }
4106 Op::ArrayDerefLen => {
4107 let r = self.pop();
4108 let line = self.line();
4109 let n = match self.interp.array_deref_len(r, line) {
4110 Ok(n) => n,
4111 Err(FlowOrError::Error(e)) => return Err(e),
4112 Err(FlowOrError::Flow(_)) => {
4113 return Err(StrykeError::runtime(
4114 "unexpected flow in tree-assisted opcode",
4115 line,
4116 ));
4117 }
4118 };
4119 self.push(StrykeValue::integer(n));
4120 Ok(())
4121 }
4122 Op::PopArrayDeref => {
4123 let r = self.pop();
4124 let line = self.line();
4125 let v = vm_interp_result(self.interp.pop_array_deref(r, line), line)?;
4126 self.push(v);
4127 Ok(())
4128 }
4129 Op::ShiftArrayDeref => {
4130 let r = self.pop();
4131 let line = self.line();
4132 let v = vm_interp_result(self.interp.shift_array_deref(r, line), line)?;
4133 self.push(v);
4134 Ok(())
4135 }
4136 Op::UnshiftArrayDeref(n_extra) => {
4137 let n = *n_extra as usize;
4138 let mut vals: Vec<StrykeValue> = Vec::with_capacity(n);
4139 for _ in 0..n {
4140 vals.push(self.pop());
4141 }
4142 vals.reverse();
4143 let r = self.pop();
4144 let line = self.line();
4145 let len = match self.interp.unshift_array_deref_multi(r, vals, line) {
4146 Ok(n) => n,
4147 Err(FlowOrError::Error(e)) => return Err(e),
4148 Err(FlowOrError::Flow(_)) => {
4149 return Err(StrykeError::runtime(
4150 "unexpected flow in tree-assisted opcode",
4151 line,
4152 ));
4153 }
4154 };
4155 self.push(StrykeValue::integer(len));
4156 Ok(())
4157 }
4158 Op::SpliceArrayDeref(n_rep) => {
4159 let n = *n_rep as usize;
4160 let mut rep_vals: Vec<StrykeValue> = Vec::with_capacity(n);
4161 for _ in 0..n {
4162 rep_vals.push(self.pop());
4163 }
4164 rep_vals.reverse();
4165 let length_val = self.pop();
4166 let offset_val = self.pop();
4167 let aref = self.pop();
4168 let line = self.line();
4169 let v = vm_interp_result(
4170 self.interp
4171 .splice_array_deref(aref, offset_val, length_val, rep_vals, line),
4172 line,
4173 )?;
4174 self.push(v);
4175 Ok(())
4176 }
4177 Op::ArrayLen(idx) => {
4178 let len = self.interp.scope.array_len(&self.names[*idx as usize]);
4179 self.push(StrykeValue::integer(len as i64));
4180 Ok(())
4181 }
4182 Op::ArraySlicePart(idx) => {
4183 let spec = self.pop();
4184 let n = names[*idx as usize].as_str();
4185 let mut out = Vec::new();
4186 if let Some(indices) = spec.as_array_vec() {
4187 for pv in indices {
4188 out.push(self.interp.scope.get_array_element(n, pv.to_int()));
4189 }
4190 } else {
4191 out.push(self.interp.scope.get_array_element(n, spec.to_int()));
4192 }
4193 self.push(StrykeValue::array(out));
4194 Ok(())
4195 }
4196 Op::GetArrayFromIndex(idx, start) => {
4197 let n = names[*idx as usize].as_str();
4198 let arr = self.interp.scope.get_array(n);
4199 let start = *start as usize;
4200 let out: Vec<StrykeValue> = if start >= arr.len() {
4201 Vec::new()
4202 } else {
4203 arr[start..].to_vec()
4204 };
4205 self.push(StrykeValue::array(out));
4206 Ok(())
4207 }
4208 Op::ArrayConcatTwo => {
4209 let b = self.pop();
4210 let a = self.pop();
4211 let mut av = a.as_array_vec().unwrap_or_else(|| vec![a]);
4212 let bv = b.as_array_vec().unwrap_or_else(|| vec![b]);
4213 av.extend(bv);
4214 self.push(StrykeValue::array(av));
4215 Ok(())
4216 }
4217
4218 Op::GetHash(idx) => {
4220 let n = names[*idx as usize].as_str();
4221 self.interp.touch_env_hash(n);
4222 let h = self.interp.scope.get_hash(n);
4223 self.push(StrykeValue::hash(h));
4224 Ok(())
4225 }
4226 Op::SetHash(idx) => {
4227 let val = self.pop();
4228 let items = val.to_list();
4229 let mut map = IndexMap::new();
4230 let mut i = 0;
4231 while i + 1 < items.len() {
4232 map.insert(items[i].to_string(), items[i + 1].clone());
4233 i += 2;
4234 }
4235 let n = names[*idx as usize].as_str();
4236 self.require_hash_mutable(n)?;
4237 self.interp
4238 .scope
4239 .set_hash(n, map)
4240 .map_err(|e| e.at_line(self.line()))?;
4241 Ok(())
4242 }
4243 Op::DeclareHash(idx) => {
4244 let val = self.pop();
4245 let n = names[*idx as usize].as_str();
4246 if val.is_undef() && n.contains("::") {
4261 let existing = self.interp.scope.get_hash(n);
4262 self.interp.scope.declare_hash(n, existing);
4263 } else {
4264 let items = val.to_list();
4265 let mut map = IndexMap::new();
4266 let mut i = 0;
4267 while i + 1 < items.len() {
4268 map.insert(items[i].to_string(), items[i + 1].clone());
4269 i += 2;
4270 }
4271 self.interp.scope.declare_hash(n, map);
4272 }
4273 Ok(())
4274 }
4275 Op::DeclareHashFrozen(idx) => {
4276 let val = self.pop();
4277 let items = val.to_list();
4278 let mut map = IndexMap::new();
4279 let mut i = 0;
4280 while i + 1 < items.len() {
4281 map.insert(items[i].to_string(), items[i + 1].clone());
4282 i += 2;
4283 }
4284 let n = names[*idx as usize].as_str();
4285 self.interp.scope.declare_hash_frozen(n, map, true);
4286 Ok(())
4287 }
4288 Op::LocalDeclareScalar(idx) => {
4289 let val = self.pop();
4290 let n = names[*idx as usize].as_str();
4291 if VMHelper::is_special_scalar_name_for_set(n) {
4296 let old = self.interp.get_special_var(n);
4297 if let Some(frame) = self.interp.special_var_restore_frames.last_mut() {
4298 frame.push((n.to_string(), old));
4299 }
4300 let line = self.line();
4301 self.interp
4302 .set_special_var(n, &val)
4303 .map_err(|e| e.at_line(line))?;
4304 }
4305 self.interp
4306 .scope
4307 .local_set_scalar(n, val.clone())
4308 .map_err(|e| e.at_line(self.line()))?;
4309 self.push(val);
4310 Ok(())
4311 }
4312 Op::LocalDeclareArray(idx) => {
4313 let val = self.pop();
4314 let n = names[*idx as usize].as_str();
4315 self.interp
4316 .scope
4317 .local_set_array(n, val.to_list())
4318 .map_err(|e| e.at_line(self.line()))?;
4319 self.push(val);
4320 Ok(())
4321 }
4322 Op::LocalDeclareHash(idx) => {
4323 let val = self.pop();
4324 let items = val.to_list();
4325 let mut map = IndexMap::new();
4326 let mut i = 0;
4327 while i + 1 < items.len() {
4328 map.insert(items[i].to_string(), items[i + 1].clone());
4329 i += 2;
4330 }
4331 let n = names[*idx as usize].as_str();
4332 self.interp.touch_env_hash(n);
4333 self.interp
4334 .scope
4335 .local_set_hash(n, map)
4336 .map_err(|e| e.at_line(self.line()))?;
4337 self.push(val);
4338 Ok(())
4339 }
4340 Op::LocalDeclareHashElement(idx) => {
4341 let key = self.pop().to_string();
4342 let val = self.pop();
4343 let n = names[*idx as usize].as_str();
4344 self.interp.touch_env_hash(n);
4345 self.interp
4346 .scope
4347 .local_set_hash_element(n, key.as_str(), val.clone())
4348 .map_err(|e| e.at_line(self.line()))?;
4349 self.push(val);
4350 Ok(())
4351 }
4352 Op::LocalDeclareArrayElement(idx) => {
4353 let index = self.pop().to_int();
4354 let val = self.pop();
4355 let n = names[*idx as usize].as_str();
4356 self.require_array_mutable(n)?;
4357 self.interp
4358 .scope
4359 .local_set_array_element(n, index, val.clone())
4360 .map_err(|e| e.at_line(self.line()))?;
4361 self.push(val);
4362 Ok(())
4363 }
4364 Op::LocalDeclareTypeglob(lhs_i, rhs_opt) => {
4365 let lhs = names[*lhs_i as usize].as_str();
4366 let rhs = rhs_opt.map(|i| names[i as usize].as_str());
4367 let line = self.line();
4368 self.interp
4369 .local_declare_typeglob(lhs, rhs, line)
4370 .map_err(|e| e.at_line(line))?;
4371 Ok(())
4372 }
4373 Op::LocalDeclareTypeglobDynamic(rhs_opt) => {
4374 let lhs = self.pop().to_string();
4375 let rhs = rhs_opt.map(|i| names[i as usize].as_str());
4376 let line = self.line();
4377 self.interp
4378 .local_declare_typeglob(lhs.as_str(), rhs, line)
4379 .map_err(|e| e.at_line(line))?;
4380 Ok(())
4381 }
4382 Op::GetHashElem(idx) => {
4383 let key = self.pop().to_string();
4384 let n = names[*idx as usize].as_str();
4385 self.interp.touch_env_hash(n);
4386 let val = self.interp.scope.get_hash_element(n, &key);
4387 self.push(val);
4388 Ok(())
4389 }
4390 Op::SetHashElem(idx) => {
4391 let key = self.pop().to_string();
4392 let val = self.pop();
4393 let n = names[*idx as usize].as_str();
4394 self.require_hash_mutable(n)?;
4395 self.interp.touch_env_hash(n);
4396 self.interp
4397 .scope
4398 .set_hash_element(n, &key, val)
4399 .map_err(|e| e.at_line(self.line()))?;
4400 Ok(())
4401 }
4402 Op::SetHashElemKeep(idx) => {
4403 let key = self.pop().to_string();
4404 let val = self.pop();
4405 let val_keep = val.clone();
4406 let n = names[*idx as usize].as_str();
4407 self.require_hash_mutable(n)?;
4408 self.interp.touch_env_hash(n);
4409 let line = self.line();
4410 self.interp
4411 .scope
4412 .set_hash_element(n, &key, val)
4413 .map_err(|e| e.at_line(line))?;
4414 self.push(val_keep);
4415 Ok(())
4416 }
4417 Op::DeleteHashElem(idx) => {
4418 let key = self.pop().to_string();
4419 let n = names[*idx as usize].as_str();
4420 self.require_hash_mutable(n)?;
4421 self.interp.touch_env_hash(n);
4422 if let Some(obj) = self.interp.tied_hashes.get(n).cloned() {
4423 let class = obj
4424 .as_blessed_ref()
4425 .map(|b| b.class.clone())
4426 .unwrap_or_default();
4427 let full = format!("{}::DELETE", class);
4428 if let Some(sub) = self.interp.subs.get(&full).cloned() {
4429 let line = self.line();
4430 let v = vm_interp_result(
4431 self.interp.call_sub(
4432 &sub,
4433 vec![obj, StrykeValue::string(key)],
4434 WantarrayCtx::Scalar,
4435 line,
4436 ),
4437 line,
4438 )?;
4439 self.push(v);
4440 return Ok(());
4441 }
4442 }
4443 let val = self
4444 .interp
4445 .scope
4446 .delete_hash_element(n, &key)
4447 .map_err(|e| e.at_line(self.line()))?;
4448 self.push(val);
4449 Ok(())
4450 }
4451 Op::ExistsHashElem(idx) => {
4452 let key = self.pop().to_string();
4453 let n = names[*idx as usize].as_str();
4454 self.interp.touch_env_hash(n);
4455 if let Some(obj) = self.interp.tied_hashes.get(n).cloned() {
4456 let class = obj
4457 .as_blessed_ref()
4458 .map(|b| b.class.clone())
4459 .unwrap_or_default();
4460 let full = format!("{}::EXISTS", class);
4461 if let Some(sub) = self.interp.subs.get(&full).cloned() {
4462 let line = self.line();
4463 let v = vm_interp_result(
4464 self.interp.call_sub(
4465 &sub,
4466 vec![obj, StrykeValue::string(key)],
4467 WantarrayCtx::Scalar,
4468 line,
4469 ),
4470 line,
4471 )?;
4472 self.push(v);
4473 return Ok(());
4474 }
4475 }
4476 let exists = self.interp.scope.exists_hash_element(n, &key);
4477 self.push(StrykeValue::integer(if exists { 1 } else { 0 }));
4478 Ok(())
4479 }
4480 Op::ExistsArrowHashElem => {
4481 let key = self.pop().to_string();
4482 let container = self.pop();
4483 let line = self.line();
4484 let yes = vm_interp_result(
4485 self.interp
4486 .exists_arrow_hash_element(container, &key, line)
4487 .map(|b| StrykeValue::integer(if b { 1 } else { 0 }))
4488 .map_err(FlowOrError::Error),
4489 line,
4490 )?;
4491 self.push(yes);
4492 Ok(())
4493 }
4494 Op::DeleteArrowHashElem => {
4495 let key = self.pop().to_string();
4496 let container = self.pop();
4497 let line = self.line();
4498 let v = vm_interp_result(
4499 self.interp
4500 .delete_arrow_hash_element(container, &key, line)
4501 .map_err(FlowOrError::Error),
4502 line,
4503 )?;
4504 self.push(v);
4505 Ok(())
4506 }
4507 Op::ExistsArrowArrayElem => {
4508 let idx = self.pop().to_int();
4509 let container = self.pop();
4510 let line = self.line();
4511 let yes = vm_interp_result(
4512 self.interp
4513 .exists_arrow_array_element(container, idx, line)
4514 .map(|b| StrykeValue::integer(if b { 1 } else { 0 }))
4515 .map_err(FlowOrError::Error),
4516 line,
4517 )?;
4518 self.push(yes);
4519 Ok(())
4520 }
4521 Op::DeleteArrowArrayElem => {
4522 let idx = self.pop().to_int();
4523 let container = self.pop();
4524 let line = self.line();
4525 let v = vm_interp_result(
4526 self.interp
4527 .delete_arrow_array_element(container, idx, line)
4528 .map_err(FlowOrError::Error),
4529 line,
4530 )?;
4531 self.push(v);
4532 Ok(())
4533 }
4534 Op::HashKeys(idx) => {
4535 let n = names[*idx as usize].as_str();
4536 self.interp.touch_env_hash(n);
4537 let h = self.interp.scope.get_hash(n);
4538 let keys: Vec<StrykeValue> =
4539 h.keys().map(|k| StrykeValue::string(k.clone())).collect();
4540 self.push(StrykeValue::array(keys));
4541 Ok(())
4542 }
4543 Op::HashKeysScalar(idx) => {
4544 let n = names[*idx as usize].as_str();
4545 self.interp.touch_env_hash(n);
4546 let h = self.interp.scope.get_hash(n);
4547 self.push(StrykeValue::integer(h.len() as i64));
4548 Ok(())
4549 }
4550 Op::HashValues(idx) => {
4551 let n = names[*idx as usize].as_str();
4552 self.interp.touch_env_hash(n);
4553 let h = self.interp.scope.get_hash(n);
4554 let vals: Vec<StrykeValue> = h.values().cloned().collect();
4555 self.push(StrykeValue::array(vals));
4556 Ok(())
4557 }
4558 Op::HashValuesScalar(idx) => {
4559 let n = names[*idx as usize].as_str();
4560 self.interp.touch_env_hash(n);
4561 let h = self.interp.scope.get_hash(n);
4562 self.push(StrykeValue::integer(h.len() as i64));
4563 Ok(())
4564 }
4565 Op::KeysFromValue => {
4566 let val = self.pop();
4567 let line = self.line();
4568 let v = vm_interp_result(VMHelper::keys_from_value(val, line), line)?;
4569 self.push(v);
4570 Ok(())
4571 }
4572 Op::KeysFromValueScalar => {
4573 let val = self.pop();
4574 let line = self.line();
4575 let v = vm_interp_result(VMHelper::keys_from_value(val, line), line)?;
4576 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
4577 self.push(StrykeValue::integer(n));
4578 Ok(())
4579 }
4580 Op::ValuesFromValue => {
4581 let val = self.pop();
4582 let line = self.line();
4583 let v = vm_interp_result(VMHelper::values_from_value(val, line), line)?;
4584 self.push(v);
4585 Ok(())
4586 }
4587 Op::ValuesFromValueScalar => {
4588 let val = self.pop();
4589 let line = self.line();
4590 let v = vm_interp_result(VMHelper::values_from_value(val, line), line)?;
4591 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
4592 self.push(StrykeValue::integer(n));
4593 Ok(())
4594 }
4595
4596 Op::Add => {
4598 let b = self.pop();
4599 let a = self.pop();
4600 self.push_binop_with_overload(BinOp::Add, a, b, |a, b| {
4601 if let Some(s) = crate::sketches::try_sketch_binop(
4602 crate::sketches::SketchOp::Add,
4603 a,
4604 b,
4605 ) {
4606 return Ok(s);
4607 }
4608 Ok(crate::value::compat_add(a, b))
4609 })
4610 }
4611 Op::Sub => {
4612 let b = self.pop();
4613 let a = self.pop();
4614 self.push_binop_with_overload(BinOp::Sub, a, b, |a, b| {
4615 if let Some(s) = crate::sketches::try_sketch_binop(
4616 crate::sketches::SketchOp::Sub,
4617 a,
4618 b,
4619 ) {
4620 return Ok(s);
4621 }
4622 Ok(crate::value::compat_sub(a, b))
4623 })
4624 }
4625 Op::Mul => {
4626 let b = self.pop();
4627 let a = self.pop();
4628 self.push_binop_with_overload(BinOp::Mul, a, b, |a, b| {
4629 Ok(crate::value::compat_mul(a, b))
4630 })
4631 }
4632 Op::Div => {
4633 let b = self.pop();
4634 let a = self.pop();
4635 let line = self.line();
4636 self.push_binop_with_overload(BinOp::Div, a, b, |a, b| {
4637 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
4638 if y == 0 {
4639 return Err(StrykeError::division_by_zero(
4640 "Illegal division by zero",
4641 line,
4642 ));
4643 }
4644 Ok(if x % y == 0 {
4645 StrykeValue::integer(x / y)
4646 } else {
4647 StrykeValue::float(x as f64 / y as f64)
4648 })
4649 } else {
4650 let d = b.to_number();
4651 if d == 0.0 {
4652 return Err(StrykeError::division_by_zero(
4653 "Illegal division by zero",
4654 line,
4655 ));
4656 }
4657 Ok(StrykeValue::float(a.to_number() / d))
4658 }
4659 })
4660 }
4661 Op::Mod => {
4662 let b = self.pop();
4663 let a = self.pop();
4664 let line = self.line();
4665 self.push_binop_with_overload(BinOp::Mod, a, b, |a, b| {
4666 let b = b.to_int();
4667 let a = a.to_int();
4668 if b == 0 {
4669 return Err(StrykeError::division_by_zero(
4670 "Illegal modulus zero",
4671 line,
4672 ));
4673 }
4674 Ok(StrykeValue::integer(crate::value::perl_mod_i64(a, b)))
4675 })
4676 }
4677 Op::Pow => {
4678 let b = self.pop();
4679 let a = self.pop();
4680 self.push_binop_with_overload(BinOp::Pow, a, b, |a, b| {
4681 Ok(crate::value::compat_pow(a, b))
4682 })
4683 }
4684 Op::Negate => {
4685 let a = self.pop();
4686 let line = self.line();
4687 if let Some(exec_res) =
4688 self.interp.try_overload_unary_dispatch("neg", &a, line)
4689 {
4690 self.push(vm_interp_result(exec_res, line)?);
4691 } else {
4692 self.push(if let Some(n) = a.as_integer() {
4693 StrykeValue::integer(-n)
4694 } else {
4695 StrykeValue::float(-a.to_number())
4696 });
4697 }
4698 Ok(())
4699 }
4700 Op::Inc => {
4701 let a = self.pop();
4702 self.push(if let Some(n) = a.as_integer() {
4703 StrykeValue::integer(n.wrapping_add(1))
4704 } else {
4705 StrykeValue::float(a.to_number() + 1.0)
4706 });
4707 Ok(())
4708 }
4709 Op::Dec => {
4710 let a = self.pop();
4711 self.push(if let Some(n) = a.as_integer() {
4712 StrykeValue::integer(n.wrapping_sub(1))
4713 } else {
4714 StrykeValue::float(a.to_number() - 1.0)
4715 });
4716 Ok(())
4717 }
4718
4719 Op::Concat => {
4721 let b = self.pop();
4722 let a = self.pop();
4723 let out = self.concat_stack_values(a, b)?;
4724 self.push(out);
4725 Ok(())
4726 }
4727 Op::ArrayStringifyListSep => {
4728 let raw = self.pop();
4729 let v = self.interp.peel_array_ref_for_list_join(raw);
4730 let sep = self.interp.list_separator.clone();
4731 let list = v.to_list();
4732 let joined = list
4733 .iter()
4734 .map(|x| x.to_string())
4735 .collect::<Vec<_>>()
4736 .join(&sep);
4737 self.push(StrykeValue::string(joined));
4738 Ok(())
4739 }
4740 Op::StringRepeat => {
4741 let n = self.pop().to_int();
4742 let val = self.pop();
4743 self.push(StrykeValue::string(val.repeat_value(n)));
4744 Ok(())
4745 }
4746 Op::ListRepeat => {
4747 let n = self.pop().to_int().max(0) as usize;
4748 let val = self.pop();
4749 let items: Vec<StrykeValue> =
4753 val.as_array_vec().unwrap_or_else(|| vec![val]);
4754 let mut out = Vec::with_capacity(items.len().saturating_mul(n));
4755 for _ in 0..n {
4756 out.extend(items.iter().cloned());
4757 }
4758 self.push(StrykeValue::array(out));
4759 Ok(())
4760 }
4761 Op::ProcessCaseEscapes => {
4762 let val = self.pop();
4763 let s = val.to_string();
4764 let processed = VMHelper::process_case_escapes(&s);
4765 self.push(StrykeValue::string(processed));
4766 Ok(())
4767 }
4768
4769 Op::NumEq => {
4771 let b = self.pop();
4772 let a = self.pop();
4773 self.push_binop_with_overload(BinOp::NumEq, a.clone(), b.clone(), |a, b| {
4774 if let (Some(sa), Some(sb)) = (a.as_struct_inst(), b.as_struct_inst()) {
4776 if sa.def.name != sb.def.name {
4777 return Ok(StrykeValue::integer(0));
4778 }
4779 let av = sa.get_values();
4780 let bv = sb.get_values();
4781 let eq = av.len() == bv.len()
4782 && av.iter().zip(bv.iter()).all(|(x, y)| x.struct_field_eq(y));
4783 Ok(StrykeValue::integer(if eq { 1 } else { 0 }))
4784 } else {
4785 if !crate::compat_mode() && both_non_numeric_strings(a, b) {
4786 let sa = a.to_string();
4787 let sb = b.to_string();
4788 return Ok(StrykeValue::integer(if sa == sb { 1 } else { 0 }));
4789 }
4790 Ok(int_cmp(a, b, |x, y| x == y, |x, y| x == y))
4791 }
4792 })
4793 }
4794 Op::NumNe => {
4795 let b = self.pop();
4796 let a = self.pop();
4797 self.push_binop_with_overload(BinOp::NumNe, a, b, |a, b| {
4798 if !crate::compat_mode() && both_non_numeric_strings(a, b) {
4804 let sa = a.to_string();
4805 let sb = b.to_string();
4806 return Ok(StrykeValue::integer(if sa != sb { 1 } else { 0 }));
4807 }
4808 Ok(int_cmp(a, b, |x, y| x != y, |x, y| x != y))
4809 })
4810 }
4811 Op::NumLt => {
4812 let b = self.pop();
4813 let a = self.pop();
4814 self.push_binop_with_overload(BinOp::NumLt, a, b, |a, b| {
4815 Ok(int_cmp(a, b, |x, y| x < y, |x, y| x < y))
4816 })
4817 }
4818 Op::NumGt => {
4819 let b = self.pop();
4820 let a = self.pop();
4821 self.push_binop_with_overload(BinOp::NumGt, a, b, |a, b| {
4822 Ok(int_cmp(a, b, |x, y| x > y, |x, y| x > y))
4823 })
4824 }
4825 Op::NumLe => {
4826 let b = self.pop();
4827 let a = self.pop();
4828 self.push_binop_with_overload(BinOp::NumLe, a, b, |a, b| {
4829 Ok(int_cmp(a, b, |x, y| x <= y, |x, y| x <= y))
4830 })
4831 }
4832 Op::NumGe => {
4833 let b = self.pop();
4834 let a = self.pop();
4835 self.push_binop_with_overload(BinOp::NumGe, a, b, |a, b| {
4836 Ok(int_cmp(a, b, |x, y| x >= y, |x, y| x >= y))
4837 })
4838 }
4839 Op::Spaceship => {
4840 let b = self.pop();
4841 let a = self.pop();
4842 self.push_binop_with_overload(BinOp::Spaceship, a, b, |a, b| {
4843 Ok(
4844 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
4845 StrykeValue::integer(if x < y {
4846 -1
4847 } else if x > y {
4848 1
4849 } else {
4850 0
4851 })
4852 } else {
4853 let x = a.to_number();
4854 let y = b.to_number();
4855 StrykeValue::integer(if x < y {
4856 -1
4857 } else if x > y {
4858 1
4859 } else {
4860 0
4861 })
4862 },
4863 )
4864 })
4865 }
4866
4867 Op::StrEq => {
4869 let b = self.pop();
4870 let a = self.pop();
4871 self.push_binop_with_overload(BinOp::StrEq, a, b, |a, b| {
4872 Ok(StrykeValue::integer(if a.str_eq(b) { 1 } else { 0 }))
4873 })
4874 }
4875 Op::StrNe => {
4876 let b = self.pop();
4877 let a = self.pop();
4878 self.push_binop_with_overload(BinOp::StrNe, a, b, |a, b| {
4879 Ok(StrykeValue::integer(if !a.str_eq(b) { 1 } else { 0 }))
4880 })
4881 }
4882 Op::StrLt => {
4883 let b = self.pop();
4884 let a = self.pop();
4885 self.push_binop_with_overload(BinOp::StrLt, a, b, |a, b| {
4886 Ok(StrykeValue::integer(
4887 if a.str_cmp(b) == std::cmp::Ordering::Less {
4888 1
4889 } else {
4890 0
4891 },
4892 ))
4893 })
4894 }
4895 Op::StrGt => {
4896 let b = self.pop();
4897 let a = self.pop();
4898 self.push_binop_with_overload(BinOp::StrGt, a, b, |a, b| {
4899 Ok(StrykeValue::integer(
4900 if a.str_cmp(b) == std::cmp::Ordering::Greater {
4901 1
4902 } else {
4903 0
4904 },
4905 ))
4906 })
4907 }
4908 Op::StrLe => {
4909 let b = self.pop();
4910 let a = self.pop();
4911 self.push_binop_with_overload(BinOp::StrLe, a, b, |a, b| {
4912 let o = a.str_cmp(b);
4913 Ok(StrykeValue::integer(
4914 if matches!(o, std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
4915 {
4916 1
4917 } else {
4918 0
4919 },
4920 ))
4921 })
4922 }
4923 Op::StrGe => {
4924 let b = self.pop();
4925 let a = self.pop();
4926 self.push_binop_with_overload(BinOp::StrGe, a, b, |a, b| {
4927 let o = a.str_cmp(b);
4928 Ok(StrykeValue::integer(
4929 if matches!(
4930 o,
4931 std::cmp::Ordering::Greater | std::cmp::Ordering::Equal
4932 ) {
4933 1
4934 } else {
4935 0
4936 },
4937 ))
4938 })
4939 }
4940 Op::StrCmp => {
4941 let b = self.pop();
4942 let a = self.pop();
4943 self.push_binop_with_overload(BinOp::StrCmp, a, b, |a, b| {
4944 let cmp = a.str_cmp(b);
4945 Ok(StrykeValue::integer(match cmp {
4946 std::cmp::Ordering::Less => -1,
4947 std::cmp::Ordering::Greater => 1,
4948 std::cmp::Ordering::Equal => 0,
4949 }))
4950 })
4951 }
4952
4953 Op::LogNot => {
4955 let a = self.pop();
4956 let line = self.line();
4957 if let Some(exec_res) =
4958 self.interp.try_overload_unary_dispatch("bool", &a, line)
4959 {
4960 let pv = vm_interp_result(exec_res, line)?;
4961 self.push(StrykeValue::integer(if pv.is_true() { 0 } else { 1 }));
4962 } else {
4963 self.push(StrykeValue::integer(if a.is_true() { 0 } else { 1 }));
4964 }
4965 Ok(())
4966 }
4967 Op::BitAnd => {
4968 let rv = self.pop();
4969 let lv = self.pop();
4970 if let Some(s) = crate::value::set_intersection(&lv, &rv) {
4971 self.push(s);
4972 } else if let Some(s) = crate::sketches::try_sketch_binop(
4973 crate::sketches::SketchOp::And,
4974 &lv,
4975 &rv,
4976 ) {
4977 self.push(s);
4978 } else {
4979 self.push(StrykeValue::integer(lv.to_int() & rv.to_int()));
4980 }
4981 Ok(())
4982 }
4983 Op::BitOr => {
4984 let rv = self.pop();
4985 let lv = self.pop();
4986 if let Some(s) = crate::value::set_union(&lv, &rv) {
4987 self.push(s);
4988 } else if let Some(s) = crate::sketches::try_sketch_binop(
4989 crate::sketches::SketchOp::Or,
4990 &lv,
4991 &rv,
4992 ) {
4993 self.push(s);
4994 } else {
4995 self.push(StrykeValue::integer(lv.to_int() | rv.to_int()));
4996 }
4997 Ok(())
4998 }
4999 Op::BitXor => {
5000 let rv = self.pop();
5001 let lv = self.pop();
5002 if let Some(s) = crate::sketches::try_sketch_binop(
5003 crate::sketches::SketchOp::Xor,
5004 &lv,
5005 &rv,
5006 ) {
5007 self.push(s);
5008 } else {
5009 self.push(StrykeValue::integer(lv.to_int() ^ rv.to_int()));
5010 }
5011 Ok(())
5012 }
5013 Op::BitNot => {
5014 let a = self.pop().to_int();
5015 self.push(StrykeValue::integer(!a));
5016 Ok(())
5017 }
5018 Op::Shl => {
5019 let b = self.pop().to_int();
5020 let a = self.pop().to_int();
5021 self.push(StrykeValue::integer(perl_shl_i64(a, b)));
5022 Ok(())
5023 }
5024 Op::Shr => {
5025 let b = self.pop().to_int();
5026 let a = self.pop().to_int();
5027 self.push(StrykeValue::integer(perl_shr_i64(a, b)));
5028 Ok(())
5029 }
5030
5031 Op::Jump(target) => {
5033 self.ip = *target;
5034 Ok(())
5035 }
5036 Op::JumpIfTrue(target) => {
5037 let val = self.pop();
5038 if val.is_true() {
5039 self.ip = *target;
5040 }
5041 Ok(())
5042 }
5043 Op::JumpIfFalse(target) => {
5044 let val = self.pop();
5045 if !val.is_true() {
5046 self.ip = *target;
5047 }
5048 Ok(())
5049 }
5050 Op::JumpIfFalseKeep(target) => {
5051 if !self.peek().is_true() {
5052 self.ip = *target;
5053 } else {
5054 self.pop();
5055 }
5056 Ok(())
5057 }
5058 Op::JumpIfTrueKeep(target) => {
5059 if self.peek().is_true() {
5060 self.ip = *target;
5061 } else {
5062 self.pop();
5063 }
5064 Ok(())
5065 }
5066 Op::JumpIfDefinedKeep(target) => {
5067 if !self.peek().is_undef() {
5068 self.ip = *target;
5069 } else {
5070 self.pop();
5071 }
5072 Ok(())
5073 }
5074
5075 Op::PreInc(idx) => {
5077 let n = names[*idx as usize].as_str();
5078 self.require_scalar_mutable(n)?;
5079 let en = self.interp.english_scalar_name(n);
5080 let new_val = self
5081 .interp
5082 .scope
5083 .atomic_mutate(en, |v| StrykeValue::integer(v.to_int() + 1))
5084 .map_err(|e| e.at_line(self.line()))?;
5085 self.push(new_val);
5086 Ok(())
5087 }
5088 Op::PreDec(idx) => {
5089 let n = names[*idx as usize].as_str();
5090 self.require_scalar_mutable(n)?;
5091 let en = self.interp.english_scalar_name(n);
5092 let new_val = self
5093 .interp
5094 .scope
5095 .atomic_mutate(en, |v| StrykeValue::integer(v.to_int() - 1))
5096 .map_err(|e| e.at_line(self.line()))?;
5097 self.push(new_val);
5098 Ok(())
5099 }
5100 Op::PostInc(idx) => {
5101 let n = names[*idx as usize].as_str();
5102 self.require_scalar_mutable(n)?;
5103 let en = self.interp.english_scalar_name(n);
5104 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5105 self.interp
5106 .scope
5107 .atomic_mutate_post(en, crate::vm_helper::perl_inc)
5108 .map_err(|e| e.at_line(self.line()))?;
5109 self.ip += 1;
5110 } else {
5111 let old = self
5112 .interp
5113 .scope
5114 .atomic_mutate_post(en, crate::vm_helper::perl_inc)
5115 .map_err(|e| e.at_line(self.line()))?;
5116 self.push(old);
5117 }
5118 Ok(())
5119 }
5120 Op::PostDec(idx) => {
5121 let n = names[*idx as usize].as_str();
5122 self.require_scalar_mutable(n)?;
5123 let en = self.interp.english_scalar_name(n);
5124 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5125 self.interp
5126 .scope
5127 .atomic_mutate_post(en, |v| StrykeValue::integer(v.to_int() - 1))
5128 .map_err(|e| e.at_line(self.line()))?;
5129 self.ip += 1;
5130 } else {
5131 let old = self
5132 .interp
5133 .scope
5134 .atomic_mutate_post(en, |v| StrykeValue::integer(v.to_int() - 1))
5135 .map_err(|e| e.at_line(self.line()))?;
5136 self.push(old);
5137 }
5138 Ok(())
5139 }
5140 Op::PreIncSlot(slot) => {
5141 let cur = self.interp.scope.get_scalar_slot(*slot);
5142 let new_val = crate::vm_helper::perl_inc(&cur);
5143 self.interp.scope.set_scalar_slot(*slot, new_val.clone());
5144 self.push(new_val);
5145 Ok(())
5146 }
5147 Op::PreIncSlotVoid(slot) => {
5148 let cur = self.interp.scope.get_scalar_slot(*slot);
5149 let new_val = crate::vm_helper::perl_inc(&cur);
5150 self.interp.scope.set_scalar_slot(*slot, new_val);
5151 Ok(())
5152 }
5153 Op::PreDecSlot(slot) => {
5154 let val = self.interp.scope.get_scalar_slot(*slot).to_int() - 1;
5155 let new_val = StrykeValue::integer(val);
5156 self.interp.scope.set_scalar_slot(*slot, new_val.clone());
5157 self.push(new_val);
5158 Ok(())
5159 }
5160 Op::PostIncSlot(slot) => {
5161 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5163 let cur = self.interp.scope.get_scalar_slot(*slot);
5164 let new_val = crate::vm_helper::perl_inc(&cur);
5165 self.interp.scope.set_scalar_slot(*slot, new_val);
5166 self.ip += 1; } else {
5168 let old = self.interp.scope.get_scalar_slot(*slot);
5169 let new_val = crate::vm_helper::perl_inc(&old);
5170 self.interp.scope.set_scalar_slot(*slot, new_val);
5171 self.push(old);
5172 }
5173 Ok(())
5174 }
5175 Op::PostDecSlot(slot) => {
5176 if self.ip < len && matches!(ops[self.ip], Op::Pop) {
5177 let val = self.interp.scope.get_scalar_slot(*slot).to_int() - 1;
5178 self.interp
5179 .scope
5180 .set_scalar_slot(*slot, StrykeValue::integer(val));
5181 self.ip += 1;
5182 } else {
5183 let old = self.interp.scope.get_scalar_slot(*slot);
5184 let new_val = StrykeValue::integer(old.to_int() - 1);
5185 self.interp.scope.set_scalar_slot(*slot, new_val);
5186 self.push(old);
5187 }
5188 Ok(())
5189 }
5190
5191 Op::Call(name_idx, argc, wa) => {
5193 let name = &self.names[*name_idx as usize];
5202 let entry_opt = if !crate::compat_mode()
5203 && !name.contains("::")
5204 && crate::builtins::is_callable_spelling(name)
5205 {
5206 None
5207 } else {
5208 self.find_sub_entry(*name_idx)
5209 };
5210 self.vm_dispatch_user_call(*name_idx, entry_opt, *argc, *wa, None)?;
5211 Ok(())
5212 }
5213 Op::CallStaticSubId(sid, name_idx, argc, wa) => {
5214 let t = self.static_sub_calls.get(*sid as usize).ok_or_else(|| {
5215 StrykeError::runtime("VM: invalid CallStaticSubId", self.line())
5216 })?;
5217 debug_assert_eq!(t.2, *name_idx);
5218 let closure_sub = self
5219 .static_sub_closure_subs
5220 .get(*sid as usize)
5221 .and_then(|x| x.clone());
5222 self.vm_dispatch_user_call(
5223 *name_idx,
5224 Some((t.0, t.1)),
5225 *argc,
5226 *wa,
5227 closure_sub,
5228 )?;
5229 Ok(())
5230 }
5231 Op::Return => {
5232 if let Some(frame) = self.call_stack.pop() {
5233 if frame.block_region {
5234 return Err(StrykeError::runtime(
5235 "Return in map/grep/sort block bytecode",
5236 self.line(),
5237 ));
5238 }
5239 if let Some(t0) = frame.sub_profiler_start {
5240 if let Some(p) = &mut self.interp.profiler {
5241 p.exit_sub(t0.elapsed());
5242 }
5243 }
5244 self.interp.debugger_leave_sub();
5245 self.interp.wantarray_kind = frame.saved_wantarray;
5246 self.stack.truncate(frame.stack_base);
5247 self.interp.pop_scope_to_depth(frame.scope_depth);
5248 self.interp.current_sub_stack.pop();
5249 if frame.jit_trampoline_return {
5250 self.jit_trampoline_out = Some(StrykeValue::UNDEF);
5251 } else {
5252 self.push(StrykeValue::UNDEF);
5253 self.ip = frame.return_ip;
5254 }
5255 } else {
5256 self.exit_main_dispatch = true;
5257 }
5258 Ok(())
5259 }
5260 Op::ReturnValue => {
5261 let val = self.pop();
5262 let val = self.resolve_binding_ref(val);
5268 let val = if matches!(self.interp.wantarray_kind, WantarrayCtx::Scalar) {
5275 if let Some(items) = val.as_array_vec() {
5276 items.last().cloned().unwrap_or(StrykeValue::UNDEF)
5277 } else {
5278 val
5279 }
5280 } else {
5281 val
5282 };
5283 if let Some(frame) = self.call_stack.pop() {
5284 if frame.block_region {
5285 return Err(StrykeError::runtime(
5286 "Return in map/grep/sort block bytecode",
5287 self.line(),
5288 ));
5289 }
5290 if let Some(t0) = frame.sub_profiler_start {
5291 if let Some(p) = &mut self.interp.profiler {
5292 p.exit_sub(t0.elapsed());
5293 }
5294 }
5295 self.interp.debugger_leave_sub();
5296 self.interp.wantarray_kind = frame.saved_wantarray;
5297 self.stack.truncate(frame.stack_base);
5298 self.interp.pop_scope_to_depth(frame.scope_depth);
5299 self.interp.current_sub_stack.pop();
5300 if frame.jit_trampoline_return {
5301 self.jit_trampoline_out = Some(val);
5302 } else {
5303 self.push(val);
5304 self.ip = frame.return_ip;
5305 }
5306 } else {
5307 self.exit_main_dispatch_value = Some(val);
5308 self.exit_main_dispatch = true;
5309 }
5310 Ok(())
5311 }
5312 Op::BlockReturnValue => {
5313 let val = self.pop();
5314 let val = self.resolve_binding_ref(val);
5315 if let Some(frame) = self.call_stack.pop() {
5316 if !frame.block_region {
5317 return Err(StrykeError::runtime(
5318 "BlockReturnValue without map/grep/sort block frame",
5319 self.line(),
5320 ));
5321 }
5322 self.interp.wantarray_kind = frame.saved_wantarray;
5323 self.stack.truncate(frame.stack_base);
5324 self.interp.pop_scope_to_depth(frame.scope_depth);
5325 self.block_region_return = Some(val);
5326 Ok(())
5327 } else {
5328 Err(StrykeError::runtime(
5329 "BlockReturnValue with empty call stack",
5330 self.line(),
5331 ))
5332 }
5333 }
5334 Op::BindSubClosure(name_idx) => {
5335 let n = names[*name_idx as usize].as_str();
5336 self.interp.rebind_sub_closure(n);
5337 Ok(())
5338 }
5339
5340 Op::PushFrame => {
5342 self.interp.scope_push_hook();
5343 Ok(())
5344 }
5345 Op::PopFrame => {
5346 self.interp.scope_pop_hook();
5347 Ok(())
5348 }
5349 Op::Print(handle_idx, argc) => {
5351 let argc = *argc as usize;
5352 let mut args = Vec::with_capacity(argc);
5353 for _ in 0..argc {
5354 args.push(self.pop());
5355 }
5356 args.reverse();
5357 let mut output = String::new();
5358 if args.is_empty() {
5359 let topic = self.interp.scope.get_scalar("_").clone();
5360 let s = match self.interp.stringify_value(topic, self.line()) {
5361 Ok(s) => s,
5362 Err(FlowOrError::Error(e)) => return Err(e),
5363 Err(FlowOrError::Flow(_)) => {
5364 return Err(StrykeError::runtime(
5365 "print: unexpected control flow",
5366 self.line(),
5367 ));
5368 }
5369 };
5370 output.push_str(&s);
5371 } else {
5372 for (i, arg) in args.iter().enumerate() {
5373 if i > 0 && !self.interp.ofs.is_empty() {
5374 output.push_str(&self.interp.ofs);
5375 }
5376 for item in arg.to_list() {
5377 let s = match self.interp.stringify_value(item, self.line()) {
5378 Ok(s) => s,
5379 Err(FlowOrError::Error(e)) => return Err(e),
5380 Err(FlowOrError::Flow(_)) => {
5381 return Err(StrykeError::runtime(
5382 "print: unexpected control flow",
5383 self.line(),
5384 ));
5385 }
5386 };
5387 output.push_str(&s);
5388 }
5389 }
5390 }
5391 output.push_str(&self.interp.ors);
5392 let handle_name = match handle_idx {
5393 Some(idx) => self.interp.resolve_io_handle_name(
5394 self.names
5395 .get(*idx as usize)
5396 .map_or("STDOUT", |s| s.as_str()),
5397 ),
5398 None => self
5399 .interp
5400 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5401 };
5402 self.interp.write_formatted_print(
5403 handle_name.as_str(),
5404 &output,
5405 self.line(),
5406 )?;
5407 self.push(StrykeValue::integer(1));
5408 Ok(())
5409 }
5410 Op::Printf(handle_idx, argc) => {
5411 let argc = *argc as usize;
5412 let mut args = Vec::with_capacity(argc);
5413 for _ in 0..argc {
5414 args.push(self.pop());
5415 }
5416 args.reverse();
5417 let (fmt, rest) = match args.split_first() {
5418 Some((f, r)) => (f.to_string(), r),
5419 None => {
5420 return Err(StrykeError::runtime(
5421 "printf requires a format string",
5422 self.line(),
5423 ));
5424 }
5425 };
5426 let mut flat = Vec::new();
5430 for a in rest {
5431 if let Some(items) = a.as_array_vec() {
5432 flat.extend(items);
5433 } else {
5434 flat.push(a.clone());
5435 }
5436 }
5437 let s = match self.interp.perl_sprintf_stringify(&fmt, &flat, self.line()) {
5438 Ok(s) => s,
5439 Err(FlowOrError::Error(e)) => return Err(e),
5440 Err(FlowOrError::Flow(_)) => {
5441 return Err(StrykeError::runtime(
5442 "printf: unexpected control flow",
5443 self.line(),
5444 ));
5445 }
5446 };
5447 let handle_name = match handle_idx {
5448 Some(idx) => self.interp.resolve_io_handle_name(
5449 self.names
5450 .get(*idx as usize)
5451 .map_or("STDOUT", |s| s.as_str()),
5452 ),
5453 None => self
5454 .interp
5455 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5456 };
5457 self.interp
5458 .write_formatted_print(handle_name.as_str(), &s, self.line())?;
5459 self.push(StrykeValue::integer(1));
5460 Ok(())
5461 }
5462 Op::Say(handle_idx, argc) => {
5463 if (self.interp.feature_bits & crate::vm_helper::FEAT_SAY) == 0 {
5464 return Err(StrykeError::runtime(
5465 "say() is disabled (enable with use feature 'say' or use feature ':5.10')",
5466 self.line(),
5467 ));
5468 }
5469 let argc = *argc as usize;
5470 let mut args = Vec::with_capacity(argc);
5471 for _ in 0..argc {
5472 args.push(self.pop());
5473 }
5474 args.reverse();
5475 let mut output = String::new();
5476 if args.is_empty() {
5477 let topic = self.interp.scope.get_scalar("_").clone();
5478 let s = match self.interp.stringify_value(topic, self.line()) {
5479 Ok(s) => s,
5480 Err(FlowOrError::Error(e)) => return Err(e),
5481 Err(FlowOrError::Flow(_)) => {
5482 return Err(StrykeError::runtime(
5483 "say: unexpected control flow",
5484 self.line(),
5485 ));
5486 }
5487 };
5488 output.push_str(&s);
5489 } else {
5490 for (i, arg) in args.iter().enumerate() {
5491 if i > 0 && !self.interp.ofs.is_empty() {
5492 output.push_str(&self.interp.ofs);
5493 }
5494 for item in arg.to_list() {
5495 let s = match self.interp.stringify_value(item, self.line()) {
5496 Ok(s) => s,
5497 Err(FlowOrError::Error(e)) => return Err(e),
5498 Err(FlowOrError::Flow(_)) => {
5499 return Err(StrykeError::runtime(
5500 "say: unexpected control flow",
5501 self.line(),
5502 ));
5503 }
5504 };
5505 output.push_str(&s);
5506 }
5507 }
5508 }
5509 output.push('\n');
5510 output.push_str(&self.interp.ors);
5511 let handle_name = match handle_idx {
5512 Some(idx) => self.interp.resolve_io_handle_name(
5513 self.names
5514 .get(*idx as usize)
5515 .map_or("STDOUT", |s| s.as_str()),
5516 ),
5517 None => self
5518 .interp
5519 .resolve_io_handle_name(self.interp.default_print_handle.as_str()),
5520 };
5521 self.interp.write_formatted_print(
5522 handle_name.as_str(),
5523 &output,
5524 self.line(),
5525 )?;
5526 self.push(StrykeValue::integer(1));
5527 Ok(())
5528 }
5529
5530 Op::CallBuiltin(id, argc) => {
5532 let argc = *argc as usize;
5533 let mut args = Vec::with_capacity(argc);
5534 for _ in 0..argc {
5535 args.push(self.pop());
5536 }
5537 args.reverse();
5538 let result = self.exec_builtin(*id, args)?;
5539 self.push(result);
5540 Ok(())
5541 }
5542 Op::WantarrayPush(wa) => {
5543 self.wantarray_stack.push(self.interp.wantarray_kind);
5544 self.interp.wantarray_kind = WantarrayCtx::from_byte(*wa);
5545 Ok(())
5546 }
5547 Op::WantarrayPop => {
5548 self.interp.wantarray_kind =
5549 self.wantarray_stack.pop().unwrap_or(WantarrayCtx::Scalar);
5550 Ok(())
5551 }
5552
5553 Op::MakeArray(n) => {
5555 let n = *n as usize;
5556 let mut stack_vals = Vec::with_capacity(n);
5562 for _ in 0..n {
5563 stack_vals.push(self.pop());
5564 }
5565 stack_vals.reverse();
5566 let mut arr = Vec::new();
5567 for v in stack_vals {
5568 if let Some(items) = v.as_array_vec() {
5569 arr.extend(items);
5570 } else if let Some(map) = v.as_hash_map() {
5571 for (k, vv) in map {
5572 arr.push(StrykeValue::string(k));
5573 arr.push(vv);
5574 }
5575 } else {
5576 arr.push(v);
5577 }
5578 }
5579 self.push(StrykeValue::array(arr));
5580 Ok(())
5581 }
5582 Op::HashSliceDeref(n) => {
5583 let n = *n as usize;
5584 let mut key_vals = Vec::with_capacity(n);
5585 for _ in 0..n {
5586 key_vals.push(self.pop());
5587 }
5588 key_vals.reverse();
5589 let container = self.pop();
5590 let line = self.line();
5591 let out = vm_interp_result(
5592 self.interp
5593 .hash_slice_deref_values(&container, &key_vals, line),
5594 line,
5595 )?;
5596 self.push(out);
5597 Ok(())
5598 }
5599 Op::ArrowArraySlice(n) => {
5600 let n = *n as usize;
5601 let idxs = self.pop_flattened_array_slice_specs(n);
5602 let r = self.pop();
5603 let line = self.line();
5604 let out = vm_interp_result(
5605 self.interp.arrow_array_slice_values(r, &idxs, line),
5606 line,
5607 )?;
5608 self.push(out);
5609 Ok(())
5610 }
5611 Op::SetHashSliceDeref(n) => {
5612 let n = *n as usize;
5613 let mut key_vals = Vec::with_capacity(n);
5614 for _ in 0..n {
5615 key_vals.push(self.pop());
5616 }
5617 key_vals.reverse();
5618 let container = self.pop();
5619 let val = self.pop();
5620 let line = self.line();
5621 vm_interp_result(
5622 self.interp
5623 .assign_hash_slice_deref(container, key_vals, val, line),
5624 line,
5625 )?;
5626 Ok(())
5627 }
5628 Op::SetHashSlice(hash_idx, n) => {
5629 let n = *n as usize;
5630 let mut key_vals = Vec::with_capacity(n);
5631 for _ in 0..n {
5632 key_vals.push(self.pop());
5633 }
5634 key_vals.reverse();
5635 let name = names[*hash_idx as usize].as_str();
5636 self.require_hash_mutable(name)?;
5637 let val = self.pop();
5638 let line = self.line();
5639 vm_interp_result(
5640 self.interp
5641 .assign_named_hash_slice(name, key_vals, val, line),
5642 line,
5643 )?;
5644 Ok(())
5645 }
5646 Op::GetHashSlice(hash_idx, n) => {
5647 let n = *n as usize;
5648 let mut key_vals = Vec::with_capacity(n);
5649 for _ in 0..n {
5650 key_vals.push(self.pop());
5651 }
5652 key_vals.reverse();
5653 let name = names[*hash_idx as usize].as_str();
5654 let h = self.interp.scope.get_hash(name);
5655 let mut result = Vec::new();
5656 for kv in &key_vals {
5657 if let Some(vv) = kv.as_array_vec() {
5660 for v in vv {
5661 let k = v.to_string();
5662 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5663 }
5664 } else if let Some(r) = kv.as_array_ref() {
5665 for v in r.read().iter() {
5666 let k = v.to_string();
5667 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5668 }
5669 } else {
5670 let k = kv.to_string();
5671 result.push(h.get(&k).cloned().unwrap_or(StrykeValue::UNDEF));
5672 }
5673 }
5674 self.push(StrykeValue::array(result));
5675 Ok(())
5676 }
5677 Op::HashSliceDerefCompound(op_byte, n) => {
5678 let n = *n as usize;
5679 let mut key_vals = Vec::with_capacity(n);
5680 for _ in 0..n {
5681 key_vals.push(self.pop());
5682 }
5683 key_vals.reverse();
5684 let container = self.pop();
5685 let rhs = self.pop();
5686 let line = self.line();
5687 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5688 .ok_or_else(|| {
5689 crate::error::StrykeError::runtime(
5690 "VM: HashSliceDerefCompound: bad op byte",
5691 line,
5692 )
5693 })?;
5694 let new_val = vm_interp_result(
5695 self.interp.compound_assign_hash_slice_deref(
5696 container, key_vals, op, rhs, line,
5697 ),
5698 line,
5699 )?;
5700 self.push(new_val);
5701 Ok(())
5702 }
5703 Op::HashSliceDerefIncDec(kind, n) => {
5704 let n = *n as usize;
5705 let mut key_vals = Vec::with_capacity(n);
5706 for _ in 0..n {
5707 key_vals.push(self.pop());
5708 }
5709 key_vals.reverse();
5710 let container = self.pop();
5711 let line = self.line();
5712 let out = vm_interp_result(
5713 self.interp
5714 .hash_slice_deref_inc_dec(container, key_vals, *kind, line),
5715 line,
5716 )?;
5717 self.push(out);
5718 Ok(())
5719 }
5720 Op::NamedHashSliceCompound(op_byte, hash_idx, n) => {
5721 let n = *n as usize;
5722 let mut key_vals = Vec::with_capacity(n);
5723 for _ in 0..n {
5724 key_vals.push(self.pop());
5725 }
5726 key_vals.reverse();
5727 let name = names[*hash_idx as usize].as_str();
5728 self.require_hash_mutable(name)?;
5729 let rhs = self.pop();
5730 let line = self.line();
5731 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5732 .ok_or_else(|| {
5733 crate::error::StrykeError::runtime(
5734 "VM: NamedHashSliceCompound: bad op byte",
5735 line,
5736 )
5737 })?;
5738 let new_val = vm_interp_result(
5739 self.interp
5740 .compound_assign_named_hash_slice(name, key_vals, op, rhs, line),
5741 line,
5742 )?;
5743 self.push(new_val);
5744 Ok(())
5745 }
5746 Op::NamedHashSliceIncDec(kind, hash_idx, n) => {
5747 let n = *n as usize;
5748 let mut key_vals = Vec::with_capacity(n);
5749 for _ in 0..n {
5750 key_vals.push(self.pop());
5751 }
5752 key_vals.reverse();
5753 let name = names[*hash_idx as usize].as_str();
5754 self.require_hash_mutable(name)?;
5755 let line = self.line();
5756 let out = vm_interp_result(
5757 self.interp
5758 .named_hash_slice_inc_dec(name, key_vals, *kind, line),
5759 line,
5760 )?;
5761 self.push(out);
5762 Ok(())
5763 }
5764 Op::NamedHashSlicePeekLast(hash_idx, n) => {
5765 let n = *n as usize;
5766 let line = self.line();
5767 let name = names[*hash_idx as usize].as_str();
5768 self.require_hash_mutable(name)?;
5769 let len = self.stack.len();
5770 if len < n {
5771 return Err(StrykeError::runtime(
5772 "VM: NamedHashSlicePeekLast: stack underflow",
5773 line,
5774 ));
5775 }
5776 let base = len - n;
5777 let key_vals: Vec<StrykeValue> = self.stack[base..base + n].to_vec();
5778 let ks = Self::flatten_hash_slice_key_slots(&key_vals);
5779 let last_k = ks.last().ok_or_else(|| {
5780 StrykeError::runtime("VM: NamedHashSlicePeekLast: empty key list", line)
5781 })?;
5782 self.interp.touch_env_hash(name);
5783 let cur = self.interp.scope.get_hash_element(name, last_k.as_str());
5784 self.push(cur);
5785 Ok(())
5786 }
5787 Op::NamedHashSliceDropKeysKeepCur(n) => {
5788 let n = *n as usize;
5789 let cur = self.pop();
5790 for _ in 0..n {
5791 self.pop();
5792 }
5793 self.push(cur);
5794 Ok(())
5795 }
5796 Op::SetNamedHashSliceLastKeep(hash_idx, n) => {
5797 let n = *n as usize;
5798 let line = self.line();
5799 let name = names[*hash_idx as usize].as_str();
5800 self.require_hash_mutable(name)?;
5801 let mut key_vals_rev = Vec::with_capacity(n);
5802 for _ in 0..n {
5803 key_vals_rev.push(self.pop());
5804 }
5805 key_vals_rev.reverse();
5806 let mut val = self.pop();
5807 if let Some(av) = val.as_array_vec() {
5808 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
5809 }
5810 let ks = Self::flatten_hash_slice_key_slots(&key_vals_rev);
5811 let last_k = ks.last().ok_or_else(|| {
5812 StrykeError::runtime(
5813 "VM: SetNamedHashSliceLastKeep: empty key list",
5814 line,
5815 )
5816 })?;
5817 let val_keep = val.clone();
5818 self.interp.touch_env_hash(name);
5819 vm_interp_result(
5820 self.interp
5821 .scope
5822 .set_hash_element(name, last_k.as_str(), val)
5823 .map(|()| StrykeValue::UNDEF)
5824 .map_err(|e| FlowOrError::Error(e.at_line(line))),
5825 line,
5826 )?;
5827 self.push(val_keep);
5828 Ok(())
5829 }
5830 Op::HashSliceDerefPeekLast(n) => {
5831 let n = *n as usize;
5832 let line = self.line();
5833 let len = self.stack.len();
5834 if len < n + 1 {
5835 return Err(StrykeError::runtime(
5836 "VM: HashSliceDerefPeekLast: stack underflow",
5837 line,
5838 ));
5839 }
5840 let base = len - n - 1;
5841 let container = self.stack[base].clone();
5842 let key_vals: Vec<StrykeValue> =
5843 self.stack[base + 1..base + 1 + n].to_vec();
5844 let list = vm_interp_result(
5845 self.interp
5846 .hash_slice_deref_values(&container, &key_vals, line),
5847 line,
5848 )?;
5849 let cur = list.to_list().last().cloned().unwrap_or(StrykeValue::UNDEF);
5850 self.push(cur);
5851 Ok(())
5852 }
5853 Op::HashSliceDerefRollValUnderKeys(n) => {
5854 let n = *n as usize;
5855 let val = self.pop();
5856 let mut keys_rev = Vec::with_capacity(n);
5857 for _ in 0..n {
5858 keys_rev.push(self.pop());
5859 }
5860 let container = self.pop();
5861 keys_rev.reverse();
5862 self.push(val);
5863 self.push(container);
5864 for k in keys_rev {
5865 self.push(k);
5866 }
5867 Ok(())
5868 }
5869 Op::HashSliceDerefSetLastKeep(n) => {
5870 let n = *n as usize;
5871 let line = self.line();
5872 let mut key_vals_rev = Vec::with_capacity(n);
5873 for _ in 0..n {
5874 key_vals_rev.push(self.pop());
5875 }
5876 key_vals_rev.reverse();
5877 let container = self.pop();
5878 let mut val = self.pop();
5879 if let Some(av) = val.as_array_vec() {
5880 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
5881 }
5882 let ks = Self::flatten_hash_slice_key_slots(&key_vals_rev);
5883 let last_k = ks.last().ok_or_else(|| {
5884 StrykeError::runtime(
5885 "VM: HashSliceDerefSetLastKeep: empty key list",
5886 line,
5887 )
5888 })?;
5889 let val_keep = val.clone();
5890 vm_interp_result(
5891 self.interp.assign_hash_slice_one_key(
5892 container,
5893 last_k.as_str(),
5894 val,
5895 line,
5896 ),
5897 line,
5898 )?;
5899 self.push(val_keep);
5900 Ok(())
5901 }
5902 Op::HashSliceDerefDropKeysKeepCur(n) => {
5903 let n = *n as usize;
5904 let cur = self.pop();
5905 for _ in 0..n {
5906 self.pop();
5907 }
5908 let _container = self.pop();
5909 self.push(cur);
5910 Ok(())
5911 }
5912 Op::SetArrowArraySlice(n) => {
5913 let n = *n as usize;
5914 let idxs = self.pop_flattened_array_slice_specs(n);
5915 let aref = self.pop();
5916 let val = self.pop();
5917 let line = self.line();
5918 vm_interp_result(
5919 self.interp.assign_arrow_array_slice(aref, idxs, val, line),
5920 line,
5921 )?;
5922 Ok(())
5923 }
5924 Op::ArrowArraySliceCompound(op_byte, n) => {
5925 let n = *n as usize;
5926 let idxs = self.pop_flattened_array_slice_specs(n);
5927 let aref = self.pop();
5928 let rhs = self.pop();
5929 let line = self.line();
5930 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
5931 .ok_or_else(|| {
5932 crate::error::StrykeError::runtime(
5933 "VM: ArrowArraySliceCompound: bad op byte",
5934 line,
5935 )
5936 })?;
5937 let new_val = vm_interp_result(
5938 self.interp
5939 .compound_assign_arrow_array_slice(aref, idxs, op, rhs, line),
5940 line,
5941 )?;
5942 self.push(new_val);
5943 Ok(())
5944 }
5945 Op::ArrowArraySliceIncDec(kind, n) => {
5946 let n = *n as usize;
5947 let idxs = self.pop_flattened_array_slice_specs(n);
5948 let aref = self.pop();
5949 let line = self.line();
5950 let out = vm_interp_result(
5951 self.interp
5952 .arrow_array_slice_inc_dec(aref, idxs, *kind, line),
5953 line,
5954 )?;
5955 self.push(out);
5956 Ok(())
5957 }
5958 Op::ArrowArraySlicePeekLast(n) => {
5959 let n = *n as usize;
5960 let line = self.line();
5961 let len = self.stack.len();
5962 if len < n + 1 {
5963 return Err(StrykeError::runtime(
5964 "VM: ArrowArraySlicePeekLast: stack underflow",
5965 line,
5966 ));
5967 }
5968 let base = len - n - 1;
5969 let aref = self.stack[base].clone();
5970 let idxs =
5971 self.flatten_array_slice_specs_ordered_values(&self.stack[base + 1..])?;
5972 let last = *idxs.last().ok_or_else(|| {
5973 StrykeError::runtime(
5974 "VM: ArrowArraySlicePeekLast: empty index list",
5975 line,
5976 )
5977 })?;
5978 let cur = vm_interp_result(
5979 self.interp.read_arrow_array_element(aref, last, line),
5980 line,
5981 )?;
5982 self.push(cur);
5983 Ok(())
5984 }
5985 Op::ArrowArraySliceDropKeysKeepCur(n) => {
5986 let n = *n as usize;
5987 let cur = self.pop();
5988 let _idxs = self.pop_flattened_array_slice_specs(n);
5989 let _aref = self.pop();
5990 self.push(cur);
5991 Ok(())
5992 }
5993 Op::ArrowArraySliceRollValUnderSpecs(n) => {
5994 let n = *n as usize;
5995 let val = self.pop();
5996 let mut specs_rev = Vec::with_capacity(n);
5997 for _ in 0..n {
5998 specs_rev.push(self.pop());
5999 }
6000 let aref = self.pop();
6001 self.push(val);
6002 self.push(aref);
6003 for s in specs_rev.into_iter().rev() {
6004 self.push(s);
6005 }
6006 Ok(())
6007 }
6008 Op::SetArrowArraySliceLastKeep(n) => {
6009 let n = *n as usize;
6010 let line = self.line();
6011 let idxs = self.pop_flattened_array_slice_specs(n);
6012 let aref = self.pop();
6013 let mut val = self.pop();
6014 if let Some(av) = val.as_array_vec() {
6017 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
6018 }
6019 let last = *idxs.last().ok_or_else(|| {
6020 StrykeError::runtime(
6021 "VM: SetArrowArraySliceLastKeep: empty index list",
6022 line,
6023 )
6024 })?;
6025 let val_keep = val.clone();
6026 vm_interp_result(
6027 self.interp.assign_arrow_array_deref(aref, last, val, line),
6028 line,
6029 )?;
6030 self.push(val_keep);
6031 Ok(())
6032 }
6033 Op::NamedArraySliceIncDec(kind, arr_idx, n) => {
6034 let n = *n as usize;
6035 let idxs = self.pop_flattened_array_slice_specs(n);
6036 let name = names[*arr_idx as usize].as_str();
6037 self.require_array_mutable(name)?;
6038 let line = self.line();
6039 let out = vm_interp_result(
6040 self.interp
6041 .named_array_slice_inc_dec(name, idxs, *kind, line),
6042 line,
6043 )?;
6044 self.push(out);
6045 Ok(())
6046 }
6047 Op::NamedArraySliceCompound(op_byte, arr_idx, n) => {
6048 let n = *n as usize;
6049 let idxs = self.pop_flattened_array_slice_specs(n);
6050 let name = names[*arr_idx as usize].as_str();
6051 self.require_array_mutable(name)?;
6052 let rhs = self.pop();
6053 let line = self.line();
6054 let op = crate::compiler::scalar_compound_op_from_byte(*op_byte)
6055 .ok_or_else(|| {
6056 crate::error::StrykeError::runtime(
6057 "VM: NamedArraySliceCompound: bad op byte",
6058 line,
6059 )
6060 })?;
6061 let new_val = vm_interp_result(
6062 self.interp
6063 .compound_assign_named_array_slice(name, idxs, op, rhs, line),
6064 line,
6065 )?;
6066 self.push(new_val);
6067 Ok(())
6068 }
6069 Op::NamedArraySlicePeekLast(arr_idx, n) => {
6070 let n = *n as usize;
6071 let line = self.line();
6072 let name = names[*arr_idx as usize].as_str();
6073 self.require_array_mutable(name)?;
6074 let len = self.stack.len();
6075 if len < n {
6076 return Err(StrykeError::runtime(
6077 "VM: NamedArraySlicePeekLast: stack underflow",
6078 line,
6079 ));
6080 }
6081 let base = len - n;
6082 let idxs =
6083 self.flatten_array_slice_specs_ordered_values(&self.stack[base..])?;
6084 let last = *idxs.last().ok_or_else(|| {
6085 StrykeError::runtime(
6086 "VM: NamedArraySlicePeekLast: empty index list",
6087 line,
6088 )
6089 })?;
6090 let cur = self.interp.scope.get_array_element(name, last);
6091 self.push(cur);
6092 Ok(())
6093 }
6094 Op::NamedArraySliceDropKeysKeepCur(n) => {
6095 let n = *n as usize;
6096 let cur = self.pop();
6097 let _idxs = self.pop_flattened_array_slice_specs(n);
6098 self.push(cur);
6099 Ok(())
6100 }
6101 Op::NamedArraySliceRollValUnderSpecs(n) => {
6102 let n = *n as usize;
6103 let val = self.pop();
6104 let mut specs_rev = Vec::with_capacity(n);
6105 for _ in 0..n {
6106 specs_rev.push(self.pop());
6107 }
6108 self.push(val);
6109 for s in specs_rev.into_iter().rev() {
6110 self.push(s);
6111 }
6112 Ok(())
6113 }
6114 Op::SetNamedArraySliceLastKeep(arr_idx, n) => {
6115 let n = *n as usize;
6116 let line = self.line();
6117 let idxs = self.pop_flattened_array_slice_specs(n);
6118 let name = names[*arr_idx as usize].as_str();
6119 self.require_array_mutable(name)?;
6120 let mut val = self.pop();
6121 if let Some(av) = val.as_array_vec() {
6122 val = av.last().cloned().unwrap_or(StrykeValue::UNDEF);
6123 }
6124 let last = *idxs.last().ok_or_else(|| {
6125 StrykeError::runtime(
6126 "VM: SetNamedArraySliceLastKeep: empty index list",
6127 line,
6128 )
6129 })?;
6130 let val_keep = val.clone();
6131 vm_interp_result(
6132 self.interp
6133 .scope
6134 .set_array_element(name, last, val)
6135 .map(|()| StrykeValue::UNDEF)
6136 .map_err(|e| FlowOrError::Error(e.at_line(line))),
6137 line,
6138 )?;
6139 self.push(val_keep);
6140 Ok(())
6141 }
6142 Op::SetNamedArraySlice(arr_idx, n) => {
6143 let n = *n as usize;
6144 let idxs = self.pop_flattened_array_slice_specs(n);
6145 let name = names[*arr_idx as usize].as_str();
6146 self.require_array_mutable(name)?;
6147 let val = self.pop();
6148 let line = self.line();
6149 vm_interp_result(
6150 self.interp.assign_named_array_slice(name, idxs, val, line),
6151 line,
6152 )?;
6153 Ok(())
6154 }
6155 Op::MakeHash(n) => {
6156 let n = *n as usize;
6157 let mut items = Vec::with_capacity(n);
6158 for _ in 0..n {
6159 items.push(self.pop());
6160 }
6161 items.reverse();
6162 let mut map = IndexMap::new();
6163 let mut i = 0;
6164 while i + 1 < items.len() {
6165 map.insert(items[i].to_string(), items[i + 1].clone());
6166 i += 2;
6167 }
6168 self.push(StrykeValue::hash(map));
6169 Ok(())
6170 }
6171 Op::Range => {
6172 let to = self.pop();
6173 let from = self.pop();
6174 let arr = perl_list_range_expand(from, to);
6175 self.push(StrykeValue::array(arr));
6176 Ok(())
6177 }
6178 Op::RangeStep => {
6179 let step = self.pop();
6180 let to = self.pop();
6181 let from = self.pop();
6182 let arr = crate::value::perl_list_range_expand_stepped(from, to, step);
6183 self.push(StrykeValue::array(arr));
6184 Ok(())
6185 }
6186 Op::ArraySliceRange(arr_idx) => {
6187 let step = self.pop();
6188 let to = self.pop();
6189 let from = self.pop();
6190 let line = self.line();
6191 let name = names[*arr_idx as usize].as_str();
6192 if let Some(real) = name.strip_prefix("__topicstr__") {
6195 let s = self.interp.scope.get_scalar(real).to_string();
6196 let chars: Vec<char> = s.chars().collect();
6197 let n = chars.len() as i64;
6198 let step_i = if step.is_undef() { 1 } else { step.to_int() };
6199 let mut from_i = if from.is_undef() {
6203 if step_i >= 0 {
6204 0
6205 } else {
6206 n - 1
6207 }
6208 } else {
6209 from.to_int()
6210 };
6211 let mut to_i = if to.is_undef() {
6212 if step_i >= 0 {
6213 n - 1
6214 } else {
6215 0
6216 }
6217 } else {
6218 to.to_int()
6219 };
6220 if from_i < 0 {
6221 from_i += n
6222 }
6223 if to_i < 0 {
6224 to_i += n
6225 }
6226 let mut out = String::new();
6227 if step_i > 0 {
6228 let mut i = from_i;
6229 while i <= to_i && i < n {
6230 if i >= 0 {
6231 out.push(chars[i as usize]);
6232 }
6233 i += step_i;
6234 }
6235 } else if step_i < 0 {
6236 let mut i = from_i;
6237 while i >= to_i && i >= 0 {
6238 if i < n {
6239 out.push(chars[i as usize]);
6240 }
6241 i += step_i;
6242 }
6243 }
6244 self.push(StrykeValue::string(out));
6245 return Ok(());
6246 }
6247 let arr_len = self.interp.scope.array_len(name) as i64;
6248 if !crate::compat_mode()
6253 && arr_len == 0
6254 && self.interp.scope.scalar_binding_exists(name)
6255 {
6256 let s = self.interp.scope.get_scalar(name).to_string();
6257 if !s.is_empty() {
6258 let chars: Vec<char> = s.chars().collect();
6259 let n = chars.len() as i64;
6260 let step_i = if step.is_undef() { 1 } else { step.to_int() };
6261 let mut from_i = if from.is_undef() {
6265 if step_i >= 0 {
6266 0
6267 } else {
6268 n - 1
6269 }
6270 } else {
6271 from.to_int()
6272 };
6273 let mut to_i = if to.is_undef() {
6274 if step_i >= 0 {
6275 n - 1
6276 } else {
6277 0
6278 }
6279 } else {
6280 to.to_int()
6281 };
6282 if from_i < 0 {
6283 from_i += n
6284 }
6285 if to_i < 0 {
6286 to_i += n
6287 }
6288 let mut out = String::new();
6289 if step_i > 0 {
6290 let mut i = from_i;
6291 while i <= to_i && i < n {
6292 if i >= 0 {
6293 out.push(chars[i as usize]);
6294 }
6295 i += step_i;
6296 }
6297 } else if step_i < 0 {
6298 let mut i = from_i;
6299 while i >= to_i && i >= 0 {
6300 if i < n {
6301 out.push(chars[i as usize]);
6302 }
6303 i += step_i;
6304 }
6305 }
6306 self.push(StrykeValue::string(out));
6307 return Ok(());
6308 }
6309 }
6310 let indices = match crate::value::compute_array_slice_indices(
6311 arr_len, &from, &to, &step,
6312 ) {
6313 Ok(v) => v,
6314 Err(msg) => {
6315 return Err(StrykeError::runtime(msg, line));
6316 }
6317 };
6318 let mut out = Vec::with_capacity(indices.len());
6319 for i in indices {
6320 out.push(self.interp.scope.get_array_element(name, i));
6321 }
6322 self.push(StrykeValue::array(out));
6323 Ok(())
6324 }
6325 Op::HashSliceRange(hash_idx) => {
6326 let step = self.pop();
6327 let to = self.pop();
6328 let from = self.pop();
6329 let line = self.line();
6330 let name = names[*hash_idx as usize].as_str();
6331 let keys = match crate::value::compute_hash_slice_keys(&from, &to, &step) {
6332 Ok(v) => v,
6333 Err(msg) => {
6334 return Err(StrykeError::runtime(msg, line));
6335 }
6336 };
6337 let h = self.interp.scope.get_hash(name);
6338 let mut out = Vec::with_capacity(keys.len());
6339 for k in &keys {
6340 out.push(h.get(k).cloned().unwrap_or(StrykeValue::UNDEF));
6341 }
6342 self.push(StrykeValue::array(out));
6343 Ok(())
6344 }
6345 Op::ScalarFlipFlop(slot, exclusive) => {
6346 let to = self.pop().to_int();
6347 let from = self.pop().to_int();
6348 let line = self.line();
6349 let v = vm_interp_result(
6350 self.interp
6351 .scalar_flip_flop_eval(from, to, *slot as usize, *exclusive != 0)
6352 .map_err(Into::into),
6353 line,
6354 )?;
6355 self.push(v);
6356 Ok(())
6357 }
6358 Op::RegexFlipFlop(slot, exclusive, lp, lf, rp, rf) => {
6359 let line = self.line();
6360 let left_pat = constants[*lp as usize].as_str_or_empty();
6361 let left_flags = constants[*lf as usize].as_str_or_empty();
6362 let right_pat = constants[*rp as usize].as_str_or_empty();
6363 let right_flags = constants[*rf as usize].as_str_or_empty();
6364 let v = vm_interp_result(
6365 self.interp
6366 .regex_flip_flop_eval(
6367 left_pat.as_str(),
6368 left_flags.as_str(),
6369 right_pat.as_str(),
6370 right_flags.as_str(),
6371 *slot as usize,
6372 *exclusive != 0,
6373 line,
6374 )
6375 .map_err(Into::into),
6376 line,
6377 )?;
6378 self.push(v);
6379 Ok(())
6380 }
6381 Op::RegexEofFlipFlop(slot, exclusive, lp, lf) => {
6382 let line = self.line();
6383 let left_pat = constants[*lp as usize].as_str_or_empty();
6384 let left_flags = constants[*lf as usize].as_str_or_empty();
6385 let v = vm_interp_result(
6386 self.interp
6387 .regex_eof_flip_flop_eval(
6388 left_pat.as_str(),
6389 left_flags.as_str(),
6390 *slot as usize,
6391 *exclusive != 0,
6392 line,
6393 )
6394 .map_err(Into::into),
6395 line,
6396 )?;
6397 self.push(v);
6398 Ok(())
6399 }
6400 Op::RegexFlipFlopExprRhs(slot, exclusive, lp, lf, rhs_idx) => {
6401 let idx = *rhs_idx as usize;
6402 let line = self.line();
6403 let right_m = if let Some(&(start, end)) = self
6404 .regex_flip_flop_rhs_expr_bytecode_ranges
6405 .get(idx)
6406 .and_then(|r| r.as_ref())
6407 {
6408 let val = self.run_block_region(start, end, op_count)?;
6409 val.is_true()
6410 } else {
6411 let e = &self.regex_flip_flop_rhs_expr_entries[idx];
6412 match self.interp.eval_boolean_rvalue_condition(e) {
6413 Ok(b) => b,
6414 Err(FlowOrError::Error(err)) => return Err(err),
6415 Err(FlowOrError::Flow(_)) => {
6416 return Err(StrykeError::runtime(
6417 "unexpected flow in regex flip-flop RHS",
6418 line,
6419 ))
6420 }
6421 }
6422 };
6423 let left_pat = constants[*lp as usize].as_str_or_empty();
6424 let left_flags = constants[*lf as usize].as_str_or_empty();
6425 let v = vm_interp_result(
6426 self.interp
6427 .regex_flip_flop_eval_dynamic_right(
6428 left_pat.as_str(),
6429 left_flags.as_str(),
6430 *slot as usize,
6431 *exclusive != 0,
6432 line,
6433 right_m,
6434 )
6435 .map_err(Into::into),
6436 line,
6437 )?;
6438 self.push(v);
6439 Ok(())
6440 }
6441 Op::RegexFlipFlopDotLineRhs(slot, exclusive, lp, lf, line_cidx) => {
6442 let line = self.line();
6443 let rhs_line = constants[*line_cidx as usize].to_int();
6444 let left_pat = constants[*lp as usize].as_str_or_empty();
6445 let left_flags = constants[*lf as usize].as_str_or_empty();
6446 let v = vm_interp_result(
6447 self.interp
6448 .regex_flip_flop_eval_dot_line_rhs(
6449 left_pat.as_str(),
6450 left_flags.as_str(),
6451 *slot as usize,
6452 *exclusive != 0,
6453 line,
6454 rhs_line,
6455 )
6456 .map_err(Into::into),
6457 line,
6458 )?;
6459 self.push(v);
6460 Ok(())
6461 }
6462
6463 Op::RegexMatch(pat_idx, flags_idx, scalar_g, pos_key_idx) => {
6465 let val = self.pop();
6466 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6467 let flags = constants[*flags_idx as usize].as_str_or_empty();
6468 let line = self.line();
6469 if val.is_iterator() {
6470 let source = crate::map_stream::into_pull_iter(val);
6471 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6472 Ok(r) => r,
6473 Err(FlowOrError::Error(e)) => return Err(e),
6474 Err(FlowOrError::Flow(_)) => {
6475 return Err(StrykeError::runtime(
6476 "unexpected flow in regex compile",
6477 line,
6478 ));
6479 }
6480 };
6481 let global = flags.contains('g');
6482 if global {
6483 self.push(StrykeValue::iterator(std::sync::Arc::new(
6484 crate::map_stream::MatchGlobalStreamIterator::new(source, re),
6485 )));
6486 } else {
6487 self.push(StrykeValue::iterator(std::sync::Arc::new(
6488 crate::map_stream::MatchStreamIterator::new(source, re),
6489 )));
6490 }
6491 return Ok(());
6492 }
6493 let string = val.into_string();
6494 let pos_key_owned = if *pos_key_idx == u16::MAX {
6495 None
6496 } else {
6497 Some(constants[*pos_key_idx as usize].as_str_or_empty())
6498 };
6499 let pos_key: &str = pos_key_owned.as_deref().unwrap_or("_");
6500 match self
6501 .interp
6502 .regex_match_execute(string, &pattern, &flags, *scalar_g, pos_key, line)
6503 {
6504 Ok(v) => {
6505 self.push(v);
6506 Ok(())
6507 }
6508 Err(FlowOrError::Error(e)) => Err(e),
6509 Err(FlowOrError::Flow(_)) => {
6510 Err(StrykeError::runtime("unexpected flow in regex match", line))
6511 }
6512 }
6513 }
6514 Op::RegexSubst(pat_idx, repl_idx, flags_idx, lvalue_idx) => {
6515 let val = self.pop();
6516 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6517 let replacement = constants[*repl_idx as usize].as_str_or_empty();
6518 let flags = constants[*flags_idx as usize].as_str_or_empty();
6519 let line = self.line();
6520 if val.is_iterator() {
6521 let source = crate::map_stream::into_pull_iter(val);
6522 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6523 Ok(r) => r,
6524 Err(FlowOrError::Error(e)) => return Err(e),
6525 Err(FlowOrError::Flow(_)) => {
6526 return Err(StrykeError::runtime(
6527 "unexpected flow in regex compile",
6528 line,
6529 ));
6530 }
6531 };
6532 let global = flags.contains('g');
6533 self.push(StrykeValue::iterator(std::sync::Arc::new(
6534 crate::map_stream::SubstStreamIterator::new(
6535 source,
6536 re,
6537 crate::vm_helper::normalize_replacement_backrefs(&replacement),
6538 global,
6539 ),
6540 )));
6541 return Ok(());
6542 }
6543 let string = val.into_string();
6544 let target = &self.lvalues[*lvalue_idx as usize];
6545 match self.interp.regex_subst_execute(
6546 string,
6547 &pattern,
6548 &replacement,
6549 &flags,
6550 target,
6551 line,
6552 ) {
6553 Ok(v) => {
6554 self.push(v);
6555 Ok(())
6556 }
6557 Err(FlowOrError::Error(e)) => Err(e),
6558 Err(FlowOrError::Flow(_)) => {
6559 Err(StrykeError::runtime("unexpected flow in s///", line))
6560 }
6561 }
6562 }
6563 Op::RegexTransliterate(from_idx, to_idx, flags_idx, lvalue_idx) => {
6564 let val = self.pop();
6565 let from = constants[*from_idx as usize].as_str_or_empty();
6566 let to = constants[*to_idx as usize].as_str_or_empty();
6567 let flags = constants[*flags_idx as usize].as_str_or_empty();
6568 let line = self.line();
6569 if val.is_iterator() {
6570 let source = crate::map_stream::into_pull_iter(val);
6571 self.push(StrykeValue::iterator(std::sync::Arc::new(
6572 crate::map_stream::TransliterateStreamIterator::new(
6573 source, &from, &to, &flags,
6574 ),
6575 )));
6576 return Ok(());
6577 }
6578 let string = val.into_string();
6579 let target = &self.lvalues[*lvalue_idx as usize];
6580 match self
6581 .interp
6582 .regex_transliterate_execute(string, &from, &to, &flags, target, line)
6583 {
6584 Ok(v) => {
6585 self.push(v);
6586 Ok(())
6587 }
6588 Err(FlowOrError::Error(e)) => Err(e),
6589 Err(FlowOrError::Flow(_)) => {
6590 Err(StrykeError::runtime("unexpected flow in tr///", line))
6591 }
6592 }
6593 }
6594 Op::RegexMatchDyn(negate) => {
6595 let rhs = self.pop();
6596 let s = self.pop().into_string();
6597 let line = self.line();
6598 let exec = if let Some((pat, fl)) = rhs.regex_src_and_flags() {
6599 self.interp
6600 .regex_match_execute(s, &pat, &fl, false, "_", line)
6601 } else {
6602 let pattern = rhs.into_string();
6603 self.interp
6604 .regex_match_execute(s, &pattern, "", false, "_", line)
6605 };
6606 match exec {
6607 Ok(v) => {
6608 let matched = v.is_true();
6609 let out = if *negate { !matched } else { matched };
6610 self.push(StrykeValue::integer(if out { 1 } else { 0 }));
6611 }
6612 Err(FlowOrError::Error(e)) => return Err(e),
6613 Err(FlowOrError::Flow(_)) => {
6614 return Err(StrykeError::runtime("unexpected flow in =~", line));
6615 }
6616 }
6617 Ok(())
6618 }
6619 Op::RegexBoolToScalar => {
6620 let v = self.pop();
6621 self.push(if v.is_true() {
6622 StrykeValue::integer(1)
6623 } else {
6624 StrykeValue::string(String::new())
6625 });
6626 Ok(())
6627 }
6628 Op::SetRegexPos => {
6629 let key = self.pop().to_string();
6630 let val = self.pop();
6631 if val.is_undef() {
6632 self.interp.regex_pos.insert(key, None);
6633 } else {
6634 let u = val.to_int().max(0) as usize;
6635 self.interp.regex_pos.insert(key, Some(u));
6636 }
6637 Ok(())
6638 }
6639 Op::LoadRegex(pat_idx, flags_idx) => {
6640 let pattern = constants[*pat_idx as usize].as_str_or_empty();
6641 let flags = constants[*flags_idx as usize].as_str_or_empty();
6642 let line = self.line();
6643 let pattern_owned = pattern.clone();
6644 let re = match self.interp.compile_regex(&pattern, &flags, line) {
6645 Ok(r) => r,
6646 Err(FlowOrError::Error(e)) => return Err(e),
6647 Err(FlowOrError::Flow(_)) => {
6648 return Err(StrykeError::runtime(
6649 "unexpected flow in qr// compile",
6650 line,
6651 ));
6652 }
6653 };
6654 self.push(StrykeValue::regex(re, pattern_owned, flags.to_string()));
6655 Ok(())
6656 }
6657 Op::ConcatAppend(idx) => {
6658 let rhs = self.pop();
6659 let n = names[*idx as usize].as_str();
6660 let line = self.line();
6661 let result = self
6662 .interp
6663 .scope
6664 .scalar_concat_inplace(n, &rhs)
6665 .map_err(|e| e.at_line(line))?;
6666 self.push(result);
6667 Ok(())
6668 }
6669 Op::ConcatAppendSlot(slot) => {
6670 let rhs = self.pop();
6671 let result = self.interp.scope.scalar_slot_concat_inplace(*slot, &rhs);
6672 self.push(result);
6673 Ok(())
6674 }
6675 Op::ConcatAppendSlotVoid(slot) => {
6676 let rhs = self.pop();
6677 self.interp.scope.scalar_slot_concat_inplace(*slot, &rhs);
6678 Ok(())
6679 }
6680 Op::SlotLtIntJumpIfFalse(slot, limit, target) => {
6681 let val = self.interp.scope.get_scalar_slot(*slot);
6682 let lt = if let Some(i) = val.as_integer() {
6683 i < *limit as i64
6684 } else {
6685 val.to_number() < *limit as f64
6686 };
6687 if !lt {
6688 self.ip = *target;
6689 }
6690 Ok(())
6691 }
6692 Op::SlotIncLtIntJumpBack(slot, limit, body_target) => {
6693 let next_i = self
6699 .interp
6700 .scope
6701 .get_scalar_slot(*slot)
6702 .to_int()
6703 .wrapping_add(1);
6704 self.interp
6705 .scope
6706 .set_scalar_slot(*slot, StrykeValue::integer(next_i));
6707 if next_i < *limit as i64 {
6708 self.ip = *body_target;
6709 }
6710 Ok(())
6711 }
6712 Op::AccumSumLoop(sum_slot, i_slot, limit) => {
6713 let mut sum = self.interp.scope.get_scalar_slot(*sum_slot).to_int();
6718 let mut i = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6719 let limit = *limit as i64;
6720 while i < limit {
6721 sum = sum.wrapping_add(i);
6722 i = i.wrapping_add(1);
6723 }
6724 self.interp
6725 .scope
6726 .set_scalar_slot(*sum_slot, StrykeValue::integer(sum));
6727 self.interp
6728 .scope
6729 .set_scalar_slot(*i_slot, StrykeValue::integer(i));
6730 Ok(())
6731 }
6732 Op::AddHashElemPlainKeyToSlot(sum_slot, k_name_idx, h_name_idx) => {
6733 let k_name = names[*k_name_idx as usize].as_str();
6738 let h_name = names[*h_name_idx as usize].as_str();
6739 self.interp.touch_env_hash(h_name);
6740 let key = self.interp.scope.get_scalar(k_name).to_string();
6741 let elem = self.interp.scope.get_hash_element(h_name, &key);
6742 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6743 let new_v =
6744 if let (Some(a), Some(b)) = (cur.as_integer(), elem.as_integer()) {
6745 StrykeValue::integer(a.wrapping_add(b))
6746 } else {
6747 StrykeValue::float(cur.to_number() + elem.to_number())
6748 };
6749 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6750 Ok(())
6751 }
6752 Op::AddHashElemSlotKeyToSlot(sum_slot, k_slot, h_name_idx) => {
6753 let h_name = names[*h_name_idx as usize].as_str();
6756 self.interp.touch_env_hash(h_name);
6757 let key_val = self.interp.scope.get_scalar_slot(*k_slot);
6758 let key = key_val.to_string();
6759 let elem = self.interp.scope.get_hash_element(h_name, &key);
6760 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6761 let new_v =
6762 if let (Some(a), Some(b)) = (cur.as_integer(), elem.as_integer()) {
6763 StrykeValue::integer(a.wrapping_add(b))
6764 } else {
6765 StrykeValue::float(cur.to_number() + elem.to_number())
6766 };
6767 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6768 Ok(())
6769 }
6770 Op::SumHashValuesToSlot(sum_slot, h_name_idx) => {
6771 let h_name = names[*h_name_idx as usize].as_str();
6777 self.interp.touch_env_hash(h_name);
6778 let cur = self.interp.scope.get_scalar_slot(*sum_slot);
6779 let mut int_acc: i64 = cur.as_integer().unwrap_or(0);
6780 let mut float_acc: f64 = 0.0;
6781 let mut is_int = cur.as_integer().is_some();
6782 if !is_int {
6783 float_acc = cur.to_number();
6784 }
6785 self.interp.scope.for_each_hash_value(h_name, |v| {
6789 if is_int {
6790 if let Some(x) = v.as_integer() {
6791 int_acc = int_acc.wrapping_add(x);
6792 return;
6793 }
6794 float_acc = int_acc as f64;
6795 is_int = false;
6796 }
6797 float_acc += v.to_number();
6798 });
6799 let new_v = if is_int {
6800 StrykeValue::integer(int_acc)
6801 } else {
6802 StrykeValue::float(float_acc)
6803 };
6804 self.interp.scope.set_scalar_slot(*sum_slot, new_v);
6805 Ok(())
6806 }
6807 Op::SetHashIntTimesLoop(h_name_idx, i_slot, k, limit) => {
6808 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6813 let lim = *limit as i64;
6814 if i_cur < lim {
6815 let n = names[*h_name_idx as usize].as_str();
6816 self.require_hash_mutable(n)?;
6817 self.interp.touch_env_hash(n);
6818 let line = self.line();
6819 self.interp
6820 .scope
6821 .set_hash_int_times_range(n, i_cur, lim, *k as i64)
6822 .map_err(|e| e.at_line(line))?;
6823 }
6824 self.interp
6825 .scope
6826 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6827 Ok(())
6828 }
6829 Op::PushIntRangeToArrayLoop(arr_name_idx, i_slot, limit) => {
6830 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6835 let lim = *limit as i64;
6836 if i_cur < lim {
6837 let n = names[*arr_name_idx as usize].as_str();
6838 self.require_array_mutable(n)?;
6839 let line = self.line();
6840 self.interp
6841 .scope
6842 .push_int_range_to_array(n, i_cur, lim)
6843 .map_err(|e| e.at_line(line))?;
6844 }
6845 self.interp
6846 .scope
6847 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6848 Ok(())
6849 }
6850 Op::ConcatConstSlotLoop(const_idx, s_slot, i_slot, limit) => {
6851 let i_cur = self.interp.scope.get_scalar_slot(*i_slot).to_int();
6858 let lim = *limit as i64;
6859 if i_cur < lim {
6860 let n_iters = (lim - i_cur) as usize;
6861 let rhs = constants[*const_idx as usize].as_str_or_empty();
6862 if !self
6863 .interp
6864 .scope
6865 .scalar_slot_concat_repeat_inplace(*s_slot, &rhs, n_iters)
6866 {
6867 self.interp
6868 .scope
6869 .scalar_slot_concat_repeat_slow(*s_slot, &rhs, n_iters);
6870 }
6871 }
6872 self.interp
6873 .scope
6874 .set_scalar_slot(*i_slot, StrykeValue::integer(lim));
6875 Ok(())
6876 }
6877 Op::AddAssignSlotSlot(dst, src) => {
6878 let a = self.interp.scope.get_scalar_slot(*dst);
6879 let b = self.interp.scope.get_scalar_slot(*src);
6880 let result = crate::value::compat_add(&a, &b);
6881 self.interp.scope.set_scalar_slot(*dst, result.clone());
6882 self.push(result);
6883 Ok(())
6884 }
6885 Op::AddAssignSlotSlotVoid(dst, src) => {
6886 let a = self.interp.scope.get_scalar_slot(*dst);
6887 let b = self.interp.scope.get_scalar_slot(*src);
6888 let result = crate::value::compat_add(&a, &b);
6889 self.interp.scope.set_scalar_slot(*dst, result);
6890 Ok(())
6891 }
6892 Op::SubAssignSlotSlot(dst, src) => {
6893 let a = self.interp.scope.get_scalar_slot(*dst);
6894 let b = self.interp.scope.get_scalar_slot(*src);
6895 let result = crate::value::compat_sub(&a, &b);
6896 self.interp.scope.set_scalar_slot(*dst, result.clone());
6897 self.push(result);
6898 Ok(())
6899 }
6900 Op::MulAssignSlotSlot(dst, src) => {
6901 let a = self.interp.scope.get_scalar_slot(*dst);
6902 let b = self.interp.scope.get_scalar_slot(*src);
6903 let result = crate::value::compat_mul(&a, &b);
6904 self.interp.scope.set_scalar_slot(*dst, result.clone());
6905 self.push(result);
6906 Ok(())
6907 }
6908
6909 Op::GetScalarSlot(slot) => {
6911 let val = self.interp.scope.get_scalar_slot(*slot);
6912 self.push(val);
6913 Ok(())
6914 }
6915 Op::SetScalarSlot(slot) => {
6916 let val = self.pop();
6917 self.interp
6918 .scope
6919 .set_scalar_slot_checked(*slot, val, None)
6920 .map_err(|e| e.at_line(self.line()))?;
6921 Ok(())
6922 }
6923 Op::SetScalarSlotKeep(slot) => {
6924 let val = self.peek().dup_stack();
6925 self.interp
6926 .scope
6927 .set_scalar_slot_checked(*slot, val, None)
6928 .map_err(|e| e.at_line(self.line()))?;
6929 Ok(())
6930 }
6931 Op::DeclareScalarSlot(slot, name_idx) => {
6932 let val = self.pop();
6933 let name_opt = if *name_idx == u16::MAX {
6934 None
6935 } else {
6936 Some(names[*name_idx as usize].as_str())
6937 };
6938 self.interp.scope.declare_scalar_slot(*slot, val, name_opt);
6939 Ok(())
6940 }
6941 Op::GetArg(idx) => {
6942 let val = if let Some(frame) = self.call_stack.last() {
6944 let arg_pos = frame.stack_base + *idx as usize;
6945 self.stack
6946 .get(arg_pos)
6947 .cloned()
6948 .unwrap_or(StrykeValue::UNDEF)
6949 } else {
6950 StrykeValue::UNDEF
6951 };
6952 self.push(val);
6953 Ok(())
6954 }
6955
6956 Op::ReadIntoVar(name_idx) => {
6957 let length = self.pop().to_int() as usize;
6958 let fh_val = self.pop();
6959 let name = &names[*name_idx as usize];
6960 let line = self.line();
6961 let result = vm_interp_result(
6962 self.interp.builtin_read_into(fh_val, name, length, line),
6963 line,
6964 )?;
6965 self.push(result);
6966 Ok(())
6967 }
6968 Op::ChompInPlace(lvalue_idx) => {
6969 let val = self.pop();
6970 let target = &self.lvalues[*lvalue_idx as usize];
6971 let line = self.line();
6972 match self.interp.chomp_inplace_execute(val, target) {
6973 Ok(v) => self.push(v),
6974 Err(FlowOrError::Error(e)) => return Err(e),
6975 Err(FlowOrError::Flow(_)) => {
6976 return Err(StrykeError::runtime("unexpected flow in chomp", line));
6977 }
6978 }
6979 Ok(())
6980 }
6981 Op::ChopInPlace(lvalue_idx) => {
6982 let val = self.pop();
6983 let target = &self.lvalues[*lvalue_idx as usize];
6984 let line = self.line();
6985 match self.interp.chop_inplace_execute(val, target) {
6986 Ok(v) => self.push(v),
6987 Err(FlowOrError::Error(e)) => return Err(e),
6988 Err(FlowOrError::Flow(_)) => {
6989 return Err(StrykeError::runtime("unexpected flow in chop", line));
6990 }
6991 }
6992 Ok(())
6993 }
6994 Op::SubstrFourArg(idx) => {
6995 let (string_e, offset_e, length_e, rep_e) =
6996 &self.substr_four_arg_entries[*idx as usize];
6997 let v = vm_interp_result(
6998 self.interp.eval_substr_expr(
6999 string_e,
7000 offset_e,
7001 length_e.as_ref(),
7002 Some(rep_e),
7003 self.line(),
7004 ),
7005 self.line(),
7006 )?;
7007 self.push(v);
7008 Ok(())
7009 }
7010 Op::KeysExpr(idx) => {
7011 let i = *idx as usize;
7012 let line = self.line();
7013 let v = if let Some(&(start, end)) = self
7014 .keys_expr_bytecode_ranges
7015 .get(i)
7016 .and_then(|r| r.as_ref())
7017 {
7018 let val = self.run_block_region(start, end, op_count)?;
7019 vm_interp_result(VMHelper::keys_from_value(val, line), line)?
7020 } else {
7021 let e = &self.keys_expr_entries[i];
7022 vm_interp_result(self.interp.eval_keys_expr(e, line), line)?
7023 };
7024 self.push(v);
7025 Ok(())
7026 }
7027 Op::KeysExprScalar(idx) => {
7028 let i = *idx as usize;
7029 let line = self.line();
7030 let v = if let Some(&(start, end)) = self
7031 .keys_expr_bytecode_ranges
7032 .get(i)
7033 .and_then(|r| r.as_ref())
7034 {
7035 let val = self.run_block_region(start, end, op_count)?;
7036 vm_interp_result(VMHelper::keys_from_value(val, line), line)?
7037 } else {
7038 let e = &self.keys_expr_entries[i];
7039 vm_interp_result(self.interp.eval_keys_expr(e, line), line)?
7040 };
7041 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
7042 self.push(StrykeValue::integer(n));
7043 Ok(())
7044 }
7045 Op::ValuesExpr(idx) => {
7046 let i = *idx as usize;
7047 let line = self.line();
7048 let v = if let Some(&(start, end)) = self
7049 .values_expr_bytecode_ranges
7050 .get(i)
7051 .and_then(|r| r.as_ref())
7052 {
7053 let val = self.run_block_region(start, end, op_count)?;
7054 vm_interp_result(VMHelper::values_from_value(val, line), line)?
7055 } else {
7056 let e = &self.values_expr_entries[i];
7057 vm_interp_result(self.interp.eval_values_expr(e, line), line)?
7058 };
7059 self.push(v);
7060 Ok(())
7061 }
7062 Op::ValuesExprScalar(idx) => {
7063 let i = *idx as usize;
7064 let line = self.line();
7065 let v = if let Some(&(start, end)) = self
7066 .values_expr_bytecode_ranges
7067 .get(i)
7068 .and_then(|r| r.as_ref())
7069 {
7070 let val = self.run_block_region(start, end, op_count)?;
7071 vm_interp_result(VMHelper::values_from_value(val, line), line)?
7072 } else {
7073 let e = &self.values_expr_entries[i];
7074 vm_interp_result(self.interp.eval_values_expr(e, line), line)?
7075 };
7076 let n = v.as_array_vec().map(|a| a.len()).unwrap_or(0) as i64;
7077 self.push(StrykeValue::integer(n));
7078 Ok(())
7079 }
7080 Op::DeleteExpr(idx) => {
7081 let e = &self.delete_expr_entries[*idx as usize];
7082 let v = vm_interp_result(
7083 self.interp.eval_delete_operand(e, self.line()),
7084 self.line(),
7085 )?;
7086 self.push(v);
7087 Ok(())
7088 }
7089 Op::ExistsExpr(idx) => {
7090 let e = &self.exists_expr_entries[*idx as usize];
7091 let v = vm_interp_result(
7092 self.interp.eval_exists_operand(e, self.line()),
7093 self.line(),
7094 )?;
7095 self.push(v);
7096 Ok(())
7097 }
7098 Op::PushExpr(idx) => {
7099 let (array, values) = &self.push_expr_entries[*idx as usize];
7100 let v = vm_interp_result(
7101 self.interp
7102 .eval_push_expr(array, values.as_slice(), self.line()),
7103 self.line(),
7104 )?;
7105 self.push(v);
7106 Ok(())
7107 }
7108 Op::PopExpr(idx) => {
7109 let e = &self.pop_expr_entries[*idx as usize];
7110 let v = vm_interp_result(
7111 self.interp.eval_pop_expr(e, self.line()),
7112 self.line(),
7113 )?;
7114 self.push(v);
7115 Ok(())
7116 }
7117 Op::ShiftExpr(idx) => {
7118 let e = &self.shift_expr_entries[*idx as usize];
7119 let v = vm_interp_result(
7120 self.interp.eval_shift_expr(e, self.line()),
7121 self.line(),
7122 )?;
7123 self.push(v);
7124 Ok(())
7125 }
7126 Op::UnshiftExpr(idx) => {
7127 let (array, values) = &self.unshift_expr_entries[*idx as usize];
7128 let v = vm_interp_result(
7129 self.interp
7130 .eval_unshift_expr(array, values.as_slice(), self.line()),
7131 self.line(),
7132 )?;
7133 self.push(v);
7134 Ok(())
7135 }
7136 Op::SpliceExpr(idx) => {
7137 let (array, offset, length, replacement) =
7138 &self.splice_expr_entries[*idx as usize];
7139 let v = vm_interp_result(
7140 self.interp.eval_splice_expr(
7141 array,
7142 offset.as_ref(),
7143 length.as_ref(),
7144 replacement.as_slice(),
7145 self.interp.wantarray_kind,
7146 self.line(),
7147 ),
7148 self.line(),
7149 )?;
7150 self.push(v);
7151 Ok(())
7152 }
7153
7154 Op::MakeScalarRef => {
7156 let val = self.pop();
7157 self.push(StrykeValue::scalar_ref(Arc::new(RwLock::new(val))));
7158 Ok(())
7159 }
7160 Op::MakeScalarBindingRef(name_idx) => {
7161 let name = names[*name_idx as usize].clone();
7162 self.push(StrykeValue::scalar_binding_ref(name));
7163 Ok(())
7164 }
7165 Op::MakeArrayBindingRef(name_idx) => {
7166 let name = &names[*name_idx as usize];
7167 let arc = self.interp.scope.promote_array_to_shared(name);
7171 self.push(StrykeValue::array_ref(arc));
7172 Ok(())
7173 }
7174 Op::MakeHashBindingRef(name_idx) => {
7175 let name = &names[*name_idx as usize];
7176 self.interp.touch_env_hash(name);
7181 let arc = self.interp.scope.promote_hash_to_shared(name);
7182 self.push(StrykeValue::hash_ref(arc));
7183 Ok(())
7184 }
7185 Op::MakeArrayRefAlias => {
7186 let v = self.pop();
7187 let line = self.line();
7188 let out =
7189 vm_interp_result(self.interp.make_array_ref_alias(v, line), line)?;
7190 self.push(out);
7191 Ok(())
7192 }
7193 Op::MakeHashRefAlias => {
7194 let v = self.pop();
7195 let line = self.line();
7196 let out = vm_interp_result(self.interp.make_hash_ref_alias(v, line), line)?;
7197 self.push(out);
7198 Ok(())
7199 }
7200 Op::MakeArrayRef => {
7201 let val = self.pop();
7202 let val = self.interp.scope.resolve_container_binding_ref(val);
7203 let arr = if let Some(a) = val.as_array_vec() {
7204 a
7205 } else {
7206 vec![val]
7207 };
7208 self.push(StrykeValue::array_ref(Arc::new(RwLock::new(arr))));
7209 Ok(())
7210 }
7211 Op::MakeHashRef => {
7212 let val = self.pop();
7213 let map = if let Some(h) = val.as_hash_map() {
7214 h
7215 } else {
7216 let items = val.to_list();
7217 let mut m = IndexMap::new();
7218 let mut i = 0;
7219 while i + 1 < items.len() {
7220 m.insert(items[i].to_string(), items[i + 1].clone());
7221 i += 2;
7222 }
7223 m
7224 };
7225 self.push(StrykeValue::hash_ref(Arc::new(RwLock::new(map))));
7226 Ok(())
7227 }
7228 Op::MakeCodeRef(block_idx, sig_idx) => {
7229 let block = self.blocks[*block_idx as usize].clone();
7230 let params = self.code_ref_sigs[*sig_idx as usize].clone();
7231 let captured = self.interp.scope.capture();
7232 self.push(StrykeValue::code_ref(Arc::new(crate::value::StrykeSub {
7233 name: "__ANON__".to_string(),
7234 params,
7235 body: block,
7236 closure_env: Some(captured),
7237 prototype: None,
7238 fib_like: None,
7239 })));
7240 Ok(())
7241 }
7242 Op::LoadNamedSubRef(name_idx) => {
7243 let name = names[*name_idx as usize].as_str();
7244 let line = self.line();
7245 let sub = self.interp.resolve_sub_by_name(name).ok_or_else(|| {
7246 StrykeError::runtime(
7247 self.interp.undefined_subroutine_resolve_message(name),
7248 line,
7249 )
7250 })?;
7251 self.push(StrykeValue::code_ref(sub));
7252 Ok(())
7253 }
7254 Op::LoadDynamicSubRef => {
7255 let name = self.pop().to_string();
7256 let line = self.line();
7257 let sub = self.interp.resolve_sub_by_name(&name).ok_or_else(|| {
7258 StrykeError::runtime(
7259 self.interp.undefined_subroutine_resolve_message(&name),
7260 line,
7261 )
7262 })?;
7263 self.push(StrykeValue::code_ref(sub));
7264 Ok(())
7265 }
7266 Op::LoadDynamicTypeglob => {
7267 let name = self.pop().to_string();
7268 let n = self.interp.resolve_io_handle_name(&name);
7269 self.push(StrykeValue::string(n));
7270 Ok(())
7271 }
7272 Op::CopyTypeglobSlots(lhs_i, rhs_i) => {
7273 let lhs = self.names[*lhs_i as usize].as_str();
7274 let rhs = self.names[*rhs_i as usize].as_str();
7275 let line = self.line();
7276 self.interp
7277 .copy_typeglob_slots(lhs, rhs, line)
7278 .map_err(|e| e.at_line(line))?;
7279 Ok(())
7280 }
7281 Op::TypeglobAssignFromValue(name_idx) => {
7282 let val = self.pop();
7283 let name = self.names[*name_idx as usize].as_str();
7284 let line = self.line();
7285 vm_interp_result(
7286 self.interp.assign_typeglob_value(name, val.clone(), line),
7287 line,
7288 )?;
7289 self.push(val);
7290 Ok(())
7291 }
7292 Op::TypeglobAssignFromValueDynamic => {
7293 let val = self.pop();
7294 let name = self.pop().to_string();
7295 let line = self.line();
7296 vm_interp_result(
7297 self.interp.assign_typeglob_value(&name, val.clone(), line),
7298 line,
7299 )?;
7300 self.push(val);
7301 Ok(())
7302 }
7303 Op::CopyTypeglobSlotsDynamicLhs(rhs_i) => {
7304 let lhs = self.pop().to_string();
7305 let rhs = self.names[*rhs_i as usize].as_str();
7306 let line = self.line();
7307 self.interp
7308 .copy_typeglob_slots(&lhs, rhs, line)
7309 .map_err(|e| e.at_line(line))?;
7310 Ok(())
7311 }
7312 Op::SymbolicDeref(kind_byte) => {
7313 let v = self.pop();
7314 let kind = match *kind_byte {
7315 0 => Sigil::Scalar,
7316 1 => Sigil::Array,
7317 2 => Sigil::Hash,
7318 3 => Sigil::Typeglob,
7319 _ => {
7320 return Err(StrykeError::runtime(
7321 "VM: bad SymbolicDeref kind byte",
7322 self.line(),
7323 ));
7324 }
7325 };
7326 let line = self.line();
7327 let out =
7328 vm_interp_result(self.interp.symbolic_deref(v, kind, line), line)?;
7329 self.push(out);
7330 Ok(())
7331 }
7332
7333 Op::ArrowArray => {
7335 let idx = self.pop().to_int();
7336 let r = self.pop();
7337 let line = self.line();
7338 let v = vm_interp_result(
7339 self.interp.read_arrow_array_element(r, idx, line),
7340 line,
7341 )?;
7342 self.push(v);
7343 Ok(())
7344 }
7345 Op::ArrowHash => {
7346 let key = self.pop().to_string();
7347 let r = self.pop();
7348 let line = self.line();
7349 let v = vm_interp_result(
7350 self.interp.read_arrow_hash_element(r, key.as_str(), line),
7351 line,
7352 )?;
7353 self.push(v);
7354 Ok(())
7355 }
7356 Op::SetArrowHash => {
7357 let key = self.pop().to_string();
7358 let r = self.pop();
7359 let val = self.pop();
7360 let line = self.line();
7361 vm_interp_result(
7362 self.interp.assign_arrow_hash_deref(r, key, val, line),
7363 line,
7364 )?;
7365 Ok(())
7366 }
7367 Op::SetArrowArray => {
7368 let idx = self.pop().to_int();
7369 let r = self.pop();
7370 let val = self.pop();
7371 let line = self.line();
7372 vm_interp_result(
7373 self.interp.assign_arrow_array_deref(r, idx, val, line),
7374 line,
7375 )?;
7376 Ok(())
7377 }
7378 Op::SetArrowArrayKeep => {
7379 let idx = self.pop().to_int();
7380 let r = self.pop();
7381 let val = self.pop();
7382 let val_keep = val.clone();
7383 let line = self.line();
7384 vm_interp_result(
7385 self.interp.assign_arrow_array_deref(r, idx, val, line),
7386 line,
7387 )?;
7388 self.push(val_keep);
7389 Ok(())
7390 }
7391 Op::SetArrowHashKeep => {
7392 let key = self.pop().to_string();
7393 let r = self.pop();
7394 let val = self.pop();
7395 let val_keep = val.clone();
7396 let line = self.line();
7397 vm_interp_result(
7398 self.interp.assign_arrow_hash_deref(r, key, val, line),
7399 line,
7400 )?;
7401 self.push(val_keep);
7402 Ok(())
7403 }
7404 Op::ArrowArrayPostfix(b) => {
7405 let idx = self.pop().to_int();
7406 let r = self.pop();
7407 let line = self.line();
7408 let old = vm_interp_result(
7409 self.interp.arrow_array_postfix(r, idx, *b == 1, line),
7410 line,
7411 )?;
7412 self.push(old);
7413 Ok(())
7414 }
7415 Op::ArrowHashPostfix(b) => {
7416 let key = self.pop().to_string();
7417 let r = self.pop();
7418 let line = self.line();
7419 let old = vm_interp_result(
7420 self.interp.arrow_hash_postfix(r, key, *b == 1, line),
7421 line,
7422 )?;
7423 self.push(old);
7424 Ok(())
7425 }
7426 Op::SetSymbolicScalarRef => {
7427 let r = self.pop();
7428 let val = self.pop();
7429 let line = self.line();
7430 vm_interp_result(self.interp.assign_scalar_ref_deref(r, val, line), line)?;
7431 Ok(())
7432 }
7433 Op::SetSymbolicScalarRefKeep => {
7434 let r = self.pop();
7435 let val = self.pop();
7436 let val_keep = val.clone();
7437 let line = self.line();
7438 vm_interp_result(self.interp.assign_scalar_ref_deref(r, val, line), line)?;
7439 self.push(val_keep);
7440 Ok(())
7441 }
7442 Op::SetSymbolicArrayRef => {
7443 let r = self.pop();
7444 let val = self.pop();
7445 let line = self.line();
7446 vm_interp_result(
7447 self.interp.assign_symbolic_array_ref_deref(r, val, line),
7448 line,
7449 )?;
7450 Ok(())
7451 }
7452 Op::SetSymbolicHashRef => {
7453 let r = self.pop();
7454 let val = self.pop();
7455 let line = self.line();
7456 vm_interp_result(
7457 self.interp.assign_symbolic_hash_ref_deref(r, val, line),
7458 line,
7459 )?;
7460 Ok(())
7461 }
7462 Op::SetSymbolicTypeglobRef => {
7463 let r = self.pop();
7464 let val = self.pop();
7465 let line = self.line();
7466 vm_interp_result(
7467 self.interp.assign_symbolic_typeglob_ref_deref(r, val, line),
7468 line,
7469 )?;
7470 Ok(())
7471 }
7472 Op::SymbolicScalarRefPostfix(b) => {
7473 let r = self.pop();
7474 let line = self.line();
7475 let old = vm_interp_result(
7476 self.interp.symbolic_scalar_ref_postfix(r, *b == 1, line),
7477 line,
7478 )?;
7479 self.push(old);
7480 Ok(())
7481 }
7482 Op::ArrowCall(wa) => {
7483 let want = WantarrayCtx::from_byte(*wa);
7484 let args_val = self.pop();
7485 let r = self.pop();
7486 let r = if let Some(inner) = r.as_scalar_ref() {
7488 inner.read().clone()
7489 } else {
7490 r
7491 };
7492 let args = args_val.to_list();
7493 if let Some(sub) = r.as_code_ref() {
7494 if let Some(hof_result) =
7499 self.interp.try_hof_dispatch(&sub, &args, want, self.line())
7500 {
7501 let v = vm_interp_result(hof_result, self.line())?;
7502 self.push(v);
7503 return Ok(());
7504 }
7505 self.interp.current_sub_stack.push(sub.clone());
7506 let saved_wa = self.interp.wantarray_kind;
7507 self.interp.wantarray_kind = want;
7508 self.interp.scope_push_hook();
7509 self.interp.scope.declare_array("_", args.clone());
7510 if let Some(ref env) = sub.closure_env {
7511 self.interp.scope.restore_capture(env);
7512 }
7513 let line = self.line();
7514 let argv = self.interp.scope.take_sub_underscore().unwrap_or_default();
7515 self.interp
7516 .apply_sub_signature(sub.as_ref(), &argv, line)
7517 .map_err(|e| e.at_line(line))?;
7518 self.interp.scope.declare_array("_", argv.clone());
7519 self.interp.scope.set_closure_args(&argv);
7521 let result = self.interp.exec_block_no_scope(&sub.body);
7522 self.interp.wantarray_kind = saved_wa;
7523 self.interp.scope_pop_hook();
7524 self.interp.current_sub_stack.pop();
7525 match result {
7526 Ok(v) => self.push(v),
7527 Err(crate::vm_helper::FlowOrError::Flow(
7528 crate::vm_helper::Flow::Return(v),
7529 )) => self.push(v),
7530 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
7531 Err(_) => self.push(StrykeValue::UNDEF),
7532 }
7533 } else {
7534 return Err(StrykeError::runtime("Not a code reference", self.line()));
7535 }
7536 Ok(())
7537 }
7538 Op::IndirectCall(argc, wa, pass_flag) => {
7539 let want = WantarrayCtx::from_byte(*wa);
7540 let line = self.line();
7541 let arg_vals = if *pass_flag != 0 {
7542 self.interp.scope.get_array("_")
7543 } else {
7544 let n = *argc as usize;
7545 let mut args = Vec::with_capacity(n);
7546 for _ in 0..n {
7547 args.push(self.pop());
7548 }
7549 args.reverse();
7550 args
7551 };
7552 let target = self.pop();
7553 if let Some(sub) = target.as_code_ref() {
7555 if let Some(hof_result) =
7556 self.interp.try_hof_dispatch(&sub, &arg_vals, want, line)
7557 {
7558 let v = vm_interp_result(hof_result, line)?;
7559 self.push(v);
7560 return Ok(());
7561 }
7562 }
7563 let r = self
7564 .interp
7565 .dispatch_indirect_call(target, arg_vals, want, line);
7566 let v = vm_interp_result(r, line)?;
7567 self.push(v);
7568 Ok(())
7569 }
7570
7571 Op::MethodCall(name_idx, argc, wa) => {
7573 self.run_method_op(*name_idx, *argc, *wa, false)?;
7574 Ok(())
7575 }
7576 Op::MethodCallSuper(name_idx, argc, wa) => {
7577 self.run_method_op(*name_idx, *argc, *wa, true)?;
7578 Ok(())
7579 }
7580
7581 Op::FileTestOp(test) => {
7583 let path = self.pop().to_string();
7584 let op = *test as char;
7585 if matches!(op, 'M' | 'A' | 'C') {
7587 #[cfg(unix)]
7588 {
7589 let v = match crate::perl_fs::filetest_age_days(&path, op) {
7590 Some(days) => StrykeValue::float(days),
7591 None => StrykeValue::UNDEF,
7592 };
7593 self.push(v);
7594 return Ok(());
7595 }
7596 #[cfg(not(unix))]
7597 {
7598 self.push(StrykeValue::UNDEF);
7599 return Ok(());
7600 }
7601 }
7602 if op == 's' {
7604 let v = match std::fs::metadata(&path) {
7605 Ok(m) => StrykeValue::integer(m.len() as i64),
7606 Err(_) => StrykeValue::UNDEF,
7607 };
7608 self.push(v);
7609 return Ok(());
7610 }
7611 let result = match op {
7612 'e' => std::path::Path::new(&path).exists(),
7613 'f' => std::path::Path::new(&path).is_file(),
7614 'd' => std::path::Path::new(&path).is_dir(),
7615 'l' => std::path::Path::new(&path).is_symlink(),
7616 #[cfg(unix)]
7617 'r' => crate::perl_fs::filetest_effective_access(&path, 4),
7618 #[cfg(not(unix))]
7619 'r' => std::fs::metadata(&path).is_ok(),
7620 #[cfg(unix)]
7621 'w' => crate::perl_fs::filetest_effective_access(&path, 2),
7622 #[cfg(not(unix))]
7623 'w' => std::fs::metadata(&path).is_ok(),
7624 #[cfg(unix)]
7625 'x' => crate::perl_fs::filetest_effective_access(&path, 1),
7626 #[cfg(not(unix))]
7627 'x' => false,
7628 #[cfg(unix)]
7629 'o' => crate::perl_fs::filetest_owned_effective(&path),
7630 #[cfg(not(unix))]
7631 'o' => false,
7632 #[cfg(unix)]
7633 'R' => crate::perl_fs::filetest_real_access(&path, libc::R_OK),
7634 #[cfg(not(unix))]
7635 'R' => false,
7636 #[cfg(unix)]
7637 'W' => crate::perl_fs::filetest_real_access(&path, libc::W_OK),
7638 #[cfg(not(unix))]
7639 'W' => false,
7640 #[cfg(unix)]
7641 'X' => crate::perl_fs::filetest_real_access(&path, libc::X_OK),
7642 #[cfg(not(unix))]
7643 'X' => false,
7644 #[cfg(unix)]
7645 'O' => crate::perl_fs::filetest_owned_real(&path),
7646 #[cfg(not(unix))]
7647 'O' => false,
7648 'z' => std::fs::metadata(&path)
7649 .map(|m| m.len() == 0)
7650 .unwrap_or(true),
7651 't' => crate::perl_fs::filetest_is_tty(&path),
7652 #[cfg(unix)]
7653 'p' => crate::perl_fs::filetest_is_pipe(&path),
7654 #[cfg(not(unix))]
7655 'p' => false,
7656 #[cfg(unix)]
7657 'S' => crate::perl_fs::filetest_is_socket(&path),
7658 #[cfg(not(unix))]
7659 'S' => false,
7660 #[cfg(unix)]
7661 'b' => crate::perl_fs::filetest_is_block_device(&path),
7662 #[cfg(not(unix))]
7663 'b' => false,
7664 #[cfg(unix)]
7665 'c' => crate::perl_fs::filetest_is_char_device(&path),
7666 #[cfg(not(unix))]
7667 'c' => false,
7668 #[cfg(unix)]
7669 'u' => crate::perl_fs::filetest_is_setuid(&path),
7670 #[cfg(not(unix))]
7671 'u' => false,
7672 #[cfg(unix)]
7673 'g' => crate::perl_fs::filetest_is_setgid(&path),
7674 #[cfg(not(unix))]
7675 'g' => false,
7676 #[cfg(unix)]
7677 'k' => crate::perl_fs::filetest_is_sticky(&path),
7678 #[cfg(not(unix))]
7679 'k' => false,
7680 'T' => crate::perl_fs::filetest_is_text(&path),
7681 'B' => crate::perl_fs::filetest_is_binary(&path),
7682 _ => false,
7683 };
7684 self.push(StrykeValue::integer(if result { 1 } else { 0 }));
7685 Ok(())
7686 }
7687
7688 Op::MapIntMul(k) => {
7690 let list = self.pop().to_list();
7691 if list.len() == 1 {
7692 if let Some(p) = list[0].as_pipeline() {
7693 let line = self.line();
7694 let sub = VMHelper::pipeline_int_mul_sub(*k);
7695 self.interp.pipeline_push(&p, PipelineOp::Map(sub), line)?;
7696 self.push(StrykeValue::pipeline(Arc::clone(&p)));
7697 return Ok(());
7698 }
7699 }
7700 let mut result = Vec::with_capacity(list.len());
7701 for item in list {
7702 let n = item.to_int();
7703 result.push(StrykeValue::integer(n.wrapping_mul(*k)));
7704 }
7705 self.push(StrykeValue::array(result));
7706 Ok(())
7707 }
7708 Op::GrepIntModEq(m, r) => {
7709 let list = self.pop().to_list();
7710 let mut result = Vec::new();
7711 for item in list {
7712 let n = item.to_int();
7713 if n % m == *r {
7714 result.push(item);
7715 }
7716 }
7717 self.push(StrykeValue::array(result));
7718 Ok(())
7719 }
7720 Op::MapWithBlock(block_idx) => {
7721 let list = self.pop().to_list();
7722 self.map_with_block_common(list, *block_idx, false, op_count)
7723 }
7724 Op::FlatMapWithBlock(block_idx) => {
7725 let list = self.pop().to_list();
7726 self.map_with_block_common(list, *block_idx, true, op_count)
7727 }
7728 Op::MapWithExpr(expr_idx) => {
7729 let list = self.pop().to_list();
7730 self.map_with_expr_common(list, *expr_idx, false, op_count)
7731 }
7732 Op::FlatMapWithExpr(expr_idx) => {
7733 let list = self.pop().to_list();
7734 self.map_with_expr_common(list, *expr_idx, true, op_count)
7735 }
7736 Op::MapsWithBlock(block_idx) => {
7737 let val = self.pop();
7738 let block = self.blocks[*block_idx as usize].clone();
7739 let out =
7740 self.interp
7741 .map_stream_block_output(val, &block, false, self.line())?;
7742 self.push(out);
7743 Ok(())
7744 }
7745 Op::MapsFlatMapWithBlock(block_idx) => {
7746 let val = self.pop();
7747 let block = self.blocks[*block_idx as usize].clone();
7748 let out =
7749 self.interp
7750 .map_stream_block_output(val, &block, true, self.line())?;
7751 self.push(out);
7752 Ok(())
7753 }
7754 Op::MapsWithExpr(expr_idx) => {
7755 let val = self.pop();
7756 let idx = *expr_idx as usize;
7757 let expr = self.map_expr_entries[idx].clone();
7758 let out =
7759 self.interp
7760 .map_stream_expr_output(val, &expr, false, self.line())?;
7761 self.push(out);
7762 Ok(())
7763 }
7764 Op::MapsFlatMapWithExpr(expr_idx) => {
7765 let val = self.pop();
7766 let idx = *expr_idx as usize;
7767 let expr = self.map_expr_entries[idx].clone();
7768 let out =
7769 self.interp
7770 .map_stream_expr_output(val, &expr, true, self.line())?;
7771 self.push(out);
7772 Ok(())
7773 }
7774 Op::FilterWithBlock(block_idx) => {
7775 let val = self.pop();
7776 let block = self.blocks[*block_idx as usize].clone();
7777 let out =
7778 self.interp
7779 .filter_stream_block_output(val, &block, self.line())?;
7780 self.push(out);
7781 Ok(())
7782 }
7783 Op::FilterWithExpr(expr_idx) => {
7784 let val = self.pop();
7785 let idx = *expr_idx as usize;
7786 let expr = self.grep_expr_entries[idx].clone();
7787 let out = self
7788 .interp
7789 .filter_stream_expr_output(val, &expr, self.line())?;
7790 self.push(out);
7791 Ok(())
7792 }
7793 Op::ChunkByWithBlock(block_idx) => {
7794 let list = self.pop().to_list();
7795 self.chunk_by_with_block_common(list, *block_idx, op_count)
7796 }
7797 Op::ChunkByWithExpr(expr_idx) => {
7798 let list = self.pop().to_list();
7799 self.chunk_by_with_expr_common(list, *expr_idx, op_count)
7800 }
7801 Op::GrepWithBlock(block_idx) => {
7802 let list = self.pop().to_list();
7803 if list.len() == 1 {
7804 if let Some(p) = list[0].as_pipeline() {
7805 let idx = *block_idx as usize;
7806 let sub = self.interp.anon_coderef_from_block(&self.blocks[idx]);
7807 let line = self.line();
7808 self.interp
7809 .pipeline_push(&p, PipelineOp::Filter(sub), line)?;
7810 self.push(StrykeValue::pipeline(Arc::clone(&p)));
7811 return Ok(());
7812 }
7813 }
7814 let idx = *block_idx as usize;
7815 let saved_chain = self.interp.scope.save_topic_chain();
7820 if let Some(&(start, end)) =
7821 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7822 {
7823 let mut result = Vec::new();
7824 for item in list {
7825 self.interp.scope.set_topic(item.clone());
7826 let val = self.run_block_region(start, end, op_count)?;
7827 let keep = if let Some(re) = val.as_regex() {
7829 re.is_match(&item.to_string())
7830 } else {
7831 val.is_true()
7832 };
7833 if keep {
7834 result.push(item);
7835 }
7836 }
7837 self.interp.scope.restore_topic_chain(saved_chain);
7838 self.push(StrykeValue::array(result));
7839 Ok(())
7840 } else {
7841 let block = self.blocks[idx].clone();
7842 let mut result = Vec::new();
7843 for item in list {
7844 self.interp.scope.set_topic(item.clone());
7845 match self.interp.exec_block(&block) {
7846 Ok(val) => {
7847 let keep = if let Some(re) = val.as_regex() {
7848 re.is_match(&item.to_string())
7849 } else {
7850 val.is_true()
7851 };
7852 if keep {
7853 result.push(item);
7854 }
7855 }
7856 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7857 self.interp.scope.restore_topic_chain(saved_chain);
7858 return Err(e);
7859 }
7860 Err(_) => {}
7861 }
7862 }
7863 self.interp.scope.restore_topic_chain(saved_chain);
7864 self.push(StrykeValue::array(result));
7865 Ok(())
7866 }
7867 }
7868 Op::ForEachWithBlock(block_idx) => {
7869 let val = self.pop();
7870 let idx = *block_idx as usize;
7871 let saved_chain = self.interp.scope.save_topic_chain();
7875 if val.is_iterator() {
7877 let iter = val.into_iterator();
7878 let mut count = 0i64;
7879 if let Some(&(start, end)) =
7880 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7881 {
7882 while let Some(item) = iter.next_item() {
7883 count += 1;
7884 self.interp.scope.set_topic(item);
7885 if let Err(e) = self.run_block_region(start, end, op_count) {
7886 self.interp.scope.restore_topic_chain(saved_chain);
7887 return Err(e);
7888 }
7889 }
7890 } else {
7891 let block = self.blocks[idx].clone();
7892 while let Some(item) = iter.next_item() {
7893 count += 1;
7894 self.interp.scope.set_topic(item);
7895 match self.interp.exec_block(&block) {
7896 Ok(_) => {}
7897 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7898 self.interp.scope.restore_topic_chain(saved_chain);
7899 return Err(e);
7900 }
7901 Err(_) => {}
7902 }
7903 }
7904 }
7905 self.interp.scope.restore_topic_chain(saved_chain);
7906 self.push(StrykeValue::integer(count));
7907 return Ok(());
7908 }
7909 let list = val.to_list();
7910 let count = list.len() as i64;
7911 if let Some(&(start, end)) =
7912 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
7913 {
7914 for item in list {
7915 self.interp.scope.set_topic(item);
7916 if let Err(e) = self.run_block_region(start, end, op_count) {
7917 self.interp.scope.restore_topic_chain(saved_chain);
7918 return Err(e);
7919 }
7920 }
7921 } else {
7922 let block = self.blocks[idx].clone();
7923 for item in list {
7924 self.interp.scope.set_topic(item);
7925 match self.interp.exec_block(&block) {
7926 Ok(_) => {}
7927 Err(crate::vm_helper::FlowOrError::Error(e)) => {
7928 self.interp.scope.restore_topic_chain(saved_chain);
7929 return Err(e);
7930 }
7931 Err(_) => {}
7932 }
7933 }
7934 }
7935 self.interp.scope.restore_topic_chain(saved_chain);
7936 self.push(StrykeValue::integer(count));
7937 Ok(())
7938 }
7939 Op::GrepWithExpr(expr_idx) => {
7940 let list = self.pop().to_list();
7941 let idx = *expr_idx as usize;
7942 let dispatch_coderef = !crate::compat_mode();
7943 if let Some(&(start, end)) = self
7947 .grep_expr_bytecode_ranges
7948 .get(idx)
7949 .and_then(|r| r.as_ref())
7950 {
7951 let mut result = Vec::new();
7952 for item in list {
7953 self.interp.scope.set_topic_local(item.clone());
7954 let val = self.run_block_region(start, end, op_count)?;
7955 let val = self.maybe_call_coderef_with_item(
7956 val,
7957 &item,
7958 dispatch_coderef,
7959 )?;
7960 let keep = if let Some(re) = val.as_regex() {
7961 re.is_match(&item.to_string())
7962 } else {
7963 val.is_true()
7964 };
7965 if keep {
7966 result.push(item);
7967 }
7968 }
7969 self.push(StrykeValue::array(result));
7970 Ok(())
7971 } else {
7972 let e = self.grep_expr_entries[idx].clone();
7973 let mut result = Vec::new();
7974 for item in list {
7975 self.interp.scope.set_topic_local(item.clone());
7976 let val = vm_interp_result(self.interp.eval_expr(&e), self.line())?;
7977 let val = self.maybe_call_coderef_with_item(
7978 val,
7979 &item,
7980 dispatch_coderef,
7981 )?;
7982 let keep = if let Some(re) = val.as_regex() {
7983 re.is_match(&item.to_string())
7984 } else {
7985 val.is_true()
7986 };
7987 if keep {
7988 result.push(item);
7989 }
7990 }
7991 self.push(StrykeValue::array(result));
7992 Ok(())
7993 }
7994 }
7995 Op::SortWithBlock(block_idx) => {
7996 let mut items = self.pop().to_list();
7997 let idx = *block_idx as usize;
7998 let saved_topic = self.interp.scope.save_topic_chain();
8001 if let Some(&(start, end)) =
8002 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8003 {
8004 let mut sort_err: Option<StrykeError> = None;
8005 items.sort_by(|a, b| {
8006 if sort_err.is_some() {
8007 return std::cmp::Ordering::Equal;
8008 }
8009 self.interp.scope.set_sort_pair(a.clone(), b.clone());
8010 match self.run_block_region(start, end, op_count) {
8011 Ok(v) => {
8012 let n = v.to_int();
8013 if n < 0 {
8014 std::cmp::Ordering::Less
8015 } else if n > 0 {
8016 std::cmp::Ordering::Greater
8017 } else {
8018 std::cmp::Ordering::Equal
8019 }
8020 }
8021 Err(e) => {
8022 sort_err = Some(e);
8023 std::cmp::Ordering::Equal
8024 }
8025 }
8026 });
8027 self.interp.scope.restore_topic_chain(saved_topic);
8028 if let Some(e) = sort_err {
8029 return Err(e);
8030 }
8031 self.push(StrykeValue::array(items));
8032 Ok(())
8033 } else {
8034 let block = self.blocks[idx].clone();
8035 items.sort_by(|a, b| {
8036 self.interp.scope.set_sort_pair(a.clone(), b.clone());
8037 match self.interp.exec_block(&block) {
8038 Ok(v) => {
8039 let n = v.to_int();
8040 if n < 0 {
8041 std::cmp::Ordering::Less
8042 } else if n > 0 {
8043 std::cmp::Ordering::Greater
8044 } else {
8045 std::cmp::Ordering::Equal
8046 }
8047 }
8048 Err(_) => std::cmp::Ordering::Equal,
8049 }
8050 });
8051 self.interp.scope.restore_topic_chain(saved_topic);
8052 self.push(StrykeValue::array(items));
8053 Ok(())
8054 }
8055 }
8056 Op::SortWithBlockFast(tag) => {
8057 let mut items = self.pop().to_list();
8058 let mode = match *tag {
8059 0 => SortBlockFast::Numeric,
8060 1 => SortBlockFast::String,
8061 2 => SortBlockFast::NumericRev,
8062 3 => SortBlockFast::StringRev,
8063 _ => SortBlockFast::Numeric,
8064 };
8065 items.sort_by(|a, b| sort_magic_cmp(a, b, mode));
8066 self.push(StrykeValue::array(items));
8067 Ok(())
8068 }
8069 Op::SortNoBlock => {
8070 let mut items = self.pop().to_list();
8071 items.sort_by_key(|a| a.to_string());
8072 self.push(StrykeValue::array(items));
8073 Ok(())
8074 }
8075 Op::SortWithCodeComparator(wa) => {
8076 let want = WantarrayCtx::from_byte(*wa);
8077 let cmp_val = self.pop();
8078 let mut items = self.pop().to_list();
8079 let line = self.line();
8080 let Some(sub) = cmp_val.as_code_ref() else {
8081 return Err(StrykeError::runtime(
8082 "sort: comparator must be a code reference",
8083 line,
8084 ));
8085 };
8086 let interp = &mut self.interp;
8087 items.sort_by(|a, b| {
8088 interp.scope.set_sort_pair(a.clone(), b.clone());
8091 match interp.call_sub(
8092 sub.as_ref(),
8093 vec![a.clone(), b.clone()],
8094 want,
8095 line,
8096 ) {
8097 Ok(v) => {
8098 let n = v.to_int();
8099 if n < 0 {
8100 std::cmp::Ordering::Less
8101 } else if n > 0 {
8102 std::cmp::Ordering::Greater
8103 } else {
8104 std::cmp::Ordering::Equal
8105 }
8106 }
8107 Err(_) => std::cmp::Ordering::Equal,
8108 }
8109 });
8110 self.push(StrykeValue::array(items));
8111 Ok(())
8112 }
8113 Op::ReverseListOp => {
8114 let val = self.pop();
8115 if val.is_iterator() {
8116 self.push(StrykeValue::iterator(std::sync::Arc::new(
8117 crate::value::RevIterator::new(val.into_iterator()),
8118 )));
8119 } else {
8120 let mut items = val.to_list();
8121 items.reverse();
8122 self.push(StrykeValue::array(items));
8123 }
8124 Ok(())
8125 }
8126 Op::ReverseScalarOp => {
8127 let val = self.pop();
8128 let items = val.to_list();
8129 let s: String = items.iter().map(|v| v.to_string()).collect();
8130 self.push(StrykeValue::string(s.chars().rev().collect()));
8131 Ok(())
8132 }
8133 Op::RevListOp => {
8134 let val = self.pop();
8135 if val.is_iterator() {
8136 let mut items = val.to_list();
8139 items.reverse();
8140 self.push(StrykeValue::array(items));
8141 } else if let Some(s) = crate::value::set_payload(&val) {
8142 let mut out = crate::value::PerlSet::new();
8143 for (k, v) in s.iter().rev() {
8144 out.insert(k.clone(), v.clone());
8145 }
8146 self.push(StrykeValue::set(std::sync::Arc::new(out)));
8147 } else if let Some(ar) = val.as_array_ref() {
8148 let items: Vec<_> = ar.read().iter().rev().cloned().collect();
8149 self.push(StrykeValue::array_ref(std::sync::Arc::new(
8150 parking_lot::RwLock::new(items),
8151 )));
8152 } else if let Some(hr) = val.as_hash_ref() {
8153 let mut out: indexmap::IndexMap<String, StrykeValue> =
8154 indexmap::IndexMap::new();
8155 for (k, v) in hr.read().iter() {
8156 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8157 }
8158 self.push(StrykeValue::hash_ref(std::sync::Arc::new(
8159 parking_lot::RwLock::new(out),
8160 )));
8161 } else if let Some(hm) = val.as_hash_map() {
8162 let mut out: indexmap::IndexMap<String, StrykeValue> =
8163 indexmap::IndexMap::new();
8164 for (k, v) in hm.iter() {
8165 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8166 }
8167 self.push(StrykeValue::hash(out));
8168 } else if val.as_array_vec().is_some() {
8169 let mut items = val.to_list();
8170 items.reverse();
8171 self.push(StrykeValue::array(items));
8172 } else {
8173 let s = val.to_string();
8174 self.push(StrykeValue::string(s.chars().rev().collect()));
8175 }
8176 Ok(())
8177 }
8178 Op::RevScalarOp => {
8179 let val = self.pop();
8180 if let Some(s) = crate::value::set_payload(&val) {
8181 let mut out = crate::value::PerlSet::new();
8182 for (k, v) in s.iter().rev() {
8183 out.insert(k.clone(), v.clone());
8184 }
8185 self.push(StrykeValue::set(std::sync::Arc::new(out)));
8186 } else if let Some(ar) = val.as_array_ref() {
8187 let items: Vec<_> = ar.read().iter().rev().cloned().collect();
8188 self.push(StrykeValue::array_ref(std::sync::Arc::new(
8189 parking_lot::RwLock::new(items),
8190 )));
8191 } else if let Some(hr) = val.as_hash_ref() {
8192 let mut out: indexmap::IndexMap<String, StrykeValue> =
8193 indexmap::IndexMap::new();
8194 for (k, v) in hr.read().iter() {
8195 out.insert(v.to_string(), StrykeValue::string(k.clone()));
8196 }
8197 self.push(StrykeValue::hash_ref(std::sync::Arc::new(
8198 parking_lot::RwLock::new(out),
8199 )));
8200 } else {
8201 let items = val.to_list();
8202 let s: String = items.iter().map(|v| v.to_string()).collect();
8203 self.push(StrykeValue::string(s.chars().rev().collect()));
8204 }
8205 Ok(())
8206 }
8207 Op::StackArrayLen => {
8208 let v = self.pop();
8209 self.push(StrykeValue::integer(v.to_list().len() as i64));
8210 Ok(())
8211 }
8212 Op::ListSliceToScalar => {
8213 let v = self.pop();
8214 let items = v.to_list();
8215 self.push(items.last().cloned().unwrap_or(StrykeValue::UNDEF));
8216 Ok(())
8217 }
8218
8219 Op::EvalBlock(block_idx, want) => {
8221 let block = self.blocks[*block_idx as usize].clone();
8222 let tail = crate::vm_helper::WantarrayCtx::from_byte(*want);
8223 self.interp.eval_nesting += 1;
8224 match self.interp.exec_block_with_tail(&block, tail) {
8227 Ok(v) => {
8228 self.interp.clear_eval_error();
8229 self.push(v);
8230 }
8231 Err(crate::vm_helper::FlowOrError::Error(e)) => {
8232 self.interp.set_eval_error_from_perl_error(&e);
8233 self.push(StrykeValue::UNDEF);
8234 }
8235 Err(_) => self.push(StrykeValue::UNDEF),
8236 }
8237 self.interp.eval_nesting -= 1;
8238 Ok(())
8239 }
8240 Op::TraceBlock(block_idx) => {
8241 let block = self.blocks[*block_idx as usize].clone();
8242 crate::parallel_trace::trace_enter();
8243 self.interp.eval_nesting += 1;
8244 match self.interp.exec_block(&block) {
8245 Ok(v) => {
8246 self.interp.clear_eval_error();
8247 self.push(v);
8248 }
8249 Err(FlowOrError::Error(e)) => {
8250 self.interp.set_eval_error_from_perl_error(&e);
8251 self.push(StrykeValue::UNDEF);
8252 }
8253 Err(_) => self.push(StrykeValue::UNDEF),
8254 }
8255 self.interp.eval_nesting -= 1;
8256 crate::parallel_trace::trace_leave();
8257 Ok(())
8258 }
8259 Op::TimerBlock(block_idx) => {
8260 let block = self.blocks[*block_idx as usize].clone();
8261 let start = std::time::Instant::now();
8262 self.interp.eval_nesting += 1;
8263 let _ = match self.interp.exec_block(&block) {
8264 Ok(v) => {
8265 self.interp.clear_eval_error();
8266 v
8267 }
8268 Err(FlowOrError::Error(e)) => {
8269 self.interp.set_eval_error_from_perl_error(&e);
8270 StrykeValue::UNDEF
8271 }
8272 Err(_) => StrykeValue::UNDEF,
8273 };
8274 self.interp.eval_nesting -= 1;
8275 let ms = start.elapsed().as_secs_f64() * 1000.0;
8276 self.push(StrykeValue::float(ms));
8277 Ok(())
8278 }
8279 Op::BenchBlock(block_idx) => {
8280 let n_i = self.pop().to_int();
8281 if n_i < 0 {
8282 return Err(StrykeError::runtime(
8283 "bench: iteration count must be non-negative",
8284 self.line(),
8285 ));
8286 }
8287 let n = n_i as usize;
8288 let block = self.blocks[*block_idx as usize].clone();
8289 let v = vm_interp_result(
8290 self.interp.run_bench_block(&block, n, self.line()),
8291 self.line(),
8292 )?;
8293 self.push(v);
8294 Ok(())
8295 }
8296 Op::Given(idx) => {
8297 let i = *idx as usize;
8298 let line = self.line();
8299 let v = if let Some(&(start, end)) = self
8300 .given_topic_bytecode_ranges
8301 .get(i)
8302 .and_then(|r| r.as_ref())
8303 {
8304 let topic_val = self.run_block_region(start, end, op_count)?;
8305 let body = &self.given_entries[i].1;
8306 vm_interp_result(
8307 self.interp.exec_given_with_topic_value(topic_val, body),
8308 line,
8309 )?
8310 } else {
8311 let (topic, body) = &self.given_entries[i];
8312 vm_interp_result(self.interp.exec_given(topic, body), line)?
8313 };
8314 self.push(v);
8315 Ok(())
8316 }
8317 Op::EvalTimeout(idx) => {
8318 let i = *idx as usize;
8319 let body = self.eval_timeout_entries[i].1.clone();
8320 let secs = if let Some(&(start, end)) = self
8321 .eval_timeout_expr_bytecode_ranges
8322 .get(i)
8323 .and_then(|r| r.as_ref())
8324 {
8325 self.run_block_region(start, end, op_count)?.to_number()
8326 } else {
8327 let timeout_expr = &self.eval_timeout_entries[i].0;
8328 vm_interp_result(self.interp.eval_expr(timeout_expr), self.line())?
8329 .to_number()
8330 };
8331 let v = vm_interp_result(
8332 self.interp.eval_timeout_block(&body, secs, self.line()),
8333 self.line(),
8334 )?;
8335 self.push(v);
8336 Ok(())
8337 }
8338 Op::AlgebraicMatch(idx) => {
8339 let i = *idx as usize;
8340 let line = self.line();
8341 let v = if let Some(&(start, end)) = self
8342 .algebraic_match_subject_bytecode_ranges
8343 .get(i)
8344 .and_then(|r| r.as_ref())
8345 {
8346 let subject_val = self.run_block_region(start, end, op_count)?;
8347 let arms = &self.algebraic_match_entries[i].1;
8348 vm_interp_result(
8349 self.interp.eval_algebraic_match_with_subject_value(
8350 subject_val,
8351 arms,
8352 line,
8353 ),
8354 self.line(),
8355 )?
8356 } else {
8357 let (subject, arms) = &self.algebraic_match_entries[i];
8358 vm_interp_result(
8359 self.interp.eval_algebraic_match(subject, arms, line),
8360 self.line(),
8361 )?
8362 };
8363 self.push(v);
8364 Ok(())
8365 }
8366 Op::ParLines(idx) => {
8367 let (path, callback, progress) = &self.par_lines_entries[*idx as usize];
8368 let v = vm_interp_result(
8369 self.interp.eval_par_lines_expr(
8370 path,
8371 callback,
8372 progress.as_ref(),
8373 self.line(),
8374 ),
8375 self.line(),
8376 )?;
8377 self.push(v);
8378 Ok(())
8379 }
8380 Op::ParWalk(idx) => {
8381 let (path, callback, progress) = &self.par_walk_entries[*idx as usize];
8382 let v = vm_interp_result(
8383 self.interp.eval_par_walk_expr(
8384 path,
8385 callback,
8386 progress.as_ref(),
8387 self.line(),
8388 ),
8389 self.line(),
8390 )?;
8391 self.push(v);
8392 Ok(())
8393 }
8394 Op::Pwatch(idx) => {
8395 let (path, callback) = &self.pwatch_entries[*idx as usize];
8396 let v = vm_interp_result(
8397 self.interp.eval_pwatch_expr(path, callback, self.line()),
8398 self.line(),
8399 )?;
8400 self.push(v);
8401 Ok(())
8402 }
8403
8404 Op::PMapWithBlock(block_idx) => {
8406 let list = self.pop().to_list();
8407 let progress_flag = self.pop().is_true();
8408 let idx = *block_idx as usize;
8409 let subs = self.interp.subs.clone();
8410 let (scope_capture, atomic_arrays, atomic_hashes) =
8411 self.interp.scope.capture_with_atomics();
8412 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8413 let n_workers = rayon::current_num_threads();
8414 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
8415 .map(|_| {
8416 let mut interp = VMHelper::new();
8417 interp.subs = subs.clone();
8418 interp.scope.restore_capture(&scope_capture);
8419 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
8420 interp.enable_parallel_guard();
8421 Mutex::new(interp)
8422 })
8423 .collect();
8424 if let Some(&(start, end)) =
8425 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8426 {
8427 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8428 let results: Vec<StrykeValue> = list
8429 .into_par_iter()
8430 .map(|item| {
8431 let tid =
8432 rayon::current_thread_index().unwrap_or(0) % pool.len();
8433 let mut local_interp = pool[tid].lock();
8434 local_interp.scope.set_topic(item);
8435 let mut vm = shared.worker_vm(&mut local_interp);
8436 let mut op_count = 0u64;
8437 let val = match vm.run_block_region(start, end, &mut op_count) {
8438 Ok(v) => v,
8439 Err(_) => StrykeValue::UNDEF,
8440 };
8441 pmap_progress.tick();
8442 val
8443 })
8444 .collect();
8445 pmap_progress.finish();
8446 self.push(StrykeValue::array(results));
8447 Ok(())
8448 } else {
8449 let block = self.blocks[idx].clone();
8450 let results: Vec<StrykeValue> = list
8451 .into_par_iter()
8452 .map(|item| {
8453 let tid =
8454 rayon::current_thread_index().unwrap_or(0) % pool.len();
8455 let mut local_interp = pool[tid].lock();
8456 local_interp.scope.set_topic(item);
8457 local_interp.scope_push_hook();
8458 let val = match local_interp.exec_block_no_scope(&block) {
8459 Ok(val) => val,
8460 Err(_) => StrykeValue::UNDEF,
8461 };
8462 local_interp.scope_pop_hook();
8463 pmap_progress.tick();
8464 val
8465 })
8466 .collect();
8467 pmap_progress.finish();
8468 self.push(StrykeValue::array(results));
8469 Ok(())
8470 }
8471 }
8472 Op::PFlatMapWithBlock(block_idx) => {
8473 let list = self.pop().to_list();
8474 let progress_flag = self.pop().is_true();
8475 let idx = *block_idx as usize;
8476 let subs = self.interp.subs.clone();
8477 let (scope_capture, atomic_arrays, atomic_hashes) =
8478 self.interp.scope.capture_with_atomics();
8479 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8480 let n_workers = rayon::current_num_threads();
8481 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
8482 .map(|_| {
8483 let mut interp = VMHelper::new();
8484 interp.subs = subs.clone();
8485 interp.scope.restore_capture(&scope_capture);
8486 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
8487 interp.enable_parallel_guard();
8488 Mutex::new(interp)
8489 })
8490 .collect();
8491 if let Some(&(start, end)) =
8492 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8493 {
8494 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8495 let mut indexed: Vec<(usize, Vec<StrykeValue>)> = list
8496 .into_par_iter()
8497 .enumerate()
8498 .map(|(i, item)| {
8499 let tid =
8500 rayon::current_thread_index().unwrap_or(0) % pool.len();
8501 let mut local_interp = pool[tid].lock();
8502 local_interp.scope.set_topic(item);
8503 let mut vm = shared.worker_vm(&mut local_interp);
8504 let mut op_count = 0u64;
8505 let val = match vm.run_block_region(start, end, &mut op_count) {
8506 Ok(v) => v,
8507 Err(_) => StrykeValue::UNDEF,
8508 };
8509 let out = val.map_flatten_outputs(true);
8510 pmap_progress.tick();
8511 (i, out)
8512 })
8513 .collect();
8514 pmap_progress.finish();
8515 indexed.sort_by_key(|(i, _)| *i);
8516 let results: Vec<StrykeValue> =
8517 indexed.into_iter().flat_map(|(_, v)| v).collect();
8518 self.push(StrykeValue::array(results));
8519 Ok(())
8520 } else {
8521 let block = self.blocks[idx].clone();
8522 let mut indexed: Vec<(usize, Vec<StrykeValue>)> = list
8523 .into_par_iter()
8524 .enumerate()
8525 .map(|(i, item)| {
8526 let tid =
8527 rayon::current_thread_index().unwrap_or(0) % pool.len();
8528 let mut local_interp = pool[tid].lock();
8529 local_interp.scope.set_topic(item);
8530 local_interp.scope_push_hook();
8531 let val = match local_interp.exec_block_no_scope(&block) {
8532 Ok(val) => val,
8533 Err(_) => StrykeValue::UNDEF,
8534 };
8535 local_interp.scope_pop_hook();
8536 let out = val.map_flatten_outputs(true);
8537 pmap_progress.tick();
8538 (i, out)
8539 })
8540 .collect();
8541 pmap_progress.finish();
8542 indexed.sort_by_key(|(i, _)| *i);
8543 let results: Vec<StrykeValue> =
8544 indexed.into_iter().flat_map(|(_, v)| v).collect();
8545 self.push(StrykeValue::array(results));
8546 Ok(())
8547 }
8548 }
8549 Op::PMapRemote { block_idx, flat } => {
8550 let cluster = self.pop();
8551 let list_pv = self.pop();
8552 let progress_flag = self.pop().is_true();
8553 let idx = *block_idx as usize;
8554 let block = self.blocks[idx].clone();
8555 let flat_outputs = *flat != 0;
8556 let v = vm_interp_result(
8557 self.interp.eval_pmap_remote(
8558 cluster,
8559 list_pv,
8560 progress_flag,
8561 &block,
8562 flat_outputs,
8563 self.line(),
8564 ),
8565 self.line(),
8566 )?;
8567 self.push(v);
8568 Ok(())
8569 }
8570 Op::Puniq => {
8571 let list = self.pop().to_list();
8572 let progress_flag = self.pop().is_true();
8573 let n_threads = self.interp.parallel_thread_count();
8574 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8575 let out = crate::par_list::puniq_run(list, n_threads, &pmap_progress);
8576 pmap_progress.finish();
8577 self.push(StrykeValue::array(out));
8578 Ok(())
8579 }
8580 Op::PFirstWithBlock(block_idx) => {
8581 let list = self.pop().to_list();
8582 let progress_flag = self.pop().is_true();
8583 let idx = *block_idx as usize;
8584 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8585 let subs = self.interp.subs.clone();
8586 let (scope_capture, atomic_arrays, atomic_hashes) =
8587 self.interp.scope.capture_with_atomics();
8588 let out = if let Some(&(start, end)) =
8589 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8590 {
8591 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8592 crate::par_list::pfirst_run(list, &pmap_progress, |item| {
8593 let mut local_interp = VMHelper::new();
8594 local_interp.subs = subs.clone();
8595 local_interp.scope.restore_capture(&scope_capture);
8596 local_interp
8597 .scope
8598 .restore_atomics(&atomic_arrays, &atomic_hashes);
8599 local_interp.enable_parallel_guard();
8600 local_interp.scope.set_topic(item);
8601 let mut vm = shared.worker_vm(&mut local_interp);
8602 let mut op_count = 0u64;
8603 match vm.run_block_region(start, end, &mut op_count) {
8604 Ok(v) => v.is_true(),
8605 Err(_) => false,
8606 }
8607 })
8608 } else {
8609 let block = self.blocks[idx].clone();
8610 crate::par_list::pfirst_run(list, &pmap_progress, |item| {
8611 let mut local_interp = VMHelper::new();
8612 local_interp.subs = subs.clone();
8613 local_interp.scope.restore_capture(&scope_capture);
8614 local_interp
8615 .scope
8616 .restore_atomics(&atomic_arrays, &atomic_hashes);
8617 local_interp.enable_parallel_guard();
8618 local_interp.scope.set_topic(item);
8619 local_interp.scope_push_hook();
8620 let ok = match local_interp.exec_block_no_scope(&block) {
8621 Ok(v) => v.is_true(),
8622 Err(_) => false,
8623 };
8624 local_interp.scope_pop_hook();
8625 ok
8626 })
8627 };
8628 pmap_progress.finish();
8629 self.push(out.unwrap_or(StrykeValue::UNDEF));
8630 Ok(())
8631 }
8632 Op::PAnyWithBlock(block_idx) => {
8633 let list = self.pop().to_list();
8634 let progress_flag = self.pop().is_true();
8635 let idx = *block_idx as usize;
8636 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8637 let subs = self.interp.subs.clone();
8638 let (scope_capture, atomic_arrays, atomic_hashes) =
8639 self.interp.scope.capture_with_atomics();
8640 let b = if let Some(&(start, end)) =
8641 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8642 {
8643 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8644 crate::par_list::pany_run(list, &pmap_progress, |item| {
8645 let mut local_interp = VMHelper::new();
8646 local_interp.subs = subs.clone();
8647 local_interp.scope.restore_capture(&scope_capture);
8648 local_interp
8649 .scope
8650 .restore_atomics(&atomic_arrays, &atomic_hashes);
8651 local_interp.enable_parallel_guard();
8652 local_interp.scope.set_topic(item);
8653 let mut vm = shared.worker_vm(&mut local_interp);
8654 let mut op_count = 0u64;
8655 match vm.run_block_region(start, end, &mut op_count) {
8656 Ok(v) => v.is_true(),
8657 Err(_) => false,
8658 }
8659 })
8660 } else {
8661 let block = self.blocks[idx].clone();
8662 crate::par_list::pany_run(list, &pmap_progress, |item| {
8663 let mut local_interp = VMHelper::new();
8664 local_interp.subs = subs.clone();
8665 local_interp.scope.restore_capture(&scope_capture);
8666 local_interp
8667 .scope
8668 .restore_atomics(&atomic_arrays, &atomic_hashes);
8669 local_interp.enable_parallel_guard();
8670 local_interp.scope.set_topic(item);
8671 local_interp.scope_push_hook();
8672 let ok = match local_interp.exec_block_no_scope(&block) {
8673 Ok(v) => v.is_true(),
8674 Err(_) => false,
8675 };
8676 local_interp.scope_pop_hook();
8677 ok
8678 })
8679 };
8680 pmap_progress.finish();
8681 self.push(StrykeValue::integer(if b { 1 } else { 0 }));
8682 Ok(())
8683 }
8684 Op::PMapChunkedWithBlock(block_idx) => {
8685 let list = self.pop().to_list();
8686 let chunk_n = self.pop().to_int().max(1) as usize;
8687 let progress_flag = self.pop().is_true();
8688 let idx = *block_idx as usize;
8689 let subs = self.interp.subs.clone();
8690 let (scope_capture, atomic_arrays, atomic_hashes) =
8691 self.interp.scope.capture_with_atomics();
8692 let indexed_chunks: Vec<(usize, Vec<StrykeValue>)> = list
8693 .chunks(chunk_n)
8694 .enumerate()
8695 .map(|(i, c)| (i, c.to_vec()))
8696 .collect();
8697 let n_chunks = indexed_chunks.len();
8698 let pmap_progress = PmapProgress::new(progress_flag, n_chunks);
8699 if let Some(&(start, end)) =
8700 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8701 {
8702 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8703 let mut chunk_results: Vec<(usize, Vec<StrykeValue>)> = indexed_chunks
8704 .into_par_iter()
8705 .map(|(chunk_idx, chunk)| {
8706 let mut local_interp = VMHelper::new();
8707 local_interp.subs = subs.clone();
8708 local_interp.scope.restore_capture(&scope_capture);
8709 local_interp
8710 .scope
8711 .restore_atomics(&atomic_arrays, &atomic_hashes);
8712 local_interp.enable_parallel_guard();
8713 let mut out = Vec::with_capacity(chunk.len());
8714 for item in chunk {
8715 local_interp.scope.set_topic(item);
8716 let mut vm = shared.worker_vm(&mut local_interp);
8717 let mut op_count = 0u64;
8718 let val =
8719 match vm.run_block_region(start, end, &mut op_count) {
8720 Ok(v) => v,
8721 Err(_) => StrykeValue::UNDEF,
8722 };
8723 out.push(val);
8724 }
8725 pmap_progress.tick();
8726 (chunk_idx, out)
8727 })
8728 .collect();
8729 pmap_progress.finish();
8730 chunk_results.sort_by_key(|(i, _)| *i);
8731 let results: Vec<StrykeValue> =
8732 chunk_results.into_iter().flat_map(|(_, v)| v).collect();
8733 self.push(StrykeValue::array(results));
8734 Ok(())
8735 } else {
8736 let block = self.blocks[idx].clone();
8737 let mut chunk_results: Vec<(usize, Vec<StrykeValue>)> = indexed_chunks
8738 .into_par_iter()
8739 .map(|(chunk_idx, chunk)| {
8740 let mut local_interp = VMHelper::new();
8741 local_interp.subs = subs.clone();
8742 local_interp.scope.restore_capture(&scope_capture);
8743 local_interp
8744 .scope
8745 .restore_atomics(&atomic_arrays, &atomic_hashes);
8746 local_interp.enable_parallel_guard();
8747 let mut out = Vec::with_capacity(chunk.len());
8748 for item in chunk {
8749 local_interp.scope.set_topic(item);
8750 local_interp.scope_push_hook();
8751 let val = match local_interp.exec_block_no_scope(&block) {
8752 Ok(val) => val,
8753 Err(_) => StrykeValue::UNDEF,
8754 };
8755 local_interp.scope_pop_hook();
8756 out.push(val);
8757 }
8758 pmap_progress.tick();
8759 (chunk_idx, out)
8760 })
8761 .collect();
8762 pmap_progress.finish();
8763 chunk_results.sort_by_key(|(i, _)| *i);
8764 let results: Vec<StrykeValue> =
8765 chunk_results.into_iter().flat_map(|(_, v)| v).collect();
8766 self.push(StrykeValue::array(results));
8767 Ok(())
8768 }
8769 }
8770 Op::ReduceWithBlock(block_idx) => {
8771 let list = self.pop().to_list();
8772 let idx = *block_idx as usize;
8773 let subs = self.interp.subs.clone();
8774 let scope_capture = self.interp.scope.capture();
8775 if list.is_empty() {
8776 self.push(StrykeValue::UNDEF);
8777 return Ok(());
8778 }
8779 if list.len() == 1 {
8780 self.push(list.into_iter().next().unwrap());
8781 return Ok(());
8782 }
8783 let mut items = list;
8784 let mut acc = items.remove(0);
8785 let rest = items;
8786 if let Some(&(start, end)) =
8787 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8788 {
8789 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8790 for b in rest {
8791 let mut local_interp = VMHelper::new();
8792 local_interp.subs = subs.clone();
8793 local_interp.scope.restore_capture(&scope_capture);
8794 local_interp.scope.set_sort_pair(acc.clone(), b.clone());
8795 let mut vm = shared.worker_vm(&mut local_interp);
8796 let mut op_count = 0u64;
8797 acc = match vm.run_block_region(start, end, &mut op_count) {
8798 Ok(v) => v,
8799 Err(_) => StrykeValue::UNDEF,
8800 };
8801 }
8802 } else {
8803 let block = self.blocks[idx].clone();
8804 for b in rest {
8805 let mut local_interp = VMHelper::new();
8806 local_interp.subs = subs.clone();
8807 local_interp.scope.restore_capture(&scope_capture);
8808 local_interp.scope.set_sort_pair(acc.clone(), b.clone());
8809 acc = match local_interp.exec_block(&block) {
8810 Ok(val) => val,
8811 Err(_) => StrykeValue::UNDEF,
8812 };
8813 }
8814 }
8815 self.push(acc);
8816 Ok(())
8817 }
8818 Op::PReduceWithBlock(block_idx) => {
8819 let list = self.pop().to_list();
8820 let progress_flag = self.pop().is_true();
8821 let idx = *block_idx as usize;
8822 let subs = self.interp.subs.clone();
8823 let scope_capture = self.interp.scope.capture();
8824 if list.is_empty() {
8825 self.push(StrykeValue::UNDEF);
8826 return Ok(());
8827 }
8828 if list.len() == 1 {
8829 self.push(list.into_iter().next().unwrap());
8830 return Ok(());
8831 }
8832 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8833 if let Some(&(start, end)) =
8834 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
8835 {
8836 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8837 let result = list
8838 .into_par_iter()
8839 .map(|x| {
8840 pmap_progress.tick();
8841 x
8842 })
8843 .reduce_with(|a, b| {
8844 let mut local_interp = VMHelper::new();
8845 local_interp.subs = subs.clone();
8846 local_interp.scope.restore_capture(&scope_capture);
8847 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8848 let mut vm = shared.worker_vm(&mut local_interp);
8849 let mut op_count = 0u64;
8850 match vm.run_block_region(start, end, &mut op_count) {
8851 Ok(val) => val,
8852 Err(_) => StrykeValue::UNDEF,
8853 }
8854 });
8855 pmap_progress.finish();
8856 self.push(result.unwrap_or(StrykeValue::UNDEF));
8857 Ok(())
8858 } else {
8859 let block = self.blocks[idx].clone();
8860 let result = list
8861 .into_par_iter()
8862 .map(|x| {
8863 pmap_progress.tick();
8864 x
8865 })
8866 .reduce_with(|a, b| {
8867 let mut local_interp = VMHelper::new();
8868 local_interp.subs = subs.clone();
8869 local_interp.scope.restore_capture(&scope_capture);
8870 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8871 match local_interp.exec_block(&block) {
8872 Ok(val) => val,
8873 Err(_) => StrykeValue::UNDEF,
8874 }
8875 });
8876 pmap_progress.finish();
8877 self.push(result.unwrap_or(StrykeValue::UNDEF));
8878 Ok(())
8879 }
8880 }
8881 Op::PReduceInitWithBlock(block_idx) => {
8882 let init_val = self.pop();
8883 let list = self.pop().to_list();
8884 let progress_flag = self.pop().is_true();
8885 let idx = *block_idx as usize;
8886 let subs = self.interp.subs.clone();
8887 let scope_capture = self.interp.scope.capture();
8888 let cap: &[(String, StrykeValue)] = scope_capture.as_slice();
8889 let block = self.blocks[idx].clone();
8890 if list.is_empty() {
8891 self.push(init_val);
8892 return Ok(());
8893 }
8894 if list.len() == 1 {
8895 let v = fold_preduce_init_step(
8896 &subs,
8897 cap,
8898 &block,
8899 preduce_init_fold_identity(&init_val),
8900 list.into_iter().next().unwrap(),
8901 );
8902 self.push(v);
8903 return Ok(());
8904 }
8905 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8906 let result = list
8907 .into_par_iter()
8908 .fold(
8909 || preduce_init_fold_identity(&init_val),
8910 |acc, item| {
8911 pmap_progress.tick();
8912 fold_preduce_init_step(&subs, cap, &block, acc, item)
8913 },
8914 )
8915 .reduce(
8916 || preduce_init_fold_identity(&init_val),
8917 |a, b| merge_preduce_init_partials(a, b, &block, &subs, cap),
8918 );
8919 pmap_progress.finish();
8920 self.push(result);
8921 Ok(())
8922 }
8923 Op::PMapReduceWithBlocks(map_idx, reduce_idx) => {
8924 let list = self.pop().to_list();
8925 let progress_flag = self.pop().is_true();
8926 let map_i = *map_idx as usize;
8927 let reduce_i = *reduce_idx as usize;
8928 let subs = self.interp.subs.clone();
8929 let scope_capture = self.interp.scope.capture();
8930 if list.is_empty() {
8931 self.push(StrykeValue::UNDEF);
8932 return Ok(());
8933 }
8934 if list.len() == 1 {
8935 let mut local_interp = VMHelper::new();
8936 local_interp.subs = subs.clone();
8937 local_interp.scope.restore_capture(&scope_capture);
8938 local_interp
8939 .scope
8940 .set_topic(list.into_iter().next().unwrap());
8941 let map_block = self.blocks[map_i].clone();
8942 let v = match local_interp.exec_block_no_scope(&map_block) {
8943 Ok(v) => v,
8944 Err(_) => StrykeValue::UNDEF,
8945 };
8946 self.push(v);
8947 return Ok(());
8948 }
8949 let pmap_progress = PmapProgress::new(progress_flag, list.len());
8950 let map_range = self
8951 .block_bytecode_ranges
8952 .get(map_i)
8953 .and_then(|r| r.as_ref())
8954 .copied();
8955 let reduce_range = self
8956 .block_bytecode_ranges
8957 .get(reduce_i)
8958 .and_then(|r| r.as_ref())
8959 .copied();
8960 if let (Some((map_start, map_end)), Some((reduce_start, reduce_end))) =
8961 (map_range, reduce_range)
8962 {
8963 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
8964 let result = list
8965 .into_par_iter()
8966 .map(|item| {
8967 let mut local_interp = VMHelper::new();
8968 local_interp.subs = subs.clone();
8969 local_interp.scope.restore_capture(&scope_capture);
8970 local_interp.scope.set_topic(item);
8971 let mut vm = shared.worker_vm(&mut local_interp);
8972 let mut op_count = 0u64;
8973 let val = match vm.run_block_region(
8974 map_start,
8975 map_end,
8976 &mut op_count,
8977 ) {
8978 Ok(val) => val,
8979 Err(_) => StrykeValue::UNDEF,
8980 };
8981 pmap_progress.tick();
8982 val
8983 })
8984 .reduce_with(|a, b| {
8985 let mut local_interp = VMHelper::new();
8986 local_interp.subs = subs.clone();
8987 local_interp.scope.restore_capture(&scope_capture);
8988 local_interp.scope.set_sort_pair(a.clone(), b.clone());
8989 let mut vm = shared.worker_vm(&mut local_interp);
8990 let mut op_count = 0u64;
8991 match vm.run_block_region(
8992 reduce_start,
8993 reduce_end,
8994 &mut op_count,
8995 ) {
8996 Ok(val) => val,
8997 Err(_) => StrykeValue::UNDEF,
8998 }
8999 });
9000 pmap_progress.finish();
9001 self.push(result.unwrap_or(StrykeValue::UNDEF));
9002 Ok(())
9003 } else {
9004 let map_block = self.blocks[map_i].clone();
9005 let reduce_block = self.blocks[reduce_i].clone();
9006 let result = list
9007 .into_par_iter()
9008 .map(|item| {
9009 let mut local_interp = VMHelper::new();
9010 local_interp.subs = subs.clone();
9011 local_interp.scope.restore_capture(&scope_capture);
9012 local_interp.scope.set_topic(item);
9013 let val = match local_interp.exec_block_no_scope(&map_block) {
9014 Ok(val) => val,
9015 Err(_) => StrykeValue::UNDEF,
9016 };
9017 pmap_progress.tick();
9018 val
9019 })
9020 .reduce_with(|a, b| {
9021 let mut local_interp = VMHelper::new();
9022 local_interp.subs = subs.clone();
9023 local_interp.scope.restore_capture(&scope_capture);
9024 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9025 match local_interp.exec_block_no_scope(&reduce_block) {
9026 Ok(val) => val,
9027 Err(_) => StrykeValue::UNDEF,
9028 }
9029 });
9030 pmap_progress.finish();
9031 self.push(result.unwrap_or(StrykeValue::UNDEF));
9032 Ok(())
9033 }
9034 }
9035 Op::PcacheWithBlock(block_idx) => {
9036 let list = self.pop().to_list();
9037 let progress_flag = self.pop().is_true();
9038 let idx = *block_idx as usize;
9039 let subs = self.interp.subs.clone();
9040 let scope_capture = self.interp.scope.capture();
9041 let block = self.blocks[idx].clone();
9042 let cache = &*crate::pcache::GLOBAL_PCACHE;
9043 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9044 if let Some(&(start, end)) =
9045 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9046 {
9047 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9048 let results: Vec<StrykeValue> = list
9049 .into_par_iter()
9050 .map(|item| {
9051 let k = crate::pcache::cache_key(&item);
9052 if let Some(v) = cache.get(&k) {
9053 pmap_progress.tick();
9054 return v.clone();
9055 }
9056 let mut local_interp = VMHelper::new();
9057 local_interp.subs = subs.clone();
9058 local_interp.scope.restore_capture(&scope_capture);
9059 local_interp.scope.set_topic(item.clone());
9060 let mut vm = shared.worker_vm(&mut local_interp);
9061 let mut op_count = 0u64;
9062 let val = match vm.run_block_region(start, end, &mut op_count) {
9063 Ok(v) => v,
9064 Err(_) => StrykeValue::UNDEF,
9065 };
9066 cache.insert(k, val.clone());
9067 pmap_progress.tick();
9068 val
9069 })
9070 .collect();
9071 pmap_progress.finish();
9072 self.push(StrykeValue::array(results));
9073 Ok(())
9074 } else {
9075 let results: Vec<StrykeValue> = list
9076 .into_par_iter()
9077 .map(|item| {
9078 let k = crate::pcache::cache_key(&item);
9079 if let Some(v) = cache.get(&k) {
9080 pmap_progress.tick();
9081 return v.clone();
9082 }
9083 let mut local_interp = VMHelper::new();
9084 local_interp.subs = subs.clone();
9085 local_interp.scope.restore_capture(&scope_capture);
9086 local_interp.scope.set_topic(item.clone());
9087 let val = match local_interp.exec_block_no_scope(&block) {
9088 Ok(v) => v,
9089 Err(_) => StrykeValue::UNDEF,
9090 };
9091 cache.insert(k, val.clone());
9092 pmap_progress.tick();
9093 val
9094 })
9095 .collect();
9096 pmap_progress.finish();
9097 self.push(StrykeValue::array(results));
9098 Ok(())
9099 }
9100 }
9101 Op::Pselect { n_rx, has_timeout } => {
9102 let timeout = if *has_timeout {
9103 let t = self.pop().to_number();
9104 Some(std::time::Duration::from_secs_f64(t.max(0.0)))
9105 } else {
9106 None
9107 };
9108 let mut rx_vals = Vec::with_capacity(*n_rx as usize);
9109 for _ in 0..*n_rx {
9110 rx_vals.push(self.pop());
9111 }
9112 rx_vals.reverse();
9113 let line = self.line();
9114 let v = crate::pchannel::pselect_recv_with_optional_timeout(
9115 &rx_vals, timeout, line,
9116 )?;
9117 self.push(v);
9118 Ok(())
9119 }
9120 Op::PGrepWithBlock(block_idx) => {
9121 let list = self.pop().to_list();
9122 let progress_flag = self.pop().is_true();
9123 let idx = *block_idx as usize;
9124 let subs = self.interp.subs.clone();
9125 let (scope_capture, atomic_arrays, atomic_hashes) =
9126 self.interp.scope.capture_with_atomics();
9127 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9128 let n_workers = rayon::current_num_threads();
9129 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
9130 .map(|_| {
9131 let mut interp = VMHelper::new();
9132 interp.subs = subs.clone();
9133 interp.scope.restore_capture(&scope_capture);
9134 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
9135 interp.enable_parallel_guard();
9136 Mutex::new(interp)
9137 })
9138 .collect();
9139 if let Some(&(start, end)) =
9140 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9141 {
9142 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9143 let results: Vec<StrykeValue> = list
9144 .into_par_iter()
9145 .filter_map(|item| {
9146 let tid =
9147 rayon::current_thread_index().unwrap_or(0) % pool.len();
9148 let mut local_interp = pool[tid].lock();
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 keep = match vm.run_block_region(start, end, &mut op_count)
9153 {
9154 Ok(val) => val.is_true(),
9155 Err(_) => false,
9156 };
9157 pmap_progress.tick();
9158 if keep {
9159 Some(item)
9160 } else {
9161 None
9162 }
9163 })
9164 .collect();
9165 pmap_progress.finish();
9166 self.push(StrykeValue::array(results));
9167 Ok(())
9168 } else {
9169 let block = self.blocks[idx].clone();
9170 let results: Vec<StrykeValue> = list
9171 .into_par_iter()
9172 .filter_map(|item| {
9173 let tid =
9174 rayon::current_thread_index().unwrap_or(0) % pool.len();
9175 let mut local_interp = pool[tid].lock();
9176 local_interp.scope.set_topic(item.clone());
9177 local_interp.scope_push_hook();
9178 let keep = match local_interp.exec_block_no_scope(&block) {
9179 Ok(val) => val.is_true(),
9180 Err(_) => false,
9181 };
9182 local_interp.scope_pop_hook();
9183 pmap_progress.tick();
9184 if keep {
9185 Some(item)
9186 } else {
9187 None
9188 }
9189 })
9190 .collect();
9191 pmap_progress.finish();
9192 self.push(StrykeValue::array(results));
9193 Ok(())
9194 }
9195 }
9196 Op::PMapsWithBlock(block_idx) => {
9197 let val = self.pop();
9198 let block = self.blocks[*block_idx as usize].clone();
9199 let source = crate::map_stream::into_pull_iter(val);
9200 let sub = self.interp.anon_coderef_from_block(&block);
9201 let (capture, atomic_arrays, atomic_hashes) =
9202 self.interp.scope.capture_with_atomics();
9203 let out = StrykeValue::iterator(Arc::new(
9204 crate::map_stream::PMapStreamIterator::new(
9205 source,
9206 sub,
9207 self.interp.subs.clone(),
9208 capture,
9209 atomic_arrays,
9210 atomic_hashes,
9211 false,
9212 ),
9213 ));
9214 self.push(out);
9215 Ok(())
9216 }
9217 Op::PFlatMapsWithBlock(block_idx) => {
9218 let val = self.pop();
9219 let block = self.blocks[*block_idx as usize].clone();
9220 let source = crate::map_stream::into_pull_iter(val);
9221 let sub = self.interp.anon_coderef_from_block(&block);
9222 let (capture, atomic_arrays, atomic_hashes) =
9223 self.interp.scope.capture_with_atomics();
9224 let out = StrykeValue::iterator(Arc::new(
9225 crate::map_stream::PMapStreamIterator::new(
9226 source,
9227 sub,
9228 self.interp.subs.clone(),
9229 capture,
9230 atomic_arrays,
9231 atomic_hashes,
9232 true,
9233 ),
9234 ));
9235 self.push(out);
9236 Ok(())
9237 }
9238 Op::PGrepsWithBlock(block_idx) => {
9239 let val = self.pop();
9240 let block = self.blocks[*block_idx as usize].clone();
9241 let source = crate::map_stream::into_pull_iter(val);
9242 let sub = self.interp.anon_coderef_from_block(&block);
9243 let (capture, atomic_arrays, atomic_hashes) =
9244 self.interp.scope.capture_with_atomics();
9245 let out = StrykeValue::iterator(Arc::new(
9246 crate::map_stream::PGrepStreamIterator::new(
9247 source,
9248 sub,
9249 self.interp.subs.clone(),
9250 capture,
9251 atomic_arrays,
9252 atomic_hashes,
9253 ),
9254 ));
9255 self.push(out);
9256 Ok(())
9257 }
9258 Op::PForWithBlock(block_idx) => {
9259 let line = self.line();
9260 let list = self.pop().to_list();
9261 let progress_flag = self.pop().is_true();
9262 let pmap_progress = PmapProgress::new(progress_flag, list.len());
9263 let idx = *block_idx as usize;
9264 let subs = self.interp.subs.clone();
9265 let (scope_capture, atomic_arrays, atomic_hashes) =
9266 self.interp.scope.capture_with_atomics();
9267 let first_err: Arc<Mutex<Option<StrykeError>>> = Arc::new(Mutex::new(None));
9268 let n_workers = rayon::current_num_threads();
9269 let pool: Vec<Mutex<VMHelper>> = (0..n_workers)
9270 .map(|_| {
9271 let mut interp = VMHelper::new();
9272 interp.subs = subs.clone();
9273 interp.scope.restore_capture(&scope_capture);
9274 interp.scope.restore_atomics(&atomic_arrays, &atomic_hashes);
9275 interp.enable_parallel_guard();
9276 Mutex::new(interp)
9277 })
9278 .collect();
9279 if let Some(&(start, end)) =
9280 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9281 {
9282 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9283 list.into_par_iter().for_each(|item| {
9284 if first_err.lock().is_some() {
9285 return;
9286 }
9287 let tid = rayon::current_thread_index().unwrap_or(0) % pool.len();
9288 let mut local_interp = pool[tid].lock();
9289 local_interp.scope.set_topic(item);
9290 let mut vm = shared.worker_vm(&mut local_interp);
9291 let mut op_count = 0u64;
9292 match vm.run_block_region(start, end, &mut op_count) {
9293 Ok(_) => {}
9294 Err(e) => {
9295 let mut g = first_err.lock();
9296 if g.is_none() {
9297 *g = Some(e);
9298 }
9299 }
9300 }
9301 pmap_progress.tick();
9302 });
9303 } else {
9304 let block = self.blocks[idx].clone();
9305 list.into_par_iter().for_each(|item| {
9306 if first_err.lock().is_some() {
9307 return;
9308 }
9309 let tid = rayon::current_thread_index().unwrap_or(0) % pool.len();
9310 let mut local_interp = pool[tid].lock();
9311 local_interp.scope.set_topic(item);
9312 local_interp.scope_push_hook();
9313 match local_interp.exec_block_no_scope(&block) {
9314 Ok(_) => {}
9315 Err(e) => {
9316 let stryke = match e {
9317 FlowOrError::Error(stryke) => stryke,
9318 FlowOrError::Flow(_) => StrykeError::runtime(
9319 "return/last/next/redo not supported inside pfor block",
9320 line,
9321 ),
9322 };
9323 let mut g = first_err.lock();
9324 if g.is_none() {
9325 *g = Some(stryke);
9326 }
9327 }
9328 }
9329 local_interp.scope_pop_hook();
9330 pmap_progress.tick();
9331 });
9332 }
9333 pmap_progress.finish();
9334 if let Some(e) = first_err.lock().take() {
9335 return Err(e);
9336 }
9337 self.push(StrykeValue::UNDEF);
9338 Ok(())
9339 }
9340 Op::PSortWithBlock(block_idx) => {
9341 let mut items = self.pop().to_list();
9342 let progress_flag = self.pop().is_true();
9343 let pmap_progress = PmapProgress::new(progress_flag, 2);
9344 pmap_progress.tick();
9345 let idx = *block_idx as usize;
9346 let subs = self.interp.subs.clone();
9347 let (scope_capture, atomic_arrays, atomic_hashes) =
9348 self.interp.scope.capture_with_atomics();
9349 if let Some(&(start, end)) =
9350 self.block_bytecode_ranges.get(idx).and_then(|r| r.as_ref())
9351 {
9352 let shared = Arc::new(ParallelBlockVmShared::from_vm(self));
9353 items.par_sort_by(|a, b| {
9354 let mut local_interp = VMHelper::new();
9355 local_interp.subs = subs.clone();
9356 local_interp.scope.restore_capture(&scope_capture);
9357 local_interp
9358 .scope
9359 .restore_atomics(&atomic_arrays, &atomic_hashes);
9360 local_interp.enable_parallel_guard();
9361 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9362 local_interp.scope.set_closure_args(&[a.clone(), b.clone()]);
9369 let mut vm = shared.worker_vm(&mut local_interp);
9370 let mut op_count = 0u64;
9371 match vm.run_block_region(start, end, &mut op_count) {
9372 Ok(v) => {
9373 let n = v.to_int();
9374 if n < 0 {
9375 std::cmp::Ordering::Less
9376 } else if n > 0 {
9377 std::cmp::Ordering::Greater
9378 } else {
9379 std::cmp::Ordering::Equal
9380 }
9381 }
9382 Err(_) => std::cmp::Ordering::Equal,
9383 }
9384 });
9385 } else {
9386 let block = self.blocks[idx].clone();
9387 items.par_sort_by(|a, b| {
9388 let mut local_interp = VMHelper::new();
9389 local_interp.subs = subs.clone();
9390 local_interp.scope.restore_capture(&scope_capture);
9391 local_interp
9392 .scope
9393 .restore_atomics(&atomic_arrays, &atomic_hashes);
9394 local_interp.enable_parallel_guard();
9395 local_interp.scope.set_sort_pair(a.clone(), b.clone());
9396 local_interp.scope.set_closure_args(&[a.clone(), b.clone()]);
9397 local_interp.scope_push_hook();
9398 let ord = match local_interp.exec_block_no_scope(&block) {
9399 Ok(v) => {
9400 let n = v.to_int();
9401 if n < 0 {
9402 std::cmp::Ordering::Less
9403 } else if n > 0 {
9404 std::cmp::Ordering::Greater
9405 } else {
9406 std::cmp::Ordering::Equal
9407 }
9408 }
9409 Err(_) => std::cmp::Ordering::Equal,
9410 };
9411 local_interp.scope_pop_hook();
9412 ord
9413 });
9414 }
9415 pmap_progress.tick();
9416 pmap_progress.finish();
9417 self.push(StrykeValue::array(items));
9418 Ok(())
9419 }
9420 Op::PSortWithBlockFast(tag) => {
9421 let mut items = self.pop().to_list();
9422 let progress_flag = self.pop().is_true();
9423 let pmap_progress = PmapProgress::new(progress_flag, 2);
9424 pmap_progress.tick();
9425 let mode = match *tag {
9426 0 => SortBlockFast::Numeric,
9427 1 => SortBlockFast::String,
9428 2 => SortBlockFast::NumericRev,
9429 3 => SortBlockFast::StringRev,
9430 _ => SortBlockFast::Numeric,
9431 };
9432 items.par_sort_by(|a, b| sort_magic_cmp(a, b, mode));
9433 pmap_progress.tick();
9434 pmap_progress.finish();
9435 self.push(StrykeValue::array(items));
9436 Ok(())
9437 }
9438 Op::PSortNoBlockParallel => {
9439 let mut items = self.pop().to_list();
9440 let progress_flag = self.pop().is_true();
9441 let pmap_progress = PmapProgress::new(progress_flag, 2);
9442 pmap_progress.tick();
9443 items.par_sort_by(|a, b| a.to_string().cmp(&b.to_string()));
9444 pmap_progress.tick();
9445 pmap_progress.finish();
9446 self.push(StrykeValue::array(items));
9447 Ok(())
9448 }
9449 Op::FanWithBlock(block_idx) => {
9450 let line = self.line();
9451 let n = self.pop().to_int().max(0) as usize;
9452 let progress_flag = self.pop().is_true();
9453 self.run_fan_block(*block_idx, n, line, progress_flag)?;
9454 Ok(())
9455 }
9456 Op::FanWithBlockAuto(block_idx) => {
9457 let line = self.line();
9458 let n = self.interp.parallel_thread_count();
9459 let progress_flag = self.pop().is_true();
9460 self.run_fan_block(*block_idx, n, line, progress_flag)?;
9461 Ok(())
9462 }
9463 Op::FanCapWithBlock(block_idx) => {
9464 let line = self.line();
9465 let n = self.pop().to_int().max(0) as usize;
9466 let progress_flag = self.pop().is_true();
9467 self.run_fan_cap_block(*block_idx, n, line, progress_flag)?;
9468 Ok(())
9469 }
9470 Op::FanCapWithBlockAuto(block_idx) => {
9471 let line = self.line();
9472 let n = self.interp.parallel_thread_count();
9473 let progress_flag = self.pop().is_true();
9474 self.run_fan_cap_block(*block_idx, n, line, progress_flag)?;
9475 Ok(())
9476 }
9477
9478 Op::AsyncBlock(block_idx) => {
9479 let block = self.blocks[*block_idx as usize].clone();
9480 let subs = self.interp.subs.clone();
9481 let (scope_capture, atomic_arrays, atomic_hashes) =
9482 self.interp.scope.capture_with_atomics();
9483 let result_slot: Arc<Mutex<Option<StrykeResult<StrykeValue>>>> =
9484 Arc::new(Mutex::new(None));
9485 let join_slot: Arc<Mutex<Option<std::thread::JoinHandle<()>>>> =
9486 Arc::new(Mutex::new(None));
9487 let rs = Arc::clone(&result_slot);
9488 let h = std::thread::spawn(move || {
9489 let mut local_interp = VMHelper::new();
9490 local_interp.subs = subs;
9491 local_interp.scope.restore_capture(&scope_capture);
9492 local_interp
9493 .scope
9494 .restore_atomics(&atomic_arrays, &atomic_hashes);
9495 local_interp.enable_parallel_guard();
9496 local_interp.scope_push_hook();
9497 let out = match local_interp.exec_block_no_scope(&block) {
9498 Ok(v) => Ok(v),
9499 Err(FlowOrError::Flow(Flow::Return(v))) => Ok(v),
9500 Err(FlowOrError::Error(e)) => Err(e),
9501 Err(_) => Ok(StrykeValue::UNDEF),
9502 };
9503 local_interp.scope_pop_hook();
9504 *rs.lock() = Some(out);
9505 });
9506 *join_slot.lock() = Some(h);
9507 self.push(StrykeValue::async_task(Arc::new(StrykeAsyncTask {
9508 result: result_slot,
9509 join: join_slot,
9510 })));
9511 Ok(())
9512 }
9513 Op::Await => {
9514 let v = self.pop();
9515 if let Some(t) = v.as_async_task() {
9516 let r = t.await_result();
9517 self.push(r?);
9518 } else {
9519 self.push(v);
9520 }
9521 Ok(())
9522 }
9523
9524 Op::LoadCurrentSub => {
9525 if let Some(sub) = self.interp.current_sub_stack.last().cloned() {
9526 self.push(StrykeValue::code_ref(sub));
9527 } else {
9528 self.push(StrykeValue::UNDEF);
9529 }
9530 Ok(())
9531 }
9532
9533 Op::DeferBlock => {
9534 let coderef = self.pop();
9535 self.interp.scope.push_defer(coderef);
9536 Ok(())
9537 }
9538
9539 Op::TryPush { .. } => {
9541 self.try_stack.push(TryFrame {
9542 try_push_op_idx: self.ip - 1,
9543 state: TryState::Trying,
9544 deferred_error: None,
9545 });
9546 Ok(())
9547 }
9548 Op::TryContinueNormal => {
9549 let frame = self.try_stack.last().ok_or_else(|| {
9550 StrykeError::runtime(
9551 "TryContinueNormal without active try",
9552 self.line(),
9553 )
9554 })?;
9555 let Op::TryPush {
9556 finally_ip,
9557 after_ip,
9558 ..
9559 } = &self.ops[frame.try_push_op_idx]
9560 else {
9561 return Err(StrykeError::runtime(
9562 "TryContinueNormal: corrupt try frame",
9563 self.line(),
9564 ));
9565 };
9566 if let Some(fin_ip) = *finally_ip {
9567 self.ip = fin_ip;
9568 Ok(())
9569 } else {
9570 self.try_stack.pop();
9571 self.ip = *after_ip;
9572 Ok(())
9573 }
9574 }
9575 Op::TryFinallyEnd => {
9576 let frame = self.try_stack.pop().ok_or_else(|| {
9577 StrykeError::runtime("TryFinallyEnd without active try", self.line())
9578 })?;
9579 if let Some(deferred) = frame.deferred_error {
9582 return Err(deferred);
9583 }
9584 let Op::TryPush { after_ip, .. } = &self.ops[frame.try_push_op_idx] else {
9585 return Err(StrykeError::runtime(
9586 "TryFinallyEnd: corrupt try frame",
9587 self.line(),
9588 ));
9589 };
9590 self.ip = *after_ip;
9591 Ok(())
9592 }
9593 Op::CatchReceive(idx) => {
9594 let val = self.pending_catch_error.take().ok_or_else(|| {
9595 StrykeError::runtime(
9596 "CatchReceive without pending exception",
9597 self.line(),
9598 )
9599 })?;
9600 let n = names[*idx as usize].as_str();
9601 self.interp.scope_pop_hook();
9602 self.interp.scope_push_hook();
9603 self.interp.scope.declare_scalar(n, val);
9604 self.interp.english_note_lexical_scalar(n);
9605 Ok(())
9606 }
9607
9608 Op::DeclareMySyncScalar(name_idx) => {
9609 let val = self.pop();
9610 let n = names[*name_idx as usize].as_str();
9611 let stored = if val.is_mysync_deque_or_heap() {
9612 val
9613 } else {
9614 StrykeValue::atomic(Arc::new(Mutex::new(val)))
9615 };
9616 self.interp.scope.declare_scalar(n, stored);
9617 Ok(())
9618 }
9619 Op::DeclareMySyncArray(name_idx) => {
9620 let val = self.pop();
9621 let n = names[*name_idx as usize].as_str();
9622 self.interp.scope.declare_atomic_array(n, val.to_list());
9623 Ok(())
9624 }
9625 Op::DeclareMySyncHash(name_idx) => {
9626 let val = self.pop();
9627 let n = names[*name_idx as usize].as_str();
9628 let items = val.to_list();
9629 let mut map = IndexMap::new();
9630 let mut i = 0usize;
9631 while i + 1 < items.len() {
9632 map.insert(items[i].to_string(), items[i + 1].clone());
9633 i += 2;
9634 }
9635 self.interp.scope.declare_atomic_hash(n, map);
9636 Ok(())
9637 }
9638 Op::DeclareOurSyncScalar(name_idx) => {
9639 let val = self.pop();
9640 let n = names[*name_idx as usize].as_str();
9641 let stored = if val.is_mysync_deque_or_heap() {
9642 val
9643 } else {
9644 StrykeValue::atomic(Arc::new(Mutex::new(val)))
9645 };
9646 self.interp.scope.declare_scalar(n, stored);
9647 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9652 self.interp.english_note_lexical_scalar_pub(&bare);
9653 self.interp.note_our_scalar_pub(&bare);
9654 Ok(())
9655 }
9656 Op::DeclareOurSyncArray(name_idx) => {
9657 let val = self.pop();
9658 let n = names[*name_idx as usize].as_str();
9659 self.interp.scope.declare_atomic_array(n, val.to_list());
9660 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9661 self.interp.english_note_lexical_scalar_pub(&bare);
9662 self.interp.note_our_scalar_pub(&bare);
9663 Ok(())
9664 }
9665 Op::DeclareOurSyncHash(name_idx) => {
9666 let val = self.pop();
9667 let n = names[*name_idx as usize].as_str();
9668 let items = val.to_list();
9669 let mut map = IndexMap::new();
9670 let mut i = 0usize;
9671 while i + 1 < items.len() {
9672 map.insert(items[i].to_string(), items[i + 1].clone());
9673 i += 2;
9674 }
9675 self.interp.scope.declare_atomic_hash(n, map);
9676 let bare = n.rsplit("::").next().unwrap_or(n).to_string();
9677 self.interp.english_note_lexical_scalar_pub(&bare);
9678 self.interp.note_our_scalar_pub(&bare);
9679 Ok(())
9680 }
9681 Op::RuntimeSubDecl(idx) => {
9682 let rs = &self.runtime_sub_decls[*idx as usize];
9683 let key = self.interp.qualify_sub_key(&rs.name);
9684 let captured = self.interp.scope.capture();
9685 let closure_env = if captured.is_empty() {
9686 None
9687 } else {
9688 Some(captured)
9689 };
9690 let mut sub = StrykeSub {
9691 name: rs.name.clone(),
9692 params: rs.params.clone(),
9693 body: rs.body.clone(),
9694 closure_env,
9695 prototype: rs.prototype.clone(),
9696 fib_like: None,
9697 };
9698 sub.fib_like = crate::fib_like_tail::detect_fib_like_recursive_add(&sub);
9699 self.interp.subs.insert(key, Arc::new(sub));
9700 Ok(())
9701 }
9702 Op::RegisterAdvice(idx) => {
9703 let rd = &self.runtime_advice_decls[*idx as usize];
9704 let id = self.interp.next_intercept_id;
9705 self.interp.next_intercept_id = id.saturating_add(1);
9706 self.interp.intercepts.push(crate::aop::Intercept {
9707 id,
9708 kind: rd.kind,
9709 pattern: rd.pattern.clone(),
9710 body: rd.body.clone(),
9711 body_block_idx: rd.body_block_idx,
9712 });
9713 Ok(())
9714 }
9715 Op::Tie {
9716 target_kind,
9717 name_idx,
9718 argc,
9719 } => {
9720 let argc = *argc as usize;
9721 let mut stack_vals = Vec::with_capacity(argc);
9722 for _ in 0..argc {
9723 stack_vals.push(self.pop());
9724 }
9725 stack_vals.reverse();
9726 let name = names[*name_idx as usize].as_str();
9727 let line = self.line();
9728 self.interp
9729 .tie_execute(*target_kind, name, stack_vals, line)
9730 .map_err(|e| e.at_line(line))?;
9731 Ok(())
9732 }
9733 Op::FormatDecl(idx) => {
9734 let (basename, lines) = &self.format_decls[*idx as usize];
9735 let line = self.line();
9736 self.interp
9737 .install_format_decl(basename.as_str(), lines, line)
9738 .map_err(|e| e.at_line(line))?;
9739 Ok(())
9740 }
9741 Op::UseOverload(idx) => {
9742 let pairs = &self.use_overload_entries[*idx as usize];
9743 self.interp.install_use_overload_pairs(pairs);
9744 Ok(())
9745 }
9746 Op::ScalarCompoundAssign { name_idx, op: op_b } => {
9747 let rhs = self.pop();
9748 let n = names[*name_idx as usize].as_str();
9749 let op = scalar_compound_op_from_byte(*op_b).ok_or_else(|| {
9750 StrykeError::runtime(
9751 "ScalarCompoundAssign: invalid op byte",
9752 self.line(),
9753 )
9754 })?;
9755 let en = self.interp.english_scalar_name(n);
9756 let val = self
9757 .interp
9758 .scalar_compound_assign_scalar_target(en, op, rhs)
9759 .map_err(|e| e.at_line(self.line()))?;
9760 self.push(val);
9761 Ok(())
9762 }
9763
9764 Op::SetGlobalPhase(phase) => {
9765 let s = match *phase {
9766 crate::bytecode::GP_START => "START",
9767 crate::bytecode::GP_UNITCHECK => "UNITCHECK",
9768 crate::bytecode::GP_CHECK => "CHECK",
9769 crate::bytecode::GP_INIT => "INIT",
9770 crate::bytecode::GP_RUN => "RUN",
9771 crate::bytecode::GP_END => "END",
9772 _ => {
9773 return Err(StrykeError::runtime(
9774 format!("SetGlobalPhase: invalid phase byte {}", phase),
9775 self.line(),
9776 ));
9777 }
9778 };
9779 self.interp.global_phase = s.to_string();
9780 Ok(())
9781 }
9782
9783 Op::Halt => {
9785 self.halt = true;
9786 Ok(())
9787 }
9788 Op::EvalAstExpr(idx) => {
9789 let expr = &self.ast_eval_exprs[*idx as usize];
9790 let val = match self.interp.eval_expr_ctx(expr, self.interp.wantarray_kind)
9791 {
9792 Ok(v) => v,
9793 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
9794 Err(crate::vm_helper::FlowOrError::Flow(f)) => {
9795 return Err(StrykeError::runtime(
9796 format!("unexpected flow control in EvalAstExpr: {:?}", f),
9797 self.line(),
9798 ));
9799 }
9800 };
9801 self.push(val);
9802 Ok(())
9803 }
9804 }
9805 })();
9806 if let (Some(prof), Some(t0)) = (&mut self.interp.profiler, op_prof_t0) {
9807 prof.on_line(&self.interp.file, line, t0.elapsed());
9808 }
9809 if let Err(e) = __op_res {
9810 if self.try_recover_from_exception(&e)? {
9811 continue;
9812 }
9813 return Err(e);
9814 }
9815 if crate::pending_destroy::pending_destroy_vm_sync_needed() {
9818 self.interp.drain_pending_destroys(line)?;
9819 }
9820 if self.exit_main_dispatch {
9821 if let Some(v) = self.exit_main_dispatch_value.take() {
9822 last = v;
9823 }
9824 break;
9825 }
9826 if self.halt {
9827 break;
9828 }
9829 }
9830
9831 if !self.stack.is_empty() {
9832 last = self.stack.last().cloned().unwrap_or(StrykeValue::UNDEF);
9833 if last.is_iterator() {
9836 let iter = last.clone().into_iterator();
9837 while iter.next_item().is_some() {}
9838 last = StrykeValue::UNDEF;
9839 }
9840 }
9841
9842 Ok(last)
9843 }
9844
9845 pub(crate) fn jit_trampoline_run_sub(
9847 &mut self,
9848 entry_ip: usize,
9849 want: WantarrayCtx,
9850 args: &[i64],
9851 ) -> StrykeResult<StrykeValue> {
9852 let saved_wa = self.interp.wantarray_kind;
9853 for a in args {
9854 self.push(StrykeValue::integer(*a));
9855 }
9856 let stack_base = self.stack.len() - args.len();
9857 let mut sub_prof_t0 = None;
9858 if let Some(nidx) = self.sub_entry_name_idx(entry_ip) {
9859 sub_prof_t0 = self.interp.profiler.is_some().then(std::time::Instant::now);
9860 let nm_owned = self.names[nidx as usize].to_string();
9861 if let Some(p) = &mut self.interp.profiler {
9862 p.enter_sub(nm_owned.as_str());
9863 }
9864 self.interp.debugger_enter_sub(nm_owned.as_str());
9865 }
9866 self.call_stack.push(CallFrame {
9867 return_ip: 0,
9868 stack_base,
9869 scope_depth: self.interp.scope.depth(),
9870 saved_wantarray: saved_wa,
9871 jit_trampoline_return: true,
9872 block_region: false,
9873 sub_profiler_start: sub_prof_t0,
9874 });
9875 self.interp.wantarray_kind = want;
9876 self.interp.scope_push_hook();
9877 if let Some(nidx) = self.sub_entry_name_idx(entry_ip) {
9878 let nm = self.names[nidx as usize].as_str();
9879 if let Some(sub) = self.interp.subs.get(nm).cloned() {
9880 if let Some(ref env) = sub.closure_env {
9881 self.interp.scope.restore_capture(env);
9882 }
9883 }
9884 }
9885 self.ip = entry_ip;
9886 self.jit_trampoline_out = None;
9887 self.jit_trampoline_depth = self.jit_trampoline_depth.saturating_add(1);
9888 let mut op_count = 0u64;
9889 let last = StrykeValue::UNDEF;
9890 let r = self.run_main_dispatch_loop(last, &mut op_count, true);
9891 self.jit_trampoline_depth = self.jit_trampoline_depth.saturating_sub(1);
9892 r?;
9893 self.jit_trampoline_out.take().ok_or_else(|| {
9894 StrykeError::runtime("JIT trampoline: subroutine did not return", self.line())
9895 })
9896 }
9897
9898 #[inline]
9899 fn find_sub_entry(&self, name_idx: u16) -> Option<(usize, bool)> {
9900 self.sub_entry_by_name.get(&name_idx).copied()
9901 }
9902
9903 fn sub_entry_name_idx(&self, entry_ip: usize) -> Option<u16> {
9905 for &(n, ip, _) in &self.sub_entries {
9906 if ip == entry_ip {
9907 return Some(n);
9908 }
9909 }
9910 None
9911 }
9912
9913 fn exec_builtin(&mut self, id: u16, args: Vec<StrykeValue>) -> StrykeResult<StrykeValue> {
9914 let line = self.line();
9915 let bid = BuiltinId::from_u16(id);
9916 match bid {
9917 Some(BuiltinId::Length) => {
9918 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9919 Ok(StrykeValue::integer(val.length_value(self.interp.utf8_pragma)))
9920 }
9921 Some(BuiltinId::Defined) => {
9922 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9923 Ok(StrykeValue::integer(if val.is_undef() { 0 } else { 1 }))
9924 }
9925 Some(BuiltinId::Abs) => {
9926 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9927 Ok(StrykeValue::float(val.to_number().abs()))
9928 }
9929 Some(BuiltinId::Int) => {
9930 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9931 Ok(StrykeValue::integer(val.to_number() as i64))
9932 }
9933 Some(BuiltinId::Sqrt) => {
9934 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9935 Ok(StrykeValue::float(val.to_number().sqrt()))
9936 }
9937 Some(BuiltinId::Sin) => {
9938 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9939 Ok(StrykeValue::float(val.to_number().sin()))
9940 }
9941 Some(BuiltinId::Cos) => {
9942 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9943 Ok(StrykeValue::float(val.to_number().cos()))
9944 }
9945 Some(BuiltinId::Atan2) => {
9946 let mut it = args.into_iter();
9947 let y = it.next().unwrap_or(StrykeValue::UNDEF);
9948 let x = it.next().unwrap_or(StrykeValue::UNDEF);
9949 Ok(StrykeValue::float(y.to_number().atan2(x.to_number())))
9950 }
9951 Some(BuiltinId::Exp) => {
9952 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9953 Ok(StrykeValue::float(val.to_number().exp()))
9954 }
9955 Some(BuiltinId::Log) => {
9956 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9957 Ok(StrykeValue::float(val.to_number().ln()))
9958 }
9959 Some(BuiltinId::Rand) => {
9960 let upper = match args.len() {
9961 0 => 1.0,
9962 _ => args[0].to_number(),
9963 };
9964 Ok(StrykeValue::float(self.interp.perl_rand(upper)))
9965 }
9966 Some(BuiltinId::Srand) => {
9967 let seed = match args.len() {
9968 0 => None,
9969 _ => Some(args[0].to_number()),
9970 };
9971 Ok(StrykeValue::integer(self.interp.perl_srand(seed)))
9972 }
9973 Some(BuiltinId::Crypt) => {
9974 let mut it = args.into_iter();
9975 let p = it.next().unwrap_or(StrykeValue::UNDEF).to_string();
9976 let salt = it.next().unwrap_or(StrykeValue::UNDEF).to_string();
9977 Ok(StrykeValue::string(crate::crypt_util::perl_crypt(
9978 &p, &salt,
9979 )))
9980 }
9981 Some(BuiltinId::Fc) => {
9982 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
9983 Ok(StrykeValue::string(s.fc_value()))
9984 }
9985 Some(BuiltinId::Quotemeta) => {
9986 let s = args
9987 .into_iter()
9988 .next()
9989 .map(|v| v.to_string())
9990 .unwrap_or_default();
9991 Ok(StrykeValue::string(crate::perl_regex::perl_quotemeta(&s)))
9992 }
9993 Some(BuiltinId::Tan) => Ok(StrykeValue::float(
9994 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().tan(),
9995 )),
9996 Some(BuiltinId::Asin) => Ok(StrykeValue::float(
9997 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().asin(),
9998 )),
9999 Some(BuiltinId::Acos) => Ok(StrykeValue::float(
10000 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().acos(),
10001 )),
10002 Some(BuiltinId::Atan) => Ok(StrykeValue::float(
10003 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().atan(),
10004 )),
10005 Some(BuiltinId::Sinh) => Ok(StrykeValue::float(
10006 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().sinh(),
10007 )),
10008 Some(BuiltinId::Cosh) => Ok(StrykeValue::float(
10009 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().cosh(),
10010 )),
10011 Some(BuiltinId::Tanh) => Ok(StrykeValue::float(
10012 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().tanh(),
10013 )),
10014 Some(BuiltinId::Log2) => Ok(StrykeValue::float(
10015 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().log2(),
10016 )),
10017 Some(BuiltinId::Log10) => Ok(StrykeValue::float(
10018 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().log10(),
10019 )),
10020 Some(BuiltinId::Ceil) => Ok(StrykeValue::integer(
10021 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().ceil() as i64,
10022 )),
10023 Some(BuiltinId::Floor) => Ok(StrykeValue::integer(
10024 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().floor() as i64,
10025 )),
10026 Some(BuiltinId::Round) => Ok(StrykeValue::integer(
10030 args.into_iter().next().unwrap_or(StrykeValue::UNDEF).to_number().round() as i64,
10031 )),
10032 Some(BuiltinId::Pos) => {
10033 let key = if args.is_empty() {
10034 "_".to_string()
10035 } else {
10036 args[0].to_string()
10037 };
10038 Ok(self
10039 .interp
10040 .regex_pos
10041 .get(&key)
10042 .copied()
10043 .flatten()
10044 .map(|n| StrykeValue::integer(n as i64))
10045 .unwrap_or(StrykeValue::UNDEF))
10046 }
10047 Some(BuiltinId::Study) => {
10048 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10049 Ok(VMHelper::study_return_value(&s.to_string()))
10050 }
10051 Some(BuiltinId::Chr) => {
10052 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10053 Ok(StrykeValue::string(val.chr_value()))
10054 }
10055 Some(BuiltinId::Ord) => {
10056 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10057 Ok(StrykeValue::integer(val.ord_value()))
10058 }
10059 Some(BuiltinId::Hex) => {
10060 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10061 Ok(StrykeValue::integer(val.hex_value()))
10062 }
10063 Some(BuiltinId::Oct) => {
10064 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10065 Ok(StrykeValue::integer(val.oct_value()))
10066 }
10067 Some(BuiltinId::Uc) => {
10068 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10069 Ok(StrykeValue::string(s.uc_value()))
10070 }
10071 Some(BuiltinId::Lc) => {
10072 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10073 Ok(StrykeValue::string(s.lc_value()))
10074 }
10075 Some(BuiltinId::Ref) => {
10076 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10077 Ok(val.ref_type())
10078 }
10079 Some(BuiltinId::Scalar) => {
10080 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10081 Ok(val.scalar_context())
10082 }
10083 Some(BuiltinId::Join) => {
10084 let mut iter = args.into_iter();
10085 let sep = iter.next().unwrap_or(StrykeValue::UNDEF).to_string();
10086 let list = iter.next().unwrap_or(StrykeValue::UNDEF).to_list();
10087 let mut strs = Vec::with_capacity(list.len());
10088 for v in list {
10089 let s = match self.interp.stringify_value(v, line) {
10090 Ok(s) => s,
10091 Err(FlowOrError::Error(e)) => return Err(e),
10092 Err(FlowOrError::Flow(_)) => {
10093 return Err(StrykeError::runtime(
10094 "join: unexpected control flow",
10095 line,
10096 ));
10097 }
10098 };
10099 strs.push(s);
10100 }
10101 Ok(StrykeValue::string(strs.join(&sep)))
10102 }
10103 Some(BuiltinId::Split) => {
10104 let mut iter = args.into_iter();
10105 let pat_val = iter.next().unwrap_or(StrykeValue::string(" ".into()));
10106 let pat = pat_val
10112 .regex_src_and_flags()
10113 .map(|(s, _)| s)
10114 .unwrap_or_else(|| pat_val.to_string());
10115 let s = iter.next().unwrap_or(StrykeValue::UNDEF).to_string();
10116 if s.is_empty() {
10119 return Ok(StrykeValue::array(vec![]));
10120 }
10121 let lim_signed: Option<i64> = iter.next().map(|v| v.to_int());
10126
10127 let mut parts: Vec<String> = if pat.is_empty() {
10128 let chars: Vec<String> = s.chars().map(|c| c.to_string()).collect();
10132 match lim_signed {
10133 Some(l) if l > 0 => {
10140 let n = l as usize;
10141 if n < chars.len() {
10142 let mut head: Vec<String> =
10143 chars.iter().take(n.saturating_sub(1)).cloned().collect();
10144 let tail: String = s.chars().skip(n.saturating_sub(1)).collect();
10145 head.push(tail);
10146 head
10147 } else if n == chars.len() {
10148 chars
10149 } else {
10150 let mut v = chars;
10151 v.push(String::new());
10152 v
10153 }
10154 }
10155 Some(l) if l < 0 => {
10157 let mut v = chars;
10158 v.push(String::new());
10159 v
10160 }
10161 _ => chars,
10164 }
10165 } else {
10166 let re =
10167 regex::Regex::new(&pat).unwrap_or_else(|_| regex::Regex::new(" ").unwrap());
10168 match lim_signed {
10169 Some(l) if l > 0 => {
10170 re.splitn(&s, l as usize).map(|p| p.to_string()).collect()
10171 }
10172 _ => re.split(&s).map(|p| p.to_string()).collect(),
10173 }
10174 };
10175
10176 let strip_trailing = matches!(lim_signed, None | Some(0));
10180 if strip_trailing {
10181 while parts.last().is_some_and(|p| p.is_empty()) {
10182 parts.pop();
10183 }
10184 }
10185
10186 Ok(StrykeValue::array(
10187 parts.into_iter().map(StrykeValue::string).collect(),
10188 ))
10189 }
10190 Some(BuiltinId::Sprintf) => {
10191 let mut flat: Vec<StrykeValue> = Vec::with_capacity(args.len());
10194 for a in args.into_iter() {
10195 if let Some(items) = a.as_array_vec() {
10196 flat.extend(items);
10197 } else {
10198 flat.push(a);
10199 }
10200 }
10201 let args = flat;
10202 if args.is_empty() {
10203 return Ok(StrykeValue::string(String::new()));
10204 }
10205 let fmt = args[0].to_string();
10206 let rest = &args[1..];
10207 match self.interp.perl_sprintf_stringify(&fmt, rest, line) {
10208 Ok(s) => Ok(StrykeValue::string(s)),
10209 Err(FlowOrError::Error(e)) => Err(e),
10210 Err(FlowOrError::Flow(_)) => Err(StrykeError::runtime(
10211 "sprintf: unexpected control flow",
10212 line,
10213 )),
10214 }
10215 }
10216 Some(BuiltinId::Reverse) => {
10217 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10218 Ok(if let Some(mut a) = val.as_array_vec() {
10219 a.reverse();
10220 StrykeValue::array(a)
10221 } else if let Some(s) = val.as_str() {
10222 StrykeValue::string(s.chars().rev().collect())
10223 } else {
10224 StrykeValue::string(val.to_string().chars().rev().collect())
10225 })
10226 }
10227 Some(BuiltinId::Die) => {
10228 if args.len() == 1 {
10231 let v = &args[0];
10232 if v.as_hash_ref().is_some()
10233 || v.as_blessed_ref().is_some()
10234 || v.as_array_ref().is_some()
10235 || v.as_code_ref().is_some()
10236 {
10237 let msg = v.to_string();
10238 self.interp.fire_pseudosig_die(&msg, line)?;
10239 return Err(StrykeError::die_with_value(v.clone(), msg, line));
10240 }
10241 }
10242 let mut msg = String::new();
10243 for a in &args {
10244 msg.push_str(&a.to_string());
10245 }
10246 if msg.is_empty() {
10247 msg = "Died".to_string();
10248 }
10249 if !msg.ends_with('\n') {
10250 msg.push_str(&self.interp.die_warn_at_suffix(line));
10251 msg.push('\n');
10252 }
10253 self.interp.fire_pseudosig_die(&msg, line)?;
10254 Err(StrykeError::die(msg, line))
10255 }
10256 Some(BuiltinId::Warn) => {
10257 let mut msg = String::new();
10258 for a in &args {
10259 msg.push_str(&a.to_string());
10260 }
10261 if msg.is_empty() {
10262 msg = "Warning: something's wrong".to_string();
10263 }
10264 if !msg.ends_with('\n') {
10265 msg.push_str(&self.interp.die_warn_at_suffix(line));
10266 msg.push('\n');
10267 }
10268 self.interp.fire_pseudosig_warn(&msg, line)?;
10269 Ok(StrykeValue::integer(1))
10270 }
10271 Some(BuiltinId::Exit) => {
10272 let code = args
10273 .into_iter()
10274 .next()
10275 .map(|v| v.to_int() as i32)
10276 .unwrap_or(0);
10277 Err(StrykeError::new(
10278 ErrorKind::Exit(code),
10279 "",
10280 line,
10281 &self.interp.file,
10282 ))
10283 }
10284 Some(BuiltinId::System) => {
10285 let strs: Vec<String> = args.iter().map(|a| a.to_string()).collect();
10294 if strs.is_empty() {
10295 self.interp.child_exit_status = -1;
10296 return Ok(StrykeValue::integer(-1));
10297 }
10298 let status = if strs.len() == 1 {
10299 std::process::Command::new("sh")
10300 .arg("-c")
10301 .arg(&strs[0])
10302 .status()
10303 } else {
10304 std::process::Command::new(&strs[0])
10305 .args(&strs[1..])
10306 .status()
10307 };
10308 match status {
10309 Ok(s) => {
10310 self.interp.record_child_exit_status(s);
10311 Ok(StrykeValue::integer(self.interp.child_exit_status))
10312 }
10313 Err(e) => {
10314 self.interp.errno = e.to_string();
10315 self.interp.child_exit_status = -1;
10316 Ok(StrykeValue::integer(-1))
10317 }
10318 }
10319 }
10320 Some(BuiltinId::Ssh) => self.interp.ssh_builtin_execute(&args),
10321 Some(BuiltinId::Chomp) => {
10322 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10325 let s = val.to_string();
10326 Ok(StrykeValue::integer(if s.ends_with('\n') { 1 } else { 0 }))
10327 }
10328 Some(BuiltinId::Chop) => {
10329 let val = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10330 let s = val.to_string();
10331 Ok(s.chars()
10332 .last()
10333 .map(|c| StrykeValue::string(c.to_string()))
10334 .unwrap_or(StrykeValue::UNDEF))
10335 }
10336 Some(BuiltinId::Substr) => {
10337 if args.len() < 3 {
10338 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10339 let off = args.get(1).map(|v| v.to_int()).unwrap_or(0);
10340 return Ok(StrykeValue::string(s.substr2_value(off)));
10341 }
10342 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10343 let off = args.get(1).map(|v| v.to_int()).unwrap_or(0);
10344 let len = args.get(2).map(|v| v.to_int()).unwrap_or(0);
10345 Ok(StrykeValue::string(s.substr3_value(off, len)))
10346 }
10347 Some(BuiltinId::Index) => {
10348 let s = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10349 let sub = args.get(1).cloned().unwrap_or(StrykeValue::UNDEF);
10350 if args.len() < 3 {
10351 return Ok(StrykeValue::integer(s.index_value(&sub)));
10352 }
10353 let s = s.to_string();
10354 let sub = sub.to_string();
10355 let pos_raw = args.get(2).map(|v| v.to_int()).unwrap_or(0);
10358 let pos = if pos_raw < 0 {
10359 0usize
10360 } else {
10361 (pos_raw as usize).min(s.len())
10362 };
10363 Ok(StrykeValue::integer(
10364 s[pos..].find(&sub).map(|i| (i + pos) as i64).unwrap_or(-1),
10365 ))
10366 }
10367 Some(BuiltinId::Rindex) => {
10368 let sv = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
10369 let subv = args.get(1).cloned().unwrap_or(StrykeValue::UNDEF);
10370 if args.len() < 3 {
10371 return Ok(StrykeValue::integer(sv.rindex_value(&subv)));
10372 }
10373 let s = sv.to_string();
10374 let sub = subv.to_string();
10375 let result = match args.get(2) {
10378 Some(v) => {
10379 let p = v.to_int();
10380 if p < 0 {
10381 -1
10382 } else {
10383 let end = (p as usize).saturating_add(sub.len()).min(s.len());
10384 s[..end].rfind(&sub).map(|i| i as i64).unwrap_or(-1)
10385 }
10386 }
10387 None => s.rfind(&sub).map(|i| i as i64).unwrap_or(-1),
10388 };
10389 Ok(StrykeValue::integer(result))
10390 }
10391 Some(BuiltinId::Ucfirst) => {
10392 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10393 Ok(StrykeValue::string(s.ucfirst_value()))
10394 }
10395 Some(BuiltinId::Lcfirst) => {
10396 let s = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10397 Ok(StrykeValue::string(s.lcfirst_value()))
10398 }
10399 Some(BuiltinId::Splice) => self.interp.splice_builtin_execute(&args, line),
10400 Some(BuiltinId::Unshift) => self.interp.unshift_builtin_execute(&args, line),
10401 Some(BuiltinId::Printf) => {
10402 let mut flat: Vec<StrykeValue> = Vec::with_capacity(args.len());
10405 for a in args.into_iter() {
10406 if let Some(items) = a.as_array_vec() {
10407 flat.extend(items);
10408 } else {
10409 flat.push(a);
10410 }
10411 }
10412 let args = flat;
10413 let (fmt, rest): (String, &[StrykeValue]) = if args.is_empty() {
10414 let s = match self
10415 .interp
10416 .stringify_value(self.interp.scope.get_scalar("_").clone(), line)
10417 {
10418 Ok(s) => s,
10419 Err(FlowOrError::Error(e)) => return Err(e),
10420 Err(FlowOrError::Flow(_)) => {
10421 return Err(StrykeError::runtime(
10422 "printf: unexpected control flow",
10423 line,
10424 ));
10425 }
10426 };
10427 (s, &[])
10428 } else {
10429 (args[0].to_string(), &args[1..])
10430 };
10431 let out = match self.interp.perl_sprintf_stringify(&fmt, rest, line) {
10432 Ok(s) => s,
10433 Err(FlowOrError::Error(e)) => return Err(e),
10434 Err(FlowOrError::Flow(_)) => {
10435 return Err(StrykeError::runtime(
10436 "printf: unexpected control flow",
10437 line,
10438 ));
10439 }
10440 };
10441 print!("{}", out);
10442 if self.interp.output_autoflush {
10443 let _ = io::stdout().flush();
10444 }
10445 Ok(StrykeValue::integer(1))
10446 }
10447 Some(BuiltinId::Open) => {
10448 if args.len() < 2 {
10449 return Err(StrykeError::runtime(
10450 "open requires at least 2 arguments",
10451 line,
10452 ));
10453 }
10454 let handle_name = args[0].to_string();
10455 let mode_s = args[1].to_string();
10456 let file_opt = args.get(2).map(|v| v.to_string());
10457 self.interp
10458 .open_builtin_execute(handle_name, mode_s, file_opt, line)
10459 }
10460 Some(BuiltinId::Close) => {
10461 let name = args
10462 .into_iter()
10463 .next()
10464 .unwrap_or(StrykeValue::UNDEF)
10465 .to_string();
10466 self.interp.close_builtin_execute(name)
10467 }
10468 Some(BuiltinId::Eof) => self.interp.eof_builtin_execute(&args, line),
10469 Some(BuiltinId::ReadLine) => {
10470 let h = if args.is_empty() {
10471 None
10472 } else {
10473 Some(args[0].to_string())
10474 };
10475 self.interp.readline_builtin_execute(h.as_deref())
10476 }
10477 Some(BuiltinId::ReadLineList) => {
10478 let h = if args.is_empty() {
10479 None
10480 } else {
10481 Some(args[0].to_string())
10482 };
10483 self.interp.readline_builtin_execute_list(h.as_deref())
10484 }
10485 Some(BuiltinId::Exec) => {
10486 let cmd = args
10487 .iter()
10488 .map(|a| a.to_string())
10489 .collect::<Vec<_>>()
10490 .join(" ");
10491 let status = std::process::Command::new("sh")
10492 .arg("-c")
10493 .arg(&cmd)
10494 .status();
10495 std::process::exit(status.map(|s| s.code().unwrap_or(-1)).unwrap_or(-1));
10496 }
10497 Some(BuiltinId::Chdir) => {
10498 let path = args
10499 .into_iter()
10500 .next()
10501 .unwrap_or(StrykeValue::UNDEF)
10502 .to_string();
10503 if std::env::set_current_dir(&path).is_ok() {
10504 if let Ok(c) = std::env::current_dir() {
10505 self.interp.stryke_pwd = std::fs::canonicalize(&c).unwrap_or(c);
10506 }
10507 Ok(StrykeValue::integer(1))
10508 } else {
10509 Ok(StrykeValue::integer(0))
10510 }
10511 }
10512 Some(BuiltinId::Mkdir) => {
10513 let path = args.first().map(|v| v.to_string()).unwrap_or_default();
10514 let path = self.interp.resolve_stryke_path_string(&path);
10515 Ok(StrykeValue::integer(
10516 if std::fs::create_dir(&path).is_ok() {
10517 1
10518 } else {
10519 0
10520 },
10521 ))
10522 }
10523 Some(BuiltinId::Unlink) => {
10524 let mut count = 0i64;
10525 for a in &args {
10526 let p = self.interp.resolve_stryke_path_string(&a.to_string());
10527 if std::fs::remove_file(&p).is_ok() {
10528 count += 1;
10529 }
10530 }
10531 Ok(StrykeValue::integer(count))
10532 }
10533 Some(BuiltinId::Rmdir) => self.interp.builtin_rmdir_execute(&args, line),
10534 Some(BuiltinId::Utime) => self.interp.builtin_utime_execute(&args, line),
10535 Some(BuiltinId::Umask) => self.interp.builtin_umask_execute(&args, line),
10536 Some(BuiltinId::Getcwd) => self.interp.builtin_getcwd_execute(&args, line),
10537 Some(BuiltinId::Pipe) => self.interp.builtin_pipe_execute(&args, line),
10538 Some(BuiltinId::Rename) => {
10539 let old = self.interp.resolve_stryke_path_string(
10540 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10541 );
10542 let new = self.interp.resolve_stryke_path_string(
10543 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10544 );
10545 Ok(crate::perl_fs::rename_paths(&old, &new))
10546 }
10547 Some(BuiltinId::Chmod) => {
10548 if args.is_empty() {
10549 return Ok(StrykeValue::integer(0));
10550 }
10551 let mode = args[0].to_int();
10552 let paths: Vec<String> = args
10553 .iter()
10554 .skip(1)
10555 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10556 .collect();
10557 Ok(StrykeValue::integer(crate::perl_fs::chmod_paths(
10558 &paths, mode,
10559 )))
10560 }
10561 Some(BuiltinId::Chown) => {
10562 if args.len() < 3 {
10563 return Ok(StrykeValue::integer(0));
10564 }
10565 let uid = args[0].to_int();
10566 let gid = args[1].to_int();
10567 let paths: Vec<String> = args
10568 .iter()
10569 .skip(2)
10570 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10571 .collect();
10572 Ok(StrykeValue::integer(crate::perl_fs::chown_paths(
10573 &paths, uid, gid,
10574 )))
10575 }
10576 Some(BuiltinId::Stat) => {
10577 let path = self.interp.resolve_stryke_path_string(
10578 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10579 );
10580 Ok(crate::perl_fs::stat_path(&path, false))
10581 }
10582 Some(BuiltinId::Lstat) => {
10583 let path = self.interp.resolve_stryke_path_string(
10584 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10585 );
10586 Ok(crate::perl_fs::stat_path(&path, true))
10587 }
10588 Some(BuiltinId::Link) => {
10589 let old = self.interp.resolve_stryke_path_string(
10590 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10591 );
10592 let new = self.interp.resolve_stryke_path_string(
10593 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10594 );
10595 Ok(crate::perl_fs::link_hard(&old, &new))
10596 }
10597 Some(BuiltinId::Symlink) => {
10598 let old = args.first().map(|v| v.to_string()).unwrap_or_default();
10599 let new = self.interp.resolve_stryke_path_string(
10600 &args.get(1).map(|v| v.to_string()).unwrap_or_default(),
10601 );
10602 Ok(crate::perl_fs::link_sym(&old, &new))
10603 }
10604 Some(BuiltinId::Readlink) => {
10605 let path = self.interp.resolve_stryke_path_string(
10606 &args.first().map(|v| v.to_string()).unwrap_or_default(),
10607 );
10608 Ok(crate::perl_fs::read_link(&path))
10609 }
10610 Some(BuiltinId::Glob) => {
10611 let pats: Vec<String> = args.iter().map(|v| v.to_string()).collect();
10617 Ok(crate::perl_fs::glob_patterns(&pats))
10618 }
10619 Some(BuiltinId::Files) => {
10620 let dir = if args.is_empty() {
10621 self.interp.resolve_stryke_path_string(".")
10622 } else {
10623 self.interp.resolve_stryke_path_string(&args[0].to_string())
10624 };
10625 Ok(crate::perl_fs::list_files(&dir))
10626 }
10627 Some(BuiltinId::Filesf) => {
10628 let dir = if args.is_empty() {
10629 self.interp.resolve_stryke_path_string(".")
10630 } else {
10631 self.interp.resolve_stryke_path_string(&args[0].to_string())
10632 };
10633 Ok(crate::perl_fs::list_filesf(&dir))
10634 }
10635 Some(BuiltinId::FilesfRecursive) => {
10636 let dir = if args.is_empty() {
10637 self.interp.resolve_stryke_path_string(".")
10638 } else {
10639 self.interp.resolve_stryke_path_string(&args[0].to_string())
10640 };
10641 Ok(StrykeValue::iterator(std::sync::Arc::new(
10642 crate::value::FsWalkIterator::new(&dir, true),
10643 )))
10644 }
10645 Some(BuiltinId::Dirs) => {
10646 let dir = if args.is_empty() {
10647 self.interp.resolve_stryke_path_string(".")
10648 } else {
10649 self.interp.resolve_stryke_path_string(&args[0].to_string())
10650 };
10651 Ok(crate::perl_fs::list_dirs(&dir))
10652 }
10653 Some(BuiltinId::DirsRecursive) => {
10654 let dir = if args.is_empty() {
10655 self.interp.resolve_stryke_path_string(".")
10656 } else {
10657 self.interp.resolve_stryke_path_string(&args[0].to_string())
10658 };
10659 Ok(StrykeValue::iterator(std::sync::Arc::new(
10660 crate::value::FsWalkIterator::new(&dir, false),
10661 )))
10662 }
10663 Some(BuiltinId::SymLinks) => {
10664 let dir = if args.is_empty() {
10665 self.interp.resolve_stryke_path_string(".")
10666 } else {
10667 self.interp.resolve_stryke_path_string(&args[0].to_string())
10668 };
10669 Ok(crate::perl_fs::list_sym_links(&dir))
10670 }
10671 Some(BuiltinId::Sockets) => {
10672 let dir = if args.is_empty() {
10673 self.interp.resolve_stryke_path_string(".")
10674 } else {
10675 self.interp.resolve_stryke_path_string(&args[0].to_string())
10676 };
10677 Ok(crate::perl_fs::list_sockets(&dir))
10678 }
10679 Some(BuiltinId::Pipes) => {
10680 let dir = if args.is_empty() {
10681 self.interp.resolve_stryke_path_string(".")
10682 } else {
10683 self.interp.resolve_stryke_path_string(&args[0].to_string())
10684 };
10685 Ok(crate::perl_fs::list_pipes(&dir))
10686 }
10687 Some(BuiltinId::BlockDevices) => {
10688 let dir = if args.is_empty() {
10689 self.interp.resolve_stryke_path_string(".")
10690 } else {
10691 self.interp.resolve_stryke_path_string(&args[0].to_string())
10692 };
10693 Ok(crate::perl_fs::list_block_devices(&dir))
10694 }
10695 Some(BuiltinId::CharDevices) => {
10696 let dir = if args.is_empty() {
10697 self.interp.resolve_stryke_path_string(".")
10698 } else {
10699 self.interp.resolve_stryke_path_string(&args[0].to_string())
10700 };
10701 Ok(crate::perl_fs::list_char_devices(&dir))
10702 }
10703 Some(BuiltinId::Executables) => {
10704 let dir = if args.is_empty() {
10705 self.interp.resolve_stryke_path_string(".")
10706 } else {
10707 self.interp.resolve_stryke_path_string(&args[0].to_string())
10708 };
10709 Ok(crate::perl_fs::list_executables(&dir))
10710 }
10711 Some(BuiltinId::GlobPar) => {
10712 let pats: Vec<String> = args
10713 .iter()
10714 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10715 .collect();
10716 Ok(crate::perl_fs::glob_par_patterns(&pats))
10717 }
10718 Some(BuiltinId::GlobParProgress) => {
10719 let progress = args.last().map(|v| v.is_true()).unwrap_or(false);
10720 let pats: Vec<String> = args[..args.len().saturating_sub(1)]
10721 .iter()
10722 .map(|v| self.interp.resolve_stryke_path_string(&v.to_string()))
10723 .collect();
10724 Ok(crate::perl_fs::glob_par_patterns_with_progress(
10725 &pats, progress,
10726 ))
10727 }
10728 Some(BuiltinId::ParSed) => self.interp.builtin_par_sed(&args, line, false),
10729 Some(BuiltinId::ParSedProgress) => self.interp.builtin_par_sed(&args, line, true),
10730 Some(BuiltinId::Opendir) => {
10731 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10732 let path = args.get(1).map(|v| v.to_string()).unwrap_or_default();
10733 Ok(self.interp.opendir_handle(&handle, &path))
10734 }
10735 Some(BuiltinId::Readdir) => {
10736 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10737 Ok(self.interp.readdir_handle(&handle))
10738 }
10739 Some(BuiltinId::ReaddirList) => {
10740 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10741 Ok(self.interp.readdir_handle_list(&handle))
10742 }
10743 Some(BuiltinId::Closedir) => {
10744 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10745 Ok(self.interp.closedir_handle(&handle))
10746 }
10747 Some(BuiltinId::Rewinddir) => {
10748 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10749 Ok(self.interp.rewinddir_handle(&handle))
10750 }
10751 Some(BuiltinId::Telldir) => {
10752 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10753 Ok(self.interp.telldir_handle(&handle))
10754 }
10755 Some(BuiltinId::Seekdir) => {
10756 let handle = args.first().map(|v| v.to_string()).unwrap_or_default();
10757 let pos = args.get(1).map(|v| v.to_int().max(0) as usize).unwrap_or(0);
10758 Ok(self.interp.seekdir_handle(&handle, pos))
10759 }
10760 Some(BuiltinId::Slurp) => {
10761 let path = args
10762 .into_iter()
10763 .next()
10764 .unwrap_or(StrykeValue::UNDEF)
10765 .to_string();
10766 let path = self.interp.resolve_stryke_path_string(&path);
10767 crate::perl_fs::read_bytes_or_glob(&path)
10768 .map(StrykeValue::bytes)
10769 .map_err(|e| StrykeError::runtime(format!("slurp: {}", e), line))
10770 }
10771 Some(BuiltinId::Swallow) => {
10772 let path = args
10773 .into_iter()
10774 .next()
10775 .unwrap_or(StrykeValue::UNDEF)
10776 .to_string();
10777 let path = self.interp.resolve_stryke_path_string(&path);
10778 crate::perl_fs::swallow_to_hash(&path)
10779 .map_err(|e| StrykeError::runtime(format!("swallow: {}", e), line))
10780 }
10781 Some(BuiltinId::Ingest) => {
10782 let path = args
10783 .into_iter()
10784 .next()
10785 .unwrap_or(StrykeValue::UNDEF)
10786 .to_string();
10787 let path = self.interp.resolve_stryke_path_string(&path);
10788 crate::perl_fs::ingest_iterator(&path)
10789 .map_err(|e| StrykeError::runtime(format!("ingest: {}", e), line))
10790 }
10791 Some(BuiltinId::Burp) => {
10792 let v = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10793 crate::perl_fs::burp_hash_to_disk(&v)
10794 .map(StrykeValue::integer)
10795 .map_err(|e| StrykeError::runtime(format!("burp: {}", e), line))
10796 }
10797 Some(BuiltinId::God) => {
10798 let v = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10799 Ok(StrykeValue::string(crate::god::god_dump(&v)))
10800 }
10801 Some(BuiltinId::Capture) => {
10802 let cmd = args
10803 .into_iter()
10804 .next()
10805 .unwrap_or(StrykeValue::UNDEF)
10806 .to_string();
10807 crate::capture::run_capture(self.interp, &cmd, line)
10808 }
10809 Some(BuiltinId::Ppool) => {
10810 let n = args
10811 .first()
10812 .map(|v| v.to_int().max(0) as usize)
10813 .unwrap_or(1);
10814 crate::ppool::create_pool(n)
10815 }
10816 Some(BuiltinId::Wantarray) => Ok(match self.interp.wantarray_kind {
10817 crate::vm_helper::WantarrayCtx::Void => StrykeValue::UNDEF,
10818 crate::vm_helper::WantarrayCtx::Scalar => StrykeValue::integer(0),
10819 crate::vm_helper::WantarrayCtx::List => StrykeValue::integer(1),
10820 }),
10821 Some(BuiltinId::FetchUrl) => {
10822 let url = args
10823 .into_iter()
10824 .next()
10825 .unwrap_or(StrykeValue::UNDEF)
10826 .to_string();
10827 ureq::get(&url)
10828 .call()
10829 .map_err(|e| StrykeError::runtime(format!("fetch_url: {}", e), line))
10830 .and_then(|r| {
10831 r.into_string()
10832 .map(StrykeValue::string)
10833 .map_err(|e| StrykeError::runtime(format!("fetch_url: {}", e), line))
10834 })
10835 }
10836 Some(BuiltinId::Pchannel) => {
10837 if args.is_empty() {
10838 Ok(crate::pchannel::create_pair())
10839 } else if args.len() == 1 {
10840 let n = args[0].to_int().max(1) as usize;
10841 Ok(crate::pchannel::create_bounded_pair(n))
10842 } else {
10843 Err(StrykeError::runtime(
10844 "pchannel() takes 0 or 1 arguments (capacity)",
10845 line,
10846 ))
10847 }
10848 }
10849 Some(BuiltinId::Pselect) => crate::pchannel::pselect_recv(&args, line),
10850 Some(BuiltinId::DequeNew) => {
10851 if !args.is_empty() {
10852 return Err(StrykeError::runtime("deque() takes no arguments", line));
10853 }
10854 Ok(StrykeValue::deque(Arc::new(Mutex::new(VecDeque::new()))))
10855 }
10856 Some(BuiltinId::HeapNew) => {
10857 if args.len() != 1 {
10858 return Err(StrykeError::runtime(
10859 "heap() expects one comparator sub",
10860 line,
10861 ));
10862 }
10863 let a0 = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10864 if let Some(sub) = a0.as_code_ref() {
10865 Ok(StrykeValue::heap(Arc::new(Mutex::new(PerlHeap {
10866 items: Vec::new(),
10867 cmp: Arc::clone(&sub),
10868 }))))
10869 } else {
10870 Err(StrykeError::runtime(
10871 "heap() requires a code reference",
10872 line,
10873 ))
10874 }
10875 }
10876 Some(BuiltinId::BarrierNew) => {
10877 let n = args
10878 .first()
10879 .map(|v| v.to_int().max(1) as usize)
10880 .unwrap_or(1);
10881 Ok(StrykeValue::barrier(PerlBarrier(Arc::new(Barrier::new(n)))))
10882 }
10883 Some(BuiltinId::ClusterNew) => {
10884 let items = if args.len() == 1 {
10890 args[0].to_list()
10891 } else {
10892 args.clone()
10893 };
10894 let c = crate::value::RemoteCluster::from_list_args(&items)
10895 .map_err(|msg| StrykeError::runtime(msg, line))?;
10896 Ok(StrykeValue::remote_cluster(std::sync::Arc::new(c)))
10897 }
10898 Some(BuiltinId::Pipeline) => {
10899 let mut items = Vec::new();
10900 for v in args {
10901 if let Some(a) = v.as_array_vec() {
10902 items.extend(a);
10903 } else {
10904 items.push(v);
10905 }
10906 }
10907 Ok(StrykeValue::pipeline(Arc::new(Mutex::new(PipelineInner {
10908 source: items,
10909 ops: Vec::new(),
10910 has_scalar_terminal: false,
10911 par_stream: false,
10912 streaming: false,
10913 streaming_workers: 0,
10914 streaming_buffer: 256,
10915 }))))
10916 }
10917 Some(BuiltinId::ParPipeline) => {
10918 if crate::par_pipeline::is_named_par_pipeline_args(&args) {
10919 return crate::par_pipeline::run_par_pipeline(self.interp, &args, line);
10920 }
10921 let mut items = Vec::new();
10922 for v in args {
10923 if let Some(a) = v.as_array_vec() {
10924 items.extend(a);
10925 } else {
10926 items.push(v);
10927 }
10928 }
10929 Ok(StrykeValue::pipeline(Arc::new(Mutex::new(PipelineInner {
10930 source: items,
10931 ops: Vec::new(),
10932 has_scalar_terminal: false,
10933 par_stream: true,
10934 streaming: false,
10935 streaming_workers: 0,
10936 streaming_buffer: 256,
10937 }))))
10938 }
10939 Some(BuiltinId::ParPipelineStream) => {
10940 if crate::par_pipeline::is_named_par_pipeline_args(&args) {
10941 return crate::par_pipeline::run_par_pipeline_streaming(
10942 self.interp,
10943 &args,
10944 line,
10945 );
10946 }
10947 self.interp.builtin_par_pipeline_stream_new(&args, line)
10948 }
10949 Some(BuiltinId::Each) => {
10950 let _arg = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10951 Ok(StrykeValue::array(vec![]))
10952 }
10953 Some(BuiltinId::Readpipe) => {
10954 let cmd = args
10955 .into_iter()
10956 .next()
10957 .unwrap_or(StrykeValue::UNDEF)
10958 .to_string();
10959 crate::capture::run_readpipe(self.interp, &cmd, line)
10960 }
10961 Some(BuiltinId::ReadpipeList) => {
10962 let cmd = args
10963 .into_iter()
10964 .next()
10965 .unwrap_or(StrykeValue::UNDEF)
10966 .to_string();
10967 let v = crate::capture::run_readpipe(self.interp, &cmd, line)?;
10968 let s = v.to_string();
10969 if s.is_empty() {
10970 return Ok(StrykeValue::array(Vec::new()));
10971 }
10972 let mut lines = Vec::new();
10973 let mut buf = String::new();
10974 for c in s.chars() {
10975 buf.push(c);
10976 if c == '\n' {
10977 lines.push(StrykeValue::string(std::mem::take(&mut buf)));
10978 }
10979 }
10980 if !buf.is_empty() {
10981 lines.push(StrykeValue::string(buf));
10982 }
10983 Ok(StrykeValue::array(lines))
10984 }
10985 Some(BuiltinId::Eval) => {
10986 let arg = args.into_iter().next().unwrap_or(StrykeValue::UNDEF);
10987 self.interp.eval_nesting += 1;
10988 let out = if let Some(sub) = arg.as_code_ref() {
10989 match self.interp.exec_block(&sub.body) {
10990 Ok(v) => {
10991 self.interp.clear_eval_error();
10992 Ok(v)
10993 }
10994 Err(crate::vm_helper::FlowOrError::Error(e)) => {
10995 self.interp.set_eval_error_from_perl_error(&e);
10996 Ok(StrykeValue::UNDEF)
10997 }
10998 Err(crate::vm_helper::FlowOrError::Flow(_)) => {
10999 self.interp.clear_eval_error();
11000 Ok(StrykeValue::UNDEF)
11001 }
11002 }
11003 } else {
11004 let code = arg.to_string();
11005 match crate::parse_and_run_string(&code, self.interp) {
11006 Ok(v) => {
11007 self.interp.clear_eval_error();
11008 Ok(v)
11009 }
11010 Err(e) => {
11011 self.interp.set_eval_error_from_perl_error(&e);
11012 Ok(StrykeValue::UNDEF)
11013 }
11014 }
11015 };
11016 self.interp.eval_nesting -= 1;
11017 out
11018 }
11019 Some(BuiltinId::Do) => {
11020 let filename = args
11021 .into_iter()
11022 .next()
11023 .unwrap_or(StrykeValue::UNDEF)
11024 .to_string();
11025 match read_file_text_perl_compat(&filename) {
11026 Ok(code) => {
11027 let code = crate::data_section::strip_perl_end_marker(&code);
11028 crate::parse_and_run_string_in_file(code, self.interp, &filename)
11029 .or(Ok(StrykeValue::UNDEF))
11030 }
11031 Err(_) => Ok(StrykeValue::UNDEF),
11032 }
11033 }
11034 Some(BuiltinId::Require) => {
11035 let name = args
11036 .into_iter()
11037 .next()
11038 .unwrap_or(StrykeValue::UNDEF)
11039 .to_string();
11040 self.interp.require_execute(&name, line)
11041 }
11042 Some(BuiltinId::Bless) => {
11043 let ref_val = args.first().cloned().unwrap_or(StrykeValue::UNDEF);
11044 let class = args
11045 .get(1)
11046 .map(|v| v.to_string())
11047 .unwrap_or_else(|| self.interp.scope.get_scalar("__PACKAGE__").to_string());
11048 Ok(StrykeValue::blessed(Arc::new(
11049 crate::value::BlessedRef::new_blessed(class, ref_val),
11050 )))
11051 }
11052 Some(BuiltinId::Caller) => {
11053 let sub_name = self
11057 .interp
11058 .current_sub_stack
11059 .last()
11060 .map(|s| StrykeValue::string(s.name.clone()))
11061 .unwrap_or(StrykeValue::UNDEF);
11062 let pkg = self.interp.current_package();
11063 Ok(StrykeValue::array(vec![
11064 StrykeValue::string(pkg),
11065 StrykeValue::string(self.interp.file.clone()),
11066 StrykeValue::integer(line as i64),
11067 sub_name,
11068 ]))
11069 }
11070 Some(BuiltinId::PMap)
11072 | Some(BuiltinId::PGrep)
11073 | Some(BuiltinId::PFor)
11074 | Some(BuiltinId::PSort)
11075 | Some(BuiltinId::Fan)
11076 | Some(BuiltinId::MapBlock)
11077 | Some(BuiltinId::GrepBlock)
11078 | Some(BuiltinId::SortBlock)
11079 | Some(BuiltinId::Sort) => Ok(StrykeValue::UNDEF),
11080 _ => Err(StrykeError::runtime(
11081 format!("Unimplemented builtin {:?}", bid),
11082 line,
11083 )),
11084 }
11085 }
11086}
11087
11088#[inline]
11090fn both_non_numeric_strings(a: &StrykeValue, b: &StrykeValue) -> bool {
11096 if !a.is_string_like() || !b.is_string_like() {
11097 return false;
11098 }
11099 let sa = a.to_string();
11100 let sb = b.to_string();
11101 !looks_numeric(&sa) && !looks_numeric(&sb)
11102}
11103
11104#[inline]
11105fn looks_numeric(s: &str) -> bool {
11106 let t = s.trim();
11107 if t.is_empty() {
11108 return false;
11109 }
11110 t.parse::<f64>().is_ok()
11111}
11112
11113fn int_cmp(
11114 a: &StrykeValue,
11115 b: &StrykeValue,
11116 int_op: fn(&i64, &i64) -> bool,
11117 float_op: fn(f64, f64) -> bool,
11118) -> StrykeValue {
11119 if let (Some(x), Some(y)) = (a.as_integer(), b.as_integer()) {
11120 StrykeValue::integer(if int_op(&x, &y) { 1 } else { 0 })
11121 } else {
11122 StrykeValue::integer(if float_op(a.to_number(), b.to_number()) {
11123 1
11124 } else {
11125 0
11126 })
11127 }
11128}
11129
11130#[no_mangle]
11136pub unsafe extern "C" fn stryke_jit_concat_vm(vm: *mut std::ffi::c_void, a: i64, b: i64) -> i64 {
11137 let vm: &mut VM<'static> = unsafe { &mut *(vm as *mut VM<'static>) };
11138 let pa = StrykeValue::from_raw_bits(crate::jit::perl_value_bits_from_jit_string_operand(a));
11139 let pb = StrykeValue::from_raw_bits(crate::jit::perl_value_bits_from_jit_string_operand(b));
11140 match vm.concat_stack_values(pa, pb) {
11141 Ok(pv) => pv.raw_bits() as i64,
11142 Err(_) => StrykeValue::UNDEF.raw_bits() as i64,
11143 }
11144}
11145
11146#[no_mangle]
11154pub unsafe extern "C" fn stryke_jit_call_sub(
11155 vm: *mut std::ffi::c_void,
11156 sub_ip: i64,
11157 argc: i64,
11158 wa: i64,
11159 a0: i64,
11160 a1: i64,
11161 a2: i64,
11162 a3: i64,
11163 a4: i64,
11164 a5: i64,
11165 a6: i64,
11166 a7: i64,
11167) -> i64 {
11168 let vm: &mut VM<'static> = unsafe { &mut *(vm as *mut VM<'static>) };
11169 let want = WantarrayCtx::from_byte(wa as u8);
11170 if want != WantarrayCtx::Scalar {
11171 return StrykeValue::UNDEF.raw_bits() as i64;
11172 }
11173 let argc = argc.clamp(0, 8) as usize;
11174 let args = [a0, a1, a2, a3, a4, a5, a6, a7];
11175 let args = &args[..argc];
11176 match vm.jit_trampoline_run_sub(sub_ip as usize, want, args) {
11177 Ok(pv) => {
11178 if let Some(n) = pv.as_integer() {
11179 n
11180 } else {
11181 pv.raw_bits() as i64
11182 }
11183 }
11184 Err(_) => StrykeValue::UNDEF.raw_bits() as i64,
11185 }
11186}
11187
11188#[cfg(test)]
11189mod tests {
11190 use super::*;
11191 use crate::bytecode::{Chunk, Op};
11192 use crate::value::StrykeValue;
11193
11194 fn run_chunk(chunk: &Chunk) -> StrykeResult<StrykeValue> {
11195 let mut interp = VMHelper::new();
11196 let mut vm = VM::new(chunk, &mut interp);
11197 vm.execute()
11198 }
11199
11200 fn block_jit_sum_chunk(limit: i64) -> Chunk {
11202 let mut c = Chunk::new();
11203 let ni = c.intern_name("i");
11204 let ns = c.intern_name("sum");
11205 c.emit(Op::LoadInt(0), 1);
11206 c.emit(Op::DeclareScalarSlot(0, ni), 1);
11207 c.emit(Op::LoadInt(0), 1);
11208 c.emit(Op::DeclareScalarSlot(1, ns), 1);
11209 c.emit(Op::GetScalarSlot(0), 1);
11210 c.emit(Op::LoadInt(limit), 1);
11211 c.emit(Op::NumLt, 1);
11212 c.emit(Op::JumpIfFalse(15), 1);
11213 c.emit(Op::GetScalarSlot(1), 1);
11214 c.emit(Op::GetScalarSlot(0), 1);
11215 c.emit(Op::Add, 1);
11216 c.emit(Op::SetScalarSlot(1), 1);
11217 c.emit(Op::PostIncSlot(0), 1);
11218 c.emit(Op::Pop, 1);
11219 c.emit(Op::Jump(4), 1);
11220 c.emit(Op::GetScalarSlot(1), 1);
11221 c.emit(Op::Halt, 1);
11222 c
11223 }
11224
11225 #[test]
11226 fn jit_disabled_same_result_as_jit_block_loop() {
11227 let limit = 500i64;
11228 let chunk = block_jit_sum_chunk(limit);
11229 let expect = limit * (limit - 1) / 2;
11230
11231 let mut interp_on = VMHelper::new();
11232 let mut vm_on = VM::new(&chunk, &mut interp_on);
11233 assert_eq!(vm_on.execute().expect("vm").to_int(), expect);
11234
11235 let mut interp_off = VMHelper::new();
11236 let mut vm_off = VM::new(&chunk, &mut interp_off);
11237 vm_off.set_jit_enabled(false);
11238 assert_eq!(vm_off.execute().expect("vm").to_int(), expect);
11239 }
11240
11241 #[test]
11242 fn vm_add_two_integers() {
11243 let mut c = Chunk::new();
11244 c.emit(Op::LoadInt(2), 1);
11245 c.emit(Op::LoadInt(3), 1);
11246 c.emit(Op::Add, 1);
11247 c.emit(Op::Halt, 1);
11248 let v = run_chunk(&c).expect("vm");
11249 assert_eq!(v.to_int(), 5);
11250 }
11251
11252 #[test]
11253 fn vm_sub_mul_div() {
11254 let mut c = Chunk::new();
11255 c.emit(Op::LoadInt(10), 1);
11256 c.emit(Op::LoadInt(3), 1);
11257 c.emit(Op::Sub, 1);
11258 c.emit(Op::Halt, 1);
11259 assert_eq!(run_chunk(&c).expect("vm").to_int(), 7);
11260
11261 let mut c = Chunk::new();
11262 c.emit(Op::LoadInt(6), 1);
11263 c.emit(Op::LoadInt(7), 1);
11264 c.emit(Op::Mul, 1);
11265 c.emit(Op::Halt, 1);
11266 assert_eq!(run_chunk(&c).expect("vm").to_int(), 42);
11267
11268 let mut c = Chunk::new();
11269 c.emit(Op::LoadInt(20), 1);
11270 c.emit(Op::LoadInt(4), 1);
11271 c.emit(Op::Div, 1);
11272 c.emit(Op::Halt, 1);
11273 assert_eq!(run_chunk(&c).expect("vm").to_int(), 5);
11274 }
11275
11276 #[test]
11277 fn vm_mod_and_pow() {
11278 let mut c = Chunk::new();
11279 c.emit(Op::LoadInt(17), 1);
11280 c.emit(Op::LoadInt(5), 1);
11281 c.emit(Op::Mod, 1);
11282 c.emit(Op::Halt, 1);
11283 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11284
11285 let mut c = Chunk::new();
11286 c.emit(Op::LoadInt(2), 1);
11287 c.emit(Op::LoadInt(3), 1);
11288 c.emit(Op::Pow, 1);
11289 c.emit(Op::Halt, 1);
11290 assert_eq!(run_chunk(&c).expect("vm").to_int(), 8);
11291 }
11292
11293 #[test]
11294 fn vm_negate() {
11295 let mut c = Chunk::new();
11296 c.emit(Op::LoadInt(7), 1);
11297 c.emit(Op::Negate, 1);
11298 c.emit(Op::Halt, 1);
11299 assert_eq!(run_chunk(&c).expect("vm").to_int(), -7);
11300 }
11301
11302 #[test]
11303 fn vm_dup_and_pop() {
11304 let mut c = Chunk::new();
11305 c.emit(Op::LoadInt(1), 1);
11306 c.emit(Op::Dup, 1);
11307 c.emit(Op::Add, 1);
11308 c.emit(Op::Halt, 1);
11309 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11310
11311 let mut c = Chunk::new();
11312 c.emit(Op::LoadInt(1), 1);
11313 c.emit(Op::LoadInt(2), 1);
11314 c.emit(Op::Pop, 1);
11315 c.emit(Op::Halt, 1);
11316 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11317 }
11318
11319 #[test]
11320 fn vm_set_get_scalar() {
11321 let mut c = Chunk::new();
11322 let i = c.intern_name("v");
11323 c.emit(Op::LoadInt(99), 1);
11324 c.emit(Op::SetScalar(i), 1);
11325 c.emit(Op::GetScalar(i), 1);
11326 c.emit(Op::Halt, 1);
11327 assert_eq!(run_chunk(&c).expect("vm").to_int(), 99);
11328 }
11329
11330 #[test]
11331 fn vm_scalar_plain_roundtrip_and_keep() {
11332 let mut c = Chunk::new();
11333 let i = c.intern_name("plainvar");
11334 c.emit(Op::LoadInt(99), 1);
11335 c.emit(Op::SetScalarPlain(i), 1);
11336 c.emit(Op::GetScalarPlain(i), 1);
11337 c.emit(Op::Halt, 1);
11338 assert_eq!(run_chunk(&c).expect("vm").to_int(), 99);
11339
11340 let mut c = Chunk::new();
11341 let k = c.intern_name("keepme");
11342 c.emit(Op::LoadInt(5), 1);
11343 c.emit(Op::SetScalarKeepPlain(k), 1);
11344 c.emit(Op::Halt, 1);
11345 assert_eq!(run_chunk(&c).expect("vm").to_int(), 5);
11346 }
11347
11348 #[test]
11349 fn vm_get_scalar_plain_skips_special_global_zero() {
11350 let mut c = Chunk::new();
11351 let idx = c.intern_name("0");
11352 c.emit(Op::GetScalar(idx), 1);
11353 c.emit(Op::Halt, 1);
11354 assert_eq!(run_chunk(&c).expect("vm").to_string(), "stryke");
11355
11356 let mut c = Chunk::new();
11357 let idx = c.intern_name("0");
11358 c.emit(Op::GetScalarPlain(idx), 1);
11359 c.emit(Op::Halt, 1);
11360 assert!(run_chunk(&c).expect("vm").is_undef());
11361 }
11362
11363 #[test]
11364 fn vm_slot_pre_post_inc_dec() {
11365 let mut c = Chunk::new();
11366 c.emit(Op::LoadInt(10), 1);
11367 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11368 c.emit(Op::PostIncSlot(0), 1);
11369 c.emit(Op::Pop, 1);
11370 c.emit(Op::GetScalarSlot(0), 1);
11371 c.emit(Op::Halt, 1);
11372 assert_eq!(run_chunk(&c).expect("vm").to_int(), 11);
11373
11374 let mut c = Chunk::new();
11375 c.emit(Op::LoadInt(0), 1);
11376 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11377 c.emit(Op::PreIncSlot(0), 1);
11378 c.emit(Op::Halt, 1);
11379 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11380
11381 let mut c = Chunk::new();
11382 c.emit(Op::LoadInt(5), 1);
11383 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11384 c.emit(Op::PreDecSlot(0), 1);
11385 c.emit(Op::Halt, 1);
11386 assert_eq!(run_chunk(&c).expect("vm").to_int(), 4);
11387
11388 let mut c = Chunk::new();
11389 c.emit(Op::LoadInt(3), 1);
11390 c.emit(Op::DeclareScalarSlot(0, u16::MAX), 1);
11391 c.emit(Op::PostDecSlot(0), 1);
11392 c.emit(Op::Pop, 1);
11393 c.emit(Op::GetScalarSlot(0), 1);
11394 c.emit(Op::Halt, 1);
11395 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11396 }
11397
11398 #[test]
11399 fn vm_str_eq_ne_heap_strings() {
11400 let mut c = Chunk::new();
11401 let a = c.add_constant(StrykeValue::string("same".into()));
11402 let b = c.add_constant(StrykeValue::string("same".into()));
11403 c.emit(Op::LoadConst(a), 1);
11404 c.emit(Op::LoadConst(b), 1);
11405 c.emit(Op::StrEq, 1);
11406 c.emit(Op::Halt, 1);
11407 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11408
11409 let mut c = Chunk::new();
11410 let a = c.add_constant(StrykeValue::string("a".into()));
11411 let b = c.add_constant(StrykeValue::string("b".into()));
11412 c.emit(Op::LoadConst(a), 1);
11413 c.emit(Op::LoadConst(b), 1);
11414 c.emit(Op::StrNe, 1);
11415 c.emit(Op::Halt, 1);
11416 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11417 }
11418
11419 #[test]
11420 fn vm_num_eq_ine() {
11421 let mut c = Chunk::new();
11422 c.emit(Op::LoadInt(1), 1);
11423 c.emit(Op::LoadInt(1), 1);
11424 c.emit(Op::NumEq, 1);
11425 c.emit(Op::Halt, 1);
11426 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11427
11428 let mut c = Chunk::new();
11429 c.emit(Op::LoadInt(1), 1);
11430 c.emit(Op::LoadInt(2), 1);
11431 c.emit(Op::NumNe, 1);
11432 c.emit(Op::Halt, 1);
11433 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11434 }
11435
11436 #[test]
11437 fn vm_num_ordering() {
11438 for (a, b, op, want) in [
11439 (1i64, 2i64, Op::NumLt, 1),
11440 (3i64, 2i64, Op::NumGt, 1),
11441 (2i64, 2i64, Op::NumLe, 1),
11442 (2i64, 2i64, Op::NumGe, 1),
11443 ] {
11444 let mut c = Chunk::new();
11445 c.emit(Op::LoadInt(a), 1);
11446 c.emit(Op::LoadInt(b), 1);
11447 c.emit(op, 1);
11448 c.emit(Op::Halt, 1);
11449 assert_eq!(run_chunk(&c).expect("vm").to_int(), want);
11450 }
11451 }
11452
11453 #[test]
11454 fn vm_concat_and_str_cmp() {
11455 let mut c = Chunk::new();
11456 let i1 = c.add_constant(StrykeValue::string("a".into()));
11457 let i2 = c.add_constant(StrykeValue::string("b".into()));
11458 c.emit(Op::LoadConst(i1), 1);
11459 c.emit(Op::LoadConst(i2), 1);
11460 c.emit(Op::Concat, 1);
11461 c.emit(Op::Halt, 1);
11462 assert_eq!(run_chunk(&c).expect("vm").to_string(), "ab");
11463
11464 let mut c = Chunk::new();
11465 let i1 = c.add_constant(StrykeValue::string("a".into()));
11466 let i2 = c.add_constant(StrykeValue::string("b".into()));
11467 c.emit(Op::LoadConst(i1), 1);
11468 c.emit(Op::LoadConst(i2), 1);
11469 c.emit(Op::StrCmp, 1);
11470 c.emit(Op::Halt, 1);
11471 let v = run_chunk(&c).expect("vm");
11472 assert!(v.to_int() < 0);
11473 }
11474
11475 #[test]
11476 fn vm_log_not() {
11477 let mut c = Chunk::new();
11478 c.emit(Op::LoadInt(0), 1);
11479 c.emit(Op::LogNot, 1);
11480 c.emit(Op::Halt, 1);
11481 assert_eq!(run_chunk(&c).expect("vm").to_int(), 1);
11482 }
11483
11484 #[test]
11485 fn vm_bit_and_or_xor_not() {
11486 let mut c = Chunk::new();
11487 c.emit(Op::LoadInt(0b1100), 1);
11488 c.emit(Op::LoadInt(0b1010), 1);
11489 c.emit(Op::BitAnd, 1);
11490 c.emit(Op::Halt, 1);
11491 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b1000);
11492
11493 let mut c = Chunk::new();
11494 c.emit(Op::LoadInt(0b1100), 1);
11495 c.emit(Op::LoadInt(0b1010), 1);
11496 c.emit(Op::BitOr, 1);
11497 c.emit(Op::Halt, 1);
11498 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b1110);
11499
11500 let mut c = Chunk::new();
11501 c.emit(Op::LoadInt(0b1100), 1);
11502 c.emit(Op::LoadInt(0b1010), 1);
11503 c.emit(Op::BitXor, 1);
11504 c.emit(Op::Halt, 1);
11505 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0b0110);
11506
11507 let mut c = Chunk::new();
11508 c.emit(Op::LoadInt(0), 1);
11509 c.emit(Op::BitNot, 1);
11510 c.emit(Op::Halt, 1);
11511 assert!((run_chunk(&c).expect("vm").to_int() & 0xFF) != 0);
11512 }
11513
11514 #[test]
11515 fn vm_shl_shr() {
11516 let mut c = Chunk::new();
11517 c.emit(Op::LoadInt(1), 1);
11518 c.emit(Op::LoadInt(3), 1);
11519 c.emit(Op::Shl, 1);
11520 c.emit(Op::Halt, 1);
11521 assert_eq!(run_chunk(&c).expect("vm").to_int(), 8);
11522
11523 let mut c = Chunk::new();
11524 c.emit(Op::LoadInt(16), 1);
11525 c.emit(Op::LoadInt(2), 1);
11526 c.emit(Op::Shr, 1);
11527 c.emit(Op::Halt, 1);
11528 assert_eq!(run_chunk(&c).expect("vm").to_int(), 4);
11529 }
11530
11531 #[test]
11532 fn vm_load_undef_float_constant() {
11533 let mut c = Chunk::new();
11534 c.emit(Op::LoadUndef, 1);
11535 c.emit(Op::Halt, 1);
11536 assert!(run_chunk(&c).expect("vm").is_undef());
11537
11538 let mut c = Chunk::new();
11539 c.emit(Op::LoadFloat(2.5), 1);
11540 c.emit(Op::Halt, 1);
11541 assert!((run_chunk(&c).expect("vm").to_number() - 2.5).abs() < 1e-9);
11542 }
11543
11544 #[test]
11545 fn vm_jump_skips_ops() {
11546 let mut c = Chunk::new();
11547 let j = c.emit(Op::Jump(0), 1);
11548 c.emit(Op::LoadInt(1), 1);
11549 c.emit(Op::LoadInt(2), 1);
11550 c.emit(Op::Add, 1);
11551 c.patch_jump_here(j);
11552 c.emit(Op::LoadInt(40), 1);
11553 c.emit(Op::Halt, 1);
11554 assert_eq!(run_chunk(&c).expect("vm").to_int(), 40);
11555 }
11556
11557 #[test]
11558 fn vm_jump_if_false() {
11559 let mut c = Chunk::new();
11560 c.emit(Op::LoadInt(0), 1);
11561 let j = c.emit(Op::JumpIfFalse(0), 1);
11562 c.emit(Op::LoadInt(1), 1);
11563 c.emit(Op::Halt, 1);
11564 c.patch_jump_here(j);
11565 c.emit(Op::LoadInt(2), 1);
11566 c.emit(Op::Halt, 1);
11567 assert_eq!(run_chunk(&c).expect("vm").to_int(), 2);
11568 }
11569
11570 #[test]
11571 fn vm_call_builtin_defined() {
11572 let mut c = Chunk::new();
11573 c.emit(Op::LoadUndef, 1);
11574 c.emit(Op::CallBuiltin(BuiltinId::Defined as u16, 1), 1);
11575 c.emit(Op::Halt, 1);
11576 assert_eq!(run_chunk(&c).expect("vm").to_int(), 0);
11577 }
11578
11579 #[test]
11580 fn vm_call_builtin_length_string() {
11581 let mut c = Chunk::new();
11582 let idx = c.add_constant(StrykeValue::string("abc".into()));
11583 c.emit(Op::LoadConst(idx), 1);
11584 c.emit(Op::CallBuiltin(BuiltinId::Length as u16, 1), 1);
11585 c.emit(Op::Halt, 1);
11586 assert_eq!(run_chunk(&c).expect("vm").to_int(), 3);
11587 }
11588
11589 #[test]
11590 fn vm_make_array_two() {
11591 let mut c = Chunk::new();
11592 c.emit(Op::LoadInt(1), 1);
11593 c.emit(Op::LoadInt(2), 1);
11594 c.emit(Op::MakeArray(2), 1);
11595 c.emit(Op::Halt, 1);
11596 let v = run_chunk(&c).expect("vm");
11597 let a = v.as_array_vec().expect("array");
11598 assert_eq!(a.len(), 2);
11599 assert_eq!(a[0].to_int(), 1);
11600 assert_eq!(a[1].to_int(), 2);
11601 }
11602
11603 #[test]
11604 fn vm_spaceship() {
11605 let mut c = Chunk::new();
11606 c.emit(Op::LoadInt(1), 1);
11607 c.emit(Op::LoadInt(2), 1);
11608 c.emit(Op::Spaceship, 1);
11609 c.emit(Op::Halt, 1);
11610 assert_eq!(run_chunk(&c).expect("vm").to_int(), -1);
11611 }
11612
11613 #[test]
11614 fn compiled_try_catch_catches_die_via_vm() {
11615 let program = crate::parse(
11616 r#"
11617 try {
11618 die "boom";
11619 } catch ($err) {
11620 42;
11621 }
11622 "#,
11623 )
11624 .expect("parse");
11625 let chunk = crate::compiler::Compiler::new()
11626 .compile_program(&program)
11627 .expect("compile");
11628 let tp = chunk
11629 .ops
11630 .iter()
11631 .position(|o| matches!(o, Op::TryPush { .. }))
11632 .expect("TryPush op");
11633 match &chunk.ops[tp] {
11634 Op::TryPush {
11635 catch_ip, after_ip, ..
11636 } => {
11637 assert_ne!(*catch_ip, 0, "catch_ip must be patched");
11638 assert_ne!(*after_ip, 0, "after_ip must be patched");
11639 }
11640 _ => unreachable!(),
11641 }
11642 let mut interp = VMHelper::new();
11643 let mut vm = VM::new(&chunk, &mut interp);
11644 vm.set_jit_enabled(false);
11645 let v = vm.execute().expect("vm should catch die");
11646 assert_eq!(v.to_int(), 42);
11647 }
11648}