Skip to content

Commit

Permalink
Try #207:
Browse files Browse the repository at this point in the history
  • Loading branch information
bors[bot] authored Apr 2, 2023
2 parents e0d45a3 + 783939c commit 864b06e
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 4 deletions.
58 changes: 55 additions & 3 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -988,11 +988,63 @@ fn make_virtual_method(class_method: &ClassMethod, ctx: &mut Context) -> TokenSt
// Virtual methods are never static.
assert!(!class_method.is_static);

let receiver = make_receiver_self_param(false, class_method.is_const);
let [params, _, _, _] = make_params(&class_method.arguments, class_method.is_vararg, ctx);
let [params, variant_types, _, arg_names] =
make_params(&class_method.arguments, class_method.is_vararg, ctx);

let is_varcall = class_method.is_vararg;
let variant_ffi = is_varcall.then(VariantFfi::type_ptr);
let (receiver, receiver_arg) = make_receiver(
class_method.is_static,
class_method.is_const,
quote! { self.object_ptr },
);
let varcall_invocation = quote! {
__call_fn(__method_bind, #receiver_arg, __args_ptr, __args.len() as i64, return_ptr, std::ptr::addr_of_mut!(__err));
};
let ptrcall_invocation = quote! {
__call_fn(__method_bind, #receiver_arg, __args_ptr, return_ptr);
};

let (prepare_arg_types, error_fn_context);
if variant_ffi.is_some() {
// varcall (using varargs)
prepare_arg_types = quote! {
let mut __arg_types = Vec::with_capacity(__explicit_args.len() + varargs.len());
// __arg_types.extend(__explicit_args.iter().map(Variant::get_type));
__arg_types.extend(varargs.iter().map(Variant::get_type));
let __vararg_str = varargs.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(", ");
};

let joined = arg_names
.iter()
.map(|n| format!("{{{n}:?}}"))
.collect::<Vec<_>>()
.join(", ");

let fmt = format!("{method_name}({joined}; {{__vararg_str}})");
error_fn_context = quote! { &format!(#fmt) };
} else {
// ptrcall
prepare_arg_types = quote! {
let __arg_types = [
#( #variant_types ),*
];
};
error_fn_context = method_name.to_token_stream();
};

let (return_value, _) = make_return(
class_method.return_value.as_ref(),
variant_ffi.as_ref(),
&varcall_invocation,
&ptrcall_invocation,
prepare_arg_types,
error_fn_context,
ctx,
);

quote! {
fn #method_name ( #receiver #( #params , )* ) {
fn #method_name ( #receiver #( #params , )* ) #return_value {
unimplemented!()
}
}
Expand Down
31 changes: 30 additions & 1 deletion itest/rust/src/virtual_methods_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use crate::TestContext;
use godot::bind::{godot_api, GodotClass};
use godot::builtin::GodotString;
use godot::engine::node::InternalMode;
use godot::engine::{Node, Node2D, Node2DVirtual, RefCounted, RefCountedVirtual};
use godot::engine::{Node, Node2D, Node2DVirtual, NodeVirtual, RefCounted, RefCountedVirtual};
use godot::obj::{Base, Gd, Share};
use godot::prelude::PackedStringArray;
use godot::test::itest;

/// Simple class, that deliberately has no constructor accessible from GDScript
Expand Down Expand Up @@ -91,6 +92,26 @@ impl Node2DVirtual for TreeVirtualTest {
}
}

#[derive(GodotClass, Debug)]
#[class(base=Node)]
struct ReturnVirtualTest {
#[base]
base: Base<Node>,
}

#[godot_api]
impl NodeVirtual for ReturnVirtualTest {
fn init(base: Base<Node>) -> Self {
ReturnVirtualTest { base }
}

fn get_configuration_warnings(&self) -> PackedStringArray {
let mut output = PackedStringArray::new();
output.push("Hello".into());
output
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------

#[itest]
Expand Down Expand Up @@ -215,3 +236,11 @@ fn test_tree_enters_exits(test_context: &TestContext) {
assert_eq!(obj.bind().tree_enters, 2);
assert_eq!(obj.bind().tree_exits, 1);
}

#[itest]
fn test_virtual_method_with_return(_test_context: &TestContext) {
let obj = Gd::<ReturnVirtualTest>::new_default();
let output = obj.bind().get_configuration_warnings();
assert!(output.contains("Hello".into()));
assert_eq!(output.len(), 1);
}

0 comments on commit 864b06e

Please sign in to comment.