5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- use std:: cell:: RefCell ;
9
8
use std:: collections:: HashMap ;
10
9
use std:: ffi:: c_void;
10
+ use std:: ops:: { Deref , DerefMut } ;
11
+ use std:: pin:: Pin ;
11
12
use std:: sync:: Mutex ;
12
13
14
+ use godot_cell:: { GdCell , MutGuard } ;
15
+
13
16
use crate :: builtin:: meta:: { MethodInfo , PropertyInfo } ;
14
17
use crate :: builtin:: { GString , StringName , Variant , VariantType } ;
15
- use crate :: obj:: Gd ;
18
+ use crate :: obj:: { Base , BaseMut , BaseRef , Gd , WithBase } ;
16
19
use crate :: sys;
17
20
18
21
use super :: { Script , ScriptLanguage } ;
@@ -65,14 +68,14 @@ use super::{Script, ScriptLanguage};
65
68
/// }
66
69
/// }
67
70
/// ```
68
- pub trait ScriptInstance {
71
+ pub trait ScriptInstance : WithBase + Sized {
69
72
/// Name of the new class the script implements.
70
73
fn class_name ( & self ) -> GString ;
71
74
72
75
/// Property setter for Godot's virtual dispatch system.
73
76
///
74
77
/// 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 ;
76
79
77
80
/// Property getter for Godot's virtual dispatch system.
78
81
///
@@ -93,7 +96,7 @@ pub trait ScriptInstance {
93
96
/// 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.
94
97
// TODO: map the sys::GDExtensionCallErrorType to some public API type.
95
98
fn call (
96
- & mut self ,
99
+ this : SiMut < Self > ,
97
100
method : StringName ,
98
101
args : & [ & Variant ] ,
99
102
) -> Result < Variant , sys:: GDExtensionCallErrorType > ;
@@ -136,81 +139,7 @@ pub trait ScriptInstance {
136
139
fn property_get_fallback ( & self , name : StringName ) -> Option < Variant > ;
137
140
138
141
/// 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 ;
214
143
}
215
144
216
145
#[ cfg( before_api = "4.2" ) ]
@@ -219,10 +148,11 @@ type ScriptInstanceInfo = sys::GDExtensionScriptInstanceInfo;
219
148
type ScriptInstanceInfo = sys:: GDExtensionScriptInstanceInfo2 ;
220
149
221
150
struct ScriptInstanceData < T : ScriptInstance > {
222
- inner : RefCell < T > ,
151
+ inner : Pin < Box < GdCell < T > > > ,
223
152
script_instance_ptr : * mut ScriptInstanceInfo ,
224
153
property_list : Mutex < HashMap < * const sys:: GDExtensionPropertyInfo , Vec < PropertyInfo > > > ,
225
154
method_list : Mutex < HashMap < * const sys:: GDExtensionMethodInfo , Vec < MethodInfo > > > ,
155
+ base : Base < T :: Base > ,
226
156
}
227
157
228
158
impl < T : ScriptInstance > Drop for ScriptInstanceData < T > {
@@ -243,7 +173,10 @@ impl<T: ScriptInstance> Drop for ScriptInstanceData<T> {
243
173
///
244
174
/// The exact GDExtension type of the pointer is `sys::GDExtensionScriptInstancePtr`, but you can treat it like an opaque pointer.
245
175
#[ 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
+ rs_instance : T ,
178
+ for_object : Gd < T :: Base > ,
179
+ ) -> * mut c_void {
247
180
// Field grouping matches C header.
248
181
let gd_instance = ScriptInstanceInfo {
249
182
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
292
225
let instance_ptr = Box :: into_raw ( Box :: new ( gd_instance) ) ;
293
226
294
227
let data = ScriptInstanceData {
295
- inner : RefCell :: new ( rs_instance) ,
228
+ inner : GdCell :: new ( rs_instance) ,
296
229
script_instance_ptr : instance_ptr,
297
230
property_list : Default :: default ( ) ,
298
231
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) } ,
299
235
} ;
300
236
301
237
let data_ptr = Box :: into_raw ( Box :: new ( data) ) ;
@@ -318,32 +254,99 @@ pub fn create_script_instance<T: ScriptInstance>(rs_instance: T) -> *mut c_void
318
254
}
319
255
}
320
256
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
+
321
306
mod script_instance_info {
322
307
use std:: any:: type_name;
323
- use std:: cell:: { BorrowError , Ref , RefMut } ;
324
308
use std:: ffi:: c_void;
325
309
use std:: mem:: ManuallyDrop ;
310
+ use std:: ops:: Deref ;
311
+ use std:: pin:: Pin ;
312
+
313
+ use godot_cell:: { GdCell , RefGuard } ;
326
314
327
315
use crate :: builtin:: { GString , StringName , Variant } ;
328
316
use crate :: engine:: ScriptLanguage ;
329
317
use crate :: obj:: Gd ;
330
318
use crate :: private:: handle_panic;
331
319
use crate :: sys;
332
320
333
- use super :: { ScriptInstance , ScriptInstanceData } ;
334
-
335
- fn borrow_instance_mut < T : ScriptInstance > ( instance : & ScriptInstanceData < T > ) -> RefMut < ' _ , T > {
336
- instance. inner . borrow_mut ( )
321
+ use super :: { ScriptInstance , ScriptInstanceData , SiMut } ;
322
+
323
+ macro_rules! borrow_panic {
324
+ ( $type: ty) => {
325
+ |err| {
326
+ panic!(
327
+ "\
328
+ ScriptInstance borrow failed, already bound; T = {}.\n \
329
+ Make sure to use `SiMut::base_mut()` when possible.\n \
330
+ Details: {err}.\
331
+ ",
332
+ type_name:: <$type>( ) ,
333
+ )
334
+ }
335
+ } ;
337
336
}
338
337
339
- fn borrow_instance < T : ScriptInstance > ( instance : & ScriptInstanceData < T > ) -> Ref < ' _ , T > {
340
- instance. inner . borrow ( )
338
+ fn borrow_instance < T : ScriptInstance > ( instance : & ScriptInstanceData < T > ) -> RefGuard < ' _ , T > {
339
+ instance
340
+ . inner
341
+ . as_ref ( )
342
+ . borrow ( )
343
+ . unwrap_or_else ( borrow_panic ! ( T ) )
341
344
}
342
345
343
- fn try_borrow_instance < T : ScriptInstance > (
346
+ fn borrow_instance_cell < T : ScriptInstance > (
344
347
instance : & ScriptInstanceData < T > ,
345
- ) -> Result < Ref < ' _ , T > , BorrowError > {
346
- instance. inner . try_borrow ( )
348
+ ) -> Pin < & GdCell < T > > {
349
+ instance. inner . as_ref ( )
347
350
}
348
351
349
352
/// # Safety
@@ -446,8 +449,12 @@ mod script_instance_info {
446
449
447
450
let result = handle_panic ( ctx, || {
448
451
let instance = instance_data_as_script_instance :: < T > ( p_instance) ;
452
+ let cell = borrow_instance_cell ( instance) ;
453
+ let mut guard = cell. as_ref ( ) . borrow_mut ( ) . unwrap_or_else ( borrow_panic ! ( T ) ) ;
454
+
455
+ let instance_guard = SiMut :: new ( cell, & mut guard, & instance. base ) ;
449
456
450
- borrow_instance_mut ( instance ) . set_property ( name, value)
457
+ ScriptInstance :: set_property ( instance_guard , name, value)
451
458
} )
452
459
// Unwrapping to a default of false, to indicate that the assignment is not handled by the script.
453
460
. unwrap_or_default ( ) ;
@@ -619,7 +626,12 @@ mod script_instance_info {
619
626
620
627
let result = handle_panic ( ctx, || {
621
628
let instance = instance_data_as_script_instance :: < T > ( p_self) ;
622
- borrow_instance_mut ( instance) . call ( method. clone ( ) , args)
629
+ let cell = borrow_instance_cell ( instance) ;
630
+ let mut guard = cell. borrow_mut ( ) . unwrap_or_else ( borrow_panic ! ( T ) ) ;
631
+
632
+ let instance_guard = SiMut :: new ( cell, & mut guard, & instance. base ) ;
633
+
634
+ ScriptInstance :: call ( instance_guard, method. clone ( ) , args)
623
635
} ) ;
624
636
625
637
match result {
@@ -779,22 +791,9 @@ mod script_instance_info {
779
791
let string = handle_panic ( ctx, || {
780
792
let instance = instance_data_as_script_instance :: < T > ( p_instance) ;
781
793
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 ( ) )
794
+ borrow_instance ( instance) . to_string ( )
795
795
} )
796
- . ok ( )
797
- . flatten ( ) ;
796
+ . ok ( ) ;
798
797
799
798
let Some ( string) = string else {
800
799
return ;
@@ -965,8 +964,11 @@ mod script_instance_info {
965
964
966
965
let result = handle_panic ( ctx, || {
967
966
let instance = instance_data_as_script_instance :: < T > ( p_instance) ;
967
+ let cell = borrow_instance_cell ( instance) ;
968
+ let mut guard = cell. borrow_mut ( ) . unwrap_or_else ( borrow_panic ! ( T ) ) ;
968
969
969
- borrow_instance_mut ( instance) . property_set_fallback ( name, value)
970
+ let instance_guard = SiMut :: new ( cell, & mut guard, & instance. base ) ;
971
+ ScriptInstance :: property_set_fallback ( instance_guard, name, value)
970
972
} )
971
973
. unwrap_or_default ( ) ;
972
974
0 commit comments