Skip to content

Commit 3d29c6c

Browse files
authored
Merge pull request #650 from TitanNano/jovan/script_instance_soundness
Store Property / Method list in ScriptInstanceData #647
2 parents e7dd8b8 + 4353f12 commit 3d29c6c

File tree

3 files changed

+58
-23
lines changed

3 files changed

+58
-23
lines changed

godot-core/src/builtin/meta/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ where
246246
/// Rusty abstraction of `sys::GDExtensionPropertyInfo`.
247247
///
248248
/// Keeps the actual allocated values (the `sys` equivalent only keeps pointers, which fall out of scope).
249-
#[derive(Debug)]
249+
#[derive(Debug, Clone)]
250250
// Note: is not #[non_exhaustive], so adding fields is a breaking change. Mostly used internally at the moment though.
251251
pub struct PropertyInfo {
252252
pub variant_type: VariantType,
@@ -288,7 +288,7 @@ impl PropertyInfo {
288288
}
289289
}
290290

291-
#[derive(Debug)]
291+
#[derive(Debug, Clone)]
292292
pub struct MethodInfo {
293293
pub id: i32,
294294
pub method_name: StringName,

godot-core/src/engine/script_instance.rs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
*/
77

88
use std::cell::RefCell;
9+
use std::collections::HashMap;
910
use std::ffi::c_void;
11+
use std::sync::Mutex;
1012

1113
use crate::builtin::meta::{MethodInfo, PropertyInfo};
1214
use crate::builtin::{GString, StringName, Variant, VariantType};
@@ -78,10 +80,10 @@ pub trait ScriptInstance {
7880
fn get_property(&self, name: StringName) -> Option<Variant>;
7981

8082
/// A list of all the properties a script exposes to the engine.
81-
fn get_property_list(&self) -> &[PropertyInfo];
83+
fn get_property_list(&self) -> Vec<PropertyInfo>;
8284

8385
/// A list of all the methods a script exposes to the engine.
84-
fn get_method_list(&self) -> &[MethodInfo];
86+
fn get_method_list(&self) -> Vec<MethodInfo>;
8587

8688
/// Method invoker for Godot's virtual dispatch system. The engine will call this function when it wants to call a method on the script.
8789
///
@@ -150,11 +152,11 @@ impl<T: ScriptInstance + ?Sized> ScriptInstance for Box<T> {
150152
self.as_ref().get_property(name)
151153
}
152154

153-
fn get_property_list(&self) -> &[PropertyInfo] {
155+
fn get_property_list(&self) -> Vec<PropertyInfo> {
154156
self.as_ref().get_property_list()
155157
}
156158

157-
fn get_method_list(&self) -> &[MethodInfo] {
159+
fn get_method_list(&self) -> Vec<MethodInfo> {
158160
self.as_ref().get_method_list()
159161
}
160162

@@ -219,6 +221,8 @@ type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
219221
struct ScriptInstanceData<T: ScriptInstance> {
220222
inner: RefCell<T>,
221223
script_instance_ptr: *mut ScriptInstanceInfo,
224+
property_list: Mutex<HashMap<*const sys::GDExtensionPropertyInfo, Vec<PropertyInfo>>>,
225+
method_list: Mutex<HashMap<*const sys::GDExtensionMethodInfo, Vec<MethodInfo>>>,
222226
}
223227

224228
impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
@@ -290,6 +294,8 @@ pub fn create_script_instance<T: ScriptInstance>(rs_instance: T) -> *mut c_void
290294
let data = ScriptInstanceData {
291295
inner: RefCell::new(rs_instance),
292296
script_instance_ptr: instance_ptr,
297+
property_list: Default::default(),
298+
method_list: Default::default(),
293299
};
294300

295301
let data_ptr = Box::into_raw(Box::new(data));
@@ -364,13 +370,13 @@ mod script_instance_info {
364370
/// # Safety
365371
///
366372
/// - The returned `*const T` is guaranteed to point to a list that has an equal length and capacity.
367-
fn transfer_ptr_list_to_godot<T>(ptr_list: Vec<T>, list_length: &mut u32) -> *const T {
373+
fn transfer_ptr_list_to_godot<T>(ptr_list: Box<[T]>, list_length: &mut u32) -> *mut T {
368374
*list_length = ptr_list.len() as u32;
369375

370-
let ptr = Box::into_raw(ptr_list.into_boxed_slice());
376+
let ptr = Box::into_raw(ptr_list);
371377

372378
// SAFETY: `ptr` was just created in the line above and should be safe to dereference.
373-
unsafe { (*ptr).as_ptr() }
379+
unsafe { (*ptr).as_mut_ptr() }
374380
}
375381

376382
/// The returned pointer's lifetime is equal to the lifetime of `script`
@@ -487,23 +493,32 @@ mod script_instance_info {
487493
) -> *const sys::GDExtensionPropertyInfo {
488494
let ctx = || format!("error when calling {}::get_property_list", type_name::<T>());
489495

490-
let property_list = handle_panic(ctx, || {
496+
let (property_list, property_sys_list) = handle_panic(ctx, || {
491497
let instance = instance_data_as_script_instance::<T>(p_instance);
492498

493-
let property_list: Vec<_> = borrow_instance(instance)
494-
.get_property_list()
499+
let property_list = borrow_instance(instance).get_property_list();
500+
501+
let property_sys_list: Box<[_]> = property_list
495502
.iter()
496503
.map(|prop| prop.property_sys())
497504
.collect();
498505

499-
property_list
506+
(property_list, property_sys_list)
500507
})
501508
.unwrap_or_default();
502509

503510
// SAFETY: list_length has to be a valid pointer to a u32.
504511
let list_length = unsafe { &mut *r_count };
512+
let return_pointer = transfer_ptr_list_to_godot(property_sys_list, list_length);
513+
514+
let instance = instance_data_as_script_instance::<T>(p_instance);
515+
instance
516+
.property_list
517+
.lock()
518+
.expect("Mutex should not be poisoned")
519+
.insert(return_pointer, property_list);
505520

506-
transfer_ptr_list_to_godot(property_list, list_length)
521+
return_pointer
507522
}
508523

509524
/// # Safety
@@ -516,23 +531,33 @@ mod script_instance_info {
516531
) -> *const sys::GDExtensionMethodInfo {
517532
let ctx = || format!("error when calling {}::get_method_list", type_name::<T>());
518533

519-
let method_list = handle_panic(ctx, || {
534+
let (method_list, method_sys_list) = handle_panic(ctx, || {
520535
let instance = instance_data_as_script_instance::<T>(p_instance);
521536

522-
let method_list: Vec<_> = borrow_instance(instance)
523-
.get_method_list()
537+
let method_list = borrow_instance(instance).get_method_list();
538+
539+
let method_sys_list = method_list
524540
.iter()
525541
.map(|method| method.method_sys())
526542
.collect();
527543

528-
method_list
544+
(method_list, method_sys_list)
529545
})
530546
.unwrap_or_default();
531547

532548
// SAFETY: list_length has to be a valid pointer to a u32.
533549
let list_length = unsafe { &mut *r_count };
550+
let return_pointer = transfer_ptr_list_to_godot(method_sys_list, list_length);
551+
552+
let instance = instance_data_as_script_instance::<T>(p_instance);
534553

535-
transfer_ptr_list_to_godot(method_list, list_length)
554+
instance
555+
.method_list
556+
.lock()
557+
.expect("mutex should not be poisoned")
558+
.insert(return_pointer, method_list);
559+
560+
return_pointer
536561
}
537562

538563
/// # Safety
@@ -561,6 +586,16 @@ mod script_instance_info {
561586
// and therefore should have the same length as before. get_propery_list_func
562587
// also guarantees that both vector length and capacity are equal.
563588
let _drop = transfer_ptr_list_from_godot(p_prop_info, length);
589+
590+
// now drop the backing data
591+
let instance = instance_data_as_script_instance::<T>(p_instance);
592+
593+
let _drop = instance
594+
.property_list
595+
.lock()
596+
.expect("mutex should not be poisoned")
597+
.remove(&p_prop_info)
598+
.expect("we can not free a list that has not been allocated");
564599
}
565600

566601
/// # Safety

itest/rust/src/builtin_tests/script/script_instance_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ impl ScriptInstance for TestScriptInstance {
116116
}
117117
}
118118

119-
fn get_property_list(&self) -> &[PropertyInfo] {
120-
&self.prop_list
119+
fn get_property_list(&self) -> Vec<PropertyInfo> {
120+
self.prop_list.clone()
121121
}
122122

123-
fn get_method_list(&self) -> &[MethodInfo] {
124-
&self.method_list
123+
fn get_method_list(&self) -> Vec<MethodInfo> {
124+
self.method_list.clone()
125125
}
126126

127127
fn call(

0 commit comments

Comments
 (0)