Skip to content

Commit f50d8e7

Browse files
authored
Merge pull request #1013 from TitanNano/jovan/script_instance_exists
Provide safe way for implementing `IScriptExtension::instance_has`
2 parents 2f4320f + 5f98714 commit f50d8e7

File tree

4 files changed

+225
-21
lines changed

4 files changed

+225
-21
lines changed

godot-codegen/src/special_cases/codegen_special_cases.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ const SELECTED_CLASSES: &[&str] = &[
173173
"SceneTreeTimer",
174174
"Script",
175175
"ScriptExtension",
176+
"ScriptNameCasing",
176177
"ScriptLanguage",
178+
"ScriptLanguageExtension",
177179
"Sprite2D",
178180
"SpriteFrames",
179181
"TextServer",

godot-core/src/obj/script.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@ use godot_cell::panicking::{GdCell, MutGuard, RefGuard};
2525
use godot_cell::blocking::{GdCell, MutGuard, RefGuard};
2626

2727
use crate::builtin::{GString, StringName, Variant, VariantType};
28-
use crate::classes::{Script, ScriptLanguage};
28+
use crate::classes::{Object, Script, ScriptLanguage};
2929
use crate::meta::{MethodInfo, PropertyInfo};
3030
use crate::obj::{Base, Gd, GodotClass};
3131
use crate::sys;
3232

3333
#[cfg(before_api = "4.3")]
3434
use self::bounded_ptr_list::BoundedPtrList;
3535

36+
#[cfg(since_api = "4.2")]
37+
use crate::classes::IScriptExtension;
38+
#[cfg(since_api = "4.2")]
39+
use crate::obj::Inherits;
40+
3641
/// Implement custom scripts that can be attached to objects in Godot.
3742
///
3843
/// To use script instances, implement this trait for your own type.
@@ -337,6 +342,40 @@ pub unsafe fn create_script_instance<T: ScriptInstance>(
337342
}
338343
}
339344

345+
/// Checks if an instance of the script exists for a given object.
346+
///
347+
/// This function both checks if the passed script matches the one currently assigned to the passed object, as well as verifies that
348+
/// there is an instance for the script.
349+
///
350+
/// Use this function to implement [`IScriptExtension::instance_has`](crate::classes::IScriptExtension::instance_has).
351+
#[cfg(since_api = "4.2")]
352+
pub fn script_instance_exists<O, S>(object: &Gd<O>, script: &Gd<S>) -> bool
353+
where
354+
O: Inherits<Object>,
355+
S: Inherits<Script> + IScriptExtension + super::Bounds<Declarer = super::bounds::DeclUser>,
356+
{
357+
let object_script_variant = object.upcast_ref().get_script();
358+
359+
if object_script_variant.is_nil() {
360+
return false;
361+
}
362+
363+
let object_script: Gd<Script> = object_script_variant.to();
364+
365+
if object_script.upcast_ref::<Script>().__object_ptr() != script.upcast_ref().__object_ptr() {
366+
return false;
367+
}
368+
369+
let Some(language) = script.bind().get_language() else {
370+
return false;
371+
};
372+
373+
let get_instance_fn = sys::interface_fn!(object_get_script_instance);
374+
let instance = unsafe { get_instance_fn(object.obj_sys(), language.obj_sys()) };
375+
376+
!instance.is_null()
377+
}
378+
340379
/// Mutable/exclusive reference guard for a `T` where `T` implements [`ScriptInstance`].
341380
///
342381
/// This can be used to access the base object of a [`ScriptInstance`], which in turn can be used to make reentrant calls to engine APIs.

itest/godot/ScriptInstanceTests.gd

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,103 +5,133 @@
55

66
extends TestSuite
77

8-
func create_script_instance() -> RefCounted:
9-
var script = TestScript.new()
8+
func create_script_instance() -> Array:
9+
var language: TestScriptLanguage = TestScriptLanguage.new()
10+
var script: TestScript = language.new_script()
1011
var script_owner = RefCounted.new()
1112

1213
script_owner.script = script
1314

14-
return script_owner
15+
return [script_owner, language]
1516

1617

