From 46a271fe9992a8e0ff68521d21732f25a6c951d5 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 1 Nov 2021 23:55:13 +0100 Subject: [PATCH 01/12] Ensure prelude contains each symbol only once Most symbols appear directly in prelude without modules. There is 'user_data' nested, as it's used rarely and helps organize its types. --- gdnative-derive/src/native_script.rs | 4 ++-- gdnative/src/prelude.rs | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index c2a1a3b50..12fad9a9a 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::nativescript::NativeClass for #name { type Base = ::gdnative::api::Object; - type UserData = ::gdnative::prelude::LocalCellData; + type UserData = ::gdnative::nativescript::user_data::LocalCellData; fn class_name() -> &'static str { unimplemented!() diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 3faebae9b..659ed0274 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -1,8 +1,8 @@ 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::{ @@ -16,15 +16,20 @@ pub use gdnative_core::object::{ }; 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, }; +// Re-export selected user_data types, but keep qualified due to rather generic names +pub mod user_data { + pub use gdnative_core::nativescript::user_data::{ + Aether, ArcData, LocalCellData, MutexData, RwLockData, + }; +} + 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, From 1b34b7cbaa49d83b24fc76b4fe7a4c291d21a333 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 1 Nov 2021 23:58:38 +0100 Subject: [PATCH 02/12] Reduce nesting in nativescript::export module Changes: * Move out symbols from export::{method, property} * Flatten property::accessor::invalid * Rename Usage -> PropertyUsage (like the already re-exported version) --- examples/array_export/src/lib.rs | 2 +- examples/spinning_cube/src/lib.rs | 2 +- gdnative-async/src/rt/bridge.rs | 3 +- gdnative-async/src/rt/func_state.rs | 7 +--- gdnative-core/src/nativescript/export.rs | 40 +++++++++++++------ .../src/nativescript/export/property.rs | 38 ++++++------------ .../nativescript/export/property/accessor.rs | 4 -- .../src/nativescript/export/property/hint.rs | 4 +- .../invalid.rs => invalid_accessor.rs} | 3 +- gdnative-core/src/nativescript/macros.rs | 4 +- gdnative-derive/src/lib.rs | 2 +- gdnative-derive/src/native_script.rs | 2 +- gdnative-derive/src/varargs.rs | 12 +++--- gdnative/tests/ui/derive_property_basic.rs | 2 +- test/src/test_register.rs | 2 +- 15 files changed, 59 insertions(+), 68 deletions(-) rename gdnative-core/src/nativescript/export/property/{accessor/invalid.rs => invalid_accessor.rs} (98%) diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs index 66631cf4c..7185e06cb 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::nativescript::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..37ffadc96 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::nativescript::export::hint::{EnumHint, IntHint, StringHint}; #[derive(gdnative::derive::NativeClass)] #[inherit(MeshInstance)] diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 87a0e979f..1b869ff53 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -6,8 +6,7 @@ use parking_lot::Mutex; use gdnative_bindings::{Object, Reference}; use gdnative_core::core_types::{GodotError, Variant, VariantArray}; use gdnative_core::godot_site; -use gdnative_core::nativescript::export::method::{Method, Varargs}; -use gdnative_core::nativescript::export::ClassBuilder; +use gdnative_core::nativescript::export::{ClassBuilder, Method, Varargs}; use gdnative_core::nativescript::user_data::{ArcData, Map}; use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; use gdnative_core::object::{ownership::Shared, TRef}; diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 5e5edb939..7948d22b0 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -1,13 +1,10 @@ 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, + 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::user_data::{LocalCellData, Map, MapMut}; use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; use gdnative_core::object::ownership::{Shared, Unique}; use gdnative_derive::FromVarargs; diff --git a/gdnative-core/src/nativescript/export.rs b/gdnative-core/src/nativescript/export.rs index 649354488..3ce003532 100644 --- a/gdnative-core/src/nativescript/export.rs +++ b/gdnative-core/src/nativescript/export.rs @@ -33,27 +33,41 @@ // 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::core_types::{GodotString, ToVariant, Variant}; +use crate::nativescript::{class_registry, emplace}; use crate::nativescript::{user_data::UserData, NativeClass, NativeClassMethods}; -use crate::object::{GodotObject, NewRef, TRef}; +use crate::object::{GodotObject, NewRef, RawObject, TRef}; use crate::private::get_api; -use super::class_registry; -use super::emplace; +pub use method::*; +pub use property::*; + +mod method; +mod property; -pub mod method; -pub mod property; +//pub use self::method::{ +// Method, MethodBuilder, RpcMode, ScriptMethod, ScriptMethodAttributes, ScriptMethodFn, Varargs, +//}; +//pub use self::property::{ExportInfo, PropertyBuilder, Usage as PropertyUsage}; -pub use self::method::{ - Method, MethodBuilder, RpcMode, ScriptMethod, ScriptMethodAttributes, ScriptMethodFn, Varargs, -}; -pub use self::property::{Export, ExportInfo, PropertyBuilder, Usage as PropertyUsage}; +/// 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; +} /// A handle that can register new classes to the engine during initialization. /// @@ -124,7 +138,7 @@ impl InitHandle { } }; - let owner = match object::RawObject::::try_from_sys_ref(this) { + let owner = match RawObject::::try_from_sys_ref(this) { Some(owner) => owner, None => { godot_error!( diff --git a/gdnative-core/src/nativescript/export/property.rs b/gdnative-core/src/nativescript/export/property.rs index e00e802ed..70061dd70 100644 --- a/gdnative-core/src/nativescript/export/property.rs +++ b/gdnative-core/src/nativescript/export/property.rs @@ -1,5 +1,8 @@ //! Property registration. +use accessor::{Getter, RawGetter, RawSetter, Setter}; +use invalid_accessor::{InvalidGetter, InvalidSetter}; + use crate::core_types::*; use crate::nativescript::{Instance, NativeClass}; use crate::object::ownership::Shared; @@ -7,29 +10,12 @@ use crate::object::GodotObject; use crate::object::Ref; use crate::private::get_api; -use super::ClassBuilder; +use super::{ClassBuilder, Export}; mod accessor; -pub mod hint; - -pub use hint::*; - -use accessor::{Getter, InvalidGetter, InvalidSetter, RawGetter, RawSetter, Setter}; +mod invalid_accessor; -/// 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 +59,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 +77,7 @@ where getter: InvalidGetter::new(name), default: None, hint: None, - usage: Usage::DEFAULT, + usage: PropertyUsage::DEFAULT, class_builder, } } @@ -283,14 +269,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 +301,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 +460,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/nativescript/export/property/accessor.rs index 9583d5ce1..7c159c431 100644 --- a/gdnative-core/src/nativescript/export/property/accessor.rs +++ b/gdnative-core/src/nativescript/export/property/accessor.rs @@ -8,10 +8,6 @@ use crate::nativescript::user_data::{Map, MapMut, UserData}; use crate::nativescript::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/nativescript/export/property/hint.rs index 34f5ca572..ccb93f828 100644 --- a/gdnative-core/src/nativescript/export/property/hint.rs +++ b/gdnative-core/src/nativescript/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::nativescript::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::nativescript::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/nativescript/export/property/invalid_accessor.rs similarity index 98% rename from gdnative-core/src/nativescript/export/property/accessor/invalid.rs rename to gdnative-core/src/nativescript/export/property/invalid_accessor.rs index ad2bfdf4d..cae106479 100644 --- a/gdnative-core/src/nativescript/export/property/accessor/invalid.rs +++ b/gdnative-core/src/nativescript/export/property/invalid_accessor.rs @@ -4,9 +4,8 @@ use std::mem; use crate::core_types::{FromVariant, ToVariant, Variant}; use crate::nativescript::NativeClass; -use crate::*; -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/macros.rs b/gdnative-core/src/nativescript/macros.rs index 50422970c..c0c519709 100644 --- a/gdnative-core/src/nativescript/macros.rs +++ b/gdnative-core/src/nativescript/macros.rs @@ -108,7 +108,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::nativescript::export::StaticArgsMethod<$type_name> for ThisMethod { type Args = Args; fn call( &self, @@ -139,7 +139,7 @@ macro_rules! godot_wrap_method_inner { } } - $crate::nativescript::export::method::StaticArgs::new(ThisMethod) + $crate::nativescript::export::StaticArgs::new(ThisMethod) } }; } diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 8a6e178fe..294b869ac 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -163,7 +163,7 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// /// ``` /// use gdnative::prelude::*; -/// use gdnative::nativescript::export::property::{RangeHint, FloatHint}; +/// use gdnative::nativescript::export::hint::{RangeHint, FloatHint}; /// /// #[derive(NativeClass)] /// #[inherit(Reference)] diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index 12fad9a9a..1acfff7e0 100644 --- a/gdnative-derive/src/native_script.rs +++ b/gdnative-derive/src/native_script.rs @@ -56,7 +56,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result Result { return Ok(quote! { #derived - impl #generics ::gdnative::nativescript::export::method::FromVarargs for #ident #generics #where_clause { + impl #generics ::gdnative::nativescript::export::FromVarargs for #ident #generics #where_clause { fn read<'a>( - #input_ident: &mut ::gdnative::nativescript::export::method::Varargs<'a>, - ) -> Result>> { + #input_ident: &mut ::gdnative::nativescript::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::nativescript::export::Varargs<'a>, + ) -> Result>> { let mut __errors = Vec::new(); #( diff --git a/gdnative/tests/ui/derive_property_basic.rs b/gdnative/tests/ui/derive_property_basic.rs index f75187461..6c3beb5c1 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::nativescript::export::hint::*; use gdnative::prelude::*; fn test_hint() -> StringHint { diff --git a/test/src/test_register.rs b/test/src/test_register.rs index f230bca52..9a8b3595d 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::nativescript::export::{StaticArgs, StaticArgsMethod}; use gdnative::prelude::*; pub(crate) fn run_tests() -> bool { From 10215659052e962d487f3923da8bf10579b7125b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Tue, 2 Nov 2021 23:14:11 +0100 Subject: [PATCH 03/12] Rename nativescript -> export; move out nativescript::export --- examples/array_export/src/lib.rs | 2 +- examples/spinning_cube/src/lib.rs | 2 +- gdnative-async/src/method.rs | 4 +- gdnative-async/src/rt.rs | 4 +- gdnative-async/src/rt/bridge.rs | 6 +-- gdnative-async/src/rt/func_state.rs | 8 ++-- gdnative-bindings/src/utils.rs | 2 +- .../src/{nativescript => export}/class.rs | 6 +-- .../export.rs => export/class_builder.rs} | 40 +++------------- .../class_registry.rs | 2 +- .../src/{nativescript => export}/emplace.rs | 0 .../src/{nativescript => export}/macros.rs | 14 +++--- .../src/{nativescript => }/export/method.rs | 4 +- gdnative-core/src/export/mod.rs | 48 +++++++++++++++++++ .../src/{nativescript => export}/profiler.rs | 4 +- .../src/{nativescript => }/export/property.rs | 4 +- .../export/property/accessor.rs | 4 +- .../export/property/hint.rs | 4 +- .../export/property/invalid_accessor.rs | 2 +- .../src/{nativescript => export}/type_tag.rs | 2 +- .../src/{nativescript => export}/user_data.rs | 4 +- gdnative-core/src/lib.rs | 2 +- gdnative-core/src/nativescript/mod.rs | 23 --------- gdnative-core/src/object/mod.rs | 4 +- gdnative-core/src/private.rs | 6 +-- gdnative-derive/src/lib.rs | 14 +++--- gdnative-derive/src/methods.rs | 6 +-- gdnative-derive/src/native_script.rs | 14 +++--- gdnative-derive/src/profiled.rs | 4 +- gdnative-derive/src/varargs.rs | 12 ++--- gdnative/src/lib.rs | 2 +- gdnative/src/prelude.rs | 11 ++--- gdnative/tests/ui/derive_property_basic.rs | 2 +- test/src/test_derive.rs | 2 +- test/src/test_map_owned.rs | 2 +- test/src/test_register.rs | 2 +- 36 files changed, 134 insertions(+), 138 deletions(-) rename gdnative-core/src/{nativescript => export}/class.rs (99%) rename gdnative-core/src/{nativescript/export.rs => export/class_builder.rs} (91%) rename gdnative-core/src/{nativescript => export}/class_registry.rs (95%) rename gdnative-core/src/{nativescript => export}/emplace.rs (100%) rename gdnative-core/src/{nativescript => export}/macros.rs (94%) rename gdnative-core/src/{nativescript => }/export/method.rs (99%) create mode 100644 gdnative-core/src/export/mod.rs rename gdnative-core/src/{nativescript => export}/profiler.rs (97%) rename gdnative-core/src/{nativescript => }/export/property.rs (99%) rename gdnative-core/src/{nativescript => }/export/property/accessor.rs (99%) rename gdnative-core/src/{nativescript => }/export/property/hint.rs (99%) rename gdnative-core/src/{nativescript => }/export/property/invalid_accessor.rs (98%) rename gdnative-core/src/{nativescript => export}/type_tag.rs (98%) rename gdnative-core/src/{nativescript => export}/user_data.rs (99%) delete mode 100644 gdnative-core/src/nativescript/mod.rs diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs index 7185e06cb..d7f47cac7 100644 --- a/examples/array_export/src/lib.rs +++ b/examples/array_export/src/lib.rs @@ -1,4 +1,4 @@ -use gdnative::nativescript::export::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 37ffadc96..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::hint::{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..257b49512 100644 --- a/gdnative-async/src/method.rs +++ b/gdnative-async/src/method.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use futures_task::{LocalFutureObj, LocalSpawn, SpawnError}; use gdnative_core::core_types::{ToVariant, Variant}; +use gdnative_core::export::{Method, Varargs}; +use gdnative_core::export::{NativeClass, RefInstance}; 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 crate::rt::Context; diff --git a/gdnative-async/src/rt.rs b/gdnative-async/src/rt.rs index 21dfeb9de..f7085113b 100644 --- a/gdnative-async/src/rt.rs +++ b/gdnative-async/src/rt.rs @@ -4,8 +4,8 @@ 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::export::InitHandle; +use gdnative_core::export::{Instance, RefInstance}; use gdnative_core::object::ownership::Shared; use gdnative_core::object::TRef; diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 1b869ff53..79949495d 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -5,10 +5,10 @@ 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::{Instance, NativeClass, NativeClassMethods, RefInstance}; use gdnative_core::godot_site; -use gdnative_core::nativescript::export::{ClassBuilder, Method, Varargs}; -use gdnative_core::nativescript::user_data::{ArcData, Map}; -use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; use gdnative_core::object::{ownership::Shared, TRef}; use crate::future::Resume; diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 7948d22b0..ec19920fb 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -1,11 +1,11 @@ use gdnative_bindings::Reference; use gdnative_core::core_types::{ToVariant, Variant, VariantType}; -use gdnative_core::godot_site; -use gdnative_core::nativescript::export::{ +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, Map, MapMut}; -use gdnative_core::nativescript::{Instance, NativeClass, NativeClassMethods, RefInstance}; +use gdnative_core::export::{Instance, NativeClass, NativeClassMethods, RefInstance}; +use gdnative_core::godot_site; use gdnative_core::object::ownership::{Shared, Unique}; use gdnative_derive::FromVarargs; diff --git a/gdnative-bindings/src/utils.rs b/gdnative-bindings/src/utils.rs index dc1e7bac8..397b7e1ad 100644 --- a/gdnative-bindings/src/utils.rs +++ b/gdnative-bindings/src/utils.rs @@ -1,7 +1,7 @@ //! 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, RefInstance}; use gdnative_core::object::ownership::Shared; use gdnative_core::object::{SubClass, TRef}; diff --git a/gdnative-core/src/nativescript/class.rs b/gdnative-core/src/export/class.rs similarity index 99% rename from gdnative-core/src/nativescript/class.rs rename to gdnative-core/src/export/class.rs index 0a8cba98f..66120d390 100644 --- a/gdnative-core/src/nativescript/class.rs +++ b/gdnative-core/src/export/class.rs @@ -3,8 +3,8 @@ 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::ClassBuilder; use crate::object::bounds::{ AssumeSafeLifetime, LifetimeConstraint, RefImplBound, SafeAsRaw, SafeDeref, }; @@ -746,7 +746,7 @@ 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; } diff --git a/gdnative-core/src/nativescript/export.rs b/gdnative-core/src/export/class_builder.rs similarity index 91% rename from gdnative-core/src/nativescript/export.rs rename to gdnative-core/src/export/class_builder.rs index 3ce003532..1932afb00 100644 --- a/gdnative-core/src/nativescript/export.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -37,38 +37,12 @@ use std::ffi::CString; use std::marker::PhantomData; use std::ptr; -use crate::core_types::{GodotString, ToVariant, Variant}; -use crate::nativescript::{class_registry, emplace}; -use crate::nativescript::{user_data::UserData, NativeClass, NativeClassMethods}; +use crate::core_types::{GodotString, Variant}; +use crate::export::user_data::UserData; +use crate::export::*; use crate::object::{GodotObject, NewRef, RawObject, TRef}; use crate::private::get_api; -pub use method::*; -pub use property::*; - -mod method; -mod property; - -//pub use self::method::{ -// Method, MethodBuilder, RpcMode, ScriptMethod, ScriptMethodAttributes, ScriptMethodFn, Varargs, -//}; -//pub use self::property::{ExportInfo, PropertyBuilder, Usage as PropertyUsage}; - -/// 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; -} - /// A handle that can register new classes to the engine during initialization. /// /// See [`godot_nativescript_init`](macro.godot_nativescript_init.html) and @@ -221,7 +195,7 @@ impl InitHandle { (get_api().godot_nativescript_set_type_tag)( self.handle as *mut _, class_name.as_ptr() as *const _, - crate::nativescript::type_tag::create::(), + crate::export::type_tag::create::(), ); let builder = ClassBuilder { @@ -240,8 +214,8 @@ impl InitHandle { #[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, } @@ -306,7 +280,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 94% rename from gdnative-core/src/nativescript/macros.rs rename to gdnative-core/src/export/macros.rs index c0c519709..aa7bfbe93 100644 --- a/gdnative-core/src/nativescript/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -1,6 +1,6 @@ #![macro_use] -/// Declare the API endpoint to initialize nativescript classes on startup. +/// 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: @@ -16,14 +16,14 @@ #[macro_export] macro_rules! godot_nativescript_init { () => { - fn godot_nativescript_init_empty(_init: $crate::nativescript::export::InitHandle) {} + fn godot_nativescript_init_empty(_init: $crate::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) {} + fn godot_nativescript_init_empty(_init: $crate::export::InitHandle) {} $crate::godot_nativescript_init!(godot_nativescript_init_empty as $fn_name); }; ($callback:ident as $fn_name:ident) => { @@ -36,7 +36,7 @@ macro_rules! godot_nativescript_init { } let __result = ::std::panic::catch_unwind(|| { - $callback($crate::nativescript::export::InitHandle::new(handle)); + $callback($crate::export::InitHandle::new(handle)); }); if __result.is_err() { @@ -97,7 +97,7 @@ macro_rules! godot_wrap_method_inner { #[derive(Copy, Clone, Default)] struct ThisMethod; - use $crate::nativescript::{NativeClass, Instance, RefInstance, OwnerArg}; + use $crate::export::{NativeClass, Instance, RefInstance, OwnerArg}; use ::gdnative::derive::FromVarargs; #[derive(FromVarargs)] @@ -108,7 +108,7 @@ macro_rules! godot_wrap_method_inner { } #[allow(unused_variables, unused_assignments, unused_mut)] - impl $crate::nativescript::export::StaticArgsMethod<$type_name> for ThisMethod { + impl $crate::export::StaticArgsMethod<$type_name> for ThisMethod { type Args = Args; fn call( &self, @@ -139,7 +139,7 @@ macro_rules! godot_wrap_method_inner { } } - $crate::nativescript::export::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..d79c1a90f 100644 --- a/gdnative-core/src/nativescript/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -8,12 +8,12 @@ use std::fmt; use std::marker::PhantomData; use crate::core_types::{FromVariant, FromVariantError, Variant}; +use crate::export::class::{NativeClass, RefInstance}; use crate::log::Site; -use crate::nativescript::class::{NativeClass, RefInstance}; use crate::object::ownership::Shared; use crate::object::{Ref, TRef}; -use super::ClassBuilder; +use crate::export::ClassBuilder; /// Builder type used to register a method on a `NativeClass`. pub struct MethodBuilder<'a, C, F> { diff --git a/gdnative-core/src/export/mod.rs b/gdnative-core/src/export/mod.rs new file mode 100644 index 000000000..5a5f78b78 --- /dev/null +++ b/gdnative-core/src/export/mod.rs @@ -0,0 +1,48 @@ +//! 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 emplace; +mod macros; +mod method; +mod property; + +pub(crate) mod class_registry; +pub(crate) mod type_tag; + +pub use class::*; +pub use method::*; +pub use property::*; + +pub mod profiler; +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/profiler.rs b/gdnative-core/src/export/profiler.rs similarity index 97% rename from gdnative-core/src/nativescript/profiler.rs rename to gdnative-core/src/export/profiler.rs index 9f35dc47e..2b0b4ca38 100644 --- a/gdnative-core/src/nativescript/profiler.rs +++ b/gdnative-core/src/export/profiler.rs @@ -167,7 +167,7 @@ where /// /// ```rust /// # fn main() { -/// use gdnative::nativescript::profiler::{profile, profile_sig}; +/// use gdnative::export::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::export::profiler::Signature::new(file!(), line!(), $tag) }; } diff --git a/gdnative-core/src/nativescript/export/property.rs b/gdnative-core/src/export/property.rs similarity index 99% rename from gdnative-core/src/nativescript/export/property.rs rename to gdnative-core/src/export/property.rs index 70061dd70..6eb26539a 100644 --- a/gdnative-core/src/nativescript/export/property.rs +++ b/gdnative-core/src/export/property.rs @@ -4,13 +4,13 @@ use accessor::{Getter, RawGetter, RawSetter, Setter}; use invalid_accessor::{InvalidGetter, InvalidSetter}; use crate::core_types::*; -use crate::nativescript::{Instance, NativeClass}; +use crate::export::{Instance, NativeClass}; use crate::object::ownership::Shared; use crate::object::GodotObject; use crate::object::Ref; use crate::private::get_api; -use super::{ClassBuilder, Export}; +use crate::export::{ClassBuilder, Export}; mod accessor; mod invalid_accessor; diff --git a/gdnative-core/src/nativescript/export/property/accessor.rs b/gdnative-core/src/export/property/accessor.rs similarity index 99% rename from gdnative-core/src/nativescript/export/property/accessor.rs rename to gdnative-core/src/export/property/accessor.rs index 7c159c431..9380848fb 100644 --- a/gdnative-core/src/nativescript/export/property/accessor.rs +++ b/gdnative-core/src/export/property/accessor.rs @@ -4,8 +4,8 @@ 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}; /// Trait for raw property setters. diff --git a/gdnative-core/src/nativescript/export/property/hint.rs b/gdnative-core/src/export/property/hint.rs similarity index 99% rename from gdnative-core/src/nativescript/export/property/hint.rs rename to gdnative-core/src/export/property/hint.rs index ccb93f828..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::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::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/invalid_accessor.rs b/gdnative-core/src/export/property/invalid_accessor.rs similarity index 98% rename from gdnative-core/src/nativescript/export/property/invalid_accessor.rs rename to gdnative-core/src/export/property/invalid_accessor.rs index cae106479..605a88e72 100644 --- a/gdnative-core/src/nativescript/export/property/invalid_accessor.rs +++ b/gdnative-core/src/export/property/invalid_accessor.rs @@ -3,7 +3,7 @@ use std::mem; use crate::core_types::{FromVariant, ToVariant, Variant}; -use crate::nativescript::NativeClass; +use crate::export::NativeClass; use super::accessor::{RawGetter, RawSetter}; 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/lib.rs b/gdnative-core/src/lib.rs index dcc368d9f..d69883af1 100644 --- a/gdnative-core/src/lib.rs +++ b/gdnative-core/src/lib.rs @@ -43,7 +43,7 @@ mod macros; pub mod core_types; #[cfg(feature = "nativescript")] -pub mod nativescript; +pub mod export; pub mod log; pub mod object; 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/object/mod.rs b/gdnative-core/src/object/mod.rs index 5755ff41a..297fa4a2e 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; @@ -15,7 +15,7 @@ use std::ops::Deref; use std::ptr::NonNull; #[cfg(feature = "nativescript")] -use crate::nativescript::{Instance, NativeClass, RefInstance}; +use crate::export::{Instance, NativeClass, RefInstance}; use crate::private::{get_api, ManuallyManagedClassPlaceholder, ReferenceCountedClassPlaceholder}; use crate::sys; diff --git a/gdnative-core/src/private.rs b/gdnative-core/src/private.rs index 0bce4d0ad..4d0ae0698 100644 --- a/gdnative-core/src/private.rs +++ b/gdnative-core/src/private.rs @@ -114,8 +114,8 @@ pub fn get_gdnative_library_sys() -> *mut sys::godot_object { 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,7 +226,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. +// export module without making this macro public. #[cfg(feature = "nativescript")] make_method_table!(struct NativeScriptMethodTable for NativeScript { set_class_name, diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 294b869ac..0d2f6fb57 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -42,14 +42,14 @@ mod variant; /// 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::*; +/// use gdnative::export::*; /// builder.build_method("foo", gdnative::macros::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::hint::{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..8b747dddc 100644 --- a/gdnative-derive/src/methods.rs +++ b/gdnative-derive/src/methods.rs @@ -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 1acfff7e0..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::nativescript::NativeClass for #name { + impl ::gdnative::export::NativeClass for #name { type Base = ::gdnative::api::Object; - type UserData = ::gdnative::nativescript::user_data::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..7255ab239 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::export::profiler::profile( + ::gdnative::export::profiler::profile_sig!(#tag), move || { #(#stmts)* }) })); diff --git a/gdnative-derive/src/varargs.rs b/gdnative-derive/src/varargs.rs index 896124344..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::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::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::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..58545be2a 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -58,7 +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}; +pub use gdnative_core::{core_types, export, log, object, InitializeInfo, TerminateInfo}; /// Collection of declarative `godot_*` macros, mostly for GDNative registration and output. pub mod macros { diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 659ed0274..d3ada0c34 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -15,17 +15,14 @@ pub use gdnative_core::object::{ AsArg, GodotObject, Instanciable, NewRef, Null, QueueFree, Ref, SubClass, TRef, }; -pub use gdnative_core::nativescript::{ - export::{ - ClassBuilder, ExportInfo, InitHandle, Method, MethodBuilder, PropertyUsage, Signal, - SignalArgument, - }, - Instance, NativeClass, NativeClassMethods, RefInstance, +pub use gdnative_core::export::{ + ClassBuilder, ExportInfo, InitHandle, Instance, Method, MethodBuilder, NativeClass, + NativeClassMethods, PropertyUsage, RefInstance, Signal, SignalArgument, }; // Re-export selected user_data types, but keep qualified due to rather generic names pub mod user_data { - pub use gdnative_core::nativescript::user_data::{ + 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 6c3beb5c1..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::hint::*; +use gdnative::export::hint::*; use gdnative::prelude::*; fn test_hint() -> StringHint { 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 9a8b3595d..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::{StaticArgs, StaticArgsMethod}; +use gdnative::export::{StaticArgs, StaticArgsMethod}; use gdnative::prelude::*; pub(crate) fn run_tests() -> bool { From 83d0d16755d6073b21076c318536c409a2a5e9cf Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 3 Nov 2021 19:25:36 +0100 Subject: [PATCH 04/12] Add module init; move symbols from macros + export --- gdnative-async/src/rt.rs | 2 +- gdnative-core/src/export/class_builder.rs | 180 ++-------------------- gdnative-core/src/export/macros.rs | 69 --------- gdnative-core/src/export/mod.rs | 2 +- gdnative-core/src/init/info.rs | 96 ++++++++++++ gdnative-core/src/init/init_handle.rs | 171 ++++++++++++++++++++ gdnative-core/src/init/macros.rs | 168 ++++++++++++++++++++ gdnative-core/src/init/mod.rs | 12 ++ gdnative-core/src/lib.rs | 98 +----------- gdnative-core/src/macros.rs | 98 ------------ gdnative-derive/src/lib.rs | 4 +- gdnative-derive/src/methods.rs | 2 +- gdnative/src/lib.rs | 9 +- gdnative/src/prelude.rs | 6 +- test/src/lib.rs | 8 +- 15 files changed, 473 insertions(+), 452 deletions(-) create mode 100644 gdnative-core/src/init/info.rs create mode 100644 gdnative-core/src/init/init_handle.rs create mode 100644 gdnative-core/src/init/macros.rs create mode 100644 gdnative-core/src/init/mod.rs diff --git a/gdnative-async/src/rt.rs b/gdnative-async/src/rt.rs index f7085113b..fc83cff4f 100644 --- a/gdnative-async/src/rt.rs +++ b/gdnative-async/src/rt.rs @@ -4,8 +4,8 @@ use gdnative_bindings::Object; use gdnative_core::object::SubClass; use gdnative_core::core_types::{GodotError, Variant}; -use gdnative_core::export::InitHandle; use gdnative_core::export::{Instance, RefInstance}; +use gdnative_core::init::InitHandle; use gdnative_core::object::ownership::Shared; use gdnative_core::object::TRef; diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index 1932afb00..f8402269d 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -38,180 +38,10 @@ use std::marker::PhantomData; use std::ptr; use crate::core_types::{GodotString, Variant}; -use crate::export::user_data::UserData; use crate::export::*; -use crate::object::{GodotObject, NewRef, RawObject, TRef}; +use crate::object::NewRef; use crate::private::get_api; -/// 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 { - init_handle: self.handle, - class_name, - _marker: PhantomData, - }; - - C::register_properties(&builder); - - // register methods - C::register(&builder); - } - } -} - #[derive(Debug)] pub struct ClassBuilder { pub(super) init_handle: *mut libc::c_void, @@ -220,6 +50,14 @@ pub struct ClassBuilder { } 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) { diff --git a/gdnative-core/src/export/macros.rs b/gdnative-core/src/export/macros.rs index aa7bfbe93..165c9cebd 100644 --- a/gdnative-core/src/export/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -1,74 +1,5 @@ #![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::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::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::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 { diff --git a/gdnative-core/src/export/mod.rs b/gdnative-core/src/export/mod.rs index 5a5f78b78..d0d9abb52 100644 --- a/gdnative-core/src/export/mod.rs +++ b/gdnative-core/src/export/mod.rs @@ -11,12 +11,12 @@ mod class; mod class_builder; -mod emplace; mod macros; mod method; mod property; pub(crate) mod class_registry; +pub(crate) mod emplace; pub(crate) mod type_tag; pub use class::*; 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 d69883af1..7e2abc008 100644 --- a/gdnative-core/src/lib.rs +++ b/gdnative-core/src/lib.rs @@ -45,106 +45,10 @@ pub mod core_types; #[cfg(feature = "nativescript")] pub mod export; +pub mod init; pub mod log; pub mod object; /// 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/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-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 0d2f6fb57..7ca6e5e74 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -39,7 +39,7 @@ mod variant; /// Will expand to /// ``` /// use gdnative::prelude::*; -/// struct Foo{} +/// struct Foo {} /// impl NativeClass for Foo { /// type Base = gdnative::api::Reference; /// type UserData = gdnative::export::user_data::LocalCellData; @@ -50,7 +50,7 @@ mod variant; /// impl gdnative::export::NativeClassMethods for Foo { /// fn register(builder: &ClassBuilder) { /// use gdnative::export::*; -/// builder.build_method("foo", gdnative::macros::godot_wrap_method!(Foo, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) +/// 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(); /// } diff --git a/gdnative-derive/src/methods.rs b/gdnative-derive/src/methods.rs index 8b747dddc..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 ); diff --git a/gdnative/src/lib.rs b/gdnative/src/lib.rs index 58545be2a..cdfa7a2f7 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -58,14 +58,11 @@ // 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, export, log, object, InitializeInfo, TerminateInfo}; +pub use gdnative_core::{core_types, export, init, log, object}; /// 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::{godot_dbg, godot_error, godot_print, godot_warn}; } // Implementation details (e.g. used by macros). @@ -85,7 +82,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 d3ada0c34..3f0c40e08 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -16,10 +16,12 @@ pub use gdnative_core::object::{ }; pub use gdnative_core::export::{ - ClassBuilder, ExportInfo, InitHandle, Instance, Method, MethodBuilder, NativeClass, - NativeClassMethods, PropertyUsage, RefInstance, Signal, SignalArgument, + ClassBuilder, ExportInfo, Instance, Method, MethodBuilder, NativeClass, NativeClassMethods, + PropertyUsage, RefInstance, Signal, SignalArgument, }; +pub use gdnative_core::init::InitHandle; + // Re-export selected user_data types, but keep qualified due to rather generic names pub mod user_data { pub use gdnative_core::export::user_data::{ 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); From 07ad4d139bebf2eeafa144d7a4df8d74de2d327d Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 3 Nov 2021 19:30:07 +0100 Subject: [PATCH 05/12] Extract module profiler --- gdnative-core/src/export/mod.rs | 5 ----- gdnative-core/src/lib.rs | 1 + gdnative-core/src/{export => }/profiler.rs | 4 ++-- gdnative-derive/src/profiled.rs | 4 ++-- gdnative/src/lib.rs | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) rename gdnative-core/src/{export => }/profiler.rs (97%) diff --git a/gdnative-core/src/export/mod.rs b/gdnative-core/src/export/mod.rs index d0d9abb52..2165bf83a 100644 --- a/gdnative-core/src/export/mod.rs +++ b/gdnative-core/src/export/mod.rs @@ -19,11 +19,6 @@ pub(crate) mod class_registry; pub(crate) mod emplace; pub(crate) mod type_tag; -pub use class::*; -pub use method::*; -pub use property::*; - -pub mod profiler; pub mod user_data; pub use crate::godot_wrap_method; diff --git a/gdnative-core/src/lib.rs b/gdnative-core/src/lib.rs index 7e2abc008..2c22b5e90 100644 --- a/gdnative-core/src/lib.rs +++ b/gdnative-core/src/lib.rs @@ -48,6 +48,7 @@ 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)] diff --git a/gdnative-core/src/export/profiler.rs b/gdnative-core/src/profiler.rs similarity index 97% rename from gdnative-core/src/export/profiler.rs rename to gdnative-core/src/profiler.rs index 2b0b4ca38..b954f1c90 100644 --- a/gdnative-core/src/export/profiler.rs +++ b/gdnative-core/src/profiler.rs @@ -167,7 +167,7 @@ where /// /// ```rust /// # fn main() { -/// use gdnative::export::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::export::profiler::Signature::new(file!(), line!(), $tag) + $crate::profiler::Signature::new(file!(), line!(), $tag) }; } diff --git a/gdnative-derive/src/profiled.rs b/gdnative-derive/src/profiled.rs index 7255ab239..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::export::profiler::profile( - ::gdnative::export::profiler::profile_sig!(#tag), move || { + ::gdnative::profiler::profile( + ::gdnative::profiler::profile_sig!(#tag), move || { #(#stmts)* }) })); diff --git a/gdnative/src/lib.rs b/gdnative/src/lib.rs index cdfa7a2f7..00531fa40 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -58,7 +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, export, init, log, object}; +pub use gdnative_core::{core_types, export, init, log, object, profiler}; /// Collection of declarative `godot_*` macros, mostly for GDNative registration and output. pub mod macros { From 6077befcbb4585eb6e1c2d78b175ecc2caa3db0e Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 3 Nov 2021 20:30:37 +0100 Subject: [PATCH 06/12] Remove feature 'nativescript' --- gdnative-core/Cargo.toml | 7 +++---- gdnative-core/src/lib.rs | 2 -- gdnative-core/src/object/mod.rs | 5 ----- gdnative-core/src/private.rs | 14 ++++---------- 4 files changed, 7 insertions(+), 21 deletions(-) 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/lib.rs b/gdnative-core/src/lib.rs index 2c22b5e90..537fd9887 100644 --- a/gdnative-core/src/lib.rs +++ b/gdnative-core/src/lib.rs @@ -42,9 +42,7 @@ mod macros; pub mod core_types; -#[cfg(feature = "nativescript")] pub mod export; - pub mod init; pub mod log; pub mod object; diff --git a/gdnative-core/src/object/mod.rs b/gdnative-core/src/object/mod.rs index 297fa4a2e..a30e82b95 100644 --- a/gdnative-core/src/object/mod.rs +++ b/gdnative-core/src/object/mod.rs @@ -14,9 +14,7 @@ use std::marker::PhantomData; use std::ops::Deref; use std::ptr::NonNull; -#[cfg(feature = "nativescript")] use crate::export::{Instance, NativeClass, RefInstance}; - use crate::private::{get_api, ManuallyManagedClassPlaceholder, ReferenceCountedClassPlaceholder}; use crate::sys; use bounds::{AssumeSafeLifetime, LifetimeConstraint, PtrWrapper, RefKindSpec}; @@ -488,7 +486,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 +499,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 +930,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 4d0ae0698..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::export::type_tag::cleanup(); - crate::export::class_registry::cleanup(); - } + crate::export::type_tag::cleanup(); + crate::export::class_registry::cleanup(); + GODOT_API = None; } @@ -227,7 +222,6 @@ make_method_table!(struct ReferenceMethodTable for Reference { // Add this one here too. It's not easy to use this macro from the // export module without making this macro public. -#[cfg(feature = "nativescript")] make_method_table!(struct NativeScriptMethodTable for NativeScript { set_class_name, set_library, From 115ca6a2f32bd1178165a39407e2261f163d1396 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 3 Nov 2021 21:21:37 +0100 Subject: [PATCH 07/12] Move export::{Instance,RefInstance} -> object --- gdnative-async/src/method.rs | 3 +- gdnative-async/src/rt.rs | 8 +- gdnative-async/src/rt/bridge.rs | 3 +- gdnative-async/src/rt/func_state.rs | 3 +- gdnative-bindings/src/utils.rs | 6 +- gdnative-core/src/export/class.rs | 606 +-------------------------- gdnative-core/src/export/macros.rs | 3 +- gdnative-core/src/export/method.rs | 6 +- gdnative-core/src/export/property.rs | 8 +- gdnative-core/src/object/instance.rs | 601 ++++++++++++++++++++++++++ gdnative-core/src/object/mod.rs | 19 +- gdnative/src/prelude.rs | 44 +- 12 files changed, 656 insertions(+), 654 deletions(-) create mode 100644 gdnative-core/src/object/instance.rs diff --git a/gdnative-async/src/method.rs b/gdnative-async/src/method.rs index 257b49512..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::export::{NativeClass, RefInstance}; use gdnative_core::log::{self, Site}; 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 fc83cff4f..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::export::{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 79949495d..e2e103e04 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -7,9 +7,10 @@ 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::{Instance, NativeClass, NativeClassMethods, RefInstance}; +use gdnative_core::export::{NativeClass, NativeClassMethods}; use gdnative_core::godot_site; 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 ec19920fb..2b21d2211 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -4,9 +4,10 @@ use gdnative_core::export::user_data::{LocalCellData, Map, MapMut}; use gdnative_core::export::{ ClassBuilder, ExportInfo, PropertyUsage, Signal, SignalArgument, StaticArgs, StaticArgsMethod, }; -use gdnative_core::export::{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/src/utils.rs b/gdnative-bindings/src/utils.rs index 397b7e1ad..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::export::{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/src/export/class.rs b/gdnative-core/src/export/class.rs index 66120d390..a0e095b42 100644 --- a/gdnative-core/src/export/class.rs +++ b/gdnative-core/src/export/class.rs @@ -1,21 +1,8 @@ -use std::ptr::NonNull; - -use crate::core_types::{ - FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant, -}; -use crate::export::user_data::{Map, MapMut, MapOwned, UserData}; +use crate::export::user_data::UserData; use crate::export::ClassBuilder; -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::private::{get_api, ReferenceCountedClassPlaceholder}; +use crate::object::ownership::{Shared, ThreadAccess, Unique}; +use crate::object::{GodotObject, Instance, Instanciable, TRef}; -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 @@ -167,593 +154,6 @@ where } } -/// 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 -/// reference-counted. Otherwise, use `assume_safe` to obtain a temporary `RefInstance`. -/// -/// See the type-level documentation on `Ref` for more information on typed thread accesses. -#[derive(Debug)] -pub struct Instance { - owner: Ref, - script: T::UserData, -} - -/// A reference to a GodotObject with a rust NativeClass attached that is assumed safe during -/// a certain lifetime. -/// -/// See the type-level documentation on `Ref` for more information on typed thread accesses. -#[derive(Debug)] -pub struct RefInstance<'a, T: NativeClass, Access: ThreadAccess> { - owner: TRef<'a, T::Base, Access>, - script: T::UserData, -} - -impl Instance { - /// Creates a `T::Base` with the script `T` attached. Both `T::Base` and `T` must have zero - /// argument constructors. - /// - /// If `T::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] - #[allow(clippy::new_without_default)] - pub fn new() -> Self - where - T::Base: Instanciable, - { - Self::maybe_emplace(None) - } - - /// Creates a `T::Base` with a given instance of the script `T` attached. `T::Base` must - /// have a zero-argument constructor. - /// - /// This may be used to create instances of scripts that do not have zero-argument - /// constructors: - /// - /// ```ignore - /// // This type does not have a zero-argument constructor. As a result, `Instance::new` - /// // will panic and `Foo.new` from GDScript will result in errors when the object is used. - /// #[derive(NativeScript)] - /// #[inherit(Reference)] - /// #[no_constructor] - /// struct MyIntVector(i64, i64); - /// - /// #[methods] - /// impl MyIntVector { - /// // - snip - - /// } - /// - /// // With `Instance::emplace`, however, we can expose "constructors" from a factory - /// // auto-load for our script type. - /// #[derive(NativeScript)] - /// #[inherit(Node)] - /// #[user_data(Aether)] - /// struct MyIntVectorFactory; - /// - /// #[methods] - /// impl MyIntVectorFactory { - /// #[export] - /// fn make(&self, _owner: &Node, x: i64, y: i64) -> Instance { - /// Instance::emplace(MyIntVector(x, y)) - /// } - /// } - /// ``` - /// - /// If `T::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] - pub fn emplace(script: T) -> Self - where - T::Base: Instanciable, - { - Self::maybe_emplace(Some(script)) - } - - fn maybe_emplace(script: Option) -> Self - where - T::Base: Instanciable, - { - unsafe { - let gd_api = get_api(); - let nativescript_methods = crate::private::NativeScriptMethodTable::get(gd_api); - - assert_ne!( - std::ptr::null(), - nativescript_methods.set_class_name, - "NativeScript::set_class_name must be available" - ); - assert_ne!( - std::ptr::null(), - nativescript_methods.set_library, - "NativeScript::set_library must be available" - ); - assert_ne!( - std::ptr::null(), - nativescript_methods.new, - "NativeScript::new must be available" - ); - - // The API functions take NUL-terminated C strings. &CStr is not used for its runtime cost. - let class_name = b"NativeScript\0".as_ptr() as *const libc::c_char; - let ctor = (gd_api.godot_get_class_constructor)(class_name).unwrap(); - - let native_script = - NonNull::new(ctor()).expect("NativeScript constructor should not return null"); - let native_script = - RawObject::::from_sys_ref_unchecked( - native_script, - ); - native_script.init_ref_count(); - - let script_class_name = GodotString::from(T::class_name()); - let mut args: [*const libc::c_void; 1] = [script_class_name.sys() as *const _]; - (gd_api.godot_method_bind_ptrcall)( - nativescript_methods.set_class_name, - native_script.sys().as_ptr(), - args.as_mut_ptr(), - std::ptr::null_mut(), - ); - - let mut args: [*const libc::c_void; 1] = [crate::private::get_gdnative_library_sys()]; - (gd_api.godot_method_bind_ptrcall)( - nativescript_methods.set_library, - native_script.sys().as_ptr(), - args.as_mut_ptr(), - std::ptr::null_mut(), - ); - - if let Some(script) = script { - emplace::place(script); - } - - let mut args: [*const sys::godot_variant; 0] = []; - let variant = (gd_api.godot_method_bind_call)( - nativescript_methods.new, - native_script.sys().as_ptr(), - args.as_mut_ptr(), - 0, - std::ptr::null_mut(), - ); - - debug_assert!(class_registry::is_class_registered::(), "`{type_name}` must be registered before it can be used; call `handle.add_class::<{type_name}>()` in your `nativescript_init` callback", type_name = std::any::type_name::()); - - assert!( - emplace::take::().is_none(), - "emplacement value should be taken by the constructor wrapper (this is a bug in the bindings)", - ); - - let variant = Variant::from_sys(variant); - - let owner = variant - .try_to_object::() - .expect("the engine should return a base object of the correct type") - .assume_unique(); - - let script_ptr = - (gd_api.godot_nativescript_get_userdata)(owner.sys()) as *const libc::c_void; - - assert_ne!( - std::ptr::null(), - script_ptr, - "script instance should not be null (did the constructor fail?)" - ); - - let script = T::UserData::clone_from_user_data_unchecked(script_ptr); - - native_script.unref(); - - Instance { owner, script } - } - } -} - -impl Instance { - /// Returns the base object, dropping the script wrapper. - #[inline] - pub fn into_base(self) -> Ref { - self.owner - } - - /// Returns the script wrapper. - #[inline] - pub fn into_script(self) -> T::UserData { - self.script - } - - /// Returns the base object and the script wrapper. - #[inline] - pub fn decouple(self) -> (Ref, T::UserData) { - (self.owner, self.script) - } - - /// Returns a reference to the base object. - #[inline] - pub fn base(&self) -> &Ref { - &self.owner - } - - /// Returns a reference to the script wrapper. - #[inline] - pub fn script(&self) -> &T::UserData { - &self.script - } -} - -impl Instance -where - RefImplBound: SafeAsRaw<::RefKind, Access>, -{ - /// Try to downcast `Ref` to `Instance`, without changing the reference - /// count if reference-counted. - /// - /// # Errors - /// - /// Returns the original `Ref` if the cast failed. - #[inline] - pub fn try_from_base(owner: Ref) -> Result> { - let user_data = match try_get_user_data_ptr::(owner.as_raw()) { - Some(user_data) => user_data, - None => return Err(owner), - }; - - let script = unsafe { T::UserData::clone_from_user_data_unchecked(user_data) }; - - Ok(Instance { owner, script }) - } - - /// Try to downcast `Ref` to `Instance`, without changing the reference - /// count if reference-counted. Shorthand for `Self::try_from_base().ok()`. - #[inline] - pub fn from_base(owner: Ref) -> Option { - Self::try_from_base(owner).ok() - } -} - -impl Instance -where - RefImplBound: SafeDeref<::RefKind, Access>, -{ - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. Can be used on reference counted types for multiple times. - #[inline] - pub fn map(&self, op: F) -> Result::Err> - where - T::UserData: Map, - F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U, - { - self.script.map(|script| op(script, self.owner.as_ref())) - } - - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. Can be used on reference counted types for multiple times. - #[inline] - pub fn map_mut(&self, op: F) -> Result::Err> - where - T::UserData: MapMut, - F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U, - { - self.script - .map_mut(|script| op(script, self.owner.as_ref())) - } - - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. Can be used on reference counted types for multiple times. - #[inline] - pub fn map_owned(&self, op: F) -> Result::Err> - where - T::UserData: MapOwned, - F: FnOnce(T, TRef<'_, T::Base, Access>) -> U, - { - self.script - .map_owned(|script| op(script, self.owner.as_ref())) - } -} - -/// Methods for instances with manually-managed base classes. -impl Instance { - /// Assume that `self` is safe to use. - /// - /// This is *not* guaranteed to be a no-op at runtime. - /// - /// # Safety - /// - /// It's safe to call `assume_safe` only if the constraints of `Ref::assume_safe` - /// are satisfied for the base object. - #[inline] - pub unsafe fn assume_safe<'a, 'r>(&'r self) -> RefInstance<'a, T, Shared> - where - AssumeSafeLifetime<'a, 'r>: LifetimeConstraint<::RefKind>, - { - RefInstance { - owner: self.owner.assume_safe(), - script: self.script.clone(), - } - } -} - -impl Instance -where - T::Base: GodotObject, -{ - /// Returns `true` if the pointer currently points to a valid object of the correct type. - /// **This does NOT guarantee that it's safe to use this pointer.** - /// - /// # Safety - /// - /// This thread must have exclusive access to the object during the call. - #[inline] - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn is_instance_sane(&self) -> bool { - self.owner.is_instance_sane() - } -} - -impl Instance -where - T::Base: GodotObject, -{ - /// Frees the base object and user-data wrapper. - /// - /// Same as `self.into_base().free()`. - #[inline] - pub fn free(self) { - self.into_base().free() - } -} - -impl Instance -where - T::Base: GodotObject + QueueFree, -{ - /// Queues the base object and user-data wrapper for deallocation in the near future. - /// This should be preferred to `free` for `Node`s. - /// - /// Same as `self.into_base().queue_free()`. - #[inline] - pub fn queue_free(self) { - self.into_base().queue_free() - } -} - -impl Instance { - /// Coverts into a `Shared` instance. - #[inline] - pub fn into_shared(self) -> Instance { - Instance { - owner: self.owner.into_shared(), - script: self.script, - } - } -} - -impl Instance -where - T::Base: GodotObject, -{ - /// Coverts into a `ThreadLocal` instance. - #[inline] - pub fn into_thread_local(self) -> Instance { - Instance { - owner: self.owner.into_thread_local(), - script: self.script, - } - } -} - -impl Instance { - /// Assume that `self` is the unique reference to the underlying base object. - /// - /// This is guaranteed to be a no-op at runtime if `debug_assertions` is disabled. Runtime - /// sanity checks may be added in debug builds to help catch bugs. - /// - /// # Safety - /// - /// Calling `assume_unique` when `self` isn't the unique reference is instant undefined - /// behavior. This is a much stronger assumption than `assume_safe` and should be used with - /// care. - #[inline] - pub unsafe fn assume_unique(self) -> Instance { - Instance { - owner: self.owner.assume_unique(), - script: self.script, - } - } -} - -impl Instance -where - T::Base: GodotObject, -{ - /// Assume that all references to the underlying object is local to the current thread. - /// - /// This is guaranteed to be a no-op at runtime. - /// - /// # Safety - /// - /// Calling `assume_thread_local` when there are references on other threads is instant - /// undefined behavior. This is a much stronger assumption than `assume_safe` and should - /// be used with care. - #[inline] - pub unsafe fn assume_thread_local(self) -> Instance { - Instance { - owner: self.owner.assume_thread_local(), - script: self.script, - } - } -} - -impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access> { - /// Returns a reference to the base object with the same lifetime. - #[inline] - pub fn base(&self) -> TRef<'a, T::Base, Access> { - self.owner - } - - /// Returns a reference to the script wrapper. - #[inline] - pub fn script(&self) -> &T::UserData { - &self.script - } - - /// Try to downcast `TRef<'a, T::Base, Access>` to `RefInstance`. - #[inline] - pub fn try_from_base(owner: TRef<'a, T::Base, Access>) -> Option { - let user_data = try_get_user_data_ptr::(owner.as_raw())?; - unsafe { Some(Self::from_raw_unchecked(owner, user_data)) } - } - - /// Pairs an `owner` and `user_data` without checking validity. Internal interface. - #[doc(hidden)] - #[inline] - pub unsafe fn from_raw_unchecked( - owner: TRef<'a, T::Base, Access>, - user_data: *mut libc::c_void, - ) -> Self { - let script = T::UserData::clone_from_user_data_unchecked(user_data); - RefInstance { owner, script } - } -} - -impl<'a, T: NativeClass, Access: NonUniqueThreadAccess> RefInstance<'a, T, Access> { - /// Persists this into a persistent `Instance` with the same thread access, without cloning - /// the userdata wrapper. - /// - /// This is only available for non-`Unique` accesses. - #[inline] - pub fn claim(self) -> Instance { - Instance { - owner: self.owner.claim(), - script: self.script, - } - } -} - -/// Methods for instances with reference-counted base classes. -impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access> -where - T::Base: GodotObject, -{ - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. - #[inline] - pub fn map(&self, op: F) -> Result::Err> - where - T::UserData: Map, - F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U, - { - self.script.map(|script| op(script, self.owner)) - } - - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. - #[inline] - pub fn map_mut(&self, op: F) -> Result::Err> - where - T::UserData: MapMut, - F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U, - { - self.script.map_mut(|script| op(script, self.owner)) - } - - /// Calls a function with a NativeClass instance and its owner, and returns its return - /// value. - #[inline] - pub fn map_owned(&self, op: F) -> Result::Err> - where - T::UserData: MapOwned, - F: FnOnce(T, TRef<'_, T::Base, Access>) -> U, - { - self.script.map_owned(|script| op(script, self.owner)) - } -} - -impl Clone for Instance -where - T: NativeClass, - Ref: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Instance { - owner: self.owner.clone(), - script: self.script.clone(), - } - } -} - -impl<'a, T, Access: ThreadAccess> Clone for RefInstance<'a, T, Access> -where - T: NativeClass, -{ - #[inline] - fn clone(&self) -> Self { - RefInstance { - owner: self.owner, - script: self.script.clone(), - } - } -} - -impl ToVariant for Instance -where - T: NativeClass, - Ref: ToVariant, -{ - #[inline] - fn to_variant(&self) -> Variant { - self.owner.to_variant() - } -} - -impl OwnedToVariant for Instance -where - T: NativeClass, -{ - #[inline] - fn owned_to_variant(self) -> Variant { - self.into_base().owned_to_variant() - } -} - -impl FromVariant for Instance -where - T: NativeClass, - T::Base: GodotObject, -{ - #[inline] - fn from_variant(variant: &Variant) -> Result { - let owner = Ref::::from_variant(variant)?; - Self::from_base(owner).ok_or(FromVariantError::InvalidInstance { - expected: T::class_name(), - }) - } -} - -fn try_get_user_data_ptr(owner: &RawObject) -> Option<*mut libc::c_void> { - unsafe { - let api = get_api(); - - let owner_ptr = owner.sys().as_ptr(); - - let type_tag = (api.godot_nativescript_get_type_tag)(owner_ptr); - if type_tag.is_null() { - return None; - } - - 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/export/macros.rs b/gdnative-core/src/export/macros.rs index 165c9cebd..d09932a82 100644 --- a/gdnative-core/src/export/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -28,7 +28,8 @@ macro_rules! godot_wrap_method_inner { #[derive(Copy, Clone, Default)] struct ThisMethod; - use $crate::export::{NativeClass, Instance, RefInstance, OwnerArg}; + use $crate::export::{NativeClass, OwnerArg}; + use $crate::object::{Instance, RefInstance}; use ::gdnative::derive::FromVarargs; #[derive(FromVarargs)] diff --git a/gdnative-core/src/export/method.rs b/gdnative-core/src/export/method.rs index d79c1a90f..66c3cff39 100644 --- a/gdnative-core/src/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, RefInstance}; +use crate::export::class::NativeClass; +use crate::export::ClassBuilder; use crate::log::Site; use crate::object::ownership::Shared; +use crate::object::RefInstance; use crate::object::{Ref, TRef}; -use crate::export::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/property.rs b/gdnative-core/src/export/property.rs index 6eb26539a..13cb7a9c2 100644 --- a/gdnative-core/src/export/property.rs +++ b/gdnative-core/src/export/property.rs @@ -4,14 +4,12 @@ use accessor::{Getter, RawGetter, RawSetter, Setter}; use invalid_accessor::{InvalidGetter, InvalidSetter}; use crate::core_types::*; -use crate::export::{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 crate::export::{ClassBuilder, Export}; - mod accessor; mod invalid_accessor; diff --git a/gdnative-core/src/object/instance.rs b/gdnative-core/src/object/instance.rs new file mode 100644 index 000000000..0ea932fe7 --- /dev/null +++ b/gdnative-core/src/object/instance.rs @@ -0,0 +1,601 @@ +use std::ptr::NonNull; + +use crate::core_types::{ + FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant, +}; +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, QueueFree, RawObject, Ref, TRef}; +use crate::private::{get_api, ReferenceCountedClassPlaceholder}; + +/// 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 +/// reference-counted. Otherwise, use `assume_safe` to obtain a temporary `RefInstance`. +/// +/// See the type-level documentation on `Ref` for more information on typed thread accesses. +#[derive(Debug)] +pub struct Instance { + owner: Ref, + script: T::UserData, +} + +/// A reference to a GodotObject with a rust NativeClass attached that is assumed safe during +/// a certain lifetime. +/// +/// See the type-level documentation on `Ref` for more information on typed thread accesses. +#[derive(Debug)] +pub struct RefInstance<'a, T: NativeClass, Access: ThreadAccess> { + owner: TRef<'a, T::Base, Access>, + script: T::UserData, +} + +impl Instance { + /// Creates a `T::Base` with the script `T` attached. Both `T::Base` and `T` must have zero + /// argument constructors. + /// + /// If `T::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] + #[allow(clippy::new_without_default)] + pub fn new() -> Self + where + T::Base: Instanciable, + { + Self::maybe_emplace(None) + } + + /// Creates a `T::Base` with a given instance of the script `T` attached. `T::Base` must + /// have a zero-argument constructor. + /// + /// This may be used to create instances of scripts that do not have zero-argument + /// constructors: + /// + /// ```ignore + /// // This type does not have a zero-argument constructor. As a result, `Instance::new` + /// // will panic and `Foo.new` from GDScript will result in errors when the object is used. + /// #[derive(NativeScript)] + /// #[inherit(Reference)] + /// #[no_constructor] + /// struct MyIntVector(i64, i64); + /// + /// #[methods] + /// impl MyIntVector { + /// // - snip - + /// } + /// + /// // With `Instance::emplace`, however, we can expose "constructors" from a factory + /// // auto-load for our script type. + /// #[derive(NativeScript)] + /// #[inherit(Node)] + /// #[user_data(Aether)] + /// struct MyIntVectorFactory; + /// + /// #[methods] + /// impl MyIntVectorFactory { + /// #[export] + /// fn make(&self, _owner: &Node, x: i64, y: i64) -> Instance { + /// Instance::emplace(MyIntVector(x, y)) + /// } + /// } + /// ``` + /// + /// If `T::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] + pub fn emplace(script: T) -> Self + where + T::Base: Instanciable, + { + Self::maybe_emplace(Some(script)) + } + + fn maybe_emplace(script: Option) -> Self + where + T::Base: Instanciable, + { + unsafe { + let gd_api = get_api(); + let nativescript_methods = crate::private::NativeScriptMethodTable::get(gd_api); + + assert_ne!( + std::ptr::null(), + nativescript_methods.set_class_name, + "NativeScript::set_class_name must be available" + ); + assert_ne!( + std::ptr::null(), + nativescript_methods.set_library, + "NativeScript::set_library must be available" + ); + assert_ne!( + std::ptr::null(), + nativescript_methods.new, + "NativeScript::new must be available" + ); + + // The API functions take NUL-terminated C strings. &CStr is not used for its runtime cost. + let class_name = b"NativeScript\0".as_ptr() as *const libc::c_char; + let ctor = (gd_api.godot_get_class_constructor)(class_name).unwrap(); + + let native_script = + NonNull::new(ctor()).expect("NativeScript constructor should not return null"); + let native_script = + RawObject::::from_sys_ref_unchecked( + native_script, + ); + native_script.init_ref_count(); + + let script_class_name = GodotString::from(T::class_name()); + let mut args: [*const libc::c_void; 1] = [script_class_name.sys() as *const _]; + (gd_api.godot_method_bind_ptrcall)( + nativescript_methods.set_class_name, + native_script.sys().as_ptr(), + args.as_mut_ptr(), + std::ptr::null_mut(), + ); + + let mut args: [*const libc::c_void; 1] = [crate::private::get_gdnative_library_sys()]; + (gd_api.godot_method_bind_ptrcall)( + nativescript_methods.set_library, + native_script.sys().as_ptr(), + args.as_mut_ptr(), + std::ptr::null_mut(), + ); + + if let Some(script) = script { + emplace::place(script); + } + + let mut args: [*const sys::godot_variant; 0] = []; + let variant = (gd_api.godot_method_bind_call)( + nativescript_methods.new, + native_script.sys().as_ptr(), + args.as_mut_ptr(), + 0, + std::ptr::null_mut(), + ); + + debug_assert!(class_registry::is_class_registered::(), "`{type_name}` must be registered before it can be used; call `handle.add_class::<{type_name}>()` in your `nativescript_init` callback", type_name = std::any::type_name::()); + + assert!( + emplace::take::().is_none(), + "emplacement value should be taken by the constructor wrapper (this is a bug in the bindings)", + ); + + let variant = Variant::from_sys(variant); + + let owner = variant + .try_to_object::() + .expect("the engine should return a base object of the correct type") + .assume_unique(); + + let script_ptr = + (gd_api.godot_nativescript_get_userdata)(owner.sys()) as *const libc::c_void; + + assert_ne!( + std::ptr::null(), + script_ptr, + "script instance should not be null (did the constructor fail?)" + ); + + let script = T::UserData::clone_from_user_data_unchecked(script_ptr); + + native_script.unref(); + + Instance { owner, script } + } + } +} + +impl Instance { + /// Returns the base object, dropping the script wrapper. + #[inline] + pub fn into_base(self) -> Ref { + self.owner + } + + /// Returns the script wrapper. + #[inline] + pub fn into_script(self) -> T::UserData { + self.script + } + + /// Returns the base object and the script wrapper. + #[inline] + pub fn decouple(self) -> (Ref, T::UserData) { + (self.owner, self.script) + } + + /// Returns a reference to the base object. + #[inline] + pub fn base(&self) -> &Ref { + &self.owner + } + + /// Returns a reference to the script wrapper. + #[inline] + pub fn script(&self) -> &T::UserData { + &self.script + } +} + +impl Instance +where + RefImplBound: SafeAsRaw<::RefKind, Access>, +{ + /// Try to downcast `Ref` to `Instance`, without changing the reference + /// count if reference-counted. + /// + /// # Errors + /// + /// Returns the original `Ref` if the cast failed. + #[inline] + pub fn try_from_base(owner: Ref) -> Result> { + let user_data = match try_get_user_data_ptr::(owner.as_raw()) { + Some(user_data) => user_data, + None => return Err(owner), + }; + + let script = unsafe { T::UserData::clone_from_user_data_unchecked(user_data) }; + + Ok(Instance { owner, script }) + } + + /// Try to downcast `Ref` to `Instance`, without changing the reference + /// count if reference-counted. Shorthand for `Self::try_from_base().ok()`. + #[inline] + pub fn from_base(owner: Ref) -> Option { + Self::try_from_base(owner).ok() + } +} + +impl Instance +where + RefImplBound: SafeDeref<::RefKind, Access>, +{ + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. Can be used on reference counted types for multiple times. + #[inline] + pub fn map(&self, op: F) -> Result::Err> + where + T::UserData: Map, + F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U, + { + self.script.map(|script| op(script, self.owner.as_ref())) + } + + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. Can be used on reference counted types for multiple times. + #[inline] + pub fn map_mut(&self, op: F) -> Result::Err> + where + T::UserData: MapMut, + F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U, + { + self.script + .map_mut(|script| op(script, self.owner.as_ref())) + } + + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. Can be used on reference counted types for multiple times. + #[inline] + pub fn map_owned(&self, op: F) -> Result::Err> + where + T::UserData: MapOwned, + F: FnOnce(T, TRef<'_, T::Base, Access>) -> U, + { + self.script + .map_owned(|script| op(script, self.owner.as_ref())) + } +} + +/// Methods for instances with manually-managed base classes. +impl Instance { + /// Assume that `self` is safe to use. + /// + /// This is *not* guaranteed to be a no-op at runtime. + /// + /// # Safety + /// + /// It's safe to call `assume_safe` only if the constraints of `Ref::assume_safe` + /// are satisfied for the base object. + #[inline] + pub unsafe fn assume_safe<'a, 'r>(&'r self) -> RefInstance<'a, T, Shared> + where + AssumeSafeLifetime<'a, 'r>: LifetimeConstraint<::RefKind>, + { + RefInstance { + owner: self.owner.assume_safe(), + script: self.script.clone(), + } + } +} + +impl Instance +where + T::Base: GodotObject, +{ + /// Returns `true` if the pointer currently points to a valid object of the correct type. + /// **This does NOT guarantee that it's safe to use this pointer.** + /// + /// # Safety + /// + /// This thread must have exclusive access to the object during the call. + #[inline] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub unsafe fn is_instance_sane(&self) -> bool { + self.owner.is_instance_sane() + } +} + +impl Instance +where + T::Base: GodotObject, +{ + /// Frees the base object and user-data wrapper. + /// + /// Same as `self.into_base().free()`. + #[inline] + pub fn free(self) { + self.into_base().free() + } +} + +impl Instance +where + T::Base: GodotObject + QueueFree, +{ + /// Queues the base object and user-data wrapper for deallocation in the near future. + /// This should be preferred to `free` for `Node`s. + /// + /// Same as `self.into_base().queue_free()`. + #[inline] + pub fn queue_free(self) { + self.into_base().queue_free() + } +} + +impl Instance { + /// Coverts into a `Shared` instance. + #[inline] + pub fn into_shared(self) -> Instance { + Instance { + owner: self.owner.into_shared(), + script: self.script, + } + } +} + +impl Instance +where + T::Base: GodotObject, +{ + /// Coverts into a `ThreadLocal` instance. + #[inline] + pub fn into_thread_local(self) -> Instance { + Instance { + owner: self.owner.into_thread_local(), + script: self.script, + } + } +} + +impl Instance { + /// Assume that `self` is the unique reference to the underlying base object. + /// + /// This is guaranteed to be a no-op at runtime if `debug_assertions` is disabled. Runtime + /// sanity checks may be added in debug builds to help catch bugs. + /// + /// # Safety + /// + /// Calling `assume_unique` when `self` isn't the unique reference is instant undefined + /// behavior. This is a much stronger assumption than `assume_safe` and should be used with + /// care. + #[inline] + pub unsafe fn assume_unique(self) -> Instance { + Instance { + owner: self.owner.assume_unique(), + script: self.script, + } + } +} + +impl Instance +where + T::Base: GodotObject, +{ + /// Assume that all references to the underlying object is local to the current thread. + /// + /// This is guaranteed to be a no-op at runtime. + /// + /// # Safety + /// + /// Calling `assume_thread_local` when there are references on other threads is instant + /// undefined behavior. This is a much stronger assumption than `assume_safe` and should + /// be used with care. + #[inline] + pub unsafe fn assume_thread_local(self) -> Instance { + Instance { + owner: self.owner.assume_thread_local(), + script: self.script, + } + } +} + +impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access> { + /// Returns a reference to the base object with the same lifetime. + #[inline] + pub fn base(&self) -> TRef<'a, T::Base, Access> { + self.owner + } + + /// Returns a reference to the script wrapper. + #[inline] + pub fn script(&self) -> &T::UserData { + &self.script + } + + /// Try to downcast `TRef<'a, T::Base, Access>` to `RefInstance`. + #[inline] + pub fn try_from_base(owner: TRef<'a, T::Base, Access>) -> Option { + let user_data = try_get_user_data_ptr::(owner.as_raw())?; + unsafe { Some(Self::from_raw_unchecked(owner, user_data)) } + } + + /// Pairs an `owner` and `user_data` without checking validity. Internal interface. + #[doc(hidden)] + #[inline] + pub unsafe fn from_raw_unchecked( + owner: TRef<'a, T::Base, Access>, + user_data: *mut libc::c_void, + ) -> Self { + let script = T::UserData::clone_from_user_data_unchecked(user_data); + RefInstance { owner, script } + } +} + +impl<'a, T: NativeClass, Access: NonUniqueThreadAccess> RefInstance<'a, T, Access> { + /// Persists this into a persistent `Instance` with the same thread access, without cloning + /// the userdata wrapper. + /// + /// This is only available for non-`Unique` accesses. + #[inline] + pub fn claim(self) -> Instance { + Instance { + owner: self.owner.claim(), + script: self.script, + } + } +} + +/// Methods for instances with reference-counted base classes. +impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access> +where + T::Base: GodotObject, +{ + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. + #[inline] + pub fn map(&self, op: F) -> Result::Err> + where + T::UserData: Map, + F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U, + { + self.script.map(|script| op(script, self.owner)) + } + + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. + #[inline] + pub fn map_mut(&self, op: F) -> Result::Err> + where + T::UserData: MapMut, + F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U, + { + self.script.map_mut(|script| op(script, self.owner)) + } + + /// Calls a function with a NativeClass instance and its owner, and returns its return + /// value. + #[inline] + pub fn map_owned(&self, op: F) -> Result::Err> + where + T::UserData: MapOwned, + F: FnOnce(T, TRef<'_, T::Base, Access>) -> U, + { + self.script.map_owned(|script| op(script, self.owner)) + } +} + +impl Clone for Instance +where + T: NativeClass, + Ref: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Instance { + owner: self.owner.clone(), + script: self.script.clone(), + } + } +} + +impl<'a, T, Access: ThreadAccess> Clone for RefInstance<'a, T, Access> +where + T: NativeClass, +{ + #[inline] + fn clone(&self) -> Self { + RefInstance { + owner: self.owner, + script: self.script.clone(), + } + } +} + +impl ToVariant for Instance +where + T: NativeClass, + Ref: ToVariant, +{ + #[inline] + fn to_variant(&self) -> Variant { + self.owner.to_variant() + } +} + +impl OwnedToVariant for Instance +where + T: NativeClass, +{ + #[inline] + fn owned_to_variant(self) -> Variant { + self.into_base().owned_to_variant() + } +} + +impl FromVariant for Instance +where + T: NativeClass, + T::Base: GodotObject, +{ + #[inline] + fn from_variant(variant: &Variant) -> Result { + let owner = Ref::::from_variant(variant)?; + Self::from_base(owner).ok_or(FromVariantError::InvalidInstance { + expected: T::class_name(), + }) + } +} + +fn try_get_user_data_ptr(owner: &RawObject) -> Option<*mut libc::c_void> { + unsafe { + let api = get_api(); + + let owner_ptr = owner.sys().as_ptr(); + + let type_tag = (api.godot_nativescript_get_type_tag)(owner_ptr); + if type_tag.is_null() { + return None; + } + + if !crate::export::type_tag::check::(type_tag) { + return None; + } + + Some((api.godot_nativescript_get_userdata)(owner_ptr)) + } +} diff --git a/gdnative-core/src/object/mod.rs b/gdnative-core/src/object/mod.rs index a30e82b95..9d0e3770d 100644 --- a/gdnative-core/src/object/mod.rs +++ b/gdnative-core/src/object/mod.rs @@ -14,21 +14,26 @@ use std::marker::PhantomData; use std::ops::Deref; use std::ptr::NonNull; -use crate::export::{Instance, NativeClass, RefInstance}; -use crate::private::{get_api, ManuallyManagedClassPlaceholder, ReferenceCountedClassPlaceholder}; -use crate::sys; -use bounds::{AssumeSafeLifetime, LifetimeConstraint, PtrWrapper, RefKindSpec}; +use bounds::{ + AssumeSafeLifetime, LifetimeConstraint, PtrWrapper, RefImplBound, RefKindSpec, SafeAsRaw, + SafeDeref, +}; 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}; +use crate::export::NativeClass; +use crate::private::{get_api, ManuallyManagedClassPlaceholder, ReferenceCountedClassPlaceholder}; +use crate::sys; + +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; diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 3f0c40e08..4b11da16d 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -1,26 +1,37 @@ +#[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::{ 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::export::{ - ClassBuilder, ExportInfo, Instance, Method, MethodBuilder, NativeClass, NativeClassMethods, - PropertyUsage, RefInstance, Signal, SignalArgument, +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::init::InitHandle; +pub use gdnative_derive::*; // Re-export selected user_data types, but keep qualified due to rather generic names pub mod user_data { @@ -28,20 +39,3 @@ pub mod user_data { Aether, ArcData, LocalCellData, MutexData, RwLockData, }; } - -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_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::*; From 9efb787918f8209a178d077598a703d0b9704035 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 6 Nov 2021 22:45:36 +0100 Subject: [PATCH 08/12] Avoid duplicate symbols in gdnative::api; inner modules only have additional types (not the API class itself) --- bindings_generator/src/api.rs | 3 +-- bindings_generator/src/classes.rs | 17 +++++++++++------ bindings_generator/src/lib.rs | 17 ++++++++++------- bindings_generator/src/special_methods.rs | 6 ++---- gdnative-bindings/build.rs | 4 ++-- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/bindings_generator/src/api.rs b/bindings_generator/src/api.rs index 12cdc68b7..ab2ec3a07 100644 --- a/bindings_generator/src/api.rs +++ b/bindings_generator/src/api.rs @@ -406,9 +406,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/lib.rs b/bindings_generator/src/lib.rs index 308856692..2e088cd44 100644 --- a/bindings_generator/src/lib.rs +++ b/bindings_generator/src/lib.rs @@ -84,9 +84,8 @@ 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 class_doc = generate_class_documentation(api, class); + let class_struct = generate_class_struct(class, class_doc); let enums = generate_enums(class); @@ -99,7 +98,6 @@ fn generate_class_bindings( let class_impl = generate_class_impl(class, icalls, docs); quote! { - #documentation #class_struct #enums #constants @@ -195,17 +193,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/gdnative-bindings/build.rs b/gdnative-bindings/build.rs index 6baafcad6..3795b0cc2 100644 --- a/gdnative-bindings/build.rs +++ b/gdnative-bindings/build.rs @@ -58,7 +58,7 @@ fn generate( use super::*; {content} }} - 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, @@ -101,7 +101,7 @@ fn generate( r#" #[path = {:?}] pub mod {mod_name}; - pub use crate::generated::{mod_name}::{class_name}; + pub use crate::generated::{mod_name}::private::{class_name}; "#, mod_path.display(), mod_name = mod_name, From a2887cb7e5ccff2a25f37693d86546801dc0d793 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 6 Nov 2021 23:47:51 +0100 Subject: [PATCH 09/12] Add bidirectional links between api::MyClass and api::my_class; remove empty nested modules --- bindings_generator/src/api.rs | 5 ++++ bindings_generator/src/documentation.rs | 32 +++++++++++++++++++-- bindings_generator/src/lib.rs | 10 ++++--- gdnative-bindings/build.rs | 38 ++++++++++++++++++------- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/bindings_generator/src/api.rs b/bindings_generator/src/api.rs index ab2ec3a07..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; diff --git a/bindings_generator/src/documentation.rs b/bindings_generator/src/documentation.rs index b5926a6bb..98db7fbe9 100644 --- a/bindings_generator/src/documentation.rs +++ b/bindings_generator/src/documentation.rs @@ -17,6 +17,17 @@ pub fn official_doc_url(class: &GodotClass) -> String { ) } +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 { "" }; @@ -26,7 +37,7 @@ pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStrea "unsafe" }; - 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() @@ -37,11 +48,11 @@ pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStrea name = class.name, base_class = class.base_class, ownership_type = ownership_type, - singleton = singleton_str + singleton = singleton_str, ) } else { format!( - "`{api_type} {singleton}class {name}` ({ownership_type}).", + "`{api_type} {singleton}class {name}` ({ownership_type})", api_type = class.api_type, name = class.name, ownership_type = ownership_type, @@ -49,6 +60,8 @@ pub fn generate_class_documentation(api: &Api, class: &GodotClass) -> TokenStrea ) }; + append_related_module(&mut summary_doc, class); + let official_docs = format!( r#"## Official documentation @@ -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 2e088cd44..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,6 +84,7 @@ fn generate_class_bindings( ) -> TokenStream { // types and methods let types_and_methods = { + let module_doc = generate_module_doc(class); let class_doc = generate_class_documentation(api, class); let class_struct = generate_class_struct(class, class_doc); @@ -98,6 +99,7 @@ fn generate_class_bindings( let class_impl = generate_class_impl(class, icalls, docs); quote! { + #module_doc #class_struct #enums #constants diff --git a/gdnative-bindings/build.rs b/gdnative-bindings/build.rs index 3795b0cc2..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}::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}; + {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(); } From 3287dfb91d25544153e76c2ac7834d84b222802a Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 8 Nov 2021 10:48:09 +0100 Subject: [PATCH 10/12] Minor doc improvements for API classes --- bindings_generator/src/documentation.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bindings_generator/src/documentation.rs b/bindings_generator/src/documentation.rs index 98db7fbe9..2775ddf97 100644 --- a/bindings_generator/src/documentation.rs +++ b/bindings_generator/src/documentation.rs @@ -12,7 +12,7 @@ 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(), ) } @@ -31,31 +31,31 @@ pub fn generate_module_doc(class: &GodotClass) -> TokenStream { 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 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, + 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, ) }; @@ -79,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. @@ -126,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 From 757d4078840f65c9b83cfd30dfd1d7d7f0126301 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 8 Nov 2021 11:19:44 +0100 Subject: [PATCH 11/12] Move remaining macros to gdnative::log --- gdnative-core/src/log.rs | 3 +++ gdnative/src/lib.rs | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) 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/src/lib.rs b/gdnative/src/lib.rs index 00531fa40..cb35afd40 100644 --- a/gdnative/src/lib.rs +++ b/gdnative/src/lib.rs @@ -60,11 +60,6 @@ #[doc(inline)] pub use gdnative_core::{core_types, export, init, log, object, profiler}; -/// Collection of declarative `godot_*` macros, mostly for GDNative registration and output. -pub mod macros { - pub use gdnative_core::{godot_dbg, godot_error, godot_print, godot_warn}; -} - // Implementation details (e.g. used by macros). // However, do not re-export macros (on crate level), thus no wildcard #[doc(hidden)] From c4c5281bd5a1c16376617574245e0c99f63a3d5b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 8 Nov 2021 12:27:56 +0100 Subject: [PATCH 12/12] Remove lesser-used macros from prelude Removed: * godot_gdnative_init * godot_gdnative_terminate * godot_nativescript_init * godot_site Also documented prelude::user_data --- gdnative/src/prelude.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index 4b11da16d..f59629919 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -27,14 +27,12 @@ pub use gdnative_core::object::{ ownership::{Shared, ThreadLocal, Unique}, AsArg, GodotObject, Instanciable, NewRef, Null, QueueFree, Ref, SubClass, TRef, }; -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::*; -// Re-export selected user_data types, but keep qualified due to rather generic names +/// 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, };