Skip to content

Commit eb5d43b

Browse files
bors[bot]Bromeon
andauthored
Merge #115
115: Implement static methods for engine + builtin classes r=Bromeon a=Bromeon Also fixes a memory leak when `RefCounted` instances are returned from engine methods. Closes #43. Co-authored-by: Jan Haller <[email protected]>
2 parents f25f65c + 3158825 commit eb5d43b

File tree

6 files changed

+83
-22
lines changed

6 files changed

+83
-22
lines changed

godot-codegen/src/api_parser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub struct ClassMethod {
172172
pub name: String,
173173
pub is_const: bool,
174174
pub is_vararg: bool,
175-
//pub is_static: bool,
175+
pub is_static: bool,
176176
pub is_virtual: bool,
177177
pub hash: Option<i64>,
178178
pub return_value: Option<MethodReturn>,

godot-codegen/src/class_generator.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -510,20 +510,21 @@ fn make_method_definition(
510510
/*if method.map_args(|args| args.is_empty()) {
511511
// Getters (i.e. 0 arguments) will be stripped of their `get_` prefix, to conform to Rust convention
512512
if let Some(remainder) = method_name.strip_prefix("get_") {
513-
// Do not apply for get_16 etc
514-
// TODO also not for get_u16 etc, in StreamPeer
513+
// TODO Do not apply for FileAccess::get_16, StreamPeer::get_u16, etc
515514
if !remainder.chars().nth(0).unwrap().is_ascii_digit() {
516515
method_name = remainder;
517516
}
518517
}
519518
}*/
520519

521520
let method_name_str = special_cases::maybe_renamed(class_name, &method.name);
522-
let receiver = if method.is_const {
523-
quote! { &self, }
524-
} else {
525-
quote! { &mut self, }
526-
};
521+
522+
let (receiver, receiver_arg) = make_receiver(
523+
method.is_static,
524+
method.is_const,
525+
quote! { self.object_ptr },
526+
);
527+
527528
let hash = method.hash;
528529
let is_varcall = method.is_vararg;
529530

@@ -546,10 +547,10 @@ fn make_method_definition(
546547
let __call_fn = sys::interface_fn!(#function_provider);
547548
};
548549
let varcall_invocation = quote! {
549-
__call_fn(__method_bind, self.object_ptr, __args_ptr, __args.len() as i64, return_ptr, std::ptr::addr_of_mut!(__err));
550+
__call_fn(__method_bind, #receiver_arg, __args_ptr, __args.len() as i64, return_ptr, std::ptr::addr_of_mut!(__err));
550551
};
551552
let ptrcall_invocation = quote! {
552-
__call_fn(__method_bind, self.object_ptr, __args_ptr, return_ptr);
553+
__call_fn(__method_bind, #receiver_arg, __args_ptr, return_ptr);
553554
};
554555

555556
make_function_definition(
@@ -579,11 +580,8 @@ fn make_builtin_method_definition(
579580

580581
let method_name_str = &method.name;
581582

582-
let receiver = if method.is_const {
583-
quote! { &self, }
584-
} else {
585-
quote! { &mut self, }
586-
};
583+
let (receiver, receiver_arg) =
584+
make_receiver(method.is_static, method.is_const, quote! { self.sys_ptr });
587585

588586
let return_value = method.return_type.as_deref().map(MethodReturn::from_type);
589587
let hash = method.hash;
@@ -602,7 +600,7 @@ fn make_builtin_method_definition(
602600
let __call_fn = __call_fn.unwrap_unchecked();
603601
};
604602
let ptrcall_invocation = quote! {
605-
__call_fn(self.sys_ptr, __args_ptr, return_ptr, __args.len() as i32);
603+
__call_fn(#receiver_arg, __args_ptr, return_ptr, __args.len() as i32);
606604
};
607605

608606
make_function_definition(
@@ -746,6 +744,28 @@ fn make_function_definition(
746744
}
747745
}
748746

747+
fn make_receiver(
748+
is_static: bool,
749+
is_const: bool,
750+
receiver_arg: TokenStream,
751+
) -> (TokenStream, TokenStream) {
752+
let receiver = if is_static {
753+
quote! {}
754+
} else if is_const {
755+
quote! { &self, }
756+
} else {
757+
quote! { &mut self, }
758+
};
759+
760+
let receiver_arg = if is_static {
761+
quote! { std::ptr::null_mut() }
762+
} else {
763+
receiver_arg
764+
};
765+
766+
(receiver, receiver_arg)
767+
}
768+
749769
fn make_params(
750770
method_args: &Option<Vec<MethodArg>>,
751771
is_varcall: bool,

godot-core/src/builtin/color.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ pub struct Color {
1717
}
1818

1919
impl Color {
20-
#[allow(dead_code)]
2120
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
2221
Self { r, g, b, a }
2322
}

godot-core/src/obj/gd.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@ impl<T: GodotClass> Gd<T> {
488488
/// Runs `init_fn` on the address of a pointer (initialized to null). If that pointer is still null after the `init_fn` call,
489489
/// then `None` will be returned; otherwise `Gd::from_obj_sys(ptr)`.
490490
///
491+
/// This method will **NOT** increment the reference-count of the object, as it assumes the input to come from a Godot API
492+
/// return value.
493+
///
491494
/// # Safety
492495
/// `init_fn` must be a function that correctly handles a _type pointer_ pointing to an _object pointer_.
493496
#[doc(hidden)]
@@ -496,7 +499,9 @@ impl<T: GodotClass> Gd<T> {
496499

497500
// Initialize pointer with given function, return Some(ptr) on success and None otherwise
498501
let object_ptr = raw_object_init(init_fn);
499-
sys::ptr_then(object_ptr, |ptr| Gd::from_obj_sys(ptr))
502+
503+
// Do not increment ref-count; assumed to be return value from FFI.
504+
sys::ptr_then(object_ptr, |ptr| Gd::from_obj_sys_weak(ptr))
500505
}
501506
}
502507

itest/rust/src/codegen_test.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7-
// This file tests the presence and naming of generated symbols, not their functionality.
7+
// This file tests the presence, naming and accessibility of generated symbols.
8+
// Functionality is only tested on a superficial level (to make sure general FFI mechanisms work).
89

910
use crate::itest;
10-
11-
use godot::engine::HttpRequest;
11+
use godot::builtin::inner::{InnerColor, InnerString};
12+
use godot::engine::{FileAccess, HttpRequest};
1213
use godot::prelude::*;
1314

1415
pub fn run() -> bool {
1516
let mut ok = true;
1617
ok &= codegen_class_renamed();
1718
ok &= codegen_base_renamed();
19+
ok &= codegen_static_builtin_method();
20+
ok &= codegen_static_class_method();
1821
ok
1922
}
2023

@@ -36,6 +39,28 @@ fn codegen_base_renamed() {
3639
obj.free();
3740
}
3841

42+
#[itest]
43+
fn codegen_static_builtin_method() {
44+
let pi = InnerString::num(std::f64::consts::PI, 3);
45+
assert_eq!(pi, GodotString::from("3.142"));
46+
47+
let col = InnerColor::html("#663399cc".into());
48+
assert_eq!(col, Color::new(0.4, 0.2, 0.6, 0.8));
49+
}
50+
51+
#[itest]
52+
fn codegen_static_class_method() {
53+
let exists = FileAccess::file_exists("inexistent".into());
54+
assert!(!exists);
55+
56+
let exists = FileAccess::file_exists("res://itest.gdextension".into());
57+
assert!(exists);
58+
59+
// see also object_test for reference count verification
60+
}
61+
62+
// ----------------------------------------------------------------------------------------------------------------------------------------------
63+
3964
#[derive(GodotClass)]
4065
#[class(base=HttpRequest)]
4166
pub struct TestBaseRenamed {

itest/rust/src/object_test.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::{expect_panic, itest};
88
use godot::bind::{godot_api, GodotClass, GodotExt};
99
use godot::builtin::{FromVariant, GodotString, StringName, ToVariant, Variant, Vector3};
10-
use godot::engine::{Node, Node3D, Object, RefCounted};
10+
use godot::engine::{file_access, FileAccess, Node, Node3D, Object, RefCounted};
1111
use godot::obj::Share;
1212
use godot::obj::{Base, Gd, InstanceId};
1313
use godot::sys::GodotFfi;
@@ -38,6 +38,7 @@ pub fn run() -> bool {
3838
ok &= object_engine_convert_variant();
3939
ok &= object_user_convert_variant_refcount();
4040
ok &= object_engine_convert_variant_refcount();
41+
ok &= object_engine_returned_refcount();
4142
ok &= object_engine_up_deref();
4243
ok &= object_engine_up_deref_mut();
4344
ok &= object_engine_upcast();
@@ -265,6 +266,17 @@ fn check_convert_variant_refcount(obj: Gd<RefCounted>) {
265266
assert_eq!(obj.get_reference_count(), 1);
266267
}
267268

269+
#[itest]
270+
fn object_engine_returned_refcount() {
271+
let Some(file) = FileAccess::open("res://itest.gdextension".into(), file_access::ModeFlags::READ) else {
272+
panic!("failed to open file used to test FileAccess")
273+
};
274+
assert!(file.is_open());
275+
276+
// There was a bug which incremented ref-counts of just-returned objects, keep this as regression test.
277+
assert_eq!(file.get_reference_count(), 1);
278+
}
279+
268280
#[itest]
269281
fn object_engine_up_deref() {
270282
let node3d: Gd<Node3D> = Node3D::new_alloc();

0 commit comments

Comments
 (0)