Skip to content

Commit 898c5a5

Browse files
committed
Make mutable Script Instance functions reentrant
1 parent fbc1426 commit 898c5a5

File tree

16 files changed

+251
-162
lines changed

16 files changed

+251
-162
lines changed

godot-codegen/src/generator/classes.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,10 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
182182
#internal_methods
183183
#constants
184184
}
185-
impl crate::obj::GodotClass for #class_name {
185+
impl crate::obj::WithBase for #class_name {
186186
type Base = #base_ty;
187-
187+
}
188+
impl crate::obj::GodotClass for #class_name {
188189
fn class_name() -> ClassName {
189190
ClassName::from_ascii_cstr(#class_name_cstr)
190191
}

godot-core/src/engine/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use crate::gen::central::global;
1515
pub use crate::gen::classes::*;
1616
pub use crate::gen::utilities;
1717
pub use io::*;
18-
pub use script_instance::{create_script_instance, ScriptInstance};
18+
pub use script_instance::{create_script_instance, ScriptInstance, SiMut};
1919

2020
use crate::builtin::meta::CallContext;
2121
use crate::sys;

godot-core/src/engine/script_instance.rs

+110-110
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use std::cell::RefCell;
98
use std::collections::HashMap;
109
use std::ffi::c_void;
10+
use std::ops::{Deref, DerefMut};
11+
use std::pin::Pin;
1112
use std::sync::Mutex;
1213

14+
use godot_cell::{GdCell, MutGuard};
15+
1316
use crate::builtin::meta::{MethodInfo, PropertyInfo};
1417
use crate::builtin::{GString, StringName, Variant, VariantType};
15-
use crate::obj::Gd;
18+
use crate::obj::{Base, BaseMut, BaseRef, Gd, WithBase};
1619
use crate::sys;
1720

1821
use super::{Script, ScriptLanguage};
@@ -65,14 +68,14 @@ use super::{Script, ScriptLanguage};
6568
/// }
6669
/// }
6770
/// ```
68-
pub trait ScriptInstance {
71+
pub trait ScriptInstance: WithBase + Sized {
6972
/// Name of the new class the script implements.
7073
fn class_name(&self) -> GString;
7174

7275
/// Property setter for Godot's virtual dispatch system.
7376
///
7477
/// The engine will call this function when it wants to change a property on the script.
75-
fn set_property(&mut self, name: StringName, value: &Variant) -> bool;
78+
fn set_property(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
7679

7780
/// Property getter for Godot's virtual dispatch system.
7881
///
@@ -93,7 +96,7 @@ pub trait ScriptInstance {
9396
/// It's important that the script does not cause a second call to this function while executing a method call. This would result in a panic.
9497
// TODO: map the sys::GDExtensionCallErrorType to some public API type.
9598
fn call(
96-
&mut self,
99+
this: SiMut<Self>,
97100
method: StringName,
98101
args: &[&Variant],
99102
) -> Result<Variant, sys::GDExtensionCallErrorType>;
@@ -136,81 +139,7 @@ pub trait ScriptInstance {
136139
fn property_get_fallback(&self, name: StringName) -> Option<Variant>;
137140

138141
/// The engine may call this function if ScriptLanguage::is_placeholder_fallback_enabled is enabled.
139-
fn property_set_fallback(&mut self, name: StringName, value: &Variant) -> bool;
140-
}
141-
142-
impl<T: ScriptInstance + ?Sized> ScriptInstance for Box<T> {
143-
fn class_name(&self) -> GString {
144-
self.as_ref().class_name()
145-
}
146-
147-
fn set_property(&mut self, name: StringName, value: &Variant) -> bool {
148-
self.as_mut().set_property(name, value)
149-
}
150-
151-
fn get_property(&self, name: StringName) -> Option<Variant> {
152-
self.as_ref().get_property(name)
153-
}
154-
155-
fn get_property_list(&self) -> Vec<PropertyInfo> {
156-
self.as_ref().get_property_list()
157-
}
158-
159-
fn get_method_list(&self) -> Vec<MethodInfo> {
160-
self.as_ref().get_method_list()
161-
}
162-
163-
fn call(
164-
&mut self,
165-
method: StringName,
166-
args: &[&Variant],
167-
) -> Result<Variant, sys::GDExtensionCallErrorType> {
168-
self.as_mut().call(method, args)
169-
}
170-
171-
fn is_placeholder(&self) -> bool {
172-
self.as_ref().is_placeholder()
173-
}
174-
175-
fn has_method(&self, method: StringName) -> bool {
176-
self.as_ref().has_method(method)
177-
}
178-
179-
fn get_script(&self) -> &Gd<Script> {
180-
self.as_ref().get_script()
181-
}
182-
183-
fn get_property_type(&self, name: StringName) -> VariantType {
184-
self.as_ref().get_property_type(name)
185-
}
186-
187-
fn to_string(&self) -> GString {
188-
self.as_ref().to_string()
189-
}
190-
191-
fn get_property_state(&self) -> Vec<(StringName, Variant)> {
192-
self.as_ref().get_property_state()
193-
}
194-
195-
fn get_language(&self) -> Gd<ScriptLanguage> {
196-
self.as_ref().get_language()
197-
}
198-
199-
fn on_refcount_decremented(&self) -> bool {
200-
self.as_ref().on_refcount_decremented()
201-
}
202-
203-
fn on_refcount_incremented(&self) {
204-
self.as_ref().on_refcount_incremented();
205-
}
206-
207-
fn property_get_fallback(&self, name: StringName) -> Option<Variant> {
208-
self.as_ref().property_get_fallback(name)
209-
}
210-
211-
fn property_set_fallback(&mut self, name: StringName, value: &Variant) -> bool {
212-
self.as_mut().property_set_fallback(name, value)
213-
}
142+
fn property_set_fallback(this: SiMut<Self>, name: StringName, value: &Variant) -> bool;
214143
}
215144

216145
#[cfg(before_api = "4.2")]
@@ -219,10 +148,11 @@ type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo;
219148
type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo2;
220149

221150
struct ScriptInstanceData<T: ScriptInstance> {
222-
inner: RefCell<T>,
151+
inner: Pin<Box<GdCell<T>>>,
223152
script_instance_ptr: *mut ScriptInstanceInfo,
224153
property_list: Mutex<HashMap<*const sys::GDExtensionPropertyInfo, Vec<PropertyInfo>>>,
225154
method_list: Mutex<HashMap<*const sys::GDExtensionMethodInfo, Vec<MethodInfo>>>,
155+
base: Base<T::Base>,
226156
}
227157

228158
impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
@@ -243,7 +173,10 @@ impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
243173
///
244174
/// The exact GDExtension type of the pointer is `sys::GDExtensionScriptInstancePtr`, but you can treat it like an opaque pointer.
245175
#[must_use]
246-
pub fn create_script_instance<T: ScriptInstance>(rs_instance: T) -> *mut c_void {
176+
pub fn create_script_instance<T: ScriptInstance>(
177+
rust_instance: T,
178+
for_object: Gd<T::Base>,
179+
) -> *mut c_void {
247180
// Field grouping matches C header.
248181
let gd_instance = ScriptInstanceInfo {
249182
set_func: Some(script_instance_info::set_property_func::<T>),
@@ -292,10 +225,13 @@ pub fn create_script_instance<T: ScriptInstance>(rs_instance: T) -> *mut c_void
292225
let instance_ptr = Box::into_raw(Box::new(gd_instance));
293226

294227
let data = ScriptInstanceData {
295-
inner: RefCell::new(rs_instance),
228+
inner: GdCell::new(rust_instance),
296229
script_instance_ptr: instance_ptr,
297230
property_list: Default::default(),
298231
method_list: Default::default(),
232+
// SAFETY: The script instance is always freed before the base object is destroyed. The weak reference should therefore never be
233+
// accessed after it has been freed.
234+
base: unsafe { Base::from_gd(&for_object) },
299235
};
300236

301237
let data_ptr = Box::into_raw(Box::new(data));
@@ -318,32 +254,94 @@ pub fn create_script_instance<T: ScriptInstance>(rs_instance: T) -> *mut c_void
318254
}
319255
}
320256

257+
/// Ref-guard for a mutable reference to T where T implements `ScriptInstance`.
258+
pub struct SiMut<'a, T: ScriptInstance> {
259+
mut_ref: &'a mut T,
260+
cell: Pin<&'a GdCell<T>>,
261+
base_ref: &'a Base<T::Base>,
262+
}
263+
264+
impl<'a, T: ScriptInstance> SiMut<'a, T> {
265+
fn new(
266+
cell: Pin<&'a GdCell<T>>,
267+
cell_guard: &'a mut MutGuard<T>,
268+
base_ref: &'a Base<T::Base>,
269+
) -> Self {
270+
let mut_ref = cell_guard.deref_mut();
271+
272+
Self {
273+
mut_ref,
274+
cell,
275+
base_ref,
276+
}
277+
}
278+
}
279+
280+
impl<'a, T: ScriptInstance> Deref for SiMut<'a, T> {
281+
type Target = T;
282+
283+
fn deref(&self) -> &Self::Target {
284+
self.mut_ref
285+
}
286+
}
287+
288+
impl<'a, T: ScriptInstance> DerefMut for SiMut<'a, T> {
289+
fn deref_mut(&mut self) -> &mut Self::Target {
290+
self.mut_ref
291+
}
292+
}
293+
294+
impl<'a, T: ScriptInstance> SiMut<'a, T> {
295+
pub fn base(&self) -> BaseRef<T> {
296+
BaseRef::new(self.base_ref.to_gd(), self.mut_ref)
297+
}
298+
299+
pub fn base_mut(&mut self) -> BaseMut<T> {
300+
let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
301+
302+
BaseMut::new(self.base_ref.to_gd(), guard)
303+
}
304+
}
305+
321306
mod script_instance_info {
322307
use std::any::type_name;
323-
use std::cell::{BorrowError, Ref, RefMut};
324308
use std::ffi::c_void;
325309
use std::mem::ManuallyDrop;
310+
use std::pin::Pin;
311+
312+
use godot_cell::{GdCell, RefGuard};
326313

327314
use crate::builtin::{GString, StringName, Variant};
328315
use crate::engine::ScriptLanguage;
329316
use crate::obj::Gd;
330317
use crate::private::handle_panic;
331318
use crate::sys;
332319

333-
use super::{ScriptInstance, ScriptInstanceData};
320+
use super::{ScriptInstance, ScriptInstanceData, SiMut};
334321

335-
fn borrow_instance_mut<T: ScriptInstance>(instance: &ScriptInstanceData<T>) -> RefMut<'_, T> {
336-
instance.inner.borrow_mut()
322+
fn borrow_panic<T: ScriptInstance, R>(err: Box<dyn std::error::Error>) -> R {
323+
panic!(
324+
"\
325+
ScriptInstance borrow failed, already bound; T = {}.\n \
326+
Make sure to use `SiMut::base_mut()` when possible.\n \
327+
Details: {err}.\
328+
",
329+
type_name::<T>(),
330+
);
337331
}
338332

339-
fn borrow_instance<T: ScriptInstance>(instance: &ScriptInstanceData<T>) -> Ref<'_, T> {
340-
instance.inner.borrow()
333+
fn borrow_instance<T: ScriptInstance>(instance: &ScriptInstanceData<T>) -> RefGuard<'_, T> {
334+
instance
335+
.inner
336+
.as_ref()
337+
.borrow()
338+
.unwrap_or_else(borrow_panic::<T, _>)
341339
}
342340

343-
fn try_borrow_instance<T: ScriptInstance>(
341+
fn borrow_instance_cell<T: ScriptInstance>(
344342
instance: &ScriptInstanceData<T>,
345-
) -> Result<Ref<'_, T>, BorrowError> {
346-
instance.inner.try_borrow()
343+
) -> Pin<&GdCell<T>> {
344+
instance.inner.as_ref()
347345
}
348346

349347
/// # Safety
@@ -446,8 +444,15 @@ mod script_instance_info {
446444

447445
let result = handle_panic(ctx, || {
448446
let instance = instance_data_as_script_instance::<T>(p_instance);
447+
let cell = borrow_instance_cell(instance);
448+
let mut guard = cell
449+
.as_ref()
450+
.borrow_mut()
451+
.unwrap_or_else(borrow_panic::<T, _>);
452+
453+
let instance_guard = SiMut::new(cell, &mut guard, &instance.base);
449454

450-
borrow_instance_mut(instance).set_property(name, value)
455+
ScriptInstance::set_property(instance_guard, name, value)
451456
})
452457
// Unwrapping to a default of false, to indicate that the assignment is not handled by the script.
453458
.unwrap_or_default();
@@ -619,7 +624,12 @@ mod script_instance_info {
619624

620625
let result = handle_panic(ctx, || {
621626
let instance = instance_data_as_script_instance::<T>(p_self);
622-
borrow_instance_mut(instance).call(method.clone(), args)
627+
let cell = borrow_instance_cell(instance);
628+
let mut guard = cell.borrow_mut().unwrap_or_else(borrow_panic::<T, _>);
629+
630+
let instance_guard = SiMut::new(cell, &mut guard, &instance.base);
631+
632+
ScriptInstance::call(instance_guard, method.clone(), args)
623633
});
624634

625635
match result {
@@ -779,22 +789,9 @@ mod script_instance_info {
779789
let string = handle_panic(ctx, || {
780790
let instance = instance_data_as_script_instance::<T>(p_instance);
781791

782-
let Ok(inner) = try_borrow_instance(instance) else {
783-
// to_string of a script instance can be called when calling to_string on the owning base object. In this case we pretend like we
784-
// can't handle the call and leave r_is_valid at it's default value of false.
785-
//
786-
// This is one of the only members of GDExtensionScripInstanceInfo which appeares to be called from an API function
787-
// (beside get_func, set_func, call_func). The unexpected behavior here is that it is being called as a replacement of Godot's
788-
// Object::to_string for the owner object. This then also happens when trying to call to_string on the base object inside a
789-
// script, which feels wrong, and most importantly, would obviously cause a panic when acquiring the Ref guard.
790-
791-
return None;
792-
};
793-
794-
Some(inner.to_string())
792+
borrow_instance(instance).to_string()
795793
})
796-
.ok()
797-
.flatten();
794+
.ok();
798795

799796
let Some(string) = string else {
800797
return;
@@ -965,8 +962,11 @@ mod script_instance_info {
965962

966963
let result = handle_panic(ctx, || {
967964
let instance = instance_data_as_script_instance::<T>(p_instance);
965+
let cell = borrow_instance_cell(instance);
966+
let mut guard = cell.borrow_mut().unwrap_or_else(borrow_panic::<T, _>);
968967

969-
borrow_instance_mut(instance).property_set_fallback(name, value)
968+
let instance_guard = SiMut::new(cell, &mut guard, &instance.base);
969+
ScriptInstance::property_set_fallback(instance_guard, name, value)
970970
})
971971
.unwrap_or_default();
972972

godot-core/src/obj/base.rs

+6
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ impl<T: GodotClass> Base<T> {
4141
Base::from_obj(Gd::from_obj_sys_weak(base.as_gd().obj_sys()))
4242
}
4343

44+
/// # Safety
45+
/// The returned Base is a weak pointer, so holding it will not keep the object alive. It must not be accessed after the object is destroyed.
46+
pub(crate) unsafe fn from_gd(gd: &Gd<T>) -> Self {
47+
Base::from_obj(Gd::from_obj_sys_weak(gd.obj_sys()))
48+
}
49+
4450
// Note: not &mut self, to only borrow one field and not the entire struct
4551
pub(crate) unsafe fn from_sys(base_ptr: sys::GDExtensionObjectPtr) -> Self {
4652
assert!(!base_ptr.is_null(), "instance base is null pointer");

0 commit comments

Comments
 (0)