qcvm/
lib.rs

1#![deny(missing_docs)]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
3
4use std::{
5    any::Any,
6    ffi::{CStr, CString},
7    fmt,
8    num::NonZeroIsize,
9    ops::{ControlFlow, Range},
10    sync::Arc,
11};
12
13use arrayvec::ArrayVec;
14use bump_scope::{Bump, BumpScope};
15use glam::Vec3;
16use itertools::Itertools;
17use snafu::Snafu;
18
19use crate::{
20    entity::EntityTypeDef,
21    load::Progs,
22    progs::{
23        StringRef, StringTable, VmFunctionRef, VmScalar, VmScalarType, VmValue,
24        functions::{ArgSize, FunctionExecutionCtx, QuakeCFunctionDef, Statement},
25        globals::GlobalRegistry,
26    },
27    userdata::{ErasedContext, ErasedEntityHandle, ErasedFunction, FnCall, QuakeCType},
28};
29
30mod entity;
31mod load;
32mod ops;
33mod progs;
34pub mod userdata;
35
36pub use crate::progs::{
37    EntityRef, VectorField,
38    functions::{Builtin, BuiltinDef, FunctionDef, FunctionRegistry, MAX_ARGS},
39};
40
41#[cfg(feature = "quake1")]
42pub mod quake1;
43
44/// The arguments passed to a QuakeC function.
45#[derive(Debug)]
46struct CallArgs<T>(T);
47
48type HashMap<K, V> = hashbrown::HashMap<K, V, std::hash::BuildHasherDefault<hash32::FnvHasher>>;
49
50// We don't implement this via `QuakeCArgs` as we want to support the scalars being
51// flattened out rather than using `Value::Vec`.
52impl QuakeCMemory for CallArgs<ArrayVec<VmScalar, MAX_ARGS>> {
53    type Scalar = Option<VmScalar>;
54
55    fn get(&self, index: usize) -> anyhow::Result<Self::Scalar> {
56        if !ARG_ADDRS.contains(&index) {
57            return Ok(None);
58        }
59
60        let arg_offset = index
61            .checked_sub(ARG_ADDRS.start)
62            .expect("Programmer error - index out of range for args");
63
64        Ok(Some(
65            self.0.get(arg_offset).unwrap_or(&VmScalar::Void).clone(),
66        ))
67    }
68}
69
70/// The type of individual values passed in and out of the QuakeC runtime.
71/// Note that, in most cases, `Void` will be converted to the default value
72/// for the type.
73#[derive(Debug, Clone, PartialEq)]
74pub enum Value {
75    /// Corresponds to QuakeC's `void`. Should rarely be used, and is mostly
76    /// for the return value of functions that do not return any sensible value.
77    Void,
78    /// An entity reference. See documentation for `EntityRef` for more details.
79    Entity(EntityRef),
80    /// A function pointer. This requires some context to call, see [`userdata::Function`].
81    Function(Arc<dyn ErasedFunction>),
82    /// A single scalar float.
83    Float(f32),
84    /// An `{x, y, z}` vector value.
85    Vector(Vec3),
86    /// A refcounted string pointer. Quake strings are not strictly ascii, but cannot have internal
87    /// `NUL`, so `CStr` is used.
88    String(Arc<CStr>),
89}
90
91/// Errors that can occur when using [`Value::get`].
92#[derive(Snafu, Debug, Copy, Clone, PartialEq, Eq)]
93pub struct GetValueError {
94    /// The field that we attempted to get (see documentation for [`VectorField`]).
95    pub field: VectorField,
96}
97
98/// Errors that can occur when using [`Value::set`].
99#[derive(Snafu, Debug, Copy, Clone, PartialEq, Eq)]
100pub enum SetValueError {
101    /// Tried to set a vector field, but the value was not a vector.
102    NoSuchField {
103        /// The field that we attempted to set (see documentation for [`VectorField`]).
104        field: VectorField,
105    },
106    /// Tried to set a vector field to a value other than float.
107    TypeError {
108        /// The expected type. For now, always [`Type::Float`], but stricter type-checking
109        /// may be implemented in the future.
110        expected: Type,
111        /// The type of the value that was specfied as the source.
112        found: Type,
113    },
114}
115
116impl Value {
117    /// The [`Type`] of this value.
118    pub fn type_(&self) -> Type {
119        match self {
120            Value::Void => Type::Void,
121            Value::Entity(_) => Type::Entity,
122            Value::Function(_) => Type::Function,
123            Value::Float(_) => Type::Float,
124            Value::Vector(_) => Type::Vector,
125            Value::String(_) => Type::String,
126        }
127    }
128
129    /// Get a field of the value, if it's a vector. Passing `None` will just clone the value
130    /// (this is useful for generic code using `VectorField` to optionally convert a type into
131    /// a scalar).
132    pub fn get(&self, field: impl Into<Option<VectorField>>) -> Result<Value, GetValueError> {
133        Ok(match (self, field.into()) {
134            (Value::Vector(v), Some(offset)) => match offset {
135                VectorField::X => v.x.into(),
136                VectorField::Y => v.y.into(),
137                VectorField::Z => v.z.into(),
138            },
139            (other, None) => other.clone(),
140            (_, Some(offset)) => return Err(GetValueError { field: offset }),
141        })
142    }
143
144    /// Clone the source value to this value, with an optional field reference. Useful to implement
145    /// [`userdata::EntityHandle::set`], as sometimes QuakeC only wants to set a single field of a
146    /// vector.
147    pub fn set(
148        &mut self,
149        field: impl Into<Option<VectorField>>,
150        value: Value,
151    ) -> Result<(), SetValueError> {
152        match (self, field.into(), value) {
153            (this, None, value) => *this = value,
154            (Value::Vector(v), Some(offset), Value::Float(f)) => match offset {
155                VectorField::X => v.x = f,
156                VectorField::Y => v.y = f,
157                VectorField::Z => v.z = f,
158            },
159            (Value::Vector(_), Some(_), val) => {
160                return Err(SetValueError::TypeError {
161                    expected: Type::Float,
162                    found: val.type_(),
163                });
164            }
165            (_, Some(offset), _) => return Err(SetValueError::NoSuchField { field: offset }),
166        }
167
168        Ok(())
169    }
170}
171
172impl From<f32> for Value {
173    fn from(value: f32) -> Self {
174        Self::Float(value)
175    }
176}
177
178impl From<Arc<dyn ErasedFunction>> for Value {
179    fn from(value: Arc<dyn ErasedFunction>) -> Self {
180        Self::Function(value)
181    }
182}
183
184impl From<EntityRef> for Value {
185    fn from(value: EntityRef) -> Self {
186        Self::Entity(value)
187    }
188}
189
190impl From<Arc<dyn ErasedEntityHandle>> for Value {
191    fn from(value: Arc<dyn ErasedEntityHandle>) -> Self {
192        Self::Entity(EntityRef::Entity(value))
193    }
194}
195
196impl From<Vec3> for Value {
197    fn from(value: Vec3) -> Self {
198        Self::Vector(value)
199    }
200}
201
202impl From<[f32; 3]> for Value {
203    fn from(value: [f32; 3]) -> Self {
204        Self::Vector(value.into())
205    }
206}
207
208impl From<Arc<CStr>> for Value {
209    fn from(value: Arc<CStr>) -> Self {
210        Self::String(value)
211    }
212}
213
214impl TryFrom<[VmScalar; 3]> for Value {
215    type Error = <VmScalar as TryInto<f32>>::Error;
216
217    fn try_from(value: [VmScalar; 3]) -> Result<Self, Self::Error> {
218        let [x, y, z]: [Result<f32, _>; 3] = value.map(TryInto::try_into);
219        let floats = [x?, y?, z?];
220
221        Ok(floats.into())
222    }
223}
224
225impl TryFrom<Value> for VmScalar {
226    type Error = anyhow::Error;
227
228    fn try_from(value: Value) -> Result<Self, Self::Error> {
229        Ok(match value {
230            Value::Void => VmScalar::Void,
231            Value::Entity(entity_ref) => VmScalar::Entity(entity_ref),
232            Value::Function(erased_function) => {
233                VmScalar::Function(VmFunctionRef::Extern(erased_function))
234            }
235            Value::Float(float) => VmScalar::Float(float),
236            // TODO: Just read x value?
237            Value::Vector(_) => anyhow::bail!("Tried to read vector as a scalar"),
238            Value::String(cstr) => VmScalar::String(StringRef::Temp(cstr)),
239        })
240    }
241}
242
243impl TryFrom<Value> for EntityRef {
244    type Error = anyhow::Error;
245
246    fn try_from(value: Value) -> Result<Self, Self::Error> {
247        match value {
248            Value::Entity(entity_ref) => Ok(entity_ref),
249            _ => anyhow::bail!("Expected {}, found {}", Type::Entity, value.type_()),
250        }
251    }
252}
253
254impl TryFrom<Value> for Arc<dyn ErasedFunction> {
255    type Error = anyhow::Error;
256
257    fn try_from(value: Value) -> Result<Self, Self::Error> {
258        match value {
259            Value::Function(func) => Ok(func),
260            _ => anyhow::bail!("Expected {}, found {}", Type::Function, value.type_()),
261        }
262    }
263}
264
265impl TryFrom<Value> for f32 {
266    type Error = anyhow::Error;
267
268    fn try_from(value: Value) -> Result<Self, Self::Error> {
269        match value {
270            Value::Float(f) => Ok(f),
271            _ => anyhow::bail!("Expected {}, found {}", Type::Float, value.type_()),
272        }
273    }
274}
275
276impl TryFrom<Value> for Vec3 {
277    type Error = anyhow::Error;
278
279    fn try_from(value: Value) -> Result<Self, Self::Error> {
280        match value {
281            Value::Vector(vec) => Ok(vec),
282            _ => anyhow::bail!("Expected {}, found {}", Type::Vector, value.type_()),
283        }
284    }
285}
286
287impl TryFrom<Value> for Arc<CStr> {
288    type Error = anyhow::Error;
289
290    fn try_from(value: Value) -> Result<Self, Self::Error> {
291        match value {
292            Value::String(str) => Ok(str),
293            _ => anyhow::bail!("Expected {}, found {}", Type::String, value.type_()),
294        }
295    }
296}
297
298impl From<Value> for VmValue {
299    fn from(value: Value) -> Self {
300        match value {
301            Value::Void => VmValue::Scalar(VmScalar::Void),
302            Value::Entity(entity_ref) => entity_ref.into(),
303            Value::Function(erased_function) => {
304                VmValue::Scalar(VmScalar::Function(VmFunctionRef::Extern(erased_function)))
305            }
306            Value::Float(f) => f.into(),
307            Value::Vector(vec3) => vec3.into(),
308            Value::String(cstr) => VmValue::Scalar(VmScalar::String(StringRef::Temp(cstr))),
309        }
310    }
311}
312
313/// The type of values that can be used as arguments to a QuakeC function. Implemented
314/// for tuples up to 8 elements (the maximum number of arguments supported by the
315/// vanilla Quake engine).
316pub trait QuakeCArgs: fmt::Debug {
317    /// The error returned by [`QuakeCArgs::nth`].
318    type Error: std::error::Error;
319
320    /// Get the nth argument, specified by `index`.
321    fn nth(&self, index: usize) -> Result<Value, Self::Error>;
322}
323
324/// Errors that can happen when parsing an argument list.
325#[derive(Debug)]
326pub enum ArgError {
327    /// An argument index was specified that was out of range for the number of supported arguments.
328    ArgOutOfRange(usize),
329    /// Some other kind of error occurred (usually in conversion to a [`Value`])
330    Other(Box<dyn std::error::Error>),
331}
332
333impl From<Box<dyn std::error::Error>> for ArgError {
334    fn from(other: Box<dyn std::error::Error>) -> Self {
335        Self::Other(other)
336    }
337}
338
339impl fmt::Display for ArgError {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        match self {
342            Self::ArgOutOfRange(i) => write!(f, "Argument out of range: {i}"),
343            Self::Other(err) => write!(f, "{err}"),
344        }
345    }
346}
347
348impl std::error::Error for ArgError {}
349
350macro_rules! impl_memory_tuple {
351    ($first:ident $(, $rest:ident)*) => {
352        impl<$first, $($rest),*> crate::QuakeCArgs for ($first, $($rest),*)
353        where
354        $first: Clone + fmt::Debug + TryInto<Value>,
355        $first::Error: std::error::Error + 'static,
356        $(
357            $rest: Clone + fmt::Debug + TryInto<Value>,
358            $rest::Error: std::error::Error + 'static,
359        )*
360        {
361            type Error = ArgError;
362
363            #[expect(non_snake_case)]
364            fn nth(&self, index: usize) -> Result<Value, Self::Error> {
365                let ($first, $($rest),*) = self;
366
367                Ok(impl_memory_tuple!(@arg_match index $first $($rest)*))
368            }
369        }
370
371        impl_memory_tuple!($($rest),*);
372    };
373    (@arg_match $match_name:ident $a0:ident $($a1:ident $($a2:ident $($a3:ident $($a4:ident $($a5:ident $($a6:ident $($a7:ident)?)?)?)?)?)?)?) => {
374        match $match_name {
375            0 => $a0.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
376            $(1 => $a1.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
377            $(2 => $a2.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
378            $(3 => $a3.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
379            $(4 => $a4.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
380            $(5 => $a5.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
381            $(6 => $a6.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,
382            $(7 => $a7.clone().try_into().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?,)?)?)?)?)?)?)?
383            _ => return Err(ArgError::ArgOutOfRange($match_name)),
384        }
385    };
386    () => {
387        impl crate::QuakeCArgs for () {
388            type Error = ArgError;
389
390            fn nth(&self, index: usize) -> Result<Value, Self::Error> {
391                Err(ArgError::ArgOutOfRange(index))
392            }
393        }
394
395    }
396}
397
398impl_memory_tuple!(A, B, C, D, E, F, G, H);
399
400impl<T> QuakeCMemory for CallArgs<T>
401where
402    T: QuakeCArgs,
403{
404    type Scalar = Option<VmScalar>;
405
406    fn get(&self, index: usize) -> anyhow::Result<Self::Scalar> {
407        if !ARG_ADDRS.contains(&index) {
408            return Ok(None);
409        }
410
411        let arg_offset = index
412            .checked_sub(ARG_ADDRS.start)
413            .expect("Programmer error - index out of range for args");
414        let field_offset = arg_offset % 3;
415        let index = arg_offset / 3;
416
417        Ok(
418            match self.0.nth(index).map_err(|e| anyhow::format_err!("{e}"))? {
419                Value::Vector(vec) => Some(<[f32; 3]>::from(vec)[field_offset].into()),
420                other => Some(other.try_into()?),
421            },
422        )
423    }
424}
425
426impl QuakeCType for QuakeCFunctionDef {
427    fn type_(&self) -> Type {
428        Type::Function
429    }
430
431    fn is_null(&self) -> bool {
432        self.offset == 0
433    }
434}
435
436/// The possible types of values exposed to the host.
437///
438/// > NOTE: These do not represent all possible types of values internal to the QuakeC
439/// > runtime. QuakeC has "entity field" and "pointer" types - which are relative pointers
440/// > within entities and absolute pointers to globals respectively - but these are implementation
441/// > details due to some quirks about how QuakeC's opcodes are defined. The values exposed to the
442/// > host are deliberately limited to values that are exposed when writing QuakeC code.
443#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
444pub enum Type {
445    /// Any scalar value. If a function wants to be generic in its arguments, it can specify
446    /// [`Type::AnyScalar`] in its arguments. The only non-scalar type is vector, and for now
447    /// it is not possible to be generic over both scalars and vectors in the same argument.
448    AnyScalar,
449    /// Corresponds to QuakeC's `void`. Should rarely be used, and is mostly
450    /// for the return value of functions that do not return any sensible value.
451    // TODO: Should we ever expose `Void` to the caller?
452    Void,
453    /// An entity reference. See documentation for `EntityRef` for more details.
454    Entity,
455    /// A function pointer. This requires some context to call, see [`userdata::Function`].
456    Function,
457    /// An `{x, y, z}` vector value.
458    Vector,
459    /// A single scalar float.
460    Float,
461    /// A refcounted string pointer. Quake strings are not strictly ascii, but cannot have internal
462    /// `NUL`, so `CStr` is used.
463    String,
464}
465
466impl TryFrom<progs::VmType> for Type {
467    type Error = anyhow::Error;
468
469    fn try_from(value: progs::VmType) -> Result<Self, Self::Error> {
470        Ok(match value {
471            progs::VmType::Scalar(scalar_type) => scalar_type.try_into()?,
472            progs::VmType::Vector => Type::Vector,
473        })
474    }
475}
476
477impl TryFrom<VmScalarType> for Type {
478    type Error = anyhow::Error;
479
480    fn try_from(value: VmScalarType) -> Result<Self, Self::Error> {
481        Ok(match value {
482            VmScalarType::Void => Type::Void,
483            VmScalarType::String => Type::String,
484            VmScalarType::Float => Type::Float,
485            VmScalarType::Entity => Type::Entity,
486            VmScalarType::Function => Type::Function,
487            VmScalarType::FieldRef => anyhow::bail!("We don't support `FieldRef` in host bindings"),
488            VmScalarType::GlobalRef => {
489                anyhow::bail!("We don't support `GlobalRef` in host bindings")
490            }
491        })
492    }
493}
494
495impl Type {
496    pub(crate) fn arg_size(&self) -> ArgSize {
497        match self {
498            Self::Vector => ArgSize::Vector,
499            _ => ArgSize::Scalar,
500        }
501    }
502}
503
504impl fmt::Display for Type {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        match self {
507            Type::Void => write!(f, "void"),
508            Type::AnyScalar => write!(f, "<any>"),
509            Type::Entity => write!(f, "entity"),
510            Type::Function => write!(f, "function"),
511            Type::Float => write!(f, "float"),
512            Type::Vector => write!(f, "vector"),
513            Type::String => write!(f, "string"),
514        }
515    }
516}
517
518impl ErasedFunction for QuakeCFunctionDef {
519    fn dyn_signature(&self) -> anyhow::Result<ArrayVec<Type, MAX_ARGS>> {
520        Ok(self
521            .args
522            .iter()
523            .map(|size| match size.arg_size() {
524                ArgSize::Scalar => Type::AnyScalar,
525                ArgSize::Vector => Type::Vector,
526            })
527            .collect())
528    }
529
530    fn dyn_call(&self, mut context: FnCall) -> anyhow::Result<Value> {
531        let vm_value = context.execution.execute_def(self)?;
532
533        context.execution.to_value(vm_value.try_into()?)
534    }
535}
536
537/// The core QuakeC runtime.
538pub struct QuakeCVm {
539    progs: Progs,
540}
541
542/// A reference to a function stored in a `QuakeCVm`.
543#[derive(Clone, PartialEq, Debug)]
544pub enum FunctionRef {
545    /// A function specified by index. This can be useful for interacting with code
546    /// that is ABI-compatible with your environment but not API-compatible.
547    Offset(i32),
548    /// A function specified by name.
549    Name(Arc<CStr>),
550}
551
552impl From<i32> for FunctionRef {
553    fn from(value: i32) -> Self {
554        Self::Offset(value)
555    }
556}
557
558impl From<Arc<CStr>> for FunctionRef {
559    fn from(value: Arc<CStr>) -> Self {
560        Self::Name(value)
561    }
562}
563
564impl From<&CStr> for FunctionRef {
565    fn from(value: &CStr) -> Self {
566        value.to_owned().into()
567    }
568}
569
570impl From<CString> for FunctionRef {
571    fn from(value: CString) -> Self {
572        Arc::<CStr>::from(value).into()
573    }
574}
575
576impl TryFrom<String> for FunctionRef {
577    type Error = std::ffi::NulError;
578
579    fn try_from(value: String) -> Result<Self, Self::Error> {
580        Ok(CString::new(value)?.into())
581    }
582}
583
584impl TryFrom<&str> for FunctionRef {
585    type Error = std::ffi::NulError;
586
587    fn try_from(value: &str) -> Result<Self, Self::Error> {
588        value.to_owned().try_into()
589    }
590}
591
592impl QuakeCVm {
593    /// Load the runtime from a `progs.dat` file.
594    pub fn load<R>(bytes: R) -> anyhow::Result<Self>
595    where
596        R: std::io::Read + std::io::Seek,
597    {
598        Ok(Self {
599            progs: Progs::load(bytes)?,
600        })
601    }
602
603    /// Run the specified function with the specified context and arguments.
604    pub fn run<'a, T, C, F>(
605        &'a self,
606        context: &'a mut C,
607        function: F,
608        args: T,
609    ) -> anyhow::Result<Value>
610    where
611        F: Into<FunctionRef>,
612        T: QuakeCArgs,
613        C: ErasedContext,
614    {
615        let mut ctx: ExecutionCtx<'_, dyn ErasedContext, _, CallArgs<T>> = ExecutionCtx {
616            alloc: Bump::new(),
617            memory: ExecutionMemory {
618                local: CallArgs(args),
619                global: &self.progs.globals,
620                last_ret: None,
621            },
622            backtrace: Default::default(),
623            context,
624            entity_def: &self.progs.entity_def,
625            string_table: &self.progs.string_table,
626            functions: &self.progs.functions,
627        };
628
629        ctx.execute(function)
630    }
631}
632
633#[derive(Default, Debug)]
634struct BacktraceFrame<'a>(Option<(&'a CStr, &'a BacktraceFrame<'a>)>);
635
636impl fmt::Display for BacktraceFrame<'_> {
637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638        if let Some((function, prev)) = self.0 {
639            write!(f, "{}", function.to_string_lossy())?;
640
641            prev.fmt(f)
642        } else {
643            Ok(())
644        }
645    }
646}
647
648#[derive(Debug)]
649struct ExecutionMemory<'a, Caller = FunctionExecutionCtx<'a>> {
650    local: Caller,
651    global: &'a GlobalRegistry,
652    /// Technically, every QuakeC function returns 3 scalars of arbitrary types.
653    /// Only one return value is available at once, if another function is called then the previous return
654    /// value is lost unless it was saved to a local. We store it here in order to allow us to access it in `get` etc.
655    last_ret: Option<[VmScalar; 3]>,
656}
657
658impl ExecutionMemory<'_> {
659    fn instr(&self, pc: usize) -> anyhow::Result<Statement> {
660        self.local.instr(pc)
661    }
662}
663
664trait QuakeCMemory {
665    type Scalar;
666
667    fn get(&self, index: usize) -> anyhow::Result<Self::Scalar>;
668
669    fn get_vector(&self, index: usize) -> anyhow::Result<[Self::Scalar; 3]> {
670        Ok([self.get(index)?, self.get(index + 1)?, self.get(index + 2)?])
671    }
672}
673
674impl<M> QuakeCMemory for ExecutionMemory<'_, M>
675where
676    M: QuakeCMemory<Scalar = Option<VmScalar>>,
677{
678    type Scalar = VmScalar;
679
680    fn get(&self, index: usize) -> anyhow::Result<VmScalar> {
681        if RETURN_ADDRS.contains(&index) {
682            return self
683                .last_ret
684                .as_ref()
685                .and_then(|val| val.get(index.checked_sub(RETURN_ADDRS.start)?).cloned())
686                .ok_or_else(|| {
687                    anyhow::format_err!("Tried to read return values before calling a function")
688                });
689        }
690
691        match self.local.get(index)? {
692            Some(val) => Ok(val),
693            None => self.global.get_value(index),
694        }
695    }
696}
697
698impl ExecutionMemory<'_> {
699    fn set(&mut self, index: usize, value: VmScalar) -> anyhow::Result<()> {
700        self.local.set(index, value)
701    }
702
703    fn set_vector(&mut self, index: usize, values: [VmScalar; 3]) -> anyhow::Result<()> {
704        self.local.set_vector(index, values)
705    }
706}
707
708#[derive(Debug)]
709struct ExecutionCtx<
710    'a,
711    Ctx = dyn ErasedContext,
712    Alloc = BumpScope<'a>,
713    Caller = FunctionExecutionCtx<'a>,
714> where
715    Ctx: ?Sized,
716{
717    alloc: Alloc,
718    memory: ExecutionMemory<'a, Caller>,
719    backtrace: BacktraceFrame<'a>,
720    context: &'a mut Ctx,
721    entity_def: &'a EntityTypeDef,
722
723    string_table: &'a StringTable,
724
725    /// Function definitions and bodies.
726    functions: &'a FunctionRegistry,
727}
728
729impl<Ctx, Alloc, Caller> ExecutionCtx<'_, Ctx, Alloc, Caller> {}
730
731enum OpResult {
732    Jump(NonZeroIsize),
733    Ret([VmScalar; 3]),
734    Continue,
735}
736
737impl From<OpResult> for ControlFlow<[VmScalar; 3], isize> {
738    fn from(value: OpResult) -> Self {
739        match value {
740            OpResult::Jump(ofs) => ControlFlow::Continue(ofs.get()),
741            OpResult::Continue => ControlFlow::Continue(1),
742            OpResult::Ret(ret) => ControlFlow::Break(ret),
743        }
744    }
745}
746
747impl From<()> for OpResult {
748    fn from(_: ()) -> Self {
749        Self::Continue
750    }
751}
752
753impl From<[VmScalar; 3]> for OpResult {
754    fn from(value: [VmScalar; 3]) -> Self {
755        Self::Ret(value)
756    }
757}
758
759const ARG_ADDRS: Range<usize> = 4..28;
760const RETURN_ADDRS: Range<usize> = 1..4;
761
762/// Frustratingly, the `bump-scope` crate doesn't have a way to be generic over `Bump` or `BumpScope`.
763trait ScopedAlloc {
764    fn scoped<F, O>(&mut self, func: F) -> O
765    where
766        F: FnOnce(BumpScope<'_>) -> O;
767}
768
769impl ScopedAlloc for Bump {
770    fn scoped<F, O>(&mut self, func: F) -> O
771    where
772        F: FnOnce(BumpScope<'_>) -> O,
773    {
774        self.scoped(func)
775    }
776}
777
778impl ScopedAlloc for BumpScope<'_> {
779    fn scoped<F, O>(&mut self, func: F) -> O
780    where
781        F: FnOnce(BumpScope<'_>) -> O,
782    {
783        self.scoped(func)
784    }
785}
786
787impl ScopedAlloc for &'_ mut BumpScope<'_> {
788    fn scoped<F, O>(&mut self, func: F) -> O
789    where
790        F: FnOnce(BumpScope<'_>) -> O,
791    {
792        (**self).scoped(func)
793    }
794}
795
796impl<Ctx, Alloc> ExecutionCtx<'_, Ctx, Alloc>
797where
798    Ctx: ?Sized,
799    Alloc: ScopedAlloc,
800{
801    pub fn instr(&self, pc: usize) -> anyhow::Result<Statement> {
802        self.memory.instr(pc)
803    }
804}
805
806impl<'a, Ctx, Alloc, Caller> ExecutionCtx<'a, Ctx, Alloc, Caller>
807where
808    Ctx: ?Sized + AsErasedContext,
809    Alloc: ScopedAlloc,
810{
811    fn with_args<F, A, O>(&mut self, name: &CStr, args: A, func: F) -> O
812    where
813        F: FnOnce(ExecutionCtx<'_, dyn ErasedContext, BumpScope<'_>, CallArgs<A>>) -> O,
814        CallArgs<A>: QuakeCMemory,
815    {
816        let ExecutionCtx {
817            alloc,
818            memory: ExecutionMemory { global, .. },
819            backtrace,
820            context,
821            entity_def,
822            string_table,
823            functions,
824        } = self;
825
826        alloc.scoped(|alloc| {
827            func(ExecutionCtx {
828                alloc,
829                memory: ExecutionMemory {
830                    local: CallArgs(args),
831                    global,
832                    last_ret: None,
833                },
834                backtrace: BacktraceFrame(Some((name, backtrace))),
835                context: context.as_erased_mut(),
836                entity_def,
837                string_table,
838                functions,
839            })
840        })
841    }
842}
843
844impl<'a, Alloc, Caller> ExecutionCtx<'a, dyn ErasedContext, Alloc, Caller> {
845    fn downcast<T>(self) -> Option<ExecutionCtx<'a, T, Alloc, Caller>>
846    where
847        T: Any,
848    {
849        let ExecutionCtx {
850            alloc,
851            memory,
852            backtrace,
853            context,
854            entity_def,
855            string_table,
856            functions,
857        } = self;
858
859        Some(ExecutionCtx {
860            alloc,
861            memory,
862            backtrace,
863            context: (context as &mut dyn Any).downcast_mut()?,
864            entity_def,
865            string_table,
866            functions,
867        })
868    }
869}
870
871/// Annoyingly, we can't generically handle `&mut T` where `T: ErasedContext` and _also_
872/// `&mut dyn ErasedContext` without introducing this ugly trait.
873///
874/// > TODO: Split up the execution context so that we handle memory and execution separately,
875/// > since the memory is the only thing really stopping us from making this more ergonomic.
876pub trait AsErasedContext {
877    /// Convert to a mutable type-erased context.
878    fn as_erased_mut(&mut self) -> &mut dyn ErasedContext;
879    /// Convert to an immutable type-erased context.
880    fn as_erased(&self) -> &dyn ErasedContext;
881}
882
883impl<T> AsErasedContext for T
884where
885    T: ErasedContext,
886{
887    fn as_erased_mut(&mut self) -> &mut dyn ErasedContext {
888        self
889    }
890
891    fn as_erased(&self) -> &dyn ErasedContext {
892        self
893    }
894}
895
896impl AsErasedContext for dyn ErasedContext {
897    fn as_erased_mut(&mut self) -> &mut dyn ErasedContext {
898        self
899    }
900
901    fn as_erased(&self) -> &dyn ErasedContext {
902        self
903    }
904}
905
906impl<'a, Ctx, Alloc, Caller> ExecutionCtx<'a, Ctx, Alloc, Caller>
907where
908    Ctx: ?Sized + AsErasedContext,
909{
910    fn to_value(&self, vm_value: VmValue) -> anyhow::Result<Value> {
911        match vm_value {
912            VmValue::Vector(vec) => Ok(Value::Vector(vec.into())),
913            VmValue::Scalar(VmScalar::Float(f)) => Ok(Value::Float(f)),
914            VmValue::Scalar(VmScalar::Void) => Ok(Value::Void),
915            VmValue::Scalar(VmScalar::Entity(ent)) => Ok(Value::Entity(ent)),
916            VmValue::Scalar(VmScalar::String(str)) => {
917                Ok(Value::String(self.string_table.get(str)?))
918            }
919            VmValue::Scalar(VmScalar::Function(VmFunctionRef::Ptr(ptr))) => {
920                let arg_func = self.functions.get_by_index(ptr.0)?.clone();
921
922                match arg_func.try_into_qc() {
923                    Ok(quakec) => Ok(Value::Function(Arc::new(quakec))),
924                    Err(builtin) => Ok(Value::Function(
925                        self.context.as_erased().dyn_builtin(&builtin)?,
926                    )),
927                }
928            }
929            VmValue::Scalar(VmScalar::Function(VmFunctionRef::Extern(func))) => {
930                Ok(Value::Function(func))
931            }
932            VmValue::Scalar(
933                VmScalar::EntityField(..) | VmScalar::Field(_) | VmScalar::Global(_),
934            ) => anyhow::bail!(
935                "Values of type {} are unsupported as arguments to builtins",
936                vm_value.type_()
937            ),
938        }
939    }
940}
941
942impl<Alloc, Caller> ExecutionCtx<'_, dyn ErasedContext, Alloc, Caller>
943where
944    Alloc: ScopedAlloc,
945    Caller: fmt::Debug + QuakeCMemory<Scalar = Option<VmScalar>>,
946{
947    pub fn execute<F>(&mut self, function: F) -> anyhow::Result<Value>
948    where
949        F: Into<FunctionRef>,
950    {
951        match function.into() {
952            FunctionRef::Offset(idx) => self.execute_by_index(idx),
953            FunctionRef::Name(name) => self.execute_by_name(name),
954        }
955    }
956
957    pub fn execute_by_index(&mut self, function: i32) -> anyhow::Result<Value> {
958        let quakec_func = self
959            .functions
960            .get_by_index(function)?
961            .clone()
962            .try_into_qc()
963            .map_err(|_| anyhow::format_err!("Not a quakec function (TODO)"))?;
964
965        let out = self.execute_def(&quakec_func)?;
966        let value = VmValue::try_from(out)?;
967
968        self.to_value(value)
969    }
970
971    pub fn execute_by_name<F>(&mut self, function: F) -> anyhow::Result<Value>
972    where
973        F: AsRef<CStr>,
974    {
975        let quakec_func = self
976            .functions
977            .get_by_name(function)?
978            .clone()
979            .try_into_qc()
980            .map_err(|_| anyhow::format_err!("Not a quakec function (TODO)"))?;
981
982        let out = self.execute_def(&quakec_func)?;
983        let value = VmValue::try_from(out)?;
984
985        self.to_value(value)
986    }
987
988    // TODO: We can use the unsafe checkpoint API if just recursing becomes too slow.
989    pub fn execute_def(&mut self, function: &QuakeCFunctionDef) -> anyhow::Result<[VmScalar; 3]> {
990        let Self {
991            alloc,
992            memory,
993            backtrace,
994            context,
995            entity_def,
996            string_table,
997            functions,
998        } = self;
999
1000        alloc.scoped(move |alloc| {
1001            let mut new_memory = ExecutionMemory {
1002                local: function.ctx(&alloc),
1003                global: memory.global,
1004                last_ret: None,
1005            };
1006
1007            let mut dst_locals = function.body.locals.clone();
1008
1009            for (mut src, dst_size) in ARG_ADDRS.chunks(3).into_iter().zip(&function.args) {
1010                match dst_size.arg_size() {
1011                    ArgSize::Scalar => {
1012                        let src = src.next().unwrap();
1013                        let dst = dst_locals
1014                            .next()
1015                            .ok_or_else(|| anyhow::format_err!("Too few locals for arguments"))?;
1016                        new_memory.set(dst, memory.get(dbg!(src))?)?;
1017                    }
1018
1019                    ArgSize::Vector => {
1020                        for (src, dst) in src.zip(dst_locals.by_ref()) {
1021                            new_memory.set(dst, memory.get(src)?)?;
1022                        }
1023                    }
1024                }
1025            }
1026
1027            let mut out = ExecutionCtx {
1028                memory: new_memory,
1029                alloc,
1030                backtrace: BacktraceFrame(Some((&function.name, &*backtrace))),
1031                context: &mut **context,
1032                entity_def,
1033                string_table,
1034                functions,
1035            };
1036
1037            out.execute_internal()
1038        })
1039    }
1040
1041    // TODO
1042    pub fn backtrace(&self) -> impl Iterator<Item = &'_ CStr> + '_ {
1043        std::iter::successors(self.backtrace.0.as_ref(), |(_, prev)| prev.0.as_ref())
1044            .map(|(name, _)| *name)
1045    }
1046
1047    // TODO
1048    #[expect(dead_code)]
1049    pub fn print_backtrace(&self, force: bool) {
1050        let backtrace_var =
1051            std::env::var("RUST_LIB_BACKTRACE").or_else(|_| std::env::var("RUST_BACKTRACE"));
1052        let backtrace_enabled = matches!(backtrace_var.as_deref(), Err(_) | Ok("0"));
1053        if force || backtrace_enabled {
1054            for (depth, name) in self.backtrace().enumerate() {
1055                // TODO: More info about the function (e.g. builtin vs internal)
1056                println!("{}: {}", depth, name.to_string_lossy());
1057            }
1058        }
1059    }
1060}
1061
1062impl ExecutionCtx<'_> {
1063    pub fn get<I, O>(&self, index: I) -> anyhow::Result<O>
1064    where
1065        I: TryInto<usize>,
1066        I::Error: std::error::Error + Send + Sync + 'static,
1067        VmScalar: TryInto<O>,
1068        <VmScalar as TryInto<O>>::Error: std::error::Error + Send + Sync + 'static,
1069    {
1070        Ok(self.memory.get(index.try_into()?)?.try_into()?)
1071    }
1072
1073    pub fn set<I, V>(&mut self, index: I, value: V) -> anyhow::Result<()>
1074    where
1075        I: TryInto<usize>,
1076        I::Error: std::error::Error + Send + Sync + 'static,
1077        V: TryInto<VmScalar>,
1078        V::Error: std::error::Error + Send + Sync + 'static,
1079    {
1080        self.memory.set(index.try_into()?, value.try_into()?)
1081    }
1082
1083    /// Sets the "last return" global. QuakeC only allows 1 function return to be accessible
1084    /// at a given time.
1085    ///
1086    /// This can't be done with the regular `set` as this shouldn't be accessible by regular
1087    /// QuakeC code, only from the engine.
1088    pub fn set_return(&mut self, values: [VmScalar; 3]) {
1089        self.memory.last_ret = Some(values);
1090    }
1091
1092    pub fn get_vector<I, O>(&self, index: I) -> anyhow::Result<[O; 3]>
1093    where
1094        I: TryInto<usize>,
1095        I::Error: std::error::Error + Send + Sync + 'static,
1096        VmScalar: TryInto<O>,
1097        <VmScalar as TryInto<O>>::Error: std::error::Error + Send + Sync + 'static,
1098    {
1099        let index = index.try_into()?;
1100
1101        Ok([self.get(index)?, self.get(index + 1)?, self.get(index + 2)?])
1102    }
1103
1104    pub fn get_vec3<I>(&self, index: I) -> anyhow::Result<Vec3>
1105    where
1106        I: TryInto<usize>,
1107        I::Error: std::error::Error + Send + Sync + 'static,
1108    {
1109        let index = index.try_into()?;
1110
1111        Ok(self.get_vector::<_, f32>(index)?.into())
1112    }
1113
1114    pub fn set_vector<I, V>(&mut self, index: I, values: [V; 3]) -> anyhow::Result<()>
1115    where
1116        I: TryInto<usize>,
1117        I::Error: std::error::Error + Send + Sync + 'static,
1118        V: TryInto<VmScalar>,
1119        V::Error: std::error::Error + Send + Sync + 'static,
1120    {
1121        let index = index.try_into()?;
1122        let [v0, v1, v2] = values.map(|val| val.try_into());
1123        let values = [v0?, v1?, v2?];
1124        self.memory.set_vector(index, values)
1125    }
1126
1127    pub fn set_vec3<I, V>(&mut self, index: I, values: V) -> anyhow::Result<()>
1128    where
1129        I: TryInto<usize>,
1130        I::Error: std::error::Error + Send + Sync + 'static,
1131        V: TryInto<[f32; 3]>,
1132        V::Error: std::error::Error + Send + Sync + 'static,
1133    {
1134        let index = index.try_into()?;
1135        let values = values.try_into()?;
1136        self.memory.set_vector(index, values.map(Into::into))
1137    }
1138
1139    fn execute_internal(&mut self) -> anyhow::Result<[VmScalar; 3]> {
1140        let mut counter: usize = 0;
1141
1142        loop {
1143            match self.execute_statement(self.instr(counter)?)?.into() {
1144                ControlFlow::Continue(idx) => {
1145                    counter = counter
1146                        .checked_add_signed(idx)
1147                        .ok_or_else(|| anyhow::format_err!("Out-of-bounds instruction access"))?;
1148                }
1149                ControlFlow::Break(vals) => return Ok(vals),
1150            }
1151        }
1152    }
1153}