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