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