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