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