netsblox_vm/
process.rs

1//! [`ByteCode`] execution utilities.
2//! 
3//! Because the NetsBlox runtime allows for the creation of cycles, all program-accessible objects must be garbage collected.
4//! To do this, we use the `gc-arena` crate, which allows a simple and correct mechanism for provably disjoint garbage collected runtime environments.
5//! However, `gc-arena` makes this guarantee by forbidding garbage collected objects from leaving the arena.
6//! Thus, many types in this module, such as  [`Value`] and [`Process`], are branded with an invariant `'gc` lifetime and can only be access by callback.
7//! Some utilities are provided to export these values from the runtime environment if needed.
8
9use alloc::rc::Rc;
10use alloc::vec::Vec;
11use alloc::borrow::ToOwned;
12use alloc::collections::{BTreeMap, BTreeSet, VecDeque, vec_deque::Iter as VecDequeIter};
13
14use core::iter::{self, Cycle};
15use core::cmp::Ordering;
16
17use unicode_segmentation::UnicodeSegmentation;
18use unicase::UniCase;
19
20#[cfg(feature = "serde")]
21use serde::Serialize;
22
23use crate::*;
24use crate::gc::*;
25use crate::json::*;
26use crate::runtime::*;
27use crate::bytecode::*;
28use crate::util::*;
29use crate::compact_str::*;
30
31/// A variable entry in the structure expected by the standard js extension.
32#[cfg_attr(feature = "serde", derive(Serialize))]
33#[derive(Debug, Clone)]
34pub struct VarEntry {
35    pub name: CompactString,
36    pub value: CompactString,
37}
38/// A trace entry in the structure expected by the standard js extension.
39#[cfg_attr(feature = "serde", derive(Serialize))]
40#[derive(Debug, Clone)]
41pub struct TraceEntry {
42    pub location: CompactString,
43    pub locals: Vec<VarEntry>,
44}
45/// A error message in the structure expected by the standard js extension.
46#[cfg_attr(feature = "serde", derive(Serialize))]
47#[derive(Debug, Clone)]
48pub struct ErrorSummary {
49    pub cause: CompactString,
50    pub entity: CompactString,
51    pub globals: Vec<VarEntry>,
52    pub fields: Vec<VarEntry>,
53    pub trace: Vec<TraceEntry>,
54}
55impl ErrorSummary {
56    pub fn extract<C: CustomTypes<S>, S: System<C>>(error: &ExecError<C, S>, process: &Process<C, S>, locations: &Locations) -> Self {
57        let raw_entity = process.call_stack.last().unwrap().entity;
58        let entity = raw_entity.borrow().name.as_str().into();
59        let cause = format_compact!("{:?}", error.cause);
60
61        fn summarize_symbols<C: CustomTypes<S>, S: System<C>>(symbols: &SymbolTable<'_, C, S>) -> Vec<VarEntry> {
62            let mut res = Vec::with_capacity(symbols.len());
63            for (k, v) in symbols.iter() {
64                res.push(VarEntry { name: k.clone(), value: format_compact!("{:?}", &*v.get()) });
65            }
66            res
67        }
68        let globals = summarize_symbols(&process.global_context.borrow().globals);
69        let fields = summarize_symbols(&raw_entity.borrow().fields);
70
71        let mut trace = Vec::with_capacity(process.call_stack.len());
72        for (pos, locals) in iter::zip(process.call_stack[1..].iter().map(|x| x.called_from).chain(iter::once(error.pos)), process.call_stack.iter().map(|x| &x.locals)) {
73            if let Some(location) = locations.lookup(pos) {
74                trace.push(TraceEntry { location, locals: summarize_symbols(locals) });
75            }
76        }
77
78        Self { entity, cause, globals, fields, trace }
79    }
80}
81
82/// An execution error from a [`Process`] (see [`Process::step`]).
83///
84/// This consists of an [`ErrorCause`] value describing the cause, as well as the bytecode location of the error.
85/// By using the [`Locations`] information from [`ByteCode::compile`], it is possible to determine
86/// a human-readable error location in the original program.
87#[derive(Educe)]
88#[educe(Debug)]
89pub struct ExecError<C: CustomTypes<S>, S: System<C>> {
90    pub cause: ErrorCause<C, S>,
91    pub pos: usize,
92}
93
94/// Result of stepping through a [`Process`].
95#[derive(Educe)]
96#[educe(Debug)]
97pub enum ProcessStep<'gc, C: CustomTypes<S>, S: System<C>> {
98    /// The process was not running.
99    Idle,
100    /// The process executed an instruction successfully and does not need to yield.
101    Normal,
102    /// The process has signaled a yield point so that other code can run.
103    /// Many yield results may occur back-to-back, such as while awaiting an asynchronous result.
104    /// 
105    /// Yielding is needed for executing an entire project's scripts so that they can appear to run simultaneously.
106    /// If instead you are explicitly only using a single sandboxed process, this can be treated equivalently to [`ProcessStep::Normal`].
107    Yield,
108    /// The process has successfully terminated with the given return value.
109    Terminate { result: Value<'gc, C, S> },
110    /// Aborts zero or more running processes, possibly including this one.
111    /// If this process is included in the abort set, this process is guaranteed to already be terminated.
112    /// For other processes, it is up to the receiver/scheduler to respect this abort request.
113    Abort { mode: AbortMode },
114    /// The process has requested to broadcast a message to all entities (if `target` is `None`) or to a specific `target`, which may trigger other code to execute.
115    Broadcast { msg_type: Text, barrier: Option<Barrier>, targets: Option<Vec<Gc<'gc, RefLock<Entity<'gc, C, S>>>>> },
116    /// The process has requested to create or destroy a new watcher for a variable.
117    /// If `create` is true, the process is requesting to register the given watcher.
118    /// If `create` if false, the process is requesting to remove a watcher which is equivalent to the given watcher.
119    /// In either case, it is up the handler of this step mode to deduplicate watchers to the same variable, if needed.
120    /// The existence of a watcher is invisible to a process, so it is perfectly valid for implementors to simply ignore all watcher requests.
121    Watcher { create: bool, watcher: Watcher<'gc, C, S> },
122    /// The process has requested to fork a new process that starts with the given parameters.
123    Fork { pos: usize, locals: SymbolTable<'gc, C, S>, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
124    /// The process has created a new clone of an existing entity.
125    /// The clone has already been created, so this is just an informational flag for any logging or other initialization logic.
126    /// Projects use this event to bind new scripts to the clone, which is an aspect of projects but not processes or entities.
127    CreatedClone { clone: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
128    /// The process has requested to delete an existing clone.
129    /// Projects use this event to abort all the scripts and processes associated with the deleted entity.
130    DeletedClone { clone: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
131    /// The process has requested to pause execution of the (entire) project.
132    /// This can be useful for student debugging (similar to breakpoints), but can be ignored by the executor if desired.
133    Pause,
134}
135
136/// An entry in the call stack of a [`Process`].
137/// 
138/// This contains information about the call origin and local variables defined in the called context.
139#[derive(Collect)]
140#[collect(no_drop, bound = "")]
141pub struct CallFrame<'gc, C: CustomTypes<S>, S: System<C>> {
142    #[collect(require_static)] pub called_from: usize,
143    #[collect(require_static)]     return_to: usize,
144                               pub entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>,
145                               pub locals: SymbolTable<'gc, C, S>,
146
147    #[collect(require_static)] warp_counter: usize,
148    #[collect(require_static)] value_stack_size: usize,
149    #[collect(require_static)] handler_stack_size: usize,
150    #[collect(require_static)] unwind_point: <C::ProcessState as Unwindable>::UnwindPoint,
151}
152
153struct Handler<C: CustomTypes<S>, S: System<C>> {
154    pos: usize,
155    var: CompactString,
156    warp_counter: usize,
157    call_stack_size: usize,
158    value_stack_size: usize,
159    unwind_point: <C::ProcessState as Unwindable>::UnwindPoint,
160}
161
162enum Defer<C: CustomTypes<S>, S: System<C>> {
163    Request { key: S::RequestKey, aft_pos: usize, action: RequestAction },
164    Command { key: S::CommandKey, aft_pos: usize },
165    MessageReply { key: ExternReplyKey, aft_pos: usize },
166    Barrier { condition: BarrierCondition, aft_pos: usize },
167    Sleep { until: u64, aft_pos: usize },
168}
169#[derive(Clone, Copy)]
170enum RequestAction {
171    Rpc, Syscall, Input, Push,
172}
173
174/// A collection of context info for starting a new [`Process`].
175#[derive(Collect)]
176#[collect(no_drop, bound = "")]
177pub struct ProcContext<'gc, C: CustomTypes<S>, S: System<C>> {
178                               pub global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>,
179                               pub entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>,
180                               pub locals: SymbolTable<'gc, C, S>,
181    #[collect(require_static)] pub state: C::ProcessState,
182    #[collect(require_static)] pub start_pos: usize,
183    #[collect(require_static)] pub barrier: Option<Barrier>,
184    #[collect(require_static)] pub reply_key: Option<InternReplyKey>,
185    #[collect(require_static)] pub local_message: Option<Text>,
186}
187
188/// A [`ByteCode`] execution primitive.
189/// 
190/// A [`Process`] is a self-contained thread of execution.
191/// It maintains its own state machine for executing instructions step by step.
192#[derive(Collect)]
193#[collect(no_drop, bound = "")]
194pub struct Process<'gc, C: CustomTypes<S>, S: System<C>> {
195                               pub global_context: Gc<'gc, RefLock<GlobalContext<'gc, C, S>>>,
196    #[collect(require_static)] pub state: C::ProcessState,
197    #[collect(require_static)]     pos: usize,
198    #[collect(require_static)]     barrier: Option<Barrier>,
199    #[collect(require_static)]     reply_key: Option<InternReplyKey>,
200    #[collect(require_static)]     warp_counter: usize,
201                                   call_stack: Vec<CallFrame<'gc, C, S>>,
202                                   value_stack: Vec<Value<'gc, C, S>>,
203    #[collect(require_static)]     handler_stack: Vec<Handler<C, S>>,
204    #[collect(require_static)]     defer: Option<Defer<C, S>>,
205                                   last_syscall_error: Option<Value<'gc, C, S>>,
206                                   last_rpc_error: Option<Value<'gc, C, S>>,
207                                   last_answer: Option<Value<'gc, C, S>>,
208                                   last_message: Option<Value<'gc, C, S>>,
209}
210impl<'gc, C: CustomTypes<S>, S: System<C>> Process<'gc, C, S> {
211    /// Creates a new [`Process`] with the given starting context.
212    pub fn new(context: ProcContext<'gc, C, S>) -> Self {
213        let unwind_point = context.state.get_unwind_point();
214        Self {
215            global_context: context.global_context,
216            barrier: context.barrier,
217            reply_key: context.reply_key,
218            pos: context.start_pos,
219            warp_counter: 0,
220            state: context.state,
221            call_stack: vec![CallFrame {
222                called_from: usize::MAX,
223                return_to: usize::MAX,
224                warp_counter: 0,
225                value_stack_size: 0,
226                handler_stack_size: 0,
227                unwind_point,
228                locals: context.locals,
229                entity: context.entity,
230            }],
231            value_stack: vec![],
232            handler_stack: vec![],
233            defer: None,
234            last_syscall_error: None,
235            last_rpc_error: None,
236            last_answer: None,
237            last_message: context.local_message.map(|x| Text::from(x.as_str()).into()),
238        }
239    }
240    /// Checks if the process is currently running.
241    /// Note that the process will not run on its own (see [`Process::step`]).
242    pub fn is_running(&self) -> bool {
243        self.pos != usize::MAX
244    }
245    /// Gets a reference to the current call stack.
246    /// This is a sequence of every call frame, including the current context entity, local variables in scope, and other hidden state information.
247    /// Due to the delicate state involved, a mutable option is not supported.
248    pub fn get_call_stack(&self) -> &[CallFrame<'gc, C, S>] {
249        &self.call_stack
250    }
251    /// Executes a single bytecode instruction.
252    /// The return value can be used to determine what additional effects the script has requested,
253    /// as well as to retrieve the return value or execution error in the event that the process terminates.
254    /// 
255    /// The process transitions to the idle state (see [`Process::is_running`]) upon failing with [`Err`] or succeeding with [`ProcessStep::Terminate`].
256    /// 
257    /// This function is not re-entrant, so recursively calling it from the mutable handle of, e.g., [`Config`] will likely lead to panics.
258    pub fn step(&mut self, mc: &Mutation<'gc>) -> Result<ProcessStep<'gc, C, S>, ExecError<C, S>> {
259        if !self.is_running() {
260            return Ok(ProcessStep::Idle);
261        }
262
263        let mut res = self.step_impl(mc).map_err(|cause| ExecError { cause, pos: self.pos });
264        if let Err(err) = &res {
265            if let Some(Handler { pos, var, warp_counter, call_stack_size, value_stack_size, unwind_point }) = self.handler_stack.last() {
266                self.warp_counter = *warp_counter;
267                self.call_stack.drain(*call_stack_size..);
268                self.value_stack.drain(*value_stack_size..);
269                debug_assert_eq!(self.call_stack.len(), *call_stack_size);
270                debug_assert_eq!(self.value_stack.len(), *value_stack_size);
271                self.state.unwind_to(unwind_point);
272
273                let msg = match &err.cause {
274                    ErrorCause::Custom { msg } => msg.clone(),
275                    x => format_compact!("{x:?}"),
276                };
277                self.call_stack.last_mut().unwrap().locals.define_or_redefine(var, Shared::Unique(Text::from(msg.as_str()).into()));
278                self.pos = *pos;
279                res = Ok(ProcessStep::Normal);
280            }
281        }
282
283        if let Ok(ProcessStep::Terminate { result: _ }) | Ok(ProcessStep::Abort { mode: AbortMode::Current | AbortMode::All }) | Err(_) = &res {
284            self.pos = usize::MAX;
285            self.barrier = None;
286            self.reply_key = None;
287            self.defer = None;
288        }
289
290        res
291    }
292    fn step_impl(&mut self, mc: &Mutation<'gc>) -> Result<ProcessStep<'gc, C, S>, ErrorCause<C, S>> {
293        fn process_result<'gc, C: CustomTypes<S>, S: System<C>, T>(result: Result<T, CompactString>, error_scheme: ErrorScheme, stack: Option<&mut Vec<Value<'gc, C, S>>>, last_ok: Option<&mut Option<Value<'gc, C, S>>>, last_err: Option<&mut Option<Value<'gc, C, S>>>, to_value: fn(T) -> Option<Value<'gc, C, S>>) -> Result<(), ErrorCause<C, S>> {
294            match result {
295                Ok(x) => match to_value(x) {
296                    Some(x) => {
297                        if let Some(last_err) = last_err { *last_err = None }
298                        match (last_ok, stack) {
299                            (Some(last_ok), Some(stack)) => {
300                                *last_ok = Some(x.clone());
301                                stack.push(x);
302                            }
303                            (Some(last_ok), None) => *last_ok = Some(x),
304                            (None, Some(stack)) => stack.push(x),
305                            (None, None) => (),
306                        }
307                    }
308                    None => assert!(last_ok.is_none() && stack.is_none()),
309                }
310                Err(x) => match error_scheme {
311                    ErrorScheme::Soft => {
312                        let x = Value::Text(x.as_str().into());
313
314                        if let Some(last_ok) = last_ok { *last_ok = None }
315                        match (last_err, stack) {
316                            (Some(last_err), Some(stack)) => {
317                                *last_err = Some(x.clone());
318                                stack.push(x);
319                            }
320                            (Some(last_err), None) => *last_err = Some(x),
321                            (None, Some(stack)) => stack.push(x),
322                            (None, None) => (),
323                        }
324                    }
325                    ErrorScheme::Hard => return Err(ErrorCause::Promoted { error: x }),
326                }
327            }
328            Ok(())
329        }
330        fn prep_call_closure<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, value_stack: &mut Vec<Value<'gc, C, S>>, args: usize) -> Result<(usize, SymbolTable<'gc, C, S>), ErrorCause<C, S>> {
331            let mut values = value_stack.drain(value_stack.len() - (args + 1)..);
332            let closure = values.next().unwrap().as_closure()?;
333            let mut closure = closure.borrow_mut(mc);
334            if closure.params.len() != args {
335                return Err(ErrorCause::ClosureArgCount { expected: closure.params.len(), got: args });
336            }
337
338            let mut locals = SymbolTable::default();
339            for (k, v) in closure.captures.iter_mut() {
340                locals.define_or_redefine(k, v.alias(mc));
341            }
342            for (var, value) in iter::zip(&closure.params, values) {
343                locals.define_or_redefine(var, value.into());
344            }
345
346            Ok((closure.pos, locals))
347        }
348
349        let system = self.global_context.borrow().system.clone();
350        match self.defer.take() {
351            None => (),
352            Some(Defer::Request { key, aft_pos, action }) => match system.poll_request(mc, &key, self)? {
353                AsyncResult::Completed(x) => {
354                    let settings = &self.global_context.borrow().settings;
355                    match action {
356                        RequestAction::Syscall => process_result(x, settings.syscall_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_syscall_error), Some)?,
357                        RequestAction::Rpc => process_result(x, settings.rpc_error_scheme, Some(&mut self.value_stack), None, Some(&mut self.last_rpc_error), Some)?,
358                        RequestAction::Input => process_result(x, ErrorScheme::Hard, None, Some(&mut self.last_answer), None, Some)?,
359                        RequestAction::Push => process_result(x, ErrorScheme::Hard, Some(&mut self.value_stack), None, None, Some)?,
360                    }
361                    self.pos = aft_pos;
362                }
363                AsyncResult::Pending => {
364                    self.defer = Some(Defer::Request { key, aft_pos, action });
365                    return Ok(ProcessStep::Yield);
366                }
367                AsyncResult::Consumed => panic!(),
368            }
369            Some(Defer::Command { key, aft_pos }) => match system.poll_command(mc, &key, self)? {
370                AsyncResult::Completed(x) => {
371                    process_result(x, ErrorScheme::Hard, None, None, None, |_| None)?;
372                    self.pos = aft_pos;
373                }
374                AsyncResult::Pending => {
375                    self.defer = Some(Defer::Command { key, aft_pos });
376                    return Ok(ProcessStep::Yield);
377                }
378                AsyncResult::Consumed => panic!(),
379            }
380            Some(Defer::MessageReply { key, aft_pos }) => match system.poll_reply(&key) {
381                AsyncResult::Completed(x) => {
382                    let value = match x {
383                        Some(x) => Value::from_simple(mc, SimpleValue::from_netsblox_json(x)?),
384                        None => Text::default().into(),
385                    };
386                    self.value_stack.push(value);
387                    self.pos = aft_pos;
388                }
389                AsyncResult::Pending => {
390                    self.defer = Some(Defer::MessageReply { key, aft_pos });
391                    return Ok(ProcessStep::Yield);
392                }
393                AsyncResult::Consumed => panic!(),
394            }
395            Some(Defer::Barrier { condition, aft_pos }) => match condition.is_completed() {
396                true => {
397                    self.pos = aft_pos;
398                }
399                false => {
400                    self.defer = Some(Defer::Barrier { condition, aft_pos });
401                    return Ok(ProcessStep::Yield);
402                }
403            }
404            Some(Defer::Sleep { until, aft_pos }) => match system.time(Precision::Low).to_arbitrary_ms()? >= until {
405                true => {
406                    self.pos = aft_pos;
407                }
408                false => {
409                    self.defer = Some(Defer::Sleep { until, aft_pos });
410                    return Ok(ProcessStep::Yield);
411                }
412            }
413        }
414
415        let mut global_context_raw = self.global_context.borrow_mut(mc);
416        let global_context = &mut *global_context_raw;
417
418        macro_rules! lookup_var {
419            (@impl $var:ident :: $m:ident :: $f:expr) => {{
420                let local_frame = self.call_stack.last_mut().unwrap();
421                match LookupGroup::new(&mut [&mut global_context.globals, &mut local_frame.entity.borrow_mut(mc).fields, &mut local_frame.locals]).$m($var) {
422                    Some($var) => $f,
423                    None => return Err(ErrorCause::UndefinedVariable { name: $var.into() }),
424                }
425            }};
426            ($var:ident => $f:expr) => {lookup_var!(@impl $var :: lookup :: $f)};
427            (mut $var:ident => $f:expr) => {lookup_var!(@impl $var :: lookup_mut :: $f)};
428        }
429
430        let (ins, aft_pos) = Instruction::read(&global_context.bytecode.code, &global_context.bytecode.data, self.pos);
431        match ins {
432            Instruction::Yield => {
433                self.pos = aft_pos;
434                if self.warp_counter == 0 { return Ok(ProcessStep::Yield) }
435            }
436            Instruction::WarpStart => {
437                self.warp_counter += 1;
438                self.pos = aft_pos;
439            }
440            Instruction::WarpStop => {
441                self.warp_counter -= 1;
442                self.pos = aft_pos;
443            }
444
445            Instruction::PushBool { value } => {
446                self.value_stack.push(value.into());
447                self.pos = aft_pos;
448            }
449            Instruction::PushInt { value } => {
450                self.value_stack.push(Number::new(value as f64)?.into());
451                self.pos = aft_pos;
452            }
453            Instruction::PushIntString { value } => {
454                self.value_stack.push(format_text!("{value}").into());
455                self.pos = aft_pos;
456            }
457            Instruction::PushNumber { value } => {
458                self.value_stack.push(Number::new(value)?.into());
459                self.pos = aft_pos;
460            }
461            Instruction::PushColor { value } => {
462                let Color { r, g, b, a } = value;
463                self.value_stack.push(Number::new(u32::from_be_bytes([a, r, g ,b]) as f64)?.into());
464                self.pos = aft_pos;
465            }
466            Instruction::PushString { value } => {
467                self.value_stack.push(Text::from(value).into());
468                self.pos = aft_pos;
469            }
470            Instruction::PushVariable { var } => {
471                self.value_stack.push(lookup_var!(var => var.get().clone()));
472                self.pos = aft_pos;
473            }
474            Instruction::PushEntity { name } => match global_context.entities.get(name).copied() {
475                Some(x) => {
476                    self.value_stack.push(Value::Entity(x));
477                    self.pos = aft_pos;
478                }
479                None => return Err(ErrorCause::UndefinedEntity { name: name.into() }),
480            }
481            Instruction::PushSelf => {
482                self.value_stack.push(self.call_stack.last().unwrap().entity.into());
483                self.pos = aft_pos;
484            }
485            Instruction::PopValue => {
486                self.value_stack.pop().unwrap();
487                self.pos = aft_pos;
488            }
489
490            Instruction::DupeValue { top_index } => {
491                let val = self.value_stack[self.value_stack.len() - 1 - top_index as usize].clone();
492                self.value_stack.push(val);
493                self.pos = aft_pos;
494            }
495            Instruction::SwapValues { top_index_1, top_index_2 } => {
496                let len = self.value_stack.len();
497                self.value_stack.swap(len - 1 - top_index_1 as usize, len - 1 - top_index_2 as usize);
498                self.pos = aft_pos;
499            }
500
501            Instruction::TypeQuery { ty } => {
502                let val = self.value_stack.pop().unwrap();
503                self.value_stack.push((val.get_type() == ty.to_type()).into());
504                self.pos = aft_pos;
505            }
506            Instruction::ToBool => {
507                let val = self.value_stack.pop().unwrap();
508                self.value_stack.push(val.as_bool()?.into());
509                self.pos = aft_pos;
510            }
511            Instruction::ToNumber => {
512                let val = self.value_stack.pop().unwrap();
513                self.value_stack.push(val.as_number()?.into());
514                self.pos = aft_pos;
515            }
516
517            Instruction::ListCons => {
518                let mut res = self.value_stack.pop().unwrap().as_list()?.borrow().clone();
519                res.push_front(self.value_stack.pop().unwrap());
520                self.value_stack.push(Gc::new(mc, RefLock::new(res)).into());
521                self.pos = aft_pos;
522            }
523            Instruction::ListCdr => {
524                let mut res = self.value_stack.pop().unwrap().as_list()?.borrow().clone();
525                if res.is_empty() { return Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }) }
526                res.pop_front().unwrap();
527                self.value_stack.push(Gc::new(mc, RefLock::new(res)).into());
528                self.pos = aft_pos;
529            }
530
531            Instruction::ListFind => {
532                let list = self.value_stack.pop().unwrap().as_list()?;
533                let value = self.value_stack.pop().unwrap();
534                let res = ops::find(list, &value)?.map(|i| i + 1).unwrap_or(0);
535                self.value_stack.push(Number::new(res as f64)?.into());
536                self.pos = aft_pos;
537            }
538            Instruction::ListContains => {
539                let value = self.value_stack.pop().unwrap();
540                let list = self.value_stack.pop().unwrap().as_list()?;
541                self.value_stack.push(ops::find(list, &value)?.is_some().into());
542                self.pos = aft_pos;
543            }
544
545            Instruction::ListIsEmpty => {
546                let list = self.value_stack.pop().unwrap().as_list()?;
547                self.value_stack.push(list.borrow().is_empty().into());
548                self.pos = aft_pos;
549            }
550            Instruction::ListLength => {
551                let list = self.value_stack.pop().unwrap().as_list()?;
552                self.value_stack.push(Number::new(list.borrow().len() as f64)?.into());
553                self.pos = aft_pos;
554            }
555            Instruction::ListDims => {
556                let list = self.value_stack.pop().unwrap();
557                self.value_stack.push(Gc::new(mc, RefLock::new(ops::dimensions(&list)?.into_iter().map(|x| Ok(Number::new(x as f64)?.into())).collect::<Result<VecDeque<_>, NumberError>>()?)).into());
558                self.pos = aft_pos;
559            }
560            Instruction::ListRank => {
561                let list = self.value_stack.pop().unwrap();
562                self.value_stack.push(Number::new(ops::dimensions(&list)?.len() as f64)?.into());
563                self.pos = aft_pos;
564            }
565
566            Instruction::ListRev => {
567                let list = self.value_stack.pop().unwrap().as_list()?;
568                self.value_stack.push(Gc::new(mc, RefLock::new(list.borrow().iter().rev().cloned().collect::<VecDeque<_>>())).into());
569                self.pos = aft_pos;
570            }
571            Instruction::ListFlatten => {
572                let list = self.value_stack.pop().unwrap();
573                self.value_stack.push(Gc::new(mc, RefLock::new(ops::flatten(&list)?)).into());
574                self.pos = aft_pos;
575            }
576            Instruction::ListReshape { len } => {
577                let raw_dims: Vec<_> = match len {
578                    VariadicLen::Fixed(len) => {
579                        let stack_size = self.value_stack.len();
580                        self.value_stack.drain(stack_size - len..).collect()
581                    }
582                    VariadicLen::Dynamic => self.value_stack.pop().unwrap().as_list()?.borrow().iter().cloned().collect(),
583                };
584                let src = self.value_stack.pop().unwrap();
585
586                let mut dims = Vec::with_capacity(raw_dims.len());
587                for dim in raw_dims {
588                    let dim = dim.as_number()?.get();
589                    if dim < 0.0 || dim > usize::MAX as f64 { return Err(ErrorCause::InvalidSize { value: dim }) }
590                    let int_dim = dim as usize;
591                    if int_dim as f64 != dim { return Err(ErrorCause::InvalidSize { value: dim }) }
592                    dims.push(int_dim);
593                }
594
595                self.value_stack.push(ops::reshape(mc, &src, &dims)?);
596                self.pos = aft_pos;
597            }
598            Instruction::ListCartesianProduct { len } => {
599                let sources: Vec<_> = match len {
600                    VariadicLen::Fixed(len) => {
601                        let stack_size = self.value_stack.len();
602                        self.value_stack.drain(stack_size - len..).map(|x| x.as_list()).collect::<Result<_,_>>()?
603                    }
604                    VariadicLen::Dynamic => self.value_stack.pop().unwrap().as_list()?.borrow().iter().map(|x| x.as_list()).collect::<Result<_,_>>()?,
605                };
606                self.value_stack.push(Gc::new(mc, RefLock::new(ops::cartesian_product(mc, &sources))).into());
607                self.pos = aft_pos;
608            }
609
610            Instruction::ListJson => {
611                let value = self.value_stack.pop().unwrap().to_simple()?.into_json()?;
612                self.value_stack.push(format_text!("{value}").into());
613                self.pos = aft_pos;
614            }
615            Instruction::ListCsv => {
616                let value = self.value_stack.pop().unwrap();
617                self.value_stack.push(ops::to_csv(&value)?.into());
618                self.pos = aft_pos;
619            }
620            Instruction::ListColumns => {
621                let value = self.value_stack.pop().unwrap();
622                self.value_stack.push(ops::columns(mc, &value)?);
623                self.pos = aft_pos;
624            }
625            Instruction::ListLines => {
626                let value = self.value_stack.pop().unwrap().as_list()?;
627                let value = value.borrow();
628
629                let mut values = value.iter();
630                let res = match values.next() {
631                    Some(x) => {
632                        let mut res = x.as_text()?.as_str().to_owned();
633                        for x in values {
634                            res.push('\n');
635                            res.push_str(&x.as_text()?);
636                        }
637                        res.as_str().into()
638                    }
639                    None => Text::default(),
640                };
641
642                self.value_stack.push(res.into());
643                self.pos = aft_pos;
644            }
645
646            Instruction::ListInsert => {
647                let list = self.value_stack.pop().unwrap().as_list()?;
648                let index = self.value_stack.pop().unwrap();
649                let val = self.value_stack.pop().unwrap();
650                let mut list = list.borrow_mut(mc);
651
652                let index_set = ops::prep_index_set(&index, list.len() + 1)?;
653                for index in index_set.into_iter().rev() {
654                    list.insert(index, val.clone());
655                }
656
657                self.pos = aft_pos;
658            }
659            Instruction::ListInsertLast => {
660                let list = self.value_stack.pop().unwrap().as_list()?;
661                let val = self.value_stack.pop().unwrap();
662                list.borrow_mut(mc).push_back(val);
663                self.pos = aft_pos;
664            }
665            Instruction::ListInsertRandom => {
666                let list = self.value_stack.pop().unwrap().as_list()?;
667                let val = self.value_stack.pop().unwrap();
668                let mut list = list.borrow_mut(mc);
669
670                let index = ops::prep_rand_index(&*system, list.len() + 1)?;
671                list.insert(index, val);
672                self.pos = aft_pos;
673            }
674
675            Instruction::ListGet => {
676                let list = self.value_stack.pop().unwrap();
677                let index = self.value_stack.pop().unwrap();
678                self.value_stack.push(ops::index_list(mc, &*system, &list, &index)?);
679                self.pos = aft_pos;
680            }
681            Instruction::ListGetLast => {
682                let list = self.value_stack.pop().unwrap().as_list()?;
683                self.value_stack.push(match list.borrow().back() {
684                    Some(x) => x.clone(),
685                    None => return Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }),
686                });
687                self.pos = aft_pos;
688            }
689            Instruction::ListGetRandom => {
690                let list = self.value_stack.pop().unwrap().as_list()?;
691                let list = list.borrow();
692                let index = ops::prep_rand_index(&*system, list.len())?;
693                self.value_stack.push(list[index].clone());
694                self.pos = aft_pos;
695            }
696
697            Instruction::ListAssign => {
698                let value = self.value_stack.pop().unwrap();
699                let list = self.value_stack.pop().unwrap().as_list()?;
700                let index = self.value_stack.pop().unwrap();
701                let mut list = list.borrow_mut(mc);
702
703                let index_set = ops::prep_index_set(&index, list.len())?;
704                for index in index_set {
705                    list[index] = value.clone();
706                }
707
708                self.pos = aft_pos;
709            }
710            Instruction::ListAssignLast => {
711                let value = self.value_stack.pop().unwrap();
712                let list = self.value_stack.pop().unwrap().as_list()?;
713                let mut list = list.borrow_mut(mc);
714                if list.is_empty() { return Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }); }
715                *list.back_mut().unwrap() = value;
716                self.pos = aft_pos;
717            }
718            Instruction::ListAssignRandom => {
719                let value = self.value_stack.pop().unwrap();
720                let list = self.value_stack.pop().unwrap().as_list()?;
721                let mut list = list.borrow_mut(mc);
722
723                let index = ops::prep_rand_index(&*system, list.len())?;
724                list[index] = value;
725                self.pos = aft_pos;
726            }
727
728            Instruction::ListRemove => {
729                let list = self.value_stack.pop().unwrap().as_list()?;
730                let index = self.value_stack.pop().unwrap();
731                let mut list = list.borrow_mut(mc);
732
733                let index_set = ops::prep_index_set(&index, list.len())?;
734                for index in index_set.into_iter().rev() {
735                    list.remove(index);
736                }
737
738                self.pos = aft_pos;
739            }
740            Instruction::ListRemoveLast => {
741                let list = self.value_stack.pop().unwrap().as_list()?;
742                let mut list = list.borrow_mut(mc);
743                if list.is_empty() { return Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }) }
744                list.pop_back().unwrap();
745                self.pos = aft_pos;
746            }
747            Instruction::ListRemoveAll => {
748                self.value_stack.pop().unwrap().as_list()?.borrow_mut(mc).clear();
749                self.pos = aft_pos;
750            }
751
752            Instruction::ListPopFirstOrElse { goto } => match self.value_stack.pop().unwrap().as_list()?.borrow_mut(mc).pop_front() {
753                Some(value) => {
754                    self.value_stack.push(value);
755                    self.pos = aft_pos;
756                }
757                None => self.pos = goto,
758            }
759
760            Instruction::BinaryOp { op } => {
761                let b = self.value_stack.pop().unwrap();
762                let a = self.value_stack.pop().unwrap();
763                self.value_stack.push(ops::binary_op(mc, &*system, &a, &b, op)?);
764                self.pos = aft_pos;
765            }
766            Instruction::VariadicOp { op, len } => {
767                type CombineEmpty<'gc, C, S> = fn(&Mutation<'gc>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>;
768                fn combine_as_binary<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, values: &mut dyn Iterator<Item = &Value<'gc, C, S>>, combine_op: BinaryOp, singleton_op: UnaryOp, empty_case: CombineEmpty<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
769                    match values.next() {
770                        Some(first) => match values.next() {
771                            Some(second) => {
772                                let mut acc = ops::binary_op(mc, system, first, second, combine_op)?;
773                                for item in values {
774                                    acc = ops::binary_op(mc, system, &acc, item, combine_op)?;
775                                }
776                                Ok(acc)
777                            }
778                            None => ops::unary_op(mc, system, first, singleton_op),
779                        }
780                        None => empty_case(mc),
781                    }
782                }
783                fn combine_by_relation<'gc, C: CustomTypes<S>, S: System<C>>(values: &mut dyn Iterator<Item = &Value<'gc, C, S>>, relation: Relation) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
784                    let mut res = match values.next() {
785                        None => return Err(ErrorCause::EmptyList),
786                        Some(x) => x,
787                    };
788                    for other in values {
789                        if ops::check_relation(other, res, relation)? {
790                            res = other;
791                        }
792                    }
793                    Ok(res.clone())
794                }
795
796                type Combine<'gc, C, S, I> = fn(&Mutation<'gc>, &S, I) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>;
797                let combine: Combine<'gc, C, S, &mut dyn Iterator<Item = &Value<'gc, C, S>>> = match op {
798                    VariadicOp::Add => |mc, system, values| combine_as_binary(mc, system, values, BinaryOp::Add, UnaryOp::ToNumber, |_| Ok(Number::new(0.0)?.into())),
799                    VariadicOp::Mul => |mc, system, values| combine_as_binary(mc, system, values, BinaryOp::Mul, UnaryOp::ToNumber, |_| Ok(Number::new(1.0)?.into())),
800                    VariadicOp::Min => |_, _, values| combine_by_relation(values, Relation::Less),
801                    VariadicOp::Max => |_, _, values| combine_by_relation(values, Relation::Greater),
802                    VariadicOp::StrCat => |_, _, values| {
803                        let mut acc = CompactString::default();
804                        for item in values {
805                            core::fmt::write(&mut acc, format_args!("{item}")).unwrap();
806                        }
807                        Ok(Text::from(acc.as_str()).into())
808                    },
809                    VariadicOp::MakeList => |mc, _, values| {
810                        Ok(Gc::new(mc, RefLock::new(values.cloned().collect::<VecDeque<_>>())).into())
811                    },
812                    VariadicOp::ListCat => |mc, _, values| {
813                        let mut acc = VecDeque::new();
814                        for item in values {
815                            acc.extend(item.as_list()?.borrow().iter().cloned());
816                        }
817                        Ok(Gc::new(mc, RefLock::new(acc)).into())
818                    },
819                };
820
821                let res = match len {
822                    VariadicLen::Fixed(len) => {
823                        let stack_size = self.value_stack.len();
824                        let res = combine(mc, &*system, &mut self.value_stack[stack_size - len..].iter())?;
825                        self.value_stack.drain(stack_size - len..);
826                        res
827                    }
828                    VariadicLen::Dynamic => {
829                        let src = self.value_stack.pop().unwrap().as_list()?;
830                        let src = src.borrow();
831                        combine(mc, &*system, &mut src.iter())?
832                    }
833                };
834                self.value_stack.push(res);
835                self.pos = aft_pos;
836            }
837            Instruction::Cmp { relation } => {
838                let b = self.value_stack.pop().unwrap();
839                let a = self.value_stack.pop().unwrap();
840                self.value_stack.push(ops::check_relation(&a, &b, relation)?.into());
841                self.pos = aft_pos;
842            }
843            Instruction::Identical => {
844                let b = self.value_stack.pop().unwrap();
845                let a = self.value_stack.pop().unwrap();
846                self.value_stack.push(ops::identical(&a, &b).into());
847                self.pos = aft_pos;
848            }
849            Instruction::UnaryOp { op } => {
850                let x = self.value_stack.pop().unwrap();
851                self.value_stack.push(ops::unary_op(mc, &*system, &x, op)?);
852                self.pos = aft_pos;
853            }
854
855            Instruction::DeclareLocal { var } => {
856                self.call_stack.last_mut().unwrap().locals.define_or_redefine(var, Shared::Unique(Number::new(0.0).unwrap().into()));
857                self.pos = aft_pos;
858            }
859            Instruction::InitUpvar { var } => {
860                let target = lookup_var!(var => var.get().clone());
861                let target = target.as_text()?;
862                let (parent_scope, current_scope) = match self.call_stack.as_mut_slice() {
863                    [] => unreachable!(),
864                    [_] => return Err(ErrorCause::UpvarAtRoot),
865                    [.., x, y] => (x, y),
866                };
867                let parent_def = match parent_scope.locals.lookup_mut(target.as_str()) {
868                    Some(x) => x,
869                    None => return Err(ErrorCause::UndefinedVariable { name: var.into() }),
870                };
871                current_scope.locals.define_or_redefine(var, parent_def.alias(mc));
872                self.pos = aft_pos;
873            }
874            Instruction::Assign { var } => {
875                let value = self.value_stack.pop().unwrap();
876                lookup_var!(mut var => var.set(mc, value));
877                self.pos = aft_pos;
878            }
879            Instruction::BinaryOpAssign { var, op } => {
880                let b = self.value_stack.pop().unwrap();
881                let a = lookup_var!(var => var.get().clone());
882                lookup_var!(mut var => var.set(mc, ops::binary_op(mc, &*system, &a, &b, op)?));
883                self.pos = aft_pos;
884            }
885
886            Instruction::Watcher { create, var } => {
887                let watcher = Watcher {
888                    entity: Gc::downgrade(self.call_stack.last().unwrap().entity),
889                    name: CompactString::new(var),
890                    value: Gc::downgrade(lookup_var!(mut var => var.alias_inner(mc))),
891                };
892                self.pos = aft_pos;
893                return Ok(ProcessStep::Watcher { create, watcher });
894            }
895            Instruction::Pause => {
896                self.pos = aft_pos;
897                return Ok(ProcessStep::Pause);
898            }
899
900            Instruction::Jump { to } => self.pos = to,
901            Instruction::ConditionalJump { to, when } => {
902                let value = self.value_stack.pop().unwrap();
903                self.pos = if value.as_bool()? == when { to } else { aft_pos };
904            }
905
906            Instruction::Call { pos, tokens } => {
907                let limit = global_context.settings.max_call_depth;
908                if self.call_stack.len() >= limit {
909                    return Err(ErrorCause::CallDepthLimit { limit });
910                }
911
912                let params = lossless_split(tokens).collect::<Vec<_>>();
913                let params_count = params.len();
914
915                let mut locals = SymbolTable::default();
916                for (var, val) in iter::zip(params.into_iter(), self.value_stack.drain(self.value_stack.len() - params_count..)) {
917                    locals.define_or_redefine(var, val.into());
918                }
919
920                let entity = self.call_stack.last().unwrap().entity;
921                self.call_stack.push(CallFrame {
922                    called_from: self.pos,
923                    return_to: aft_pos,
924                    warp_counter: self.warp_counter,
925                    value_stack_size: self.value_stack.len(),
926                    handler_stack_size: self.handler_stack.len(),
927                    unwind_point: self.state.get_unwind_point(),
928                    entity,
929                    locals,
930                });
931                self.pos = pos;
932            }
933            Instruction::MakeClosure { pos, params, tokens } => {
934                let mut tokens = lossless_split(tokens);
935                let params = (&mut tokens).take(params).map(CompactString::new).collect::<Vec<_>>();
936                let captures = tokens.collect::<Vec<_>>();
937
938                let mut caps = SymbolTable::default();
939                for &var in captures.iter() {
940                    caps.define_or_redefine(var, lookup_var!(mut var => var.alias(mc)));
941                }
942                self.value_stack.push(Gc::new(mc, RefLock::new(Closure { pos, params, captures: caps })).into());
943                self.pos = aft_pos;
944            }
945            Instruction::CallClosure { new_entity, args } => {
946                let (closure_pos, locals) = prep_call_closure(mc, &mut self.value_stack, args)?;
947                let entity = match new_entity {
948                    false => self.call_stack.last().unwrap().entity,
949                    true => self.value_stack.pop().unwrap().as_entity()?,
950                };
951
952                self.call_stack.push(CallFrame {
953                    called_from: self.pos,
954                    return_to: aft_pos,
955                    warp_counter: self.warp_counter,
956                    value_stack_size: self.value_stack.len(),
957                    handler_stack_size: self.handler_stack.len(),
958                    unwind_point: self.state.get_unwind_point(),
959                    locals,
960                    entity,
961                });
962                self.pos = closure_pos;
963            }
964            Instruction::ForkClosure { args } => {
965                let (closure_pos, locals) = prep_call_closure(mc, &mut self.value_stack, args)?;
966                self.pos = aft_pos;
967                return Ok(ProcessStep::Fork { pos: closure_pos, locals, entity: self.call_stack.last().unwrap().entity });
968            }
969            Instruction::Return => {
970                let CallFrame { called_from, return_to, warp_counter, value_stack_size, handler_stack_size, unwind_point, entity: _, locals: _ } = self.call_stack.last().unwrap();
971                let return_value = self.value_stack.pop().unwrap();
972
973                self.pos = *return_to;
974                self.warp_counter = *warp_counter;
975                self.value_stack.drain(value_stack_size..);
976                self.handler_stack.drain(handler_stack_size..);
977                debug_assert_eq!(self.value_stack.len(), *value_stack_size);
978                debug_assert_eq!(self.handler_stack.len(), *handler_stack_size);
979                self.state.unwind_to(unwind_point);
980
981                if self.call_stack.len() > 1 {
982                    self.call_stack.pop();
983                    self.value_stack.push(return_value);
984                } else {
985                    debug_assert_eq!(self.value_stack.len(), 0);
986                    debug_assert_eq!(*called_from, usize::MAX);
987                    debug_assert_eq!(*return_to, usize::MAX);
988                    debug_assert_eq!(*warp_counter, 0);
989                    debug_assert_eq!(*value_stack_size, 0);
990                    debug_assert_eq!(*handler_stack_size, 0);
991                    return Ok(ProcessStep::Terminate { result: return_value });
992                }
993            }
994            Instruction::Abort { mode } => {
995                match mode {
996                    AbortMode::Current | AbortMode::All => (),
997                    AbortMode::Others | AbortMode::MyOthers => self.pos = aft_pos,
998                }
999                return Ok(ProcessStep::Abort { mode });
1000            }
1001            Instruction::PushHandler { pos, var } => {
1002                self.handler_stack.push(Handler {
1003                    pos,
1004                    var: CompactString::new(var),
1005                    warp_counter: self.warp_counter,
1006                    call_stack_size: self.call_stack.len(),
1007                    value_stack_size: self.value_stack.len(),
1008                    unwind_point: self.state.get_unwind_point(),
1009                });
1010                self.pos = aft_pos;
1011            }
1012            Instruction::PopHandler => {
1013                self.handler_stack.pop().unwrap();
1014                self.pos = aft_pos;
1015            }
1016            Instruction::Throw => {
1017                let msg = self.value_stack.pop().unwrap().as_text()?;
1018                return Err(ErrorCause::Custom { msg: msg.as_str().into() });
1019            }
1020            Instruction::CallRpc { tokens } => {
1021                let mut tokens = lossless_split(tokens);
1022                let host = match tokens.next().unwrap() {
1023                    "" => None,
1024                    x => Some(CompactString::new(x)),
1025                };
1026                let service = CompactString::new(tokens.next().unwrap());
1027                let rpc = CompactString::new(tokens.next().unwrap());
1028
1029                let arg_names = tokens.map(CompactString::new).collect::<Vec<_>>();
1030                let arg_count = arg_names.len();
1031                let args = iter::zip(arg_names, self.value_stack.drain(self.value_stack.len() - arg_count..)).collect();
1032
1033                drop(global_context_raw);
1034                self.defer = Some(Defer::Request {
1035                    key: system.perform_request(mc, Request::Rpc { host, service, rpc, args }, self)?,
1036                    action: RequestAction::Rpc,
1037                    aft_pos
1038                });
1039            }
1040            Instruction::PushRpcError => {
1041                self.value_stack.push(self.last_rpc_error.clone().unwrap_or_else(|| Text::default().into()));
1042                self.pos = aft_pos;
1043            }
1044            Instruction::Syscall { len } => {
1045                let args = match len {
1046                    VariadicLen::Fixed(len) => {
1047                        let stack_size = self.value_stack.len();
1048                        self.value_stack.drain(stack_size - len..).collect()
1049                    }
1050                    VariadicLen::Dynamic => self.value_stack.pop().unwrap().as_list()?.borrow().iter().cloned().collect(),
1051                };
1052                let name = self.value_stack.pop().unwrap().as_text()?;
1053
1054                drop(global_context_raw);
1055                self.defer = Some(Defer::Request {
1056                    key: system.perform_request(mc, Request::Syscall { name: name.as_str().into(), args }, self)?,
1057                    action: RequestAction::Syscall,
1058                    aft_pos
1059                });
1060            }
1061            Instruction::PushSyscallError => {
1062                self.value_stack.push(self.last_syscall_error.clone().unwrap_or_else(|| Text::default().into()));
1063                self.pos = aft_pos;
1064            }
1065            Instruction::SendLocalMessage { wait, target } => {
1066                let targets = match target {
1067                    false => None,
1068                    true => Some(match self.value_stack.pop().unwrap() {
1069                        Value::List(x) => x.borrow().iter().map(Value::as_entity).collect::<Result<_,_>>()?,
1070                        x => vec![x.as_entity()?],
1071                    }),
1072                };
1073                let msg_type = self.value_stack.pop().unwrap().as_text()?;
1074                let barrier = match wait {
1075                    false => {
1076                        self.pos = aft_pos;
1077                        None
1078                    }
1079                    true => {
1080                        let barrier = Barrier::new();
1081                        self.defer = Some(Defer::Barrier { condition: barrier.get_condition(), aft_pos });
1082                        Some(barrier)
1083                    }
1084                };
1085                return Ok(ProcessStep::Broadcast { msg_type, barrier, targets });
1086            }
1087            Instruction::PushLocalMessage => {
1088                self.value_stack.push(self.last_message.clone().unwrap_or_else(|| Text::default().into()));
1089                self.pos = aft_pos;
1090            }
1091            Instruction::Print { style } => {
1092                let value = self.value_stack.pop().unwrap();
1093                let is_empty = match &value { Value::Text(x) => x.is_empty(), _ => false };
1094
1095                drop(global_context_raw);
1096                self.defer = Some(Defer::Command {
1097                    key: system.perform_command(mc, Command::Print { style, value: if is_empty { None } else { Some(value) } }, self)?,
1098                    aft_pos,
1099                });
1100            }
1101            Instruction::Ask => {
1102                let prompt = self.value_stack.pop().unwrap();
1103                let is_empty = match &prompt { Value::Text(x) => x.is_empty(), _ => false };
1104
1105                drop(global_context_raw);
1106                self.defer = Some(Defer::Request {
1107                    key: system.perform_request(mc, Request::Input { prompt: if is_empty { None } else { Some(prompt) } }, self)?,
1108                    action: RequestAction::Input,
1109                    aft_pos
1110                });
1111            }
1112            Instruction::PushAnswer => {
1113                self.value_stack.push(self.last_answer.clone().unwrap_or_else(|| Text::default().into()));
1114                self.pos = aft_pos;
1115            }
1116            Instruction::ResetTimer => {
1117                let t = system.time(Precision::Medium).to_arbitrary_ms()?;
1118                global_context.timer_start = t;
1119                self.pos = aft_pos;
1120            }
1121            Instruction::PushTimer => {
1122                let t = system.time(Precision::Low).to_arbitrary_ms()?;
1123                self.value_stack.push(Number::new(t.saturating_sub(global_context.timer_start) as f64 / 1000.0)?.into());
1124                self.pos = aft_pos;
1125            }
1126            Instruction::Sleep => {
1127                let ms = self.value_stack.pop().unwrap().as_number()?.get() * 1000.0;
1128                if ms <= 0.0 {
1129                    self.pos = aft_pos;
1130                    return Ok(ProcessStep::Yield);
1131                }
1132                self.defer = Some(Defer::Sleep { until: system.time(Precision::Medium).to_arbitrary_ms()? + ms as u64, aft_pos });
1133            }
1134            Instruction::PushRealTime { query } => {
1135                let t = system.time(Precision::High).to_real_local()?;
1136                let v = match query {
1137                    TimeQuery::UnixTimestampMs => (t.unix_timestamp_nanos() / 1000000) as f64,
1138                    TimeQuery::Year => t.year() as f64,
1139                    TimeQuery::Month => t.month() as u8 as f64,
1140                    TimeQuery::Date => t.day() as f64,
1141                    TimeQuery::DayOfWeek => t.date().weekday().number_from_sunday() as f64,
1142                    TimeQuery::Hour => t.hour() as f64,
1143                    TimeQuery::Minute => t.minute() as f64,
1144                    TimeQuery::Second => t.second() as f64,
1145                };
1146                self.value_stack.push(Number::new(v)?.into());
1147                self.pos = aft_pos;
1148            }
1149            Instruction::SendNetworkMessage { tokens, expect_reply } => {
1150                let mut tokens = lossless_split(tokens);
1151                let msg_type = tokens.next().unwrap();
1152
1153                let targets = match self.value_stack.pop().unwrap() {
1154                    Value::Text(x) => vec![CompactString::new(x.as_str())],
1155                    Value::List(x) => {
1156                        let x = x.borrow();
1157                        let mut res = Vec::with_capacity(x.len());
1158                        for val in x.iter() {
1159                            match val {
1160                                Value::Text(x) => res.push(CompactString::new(x.as_str())),
1161                                x => return Err(ErrorCause::VariadicConversionError { got: x.get_type(), expected: Type::Text }),
1162                            }
1163                        }
1164                        res
1165                    }
1166                    x => return Err(ErrorCause::VariadicConversionError { got: x.get_type(), expected: Type::Text }),
1167                };
1168
1169                let values = {
1170                    let field_names = tokens.map(CompactString::new).collect::<Vec<_>>();
1171                    let field_count = field_names.len();
1172                    iter::zip(field_names.into_iter(), self.value_stack.drain(self.value_stack.len() - field_count..)).map(|(k, v)| Ok((k, v.to_simple()?.into_netsblox_json()))).collect::<Result<_,ToSimpleError<_,_>>>()?
1173                };
1174
1175                match system.send_message(msg_type.into(), values, targets, expect_reply)? {
1176                    Some(key) => self.defer = Some(Defer::MessageReply { key, aft_pos }),
1177                    None => self.pos = aft_pos,
1178                }
1179            }
1180            Instruction::SendNetworkReply => {
1181                let value = self.value_stack.pop().unwrap().to_simple()?.into_netsblox_json();
1182                if let Some(key) = self.reply_key.take() {
1183                    system.send_reply(key, value)?;
1184                }
1185                self.pos = aft_pos;
1186            }
1187            Instruction::PushProperty { prop } => {
1188                drop(global_context_raw);
1189                self.defer = Some(Defer::Request {
1190                    key: system.perform_request(mc, Request::Property { prop }, self)?,
1191                    action: RequestAction::Push,
1192                    aft_pos
1193                });
1194            }
1195            Instruction::SetProperty { prop } => {
1196                let value = self.value_stack.pop().unwrap();
1197
1198                drop(global_context_raw);
1199                self.defer = Some(Defer::Command {
1200                    key: system.perform_command(mc, Command::SetProperty { prop, value }, self)?,
1201                    aft_pos,
1202                });
1203            }
1204            Instruction::ChangeProperty { prop } => {
1205                let delta = self.value_stack.pop().unwrap();
1206
1207                drop(global_context_raw);
1208                self.defer = Some(Defer::Command {
1209                    key: system.perform_command(mc, Command::ChangeProperty { prop, delta }, self)?,
1210                    aft_pos,
1211                });
1212            }
1213            Instruction::PushCostume => {
1214                let entity = self.call_stack.last().unwrap().entity.borrow();
1215                self.value_stack.push(entity.costume.clone().map(|x| Value::Image(x)).unwrap_or_else(|| Text::default().into()));
1216                self.pos = aft_pos;
1217            }
1218            Instruction::PushCostumeNumber => {
1219                let entity = self.call_stack.last().unwrap().entity.borrow();
1220                let res = entity.costume.as_ref().and_then(|x| entity.costume_list.iter().enumerate().find(|c| Rc::ptr_eq(x, &c.1.1))).map(|x| x.0 + 1).unwrap_or(0);
1221                self.value_stack.push(Value::Number(Number::new(res as f64)?));
1222                self.pos = aft_pos;
1223            }
1224            Instruction::PushCostumeList => {
1225                let entity = self.call_stack.last().unwrap().entity.borrow();
1226                self.value_stack.push(Value::List(Gc::new(mc, RefLock::new(entity.costume_list.iter().map(|x| Value::Image(x.1.clone())).collect()))));
1227                self.pos = aft_pos;
1228            }
1229            Instruction::PushCostumeProperty { prop } => {
1230                let mut entity_raw = self.call_stack.last().unwrap().entity.borrow_mut(mc);
1231                let entity = &mut *entity_raw;
1232
1233                let costume = match self.value_stack.pop().unwrap() {
1234                    Value::Text(x) => match x.as_str() {
1235                        "" => None,
1236                        x => match entity.costume_list.get(x) {
1237                            Some(x) => Some(x.clone()),
1238                            None => return Err(ErrorCause::UndefinedCostume { name: x.into() }),
1239                        }
1240                    }
1241                    Value::Image(x) => Some(x),
1242                    x => return Err(ErrorCause::ConversionError { got: x.get_type(), expected: Type::Image }),
1243                };
1244                self.value_stack.push(match prop {
1245                    ImageProperty::Name => costume.map(|x| Text::from(x.name.as_str())).unwrap_or_default().into(),
1246                });
1247                self.pos = aft_pos;
1248            }
1249            Instruction::SetCostume => {
1250                let mut entity_raw = self.call_stack.last().unwrap().entity.borrow_mut(mc);
1251                let entity = &mut *entity_raw;
1252
1253                let new_costume = match self.value_stack.pop().unwrap() {
1254                    Value::Image(x) => Some(x),
1255                    Value::Text(x) => match x.as_str() {
1256                        "" => None,
1257                        x => match entity.costume_list.get(x) {
1258                            Some(c) => Some(c.clone()),
1259                            None => return Err(ErrorCause::UndefinedCostume { name: x.into() }),
1260                        }
1261                    }
1262                    x => return Err(ErrorCause::ConversionError { got: x.get_type(), expected: Type::Image }),
1263                };
1264
1265                if new_costume.as_ref().map(Rc::as_ptr) != entity.costume.as_ref().map(Rc::as_ptr) {
1266                    entity.costume = new_costume;
1267
1268                    drop(entity_raw);
1269                    drop(global_context_raw);
1270                    self.defer = Some(Defer::Command {
1271                        key: system.perform_command(mc, Command::SetCostume, self)?,
1272                        aft_pos,
1273                    });
1274                } else {
1275                    self.pos = aft_pos;
1276                }
1277            }
1278            Instruction::NextCostume => {
1279                let mut entity_raw = self.call_stack.last().unwrap().entity.borrow_mut(mc);
1280                let entity = &mut *entity_raw;
1281
1282                match entity.costume.as_ref().and_then(|x| entity.costume_list.iter().enumerate().find(|c| Rc::ptr_eq(x, &c.1.1))).map(|x| x.0) {
1283                    Some(idx) => {
1284                        let new_costume = Some(entity.costume_list.as_slice()[(idx + 1) % entity.costume_list.len()].value.clone());
1285
1286                        if new_costume.as_ref().map(Rc::as_ptr) != entity.costume.as_ref().map(Rc::as_ptr) {
1287                            entity.costume = new_costume;
1288
1289                            drop(entity_raw);
1290                            drop(global_context_raw);
1291                            self.defer = Some(Defer::Command {
1292                                key: system.perform_command(mc, Command::SetCostume, self)?,
1293                                aft_pos,
1294                            });
1295                        } else {
1296                            self.pos = aft_pos;
1297                        }
1298                    }
1299                    None => self.pos = aft_pos,
1300                }
1301            }
1302            Instruction::PushSoundList => {
1303                let entity = self.call_stack.last().unwrap().entity.borrow();
1304                self.value_stack.push(Value::List(Gc::new(mc, RefLock::new(entity.sound_list.iter().map(|x| Value::Audio(x.1.clone())).collect()))));
1305                self.pos = aft_pos;
1306            }
1307            Instruction::PushSoundProperty { prop } => {
1308                let entity_raw = self.call_stack.last().unwrap().entity.borrow();
1309                let entity = &*entity_raw;
1310
1311                let sound = match self.value_stack.pop().unwrap() {
1312                    Value::Audio(x) => x,
1313                    Value::Text(x) => match entity.sound_list.get(x.as_str()) {
1314                        Some(x) => x.clone(),
1315                        None => return Err(ErrorCause::UndefinedSound { name: x.as_str().into() }),
1316                    }
1317                    x => return Err(ErrorCause::ConversionError { got: x.get_type(), expected: Type::Audio }),
1318                };
1319                self.value_stack.push(match prop {
1320                    AudioProperty::Name => Text::from(sound.name.as_str()).into(),
1321                });
1322                self.pos = aft_pos;
1323            }
1324            Instruction::PlaySound { blocking } => {
1325                let entity_raw = self.call_stack.last().unwrap().entity.borrow();
1326                let entity = &*entity_raw;
1327
1328                let sound = match self.value_stack.pop().unwrap() {
1329                    Value::Audio(x) => Some(x),
1330                    Value::Text(x) => match x.as_str() {
1331                        "" => None,
1332                        x => match entity.sound_list.get(x) {
1333                            Some(x) => Some(x.clone()),
1334                            None => return Err(ErrorCause::UndefinedSound { name: x.into() }),
1335                        }
1336                    }
1337                    x => return Err(ErrorCause::ConversionError { got: x.get_type(), expected: Type::Audio }),
1338                };
1339
1340                if let Some(sound) = sound {
1341                    drop(entity_raw);
1342                    drop(global_context_raw);
1343                    self.defer = Some(Defer::Command {
1344                        key: system.perform_command(mc, Command::PlaySound { sound, blocking }, self)?,
1345                        aft_pos,
1346                    });
1347                } else {
1348                    self.pos = aft_pos;
1349                }
1350            }
1351            Instruction::PlayNotes { blocking } => {
1352                let beats = self.value_stack.pop().unwrap().as_number()?;
1353                let notes = match self.value_stack.pop().unwrap() {
1354                    Value::List(x) => x.borrow().iter().map(ops::prep_note).collect::<Result<_,_>>()?,
1355                    x => vec![ops::prep_note(&x)?],
1356                };
1357
1358                if beats.get() > 0.0 {
1359                    drop(global_context_raw);
1360                    self.defer = Some(Defer::Command {
1361                        key: system.perform_command(mc, Command::PlayNotes { notes, beats, blocking }, self)?,
1362                        aft_pos,
1363                    });
1364                } else {
1365                    self.pos = aft_pos;
1366                }
1367            }
1368            Instruction::StopSounds => {
1369                drop(global_context_raw);
1370                self.defer = Some(Defer::Command {
1371                    key: system.perform_command(mc, Command::StopSounds, self)?,
1372                    aft_pos,
1373                });
1374            }
1375            Instruction::Clone => {
1376                let target_cell = self.value_stack.pop().unwrap().as_entity()?;
1377                let target = target_cell.borrow();
1378                let clone = Gc::new(mc, RefLock::new(Entity {
1379                    name: target.name.clone(),
1380                    sound_list: target.sound_list.clone(),
1381                    costume_list: target.costume_list.clone(),
1382                    costume: target.costume.clone(),
1383                    state: C::EntityState::from(EntityKind::Clone { parent: &*target }),
1384                    original: Some(target.original.unwrap_or(target_cell)),
1385                    fields: target.fields.clone(),
1386                }));
1387                self.value_stack.push(clone.into());
1388                self.pos = aft_pos;
1389                return Ok(ProcessStep::CreatedClone { clone });
1390            }
1391            Instruction::DeleteClone => {
1392                self.pos = aft_pos;
1393                let entity = self.call_stack.last().unwrap().entity;
1394                if entity.borrow().original.is_some() {
1395                    return Ok(ProcessStep::DeletedClone { clone: entity });
1396                }
1397            }
1398            Instruction::ClearEffects => {
1399                drop(global_context_raw);
1400                self.defer = Some(Defer::Command {
1401                    key: system.perform_command(mc, Command::ClearEffects, self)?,
1402                    aft_pos,
1403                });
1404            }
1405            Instruction::ClearDrawings => {
1406                drop(global_context_raw);
1407                self.defer = Some(Defer::Command {
1408                    key: system.perform_command(mc, Command::ClearDrawings, self)?,
1409                    aft_pos,
1410                });
1411            }
1412            Instruction::GotoXY => {
1413                let y = self.value_stack.pop().unwrap().as_number()?;
1414                let x = self.value_stack.pop().unwrap().as_number()?;
1415
1416                drop(global_context_raw);
1417                self.defer = Some(Defer::Command {
1418                    key: system.perform_command(mc, Command::GotoXY { x, y }, self)?,
1419                    aft_pos,
1420                });
1421            }
1422            Instruction::Goto => match self.value_stack.pop().unwrap() {
1423                Value::List(target) => {
1424                    let target = target.borrow();
1425                    if target.len() != 2 { return Err(ErrorCause::InvalidListLength { expected: 2, got: target.len() }); }
1426                    let (x, y) = (target[0].as_number()?, target[1].as_number()?);
1427
1428                    drop(global_context_raw);
1429                    self.defer = Some(Defer::Command {
1430                        key: system.perform_command(mc, Command::GotoXY { x, y }, self)?,
1431                        aft_pos,
1432                    });
1433                }
1434                Value::Entity(target) => {
1435                    let target = target.borrow();
1436
1437                    drop(global_context_raw);
1438                    self.defer = Some(Defer::Command {
1439                        key: system.perform_command(mc, Command::GotoEntity { target: &*target }, self)?,
1440                        aft_pos,
1441                    });
1442                }
1443                target => return Err(ErrorCause::ConversionError { got: target.get_type(), expected: Type::Entity }),
1444            }
1445            Instruction::PointTowardsXY => {
1446                let x = self.value_stack.pop().unwrap().as_number()?;
1447                let y = self.value_stack.pop().unwrap().as_number()?;
1448
1449                drop(global_context_raw);
1450                self.defer = Some(Defer::Command {
1451                    key: system.perform_command(mc, Command::PointTowardsXY { x, y }, self)?,
1452                    aft_pos,
1453                });
1454            }
1455            Instruction::PointTowards => match self.value_stack.pop().unwrap() {
1456                Value::List(target) => {
1457                    let target = target.borrow();
1458                    if target.len() != 2 { return Err(ErrorCause::InvalidListLength { expected: 2, got: target.len() }); }
1459                    let (x, y) = (target[0].as_number()?, target[1].as_number()?);
1460
1461                    drop(global_context_raw);
1462                    self.defer = Some(Defer::Command {
1463                        key: system.perform_command(mc, Command::PointTowardsXY { x, y }, self)?,
1464                        aft_pos,
1465                    });
1466                }
1467                Value::Entity(target) => {
1468                    let target = target.borrow();
1469
1470                    drop(global_context_raw);
1471                    self.defer = Some(Defer::Command {
1472                        key: system.perform_command(mc, Command::PointTowardsEntity { target: &*target }, self)?,
1473                        aft_pos,
1474                    });
1475                }
1476                target => return Err(ErrorCause::ConversionError { got: target.get_type(), expected: Type::Entity }),
1477            }
1478            Instruction::Forward => {
1479                let distance = self.value_stack.pop().unwrap().as_number()?;
1480
1481                drop(global_context_raw);
1482                self.defer = Some(Defer::Command {
1483                    key: system.perform_command(mc, Command::Forward { distance }, self)?,
1484                    aft_pos,
1485                });
1486            }
1487            Instruction::UnknownBlock { name, args } => {
1488                let name = CompactString::new(name);
1489                let args = self.value_stack.drain(self.value_stack.len() - args..).collect();
1490
1491                drop(global_context_raw);
1492                self.defer = Some(Defer::Request {
1493                    key: system.perform_request(mc, Request::UnknownBlock { name, args }, self)?,
1494                    action: RequestAction::Push,
1495                    aft_pos
1496                });
1497            }
1498        }
1499
1500        Ok(ProcessStep::Normal)
1501    }
1502}
1503
1504mod ops {
1505    use super::*;
1506
1507    #[derive(Clone, Copy, PartialEq, Eq)]
1508    enum OpType {
1509        Deterministic,
1510        Nondeterministic,
1511    }
1512
1513    fn as_list<'gc, C: CustomTypes<S>, S: System<C>>(v: &Value<'gc, C, S>) -> Option<Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>> {
1514        v.as_list().ok()
1515    }
1516    fn as_matrix<'gc, C: CustomTypes<S>, S: System<C>>(v: &Value<'gc, C, S>) -> Option<Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>> {
1517        let vals = as_list(v)?;
1518        let good = match vals.borrow().front() {
1519            None => false,
1520            Some(first) => as_list(first).is_some(),
1521        };
1522        if good { Some(vals) } else { None }
1523    }
1524
1525    pub(super) fn prep_note<C: CustomTypes<S>, S: System<C>>(value: &Value<'_, C, S>) -> Result<Note, ErrorCause<C, S>> {
1526        if let Ok(v) = value.as_number().map(Number::get) {
1527            let vv = v as i64;
1528            if v != vv as f64 { return Err(ErrorCause::NoteNotInteger { note: v }); }
1529            let res = Note::from_midi(vv as u8, false);
1530            if vv < 0 || res.is_none() { return Err(ErrorCause::NoteNotMidi { note: vv.to_compact_string() }); }
1531            return Ok(res.unwrap());
1532        }
1533        let s = value.as_text()?;
1534        Note::from_name(&s).ok_or_else(|| ErrorCause::NoteNotMidi { note: s.as_str().into() })
1535    }
1536
1537    pub(super) fn prep_index<C: CustomTypes<S>, S: System<C>>(index: &Value<'_, C, S>, len: usize) -> Result<usize, ErrorCause<C, S>> {
1538        let raw_index = index.as_number()?.get();
1539        let index = raw_index as i64;
1540        if index as f64 != raw_index { return Err(ErrorCause::IndexNotInteger { index: raw_index }) }
1541        if index < 1 || index > len as i64 { return Err(ErrorCause::IndexOutOfBounds { index, len }) }
1542        Ok(index as usize - 1)
1543    }
1544    pub(super) fn prep_rand_index<C: CustomTypes<S>, S: System<C>>(system: &S, len: usize) -> Result<usize, ErrorCause<C, S>> {
1545        if len == 0 { return Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }) }
1546        Ok(system.rand(0..len))
1547    }
1548    pub(super) fn prep_index_set<'gc, C: CustomTypes<S>, S: System<C>>(index: &Value<'gc, C, S>, len: usize) -> Result<BTreeSet<usize>, ErrorCause<C, S>> {
1549        fn set_impl<'gc, C: CustomTypes<S>, S: System<C>>(index: &Value<'gc, C, S>, len: usize, dest: &mut BTreeSet<usize>, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> Result<(), ErrorCause<C, S>> {
1550            match index {
1551                Value::List(values) => if cache.insert(index.identity()) {
1552                    for value in values.borrow().iter() {
1553                        set_impl(value, len, dest, cache)?;
1554                    }
1555                }
1556                _ => {
1557                    dest.insert(ops::prep_index(index, len)?);
1558                }
1559            }
1560            Ok(())
1561        }
1562        let mut res = Default::default();
1563        set_impl(index, len, &mut res, &mut Default::default())?;
1564        Ok(res)
1565    }
1566
1567    pub(super) fn flatten<'gc, C: CustomTypes<S>, S: System<C>>(value: &Value<'gc, C, S>) -> Result<VecDeque<Value<'gc, C, S>>, ErrorCause<C, S>> {
1568        fn flatten_impl<'gc, C: CustomTypes<S>, S: System<C>>(value: &Value<'gc, C, S>, dest: &mut VecDeque<Value<'gc, C, S>>, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> Result<(), ErrorCause<C, S>> {
1569            match value {
1570                Value::List(values) => {
1571                    let key = value.identity();
1572                    if !cache.insert(key) { return Err(ErrorCause::CyclicValue) }
1573                    for value in values.borrow().iter() {
1574                        flatten_impl(value, dest, cache)?;
1575                    }
1576                    cache.remove(&key);
1577                }
1578                _ => dest.push_back(value.clone()),
1579            }
1580            Ok(())
1581        }
1582        let mut res = Default::default();
1583        let mut cache = Default::default();
1584        flatten_impl(value, &mut res, &mut cache)?;
1585        debug_assert_eq!(cache.len(), 0);
1586        Ok(res)
1587    }
1588    pub(super) fn dimensions<C: CustomTypes<S>, S: System<C>>(value: &Value<'_, C, S>) -> Result<Vec<usize>, ErrorCause<C, S>> {
1589        fn dimensions_impl<'gc, C: CustomTypes<S>, S: System<C>>(value: &Value<'gc, C, S>, depth: usize, res: &mut Vec<usize>, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> Result<(), ErrorCause<C, S>> {
1590            debug_assert!(depth <= res.len());
1591
1592            if let Value::List(values) = value {
1593                if depth == res.len() { res.push(0); }
1594
1595                let key = value.identity();
1596                if !cache.insert(key) { return Err(ErrorCause::CyclicValue) }
1597
1598                let values = values.borrow();
1599                res[depth] = res[depth].max(values.len());
1600                for value in values.iter() {
1601                    dimensions_impl(value, depth + 1, res, cache)?;
1602                }
1603
1604                cache.remove(&key);
1605            }
1606            Ok(())
1607        }
1608        let mut res = Default::default();
1609        let mut cache = Default::default();
1610        dimensions_impl(value, 0, &mut res, &mut cache)?;
1611        debug_assert_eq!(cache.len(), 0);
1612        Ok(res)
1613    }
1614    pub(super) fn reshape<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, src: &Value<'gc, C, S>, dims: &[usize]) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1615        let src = ops::flatten(src)?;
1616        if src.is_empty() {
1617            return Err(ErrorCause::EmptyList);
1618        }
1619
1620        fn reshape_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, src: &mut Cycle<VecDequeIter<Value<'gc, C, S>>>, dims: &[usize]) -> Value<'gc, C, S> {
1621            match dims {
1622                [] => src.next().unwrap().clone(),
1623                [first, rest @ ..] => Gc::new(mc, RefLock::new((0..*first).map(|_| reshape_impl(mc, src, rest)).collect::<VecDeque<_>>())).into(),
1624            }
1625        }
1626        Ok(reshape_impl(mc, &mut src.iter().cycle(), dims))
1627    }
1628    pub(super) fn columns<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, src: &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1629        let src = src.as_list()?;
1630        let src = src.borrow();
1631
1632        let columns = src.iter().map(|x| match x {
1633            Value::List(x) => x.borrow().len(),
1634            _ => 1,
1635        }).max().unwrap_or(0);
1636
1637        let mut res = VecDeque::with_capacity(columns);
1638        for column in 0..columns {
1639            let mut inner = VecDeque::with_capacity(src.len());
1640            for row in src.iter() {
1641                inner.push_back(match row {
1642                    Value::List(x) => x.borrow().get(column).cloned().unwrap_or_else(|| Text::default().into()),
1643                    _ => row.clone(),
1644                });
1645            }
1646            res.push_back(Value::List(Gc::new(mc, RefLock::new(inner))));
1647        }
1648        Ok(Gc::new(mc, RefLock::new(res)).into())
1649    }
1650    pub(super) fn cartesian_product<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, sources: &[Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>]) -> VecDeque<Value<'gc, C, S>> {
1651        if sources.is_empty() { return Default::default() }
1652
1653        fn cartesian_product_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, res: &mut VecDeque<Value<'gc, C, S>>, partial: &mut VecDeque<Value<'gc, C, S>>, sources: &[Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>]) {
1654            match sources {
1655                [] => res.push_back(Gc::new(mc, RefLock::new(partial.clone())).into()),
1656                [first, rest @ ..] => for item in first.borrow().iter() {
1657                    partial.push_back(item.clone());
1658                    cartesian_product_impl(mc, res, partial, rest);
1659                    partial.pop_back();
1660                }
1661            }
1662        }
1663        let mut res = VecDeque::with_capacity(sources.iter().fold(1, |a, b| a * b.borrow().len()));
1664        let mut partial = VecDeque::with_capacity(sources.len());
1665        cartesian_product_impl(mc, &mut res, &mut partial, sources);
1666        res
1667    }
1668    pub(super) fn from_csv<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, value: &str) -> Result<VecDeque<Value<'gc, C, S>>, ErrorCause<C, S>> {
1669        let mut src = value.chars();
1670        let mut table = VecDeque::new();
1671
1672        if value.is_empty() { return Ok(table); }
1673
1674        'next_vector: loop {
1675            let mut vector = VecDeque::new();
1676
1677            'next_scalar: loop {
1678                let mut scalar = CompactString::default();
1679                let mut in_quote = false;
1680
1681                loop {
1682                    macro_rules! finish {
1683                        (scalar) => {{
1684                            vector.push_back(Text::from(scalar.as_str()).into());
1685                            continue 'next_scalar;
1686                        }};
1687                        (vector) => {{
1688                            vector.push_back(Text::from(scalar.as_str()).into());
1689                            table.push_back(Gc::new(mc, RefLock::new(vector)).into());
1690                            continue 'next_vector;
1691                        }};
1692                        (table) => {{
1693                            vector.push_back(Text::from(scalar.as_str()).into());
1694                            table.push_back(Gc::new(mc, RefLock::new(vector)).into());
1695                            return Ok(table);
1696                        }}
1697                    }
1698
1699                    match src.next() {
1700                        Some('"') if !in_quote => match scalar.is_empty() {
1701                            true => in_quote = true,
1702                            false => return Err(ErrorCause::NotCsv { value: CompactString::new(value) }),
1703                        }
1704                        Some('"') if in_quote => match src.next() {
1705                            Some('"') => scalar.push('"'),
1706                            Some(',') => finish!(scalar),
1707                            Some('\n') => finish!(vector),
1708                            None => finish!(table),
1709                            Some(_) => return Err(ErrorCause::NotCsv { value: CompactString::new(value) }),
1710                        }
1711                        Some(',') if !in_quote => finish!(scalar),
1712                        Some('\n') if !in_quote => finish!(vector),
1713                        Some(x) => scalar.push(x),
1714                        None => match in_quote {
1715                            true => return Err(ErrorCause::NotCsv { value: CompactString::new(value) }),
1716                            false => finish!(table),
1717                        }
1718                    }
1719                }
1720            }
1721        }
1722    }
1723    pub(super) fn to_csv<C: CustomTypes<S>, S: System<C>>(value: &Value<C, S>) -> Result<Text, ErrorCause<C, S>> {
1724        let value = value.as_list()?;
1725        let value = value.borrow();
1726
1727        fn process_scalar(res: &mut CompactString, value: &str) {
1728            let needs_quotes = value.chars().any(|x| matches!(x, '"' | ',' | '\n'));
1729
1730            if needs_quotes { res.push('"'); }
1731            for ch in value.chars() {
1732                match ch {
1733                    '"' => res.push_str("\"\""),
1734                    x => res.push(x),
1735                }
1736            }
1737            if needs_quotes { res.push('"'); }
1738        }
1739        fn process_vector<C: CustomTypes<S>, S: System<C>>(res: &mut CompactString, value: &VecDeque<Value<C, S>>) -> Result<(), ErrorCause<C, S>> {
1740            for (i, x) in value.iter().enumerate() {
1741                if i != 0 { res.push(','); }
1742                process_scalar(res, x.as_text()?.as_str())
1743            }
1744            Ok(())
1745        }
1746        fn process_table<C: CustomTypes<S>, S: System<C>>(res: &mut CompactString, value: &VecDeque<Value<C, S>>) -> Result<(), ErrorCause<C, S>> {
1747            for (i, x) in value.iter().enumerate() {
1748                if i != 0 { res.push('\n'); }
1749                process_vector(res, &*x.as_list()?.borrow())?;
1750            }
1751            Ok(())
1752        }
1753
1754        let mut res = CompactString::default();
1755        let table_mode = value.iter().any(|x| matches!(x, Value::List(..)));
1756        let f = if table_mode { process_table } else { process_vector };
1757        f(&mut res, &*value)?;
1758        Ok(res.as_str().into())
1759    }
1760
1761    fn binary_op_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, a: &Value<'gc, C, S>, b: &Value<'gc, C, S>, matrix_mode: bool, cache: &mut BTreeMap<(Identity<'gc, C, S>, Identity<'gc, C, S>, bool), Value<'gc, C, S>>, scalar_op: fn(&Mutation<'gc>, &S, &Value<'gc, C, S>, &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1762        let cache_key = (a.identity(), b.identity(), matrix_mode);
1763        Ok(match cache.get(&cache_key) {
1764            Some(x) => x.clone(),
1765            None => {
1766                let checker = if matrix_mode { as_matrix } else { as_list };
1767                match (checker(a), checker(b)) {
1768                    (Some(a), Some(b)) => {
1769                        let (a, b) = (a.borrow(), b.borrow());
1770                        let real_res: Value<C, S> = Gc::new(mc, RefLock::new(VecDeque::with_capacity(a.len().min(b.len())))).into();
1771                        cache.insert(cache_key, real_res.clone());
1772                        let res = as_list(&real_res).unwrap();
1773                        let mut res = res.borrow_mut(mc);
1774                        for (a, b) in iter::zip(&*a, &*b) {
1775                            res.push_back(binary_op_impl(mc, system, a, b, matrix_mode, cache, scalar_op)?);
1776                        }
1777                        real_res
1778                    }
1779                    (Some(a), None) => {
1780                        let a = a.borrow();
1781                        let real_res: Value<C, S> = Gc::new(mc, RefLock::new(VecDeque::with_capacity(a.len()))).into();
1782                        cache.insert(cache_key, real_res.clone());
1783                        let res = as_list(&real_res).unwrap();
1784                        let mut res = res.borrow_mut(mc);
1785                        for a in &*a {
1786                            res.push_back(binary_op_impl(mc, system, a, b, matrix_mode, cache, scalar_op)?);
1787                        }
1788                        real_res
1789                    }
1790                    (None, Some(b)) => {
1791                        let b = b.borrow();
1792                        let real_res: Value<C, S> = Gc::new(mc, RefLock::new(VecDeque::with_capacity(b.len()))).into();
1793                        cache.insert(cache_key, real_res.clone());
1794                        let res = as_list(&real_res).unwrap();
1795                        let mut res = res.borrow_mut(mc);
1796                        for b in &*b {
1797                            res.push_back(binary_op_impl(mc, system, a, b, matrix_mode, cache, scalar_op)?);
1798                        }
1799                        real_res
1800                    }
1801                    (None, None) => if matrix_mode { binary_op_impl(mc, system, a, b, false, cache, scalar_op)? } else { scalar_op(mc, system, a, b)? }
1802                }
1803            }
1804        })
1805    }
1806    pub(super) fn binary_op<'gc, 'a, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, a: &'a Value<'gc, C, S>, b: &'a Value<'gc, C, S>, op: BinaryOp) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1807        let mut cache = Default::default();
1808        match op {
1809            BinaryOp::Add       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.add(b.as_number()?)?.into())),
1810            BinaryOp::Sub       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.sub(b.as_number()?)?.into())),
1811            BinaryOp::Mul       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.mul(b.as_number()?)?.into())),
1812            BinaryOp::Div       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.div(b.as_number()?)?.into())),
1813            BinaryOp::Pow       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.powf(b.as_number()?)?.into())),
1814            BinaryOp::Log       => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(b.as_number()?.log(a.as_number()?)?.into())),
1815            BinaryOp::Atan2     => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| Ok(a.as_number()?.atan2(b.as_number()?)?.to_degrees()?.into())),
1816
1817            BinaryOp::StrGet => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| {
1818                let string = b.as_text()?;
1819                let index = prep_index(a, string.graphemes(true).count())?;
1820                Ok(Text::from(string.graphemes(true).nth(index).unwrap()).into())
1821            }),
1822
1823            BinaryOp::Mod => binary_op_impl(mc, system, a, b, true, &mut cache, |_, _, a, b| {
1824                let (a, b) = (a.as_number()?.get(), b.as_number()?.get());
1825                Ok(Number::new(util::modulus(a, b))?.into())
1826            }),
1827            BinaryOp::SplitBy => binary_op_impl(mc, system, a, b, true, &mut cache, |mc, _, a, b| {
1828                let (text, pattern) = (a.as_text()?, b.as_text()?);
1829                Ok(Gc::new(mc, RefLock::new(text.split(pattern.as_str()).map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1830            }),
1831
1832            BinaryOp::Range => binary_op_impl(mc, system, a, b, true, &mut cache, |mc, _, a, b| {
1833                let (mut a, b) = (a.as_number()?.get(), b.as_number()?.get());
1834                let mut res = VecDeque::new();
1835                if a.is_finite() && b.is_finite() {
1836                    if a <= b {
1837                        while a <= b {
1838                            res.push_back(Number::new(a)?.into());
1839                            a += 1.0;
1840                        }
1841                    } else {
1842                        while a >= b {
1843                            res.push_back(Number::new(a)?.into());
1844                            a -= 1.0;
1845                        }
1846                    }
1847                }
1848                Ok(Gc::new(mc, RefLock::new(res)).into())
1849            }),
1850            BinaryOp::Random => binary_op_impl(mc, system, a, b, true, &mut cache, |_, system, a, b| {
1851                let (mut a, mut b) = (a.as_number()?.get(), b.as_number()?.get());
1852                if a > b { (a, b) = (b, a); }
1853                let res = if a == libm::round(a) && b == libm::round(b) {
1854                    let (a, b) = (a as i64, b as i64);
1855                    system.rand(a..=b) as f64
1856                } else {
1857                    system.rand(a..=b)
1858                };
1859                Ok(Number::new(res)?.into())
1860            }),
1861        }
1862    }
1863
1864    fn unary_op_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, x: &Value<'gc, C, S>, cache: &mut BTreeMap<Identity<'gc, C, S>, Value<'gc, C, S>>, op_type: OpType, scalar_op: &dyn Fn(&Mutation<'gc>, &S, &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1865        let cache_key = x.identity();
1866        Ok(match cache.get(&cache_key) {
1867            Some(x) => x.clone(),
1868            None => match as_list(x) {
1869                Some(x) => {
1870                    let x = x.borrow();
1871                    let real_res: Value<C, S> = Gc::new(mc, RefLock::new(VecDeque::with_capacity(x.len()))).into();
1872                    cache.insert(cache_key, real_res.clone());
1873                    let res = as_list(&real_res).unwrap();
1874                    let mut res = res.borrow_mut(mc);
1875                    for x in &*x {
1876                        res.push_back(unary_op_impl(mc, system, x, cache, op_type, scalar_op)?);
1877                    }
1878                    match op_type {
1879                        OpType::Deterministic => (),
1880                        OpType::Nondeterministic => { cache.remove(&cache_key); }
1881                    }
1882                    real_res
1883                }
1884                None => scalar_op(mc, system, x)?,
1885            }
1886        })
1887    }
1888    pub(super) fn unary_op<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, x: &Value<'gc, C, S>, op: UnaryOp) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1889        let mut cache = Default::default();
1890        match op {
1891            UnaryOp::ToNumber => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.into())),
1892            UnaryOp::Not      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok((!x.as_bool()?).into())),
1893            UnaryOp::Abs      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.abs()?.into())),
1894            UnaryOp::Neg      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.neg()?.into())),
1895            UnaryOp::Sqrt     => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.sqrt()?.into())),
1896            UnaryOp::Round    => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.round()?.into())),
1897            UnaryOp::Floor    => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.floor()?.into())),
1898            UnaryOp::Ceil     => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.ceil()?.into())),
1899            UnaryOp::Sin      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::sin(x.as_number()?.get().to_radians()))?.into())),
1900            UnaryOp::Cos      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::cos(x.as_number()?.get().to_radians()))?.into())),
1901            UnaryOp::Tan      => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::tan(x.as_number()?.get().to_radians()))?.into())),
1902            UnaryOp::Asin     => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::asin(x.as_number()?.get()).to_degrees())?.into())),
1903            UnaryOp::Acos     => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::acos(x.as_number()?.get()).to_degrees())?.into())),
1904            UnaryOp::Atan     => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::atan(x.as_number()?.get()).to_degrees())?.into())),
1905            UnaryOp::StrLen   => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(x.as_text()?.graphemes(true).count() as f64)?.into())),
1906
1907            UnaryOp::StrGetLast => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| match x.as_text()?.graphemes(true).next_back() {
1908                Some(ch) => Ok(Text::from(ch).into()),
1909                None => Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }),
1910            }),
1911            UnaryOp::StrGetRandom => unary_op_impl(mc, system, x, &mut cache, OpType::Nondeterministic, &|_, system, x| {
1912                let x = x.as_text()?;
1913                let i = prep_rand_index(system, x.graphemes(true).count())?;
1914                Ok(Text::from(x.graphemes(true).nth(i).unwrap()).into())
1915            }),
1916
1917            UnaryOp::SplitLetter => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1918                Ok(Gc::new(mc, RefLock::new(x.as_text()?.graphemes(true).map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1919            }),
1920            UnaryOp::SplitWord => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1921                Ok(Gc::new(mc, RefLock::new(x.as_text()?.unicode_words().map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1922            }),
1923            UnaryOp::SplitTab => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1924                Ok(Gc::new(mc, RefLock::new(x.as_text()?.split('\t').map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1925            }),
1926            UnaryOp::SplitCR => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1927                Ok(Gc::new(mc, RefLock::new(x.as_text()?.split('\r').map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1928            }),
1929            UnaryOp::SplitLF => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1930                Ok(Gc::new(mc, RefLock::new(x.as_text()?.lines().map(|x| Text::from(x).into()).collect::<VecDeque<_>>())).into())
1931            }),
1932            UnaryOp::SplitCsv => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1933                let value = from_csv(mc, x.as_text()?.as_str())?;
1934                Ok(Gc::new(mc, RefLock::new(value)).into())
1935            }),
1936            UnaryOp::SplitJson => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1937                let value = x.as_text()?;
1938                match parse_json::<Json>(&value) {
1939                    Ok(json) => Ok(Value::from_simple(mc, SimpleValue::from_json(json)?)),
1940                    Err(_) => Err(ErrorCause::NotJson { value: value.as_str().into() }),
1941                }
1942            }),
1943
1944            UnaryOp::UnicodeToChar => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| {
1945                let fnum = x.as_number()?.get();
1946                if fnum < 0.0 || fnum > u32::MAX as f64 { return Err(ErrorCause::InvalidUnicode { value: fnum }) }
1947                let num = fnum as u32;
1948                if num as f64 != fnum { return Err(ErrorCause::InvalidUnicode { value: fnum }) }
1949                match char::from_u32(num) {
1950                    Some(ch) => Ok(format_text!("{ch}").into()),
1951                    None => Err(ErrorCause::InvalidUnicode { value: fnum }),
1952                }
1953            }),
1954            UnaryOp::CharToUnicode => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
1955                let src = x.as_text()?;
1956                let values: VecDeque<_> = src.chars().map(|ch| Ok(Number::new(ch as u32 as f64)?.into())).collect::<Result<_, NumberError>>()?;
1957                Ok(match values.len() {
1958                    1 => values.into_iter().next().unwrap(),
1959                    _ => Gc::new(mc, RefLock::new(values)).into(),
1960                })
1961            }),
1962        }
1963    }
1964    pub(super) fn index_list<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, list: &Value<'gc, C, S>, index: &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
1965        let list = list.as_list()?;
1966        let list = list.borrow();
1967        unary_op_impl(mc, system, index, &mut Default::default(), OpType::Deterministic, &|_, _, x| Ok(list[prep_index(x, list.len())?].clone()))
1968    }
1969
1970    fn cmp_impl<'gc, C: CustomTypes<S>, S: System<C>>(a: &Value<'gc, C, S>, b: &Value<'gc, C, S>, cache: &mut BTreeMap<(Identity<'gc, C, S>, Identity<'gc, C, S>), Option<Option<Ordering>>>) -> Result<Option<Ordering>, ErrorCause<C, S>> {
1971        let key = (a.identity(), b.identity());
1972        match cache.get(&key) {
1973            Some(Some(x)) => return Ok(*x),
1974            Some(None) => return Err(ErrorCause::CyclicValue),
1975            None => { cache.insert(key, None); }
1976        }
1977
1978        let res = match (a, b) {
1979            (Value::Bool(a), Value::Bool(b)) => a.cmp(b).into(),
1980            (Value::Bool(_), _) | (_, Value::Bool(_)) => None,
1981
1982            (Value::Number(a), Value::Number(b)) => a.cmp(b).into(),
1983            (Value::Text(a), Value::Text(b)) => match SimpleValue::parse_number(a).and_then(|a| SimpleValue::parse_number(b).map(|b| (a, b))) {
1984                Some((a, b)) => a.cmp(&b).into(),
1985                None => UniCase::new(a.as_str()).cmp(&UniCase::new(b.as_str())).into(),
1986            }
1987            (Value::Number(a), Value::Text(b)) => match SimpleValue::parse_number(b) {
1988                Some(b) => a.cmp(&b).into(),
1989                None => UniCase::new(SimpleValue::stringify_number(*a).as_str()).cmp(&UniCase::new(b.as_str())).into(),
1990            }
1991            (Value::Text(a), Value::Number(b)) => match SimpleValue::parse_number(a) {
1992                Some(a) => a.cmp(b).into(),
1993                None => UniCase::new(a.as_str()).cmp(&UniCase::new(&SimpleValue::stringify_number(*b).as_str())).into(),
1994            }
1995            (Value::Number(_), _) | (_, Value::Number(_)) => None,
1996            (Value::Text(_), _) | (_, Value::Text(_)) => None,
1997
1998            (Value::List(a), Value::List(b)) => {
1999                let (a, b) = (a.borrow(), b.borrow());
2000                let (mut a, mut b) = (a.iter(), b.iter());
2001                loop {
2002                    match (a.next(), b.next()) {
2003                        (Some(a), Some(b)) => match cmp_impl(a, b, cache)? {
2004                            Some(Ordering::Equal) => (),
2005                            x => break x,
2006                        }
2007                        (None, Some(_)) => break Some(Ordering::Less),
2008                        (Some(_), None) => break Some(Ordering::Greater),
2009                        (None, None) => break Some(Ordering::Equal),
2010                    }
2011                }
2012            }
2013            (Value::List(_), _) | (_, Value::List(_)) => None,
2014
2015            (Value::Image(a), Value::Image(b)) => if Rc::ptr_eq(a, b) { Some(Ordering::Equal) } else { None },
2016            (Value::Image(_), _) | (_, Value::Image(_)) => None,
2017
2018            (Value::Audio(a), Value::Audio(b)) => if Rc::ptr_eq(a, b) { Some(Ordering::Equal) } else { None },
2019            (Value::Audio(_), _) | (_, Value::Audio(_)) => None,
2020
2021            (Value::Closure(a), Value::Closure(b)) => if a.as_ptr() == b.as_ptr() { Some(Ordering::Equal) } else { None },
2022            (Value::Closure(_), _) | (_, Value::Closure(_)) => None,
2023
2024            (Value::Entity(a), Value::Entity(b)) => if a.as_ptr() == b.as_ptr() { Some(Ordering::Equal) } else { None },
2025            (Value::Entity(_), _) | (_, Value::Entity(_)) => None,
2026
2027            (Value::Native(a), Value::Native(b)) => if Rc::ptr_eq(a, b) { Some(Ordering::Equal) } else { None },
2028        };
2029
2030        debug_assert_eq!(cache.get(&key).cloned(), Some(None));
2031        *cache.get_mut(&key).unwrap() = Some(res);
2032        Ok(res)
2033    }
2034    pub(super) fn cmp<'gc, C: CustomTypes<S>, S: System<C>>(a: &Value<'gc, C, S>, b: &Value<'gc, C, S>) -> Result<Option<Ordering>, ErrorCause<C, S>> {
2035        cmp_impl(a, b, &mut Default::default())
2036    }
2037
2038    pub(super) fn check_relation<'gc, C: CustomTypes<S>, S: System<C>>(a: &Value<'gc, C, S>, b: &Value<'gc, C, S>, relation: Relation) -> Result<bool, ErrorCause<C, S>> {
2039        let ord = cmp(a, b)?;
2040        Ok(match relation {
2041            Relation::Equal => ord == Some(Ordering::Equal),
2042            Relation::NotEqual => ord != Some(Ordering::Equal),
2043            _ => match ord {
2044                Some(ord) => match relation {
2045                    Relation::Equal | Relation::NotEqual => unreachable!(),
2046                    Relation::Less => ord == Ordering::Less,
2047                    Relation::LessEq => ord != Ordering::Greater,
2048                    Relation::Greater => ord == Ordering::Greater,
2049                    Relation::GreaterEq => ord != Ordering::Less,
2050                }
2051                None => return Err(ErrorCause::Incomparable { left: a.get_type(), right: b.get_type() }),
2052            }
2053        })
2054    }
2055
2056    pub(super) fn identical<'gc, C: CustomTypes<S>, S: System<C>>(a: &Value<'gc, C, S>, b: &Value<'gc, C, S>) -> bool {
2057        match (a, b) {
2058            (Value::Bool(a), Value::Bool(b)) => a == b,
2059            (Value::Bool(_), _) | (_, Value::Bool(_)) => false,
2060
2061            (Value::Number(a), Value::Number(b)) => a.get().to_bits() == b.get().to_bits(),
2062            (Value::Number(_), _) | (_, Value::Number(_)) => false,
2063
2064            (Value::Text(a), Value::Text(b)) => a == b,
2065            (Value::Text(_), _) | (_, Value::Text(_)) => false,
2066
2067            (Value::Image(a), Value::Image(b)) => Rc::ptr_eq(a, b),
2068            (Value::Image(_), _) | (_, Value::Image(_)) => false,
2069
2070            (Value::Audio(a), Value::Audio(b)) => Rc::ptr_eq(a, b),
2071            (Value::Audio(_), _) | (_, Value::Audio(_)) => false,
2072
2073            (Value::Closure(a), Value::Closure(b)) => a.as_ptr() == b.as_ptr(),
2074            (Value::Closure(_), _) | (_, Value::Closure(_)) => false,
2075
2076            (Value::List(a), Value::List(b)) => a.as_ptr() == b.as_ptr(),
2077            (Value::List(_), _) | (_, Value::List(_)) => false,
2078
2079            (Value::Entity(a), Value::Entity(b)) => a.as_ptr() == b.as_ptr(),
2080            (Value::Entity(_), _) | (_, Value::Entity(_)) => false,
2081
2082            (Value::Native(a), Value::Native(b)) => Rc::ptr_eq(a, b),
2083        }
2084    }
2085
2086    pub(super) fn find<'gc, C: CustomTypes<S>, S: System<C>>(list: Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>, value: &Value<'gc, C, S>) -> Result<Option<usize>, ErrorCause<C, S>> {
2087        let list = list.borrow();
2088        for (i, x) in list.iter().enumerate() {
2089            if cmp(x, value)? == Some(Ordering::Equal) {
2090                return Ok(Some(i));
2091            }
2092        }
2093        Ok(None)
2094    }
2095}