Skip to content

GDExtension catch-up: class registration via StringName #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 20, 2022
2 changes: 2 additions & 0 deletions .github/workflows/minimal-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ name: Minimal CI

on:
push:
branches:
- master
pull_request:
# branches:
# - master
Expand Down
535 changes: 535 additions & 0 deletions gdext-class/src/obj/gd.rs

Large diffs are not rendered by default.

126 changes: 71 additions & 55 deletions godot-codegen/input/gdnative_interface.h

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions godot-codegen/src/central_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ fn make_sys_code(central_items: &CentralItems) -> String {

impl VariantType {
#[doc(hidden)]
pub fn from_ord(enumerator: crate::GDNativeVariantType) -> Self {
pub fn from_sys(enumerator: crate::GDNativeVariantType) -> Self {
// Annoying, but only stable alternative is transmute(), which dictates enum size
match enumerator {
0 => Self::Nil,
Expand All @@ -136,7 +136,7 @@ fn make_sys_code(central_items: &CentralItems) -> String {
}

#[doc(hidden)]
pub fn to_ord(self) -> crate::GDNativeVariantType {
pub fn sys(self) -> crate::GDNativeVariantType {
self as _
}
}
Expand All @@ -155,7 +155,7 @@ fn make_sys_code(central_items: &CentralItems) -> String {

impl VariantOperator {
#[doc(hidden)]
pub fn from_ord(enumerator: crate::GDNativeVariantOperator) -> Self {
pub fn from_sys(enumerator: crate::GDNativeVariantOperator) -> Self {
match enumerator {
#(
#variant_op_enumerators_ord => Self::#variant_op_enumerators_pascal,
Expand All @@ -165,7 +165,7 @@ fn make_sys_code(central_items: &CentralItems) -> String {
}

#[doc(hidden)]
pub fn to_ord(self) -> crate::GDNativeVariantOperator {
pub fn sys(self) -> crate::GDNativeVariantOperator {
self as _
}
}
Expand Down
48 changes: 30 additions & 18 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

//! Generates a file for each Godot class

use proc_macro2::TokenStream;
use proc_macro2::{Literal, TokenStream};
use quote::{format_ident, quote};
use std::path::{Path, PathBuf};

use crate::api_parser::*;
use crate::util::{c_str, ident, safe_ident, strlit, to_module_name, to_rust_type};
use crate::util::{ident, safe_ident, strlit, to_module_name, to_rust_type};
use crate::{special_cases, util, Context, GeneratedClass, GeneratedModule, RustTy};

pub(crate) fn generate_class_files(
Expand Down Expand Up @@ -59,7 +59,7 @@ pub(crate) fn generate_class_files(
out_files.push(out_path);
}

fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream) -> TokenStream {
fn make_constructor(class: &Class, ctx: &Context, class_name_str: &Literal) -> TokenStream {
if ctx.is_singleton(&class.name) {
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
// &'static Self would be possible, but we would lose the whole mutability information (even if that
Expand All @@ -69,7 +69,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream)
quote! {
pub fn singleton() -> Gd<Self> {
unsafe {
let object_ptr = sys::interface_fn!(global_get_singleton)(#class_name_cstr);
let class_name = StringName::from(#class_name_str);
let object_ptr = sys::interface_fn!(global_get_singleton)(class_name.string_sys());
Gd::from_obj_sys(object_ptr)
}
}
Expand All @@ -82,7 +83,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream)
quote! {
pub fn new() -> Gd<Self> {
unsafe {
let object_ptr = sys::interface_fn!(classdb_construct_object)(#class_name_cstr);
let class_name = StringName::from(#class_name_str);
let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys());
//let instance = Self { object_ptr };
Gd::from_obj_sys(object_ptr)
}
Expand All @@ -94,7 +96,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream)
#[must_use]
pub fn new_alloc() -> Gd<Self> {
unsafe {
let object_ptr = sys::interface_fn!(classdb_construct_object)(#class_name_cstr);
let class_name = StringName::from(#class_name_str);
let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys());
Gd::from_obj_sys(object_ptr)
}
}
Expand All @@ -114,9 +117,8 @@ fn make_class(class: &Class, ctx: &mut Context) -> GeneratedClass {

let name = ident(&class.name);
let name_str = strlit(&class.name);
let name_cstr = c_str(&class.name);

let constructor = make_constructor(class, ctx, name_cstr);
let constructor = make_constructor(class, ctx, &name_str);

let methods = make_methods(&class.methods, &class.name, ctx);
let enums = make_enums(&class.enums, &class.name, ctx);
Expand Down Expand Up @@ -356,7 +358,7 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context)
let is_varcall = method.is_vararg;
let (params, arg_exprs) = make_params(&method.arguments, is_varcall, ctx);

let method_name = special_cases::maybe_renamed(class_name, &method.name);
let method_name_str = special_cases::maybe_renamed(class_name, &method.name);
/*if method.map_args(|args| args.is_empty()) {
// Getters (i.e. 0 arguments) will be stripped of their `get_` prefix, to conform to Rust convention
if let Some(remainder) = method_name.strip_prefix("get_") {
Expand All @@ -367,10 +369,7 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context)
}
}
}*/
let method_name = safe_ident(method_name);

let c_method_name = c_str(&method.name);
let c_class_name = c_str(class_name);
let method_name = safe_ident(method_name_str);
let hash = method.hash;

// TODO &mut safety
Expand All @@ -393,7 +392,13 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context)
quote! {
#vis fn #method_name( #receiver #(, #params )*, varargs: &[Variant]) #return_decl {
unsafe {
let method_bind = sys::interface_fn!(classdb_get_method_bind)(#c_class_name, #c_method_name, #hash);
let class_name = StringName::from(#class_name);
let method_name = StringName::from(#method_name_str);
let method_bind = sys::interface_fn!(classdb_get_method_bind)(
class_name.string_sys(),
method_name.string_sys(),
#hash
);
let call_fn = sys::interface_fn!(object_method_bind_call);

let explicit_args = [
Expand All @@ -414,7 +419,13 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context)
quote! {
#vis fn #method_name( #receiver, #( #params ),* ) #return_decl {
unsafe {
let method_bind = sys::interface_fn!(classdb_get_method_bind)(#c_class_name, #c_method_name, #hash);
let class_name = StringName::from(#class_name);
let method_name = StringName::from(#method_name_str);
let method_bind = sys::interface_fn!(classdb_get_method_bind)(
class_name.string_sys(),
method_name.string_sys(),
#hash
);
let call_fn = sys::interface_fn!(object_method_bind_ptrcall);

let args = [
Expand All @@ -441,16 +452,17 @@ pub(crate) fn make_function_definition(
let is_vararg = function.is_vararg;
let (params, arg_exprs) = make_params(&function.arguments, is_vararg, ctx);

let function_name = safe_ident(&function.name);
let c_function_name = c_str(&function.name);
let function_name_str = &function.name;
let function_name = safe_ident(function_name_str);
let hash = function.hash;

let (return_decl, call) = make_utility_return(&function.return_type, ctx);

quote! {
pub fn #function_name( #( #params ),* ) #return_decl {
let result = unsafe {
let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(#c_function_name, #hash);
let function_name = StringName::from(#function_name_str);
let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(function_name.string_sys(), #hash);
let call_fn = call_fn.unwrap_unchecked();

let args = [
Expand Down
8 changes: 0 additions & 8 deletions godot-codegen/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,6 @@ pub fn safe_ident(s: &str) -> Ident {
}
}

// Code duplicated between here and godot-macros
pub fn c_str(s: &str) -> TokenStream {
let s = Literal::string(&format!("{}\0", s));
quote! {
#s.as_ptr() as *const i8
}
}

pub fn strlit(s: &str) -> Literal {
Literal::string(s)
}
Expand Down
43 changes: 24 additions & 19 deletions godot-core/src/builtin/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@

#![macro_use]

macro_rules! impl_basic_trait_as_sys {
( Drop for $Type:ty => $gd_method:ident ) => {
impl Drop for $Type {
macro_rules! impl_builtin_traits_inner {
( Default for $Type:ty => $gd_method:ident ) => {
impl Default for $Type {
#[inline]
fn drop(&mut self) {
unsafe { (get_api().$gd_method)(self.sys_mut()) }
fn default() -> Self {
unsafe {
let mut gd_val = sys::$GdType::default();
(sys::method_table().$gd_method)(&mut gd_val);
<$Type>::from_sys(gd_val)
}
}
}
};
Expand All @@ -21,22 +25,23 @@ macro_rules! impl_basic_trait_as_sys {
#[inline]
fn clone(&self) -> Self {
unsafe {
let mut result = sys::$GdType::default();
(get_api().$gd_method)(&mut result, self.sys());
<$Type>::from_sys(result)
Self::from_sys_init(|self_ptr| {
let ctor = sys::method_table().$gd_method;
let args = [self.sys()];
ctor(self_ptr, args.as_ptr());
})
}
}
}
};

( Default for $Type:ty => $gd_method:ident ) => {
impl Default for $Type {
( Drop for $Type:ty => $gd_method:ident ) => {
impl Drop for $Type {
#[inline]
fn default() -> Self {
fn drop(&mut self) {
unsafe {
let mut gd_val = sys::$GdType::default();
(get_api().$gd_method)(&mut gd_val);
<$Type>::from_sys(gd_val)
let destructor = sys::method_table().$gd_method;
destructor(self.sys_mut());
}
}
}
Expand All @@ -58,7 +63,7 @@ macro_rules! impl_basic_trait_as_sys {
};

( Eq for $Type:ty => $gd_method:ident ) => {
impl_basic_trait_as_sys!(PartialEq for $Type => $gd_method);
impl_builtin_traits_inner!(PartialEq for $Type => $gd_method);
impl Eq for $Type {}
};

Expand Down Expand Up @@ -86,7 +91,7 @@ macro_rules! impl_basic_trait_as_sys {
};

( Ord for $Type:ty => $gd_method:ident ) => {
impl_basic_trait_as_sys!(PartialOrd for $Type => $gd_method);
impl_builtin_traits_inner!(PartialOrd for $Type => $gd_method);
impl Ord for $Type {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
Expand All @@ -96,16 +101,16 @@ macro_rules! impl_basic_trait_as_sys {
};
}

macro_rules! impl_traits_as_sys {
macro_rules! impl_builtin_traits {
(
for $Type:ty {
$( $Trait:ident => $gd_method:ident; )*
}
) => (
$(
impl_basic_trait_as_sys!(
impl_builtin_traits_inner! {
$Trait for $Type => $gd_method
);
}
)*
)
}
Expand Down
43 changes: 43 additions & 0 deletions godot-core/src/builtin/meta/class_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use godot_ffi as sys;

use std::fmt::{Display, Formatter, Result as FmtResult};

use crate::builtin::*;
use crate::obj::GodotClass;

/// Utility to construct class names known at compile time.
/// Cannot be a function since the backing string must be retained.
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
pub struct ClassName {
backing: StringName,
}

impl ClassName {
pub fn new<T: GodotClass>() -> Self {
Self {
backing: StringName::from(T::CLASS_NAME),
}
}

pub fn from_static(string: &'static str) -> Self {
Self {
backing: StringName::from(string),
}
}

pub fn string_sys(&self) -> sys::GDNativeStringNamePtr {
self.backing.string_sys()
}
}

impl Display for ClassName {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.backing.fmt(f)
}
}
Loading