diff --git a/bindings_generator/src/api.rs b/bindings_generator/src/api.rs index 12cdc68b7..26743d5b8 100644 --- a/bindings_generator/src/api.rs +++ b/bindings_generator/src/api.rs @@ -160,6 +160,11 @@ impl GodotClass { pub fn is_getter(&self, name: &str) -> bool { self.properties.iter().any(|p| p.getter == name) } + + /// Whether there is a snake_case module containing related symbols (nested types in C++) + pub fn has_related_module(&self) -> bool { + !self.enums.is_empty() + } } pub type ConstantName = String; @@ -406,9 +411,8 @@ impl Ty { } } ty => { - let module = format_ident!("{}", module_name_from_class_name(ty)); let ty = format_ident!("{}", ty); - Ty::Object(syn::parse_quote! { crate::generated::#module::#ty }) + Ty::Object(syn::parse_quote! { crate::generated::#ty }) } } } diff --git a/bindings_generator/src/classes.rs b/bindings_generator/src/classes.rs index 68dfa2bc1..bbbf158c5 100644 --- a/bindings_generator/src/classes.rs +++ b/bindings_generator/src/classes.rs @@ -9,17 +9,22 @@ use quote::{format_ident, quote}; use std::collections::HashMap; -pub(crate) fn generate_class_struct(class: &GodotClass) -> TokenStream { +pub(crate) fn generate_class_struct(class: &GodotClass, class_doc: TokenStream) -> TokenStream { let class_name = format_ident!("{}", &class.name); // dead_code: 'this' might not be read + // mod private: hide the type in the #module_name module, export it only in gdnative::api quote! { - #[allow(non_camel_case_types)] - #[derive(Debug)] - pub struct #class_name { - #[allow(dead_code)] - this: RawObject, + pub(crate) mod private { + #class_doc + #[allow(non_camel_case_types)] + #[derive(Debug)] + pub struct #class_name { + #[allow(dead_code)] + pub(crate) this: super::RawObject, + } } + use private::#class_name; } } diff --git a/bindings_generator/src/documentation.rs b/bindings_generator/src/documentation.rs index b5926a6bb..2775ddf97 100644 --- a/bindings_generator/src/documentation.rs +++ b/bindings_generator/src/documentation.rs @@ -12,43 +12,56 @@ pub fn class_doc_link(class: &GodotClass) -> String { pub fn official_doc_url(class: &GodotClass) -> String { format!( - "https://godot.readthedocs.io/en/latest/classes/class_{lower_case}.html", + "https://godot.readthedocs.io/en/stable/classes/class_{lower_case}.html", lower_case = class.name.to_lowercase(), ) } +pub fn generate_module_doc(class: &GodotClass) -> TokenStream { + let module_doc = format!( + "This module contains types related to the API class [`{m}`][super::{m}].", + m = class.name + ); + + quote! { + #![doc=#module_doc] + } +} + pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStream { let has_parent = !class.base_class.is_empty(); let singleton_str = if class.singleton { "singleton " } else { "" }; - let ownership_type = if class.is_refcounted() { - "reference counted" + let memory_type = if class.is_refcounted() { + "reference-counted" } else { - "unsafe" + "manually managed" }; - let summary_doc = if &class.name == "Reference" { + let mut summary_doc = if &class.name == "Reference" { "Base class of all reference-counted types. Inherits `Object`.".into() } else if &class.name == "Object" { - "The base class of most Godot classes.".into() + "The base class of all classes in the Godot hierarchy.".into() } else if has_parent { format!( - "`{api_type} {singleton}class {name}` inherits `{base_class}` ({ownership_type}).", + "`{api_type} {singleton}class {name}` inherits `{base_class}` ({memory_type}).", api_type = class.api_type, name = class.name, base_class = class.base_class, - ownership_type = ownership_type, - singleton = singleton_str + memory_type = memory_type, + singleton = singleton_str, ) } else { format!( - "`{api_type} {singleton}class {name}` ({ownership_type}).", + "`{api_type} {singleton}class {name}` ({memory_type})", api_type = class.api_type, name = class.name, - ownership_type = ownership_type, + memory_type = memory_type, singleton = singleton_str, ) }; + append_related_module(&mut summary_doc, class); + let official_docs = format!( r#"## Official documentation @@ -66,7 +79,7 @@ The lifetime of this object is automatically managed through reference counting. format!( r#"## Memory management -Non reference counted objects such as the ones of this type are usually owned by the engine. +Non-reference-counted objects, such as the ones of this type, are usually owned by the engine. `{name}` is a reference-only type. Persistent references can only exist in the unsafe `Ref<{name}>` form. @@ -113,7 +126,7 @@ This class is used to interact with Godot's editor."# let safety_doc = r#" ## Safety -All types in the Godot API have "interior mutability" in Rust parlance. +All types in the Godot API have _interior mutability_ in Rust parlance. To enforce that the official [thread-safety guidelines][thread-safety] are followed, the typestate pattern is used in the `Ref` and `TRef` smart pointers, and the `Instance` API. The typestate `Access` in these types tracks whether the @@ -145,3 +158,16 @@ fn list_base_classes(output: &mut impl Write, api: &Api, parent_name: &str) -> G Ok(()) } + +// If present, links to the module with related (C++: nested) types. +fn append_related_module(string: &mut String, class: &GodotClass) { + use std::fmt::Write; + if class.has_related_module() { + write!( + string, + "\n\nThis class has related types in the [`{m}`][super::{m}] module.", + m = class.module() + ) + .expect("append to string via write!"); + } +} diff --git a/bindings_generator/src/lib.rs b/bindings_generator/src/lib.rs index 308856692..f355ed852 100644 --- a/bindings_generator/src/lib.rs +++ b/bindings_generator/src/lib.rs @@ -38,12 +38,12 @@ use std::io; pub type GeneratorResult = Result; -pub struct BindingResult { - pub class_bindings: HashMap, +pub struct BindingResult<'a> { + pub class_bindings: Vec<(&'a GodotClass, TokenStream)>, pub icalls: TokenStream, } -pub fn generate_bindings(api: &Api, docs: Option<&GodotXmlDocs>) -> BindingResult { +pub fn generate_bindings<'a>(api: &'a Api, docs: Option<&GodotXmlDocs>) -> BindingResult<'a> { let mut icalls = HashMap::new(); let class_bindings = api @@ -51,7 +51,7 @@ pub fn generate_bindings(api: &Api, docs: Option<&GodotXmlDocs>) -> BindingResul .iter() .map(|class| { ( - class.name.clone(), + class, generate_class_bindings(api, class, &mut icalls, docs), ) }) @@ -84,9 +84,9 @@ fn generate_class_bindings( ) -> TokenStream { // types and methods let types_and_methods = { - let documentation = generate_class_documentation(api, class); - - let class_struct = generate_class_struct(class); + let module_doc = generate_module_doc(class); + let class_doc = generate_class_documentation(api, class); + let class_struct = generate_class_struct(class, class_doc); let enums = generate_enums(class); @@ -99,7 +99,7 @@ fn generate_class_bindings( let class_impl = generate_class_impl(class, icalls, docs); quote! { - #documentation + #module_doc #class_struct #enums #constants @@ -195,17 +195,22 @@ pub(crate) mod test_prelude { #[test] fn sanity_test_generated_code() { + // Tests whether each generated snippet individually constitutes a valid AST representation of Rust code + let api = Api::new(include_str!("../../gdnative-bindings/api.json")); let mut buffer = BufWriter::new(Vec::with_capacity(16384)); for class in &api.classes { let mut icalls = HashMap::new(); - let code = generate_class_documentation(&api, &class); + let code = generate_module_doc(&class); + write!(&mut buffer, "{}", code).unwrap(); + validate_and_clear_buffer!(buffer); + + let class_doc = generate_class_documentation(&api, &class); write!(&mut buffer, "{}", code).unwrap(); - write!(&mut buffer, "{}", quote! { struct Docs {} }).unwrap(); validate_and_clear_buffer!(buffer); - let code = generate_class_struct(&class); + let code = generate_class_struct(&class, class_doc); write!(&mut buffer, "{}", code).unwrap(); validate_and_clear_buffer!(buffer); diff --git a/bindings_generator/src/special_methods.rs b/bindings_generator/src/special_methods.rs index a26a8944a..0cb546efd 100644 --- a/bindings_generator/src/special_methods.rs +++ b/bindings_generator/src/special_methods.rs @@ -154,11 +154,10 @@ pub fn generate_deref_impl(class: &GodotClass) -> TokenStream { ); let class_name = format_ident!("{}", class.name); - let base_class_module = format_ident!("{}", class.base_class_module()); let base_class = format_ident!("{}", class.base_class); let qualified_base_class = quote! { - crate::generated::#base_class_module::#base_class + crate::generated::#base_class }; quote! { @@ -190,11 +189,10 @@ pub fn generate_sub_class_impls<'a>(api: &'a Api, mut class: &'a GodotClass) -> let mut tokens = TokenStream::new(); while let Some(base_class) = class.base_class(api) { - let base_class_module = format_ident!("{}", base_class.module()); let base_class_ident = format_ident!("{}", base_class.name); tokens.extend(quote! { - unsafe impl SubClass for #class_name {} + unsafe impl SubClass for #class_name {} }); class = base_class; diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs index 66631cf4c..d7f47cac7 100644 --- a/examples/array_export/src/lib.rs +++ b/examples/array_export/src/lib.rs @@ -1,4 +1,4 @@ -use gdnative::nativescript::export::property::hint::{ArrayHint, IntHint, RangeHint}; +use gdnative::export::hint::{ArrayHint, IntHint, RangeHint}; use gdnative::prelude::*; #[derive(NativeClass)] diff --git a/examples/spinning_cube/src/lib.rs b/examples/spinning_cube/src/lib.rs index 1bd3f1e35..a79f29c23 100644 --- a/examples/spinning_cube/src/lib.rs +++ b/examples/spinning_cube/src/lib.rs @@ -1,7 +1,7 @@ use gdnative::api::MeshInstance; use gdnative::prelude::*; -use gdnative::nativescript::export::property::{EnumHint, IntHint, StringHint}; +use gdnative::export::hint::{EnumHint, IntHint, StringHint}; #[derive(gdnative::derive::NativeClass)] #[inherit(MeshInstance)] diff --git a/gdnative-async/src/method.rs b/gdnative-async/src/method.rs index a79eb2937..d709f4bf4 100644 --- a/gdnative-async/src/method.rs +++ b/gdnative-async/src/method.rs @@ -5,10 +5,11 @@ use std::sync::Arc; use futures_task::{LocalFutureObj, LocalSpawn, SpawnError}; use gdnative_core::core_types::{ToVariant, Variant}; +use gdnative_core::export::NativeClass; +use gdnative_core::export::{Method, Varargs}; use gdnative_core::log::{self, Site}; -use gdnative_core::nativescript::export::{Method, Varargs}; -use gdnative_core::nativescript::{NativeClass, RefInstance}; use gdnative_core::object::ownership::Shared; +use gdnative_core::object::RefInstance; use crate::rt::Context; diff --git a/gdnative-async/src/rt.rs b/gdnative-async/src/rt.rs index 21dfeb9de..b01dccec0 100644 --- a/gdnative-async/src/rt.rs +++ b/gdnative-async/src/rt.rs @@ -1,21 +1,19 @@ use std::marker::PhantomData; +use func_state::FuncState; use gdnative_bindings::Object; -use gdnative_core::object::SubClass; - use gdnative_core::core_types::{GodotError, Variant}; -use gdnative_core::nativescript::export::InitHandle; -use gdnative_core::nativescript::{Instance, RefInstance}; +use gdnative_core::init::InitHandle; use gdnative_core::object::ownership::Shared; +use gdnative_core::object::SubClass; use gdnative_core::object::TRef; +use gdnative_core::object::{Instance, RefInstance}; use crate::future; mod bridge; mod func_state; -use func_state::FuncState; - /// Context for creating `yield`-like futures in async methods. pub struct Context { func_state: Instance, diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 87a0e979f..e2e103e04 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -5,12 +5,12 @@ use parking_lot::Mutex; use gdnative_bindings::{Object, Reference}; use gdnative_core::core_types::{GodotError, Variant, VariantArray}; +use gdnative_core::export::user_data::{ArcData, Map}; +use gdnative_core::export::{ClassBuilder, Method, Varargs}; +use gdnative_core::export::{NativeClass, NativeClassMethods}; use gdnative_core::godot_site; -use gdnative_core::nativescript::export::method::{Method, Varargs}; -use gdnative_core::nativescript::export::ClassBuilder; -use gdnative_core::nativescript::user_data::{ArcData, Map}; -use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; use gdnative_core::object::{ownership::Shared, TRef}; +use gdnative_core::object::{Instance, RefInstance}; use crate::future::Resume; diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 5e5edb939..2b21d2211 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -1,15 +1,13 @@ use gdnative_bindings::Reference; use gdnative_core::core_types::{ToVariant, Variant, VariantType}; -use gdnative_core::godot_site; -use gdnative_core::nativescript::export::method::StaticArgs; -use gdnative_core::nativescript::export::method::StaticArgsMethod; -use gdnative_core::nativescript::export::{ - ClassBuilder, ExportInfo, PropertyUsage, Signal, SignalArgument, +use gdnative_core::export::user_data::{LocalCellData, Map, MapMut}; +use gdnative_core::export::{ + ClassBuilder, ExportInfo, PropertyUsage, Signal, SignalArgument, StaticArgs, StaticArgsMethod, }; -use gdnative_core::nativescript::user_data::LocalCellData; -use gdnative_core::nativescript::user_data::{Map, MapMut}; -use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; +use gdnative_core::export::{NativeClass, NativeClassMethods}; +use gdnative_core::godot_site; use gdnative_core::object::ownership::{Shared, Unique}; +use gdnative_core::object::{Instance, RefInstance}; use gdnative_derive::FromVarargs; use crate::future::Resume; diff --git a/gdnative-bindings/build.rs b/gdnative-bindings/build.rs index 6baafcad6..7c7f00e4a 100644 --- a/gdnative-bindings/build.rs +++ b/gdnative-bindings/build.rs @@ -50,18 +50,26 @@ fn generate( generated_file: &mut BufWriter, binding_res: &BindingResult, ) { - for (class_name, code) in &binding_res.class_bindings { + // Note: 'use super::*;' needs to be after content, as the latter may contain #![doc] attributes, + // which need to be at the beginning of the module + for (class, code) in &binding_res.class_bindings { + let modifier = if class.has_related_module() { + "pub" + } else { + "pub(crate)" + }; write!( generated_file, r#" - pub mod {mod_name} {{ - use super::*; + {modifier} mod {mod_name} {{ {content} + use super::*; }} - pub use crate::generated::{mod_name}::{class_name}; + pub use crate::generated::{mod_name}::private::{class_name}; "#, - mod_name = module_name_from_class_name(class_name), - class_name = class_name, + modifier = modifier, + mod_name = module_name_from_class_name(&class.name), + class_name = class.name, content = code, ) .unwrap(); @@ -76,16 +84,18 @@ fn generate( generated_file: &mut BufWriter, binding_res: &BindingResult, ) { - for (class_name, code) in &binding_res.class_bindings { - let mod_name = module_name_from_class_name(class_name); + for (class, code) in &binding_res.class_bindings { + let mod_name = module_name_from_class_name(&class.name); let mod_path = out_path.join(format!("{}.rs", mod_name)); let mut mod_output = BufWriter::new(File::create(&mod_path).unwrap()); write!( &mut mod_output, - r#"use super::*; - {content}"#, + r#" + {content} + use super::*; + "#, content = code, ) .unwrap(); @@ -96,16 +106,22 @@ fn generate( format_file(&mod_path); } + let modifier = if class.has_related_module() { + "pub" + } else { + "pub(crate)" + }; writeln!( generated_file, r#" #[path = {:?}] - pub mod {mod_name}; - pub use crate::generated::{mod_name}::{class_name}; + {modifier} mod {mod_name}; + pub use crate::generated::{mod_name}::private::{class_name}; "#, mod_path.display(), + modifier = modifier, mod_name = mod_name, - class_name = class_name, + class_name = class.name, ) .unwrap(); } diff --git a/gdnative-bindings/src/utils.rs b/gdnative-bindings/src/utils.rs index dc1e7bac8..8bd02ef0a 100644 --- a/gdnative-bindings/src/utils.rs +++ b/gdnative-bindings/src/utils.rs @@ -1,10 +1,12 @@ //! Utility functions and extension traits that depend on generated bindings -use super::generated::{Engine, Node, SceneTree}; -use gdnative_core::nativescript::{NativeClass, RefInstance}; +use gdnative_core::export::NativeClass; use gdnative_core::object::ownership::Shared; +use gdnative_core::object::RefInstance; use gdnative_core::object::{SubClass, TRef}; +use super::generated::{Engine, Node, SceneTree}; + /// Convenience method to obtain a reference to an "auto-load" node, that is a child of the root /// node. Returns `None` if the node does not exist or is not of the correct type. /// diff --git a/gdnative-core/Cargo.toml b/gdnative-core/Cargo.toml index ed8606895..8f2c6c91a 100644 --- a/gdnative-core/Cargo.toml +++ b/gdnative-core/Cargo.toml @@ -11,9 +11,8 @@ workspace = ".." edition = "2018" [features] -default = ["nativescript"] +default = [] gd_test = [] -nativescript = ["bitflags", "parking_lot"] type_tag_fallback = [] [dependencies] @@ -22,12 +21,12 @@ gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.3" } ahash = "0.7.6" approx = "0.5.0" atomic-take = "1.0.0" -bitflags = { version = "1.3.2", optional = true } +bitflags = { version = "1.3.2" } glam = "0.19.0" indexmap = "1.7.0" libc = "0.2.104" once_cell = "1.8.0" -parking_lot = { version = "0.11.2", optional = true } +parking_lot = { version = "0.11.2" } serde = { version = "1.0.130", features = ["derive"], optional = true } [dev-dependencies] diff --git a/gdnative-core/src/export/class.rs b/gdnative-core/src/export/class.rs new file mode 100644 index 000000000..a0e095b42 --- /dev/null +++ b/gdnative-core/src/export/class.rs @@ -0,0 +1,159 @@ +use crate::export::user_data::UserData; +use crate::export::ClassBuilder; +use crate::object::ownership::{Shared, ThreadAccess, Unique}; +use crate::object::{GodotObject, Instance, Instanciable, TRef}; + +/// Trait used for describing and initializing a Godot script class. +/// +/// This trait is used to provide data and functionality to the +/// "data-part" of the class, such as name, initialization and information +/// about exported properties. +/// +/// A derive macro is available for this trait. See documentation on the +/// `NativeClass` macro for detailed usage and examples. +/// +/// For exported methods, see the [`NativeClassMethods`] trait. +/// +/// [`NativeClassMethods`]: ./trait.NativeClassMethods.html +pub trait NativeClass: Sized + 'static { + /// Base type of the class. + /// + /// In Godot, scripting languages can define "script instances" which can be + /// attached to objects. Because of the dynamic nature, the intended "inheritance" + /// is not easily implementable properly. + /// + /// Instead, delegation is used and most calls to a Godot object query the script instance + /// first. This way, some methods can be "overwritten" and new ones can be exposed. + /// + /// This only works when using so called "variant calls", since the querying of the script + /// instance is performed there. + /// When not using variant calls, any direct(*) calls have to be made to the Godot object + /// directly. + /// + /// The base type describes the "most general" type of object this script class can be + /// attached to. + /// + /// *(\*)*: GDNative enables use of "ptrcall"s, which are wrappers for function pointers. + /// Those do not do explicit checks for script implementations **unless the method + /// implementation does**. + type Base: GodotObject; + + /// User-data wrapper type of the class. + /// + /// See module-level documentation on `user_data` for more info. + type UserData: UserData; + + /// The name of the class. + /// + /// In GDNative+NativeScript many classes can be defined in one dynamic library. + /// To identify which class has to be used, a library-unique name has to be given. + fn class_name() -> &'static str; + + /// Function that creates a value of `Self`, used for the script-instance. The default + /// implementation simply panics. + /// + /// This function has a reference to the owner object as a parameter, which can be used to + /// set state on the owner upon creation or to query values + /// + /// It is possible to declare script classes without zero-argument constructors. Instances + /// of such scripts can only be created from Rust using `Instance::emplace`. See + /// documentation on `Instance::emplace` for an example. + #[inline] + fn init(_owner: TRef<'_, Self::Base, Shared>) -> Self { + panic!( + "{} does not have a zero-argument constructor", + Self::class_name() + ) + } + + /// Register any exported properties to Godot. + #[inline] + fn register_properties(_builder: &ClassBuilder) {} + + /// Convenience method to create an `Instance`. This is a new `Self::Base` + /// with the script attached. + /// + /// If `Self::Base` is manually-managed, then the resulting `Instance` must be passed to + /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be + /// leaked. + /// + /// Must be called after the library is initialized. + #[inline] + fn new_instance() -> Instance + where + Self::Base: Instanciable, + { + Instance::new() + } + + /// Convenience method to emplace `self` into an `Instance`. This is a new + /// `Self::Base` with the script attached. + /// + /// If `Self::Base` is manually-managed, then the resulting `Instance` must be passed to + /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be + /// leaked. + /// + /// Must be called after the library is initialized. + #[inline] + fn emplace(self) -> Instance + where + Self::Base: Instanciable, + { + Instance::emplace(self) + } +} + +/// Trait used to provide information of Godot-exposed methods of a script class. +pub trait NativeClassMethods: NativeClass { + /// Function that registers all exposed methods to Godot. + fn register(builder: &ClassBuilder); +} + +/// Trait for types that can be used as the `owner` arguments of exported methods. This trait +/// is sealed and has no public interface. +/// +/// # Safety +/// +/// Whenever a NativeScript methods is called, it's assumed that the owner is safe to use. +/// When calling a method that may call non-thread-safe methods on its owner from non-Rust +/// code, the official [thread-safety guidelines][thread-safety] must be followed to prevent +/// undefined behavior. +/// +/// [thread-safety]: https://docs.godotengine.org/en/stable/tutorials/threads/thread_safe_apis.html +pub trait OwnerArg<'a, T: GodotObject, Access: ThreadAccess + 'static>: private::Sealed { + #[doc(hidden)] + fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self; +} + +impl<'a, T> private::Sealed for &'a T where T: GodotObject {} +impl<'a, T, Access> OwnerArg<'a, T, Access> for &'a T +where + T: GodotObject, + Access: ThreadAccess + 'static, +{ + #[inline] + fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self { + owner.as_ref() + } +} + +impl<'a, T, Access> private::Sealed for TRef<'a, T, Access> +where + T: GodotObject, + Access: ThreadAccess + 'static, +{ +} +impl<'a, T, Access> OwnerArg<'a, T, Access> for TRef<'a, T, Access> +where + T: GodotObject, + Access: ThreadAccess + 'static, +{ + #[inline] + fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self { + owner + } +} + +mod private { + pub trait Sealed {} +} diff --git a/gdnative-core/src/nativescript/export.rs b/gdnative-core/src/export/class_builder.rs similarity index 55% rename from gdnative-core/src/nativescript/export.rs rename to gdnative-core/src/export/class_builder.rs index 649354488..f8402269d 100644 --- a/gdnative-core/src/nativescript/export.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -33,205 +33,31 @@ // Temporary for unsafe method registration #![allow(deprecated)] -use crate::*; - use std::ffi::CString; use std::marker::PhantomData; use std::ptr; use crate::core_types::{GodotString, Variant}; -use crate::nativescript::{user_data::UserData, NativeClass, NativeClassMethods}; -use crate::object::{GodotObject, NewRef, TRef}; +use crate::export::*; +use crate::object::NewRef; use crate::private::get_api; -use super::class_registry; -use super::emplace; - -pub mod method; -pub mod property; - -pub use self::method::{ - Method, MethodBuilder, RpcMode, ScriptMethod, ScriptMethodAttributes, ScriptMethodFn, Varargs, -}; -pub use self::property::{Export, ExportInfo, PropertyBuilder, Usage as PropertyUsage}; - -/// A handle that can register new classes to the engine during initialization. -/// -/// See [`godot_nativescript_init`](macro.godot_nativescript_init.html) and -/// [`godot_init`](macro.godot_init.html). -#[derive(Copy, Clone)] -pub struct InitHandle { - #[doc(hidden)] - handle: *mut libc::c_void, -} - -impl InitHandle { - #[doc(hidden)] - #[inline] - pub unsafe fn new(handle: *mut libc::c_void) -> Self { - InitHandle { handle } - } - - /// Registers a new class to the engine. - #[inline] - pub fn add_class(self) - where - C: NativeClassMethods, - { - self.add_maybe_tool_class::(false) - } - - /// Registers a new tool class to the engine. - #[inline] - pub fn add_tool_class(self) - where - C: NativeClassMethods, - { - self.add_maybe_tool_class::(true) - } - - #[inline] - fn add_maybe_tool_class(self, is_tool: bool) - where - C: NativeClassMethods, - { - if !class_registry::register_class::() { - panic!( - "`{type_name}` has already been registered", - type_name = std::any::type_name::() - ); - } - unsafe { - let class_name = CString::new(C::class_name()).unwrap(); - let base_name = CString::new(C::Base::class_name()).unwrap(); - - let create = { - unsafe extern "C" fn constructor( - this: *mut sys::godot_object, - _method_data: *mut libc::c_void, - ) -> *mut libc::c_void { - use std::panic::{self, AssertUnwindSafe}; - - let this = match ptr::NonNull::new(this) { - Some(this) => this, - None => { - godot_error!( - "gdnative-core: error constructing {}: owner pointer is null", - C::class_name(), - ); - - return ptr::null_mut(); - } - }; - - let owner = match object::RawObject::::try_from_sys_ref(this) { - Some(owner) => owner, - None => { - godot_error!( - "gdnative-core: error constructing {}: incompatible owner type, expecting {}", - C::class_name(), - C::Base::class_name(), - ); - return ptr::null_mut(); - } - }; - - let val = match panic::catch_unwind(AssertUnwindSafe(|| { - emplace::take() - .unwrap_or_else(|| C::init(TRef::new(C::Base::cast_ref(owner)))) - })) { - Ok(val) => val, - Err(_) => { - godot_error!( - "gdnative-core: error constructing {}: constructor panicked", - C::class_name(), - ); - return ptr::null_mut(); - } - }; - - let wrapper = C::UserData::new(val); - C::UserData::into_user_data(wrapper) as *mut _ - } - - sys::godot_instance_create_func { - create_func: Some(constructor::), - method_data: ptr::null_mut(), - free_func: None, - } - }; - - let destroy = { - unsafe extern "C" fn destructor( - _this: *mut sys::godot_object, - _method_data: *mut libc::c_void, - user_data: *mut libc::c_void, - ) { - if user_data.is_null() { - godot_error!( - "gdnative-core: user data pointer for {} is null (did the constructor fail?)", - C::class_name(), - ); - return; - } - - let wrapper = C::UserData::consume_user_data_unchecked(user_data); - drop(wrapper) - } - - sys::godot_instance_destroy_func { - destroy_func: Some(destructor::), - method_data: ptr::null_mut(), - free_func: None, - } - }; - - if is_tool { - (get_api().godot_nativescript_register_tool_class)( - self.handle as *mut _, - class_name.as_ptr() as *const _, - base_name.as_ptr() as *const _, - create, - destroy, - ); - } else { - (get_api().godot_nativescript_register_class)( - self.handle as *mut _, - class_name.as_ptr() as *const _, - base_name.as_ptr() as *const _, - create, - destroy, - ); - } - - (get_api().godot_nativescript_set_type_tag)( - self.handle as *mut _, - class_name.as_ptr() as *const _, - crate::nativescript::type_tag::create::(), - ); - - let builder = ClassBuilder { - init_handle: self.handle, - class_name, - _marker: PhantomData, - }; - - C::register_properties(&builder); - - // register methods - C::register(&builder); - } - } -} - #[derive(Debug)] pub struct ClassBuilder { - init_handle: *mut libc::c_void, - class_name: CString, + pub(super) init_handle: *mut libc::c_void, + pub(super) class_name: CString, _marker: PhantomData, } impl ClassBuilder { + pub(crate) fn new(init_handle: *mut libc::c_void, class_name: CString) -> Self { + Self { + init_handle, + class_name, + _marker: PhantomData, + } + } + #[inline] #[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")] pub fn add_method_advanced(&self, method: ScriptMethod) { @@ -292,7 +118,7 @@ impl ClassBuilder { /// Basic usage: /// ``` /// use gdnative::prelude::*; - /// use gdnative::nativescript::export::{RpcMode, Varargs}; + /// use gdnative::export::{RpcMode, Varargs}; /// /// #[derive(NativeClass)] /// #[register_with(Self::my_register)] diff --git a/gdnative-core/src/nativescript/class_registry.rs b/gdnative-core/src/export/class_registry.rs similarity index 95% rename from gdnative-core/src/nativescript/class_registry.rs rename to gdnative-core/src/export/class_registry.rs index 1911ade28..4604b7b89 100644 --- a/gdnative-core/src/nativescript/class_registry.rs +++ b/gdnative-core/src/export/class_registry.rs @@ -1,4 +1,4 @@ -use crate::nativescript::NativeClass; +use crate::export::NativeClass; use once_cell::sync::Lazy; use parking_lot::RwLock; use std::any::TypeId; diff --git a/gdnative-core/src/nativescript/emplace.rs b/gdnative-core/src/export/emplace.rs similarity index 100% rename from gdnative-core/src/nativescript/emplace.rs rename to gdnative-core/src/export/emplace.rs diff --git a/gdnative-core/src/nativescript/macros.rs b/gdnative-core/src/export/macros.rs similarity index 72% rename from gdnative-core/src/nativescript/macros.rs rename to gdnative-core/src/export/macros.rs index 50422970c..d09932a82 100644 --- a/gdnative-core/src/nativescript/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -1,74 +1,5 @@ #![macro_use] -/// Declare the API endpoint to initialize nativescript classes on startup. -/// -/// By default this declares an extern function named `godot_nativescript_init`. -/// This can be overridden, for example: -/// -/// ```ignore -/// // Declares an extern function named custom_nativescript_init instead of -/// // godot_nativescript_init. -/// godot_gdnative_terminate!(my_registration_callback as custom_nativescript_init); -/// ``` -/// -/// Overriding the default entry point names can be useful if several gdnative -/// libraries are linked statically to avoid name clashes. -#[macro_export] -macro_rules! godot_nativescript_init { - () => { - fn godot_nativescript_init_empty(_init: $crate::nativescript::export::InitHandle) {} - $crate::godot_nativescript_init!(godot_nativescript_init_empty); - }; - ($callback:ident) => { - $crate::godot_nativescript_init!($callback as godot_nativescript_init); - }; - (_ as $fn_name:ident) => { - fn godot_nativescript_init_empty(_init: $crate::nativescript::export::InitHandle) {} - $crate::godot_nativescript_init!(godot_nativescript_init_empty as $fn_name); - }; - ($callback:ident as $fn_name:ident) => { - #[no_mangle] - #[doc(hidden)] - #[allow(unused_unsafe)] - pub unsafe extern "C" fn $fn_name(handle: *mut $crate::libc::c_void) { - if !$crate::private::is_api_bound() { - return; - } - - let __result = ::std::panic::catch_unwind(|| { - $callback($crate::nativescript::export::InitHandle::new(handle)); - }); - - if __result.is_err() { - $crate::godot_error!("gdnative-core: nativescript_init callback panicked"); - } - } - }; -} - -/// Declare all the API endpoints necessary to initialize a NativeScript library. -/// -/// `godot_init!(init)` is a shorthand for: -/// -/// ```ignore -/// godot_gdnative_init!(); -/// godot_nativescript_init!(init); -/// godot_gdnative_terminate!(); -/// ``` -/// -/// This declares three extern functions, named `godot_gdnative_init`, -/// `godot_nativescript_init`, and `godot_gdnative_terminate`. If you need different prefixes -/// to avoid name clashes when multiple GDNative libraries are linked statically, please use -/// the respective macros directly. -#[macro_export] -macro_rules! godot_init { - ($callback:ident) => { - $crate::godot_gdnative_init!(); - $crate::godot_nativescript_init!($callback); - $crate::godot_gdnative_terminate!(); - }; -} - #[doc(hidden)] #[macro_export] macro_rules! godot_wrap_method_parameter_count { @@ -97,7 +28,8 @@ macro_rules! godot_wrap_method_inner { #[derive(Copy, Clone, Default)] struct ThisMethod; - use $crate::nativescript::{NativeClass, Instance, RefInstance, OwnerArg}; + use $crate::export::{NativeClass, OwnerArg}; + use $crate::object::{Instance, RefInstance}; use ::gdnative::derive::FromVarargs; #[derive(FromVarargs)] @@ -108,7 +40,7 @@ macro_rules! godot_wrap_method_inner { } #[allow(unused_variables, unused_assignments, unused_mut)] - impl $crate::nativescript::export::method::StaticArgsMethod<$type_name> for ThisMethod { + impl $crate::export::StaticArgsMethod<$type_name> for ThisMethod { type Args = Args; fn call( &self, @@ -139,7 +71,7 @@ macro_rules! godot_wrap_method_inner { } } - $crate::nativescript::export::method::StaticArgs::new(ThisMethod) + $crate::export::StaticArgs::new(ThisMethod) } }; } diff --git a/gdnative-core/src/nativescript/export/method.rs b/gdnative-core/src/export/method.rs similarity index 99% rename from gdnative-core/src/nativescript/export/method.rs rename to gdnative-core/src/export/method.rs index 9ad58f5e1..66c3cff39 100644 --- a/gdnative-core/src/nativescript/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -8,13 +8,13 @@ use std::fmt; use std::marker::PhantomData; use crate::core_types::{FromVariant, FromVariantError, Variant}; +use crate::export::class::NativeClass; +use crate::export::ClassBuilder; use crate::log::Site; -use crate::nativescript::class::{NativeClass, RefInstance}; use crate::object::ownership::Shared; +use crate::object::RefInstance; use crate::object::{Ref, TRef}; -use super::ClassBuilder; - /// Builder type used to register a method on a `NativeClass`. pub struct MethodBuilder<'a, C, F> { class_builder: &'a super::ClassBuilder, diff --git a/gdnative-core/src/export/mod.rs b/gdnative-core/src/export/mod.rs new file mode 100644 index 000000000..2165bf83a --- /dev/null +++ b/gdnative-core/src/export/mod.rs @@ -0,0 +1,43 @@ +//! Functionality for user-defined types exported to the engine (native scripts) +//! +//! NativeScript allows users to have their own scripts in a native language (in this case Rust). +//! It is _not_ the same as GDNative, the native interface to call into Godot. +//! +//! Symbols in this module allow registration, exporting and management of user-defined types +//! which are wrapped in native scripts. +//! +//! If you are looking for how to manage Godot core types or classes (objects), check +//! out the [`core_types`][crate::core_types] and [`object`][crate::object] modules, respectively. + +mod class; +mod class_builder; +mod macros; +mod method; +mod property; + +pub(crate) mod class_registry; +pub(crate) mod emplace; +pub(crate) mod type_tag; + +pub mod user_data; + +pub use crate::godot_wrap_method; +pub use class::*; +pub use class_builder::*; +pub use method::*; +pub use property::*; + +/// Trait for exportable types. +pub trait Export: crate::core_types::ToVariant { + /// A type-specific hint type that is valid for the type being exported. + /// + /// If this type shows up as `NoHint`, a private, uninhabitable type indicating + /// that there are no hints available for the time being, users *must* use `None` + /// for properties of this type. This ensures that it will not be a breaking change + /// to add a hint for the type later, since it supports no operations and cannot + /// be named directly in user code. + type Hint; + + /// Returns `ExportInfo` given an optional typed hint. + fn export_info(hint: Option) -> ExportInfo; +} diff --git a/gdnative-core/src/nativescript/export/property.rs b/gdnative-core/src/export/property.rs similarity index 93% rename from gdnative-core/src/nativescript/export/property.rs rename to gdnative-core/src/export/property.rs index e00e802ed..13cb7a9c2 100644 --- a/gdnative-core/src/nativescript/export/property.rs +++ b/gdnative-core/src/export/property.rs @@ -1,35 +1,19 @@ //! Property registration. +use accessor::{Getter, RawGetter, RawSetter, Setter}; +use invalid_accessor::{InvalidGetter, InvalidSetter}; + use crate::core_types::*; -use crate::nativescript::{Instance, NativeClass}; +use crate::export::NativeClass; +use crate::export::{ClassBuilder, Export}; use crate::object::ownership::Shared; -use crate::object::GodotObject; -use crate::object::Ref; +use crate::object::{GodotObject, Instance, Ref}; use crate::private::get_api; -use super::ClassBuilder; - mod accessor; -pub mod hint; - -pub use hint::*; +mod invalid_accessor; -use accessor::{Getter, InvalidGetter, InvalidSetter, RawGetter, RawSetter, Setter}; - -/// Trait for exportable types. -pub trait Export: ToVariant { - /// A type-specific hint type that is valid for the type being exported. - /// - /// If this type shows up as `NoHint`, a private, uninhabitable type indicating - /// that there are no hints available for the time being, users *must* use `None` - /// for properties of this type. This ensures that it will not be a breaking change - /// to add a hint for the type later, since it supports no operations and cannot - /// be named directly in user code. - type Hint; - - /// Returns `ExportInfo` given an optional typed hint. - fn export_info(hint: Option) -> ExportInfo; -} +pub mod hint; /// Metadata about the exported property. #[derive(Debug)] @@ -73,7 +57,7 @@ pub struct PropertyBuilder<'a, C, T: Export, S = InvalidSetter<'a>, G = InvalidG getter: G, default: Option, hint: Option, - usage: Usage, + usage: PropertyUsage, class_builder: &'a ClassBuilder, } @@ -91,7 +75,7 @@ where getter: InvalidGetter::new(name), default: None, hint: None, - usage: Usage::DEFAULT, + usage: PropertyUsage::DEFAULT, class_builder, } } @@ -283,14 +267,14 @@ where /// Sets a property usage. #[inline] - pub fn with_usage(mut self, usage: Usage) -> Self { + pub fn with_usage(mut self, usage: PropertyUsage) -> Self { self.usage = usage; self } } bitflags::bitflags! { - pub struct Usage: u32 { + pub struct PropertyUsage: u32 { const STORAGE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORAGE as u32; const EDITOR = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_EDITOR as u32; const NETWORK = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_NETWORK as u32; @@ -315,7 +299,7 @@ bitflags::bitflags! { } } -impl Usage { +impl PropertyUsage { #[inline] pub fn to_sys(self) -> sys::godot_property_usage_flags { self.bits() as sys::godot_property_usage_flags @@ -474,7 +458,7 @@ mod impl_export { } impl Export for VariantArray { - type Hint = ArrayHint; + type Hint = hint::ArrayHint; #[inline] fn export_info(hint: Option) -> ExportInfo { diff --git a/gdnative-core/src/nativescript/export/property/accessor.rs b/gdnative-core/src/export/property/accessor.rs similarity index 98% rename from gdnative-core/src/nativescript/export/property/accessor.rs rename to gdnative-core/src/export/property/accessor.rs index 9583d5ce1..9380848fb 100644 --- a/gdnative-core/src/nativescript/export/property/accessor.rs +++ b/gdnative-core/src/export/property/accessor.rs @@ -4,14 +4,10 @@ use std::marker::PhantomData; use std::ptr::NonNull; use crate::core_types::{FromVariant, ToVariant, Variant}; -use crate::nativescript::user_data::{Map, MapMut, UserData}; -use crate::nativescript::NativeClass; +use crate::export::user_data::{Map, MapMut, UserData}; +use crate::export::NativeClass; use crate::object::{GodotObject, RawObject, TRef}; -mod invalid; - -pub use self::invalid::{InvalidGetter, InvalidSetter}; - /// Trait for raw property setters. /// /// This is an internal interface. User code should not use this directly. diff --git a/gdnative-core/src/nativescript/export/property/hint.rs b/gdnative-core/src/export/property/hint.rs similarity index 98% rename from gdnative-core/src/nativescript/export/property/hint.rs rename to gdnative-core/src/export/property/hint.rs index 34f5ca572..98ed102ac 100644 --- a/gdnative-core/src/nativescript/export/property/hint.rs +++ b/gdnative-core/src/export/property/hint.rs @@ -16,7 +16,7 @@ use super::{Export, ExportInfo}; /// Basic usage: /// /// ```rust -/// use gdnative_core::nativescript::export::property::hint::RangeHint; +/// use gdnative_core::export::hint::RangeHint; /// /// let hint: RangeHint = RangeHint::new(0.0, 20.0).or_greater(); /// ``` @@ -110,7 +110,7 @@ where /// Basic usage: /// /// ```rust -/// use gdnative_core::nativescript::export::property::hint::EnumHint; +/// use gdnative_core::export::hint::EnumHint; /// /// let hint = EnumHint::new(vec!["Foo".into(), "Bar".into(), "Baz".into()]); /// ``` diff --git a/gdnative-core/src/nativescript/export/property/accessor/invalid.rs b/gdnative-core/src/export/property/invalid_accessor.rs similarity index 97% rename from gdnative-core/src/nativescript/export/property/accessor/invalid.rs rename to gdnative-core/src/export/property/invalid_accessor.rs index ad2bfdf4d..605a88e72 100644 --- a/gdnative-core/src/nativescript/export/property/accessor/invalid.rs +++ b/gdnative-core/src/export/property/invalid_accessor.rs @@ -3,10 +3,9 @@ use std::mem; use crate::core_types::{FromVariant, ToVariant, Variant}; -use crate::nativescript::NativeClass; -use crate::*; +use crate::export::NativeClass; -use super::{RawGetter, RawSetter}; +use super::accessor::{RawGetter, RawSetter}; /// Default setter used for a new property indicating that no valid setter is present. Outputs errors when invoked. #[derive(Debug)] diff --git a/gdnative-core/src/nativescript/type_tag.rs b/gdnative-core/src/export/type_tag.rs similarity index 98% rename from gdnative-core/src/nativescript/type_tag.rs rename to gdnative-core/src/export/type_tag.rs index e6f1e06fb..087563d2e 100644 --- a/gdnative-core/src/nativescript/type_tag.rs +++ b/gdnative-core/src/export/type_tag.rs @@ -3,7 +3,7 @@ use std::mem::{align_of, size_of}; use indexmap::IndexSet; -use crate::nativescript::NativeClass; +use crate::export::NativeClass; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] struct Tag { diff --git a/gdnative-core/src/nativescript/user_data.rs b/gdnative-core/src/export/user_data.rs similarity index 99% rename from gdnative-core/src/nativescript/user_data.rs rename to gdnative-core/src/export/user_data.rs index 6b076b344..496de698b 100644 --- a/gdnative-core/src/nativescript/user_data.rs +++ b/gdnative-core/src/export/user_data.rs @@ -25,7 +25,7 @@ //! ```ignore //! #[derive(NativeClass)] //! #[inherit(gdnative::api::Node)] -//! #[user_data(gdnative::nativescript::user_data::MutexData)] +//! #[user_data(gdnative::export::user_data::MutexData)] //! struct HelloWorld; //! ``` //! @@ -69,7 +69,7 @@ use std::mem; use std::sync::Arc; use std::time::Duration; -use crate::nativescript::NativeClass; +use crate::export::NativeClass; /// Trait for customizable user-data wrappers. /// diff --git a/gdnative-core/src/init/info.rs b/gdnative-core/src/init/info.rs new file mode 100644 index 000000000..a43dd9b8e --- /dev/null +++ b/gdnative-core/src/init/info.rs @@ -0,0 +1,96 @@ +use crate::core_types::GodotString; + +/// Context for the [`godot_gdnative_init`][crate::init::godot_gdnative_init] callback. +pub struct InitializeInfo { + in_editor: bool, + active_library_path: GodotString, + options: *mut crate::sys::godot_gdnative_init_options, +} + +impl InitializeInfo { + /// Returns true if the library is loaded in the Godot Editor. + #[inline] + pub fn in_editor(&self) -> bool { + self.in_editor + } + + /// Returns a path to the library relative to the project. + /// + /// Example: `res://../../target/debug/libhello_world.dylib` + #[inline] + pub fn active_library_path(&self) -> &GodotString { + &self.active_library_path + } + + /// Internal interface. + /// + /// # Safety + /// + /// Will `panic!()` if options is NULL, UB if invalid. + #[inline] + #[doc(hidden)] + pub unsafe fn new(options: *mut crate::sys::godot_gdnative_init_options) -> Self { + assert!(!options.is_null(), "options were NULL"); + let crate::sys::godot_gdnative_init_options { + in_editor, + active_library_path, + .. + } = *options; + + let active_library_path = GodotString::clone_from_sys(*active_library_path); + + Self { + in_editor, + active_library_path, + options, + } + } + + #[inline] + pub fn report_loading_error(&self, message: T) + where + T: std::fmt::Display, + { + let crate::sys::godot_gdnative_init_options { + report_loading_error, + gd_native_library, + .. + } = unsafe { *self.options }; + + if let Some(report_loading_error_fn) = report_loading_error { + // Add the trailing zero and convert Display => String + let message = format!("{}\0", message); + + // Convert to FFI compatible string + let message = std::ffi::CStr::from_bytes_with_nul(message.as_bytes()) + .expect("message should not have a NULL"); + + unsafe { + report_loading_error_fn(gd_native_library, message.as_ptr()); + } + } + } +} + +/// Context for the [`godot_gdnative_terminate`][crate::init::godot_gdnative_terminate] callback. +pub struct TerminateInfo { + in_editor: bool, +} + +impl TerminateInfo { + #[inline] + #[doc(hidden)] // avoids clippy warning: unsafe function's docs miss `# Safety` section + pub unsafe fn new(options: *mut crate::sys::godot_gdnative_terminate_options) -> Self { + assert!(!options.is_null(), "options were NULL"); + + let crate::sys::godot_gdnative_terminate_options { in_editor } = *options; + + Self { in_editor } + } + + /// Returns `true` if the library is loaded in the Godot Editor. + #[inline] + pub fn in_editor(&self) -> bool { + self.in_editor + } +} diff --git a/gdnative-core/src/init/init_handle.rs b/gdnative-core/src/init/init_handle.rs new file mode 100644 index 000000000..db6e8820e --- /dev/null +++ b/gdnative-core/src/init/init_handle.rs @@ -0,0 +1,171 @@ +use crate::export::user_data::UserData; +use crate::export::{class_registry, emplace, ClassBuilder, NativeClass, NativeClassMethods}; +use crate::object::{GodotObject, RawObject, TRef}; +use crate::private::get_api; +use std::ffi::CString; +use std::ptr; + +/// A handle that can register new classes to the engine during initialization. +/// +/// See [`godot_nativescript_init`](macro.godot_nativescript_init.html) and +/// [`godot_init`](macro.godot_init.html). +#[derive(Copy, Clone)] +pub struct InitHandle { + #[doc(hidden)] + handle: *mut libc::c_void, +} + +impl InitHandle { + #[doc(hidden)] + #[inline] + pub unsafe fn new(handle: *mut libc::c_void) -> Self { + InitHandle { handle } + } + + /// Registers a new class to the engine. + #[inline] + pub fn add_class(self) + where + C: NativeClassMethods, + { + self.add_maybe_tool_class::(false) + } + + /// Registers a new tool class to the engine. + #[inline] + pub fn add_tool_class(self) + where + C: NativeClassMethods, + { + self.add_maybe_tool_class::(true) + } + + #[inline] + fn add_maybe_tool_class(self, is_tool: bool) + where + C: NativeClassMethods, + { + if !class_registry::register_class::() { + panic!( + "`{type_name}` has already been registered", + type_name = std::any::type_name::() + ); + } + unsafe { + let class_name = CString::new(C::class_name()).unwrap(); + let base_name = CString::new(C::Base::class_name()).unwrap(); + + let create = { + unsafe extern "C" fn constructor( + this: *mut sys::godot_object, + _method_data: *mut libc::c_void, + ) -> *mut libc::c_void { + use std::panic::{self, AssertUnwindSafe}; + + let this = match ptr::NonNull::new(this) { + Some(this) => this, + None => { + godot_error!( + "gdnative-core: error constructing {}: owner pointer is null", + C::class_name(), + ); + + return ptr::null_mut(); + } + }; + + let owner = match RawObject::::try_from_sys_ref(this) { + Some(owner) => owner, + None => { + godot_error!( + "gdnative-core: error constructing {}: incompatible owner type, expecting {}", + C::class_name(), + C::Base::class_name(), + ); + return ptr::null_mut(); + } + }; + + let val = match panic::catch_unwind(AssertUnwindSafe(|| { + emplace::take() + .unwrap_or_else(|| C::init(TRef::new(C::Base::cast_ref(owner)))) + })) { + Ok(val) => val, + Err(_) => { + godot_error!( + "gdnative-core: error constructing {}: constructor panicked", + C::class_name(), + ); + return ptr::null_mut(); + } + }; + + let wrapper = C::UserData::new(val); + C::UserData::into_user_data(wrapper) as *mut _ + } + + sys::godot_instance_create_func { + create_func: Some(constructor::), + method_data: ptr::null_mut(), + free_func: None, + } + }; + + let destroy = { + unsafe extern "C" fn destructor( + _this: *mut sys::godot_object, + _method_data: *mut libc::c_void, + user_data: *mut libc::c_void, + ) { + if user_data.is_null() { + godot_error!( + "gdnative-core: user data pointer for {} is null (did the constructor fail?)", + C::class_name(), + ); + return; + } + + let wrapper = C::UserData::consume_user_data_unchecked(user_data); + drop(wrapper) + } + + sys::godot_instance_destroy_func { + destroy_func: Some(destructor::), + method_data: ptr::null_mut(), + free_func: None, + } + }; + + if is_tool { + (get_api().godot_nativescript_register_tool_class)( + self.handle as *mut _, + class_name.as_ptr() as *const _, + base_name.as_ptr() as *const _, + create, + destroy, + ); + } else { + (get_api().godot_nativescript_register_class)( + self.handle as *mut _, + class_name.as_ptr() as *const _, + base_name.as_ptr() as *const _, + create, + destroy, + ); + } + + (get_api().godot_nativescript_set_type_tag)( + self.handle as *mut _, + class_name.as_ptr() as *const _, + crate::export::type_tag::create::(), + ); + + let builder = ClassBuilder::new(self.handle, class_name); + + C::register_properties(&builder); + + // register methods + C::register(&builder); + } + } +} diff --git a/gdnative-core/src/init/macros.rs b/gdnative-core/src/init/macros.rs new file mode 100644 index 000000000..9b001e3d4 --- /dev/null +++ b/gdnative-core/src/init/macros.rs @@ -0,0 +1,168 @@ +#![macro_use] + +/// Declare the API endpoint to initialize export classes on startup. +/// +/// By default this declares an extern function named `godot_nativescript_init`. +/// This can be overridden, for example: +/// +/// ```ignore +/// // Declares an extern function named custom_nativescript_init instead of +/// // godot_nativescript_init. +/// godot_gdnative_terminate!(my_registration_callback as custom_nativescript_init); +/// ``` +/// +/// Overriding the default entry point names can be useful if several gdnative +/// libraries are linked statically to avoid name clashes. +#[macro_export] +macro_rules! godot_nativescript_init { + () => { + fn godot_nativescript_init_empty(_init: $crate::init::InitHandle) {} + $crate::godot_nativescript_init!(godot_nativescript_init_empty); + }; + ($callback:ident) => { + $crate::godot_nativescript_init!($callback as godot_nativescript_init); + }; + (_ as $fn_name:ident) => { + fn godot_nativescript_init_empty(_init: $crate::init::InitHandle) {} + $crate::godot_nativescript_init!(godot_nativescript_init_empty as $fn_name); + }; + ($callback:ident as $fn_name:ident) => { + #[no_mangle] + #[doc(hidden)] + #[allow(unused_unsafe)] + pub unsafe extern "C" fn $fn_name(handle: *mut $crate::libc::c_void) { + if !$crate::private::is_api_bound() { + return; + } + + let __result = ::std::panic::catch_unwind(|| { + $callback($crate::init::InitHandle::new(handle)); + }); + + if __result.is_err() { + $crate::godot_error!("gdnative-core: nativescript_init callback panicked"); + } + } + }; +} + +/// Declare the API endpoint to initialize the gdnative API on startup. +/// +/// By default this declares an extern function named `godot_gdnative_init`. +/// This can be overridden, for example: +/// +/// ```ignore +/// // Declares an extern function named custom_gdnative_init instead of +/// // godot_gdnative_init. +/// godot_gdnative_init!(my_init_callback as custom_gdnative_init); +/// ``` +/// +/// Overriding the default entry point names can be useful if several gdnative +/// libraries are linked statically to avoid name clashes. +#[macro_export] +macro_rules! godot_gdnative_init { + () => { + fn godot_gdnative_init_empty(_options: &$crate::init::InitializeInfo) {} + $crate::init::godot_gdnative_init!(godot_gdnative_init_empty); + }; + (_ as $fn_name:ident) => { + fn godot_gdnative_init_empty(_options: &$crate::init::InitializeInfo) {} + $crate::init::godot_gdnative_init!(godot_gdnative_init_empty as $fn_name); + }; + ($callback:ident) => { + $crate::init::godot_gdnative_init!($callback as godot_gdnative_init); + }; + ($callback:ident as $fn_name:ident) => { + #[no_mangle] + #[doc(hidden)] + #[allow(unused_unsafe)] + pub unsafe extern "C" fn $fn_name(options: *mut $crate::sys::godot_gdnative_init_options) { + if !$crate::private::bind_api(options) { + // Can't use godot_error here because the API is not bound. + // Init errors should be reported by bind_api. + return; + } + + let __result = ::std::panic::catch_unwind(|| { + let callback_options = $crate::init::InitializeInfo::new(options); + $callback(&callback_options) + }); + if __result.is_err() { + $crate::godot_error!("gdnative-core: gdnative_init callback panicked"); + } + } + }; +} + +/// Declare the API endpoint invoked during shutdown. +/// +/// By default this declares an extern function named `godot_gdnative_terminate`. +/// This can be overridden, for example: +/// +/// ```ignore +/// // Declares an extern function named custom_gdnative_terminate instead of +/// // godot_gdnative_terminate. +/// godot_gdnative_terminate!(my_shutdown_callback as custom_gdnative_terminate); +/// ``` +/// +/// Overriding the default entry point names can be useful if several gdnative +/// libraries are linked statically to avoid name clashes. +#[macro_export] +macro_rules! godot_gdnative_terminate { + () => { + fn godot_gdnative_terminate_empty(_term_info: &$crate::init::TerminateInfo) {} + $crate::init::godot_gdnative_terminate!(godot_gdnative_terminate_empty); + }; + ($callback:ident) => { + $crate::init::godot_gdnative_terminate!($callback as godot_gdnative_terminate); + }; + (_ as $fn_name:ident) => { + fn godot_gdnative_terminate_empty(_term_info: &$crate::init::TerminateInfo) {} + $crate::init::godot_gdnative_terminate!(godot_gdnative_terminate_empty as $fn_name); + }; + ($callback:ident as $fn_name:ident) => { + #[no_mangle] + #[doc(hidden)] + #[allow(unused_unsafe)] + pub unsafe extern "C" fn $fn_name( + options: *mut $crate::sys::godot_gdnative_terminate_options, + ) { + if !$crate::private::is_api_bound() { + return; + } + + let __result = ::std::panic::catch_unwind(|| { + let term_info = $crate::init::TerminateInfo::new(options); + $callback(&term_info) + }); + if __result.is_err() { + $crate::godot_error!("gdnative-core: nativescript_init callback panicked"); + } + + $crate::private::cleanup_internal_state(); + } + }; +} + +/// Declare all the API endpoints necessary to initialize a NativeScript library. +/// +/// `godot_init!(init)` is a shorthand for: +/// +/// ```ignore +/// godot_gdnative_init!(); +/// godot_nativescript_init!(init); +/// godot_gdnative_terminate!(); +/// ``` +/// +/// This declares three extern functions, named `godot_gdnative_init`, +/// `godot_nativescript_init`, and `godot_gdnative_terminate`. If you need different prefixes +/// to avoid name clashes when multiple GDNative libraries are linked statically, please use +/// the respective macros directly. +#[macro_export] +macro_rules! godot_init { + ($callback:ident) => { + $crate::init::godot_gdnative_init!(); + $crate::init::godot_nativescript_init!($callback); + $crate::init::godot_gdnative_terminate!(); + }; +} diff --git a/gdnative-core/src/init/mod.rs b/gdnative-core/src/init/mod.rs new file mode 100644 index 000000000..d2c15c214 --- /dev/null +++ b/gdnative-core/src/init/mod.rs @@ -0,0 +1,12 @@ +//! Global initialization and termination of the library. + +mod info; +mod init_handle; +mod macros; + +pub use info::*; +pub use init_handle::*; + +pub use crate::{ + godot_gdnative_init, godot_gdnative_terminate, godot_init, godot_nativescript_init, +}; diff --git a/gdnative-core/src/lib.rs b/gdnative-core/src/lib.rs index dcc368d9f..537fd9887 100644 --- a/gdnative-core/src/lib.rs +++ b/gdnative-core/src/lib.rs @@ -42,109 +42,12 @@ mod macros; pub mod core_types; -#[cfg(feature = "nativescript")] -pub mod nativescript; - +pub mod export; +pub mod init; pub mod log; pub mod object; +pub mod profiler; /// Internal low-level API for use by macros and generated bindings. Not a part of the public API. #[doc(hidden)] pub mod private; - -use core_types::GodotString; - -/// Context for the [`godot_gdnative_terminate`] callback. -pub struct TerminateInfo { - in_editor: bool, -} - -impl TerminateInfo { - #[inline] - #[doc(hidden)] // avoids clippy warning: unsafe function's docs miss `# Safety` section - pub unsafe fn new(options: *mut crate::sys::godot_gdnative_terminate_options) -> Self { - assert!(!options.is_null(), "options were NULL"); - - let crate::sys::godot_gdnative_terminate_options { in_editor } = *options; - - Self { in_editor } - } - - /// Returns `true` if the library is loaded in the Godot Editor. - #[inline] - pub fn in_editor(&self) -> bool { - self.in_editor - } -} - -/// Context for the [`godot_gdnative_init`] callback. -pub struct InitializeInfo { - in_editor: bool, - active_library_path: GodotString, - options: *mut crate::sys::godot_gdnative_init_options, -} - -impl InitializeInfo { - /// Returns true if the library is loaded in the Godot Editor. - #[inline] - pub fn in_editor(&self) -> bool { - self.in_editor - } - - /// Returns a path to the library relative to the project. - /// - /// Example: `res://../../target/debug/libhello_world.dylib` - #[inline] - pub fn active_library_path(&self) -> &GodotString { - &self.active_library_path - } - - /// Internal interface. - /// - /// # Safety - /// - /// Will `panic!()` if options is NULL, UB if invalid. - #[inline] - #[doc(hidden)] - pub unsafe fn new(options: *mut crate::sys::godot_gdnative_init_options) -> Self { - assert!(!options.is_null(), "options were NULL"); - let crate::sys::godot_gdnative_init_options { - in_editor, - active_library_path, - .. - } = *options; - - let active_library_path = GodotString::clone_from_sys(*active_library_path); - - Self { - in_editor, - active_library_path, - options, - } - } - - #[inline] - pub fn report_loading_error(&self, message: T) - where - T: std::fmt::Display, - { - let crate::sys::godot_gdnative_init_options { - report_loading_error, - gd_native_library, - .. - } = unsafe { *self.options }; - - if let Some(report_loading_error_fn) = report_loading_error { - // Add the trailing zero and convert Display => String - let message = format!("{}\0", message); - - // Convert to FFI compatible string - let message = std::ffi::CStr::from_bytes_with_nul(message.as_bytes()) - .expect("message should not have a NULL"); - - unsafe { - report_loading_error_fn(gd_native_library, message.as_ptr()); - } - } - } -} diff --git a/gdnative-core/src/log.rs b/gdnative-core/src/log.rs index e804d8cca..c818dd344 100644 --- a/gdnative-core/src/log.rs +++ b/gdnative-core/src/log.rs @@ -2,6 +2,9 @@ use std::ffi::CStr; use std::fmt::{self, Display}; +// Collection of macros accessing the Godot engine log/print functionality +pub use crate::{godot_dbg, godot_error, godot_print, godot_warn}; + use crate::core_types::GodotString; use crate::private; diff --git a/gdnative-core/src/macros.rs b/gdnative-core/src/macros.rs index cac24ce63..955b3f573 100644 --- a/gdnative-core/src/macros.rs +++ b/gdnative-core/src/macros.rs @@ -1,103 +1,5 @@ #![macro_use] -/// Declare the API endpoint to initialize the gdnative API on startup. -/// -/// By default this declares an extern function named `godot_gdnative_init`. -/// This can be overridden, for example: -/// -/// ```ignore -/// // Declares an extern function named custom_gdnative_init instead of -/// // godot_gdnative_init. -/// godot_gdnative_init!(my_init_callback as custom_gdnative_init); -/// ``` -/// -/// Overriding the default entry point names can be useful if several gdnative -/// libraries are linked statically to avoid name clashes. -#[macro_export] -macro_rules! godot_gdnative_init { - () => { - fn godot_gdnative_init_empty(_options: &$crate::InitializeInfo) {} - $crate::godot_gdnative_init!(godot_gdnative_init_empty); - }; - (_ as $fn_name:ident) => { - fn godot_gdnative_init_empty(_options: &$crate::InitializeInfo) {} - $crate::godot_gdnative_init!(godot_gdnative_init_empty as $fn_name); - }; - ($callback:ident) => { - $crate::godot_gdnative_init!($callback as godot_gdnative_init); - }; - ($callback:ident as $fn_name:ident) => { - #[no_mangle] - #[doc(hidden)] - #[allow(unused_unsafe)] - pub unsafe extern "C" fn $fn_name(options: *mut $crate::sys::godot_gdnative_init_options) { - if !$crate::private::bind_api(options) { - // Can't use godot_error here because the API is not bound. - // Init errors should be reported by bind_api. - return; - } - - let __result = ::std::panic::catch_unwind(|| { - let callback_options = $crate::InitializeInfo::new(options); - $callback(&callback_options) - }); - if __result.is_err() { - $crate::godot_error!("gdnative-core: gdnative_init callback panicked"); - } - } - }; -} - -/// Declare the API endpoint invoked during shutdown. -/// -/// By default this declares an extern function named `godot_gdnative_terminate`. -/// This can be overridden, for example: -/// -/// ```ignore -/// // Declares an extern function named custom_gdnative_terminate instead of -/// // godot_gdnative_terminate. -/// godot_gdnative_terminate!(my_shutdown_callback as custom_gdnative_terminate); -/// ``` -/// -/// Overriding the default entry point names can be useful if several gdnative -/// libraries are linked statically to avoid name clashes. -#[macro_export] -macro_rules! godot_gdnative_terminate { - () => { - fn godot_gdnative_terminate_empty(_term_info: &$crate::TerminateInfo) {} - $crate::godot_gdnative_terminate!(godot_gdnative_terminate_empty); - }; - ($callback:ident) => { - $crate::godot_gdnative_terminate!($callback as godot_gdnative_terminate); - }; - (_ as $fn_name:ident) => { - fn godot_gdnative_terminate_empty(_term_info: &$crate::TerminateInfo) {} - $crate::godot_gdnative_terminate!(godot_gdnative_terminate_empty as $fn_name); - }; - ($callback:ident as $fn_name:ident) => { - #[no_mangle] - #[doc(hidden)] - #[allow(unused_unsafe)] - pub unsafe extern "C" fn $fn_name( - options: *mut $crate::sys::godot_gdnative_terminate_options, - ) { - if !$crate::private::is_api_bound() { - return; - } - - let __result = ::std::panic::catch_unwind(|| { - let term_info = $crate::TerminateInfo::new(options); - $callback(&term_info) - }); - if __result.is_err() { - $crate::godot_error!("gdnative-core: nativescript_init callback panicked"); - } - - $crate::private::cleanup_internal_state(); - } - }; -} - /// Print a message using the engine's logging system (visible in the editor). #[macro_export] macro_rules! godot_print { diff --git a/gdnative-core/src/nativescript/mod.rs b/gdnative-core/src/nativescript/mod.rs deleted file mode 100644 index 45c6833df..000000000 --- a/gdnative-core/src/nativescript/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Functionality for user-defined types exported to the engine (native scripts) -//! -//! NativeScript allows users to have their own scripts in a native language (in this case Rust). -//! It is _not_ the same as GDNative, the native interface to call into Godot. -//! -//! Symbols in this module allow registration, exporting and management of user-defined types -//! which are wrapped in native scripts. -//! -//! If you are looking for how to manage Godot core types or classes (objects), check -//! out the [`core_types`][crate::core_types] and [`object`][crate::object] modules, respectively. - -mod class; -mod emplace; -mod macros; - -pub(crate) mod class_registry; -pub(crate) mod type_tag; - -pub mod export; -pub mod profiler; -pub mod user_data; - -pub use class::*; diff --git a/gdnative-core/src/nativescript/class.rs b/gdnative-core/src/object/instance.rs similarity index 76% rename from gdnative-core/src/nativescript/class.rs rename to gdnative-core/src/object/instance.rs index 0a8cba98f..0ea932fe7 100644 --- a/gdnative-core/src/nativescript/class.rs +++ b/gdnative-core/src/object/instance.rs @@ -3,170 +3,16 @@ use std::ptr::NonNull; use crate::core_types::{ FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant, }; -use crate::nativescript::export::ClassBuilder; -use crate::nativescript::user_data::{Map, MapMut, MapOwned, UserData}; +use crate::export::user_data::{Map, MapMut, MapOwned, UserData}; +use crate::export::{class_registry, emplace, NativeClass}; use crate::object::bounds::{ AssumeSafeLifetime, LifetimeConstraint, RefImplBound, SafeAsRaw, SafeDeref, }; use crate::object::memory::{ManuallyManaged, RefCounted}; use crate::object::ownership::{NonUniqueThreadAccess, Shared, ThreadAccess, ThreadLocal, Unique}; -use crate::object::{GodotObject, Instanciable}; -use crate::object::{QueueFree, RawObject, Ref, TRef}; +use crate::object::{GodotObject, Instanciable, QueueFree, RawObject, Ref, TRef}; use crate::private::{get_api, ReferenceCountedClassPlaceholder}; -use super::class_registry; -use super::emplace; -/// Trait used for describing and initializing a Godot script class. -/// -/// This trait is used to provide data and functionality to the -/// "data-part" of the class, such as name, initialization and information -/// about exported properties. -/// -/// A derive macro is available for this trait. See documentation on the -/// `NativeClass` macro for detailed usage and examples. -/// -/// For exported methods, see the [`NativeClassMethods`] trait. -/// -/// [`NativeClassMethods`]: ./trait.NativeClassMethods.html -pub trait NativeClass: Sized + 'static { - /// Base type of the class. - /// - /// In Godot, scripting languages can define "script instances" which can be - /// attached to objects. Because of the dynamic nature, the intended "inheritance" - /// is not easily implementable properly. - /// - /// Instead, delegation is used and most calls to a Godot object query the script instance - /// first. This way, some methods can be "overwritten" and new ones can be exposed. - /// - /// This only works when using so called "variant calls", since the querying of the script - /// instance is performed there. - /// When not using variant calls, any direct(*) calls have to be made to the Godot object - /// directly. - /// - /// The base type describes the "most general" type of object this script class can be - /// attached to. - /// - /// *(\*)*: GDNative enables use of "ptrcall"s, which are wrappers for function pointers. - /// Those do not do explicit checks for script implementations **unless the method - /// implementation does**. - type Base: GodotObject; - - /// User-data wrapper type of the class. - /// - /// See module-level documentation on `user_data` for more info. - type UserData: UserData; - - /// The name of the class. - /// - /// In GDNative+NativeScript many classes can be defined in one dynamic library. - /// To identify which class has to be used, a library-unique name has to be given. - fn class_name() -> &'static str; - - /// Function that creates a value of `Self`, used for the script-instance. The default - /// implementation simply panics. - /// - /// This function has a reference to the owner object as a parameter, which can be used to - /// set state on the owner upon creation or to query values - /// - /// It is possible to declare script classes without zero-argument constructors. Instances - /// of such scripts can only be created from Rust using `Instance::emplace`. See - /// documentation on `Instance::emplace` for an example. - #[inline] - fn init(_owner: TRef<'_, Self::Base, Shared>) -> Self { - panic!( - "{} does not have a zero-argument constructor", - Self::class_name() - ) - } - - /// Register any exported properties to Godot. - #[inline] - fn register_properties(_builder: &ClassBuilder) {} - - /// Convenience method to create an `Instance`. This is a new `Self::Base` - /// with the script attached. - /// - /// If `Self::Base` is manually-managed, then the resulting `Instance` must be passed to - /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be - /// leaked. - /// - /// Must be called after the library is initialized. - #[inline] - fn new_instance() -> Instance - where - Self::Base: Instanciable, - { - Instance::new() - } - - /// Convenience method to emplace `self` into an `Instance`. This is a new - /// `Self::Base` with the script attached. - /// - /// If `Self::Base` is manually-managed, then the resulting `Instance` must be passed to - /// the engine or manually freed with `Instance::free`. Otherwise, the base object will be - /// leaked. - /// - /// Must be called after the library is initialized. - #[inline] - fn emplace(self) -> Instance - where - Self::Base: Instanciable, - { - Instance::emplace(self) - } -} - -/// Trait used to provide information of Godot-exposed methods of a script class. -pub trait NativeClassMethods: NativeClass { - /// Function that registers all exposed methods to Godot. - fn register(builder: &ClassBuilder); -} - -/// Trait for types that can be used as the `owner` arguments of exported methods. This trait -/// is sealed and has no public interface. -/// -/// # Safety -/// -/// Whenever a NativeScript methods is called, it's assumed that the owner is safe to use. -/// When calling a method that may call non-thread-safe methods on its owner from non-Rust -/// code, the official [thread-safety guidelines][thread-safety] must be followed to prevent -/// undefined behavior. -/// -/// [thread-safety]: https://docs.godotengine.org/en/stable/tutorials/threads/thread_safe_apis.html -pub trait OwnerArg<'a, T: GodotObject, Access: ThreadAccess + 'static>: private::Sealed { - #[doc(hidden)] - fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self; -} - -impl<'a, T> private::Sealed for &'a T where T: GodotObject {} -impl<'a, T, Access> OwnerArg<'a, T, Access> for &'a T -where - T: GodotObject, - Access: ThreadAccess + 'static, -{ - #[inline] - fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self { - owner.as_ref() - } -} - -impl<'a, T, Access> private::Sealed for TRef<'a, T, Access> -where - T: GodotObject, - Access: ThreadAccess + 'static, -{ -} -impl<'a, T, Access> OwnerArg<'a, T, Access> for TRef<'a, T, Access> -where - T: GodotObject, - Access: ThreadAccess + 'static, -{ - #[inline] - fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self { - owner - } -} - /// A persistent reference to a GodotObject with a rust NativeClass attached. /// /// `Instance`s can be worked on directly using `map` and `map_mut` if the base object is @@ -746,14 +592,10 @@ fn try_get_user_data_ptr(owner: &RawObject) -> Option<* return None; } - if !crate::nativescript::type_tag::check::(type_tag) { + if !crate::export::type_tag::check::(type_tag) { return None; } Some((api.godot_nativescript_get_userdata)(owner_ptr)) } } - -mod private { - pub trait Sealed {} -} diff --git a/gdnative-core/src/object/mod.rs b/gdnative-core/src/object/mod.rs index 5755ff41a..9d0e3770d 100644 --- a/gdnative-core/src/object/mod.rs +++ b/gdnative-core/src/object/mod.rs @@ -4,7 +4,7 @@ //! In Godot, classes stand in an inheritance relationship, with the root at `Object`. //! //! If you are looking for how to manage user-defined types (native scripts), -//! check out the [`nativescript`][crate::nativescript] module. +//! check out the [`export`][crate::export] module. use std::borrow::Borrow; use std::ffi::CString; @@ -14,23 +14,26 @@ use std::marker::PhantomData; use std::ops::Deref; use std::ptr::NonNull; -#[cfg(feature = "nativescript")] -use crate::nativescript::{Instance, NativeClass, RefInstance}; +use bounds::{ + AssumeSafeLifetime, LifetimeConstraint, PtrWrapper, RefImplBound, RefKindSpec, SafeAsRaw, + SafeDeref, +}; +use memory::{ManuallyManaged, RefCounted, RefKind}; +use ownership::{NonUniqueThreadAccess, Shared, ThreadAccess, ThreadLocal, Unique}; +use crate::export::NativeClass; use crate::private::{get_api, ManuallyManagedClassPlaceholder, ReferenceCountedClassPlaceholder}; use crate::sys; -use bounds::{AssumeSafeLifetime, LifetimeConstraint, PtrWrapper, RefKindSpec}; -use memory::{ManuallyManaged, RefCounted, RefKind}; -use ownership::{NonUniqueThreadAccess, Shared, ThreadAccess, ThreadLocal, Unique}; -pub use self::new_ref::NewRef; -pub use self::raw::RawObject; -use crate::object::bounds::{RefImplBound, SafeAsRaw, SafeDeref}; +pub use instance::*; +pub use new_ref::NewRef; +pub use raw::RawObject; pub mod bounds; pub mod memory; pub mod ownership; +mod instance; mod new_ref; mod raw; @@ -488,7 +491,6 @@ where /// /// The resulting `Instance` is not necessarily safe to use directly. #[inline] - #[cfg(feature = "nativescript")] pub fn cast_instance(self) -> Option> where C: NativeClass, @@ -502,7 +504,6 @@ where /// /// Returns `Err(self)` if the cast failed. #[inline] - #[cfg(feature = "nativescript")] pub fn try_cast_instance(self) -> Result, Self> where C: NativeClass, @@ -934,7 +935,6 @@ impl<'a, T: GodotObject, Access: ThreadAccess> TRef<'a, T, Access> { /// Convenience method to downcast to `RefInstance` where `self` is the base object. #[inline] - #[cfg(feature = "nativescript")] pub fn cast_instance(self) -> Option> where C: NativeClass, diff --git a/gdnative-core/src/private.rs b/gdnative-core/src/private.rs index 0bce4d0ad..c99ceddc1 100644 --- a/gdnative-core/src/private.rs +++ b/gdnative-core/src/private.rs @@ -33,10 +33,7 @@ pub unsafe fn bind_api(options: *mut sys::godot_gdnative_init_options) -> bool { ObjectMethodTable::get(get_api()); ReferenceMethodTable::get(get_api()); - #[cfg(feature = "nativescript")] - { - NativeScriptMethodTable::get(get_api()); - } + NativeScriptMethodTable::get(get_api()); true } @@ -112,11 +109,9 @@ pub fn get_gdnative_library_sys() -> *mut sys::godot_object { /// This is intended to be an internal interface. #[inline] pub unsafe fn cleanup_internal_state() { - #[cfg(feature = "nativescript")] - { - crate::nativescript::type_tag::cleanup(); - crate::nativescript::class_registry::cleanup(); - } + crate::export::type_tag::cleanup(); + crate::export::class_registry::cleanup(); + GODOT_API = None; } @@ -226,8 +221,7 @@ make_method_table!(struct ReferenceMethodTable for Reference { }); // Add this one here too. It's not easy to use this macro from the -// nativescript module without making this macro public. -#[cfg(feature = "nativescript")] +// export module without making this macro public. make_method_table!(struct NativeScriptMethodTable for NativeScript { set_class_name, set_library, diff --git a/gdnative-core/src/nativescript/profiler.rs b/gdnative-core/src/profiler.rs similarity index 97% rename from gdnative-core/src/nativescript/profiler.rs rename to gdnative-core/src/profiler.rs index 9f35dc47e..b954f1c90 100644 --- a/gdnative-core/src/nativescript/profiler.rs +++ b/gdnative-core/src/profiler.rs @@ -167,7 +167,7 @@ where /// /// ```rust /// # fn main() { -/// use gdnative::nativescript::profiler::{profile, profile_sig}; +/// use gdnative::profiler::{profile, profile_sig}; /// /// let answer = profile(profile_sig!("foo"), || 42); /// assert_eq!(answer, 42); @@ -176,7 +176,7 @@ where #[macro_export] macro_rules! _profile_sig { ($tag:expr) => { - $crate::nativescript::profiler::Signature::new(file!(), line!(), $tag) + $crate::profiler::Signature::new(file!(), line!(), $tag) }; } diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 8a6e178fe..7ca6e5e74 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -39,18 +39,18 @@ mod variant; /// Will expand to /// ``` /// use gdnative::prelude::*; -/// struct Foo{} +/// struct Foo {} /// impl NativeClass for Foo { /// type Base = gdnative::api::Reference; -/// type UserData = gdnative::nativescript::user_data::LocalCellData; +/// type UserData = gdnative::export::user_data::LocalCellData; /// fn class_name() -> &'static str { /// "Foo" /// } /// } -/// impl gdnative::nativescript::NativeClassMethods for Foo { +/// impl gdnative::export::NativeClassMethods for Foo { /// fn register(builder: &ClassBuilder) { -/// use gdnative::nativescript::export::*; -/// builder.build_method("foo", gdnative::macros::godot_wrap_method!(Foo, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) +/// use gdnative::export::*; +/// builder.build_method("foo", gdnative::export::godot_wrap_method!(Foo, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) /// .with_rpc_mode(RpcMode::Disabled) /// .done_stateless(); /// } @@ -96,7 +96,7 @@ pub fn methods(meta: TokenStream, input: TokenStream) -> TokenStream { /// /// A custom tag can also be provided using the `tag` option. /// -/// See the `gdnative::nativescript::profiling` for a lower-level API to the profiler with +/// See the `gdnative::export::profiler` for a lower-level API to the profiler with /// more control. /// /// # Examples @@ -104,7 +104,7 @@ pub fn methods(meta: TokenStream, input: TokenStream) -> TokenStream { /// ```ignore /// mod foo { /// // This function will show up as `foo/bar` under Script Functions. -/// #[gdnative::profiled] +/// #[profiled] /// fn bar() { /// std::thread::sleep(std::time::Duration::from_millis(1)); /// } @@ -113,7 +113,7 @@ pub fn methods(meta: TokenStream, input: TokenStream) -> TokenStream { /// /// ```ignore /// // This function will show up as `my_custom_tag` under Script Functions. -/// #[gdnative::profiled(tag = "my_custom_tag")] +/// #[profiled(tag = "my_custom_tag")] /// fn baz() { /// std::thread::sleep(std::time::Duration::from_millis(1)); /// } @@ -163,7 +163,7 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// /// ``` /// use gdnative::prelude::*; -/// use gdnative::nativescript::export::property::{RangeHint, FloatHint}; +/// use gdnative::export::hint::{RangeHint, FloatHint}; /// /// #[derive(NativeClass)] /// #[inherit(Reference)] diff --git a/gdnative-derive/src/methods.rs b/gdnative-derive/src/methods.rs index 115255a25..1a1d15ef3 100644 --- a/gdnative-derive/src/methods.rs +++ b/gdnative-derive/src/methods.rs @@ -128,7 +128,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { quote_spanned!( sig_span=> { - let method = ::gdnative::macros::godot_wrap_method!( + let method = ::gdnative::export::godot_wrap_method!( #class_name, fn #name ( #( #args )* ) -> #ret_ty ); @@ -145,9 +145,9 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { #impl_block #derived - impl gdnative::nativescript::NativeClassMethods for #class_name { - fn register(#builder: &::gdnative::nativescript::export::ClassBuilder) { - use gdnative::nativescript::export::*; + impl gdnative::export::NativeClassMethods for #class_name { + fn register(#builder: &::gdnative::export::ClassBuilder) { + use gdnative::export::*; #(#methods)* } diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index c2a1a3b50..828d1130d 100644 --- a/gdnative-derive/src/native_script.rs +++ b/gdnative-derive/src/native_script.rs @@ -22,9 +22,9 @@ pub(crate) fn impl_empty_nativeclass(derive_input: &DeriveInput) -> TokenStream2 quote! { #derived - impl ::gdnative::prelude::NativeClass for #name { + impl ::gdnative::export::NativeClass for #name { type Base = ::gdnative::api::Object; - type UserData = ::gdnative::prelude::LocalCellData; + type UserData = ::gdnative::export::user_data::LocalCellData; fn class_name() -> &'static str { unimplemented!() @@ -56,7 +56,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result Result) -> Self { - Self::new(::gdnative::nativescript::OwnerArg::from_safe_ref(owner)) + Self::new(::gdnative::export::OwnerArg::from_safe_ref(owner)) } }) }; quote!( #derived - impl ::gdnative::nativescript::NativeClass for #name { + impl ::gdnative::export::NativeClass for #name { type Base = #base; type UserData = #user_data; @@ -123,7 +123,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result) { + fn register_properties(builder: &::gdnative::export::ClassBuilder) { #(#properties)*; #register_callback } @@ -163,7 +163,7 @@ fn parse_derive_input(input: &DeriveInput) -> Result { .map(|attr| attr.parse_args::()) .unwrap_or_else(|| { Ok(syn::parse2::( - quote! { ::gdnative::nativescript::user_data::DefaultUserData<#ident> }, + quote! { ::gdnative::export::user_data::DefaultUserData<#ident> }, ) .expect("quoted tokens for default userdata should be a valid type")) })?; diff --git a/gdnative-derive/src/profiled.rs b/gdnative-derive/src/profiled.rs index 5f6f9bd99..ea2f7a8a0 100644 --- a/gdnative-derive/src/profiled.rs +++ b/gdnative-derive/src/profiled.rs @@ -111,8 +111,8 @@ pub(crate) fn derive_profiled( let stmts = std::mem::take(&mut item_fn.block.stmts); item_fn.block = Box::new(parse_quote!({ - ::gdnative::nativescript::profiler::profile( - ::gdnative::nativescript::profiler::profile_sig!(#tag), move || { + ::gdnative::profiler::profile( + ::gdnative::profiler::profile_sig!(#tag), move || { #(#stmts)* }) })); diff --git a/gdnative-derive/src/varargs.rs b/gdnative-derive/src/varargs.rs index f99c8846e..ff73a0091 100644 --- a/gdnative-derive/src/varargs.rs +++ b/gdnative-derive/src/varargs.rs @@ -35,10 +35,10 @@ pub(crate) fn derive_from_varargs(input: DeriveInput) -> Result { return Ok(quote! { #derived - impl #generics ::gdnative::nativescript::export::method::FromVarargs for #ident #generics #where_clause { + impl #generics ::gdnative::export::FromVarargs for #ident #generics #where_clause { fn read<'a>( - #input_ident: &mut ::gdnative::nativescript::export::method::Varargs<'a>, - ) -> Result>> { + #input_ident: &mut ::gdnative::export::Varargs<'a>, + ) -> Result>> { Ok(#ident) } } @@ -113,10 +113,10 @@ pub(crate) fn derive_from_varargs(input: DeriveInput) -> Result( - #input_ident: &mut ::gdnative::nativescript::export::method::Varargs<'a>, - ) -> Result>> { + #input_ident: &mut ::gdnative::export::Varargs<'a>, + ) -> Result>> { let mut __errors = Vec::new(); #( diff --git a/gdnative/src/lib.rs b/gdnative/src/lib.rs index 373ffa2a8..cb35afd40 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -58,15 +58,7 @@ // Items, which are #[doc(hidden)] in their original crate and re-exported with a wildcard, lose // their hidden status. Re-exporting them manually and hiding the wildcard solves this. #[doc(inline)] -pub use gdnative_core::{core_types, log, nativescript, object, InitializeInfo, TerminateInfo}; - -/// Collection of declarative `godot_*` macros, mostly for GDNative registration and output. -pub mod macros { - pub use gdnative_core::{ - godot_dbg, godot_error, godot_gdnative_init, godot_gdnative_terminate, godot_init, - godot_nativescript_init, godot_print, godot_warn, godot_wrap_method, - }; -} +pub use gdnative_core::{core_types, export, init, log, object, profiler}; // Implementation details (e.g. used by macros). // However, do not re-export macros (on crate level), thus no wildcard @@ -85,7 +77,7 @@ pub mod prelude; #[cfg(feature = "bindings")] pub use gdnative_bindings as api; +/// Support for async code #[doc(inline)] #[cfg(feature = "async")] -/// Support for async code pub use gdnative_async as tasks; diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 3faebae9b..f59629919 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -1,43 +1,39 @@ +#[cfg(feature = "bindings")] +pub use gdnative_bindings::utils::*; +#[cfg(feature = "bindings")] +pub use gdnative_bindings::{ + Button, CanvasItem, CanvasLayer, ColorRect, Control, Image, Input, InputEvent, InputEventKey, + KinematicBody, KinematicBody2D, Label, Node, Node2D, Object, PackedScene, Reference, + ResourceLoader, SceneTree, Shader, Spatial, Sprite, Texture, Timer, Tween, Viewport, +}; pub use gdnative_core::core_types::{ - self, Aabb, Basis, ByteArray, Color, ColorArray, Dictionary, Float32Array, GodotError, - GodotString, Int32Array, NodePath, Plane, Quat, Rect2, Rid, StringArray, StringName, Transform, - Transform2D, TypedArray, Variant, VariantArray, VariantDispatch, VariantOperator, VariantType, - Vector2, Vector2Array, Vector3, Vector3Array, + Aabb, Basis, ByteArray, Color, ColorArray, Dictionary, Float32Array, GodotError, GodotString, + Int32Array, NodePath, Plane, Quat, Rect2, Rid, StringArray, StringName, Transform, Transform2D, + TypedArray, Variant, VariantArray, VariantDispatch, VariantOperator, VariantType, Vector2, + Vector2Array, Vector3, Vector3Array, }; - pub use gdnative_core::core_types::{ FromVariant, FromVariantError, OwnedToVariant, ToVariant, ToVariantEq, }; - +pub use gdnative_core::export::{ + ClassBuilder, ExportInfo, Method, MethodBuilder, NativeClass, NativeClassMethods, + PropertyUsage, Signal, SignalArgument, +}; +pub use gdnative_core::init::InitHandle; +pub use gdnative_core::object::Instance; +pub use gdnative_core::object::RefInstance; pub use gdnative_core::object::{ memory::{ManuallyManaged, RefCounted}, ownership::{Shared, ThreadLocal, Unique}, AsArg, GodotObject, Instanciable, NewRef, Null, QueueFree, Ref, SubClass, TRef, }; - -pub use gdnative_core::nativescript::{ - self, - export::{ - ClassBuilder, ExportInfo, InitHandle, Method, MethodBuilder, PropertyUsage, Signal, - SignalArgument, - }, - user_data::{self, Aether, ArcData, LocalCellData, MutexData, RwLockData}, - Instance, NativeClass, NativeClassMethods, RefInstance, -}; - -pub use gdnative_core::{ - godot_dbg, godot_error, godot_gdnative_init, godot_gdnative_terminate, godot_init, - godot_nativescript_init, godot_print, godot_site, godot_warn, -}; - +pub use gdnative_core::{godot_dbg, godot_error, godot_init, godot_print, godot_warn}; pub use gdnative_derive::*; -#[cfg(feature = "bindings")] -pub use gdnative_bindings::{ - Button, CanvasItem, CanvasLayer, ColorRect, Control, Image, Input, InputEvent, InputEventKey, - KinematicBody, KinematicBody2D, Label, Node, Node2D, Object, PackedScene, Reference, - ResourceLoader, SceneTree, Shader, Spatial, Sprite, Texture, Timer, Tween, Viewport, -}; - -#[cfg(feature = "bindings")] -pub use gdnative_bindings::utils::*; +/// User-data attributes from [`export::user_data`][crate::export::user_data] module. +pub mod user_data { + // Re-export selected user_data types, but keep qualified due to rather generic names + pub use gdnative_core::export::user_data::{ + Aether, ArcData, LocalCellData, MutexData, RwLockData, + }; +} diff --git a/gdnative/tests/ui/derive_property_basic.rs b/gdnative/tests/ui/derive_property_basic.rs index f75187461..26343d3d1 100644 --- a/gdnative/tests/ui/derive_property_basic.rs +++ b/gdnative/tests/ui/derive_property_basic.rs @@ -1,4 +1,4 @@ -use gdnative::nativescript::export::property::*; +use gdnative::export::hint::*; use gdnative::prelude::*; fn test_hint() -> StringHint { diff --git a/test/src/lib.rs b/test/src/lib.rs index 210d46b36..0445252be 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -272,10 +272,10 @@ fn init(handle: InitHandle) { test_vararray_return::register(handle); } -fn terminate(_term_info: &gdnative::TerminateInfo) { +fn terminate(_term_info: &gdnative::init::TerminateInfo) { gdnative::tasks::terminate_runtime(); } -gdnative::macros::godot_gdnative_init!(); -gdnative::macros::godot_nativescript_init!(init); -gdnative::macros::godot_gdnative_terminate!(terminate); +gdnative::init::godot_gdnative_init!(); +gdnative::init::godot_nativescript_init!(init); +gdnative::init::godot_gdnative_terminate!(terminate); diff --git a/test/src/test_derive.rs b/test/src/test_derive.rs index b296da33a..32b2a4218 100644 --- a/test/src/test_derive.rs +++ b/test/src/test_derive.rs @@ -238,7 +238,7 @@ fn test_derive_nativeclass_with_property_hooks() -> bool { println!(" -- test_derive_nativeclass_with_property_hooks"); let ok = std::panic::catch_unwind(|| { - use gdnative::nativescript::user_data::MapMut; + use gdnative::export::user_data::MapMut; let thing = Instance::::new(); let (owner, script) = thing.decouple(); diff --git a/test/src/test_map_owned.rs b/test/src/test_map_owned.rs index cd9c61cb6..3995df9ec 100644 --- a/test/src/test_map_owned.rs +++ b/test/src/test_map_owned.rs @@ -1,4 +1,4 @@ -use gdnative::nativescript::user_data::Once; +use gdnative::export::user_data::Once; use gdnative::prelude::*; pub(crate) fn run_tests() -> bool { diff --git a/test/src/test_register.rs b/test/src/test_register.rs index f230bca52..02c9e56a8 100644 --- a/test/src/test_register.rs +++ b/test/src/test_register.rs @@ -1,6 +1,6 @@ use std::ops::Add; -use gdnative::nativescript::export::method::{StaticArgs, StaticArgsMethod}; +use gdnative::export::{StaticArgs, StaticArgsMethod}; use gdnative::prelude::*; pub(crate) fn run_tests() -> bool {