1718
func test_script_instance_get_property():
18-
var object = create_script_instance()
19+
var tuple := create_script_instance()
20+
var object: RefCounted = tuple[0]
21+
var language: TestScriptLanguage = tuple[1]
1922

2023
var value: int = object.script_property_a
2124

2225
assert_eq(value, 10)
26+
language.free()
2327

2428

2529
func test_script_instance_set_property():
26-
var object = create_script_instance()
30+
var tuple := create_script_instance()
31+
var object: RefCounted = tuple[0]
32+
var language: TestScriptLanguage = tuple[1]
2733

2834
assert_eq(object.script_property_b, false)
2935

3036
object.script_property_b = true
3137

3238
assert_eq(object.script_property_b, true)
39+
language.free()
3340

3441

3542
func test_script_instance_call():
36-
var object = create_script_instance()
43+
var tuple := create_script_instance()
44+
var object: RefCounted = tuple[0]
45+
var language: TestScriptLanguage = tuple[1]
3746

3847
var arg_a = "test string"
3948
var arg_b = 5
4049

4150
var result = object.script_method_a(arg_a, arg_b)
4251

4352
assert_eq(result, "{0}{1}".format([arg_a, arg_b]))
53+
language.free()
4454

4555

4656
func test_script_instance_property_list():
47-
var object = create_script_instance()
57+
var tuple := create_script_instance()
58+
var object: RefCounted = tuple[0]
59+
var language: TestScriptLanguage = tuple[1]
4860

4961
var list = object.get_property_list()
5062

5163
assert_eq(list[-1]["name"], "script_property_a");
5264
assert_eq(list[-1]["type"], Variant.Type.TYPE_INT)
65+
language.free()
5366

5467

5568
func test_script_instance_method_list():
56-
var object = create_script_instance()
69+
var tuple := create_script_instance()
70+
var object: RefCounted = tuple[0]
71+
var language: TestScriptLanguage = tuple[1]
5772

5873
var list = object.get_method_list()
5974

6075
assert_eq(list[-1]["name"], "script_method_a")
6176
assert_eq(list[-1]["args"][0]["type"], Variant.Type.TYPE_STRING)
6277
assert_eq(list[-1]["args"][1]["type"], Variant.Type.TYPE_INT)
78+
language.free()
6379

6480

6581
func test_script_instance_has_method():
66-
var object = create_script_instance()
82+
var tuple := create_script_instance()
83+
var object: RefCounted = tuple[0]
84+
var language: TestScriptLanguage = tuple[1]
6785

6886
assert(object.has_method("script_method_a"));
6987
assert(!object.has_method("script_method_z"));
88+
language.free()
7089

7190

7291
func test_script_instance_to_string():
73-
var object = create_script_instance()
92+
var tuple := create_script_instance()
93+
var object: RefCounted = tuple[0]
94+
var language: TestScriptLanguage = tuple[1]
7495

7596
assert_eq(object.to_string(), "script instance to string")
97+
language.free()
7698

7799

78100
func test_script_instance_mut_call():
79-
var object = create_script_instance()
101+
var tuple := create_script_instance()
102+
var object: RefCounted = tuple[0]
103+
var language: TestScriptLanguage = tuple[1]
80104
var before = object.script_property_b
81105

82106
var result = object.script_method_toggle_property_b()
83107

84108
assert(result)
85109
assert_eq(object.script_property_b, !before)
110+
language.free()
86111

87112

88113
func test_script_instance_re_entering_call():
89-
var object = create_script_instance()
114+
var tuple := create_script_instance()
115+
var object: RefCounted = tuple[0]
116+
var language: TestScriptLanguage = tuple[1]
90117
var before = object.script_property_b
91118

92119
var result = object.script_method_re_entering()
93120

94121
assert(result)
95122
assert_eq(object.script_property_b, !before)
123+
language.free()
96124

97125

98126
func test_object_script_instance():
99127
var object = Node.new()
100-
var script = TestScript.new()
128+
var language: TestScriptLanguage = TestScriptLanguage.new()
129+
var script: TestScript = language.new_script()
101130

102131
object.script = script
103132

104133
var result = object.script_method_re_entering()
105134

106135
assert(result)
107136
object.free()
137+
language.free()

0 commit comments

Comments
 (0)