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#[derive(Debug)]
46struct CallArgs<T>(T);
47
48type HashMap<K, V> = hashbrown::HashMap<K, V, std::hash::BuildHasherDefault<hash32::FnvHasher>>;
49
50impl 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#[derive(Debug, Clone, PartialEq)]
74pub enum Value {
75 Void,
78 Entity(EntityRef),
80 Function(Arc<dyn ErasedFunction>),
82 Float(f32),
84 Vector(Vec3),
86 String(Arc<CStr>),
89}
90
91#[derive(Snafu, Debug, Copy, Clone, PartialEq, Eq)]
93pub struct GetValueError {
94 pub field: VectorField,
96}
97
98#[derive(Snafu, Debug, Copy, Clone, PartialEq, Eq)]
100pub enum SetValueError {
101 NoSuchField {
103 field: VectorField,
105 },
106 TypeError {
108 expected: Type,
111 found: Type,
113 },
114}
115
116impl Value {
117 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 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 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 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
313pub trait QuakeCArgs: fmt::Debug {
317 type Error: std::error::Error;
319
320 fn nth(&self, index: usize) -> Result<Value, Self::Error>;
322}
323
324#[derive(Debug)]
326pub enum ArgError {
327 ArgOutOfRange(usize),
329 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#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
444pub enum Type {
445 AnyScalar,
449 Void,
453 Entity,
455 Function,
457 Vector,
459 Float,
461 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
537pub struct QuakeCVm {
539 progs: Progs,
540}
541
542#[derive(Clone, PartialEq, Debug)]
544pub enum FunctionRef {
545 Offset(i32),
548 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 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 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 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 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
762trait 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
871pub trait AsErasedContext {
877 fn as_erased_mut(&mut self) -> &mut dyn ErasedContext;
879 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 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 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 #[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 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 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}