1use 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#[cfg_attr(feature = "serde", derive(Serialize))]
33#[derive(Debug, Clone)]
34pub struct VarEntry {
35 pub name: CompactString,
36 pub value: CompactString,
37}
38#[cfg_attr(feature = "serde", derive(Serialize))]
40#[derive(Debug, Clone)]
41pub struct TraceEntry {
42 pub location: CompactString,
43 pub locals: Vec<VarEntry>,
44}
45#[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#[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#[derive(Educe)]
96#[educe(Debug)]
97pub enum ProcessStep<'gc, C: CustomTypes<S>, S: System<C>> {
98 Idle,
100 Normal,
102 Yield,
108 Terminate { result: Value<'gc, C, S> },
110 Abort { mode: AbortMode },
114 Broadcast { msg_type: Text, barrier: Option<Barrier>, targets: Option<Vec<Gc<'gc, RefLock<Entity<'gc, C, S>>>>> },
116 Watcher { create: bool, watcher: Watcher<'gc, C, S> },
122 Fork { pos: usize, locals: SymbolTable<'gc, C, S>, entity: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
124 CreatedClone { clone: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
128 DeletedClone { clone: Gc<'gc, RefLock<Entity<'gc, C, S>>> },
131 Pause,
134}
135
136#[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#[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#[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 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 pub fn is_running(&self) -> bool {
243 self.pos != usize::MAX
244 }
245 pub fn get_call_stack(&self) -> &[CallFrame<'gc, C, S>] {
249 &self.call_stack
250 }
251 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}