feat: add Vtable marker trait for CTask<V> type safety
Browse filesAdd unsafe trait Vtable as a bound on CTask<V> and CGameTask<V>,
preventing arbitrary types from being used as vtable parameters.
- *const c_void implements Vtable (the default/untyped case)
- #[vtable] proc macro auto-generates `impl Vtable for *const MyVTable`
- CTask<V: Vtable> and CGameTask<V: Vtable> enforce the bound
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/task/base.rs
CHANGED
|
@@ -105,7 +105,7 @@ crate::define_addresses! {
|
|
| 105 |
/// 0x1C: 0x563210 ProcessFrame
|
| 106 |
#[derive(FieldRegistry)]
|
| 107 |
#[repr(C)]
|
| 108 |
-
pub struct CTask<V:
|
| 109 |
/// 0x00: Pointer to virtual method table
|
| 110 |
pub vtable: V,
|
| 111 |
/// 0x04: Parent task in the hierarchy
|
|
@@ -141,6 +141,18 @@ pub struct CTask<V: 'static = *const core::ffi::c_void> {
|
|
| 141 |
|
| 142 |
const _: () = assert!(core::mem::size_of::<CTask>() == 0x30);
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
/// Trait for all task types in the CTask hierarchy.
|
| 145 |
///
|
| 146 |
/// Provides safe access to the underlying CTask fields regardless of
|
|
@@ -177,7 +189,7 @@ pub unsafe trait Task {
|
|
| 177 |
}
|
| 178 |
|
| 179 |
// Blanket impl for any CTask<V>
|
| 180 |
-
unsafe impl<V:
|
| 181 |
|
| 182 |
// ---------------------------------------------------------------------------
|
| 183 |
// Shared-data entity registry
|
|
|
|
| 105 |
/// 0x1C: 0x563210 ProcessFrame
|
| 106 |
#[derive(FieldRegistry)]
|
| 107 |
#[repr(C)]
|
| 108 |
+
pub struct CTask<V: Vtable = *const core::ffi::c_void> {
|
| 109 |
/// 0x00: Pointer to virtual method table
|
| 110 |
pub vtable: V,
|
| 111 |
/// 0x04: Parent task in the hierarchy
|
|
|
|
| 141 |
|
| 142 |
const _: () = assert!(core::mem::size_of::<CTask>() == 0x30);
|
| 143 |
|
| 144 |
+
/// Marker trait for types that can be used as vtable pointers in `CTask<V>`.
|
| 145 |
+
///
|
| 146 |
+
/// Implemented automatically by the `#[vtable]` proc macro for `*const MyVTable`.
|
| 147 |
+
/// Also implemented for `*const c_void` (the default/untyped case).
|
| 148 |
+
///
|
| 149 |
+
/// # Safety
|
| 150 |
+
/// Implementors must be pointer-sized types pointing to valid vtable data.
|
| 151 |
+
pub unsafe trait Vtable: 'static {}
|
| 152 |
+
|
| 153 |
+
// Default vtable type (untyped)
|
| 154 |
+
unsafe impl Vtable for *const core::ffi::c_void {}
|
| 155 |
+
|
| 156 |
/// Trait for all task types in the CTask hierarchy.
|
| 157 |
///
|
| 158 |
/// Provides safe access to the underlying CTask fields regardless of
|
|
|
|
| 189 |
}
|
| 190 |
|
| 191 |
// Blanket impl for any CTask<V>
|
| 192 |
+
unsafe impl<V: Vtable> Task for CTask<V> {}
|
| 193 |
|
| 194 |
// ---------------------------------------------------------------------------
|
| 195 |
// Shared-data entity registry
|
crates/openwa-core/src/task/game_task.rs
CHANGED
|
@@ -31,7 +31,7 @@ pub const CGAMETASK_SOUND_EMITTER_VT: u32 = SOUND_EMITTER_VTABLE;
|
|
| 31 |
/// Additional vtable (12 methods at offsets 0x1C-0x48 in vtable)
|
| 32 |
#[derive(FieldRegistry)]
|
| 33 |
#[repr(C)]
|
| 34 |
-
pub struct CGameTask<V:
|
| 35 |
/// 0x00-0x2F: Base CTask fields
|
| 36 |
pub base: CTask<V>,
|
| 37 |
/// 0x30-0x83: Subclass-specific data (84 bytes). Each CGameTask derivative
|
|
|
|
| 31 |
/// Additional vtable (12 methods at offsets 0x1C-0x48 in vtable)
|
| 32 |
#[derive(FieldRegistry)]
|
| 33 |
#[repr(C)]
|
| 34 |
+
pub struct CGameTask<V: super::base::Vtable = *const core::ffi::c_void> {
|
| 35 |
/// 0x00-0x2F: Base CTask fields
|
| 36 |
pub base: CTask<V>,
|
| 37 |
/// 0x30-0x83: Subclass-specific data (84 bytes). Each CGameTask derivative
|
crates/openwa-core/src/task/mod.rs
CHANGED
|
@@ -14,7 +14,7 @@ pub mod turn_game;
|
|
| 14 |
pub mod worm;
|
| 15 |
|
| 16 |
pub use arrow::{CTaskArrow, CTaskArrowVTable};
|
| 17 |
-
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable, Task};
|
| 18 |
pub use cloud::{CTaskCloud, CTaskCloudVTable};
|
| 19 |
pub use filter::{CTaskFilter, CTaskFilterVTable};
|
| 20 |
pub use fire::{CTaskFire, CTaskFireVTable};
|
|
@@ -29,7 +29,7 @@ pub use worm::{CTaskWorm, CTaskWormVTable};
|
|
| 29 |
|
| 30 |
// Task trait impls — safe access to CTask base regardless of inheritance depth.
|
| 31 |
// CTask<V> impl is in base.rs (blanket impl).
|
| 32 |
-
unsafe impl<V:
|
| 33 |
unsafe impl Task for CTaskTeam {}
|
| 34 |
unsafe impl Task for CTaskTurnGame {}
|
| 35 |
unsafe impl Task for CTaskFilter {}
|
|
|
|
| 14 |
pub mod worm;
|
| 15 |
|
| 16 |
pub use arrow::{CTaskArrow, CTaskArrowVTable};
|
| 17 |
+
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable, Task, Vtable};
|
| 18 |
pub use cloud::{CTaskCloud, CTaskCloudVTable};
|
| 19 |
pub use filter::{CTaskFilter, CTaskFilterVTable};
|
| 20 |
pub use fire::{CTaskFire, CTaskFireVTable};
|
|
|
|
| 29 |
|
| 30 |
// Task trait impls — safe access to CTask base regardless of inheritance depth.
|
| 31 |
// CTask<V> impl is in base.rs (blanket impl).
|
| 32 |
+
unsafe impl<V: Vtable> Task for CGameTask<V> {}
|
| 33 |
unsafe impl Task for CTaskTeam {}
|
| 34 |
unsafe impl Task for CTaskTurnGame {}
|
| 35 |
unsafe impl Task for CTaskFilter {}
|
crates/openwa-derive/src/lib.rs
CHANGED
|
@@ -311,6 +311,9 @@ fn vtable_impl(
|
|
| 311 |
|
| 312 |
#va_items
|
| 313 |
|
|
|
|
|
|
|
|
|
|
| 314 |
/// Bind this vtable's known methods as wrapper methods on a class struct.
|
| 315 |
///
|
| 316 |
/// Usage: `bind_VtableName!(ClassName, vtable_field_name);`
|
|
|
|
| 311 |
|
| 312 |
#va_items
|
| 313 |
|
| 314 |
+
// Mark *const ThisVtable as a valid vtable pointer type for CTask<V>.
|
| 315 |
+
unsafe impl openwa_core::task::Vtable for *const #struct_name {}
|
| 316 |
+
|
| 317 |
/// Bind this vtable's known methods as wrapper methods on a class struct.
|
| 318 |
///
|
| 319 |
/// Usage: `bind_VtableName!(ClassName, vtable_field_name);`
|