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