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