-
-
Notifications
You must be signed in to change notification settings - Fork 237
Description
Related to #501, though ScriptInstance
is a bit of a special case.
Godot actually passes ScriptInstance
around as void*
for some reason, so on the Rust side we implement a trait called ScriptInstance
whose methods take a &mut self
. In particular, the method of most concern is ScriptInstance::call
fn call( &mut self, method: StringName, args: &[&Variant] ) -> Result<Variant, u32>
This method documentation makes the following note.
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.
Which makes any attempts to implement recursion in a Rust-side scripting language a non-starter. I think we can make this method re-entrant, and I'd like to start the discussion on how to do that.
Currently, we implement the GDExtension ScriptInterface
type as ScriptInstanceData<T: ScriptInstance>
, which contains a RefCell<T>
. When we need to invoke a trait function like ScriptInstance::call
, we take the ScriptInstanceData<T>
and do
borrow_instance_mut(instance).call(method.clone(), args)
That is, we borrow the contents of the RefCell<T>
, instance.inner
, for the duration of the call. That makes it impossible to invoke call
again on the same instance recursively from Godot until the original call exits.
Proposed Solution 1
My first thought is this. ScriptInstance
is never used as a trait object, so it needn't be object-safe. So one solution is to change the signature of all of the ScriptInstance
methods from
fn call(&mut self, ...) -> ...
to
fn call(instance: RefCell<Self>, ...) -> ...
Then the implementor of call
can borrow mutably, decide what it needs to do, and then release the borrow if it's going to make a potentially-recursive call. This just puts the control (and responsibility) of borrowing correctly in the hands of the implementor. I'm not entirely sure how I feel about that, as instance: RefCell<Self>
is a much more obtuse API than &mut self
, but it definitely would get the job done.
Proposed Solution 2
We may be able to apply the GodotCell
trick to ScriptInstance
. I don't claim to fully understand the trickery exhibited by this new cell type, but it looks like it hinges on the fact that the self
in these calls is actually owned by a Gd<Self>
. In our case with ScriptInstance
, the self
we have is always owned by a ScriptInstanceData<Self>
, so a similar trick may be viable.