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