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