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