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