From 5264607857a9ab4f687808782b21d8c989fb69e3 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 3 Dec 2022 12:52:30 +0100 Subject: [PATCH 01/16] Fix macOS build setup + .gdextension file --- godot-ffi/Cargo.toml | 2 +- godot-ffi/build.rs | 32 +++++++++++++++++++++++++++++++- itest/godot/itest.gdextension | 5 +++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/godot-ffi/Cargo.toml b/godot-ffi/Cargo.toml index ef1208205..7518dba8c 100644 --- a/godot-ffi/Cargo.toml +++ b/godot-ffi/Cargo.toml @@ -14,5 +14,5 @@ minimal = ["godot-codegen/minimal"] paste = "1" [build-dependencies] -bindgen = { version = "0.60.1", default-features = false, features = [] } +bindgen = { version = "0.63.0", default-features = false, features = ["runtime"] } godot-codegen = { path = "../godot-codegen" } diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index 8d3475c82..7a6b167e8 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -65,7 +65,8 @@ fn configure_platform_specific(builder: bindgen::Builder) -> bindgen::Builder { builder .clang_arg("-I") - .clang_arg(format!("{path}/include")) + // .clang_arg(format!("{path}/include")) + .clang_arg(apple_include_path().expect("apple include path")) .clang_arg("-L") .clang_arg(format!("{path}/lib")) } else { @@ -74,6 +75,35 @@ fn configure_platform_specific(builder: bindgen::Builder) -> bindgen::Builder { } } +fn apple_include_path() -> Result { + use std::process::Command; + + let target = std::env::var("TARGET").unwrap(); + let platform = if target.contains("apple-darwin") { + "macosx" + } else if target == "x86_64-apple-ios" || target == "aarch64-apple-ios-sim" { + "iphonesimulator" + } else if target == "aarch64-apple-ios" { + "iphoneos" + } else { + panic!("not building for macOS or iOS"); + }; + + // run `xcrun --sdk iphoneos --show-sdk-path` + let output = Command::new("xcrun") + .args(["--sdk", platform, "--show-sdk-path"]) + .output()? + .stdout; + let prefix = std::str::from_utf8(&output) + .expect("invalid output from `xcrun`") + .trim_end(); + + let suffix = "usr/include"; + let directory = format!("{}/{}", prefix, suffix); + + Ok(directory) +} + // #[cfg(not(target_os = "macos"))] // fn configure_platform_specific(builder: Builder) -> Builder { // println!("Build selected for Linux/Windows."); diff --git a/itest/godot/itest.gdextension b/itest/godot/itest.gdextension index cbf50b53f..0f155f806 100644 --- a/itest/godot/itest.gdextension +++ b/itest/godot/itest.gdextension @@ -2,5 +2,6 @@ entry_symbol = "itest_init" [libraries] -linux.64 = "res://../../target/debug/libitest.so" -windows.64 = "res://../../target/debug/itest.dll" +linux.x86_64 = "res://../../target/debug/libitest.so" +windows.x86_64 = "res://../../target/debug/itest.dll" +macos = "res://../../target/debug/libitest.dylib" From fde3058bbf21b0f2f1e452b652ec054aa5afa440 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 3 Dec 2022 16:52:42 +0100 Subject: [PATCH 02/16] Enable unit-tests without running Godot --- .github/composite/godot/action.yml | 1 + .github/workflows/minimal-ci.yml | 79 +++++++++++++++++++------- godot-codegen/Cargo.toml | 4 +- godot-codegen/src/central_generator.rs | 1 + godot-codegen/src/lib.rs | 6 ++ godot-core/Cargo.toml | 3 +- godot-ffi/Cargo.toml | 1 + godot-ffi/build.rs | 4 +- godot-ffi/src/lib.rs | 51 +++++++++++++++-- godot-macros/Cargo.toml | 3 + 10 files changed, 123 insertions(+), 30 deletions(-) diff --git a/.github/composite/godot/action.yml b/.github/composite/godot/action.yml index b3ddb81e3..674a8739f 100644 --- a/.github/composite/godot/action.yml +++ b/.github/composite/godot/action.yml @@ -92,6 +92,7 @@ runs: - name: "Install LLVM" uses: ./.github/composite/llvm + # TODO only run it on systems needed - name: "Build godot-rust" run: | diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 863608399..a516eda9e 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -49,28 +49,63 @@ jobs: # - name: "Check rustfmt" # run: cargo fmt --all -- --check -# unit-test: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v3 -# - name: "Install Rust" -# uses: ./.github/composite/rust -# -# # TODO seems unneeded in GDNative...? -# - name: "Install LLVM" -# uses: ./.github/composite/llvm -# -# - name: "Compile tests" -# run: cargo test --workspace --features ${GDEXT_FEATURES} --no-run -# -# - name: "Test" -# run: cargo test --workspace --features ${GDEXT_FEATURES} + unit-test: + name: test (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + continue-on-error: false + strategy: + fail-fast: false # cancel all jobs as soon as one fails? + matrix: + # Order this way because macOS typically has the longest duration, followed by Windows, so it benefits total workflow execution time. + # Additionally, the 'linux (msrv *)' special case will then be listed next to the other 'linux' jobs. + # Note: Windows uses '--target x86_64-pc-windows-msvc' by default as Cargo argument. + include: + - name: macos + os: macos-11 + rust-toolchain: stable + + - name: windows + os: windows-latest + rust-toolchain: stable-x86_64-pc-windows-msvc + + # Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility. + # If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM. + - name: linux + os: ubuntu-20.04 + rust-toolchain: stable + + steps: + - uses: actions/checkout@v3 + + - name: "Install Rust" + uses: ./.github/composite/rust + + - name: "Install minimal dependency versions from Cargo" + run: cargo +nightly update -Z minimal-versions + if: ${{ matrix.rust.special == 'minimal-deps' }} + + - name: "Install Rust" + uses: ./.github/composite/rust + with: + rust: stable + cache-key: ${{ matrix.rust.special }} # 'minimal-deps' or empty/not defined + + - name: "Install LLVM" + uses: ./.github/composite/llvm + if: matrix.name == 'macos' + + - name: "Compile tests" + run: cargo test --workspace --features ${GDEXT_FEATURES} --no-run + + - name: "Test" + run: cargo test --workspace --features ${GDEXT_FEATURES} ${{ matrix.testflags }} + integration-test-godot: - name: itest-godot-${{ matrix.name }} - timeout-minutes: 15 + name: itest-godot (${{ matrix.name }}) runs-on: ${{ matrix.os }} continue-on-error: false + timeout-minutes: 15 strategy: fail-fast: false # cancel all jobs as soon as one fails? matrix: @@ -78,10 +113,10 @@ jobs: # Additionally, the 'linux (msrv *)' special case will then be listed next to the other 'linux' jobs. # Note: Windows uses '--target x86_64-pc-windows-msvc' by default as Cargo argument. include: -# - name: macos -# os: macos-12 -# rust-toolchain: stable -# godot-binary: godot.macos.editor.dev.x86_64 + - name: macos + os: macos-12 + rust-toolchain: stable + godot-binary: godot.macos.editor.dev.x86_64 - name: windows os: windows-latest diff --git a/godot-codegen/Cargo.toml b/godot-codegen/Cargo.toml index 8ab1b6821..ba172ec03 100644 --- a/godot-codegen/Cargo.toml +++ b/godot-codegen/Cargo.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2021" rust-version = "1.63" +# Note: features not additive, but this is an internal crate [features] +codegen-disabled = [] # if active, nothing is generated (for unit tests) codegen-fmt = [] -minimal = ["codegen-fmt"] # note: should be additive, i.e. inverted +minimal = ["codegen-fmt"] [dependencies] quote = "1" diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index 37efe3b7c..efb46fd39 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -106,6 +106,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { } impl GlobalMethodTable { + #[cfg(not(test))] pub(crate) unsafe fn new(interface: &crate::GDNativeInterface) -> Self { Self { #(#variant_fn_inits)* diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index 4e4506480..16680ad2f 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -31,6 +31,12 @@ use quote::{quote, ToTokens}; use std::path::{Path, PathBuf}; pub fn generate_all_files(sys_out_dir: &Path, core_out_dir: &Path, stats_out_dir: &Path) { + // When invoked by another crate during unit-test (not integration test), don't run generator + // cfg! is easier to handle than #[cfg] regarding all the imports, and neither code size nor performance matter in unit-test + if cfg!(feature = "codegen-disabled") { + return; + } + let central_sys_gen_path = sys_out_dir; let central_core_gen_path = core_out_dir; let class_gen_path = core_out_dir; diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 50bee352e..1154da012 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -23,4 +23,5 @@ glam = { version = "0.22", features = ["debug-glam-assert", "scalar-math"] } # Reverse dev dependencies so doctests can use `godot::` prefix [dev-dependencies] -godot = { path = "../godot" } \ No newline at end of file +godot = { path = "../godot" } +godot-ffi = { path = "../godot-ffi", features = ["codegen-disabled"] } # unit-test diff --git a/godot-ffi/Cargo.toml b/godot-ffi/Cargo.toml index 7518dba8c..bf2ba4a82 100644 --- a/godot-ffi/Cargo.toml +++ b/godot-ffi/Cargo.toml @@ -9,6 +9,7 @@ rust-version = "1.63" [features] codegen-fmt = ["godot-codegen/codegen-fmt"] minimal = ["godot-codegen/minimal"] +codegen-disabled = ["godot-codegen/codegen-disabled"] [dependencies] paste = "1" diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index 7a6b167e8..766779a5d 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -4,10 +4,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use godot_codegen as gen; use std::env; use std::path::Path; +#[cfg(not(test))] +use godot_codegen as gen; + // Note: this macro is fine during codegen, but not for building module structures // It confuses IDEs, and can cause symbols not to be found macro_rules! codegen_path { diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 064462e3a..1df92ed19 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -38,7 +38,7 @@ mod plugins; // See https://github.com/dtolnay/paste/issues/69#issuecomment-962418430 // and https://users.rust-lang.org/t/proc-macros-using-third-party-crate/42465/4 #[doc(hidden)] -pub use ::paste; +pub use paste; //pub use opaque::Opaque; use global_registry::GlobalRegistry; @@ -46,11 +46,10 @@ use global_registry::GlobalRegistry; pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; pub use central::*; -/// Late-init globals -// Note: static mut is _very_ dangerous. Here a bit less so, since modification happens only once (during init) and no -// &mut references are handed out (except for registry, see below). Overall, UnsafeCell/RefCell + Sync might be a safer abstraction. -static mut BINDING: Option = None; +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Real implementation, when Godot engine is running +#[cfg(not(test))] struct GodotBinding { interface: GDNativeInterface, library: GDNativeExtensionClassLibraryPtr, @@ -58,12 +57,19 @@ struct GodotBinding { registry: GlobalRegistry, } +/// Late-init globals +// Note: static mut is _very_ dangerous. Here a bit less so, since modification happens only once (during init) and no +// &mut references are handed out (except for registry, see below). Overall, UnsafeCell/RefCell + Sync might be a safer abstraction. +#[cfg(not(test))] +static mut BINDING: Option = None; + /// # Safety /// /// - The `interface` pointer must be a valid pointer to a [`GDNativeInterface`] obj. /// - The `library` pointer must be the pointer given by Godot at initialisation. /// - This function must not be called from multiple threads. /// - This function must be called before any use of [`get_library`]. +#[cfg(not(test))] pub unsafe fn initialize( interface: *const GDNativeInterface, library: GDNativeExtensionClassLibraryPtr, @@ -86,6 +92,7 @@ pub unsafe fn initialize( /// # Safety /// /// The interface must have been initialised with [`initialize`] before calling this function. +#[cfg(not(test))] #[inline(always)] pub unsafe fn get_interface() -> &'static GDNativeInterface { &unwrap_ref_unchecked(&BINDING).interface @@ -94,6 +101,7 @@ pub unsafe fn get_interface() -> &'static GDNativeInterface { /// # Safety /// /// The library must have been initialised with [`initialize`] before calling this function. +#[cfg(not(test))] #[inline(always)] pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { unwrap_ref_unchecked(&BINDING).library @@ -102,6 +110,7 @@ pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { /// # Safety /// /// The interface must have been initialised with [`initialize`] before calling this function. +#[cfg(not(test))] #[inline(always)] pub unsafe fn method_table() -> &'static GlobalMethodTable { &unwrap_ref_unchecked(&BINDING).method_table @@ -113,11 +122,41 @@ pub unsafe fn method_table() -> &'static GlobalMethodTable { /// /// Calling this while another place holds a reference (threads, re-entrancy, iteration, etc) is immediate undefined behavior. // note: could potentially avoid &mut aliasing, using UnsafeCell/RefCell +#[cfg(not(test))] #[inline(always)] pub unsafe fn get_registry() -> &'static mut GlobalRegistry { &mut unwrap_ref_unchecked_mut(&mut BINDING).registry } +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Stubs when in unit-test (without Godot) + +#[cfg(test)] +#[inline(always)] +pub unsafe fn get_interface() -> &'static GDNativeInterface { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") +} + +#[cfg(test)] +#[inline(always)] +pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") +} + +#[cfg(test)] +#[inline(always)] +pub unsafe fn method_table() -> &'static GlobalMethodTable { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") +} + +#[cfg(test)] +#[inline(always)] +pub unsafe fn get_registry() -> &'static mut GlobalRegistry { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + #[macro_export] #[doc(hidden)] macro_rules! interface_fn { @@ -151,6 +190,7 @@ macro_rules! static_assert_eq_size { /// Combination of `as_ref()` and `unwrap_unchecked()`, but without the case differentiation in /// the former (thus raw pointer access in release mode) +#[cfg(not(test))] unsafe fn unwrap_ref_unchecked(opt: &Option) -> &T { debug_assert!(opt.is_some(), "unchecked access to Option::None"); match opt { @@ -159,6 +199,7 @@ unsafe fn unwrap_ref_unchecked(opt: &Option) -> &T { } } +#[cfg(not(test))] unsafe fn unwrap_ref_unchecked_mut(opt: &mut Option) -> &mut T { debug_assert!(opt.is_some(), "unchecked access to Option::None"); match opt { diff --git a/godot-macros/Cargo.toml b/godot-macros/Cargo.toml index 34b9c7817..7e13f01ee 100644 --- a/godot-macros/Cargo.toml +++ b/godot-macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version = "1.63" [lib] proc-macro = true +[dev-dependencies] +godot = { path = "../godot" } # doctest + [dependencies] quote = "1" proc-macro2 = "1" From cdb0f60ac9b1755c6340fa71ceb8550bba3a6a56 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 4 Dec 2022 11:58:24 +0100 Subject: [PATCH 03/16] Both godot-core and godot-ffi now depend on godot-codegen; no more target and include! This means that JSON parsing efforts are duplicated, but we crates are more self-contained now. Each crate now has the source code it needs to process in its own directory, also confusing IDEs less. --- godot-codegen/src/central_generator.rs | 17 +++++++--- godot-codegen/src/lib.rs | 46 ++++++++++++++++---------- godot-core/Cargo.toml | 3 ++ godot-core/build.rs | 15 +++++++++ godot-core/src/engine.rs | 38 ++------------------- godot-core/src/lib.rs | 10 +++++- godot-ffi/build.rs | 23 +++++-------- godot-ffi/src/lib.rs | 22 ++++-------- 8 files changed, 87 insertions(+), 87 deletions(-) create mode 100644 godot-core/build.rs diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index efb46fd39..62247d9cf 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -51,20 +51,29 @@ struct BuiltinTypeInfo<'a> { operators: Option<&'a Vec>, } -pub(crate) fn generate_central_files( +pub(crate) fn generate_sys_central_file( api: &ExtensionApi, ctx: &mut Context, build_config: &str, sys_gen_path: &Path, - core_gen_path: &Path, out_files: &mut Vec, ) { let central_items = make_central_items(api, build_config, ctx); - let sys_code = make_sys_code(¢ral_items); - let core_code = make_core_code(¢ral_items); write_files(sys_gen_path, sys_code, out_files); +} + +pub(crate) fn generate_core_central_file( + api: &ExtensionApi, + ctx: &mut Context, + build_config: &str, + core_gen_path: &Path, + out_files: &mut Vec, +) { + let central_items = make_central_items(api, build_config, ctx); + let core_code = make_core_code(¢ral_items); + write_files(core_gen_path, core_code, out_files); } diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index 16680ad2f..9ad6599e2 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -19,7 +19,7 @@ mod watch; mod tests; use api_parser::{load_extension_api, ExtensionApi}; -use central_generator::generate_central_files; +use central_generator::{generate_core_central_file, generate_sys_central_file}; use class_generator::generate_class_files; use context::Context; use util::ident; @@ -30,36 +30,46 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use std::path::{Path, PathBuf}; -pub fn generate_all_files(sys_out_dir: &Path, core_out_dir: &Path, stats_out_dir: &Path) { +pub fn generate_sys_files(sys_gen_path: &Path) { // When invoked by another crate during unit-test (not integration test), don't run generator // cfg! is easier to handle than #[cfg] regarding all the imports, and neither code size nor performance matter in unit-test if cfg!(feature = "codegen-disabled") { return; } - let central_sys_gen_path = sys_out_dir; - let central_core_gen_path = core_out_dir; - let class_gen_path = core_out_dir; - let mut out_files = vec![]; + let mut watch = StopWatch::start(); + + let (api, build_config) = load_extension_api(&mut watch); + let mut ctx = Context::build_from_api(&api); + watch.record("build_context"); + generate_sys_central_file(&api, &mut ctx, build_config, sys_gen_path, &mut out_files); + watch.record("generate_central_file"); + + rustfmt_if_needed(out_files); + watch.record("rustfmt"); + watch.write_stats_to(&sys_gen_path.join("codegen-stats.txt")); +} + +pub fn generate_core_files(core_gen_path: &Path) { + // When invoked by another crate during unit-test (not integration test), don't run generator + // cfg! is easier to handle than #[cfg] regarding all the imports, and neither code size nor performance matter in unit-test + if cfg!(feature = "codegen-disabled") { + return; + } + + let mut out_files = vec![]; let mut watch = StopWatch::start(); let (api, build_config) = load_extension_api(&mut watch); let mut ctx = Context::build_from_api(&api); watch.record("build_context"); - generate_central_files( - &api, - &mut ctx, - build_config, - central_sys_gen_path, - central_core_gen_path, - &mut out_files, - ); - watch.record("generate_central_files"); + generate_core_central_file(&api, &mut ctx, build_config, core_gen_path, &mut out_files); + watch.record("generate_central_file"); - generate_utilities_file(&api, &mut ctx, class_gen_path, &mut out_files); + generate_utilities_file(&api, &mut ctx, core_gen_path, &mut out_files); watch.record("generate_utilities_file"); // Class files -- currently output in godot-core; could maybe be separated cleaner @@ -68,14 +78,14 @@ pub fn generate_all_files(sys_out_dir: &Path, core_out_dir: &Path, stats_out_dir &api, &mut ctx, build_config, - &class_gen_path.join("classes"), + &core_gen_path.join("classes"), &mut out_files, ); watch.record("generate_class_files"); rustfmt_if_needed(out_files); watch.record("rustfmt"); - watch.write_stats_to(&stats_out_dir.join("codegen-stats.txt")); + watch.write_stats_to(&core_gen_path.join("codegen-stats.txt")); } #[cfg(feature = "codegen-fmt")] diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 1154da012..369e80a93 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -25,3 +25,6 @@ glam = { version = "0.22", features = ["debug-glam-assert", "scalar-math"] } [dev-dependencies] godot = { path = "../godot" } godot-ffi = { path = "../godot-ffi", features = ["codegen-disabled"] } # unit-test + +[build-dependencies] +godot-codegen = { path = "../godot-codegen" } diff --git a/godot-core/build.rs b/godot-core/build.rs new file mode 100644 index 000000000..debf371ff --- /dev/null +++ b/godot-core/build.rs @@ -0,0 +1,15 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::path::Path; + +fn main() { + godot_codegen::generate_core_files(Path::new(concat!( + // + env!("CARGO_MANIFEST_DIR"), + "/src/gen/" + ))); +} diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index 9ecf5cda5..8b31db0e9 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -9,43 +9,11 @@ // Re-exports of generated symbols use crate::builtin::{GodotString, NodePath}; use crate::engine::resource_loader::CacheMode; +pub use crate::gen::central::global; +pub use crate::gen::classes::*; +pub use crate::gen::utilities; use crate::obj::dom::EngineDomain; use crate::obj::{Gd, GodotClass, Inherits}; -pub use gen::central_core::global; -pub use gen::classes::*; -pub use gen::utilities; - -/// Output of generated code. -pub(super) mod gen { - #[allow(unused_imports, dead_code, non_upper_case_globals, non_snake_case)] - pub(crate) mod classes { - // Path to core/classes/obj - // Do not write macro for this, as it confuses IDEs -- just search&replace - include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../target/godot-gen/core/classes/mod.rs" - )); - } - - pub mod utilities { - // Path to core/utilities.rs - // Do not write macro for this, as it confuses IDEs -- just search&replace - include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../target/godot-gen/core/utilities.rs" - )); - } - - #[allow(non_upper_case_globals, non_snake_case)] - pub mod central_core { - // Path to core/utilities.rs - // Do not write macro for this, as it confuses IDEs -- just search&replace - include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../target/godot-gen/core/central.rs" - )); - } -} /// Extension trait with convenience functions for the node tree. pub trait NodeExt { diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index 1c5b35877..dbe84d74c 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -20,13 +20,21 @@ pub use registry::*; pub use godot_ffi as sys; +// Output of generated code. Mimics the file structure, symbols are re-exported. +#[allow(unused_imports, dead_code, non_upper_case_globals, non_snake_case)] +mod gen { + pub mod central; + pub mod classes; + pub mod utilities; +} + #[doc(hidden)] pub mod private { // If someone forgets #[godot_api], this causes a compile error, rather than virtual functions not being called at runtime. #[allow(non_camel_case_types)] pub trait You_forgot_the_attribute__godot_api {} - pub use crate::engine::gen::classes::class_macros; + pub use crate::gen::classes::class_macros; pub use crate::registry::{callbacks, ClassPlugin, ErasedRegisterFn, PluginComponent}; pub use crate::storage::as_storage; pub use crate::{ diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index 766779a5d..c00f4f815 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -10,24 +10,19 @@ use std::path::Path; #[cfg(not(test))] use godot_codegen as gen; -// Note: this macro is fine during codegen, but not for building module structures -// It confuses IDEs, and can cause symbols not to be found -macro_rules! codegen_path { - ($path:literal) => { - concat!(env!("CARGO_MANIFEST_DIR"), "/../target/godot-gen/", $path) - }; -} - fn main() { // For custom path on macOS, iOS, Android etc: see gdnative-sys/build.rs - run_bindgen(Path::new(codegen_path!("gdnative_interface.rs"))); + run_bindgen(Path::new(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/gen/gdnative_interface.rs" + ))); - gen::generate_all_files( - Path::new(codegen_path!("sys")), - Path::new(codegen_path!("core")), - Path::new(codegen_path!("")), - ); + gen::generate_sys_files(Path::new(concat!( + // + env!("CARGO_MANIFEST_DIR"), + "/src/gen/" + ))); } fn run_bindgen(out_file: &Path) { diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 1df92ed19..b30ef8839 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -13,20 +13,10 @@ clippy::redundant_static_lifetimes )] -// Path to gdnative_interface.rs -// Do not write macro for this, as it confuses IDEs -- just search&replace -include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../target/godot-gen/gdnative_interface.rs" -)); - -pub(crate) mod central { - // Path to sys/central.rs - // Do not write macro for this, as it confuses IDEs -- just search&replace - include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../target/godot-gen/sys/central.rs" - )); +// Output of generated code. Mimics the file structure, symbols are re-exported. +pub(crate) mod gen { + pub mod central; + pub mod gdnative_interface; } mod global_registry; @@ -44,7 +34,9 @@ pub use paste; use global_registry::GlobalRegistry; pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; -pub use central::*; + +pub use gen::central::*; +pub use gen::gdnative_interface::*; // ---------------------------------------------------------------------------------------------------------------------------------------------- // Real implementation, when Godot engine is running From f3b6a78135220c9187dc842ddd39fc5f412ce8f4 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 4 Dec 2022 12:40:15 +0100 Subject: [PATCH 04/16] More adaptations for unit-test support in godot-ffi --- godot-ffi/src/lib.rs | 258 ++++++++++++++++++++++--------------------- 1 file changed, 132 insertions(+), 126 deletions(-) diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index b30ef8839..4b81d3537 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -5,15 +5,17 @@ */ //! Low level bindings to the provided C core API -#![allow( + +#![cfg_attr(test, allow(unused))] + +// Output of generated code. Mimics the file structure, symbols are re-exported. +#[allow( non_camel_case_types, non_upper_case_globals, non_snake_case, deref_nullptr, clippy::redundant_static_lifetimes )] - -// Output of generated code. Mimics the file structure, symbols are re-exported. pub(crate) mod gen { pub mod central; pub mod gdnative_interface; @@ -22,7 +24,6 @@ pub(crate) mod gen { mod global_registry; mod godot_ffi; mod opaque; - mod plugins; // See https://github.com/dtolnay/paste/issues/69#issuecomment-962418430 @@ -30,121 +31,154 @@ mod plugins; #[doc(hidden)] pub use paste; -//pub use opaque::Opaque; -use global_registry::GlobalRegistry; +pub use gen::gdnative_interface::*; +pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; // needs `crate::` -pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; +#[cfg(not(test))] +#[doc(inline)] +pub use real_impl::*; -pub use gen::central::*; -pub use gen::gdnative_interface::*; +#[cfg(test)] +#[doc(inline)] +pub use test_impl::*; // ---------------------------------------------------------------------------------------------------------------------------------------------- // Real implementation, when Godot engine is running #[cfg(not(test))] -struct GodotBinding { - interface: GDNativeInterface, - library: GDNativeExtensionClassLibraryPtr, - method_table: GlobalMethodTable, - registry: GlobalRegistry, -} +mod real_impl { + pub use super::gen::central::*; + use super::gen::gdnative_interface::*; + use super::global_registry::GlobalRegistry; + + struct GodotBinding { + interface: GDNativeInterface, + library: GDNativeExtensionClassLibraryPtr, + method_table: GlobalMethodTable, + registry: GlobalRegistry, + } -/// Late-init globals -// Note: static mut is _very_ dangerous. Here a bit less so, since modification happens only once (during init) and no -// &mut references are handed out (except for registry, see below). Overall, UnsafeCell/RefCell + Sync might be a safer abstraction. -#[cfg(not(test))] -static mut BINDING: Option = None; - -/// # Safety -/// -/// - The `interface` pointer must be a valid pointer to a [`GDNativeInterface`] obj. -/// - The `library` pointer must be the pointer given by Godot at initialisation. -/// - This function must not be called from multiple threads. -/// - This function must be called before any use of [`get_library`]. -#[cfg(not(test))] -pub unsafe fn initialize( - interface: *const GDNativeInterface, - library: GDNativeExtensionClassLibraryPtr, -) { - let ver = std::ffi::CStr::from_ptr((*interface).version_string); - println!( - "Initialize GDExtension interface: {}", - ver.to_str().unwrap() - ); - //dbg!(*interface); - - BINDING = Some(GodotBinding { - interface: *interface, - method_table: GlobalMethodTable::new(&*interface), - registry: GlobalRegistry::default(), - library, - }); -} + /// Late-init globals + // Note: static mut is _very_ dangerous. Here a bit less so, since modification happens only once (during init) and no + // &mut references are handed out (except for registry, see below). Overall, UnsafeCell/RefCell + Sync might be a safer abstraction. + static mut BINDING: Option = None; + + /// # Safety + /// + /// - The `interface` pointer must be a valid pointer to a [`GDNativeInterface`] obj. + /// - The `library` pointer must be the pointer given by Godot at initialisation. + /// - This function must not be called from multiple threads. + /// - This function must be called before any use of [`get_library`]. + pub unsafe fn initialize( + interface: *const GDNativeInterface, + library: GDNativeExtensionClassLibraryPtr, + ) { + let ver = std::ffi::CStr::from_ptr((*interface).version_string); + println!( + "Initialize GDExtension interface: {}", + ver.to_str().unwrap() + ); + //dbg!(*interface); + + BINDING = Some(GodotBinding { + interface: *interface, + method_table: GlobalMethodTable::new(&*interface), + registry: GlobalRegistry::default(), + library, + }); + } -/// # Safety -/// -/// The interface must have been initialised with [`initialize`] before calling this function. -#[cfg(not(test))] -#[inline(always)] -pub unsafe fn get_interface() -> &'static GDNativeInterface { - &unwrap_ref_unchecked(&BINDING).interface -} + /// # Safety + /// + /// The interface must have been initialised with [`initialize`] before calling this function. + #[inline(always)] + pub unsafe fn get_interface() -> &'static GDNativeInterface { + &unwrap_ref_unchecked(&BINDING).interface + } -/// # Safety -/// -/// The library must have been initialised with [`initialize`] before calling this function. -#[cfg(not(test))] -#[inline(always)] -pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { - unwrap_ref_unchecked(&BINDING).library -} + /// # Safety + /// + /// The library must have been initialised with [`initialize`] before calling this function. + #[inline(always)] + pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { + unwrap_ref_unchecked(&BINDING).library + } -/// # Safety -/// -/// The interface must have been initialised with [`initialize`] before calling this function. -#[cfg(not(test))] -#[inline(always)] -pub unsafe fn method_table() -> &'static GlobalMethodTable { - &unwrap_ref_unchecked(&BINDING).method_table -} + /// # Safety + /// + /// The interface must have been initialised with [`initialize`] before calling this function. + #[inline(always)] + pub unsafe fn method_table() -> &'static GlobalMethodTable { + &unwrap_ref_unchecked(&BINDING).method_table + } -/// # Safety -/// -/// The interface must have been initialised with [`initialize`] before calling this function. -/// -/// Calling this while another place holds a reference (threads, re-entrancy, iteration, etc) is immediate undefined behavior. -// note: could potentially avoid &mut aliasing, using UnsafeCell/RefCell -#[cfg(not(test))] -#[inline(always)] -pub unsafe fn get_registry() -> &'static mut GlobalRegistry { - &mut unwrap_ref_unchecked_mut(&mut BINDING).registry + /// # Safety + /// + /// The interface must have been initialised with [`initialize`] before calling this function. + /// + /// Calling this while another place holds a reference (threads, re-entrancy, iteration, etc) is immediate undefined behavior. + // note: could potentially avoid &mut aliasing, using UnsafeCell/RefCell + #[inline(always)] + pub unsafe fn get_registry() -> &'static mut GlobalRegistry { + &mut unwrap_ref_unchecked_mut(&mut BINDING).registry + } + + /// Combination of `as_ref()` and `unwrap_unchecked()`, but without the case differentiation in + /// the former (thus raw pointer access in release mode) + unsafe fn unwrap_ref_unchecked(opt: &Option) -> &T { + debug_assert!(opt.is_some(), "unchecked access to Option::None"); + match opt { + Some(ref val) => val, + None => std::hint::unreachable_unchecked(), + } + } + + unsafe fn unwrap_ref_unchecked_mut(opt: &mut Option) -> &mut T { + debug_assert!(opt.is_some(), "unchecked access to Option::None"); + match opt { + Some(ref mut val) => val, + None => std::hint::unreachable_unchecked(), + } + } + + pub fn default_call_error() -> GDNativeCallError { + GDNativeCallError { + error: GDNATIVE_CALL_OK, + argument: -1, + expected: -1, + } + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- // Stubs when in unit-test (without Godot) #[cfg(test)] -#[inline(always)] -pub unsafe fn get_interface() -> &'static GDNativeInterface { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") -} +mod test_impl { + use super::gen::gdnative_interface::*; + use super::global_registry::GlobalRegistry; -#[cfg(test)] -#[inline(always)] -pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") -} + pub struct GlobalMethodTable {} -#[cfg(test)] -#[inline(always)] -pub unsafe fn method_table() -> &'static GlobalMethodTable { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") -} + #[inline(always)] + pub unsafe fn get_interface() -> &'static GDNativeInterface { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") + } -#[cfg(test)] -#[inline(always)] -pub unsafe fn get_registry() -> &'static mut GlobalRegistry { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") + #[inline(always)] + pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") + } + + #[inline(always)] + pub unsafe fn method_table() -> &'static GlobalMethodTable { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") + } + + #[inline(always)] + pub unsafe fn get_registry() -> &'static mut GlobalRegistry { + unimplemented!("Not available in unit-tests; needs Godot engine to run.") + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -180,36 +214,8 @@ macro_rules! static_assert_eq_size { }; } -/// Combination of `as_ref()` and `unwrap_unchecked()`, but without the case differentiation in -/// the former (thus raw pointer access in release mode) -#[cfg(not(test))] -unsafe fn unwrap_ref_unchecked(opt: &Option) -> &T { - debug_assert!(opt.is_some(), "unchecked access to Option::None"); - match opt { - Some(ref val) => val, - None => std::hint::unreachable_unchecked(), - } -} - -#[cfg(not(test))] -unsafe fn unwrap_ref_unchecked_mut(opt: &mut Option) -> &mut T { - debug_assert!(opt.is_some(), "unchecked access to Option::None"); - match opt { - Some(ref mut val) => val, - None => std::hint::unreachable_unchecked(), - } -} - /// Extract value from box before `into_inner()` is stable pub fn unbox(value: Box) -> T { // Deref-move is a Box magic feature; see https://stackoverflow.com/a/42264074 *value } - -pub fn default_call_error() -> GDNativeCallError { - GDNativeCallError { - error: GDNATIVE_CALL_OK, - argument: -1, - expected: -1, - } -} From db55b7ea0aaf8201c46a853262c1b741f72ddc0b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 4 Dec 2022 13:27:02 +0100 Subject: [PATCH 05/16] Delete `gen` directory before writing --- godot-core/build.rs | 12 +++++++----- godot-ffi/build.rs | 15 ++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/godot-core/build.rs b/godot-core/build.rs index debf371ff..55854aed8 100644 --- a/godot-core/build.rs +++ b/godot-core/build.rs @@ -7,9 +7,11 @@ use std::path::Path; fn main() { - godot_codegen::generate_core_files(Path::new(concat!( - // - env!("CARGO_MANIFEST_DIR"), - "/src/gen/" - ))); + let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/")); + + if gen_path.exists() { + std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); + } + + godot_codegen::generate_core_files(gen_path); } diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index c00f4f815..e1f7fa3f6 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -12,17 +12,14 @@ use godot_codegen as gen; fn main() { // For custom path on macOS, iOS, Android etc: see gdnative-sys/build.rs + let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/")); - run_bindgen(Path::new(concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/gen/gdnative_interface.rs" - ))); + if gen_path.exists() { + std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); + } - gen::generate_sys_files(Path::new(concat!( - // - env!("CARGO_MANIFEST_DIR"), - "/src/gen/" - ))); + run_bindgen(&gen_path.join("gdnative_interface.rs")); + gen::generate_sys_files(gen_path); } fn run_bindgen(out_file: &Path) { From 5d6d66aec669713109d56ea27475be9d10e8c176 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 4 Dec 2022 22:45:30 +0100 Subject: [PATCH 06/16] Massive "mocking" of Godot APIs to allow unit testing --- gdext-class/src/obj/gd.rs | 535 ------------------ godot-codegen/src/central_generator.rs | 1 - godot-core/Cargo.toml | 3 +- godot-core/build.rs | 1 + godot-core/src/builtin/arrays.rs | 4 +- godot-core/src/builtin/macros.rs | 28 +- godot-core/src/builtin/mod.rs | 7 + godot-core/src/builtin/node_path.rs | 4 +- godot-core/src/builtin/others.rs | 3 +- godot-core/src/builtin/string.rs | 5 +- godot-core/src/builtin/string_name.rs | 9 +- godot-core/src/builtin/variant/impls.rs | 18 +- godot-core/src/builtin/variant/mod.rs | 7 +- .../src/builtin/variant/variant_traits.rs | 16 - godot-core/src/engine.rs | 33 +- godot-core/src/init/mod.rs | 10 + godot-core/src/lib.rs | 116 ++++ godot-core/src/obj/base.rs | 13 +- godot-core/src/obj/gd.rs | 20 +- godot-ffi/Cargo.toml | 2 +- godot-ffi/build.rs | 7 +- godot-ffi/src/gen_central_stub.rs | 222 ++++++++ godot-ffi/src/lib.rs | 91 ++- godot-macros/Cargo.toml | 2 +- godot/Cargo.toml | 2 + godot/src/lib.rs | 10 +- 26 files changed, 533 insertions(+), 636 deletions(-) delete mode 100644 gdext-class/src/obj/gd.rs create mode 100644 godot-ffi/src/gen_central_stub.rs diff --git a/gdext-class/src/obj/gd.rs b/gdext-class/src/obj/gd.rs deleted file mode 100644 index d524c0cb2..000000000 --- a/gdext-class/src/obj/gd.rs +++ /dev/null @@ -1,535 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::ptr; - -use gdext_builtin::{FromVariant, ToVariant, Variant, VariantConversionError}; -use gdext_sys as sys; -use sys::types::OpaqueObject; -use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi}; - -use crate::obj::{GdMut, GdRef, InstanceId}; -use crate::property_info::PropertyInfoBuilder; -use crate::storage::InstanceStorage; -use crate::traits::dom::Domain as _; -use crate::traits::mem::Memory as _; -use crate::traits::{cap, dom, mem, GodotClass, Inherits, Share}; -use crate::{api, callbacks, out, ClassName}; - -/// Smart pointer to objects owned by the Godot engine. -/// -/// This smart pointer can only hold _objects_ in the Godot sense: instances of Godot classes (`Node`, `RefCounted`, etc.) -/// or user-declared structs (`#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`). -/// -/// This smart pointer behaves differently depending on `T`'s associated types, see [`GodotClass`] for their documentation. -/// In particular, the memory management strategy is fully dependent on `T`: -/// -/// * Objects of type `RefCounted` or inherited from it are **reference-counted**. This means that every time a smart pointer is -/// shared using [`Share::share()`], the reference counter is incremented, and every time one is dropped, it is decremented. -/// This ensures that the last reference (either in Rust or Godot) will deallocate the object and call `T`'s destructor. -/// -/// * Objects inheriting from `Object` which are not `RefCounted` (or inherited) are **manually-managed**. -/// Their destructor is not automatically called (unless they are part of the scene tree). Creating a `Gd` means that -/// you are responsible of explicitly deallocating such objects using [`Gd::free()`]. -/// -/// * For `T=Object`, the memory strategy is determined **dynamically**. Due to polymorphism, a `Gd` can point to either -/// reference-counted or manually-managed types at runtime. The behavior corresponds to one of the two previous points. -/// Note that if the dynamic type is also `Object`, the memory is manually-managed. -pub struct Gd { - // Note: `opaque` has the same layout as GDNativeObjectPtr == Object* in C++, i.e. the bytes represent a pointer - // To receive a GDNativeTypePtr == GDNativeObjectPtr* == Object**, we need to get the address of this - // Hence separate sys() for GDNativeTypePtr, and obj_sys() for GDNativeObjectPtr. - // The former is the standard FFI type, while the latter is used in object-specific GDExtension APIs. - // pub(crate) because accessed in traits::dom - pub(crate) opaque: OpaqueObject, - _marker: PhantomData<*const T>, -} - -// Size equality check (should additionally be covered by mem::transmute()) -static_assert_eq_size!( - sys::GDNativeObjectPtr, - sys::types::OpaqueObject, - "Godot FFI: pointer type `Object*` should have size advertised in JSON extension file" -); - -/// The methods in this impl block are only available for user-declared `T`, that is, -/// structs with `#[derive(GodotClass)]` but not Godot classes like `Node` or `RefCounted`. -impl Gd -where - T: GodotClass, -{ - /// Moves a user-created object into this smart pointer, submitting ownership to the Godot engine. - /// - /// This is only useful for types `T` which do not store their base objects (if they have a base, - /// you cannot construct them standalone). - pub fn new(user_object: T) -> Self { - /*let result = unsafe { - //let ptr = interface_fn!(classdb_construct_object)(class_name.c_str()); - let ptr = callbacks::create::(ptr::null_mut()); - Obj::from_obj_sys(ptr) - }; - - result.storage().initialize(user_object);*/ - - let object_ptr = callbacks::create_custom(move |_base| user_object); - let result = unsafe { Gd::from_obj_sys(object_ptr) }; - - T::Mem::maybe_init_ref(&result); - result - } - - /// Creates a default-constructed instance of `T` inside a smart pointer. - /// - /// This is equivalent to the GDScript expression `T.new()`. - pub fn new_default() -> Self - where - T: cap::GodotInit, - { - /*let class_name = ClassName::new::(); - let result = unsafe { - let ptr = interface_fn!(classdb_construct_object)(class_name.c_str()); - Obj::from_obj_sys(ptr) - }; - - result.storage().initialize_default(); - T::Mem::maybe_init_ref(&result); - result*/ - - let result = unsafe { - let object_ptr = callbacks::create::(ptr::null_mut()); - Gd::from_obj_sys(object_ptr) - }; - - T::Mem::maybe_init_ref(&result); - result - } - - // Note: possible names: write/read, hold/hold_mut, r/w, r/rw, ... - pub fn bind(&self) -> GdRef { - GdRef::from_cell(self.storage().get()) - } - - pub fn bind_mut(&mut self) -> GdMut { - GdMut::from_cell(self.storage().get_mut()) - } - - /// Storage object associated with the extension instance - pub(crate) fn storage(&self) -> &mut InstanceStorage { - let callbacks = crate::storage::nop_instance_callbacks(); - - unsafe { - let token = sys::get_library(); - let binding = - interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); - - debug_assert!( - !binding.is_null(), - "Class {} -- null instance; does the class have a Godot creator function?", - std::any::type_name::() - ); - crate::private::as_storage::(binding) - } - } -} - -/// The methods in this impl block are available for any `T`. -impl Gd { - /// Looks up the given instance ID and returns the associated object, if possible. - /// - /// If no such instance ID is registered, or if the dynamic type of the object behind that instance ID - /// is not compatible with `T`, then `None` is returned. - pub fn try_from_instance_id(instance_id: InstanceId) -> Option { - // SAFETY: Godot looks up ID in ObjectDB and returns null if not found - let ptr = unsafe { interface_fn!(object_get_instance_from_id)(instance_id.to_u64()) }; - - if ptr.is_null() { - None - } else { - // SAFETY: assumes that the returned GDNativeObjectPtr is convertible to Object* (i.e. C++ upcast doesn't modify the pointer) - let untyped = unsafe { Gd::::from_obj_sys(ptr).ready() }; - untyped.owned_cast::() - } - } - - /// Looks up the given instance ID and returns the associated object. - /// - /// # Panics - /// If no such instance ID is registered, or if the dynamic type of the object behind that instance ID - /// is not compatible with `T`. - #[cfg(feature = "convenience")] - pub fn from_instance_id(instance_id: InstanceId) -> Self { - Self::try_from_instance_id(instance_id).unwrap_or_else(|| { - panic!( - "Instance ID {} does not belong to a valid object of class '{}'", - instance_id, - T::CLASS_NAME - ) - }) - } - - fn from_opaque(opaque: OpaqueObject) -> Self { - Self { - opaque, - _marker: PhantomData, - } - } - - /// Returns the instance ID of this object, or `None` if the object is dead. - /// - pub fn instance_id_or_none(&self) -> Option { - // Note: bit 'id & (1 << 63)' determines if the instance is ref-counted - let id = unsafe { interface_fn!(object_get_instance_id)(self.obj_sys()) }; - InstanceId::try_from_u64(id) - } - - /// Returns the instance ID of this object (panics when dead). - /// - /// # Panics - /// If this object is no longer alive (registered in Godot's object database). - #[cfg(feature = "convenience")] - pub fn instance_id(&self) -> InstanceId { - self.instance_id_or_none().unwrap_or_else(|| { - panic!( - "failed to call instance_id() on destroyed object; \ - use instance_id_or_none() or keep your objects alive" - ) - }) - } - - /// Checks if this smart pointer points to a live object (read description!). - /// - /// Using this method is often indicative of bad design -- you should dispose of your pointers once an object is - /// destroyed. However, this method exists because GDScript offers it and there may be **rare** use cases. - /// - /// Do not use this method to check if you can safely access an object. Accessing dead objects is generally safe - /// and will panic in a defined manner. Encountering such panics is almost always a bug you should fix, and not a - /// runtime condition to check against. - pub fn is_instance_valid(&self) -> bool { - // TODO Is this really necessary, or is Godot's instance_id() guaranteed to return 0 for destroyed objects? - if let Some(id) = self.instance_id_or_none() { - api::utilities::is_instance_id_valid(id.to_i64()) - } else { - false - } - } - - /// Needed to initialize ref count -- must be explicitly invoked. - /// - /// Could be made part of FFI methods, but there are some edge cases where this is not intended. - pub(crate) fn ready(self) -> Self { - T::Mem::maybe_inc_ref(&self); - self - } - - /// **Upcast:** convert into a smart pointer to a base class. Always succeeds. - /// - /// Moves out of this value. If you want to create _another_ smart pointer instance, - /// use this idiom: - /// ```ignore - /// let obj: Gd = ...; - /// let base = obj.share().upcast::(); - /// ``` - pub fn upcast(self) -> Gd - where - Base: GodotClass, - T: Inherits, - { - self.owned_cast() - .expect("Upcast failed. This is a bug; please report it.") - } - - /// **Downcast:** try to convert into a smart pointer to a derived class. - /// - /// If `T`'s dynamic type is not `Derived` or one of its subclasses, `None` is returned - /// and the reference is dropped. Otherwise, `Some` is returned and the ownership is moved - /// to the returned value. - // TODO consider Result, Self> so that user can still use original object (e.g. to free if manual) - pub fn try_cast(self) -> Option> - where - Derived: GodotClass + Inherits, - { - self.owned_cast() - } - - /// **Downcast:** convert into a smart pointer to a derived class. Panics on error. - /// - /// # Panics - /// If the class' dynamic type is not `Derived` or one of its subclasses. Use [`Self::try_cast()`] if you want to check the result. - #[cfg(feature = "convenience")] - pub fn cast(self) -> Gd - where - Derived: GodotClass + Inherits, - { - self.owned_cast().unwrap_or_else(|| { - panic!( - "downcast from {from} to {to} failed; correct the code or use try_cast()", - from = T::CLASS_NAME, - to = Derived::CLASS_NAME - ) - }) - } - - fn owned_cast(self) -> Option> - where - U: GodotClass, - { - // The unsafe { std::mem::transmute<&T, &Base>(self.inner()) } relies on the C++ static_cast class casts - // to return the same pointer, however in theory those may yield a different pointer (VTable offset, - // virtual inheritance etc.). It *seems* to work so far, but this is no indication it's not UB. - // - // The Deref/DerefMut impls for T implement an "implicit upcast" on the object (not Gd) level and - // rely on this (e.g. &Node3D -> &Node). - - let result = unsafe { self.ffi_cast::() }; - if result.is_some() { - // duplicated ref, one must be wiped - std::mem::forget(self); - } - - result - } - - // Note: does not transfer ownership and is thus unsafe. Also operates on shared ref. - // Either the parameter or the return value *must* be forgotten (since reference counts are not updated). - unsafe fn ffi_cast(&self) -> Option> - where - U: GodotClass, - { - let class_name = ClassName::new::(); - let class_tag = interface_fn!(classdb_get_class_tag)(class_name.c_str()); - let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); - - if cast_object_ptr.is_null() { - None - } else { - Some(Gd::from_obj_sys(cast_object_ptr)) - } - } - - pub(crate) fn as_ref_counted(&self, apply: impl Fn(&mut api::RefCounted) -> R) -> R { - debug_assert!( - self.is_instance_valid(), - "as_ref_counted() on freed instance; maybe forgot to increment reference count?" - ); - - let tmp = unsafe { self.ffi_cast::() }; - let mut tmp = tmp.expect("object expected to inherit RefCounted"); - let return_val = - ::Declarer::scoped_mut(&mut tmp, |obj| apply(obj)); - - std::mem::forget(tmp); // no ownership transfer - return_val - } - - // pub(crate) fn as_object(&self, apply: impl Fn(&mut api::Object) -> R) -> R { - // let tmp = unsafe { self.ffi_cast::() }; - // let mut tmp = tmp.expect("object expected to inherit Object; should never fail"); - // let return_val = apply(tmp.inner_mut()); - // std::mem::forget(tmp); // no ownership transfer - // return_val - // } - - // Conversions from/to Godot C++ `Object*` pointers - ffi_methods! { - type sys::GDNativeObjectPtr = Opaque; - - fn from_obj_sys = from_sys; - fn from_obj_sys_init = from_sys_init; - fn obj_sys = sys; - fn write_obj_sys = write_sys; - } -} - -/// The methods in this impl block are only available for objects `T` that are manually managed, -/// i.e. anything that is not `RefCounted` or inherited from it. -impl Gd -where - T: GodotClass, - M: mem::PossiblyManual + mem::Memory, -{ - /// Destroy the manually-managed Godot object. - /// - /// Consumes this smart pointer and renders all other `Gd` smart pointers (as well as any GDScript references) to the same object - /// immediately invalid. Using those `Gd` instances will lead to panics, but not undefined behavior. - /// - /// This operation is **safe** and effectively prevents double-free. - /// - /// # Panics - /// When the referred-to object has already been destroyed, or when this is invoked on an upcast `Gd` - /// that dynamically points to a reference-counted type. - pub fn free(self) { - // Runtime check in case of T=Object, no-op otherwise - let ref_counted = T::Mem::is_ref_counted(&self); - assert_ne!( - ref_counted, Some(true), - "called free() on Gd which points to a RefCounted dynamic type; free() only supported for manually managed types." - ); - - // If ref_counted returned None, that means the instance was destroyed - assert!( - ref_counted == Some(false) && self.is_instance_valid(), - "called free() on already destroyed object" - ); - - // This destroys the Storage instance, no need to run destructor again - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - - std::mem::forget(self); - } -} - -impl Deref for Gd -where - T: GodotClass, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - // SAFETY: - // This relies on Gd having the layout as Node3D (as an example), - // which also needs #[repr(transparent)]: - // - // struct Gd { - // opaque: OpaqueObject, <- size of GDNativeObjectPtr - // _marker: PhantomData, <- ZST - // } - // struct Node3D { - // object_ptr: sys::GDNativeObjectPtr, - // } - unsafe { std::mem::transmute::<&OpaqueObject, &T>(&self.opaque) } - } -} - -impl DerefMut for Gd -where - T: GodotClass, -{ - fn deref_mut(&mut self) -> &mut T { - // SAFETY: see Deref - unsafe { std::mem::transmute::<&mut OpaqueObject, &mut T>(&mut self.opaque) } - } -} - -impl GodotFfi for Gd { - ffi_methods! { type sys::GDNativeTypePtr = Opaque; .. } -} - -/// Destructor with semantics depending on memory strategy. -/// -/// * If this `Gd` smart pointer holds a reference-counted type, this will decrement the reference counter. -/// If this was the last remaining reference, dropping it will invoke `T`'s destructor. -/// -/// * If the held object is manually-managed, **nothing happens**. -/// To destroy manually-managed `Gd` pointers, you need to call [`Self::free()`]. -impl Drop for Gd { - fn drop(&mut self) { - // No-op for manually managed objects - - out!("Gd::drop <{}>", std::any::type_name::()); - let is_last = T::Mem::maybe_dec_ref(&self); // may drop - if is_last { - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - } - - /*let st = self.storage(); - out!(" objd; self={:?}, val={:?}", st as *mut _, st.lifecycle); - //out!(" objd2; self={:?}, val={:?}", st as *mut _, st.lifecycle); - - // If destruction is triggered by Godot, Storage already knows about it, no need to notify it - if !self.storage().destroyed_by_godot() { - let is_last = T::Mem::maybe_dec_ref(&self); // may drop - if is_last { - //T::Declarer::destroy(self); - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - } - }*/ - } -} - -impl Share for Gd { - fn share(&self) -> Self { - out!("Gd::share"); - Self::from_opaque(self.opaque).ready() - } -} - -// ---------------------------------------------------------------------------------------------------------------------------------------------- -// Trait impls - -impl FromVariant for Gd { - fn try_from_variant(variant: &Variant) -> Result { - let result = unsafe { - let result = Self::from_sys_init(|self_ptr| { - let converter = sys::method_table().object_from_variant; - converter(self_ptr, variant.var_sys()); - }); - result.ready() - }; - - Ok(result) - } -} - -impl ToVariant for Gd { - fn to_variant(&self) -> Variant { - let variant = unsafe { - Variant::from_var_sys_init(|variant_ptr| { - let converter = sys::method_table().object_to_variant; - - // Note: this is a special case because of an inconsistency in Godot, where sometimes the equivalency is - // GDNativeTypePtr == Object** and sometimes GDNativeTypePtr == Object*. Here, it is the former, thus extra pointer. - // Reported at https://github.com/godotengine/godot/issues/61967 - let type_ptr = self.sys(); - converter(variant_ptr, ptr::addr_of!(type_ptr) as *mut _); - }) - }; - - variant - } -} - -impl std::fmt::Debug for Gd { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // If you change this, don't forget to update Base::fmt() - if let Some(id) = self.instance_id_or_none() { - write!(f, "Gd{{ id: {} }}", id) - } else { - write!(f, "Gd{{ freed object }}") - } - } -} - -impl PropertyInfoBuilder for Gd { - fn variant_type() -> sys::GDNativeVariantType { - gdext_sys::GDNativeVariantType_GDNATIVE_VARIANT_TYPE_OBJECT - } - - fn property_info(name: &str) -> sys::GDNativePropertyInfo { - // Note: filling this information properly is important so that Godot can use ptrcalls instead of varcalls - // (requires typed GDScript + sufficient information from the extension side) - let reg = unsafe { sys::get_registry() }; - - let property_name = reg.c_string(name); - let class_name = reg.c_string(T::CLASS_NAME); - - sys::GDNativePropertyInfo { - type_: Self::variant_type(), - name: property_name, - class_name, - hint: 0, - hint_string: ptr::null_mut(), - usage: 7, // Default, TODO generate global enums - } - } -} diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index 62247d9cf..afa483236 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -115,7 +115,6 @@ fn make_sys_code(central_items: &CentralItems) -> String { } impl GlobalMethodTable { - #[cfg(not(test))] pub(crate) unsafe fn new(interface: &crate::GDNativeInterface) -> Self { Self { #(#variant_fn_inits)* diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 369e80a93..91caef82c 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -13,6 +13,7 @@ trace = [] convenience = [] codegen-fmt = ["godot-ffi/codegen-fmt"] minimal = ["godot-ffi/minimal"] +unit-test = ["godot-ffi/unit-test", "godot-codegen/codegen-disabled"] # If this crate is built for a downstream unit test [dependencies] godot-ffi = { path = "../godot-ffi" } @@ -24,7 +25,7 @@ glam = { version = "0.22", features = ["debug-glam-assert", "scalar-math"] } # Reverse dev dependencies so doctests can use `godot::` prefix [dev-dependencies] godot = { path = "../godot" } -godot-ffi = { path = "../godot-ffi", features = ["codegen-disabled"] } # unit-test +godot-ffi = { path = "../godot-ffi", features = ["unit-test"] } # unit-test [build-dependencies] godot-codegen = { path = "../godot-codegen" } diff --git a/godot-core/build.rs b/godot-core/build.rs index 55854aed8..7700917a0 100644 --- a/godot-core/build.rs +++ b/godot-core/build.rs @@ -13,5 +13,6 @@ fn main() { std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); } + #[cfg(not(feature = "unit-test"))] godot_codegen::generate_core_files(gen_path); } diff --git a/godot-core/src/builtin/arrays.rs b/godot-core/src/builtin/arrays.rs index 00720a600..cef0695ed 100644 --- a/godot-core/src/builtin/arrays.rs +++ b/godot-core/src/builtin/arrays.rs @@ -73,7 +73,7 @@ impl Clone for TypedArray { fn clone(&self) -> Self { unsafe { Self::from_sys_init(|opaque_ptr| { - let ctor = sys::method_table().array_construct_copy; + let ctor = sys::builtin_fn!(array_construct_copy); ctor(opaque_ptr, &self.sys() as *const sys::GDNativeTypePtr); }) } @@ -85,7 +85,7 @@ impl GodotFfi for TypedArray { impl Drop for TypedArray { fn drop(&mut self) { unsafe { - let destructor = sys::method_table().array_destroy; + let destructor = sys::builtin_fn!(array_destroy @1); destructor(self.sys_mut()); } } diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index e7d9791d8..de13b9249 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -13,7 +13,7 @@ macro_rules! impl_builtin_traits_inner { fn default() -> Self { unsafe { let mut gd_val = sys::$GdType::default(); - (sys::method_table().$gd_method)(&mut gd_val); + ::godot_ffi::builtin_fn!($gd_method)(&mut gd_val); <$Type>::from_sys(gd_val) } } @@ -26,7 +26,7 @@ macro_rules! impl_builtin_traits_inner { fn clone(&self) -> Self { unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().$gd_method; + let ctor = ::godot_ffi::builtin_fn!($gd_method); let args = [self.sys()]; ctor(self_ptr, args.as_ptr()); }) @@ -40,7 +40,7 @@ macro_rules! impl_builtin_traits_inner { #[inline] fn drop(&mut self) { unsafe { - let destructor = sys::method_table().$gd_method; + let destructor = ::godot_ffi::builtin_fn!($gd_method @1); destructor(self.sys_mut()); } } @@ -52,10 +52,10 @@ macro_rules! impl_builtin_traits_inner { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { - let operator = godot_ffi::method_table().$gd_method; - - let mut result: bool = false; - operator(self.sys(), other.sys(), result.sys_mut()); + let mut result = false; + ::godot_ffi::builtin_call! { + $gd_method(self.sys(), other.sys(), result.sys_mut()) + }; result } } @@ -72,10 +72,10 @@ macro_rules! impl_builtin_traits_inner { #[inline] fn partial_cmp(&self, other: &Self) -> Option { let op_less = |lhs, rhs| unsafe { - let operator = godot_ffi::method_table().$gd_method; - - let mut result: bool = false; - operator(lhs, rhs, result.sys_mut()); + let mut result = false; + ::godot_ffi::builtin_call! { + $gd_method(lhs, rhs, result.sys_mut()) + }; result }; @@ -141,8 +141,10 @@ macro_rules! impl_builtin_froms { fn from(other: &$From) -> Self { unsafe { Self::from_sys_init(|ptr| { - let converter = sys::method_table().$from_fn; - converter(ptr, [other.sys()].as_ptr()); + let args = [other.sys()]; + ::godot_ffi::builtin_call! { + $from_fn(ptr, args.as_ptr()) + } }) } } diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index 9f8c83c4a..ec5432323 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -19,7 +19,14 @@ mod vector2; mod vector3; mod vector4; +// #[cfg(not(feature = "unit-test"))] pub mod meta; +// +// #[cfg(feature = "unit-test")] +// pub mod meta { +// mod class_name; +// pub use class_name::*; +// } pub use arrays::*; pub use color::*; diff --git a/godot-core/src/builtin/node_path.rs b/godot-core/src/builtin/node_path.rs index 64cfaca31..9a5f9f1a8 100644 --- a/godot-core/src/builtin/node_path.rs +++ b/godot-core/src/builtin/node_path.rs @@ -27,7 +27,7 @@ impl From<&GodotString> for NodePath { fn from(path: &GodotString) -> Self { unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().node_path_from_string; + let ctor = sys::builtin_fn!(node_path_from_string); let args = [path.sys()]; ctor(self_ptr, args.as_ptr()); }) @@ -39,7 +39,7 @@ impl From<&NodePath> for GodotString { fn from(path: &NodePath) -> Self { unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().string_from_node_path; + let ctor = sys::builtin_fn!(string_from_node_path); let args = [path.sys()]; ctor(self_ptr, args.as_ptr()); }) diff --git a/godot-core/src/builtin/others.rs b/godot-core/src/builtin/others.rs index e1cca4196..caf6b8163 100644 --- a/godot-core/src/builtin/others.rs +++ b/godot-core/src/builtin/others.rs @@ -33,6 +33,7 @@ struct InnerRect { size: Vector2, } +#[cfg(not(feature = "unit-test"))] impl Rect2 { pub fn size(self) -> Vector2 { self.inner().size @@ -53,7 +54,7 @@ impl Callable { let method = method.into(); unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().callable_from_object_method; + let ctor = sys::builtin_fn!(callable_from_object_method); let args = [object.sys(), method.sys()]; ctor(self_ptr, args.as_ptr()); }) diff --git a/godot-core/src/builtin/string.rs b/godot-core/src/builtin/string.rs index cc9f4b43b..9a3ea48a0 100644 --- a/godot-core/src/builtin/string.rs +++ b/godot-core/src/builtin/string.rs @@ -61,8 +61,9 @@ impl Default for GodotString { unsafe { let self_ptr = (*uninit.as_mut_ptr()).sys_mut(); - let ctor = sys::method_table().string_construct_default; - ctor(self_ptr, std::ptr::null_mut()); + let ctor = sys::builtin_call! { + string_construct_default(self_ptr, std::ptr::null_mut()) + }; uninit.assume_init() } diff --git a/godot-core/src/builtin/string_name.rs b/godot-core/src/builtin/string_name.rs index b9107b7c5..02859a9f1 100644 --- a/godot-core/src/builtin/string_name.rs +++ b/godot-core/src/builtin/string_name.rs @@ -67,8 +67,9 @@ impl Default for StringName { unsafe { let self_ptr = (*uninit.as_mut_ptr()).sys_mut(); - let ctor = sys::method_table().string_name_construct_default; - ctor(self_ptr, std::ptr::null_mut()); + sys::builtin_call! { + string_name_construct_default(self_ptr, std::ptr::null_mut()) + }; uninit.assume_init() } @@ -106,7 +107,7 @@ impl From<&GodotString> for StringName { fn from(s: &GodotString) -> Self { unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().string_name_from_string; + let ctor = sys::builtin_fn!(string_name_from_string); let args = [s.sys()]; ctor(self_ptr, args.as_ptr()); }) @@ -125,7 +126,7 @@ impl From<&StringName> for GodotString { fn from(s: &StringName) -> Self { unsafe { Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().string_from_string_name; + let ctor = sys::builtin_fn!(string_from_string_name); let args = [s.sys()]; ctor(self_ptr, args.as_ptr()); }) diff --git a/godot-core/src/builtin/variant/impls.rs b/godot-core/src/builtin/variant/impls.rs index 43a737cc4..54618babf 100644 --- a/godot-core/src/builtin/variant/impls.rs +++ b/godot-core/src/builtin/variant/impls.rs @@ -7,6 +7,7 @@ use super::*; use crate::builtin::meta::VariantMetadata; use crate::builtin::*; +use crate::obj::EngineEnum; use godot_ffi as sys; use sys::GodotFfi; @@ -31,7 +32,7 @@ macro_rules! impl_variant_traits { fn to_variant(&self) -> Variant { let variant = unsafe { Variant::from_var_sys_init(|variant_ptr| { - let converter = sys::method_table().$from_fn; + let converter = sys::builtin_fn!($from_fn); converter(variant_ptr, self.sys()); }) }; @@ -50,7 +51,7 @@ macro_rules! impl_variant_traits { let mut value = <$T>::default(); let result = unsafe { - let converter = sys::method_table().$to_fn; + let converter = sys::builtin_fn!($to_fn); converter(value.sys_mut(), variant.var_sys()); value }; @@ -189,3 +190,16 @@ impl VariantMetadata for Variant { VariantType::Nil // FIXME is this correct? what else to use? is this called at all? } } + +impl ToVariant for T { + fn to_variant(&self) -> Variant { + ::to_variant(&self.ord()) + } +} + +impl FromVariant for T { + fn try_from_variant(variant: &Variant) -> Result { + ::try_from_variant(variant) + .and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError)) + } +} diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index a70199c30..1e52955c9 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -15,11 +15,8 @@ mod impls; mod variant_traits; pub use impls::*; -pub use variant_traits::*; - pub use sys::{VariantOperator, VariantType}; - -// pub use crate::gen::central::*; +pub use variant_traits::*; #[repr(C, align(8))] pub struct Variant { @@ -191,7 +188,7 @@ impl PartialEq for Variant { // impl Eq for Variant {} // impl PartialEq for Variant { // fn eq(&self, other: &Self) -> bool { -// unsafe { sys::method_table().ope } +// unsafe { builtin_fn!(ope) } // } // } diff --git a/godot-core/src/builtin/variant/variant_traits.rs b/godot-core/src/builtin/variant/variant_traits.rs index a208e257f..8ead0287f 100644 --- a/godot-core/src/builtin/variant/variant_traits.rs +++ b/godot-core/src/builtin/variant/variant_traits.rs @@ -5,7 +5,6 @@ */ use crate::builtin::Variant; -use crate::obj::EngineEnum; pub trait FromVariant: Sized { fn try_from_variant(variant: &Variant) -> Result; @@ -50,18 +49,3 @@ pub struct VariantConversionError; /// Variant value cannot be represented in target type BadValue, }*/ - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - -impl ToVariant for T { - fn to_variant(&self) -> Variant { - ::to_variant(&self.ord()) - } -} - -impl FromVariant for T { - fn try_from_variant(variant: &Variant) -> Result { - ::try_from_variant(variant) - .and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError)) - } -} diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index 8b31db0e9..4a6e8c318 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -8,12 +8,13 @@ // Re-exports of generated symbols use crate::builtin::{GodotString, NodePath}; -use crate::engine::resource_loader::CacheMode; +use crate::obj::dom::EngineDomain; +use crate::obj::{Gd, GodotClass, Inherits}; +use resource_loader::CacheMode; + pub use crate::gen::central::global; pub use crate::gen::classes::*; pub use crate::gen::utilities; -use crate::obj::dom::EngineDomain; -use crate::obj::{Gd, GodotClass, Inherits}; /// Extension trait with convenience functions for the node tree. pub trait NodeExt { @@ -140,6 +141,32 @@ where load_impl(&path.into()) } +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Utilities for crate + +pub(crate) fn debug_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, + ty: &str, +) -> std::fmt::Result { + if let Some(id) = ptr.instance_id_or_none() { + let class: GodotString = ptr.as_object(|obj| Object::get_class(obj)); + + write!(f, "{ty} {{ id: {id}, class: {class} }}") + } else { + write!(f, "{ty} {{ freed obj }}") + } +} + +pub(crate) fn display_string(ptr: &Gd, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let string: GodotString = ptr.as_object(|obj| Object::to_string(obj)); + + ::fmt(&string, f) +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Implementation of this file + // Separate function, to avoid constructing string twice // Note that more optimizations than that likely make no sense, as loading is quite expensive fn load_impl(path: &GodotString) -> Option> diff --git a/godot-core/src/init/mod.rs b/godot-core/src/init/mod.rs index 82ba8dbfc..10ab81b7a 100644 --- a/godot-core/src/init/mod.rs +++ b/godot-core/src/init/mod.rs @@ -7,6 +7,16 @@ use godot_ffi as sys; use std::collections::btree_map::BTreeMap; +#[cfg(feature = "unit-test")] +pub fn __gdext_load_library( + interface: *const sys::GDNativeInterface, + library: sys::GDNativeExtensionClassLibraryPtr, + init: *mut sys::GDNativeInitialization, +) -> sys::GDNativeBool { + sys::panic_no_godot!(__gdext_load_library) +} + +#[cfg(not(feature = "unit-test"))] #[doc(hidden)] pub fn __gdext_load_library( interface: *const sys::GDNativeInterface, diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index dbe84d74c..319970772 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -10,6 +10,7 @@ mod storage; pub mod bind; pub mod builder; pub mod builtin; +#[cfg(not(feature = "unit-test"))] pub mod engine; pub mod init; pub mod log; @@ -21,6 +22,7 @@ pub use registry::*; pub use godot_ffi as sys; // Output of generated code. Mimics the file structure, symbols are re-exported. +#[cfg(not(feature = "unit-test"))] #[allow(unused_imports, dead_code, non_upper_case_globals, non_snake_case)] mod gen { pub mod central; @@ -28,6 +30,120 @@ mod gen { pub mod utilities; } +#[cfg(feature = "unit-test")] +mod gen { + pub mod central { + pub mod global {} + } + pub mod classes { + pub struct Node {} + pub struct Resource {} + + pub mod class_macros {} + } + pub mod utilities {} +} + +#[cfg(feature = "unit-test")] +pub mod engine { + use super::sys; + use crate::obj::{Gd, GodotClass}; + + pub struct Object {} + pub struct RefCounted {} + impl RefCounted { + pub fn init_ref(&self) -> bool { + sys::panic_no_godot!(RefCounted::init_ref) + } + pub fn reference(&self) -> bool { + sys::panic_no_godot!(RefCounted::reference) + } + pub fn unreference(&self) -> bool { + sys::panic_no_godot!(RefCounted::unreference) + } + } + + impl GodotClass for Object { + type Base = (); + type Declarer = crate::obj::dom::EngineDomain; + type Mem = crate::obj::mem::DynamicRefCount; + const CLASS_NAME: &'static str = ""; + } + impl GodotClass for RefCounted { + type Base = Object; + type Declarer = crate::obj::dom::EngineDomain; + type Mem = crate::obj::mem::StaticRefCount; + const CLASS_NAME: &'static str = ""; + } + + pub mod utilities { + use super::sys; + + pub fn is_instance_id_valid(id: i64) -> bool { + sys::panic_no_godot!(is_instance_id_valid) + } + } + + #[allow(non_camel_case_types)] + pub mod global { + use super::sys; + + #[derive(Debug)] + pub enum PropertyHint { + PROPERTY_HINT_NONE, + } + impl PropertyHint { + pub fn ord(&self) -> i32 { + sys::panic_no_godot!(PropertyHint::ord) + } + } + + #[derive(Debug)] + pub enum PropertyUsageFlags { + PROPERTY_USAGE_DEFAULT, + } + impl PropertyUsageFlags { + pub fn ord(&self) -> i32 { + sys::panic_no_godot!(PropertyUsageFlags::ord) + } + } + } + + pub(crate) fn debug_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, + ty: &str, + ) -> std::fmt::Result { + sys::panic_no_godot!(Debug) + } + + pub(crate) fn display_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + sys::panic_no_godot!(Display) + } +} + +pub mod callbacks { + use super::sys; + use crate::obj::{Base, GodotClass}; + + pub unsafe extern "C" fn create( + _class_userdata: *mut std::ffi::c_void, + ) -> sys::GDNativeObjectPtr { + sys::panic_no_godot!(create) + } + + pub(crate) fn create_custom(_make_user_instance: F) -> sys::GDNativeObjectPtr + where + T: GodotClass, + F: FnOnce(Base) -> T, + { + sys::panic_no_godot!(create_custom) + } +} + #[doc(hidden)] pub mod private { // If someone forgets #[godot_api], this causes a compile error, rather than virtual functions not being called at runtime. diff --git a/godot-core/src/obj/base.rs b/godot-core/src/obj/base.rs index 306076371..3cbf3c910 100644 --- a/godot-core/src/obj/base.rs +++ b/godot-core/src/obj/base.rs @@ -4,7 +4,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::GodotString; use crate::obj::Gd; use crate::obj::GodotClass; use crate::{engine, sys}; @@ -59,21 +58,13 @@ impl Base { impl Debug for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(id) = self.instance_id_or_none() { - let class: GodotString = self.as_object(|obj| engine::Object::get_class(obj)); - - write!(f, "Base {{ id: {}, class: {} }}", id, class) - } else { - write!(f, "Base {{ freed obj }}") - } + engine::debug_string(&self.obj, f, "Base") } } impl Display for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let string: GodotString = self.as_object(|obj| engine::Object::to_string(obj)); - - ::fmt(&string, f) + engine::display_string(&self.obj, f) } } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index fa7b7293e..114b74924 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -15,7 +15,6 @@ use sys::types::OpaqueObject; use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi}; use crate::builtin::meta::{ClassName, PropertyInfo, VariantMetadata}; -use crate::builtin::GodotString; use crate::builtin::{FromVariant, StringName, ToVariant, Variant, VariantConversionError}; use crate::obj::dom::Domain as _; use crate::obj::mem::Memory as _; @@ -538,7 +537,7 @@ impl FromVariant for Gd { fn try_from_variant(variant: &Variant) -> Result { let result = unsafe { let result = Self::from_sys_init(|self_ptr| { - let converter = sys::method_table().object_from_variant; + let converter = sys::builtin_fn!(object_from_variant); converter(self_ptr, variant.var_sys()); }); result.ready() @@ -552,13 +551,13 @@ impl ToVariant for Gd { fn to_variant(&self) -> Variant { let variant = unsafe { Variant::from_var_sys_init(|variant_ptr| { - let converter = sys::method_table().object_to_variant; + let converter = sys::builtin_fn!(object_to_variant); // Note: this is a special case because of an inconsistency in Godot, where sometimes the equivalency is // GDNativeTypePtr == Object** and sometimes GDNativeTypePtr == Object*. Here, it is the former, thus extra pointer. // Reported at https://github.com/godotengine/godot/issues/61967 let type_ptr = self.sys(); - converter(variant_ptr, ptr::addr_of!(type_ptr) as *mut _); + converter(variant_ptr, ptr::addr_of!(type_ptr) as sys::GDNativeTypePtr); }) }; @@ -575,22 +574,13 @@ where // and thus implement it for each class separately (or blanket GodotClass/EngineClass/...). fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let string: GodotString = self.as_object(|obj| engine::Object::to_string(obj)); - - ::fmt(&string, f) + engine::display_string(self, f) } } impl Debug for Gd { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - // If you change this, don't forget to update Base::fmt() - if let Some(id) = self.instance_id_or_none() { - let class: GodotString = self.as_object(|obj| engine::Object::get_class(obj)); - - write!(f, "Gd {{ id: {id}, class: {class} }}") - } else { - write!(f, "Gd {{ freed obj }}") - } + engine::debug_string(self, f, "Gd") } } diff --git a/godot-ffi/Cargo.toml b/godot-ffi/Cargo.toml index bf2ba4a82..039794557 100644 --- a/godot-ffi/Cargo.toml +++ b/godot-ffi/Cargo.toml @@ -9,7 +9,7 @@ rust-version = "1.63" [features] codegen-fmt = ["godot-codegen/codegen-fmt"] minimal = ["godot-codegen/minimal"] -codegen-disabled = ["godot-codegen/codegen-disabled"] +unit-test = ["godot-codegen/codegen-disabled"] # If this crate is built for a downstream unit test [dependencies] paste = "1" diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index e1f7fa3f6..10b1cf000 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -7,9 +7,6 @@ use std::env; use std::path::Path; -#[cfg(not(test))] -use godot_codegen as gen; - fn main() { // For custom path on macOS, iOS, Android etc: see gdnative-sys/build.rs let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/")); @@ -19,7 +16,9 @@ fn main() { } run_bindgen(&gen_path.join("gdnative_interface.rs")); - gen::generate_sys_files(gen_path); + + #[cfg(not(feature = "unit-test"))] + godot_codegen::generate_sys_files(gen_path); } fn run_bindgen(out_file: &Path) { diff --git a/godot-ffi/src/gen_central_stub.rs b/godot-ffi/src/gen_central_stub.rs new file mode 100644 index 000000000..05f57e9e0 --- /dev/null +++ b/godot-ffi/src/gen_central_stub.rs @@ -0,0 +1,222 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// Note: this code is only used during unit tests, and may be out of sync with the engine's values. +// The concrete values matter much less than having a structure at all, to avoid thousands of upstream +// conditional compilation differentiations. + +use crate::{ffi_methods, GDNativeTypePtr, GDNativeVariantPtr, GodotFfi}; +pub mod types { + pub type OpaqueNil = crate::opaque::Opaque<0usize>; + pub type OpaqueBool = crate::opaque::Opaque<1usize>; + pub type OpaqueInt = crate::opaque::Opaque<8usize>; + pub type OpaqueFloat = crate::opaque::Opaque<8usize>; + pub type OpaqueString = crate::opaque::Opaque<8usize>; + pub type OpaqueVector2 = crate::opaque::Opaque<8usize>; + pub type OpaqueVector2i = crate::opaque::Opaque<8usize>; + pub type OpaqueRect2 = crate::opaque::Opaque<16usize>; + pub type OpaqueRect2i = crate::opaque::Opaque<16usize>; + pub type OpaqueVector3 = crate::opaque::Opaque<12usize>; + pub type OpaqueVector3i = crate::opaque::Opaque<12usize>; + pub type OpaqueTransform2D = crate::opaque::Opaque<24usize>; + pub type OpaqueVector4 = crate::opaque::Opaque<16usize>; + pub type OpaqueVector4i = crate::opaque::Opaque<16usize>; + pub type OpaquePlane = crate::opaque::Opaque<16usize>; + pub type OpaqueQuaternion = crate::opaque::Opaque<16usize>; + pub type OpaqueAABB = crate::opaque::Opaque<24usize>; + pub type OpaqueBasis = crate::opaque::Opaque<36usize>; + pub type OpaqueTransform3D = crate::opaque::Opaque<48usize>; + pub type OpaqueProjection = crate::opaque::Opaque<64usize>; + pub type OpaqueColor = crate::opaque::Opaque<16usize>; + pub type OpaqueStringName = crate::opaque::Opaque<8usize>; + pub type OpaqueNodePath = crate::opaque::Opaque<8usize>; + pub type OpaqueRID = crate::opaque::Opaque<8usize>; + pub type OpaqueObject = crate::opaque::Opaque<8usize>; + pub type OpaqueCallable = crate::opaque::Opaque<16usize>; + pub type OpaqueSignal = crate::opaque::Opaque<16usize>; + pub type OpaqueDictionary = crate::opaque::Opaque<8usize>; + pub type OpaqueArray = crate::opaque::Opaque<8usize>; + pub type OpaquePackedByteArray = crate::opaque::Opaque<16usize>; + pub type OpaquePackedInt32Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedInt64Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedFloat32Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedFloat64Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedStringArray = crate::opaque::Opaque<16usize>; + pub type OpaquePackedVector2Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedVector3Array = crate::opaque::Opaque<16usize>; + pub type OpaquePackedColorArray = crate::opaque::Opaque<16usize>; + pub type OpaqueVariant = crate::opaque::Opaque<24usize>; +} +pub struct GlobalMethodTable {} +impl GlobalMethodTable { + // pub(crate) unsafe fn new(interface: &crate::GDNativeInterface) -> Self { + // Self {} + // } +} +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +#[repr(i32)] +pub enum VariantType { + Nil = 0, + Bool = 1, + Int = 2, + Float = 3, + String = 4, + Vector2 = 5, + Vector2i = 6, + Rect2 = 7, + Rect2i = 8, + Vector3 = 9, + Vector3i = 10, + Transform2D = 11, + Vector4 = 12, + Vector4i = 13, + Plane = 14, + Quaternion = 15, + AABB = 16, + Basis = 17, + Transform3D = 18, + Projection = 19, + Color = 20, + StringName = 21, + NodePath = 22, + RID = 23, + Object = 24, + Callable = 25, + Signal = 26, + Dictionary = 27, + Array = 28, + PackedByteArray = 29, + PackedInt32Array = 30, + PackedInt64Array = 31, + PackedFloat32Array = 32, + PackedFloat64Array = 33, + PackedStringArray = 34, + PackedVector2Array = 35, + PackedVector3Array = 36, + PackedColorArray = 37, +} +impl VariantType { + #[doc(hidden)] + pub fn from_sys(enumerator: crate::GDNativeVariantType) -> Self { + match enumerator { + 0 => Self::Nil, + 1 => Self::Bool, + 2 => Self::Int, + 3 => Self::Float, + 4 => Self::String, + 5 => Self::Vector2, + 6 => Self::Vector2i, + 7 => Self::Rect2, + 8 => Self::Rect2i, + 9 => Self::Vector3, + 10 => Self::Vector3i, + 11 => Self::Transform2D, + 12 => Self::Vector4, + 13 => Self::Vector4i, + 14 => Self::Plane, + 15 => Self::Quaternion, + 16 => Self::AABB, + 17 => Self::Basis, + 18 => Self::Transform3D, + 19 => Self::Projection, + 20 => Self::Color, + 21 => Self::StringName, + 22 => Self::NodePath, + 23 => Self::RID, + 24 => Self::Object, + 25 => Self::Callable, + 26 => Self::Signal, + 27 => Self::Dictionary, + 28 => Self::Array, + 29 => Self::PackedByteArray, + 30 => Self::PackedInt32Array, + 31 => Self::PackedInt64Array, + 32 => Self::PackedFloat32Array, + 33 => Self::PackedFloat64Array, + 34 => Self::PackedStringArray, + 35 => Self::PackedVector2Array, + 36 => Self::PackedVector3Array, + 37 => Self::PackedColorArray, + _ => unreachable!("invalid variant type {}", enumerator), + } + } + #[doc(hidden)] + pub fn sys(self) -> crate::GDNativeVariantType { + self as _ + } +} +impl GodotFfi for VariantType { + ffi_methods! { type GDNativeTypePtr = * mut Self ; .. } +} +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +#[repr(i32)] +pub enum VariantOperator { + Equal = 0, + NotEqual = 1, + Less = 2, + LessEqual = 3, + Greater = 4, + GreaterEqual = 5, + Add = 6, + Subtract = 7, + Multiply = 8, + Divide = 9, + Negate = 10, + Positive = 11, + Module = 12, + Power = 13, + ShiftLeft = 14, + ShiftRight = 15, + BitAnd = 16, + BitOr = 17, + BitXor = 18, + BitNegate = 19, + And = 20, + Or = 21, + Xor = 22, + Not = 23, + In = 24, +} +impl VariantOperator { + #[doc(hidden)] + pub fn from_sys(enumerator: crate::GDNativeVariantOperator) -> Self { + match enumerator { + 0 => Self::Equal, + 1 => Self::NotEqual, + 2 => Self::Less, + 3 => Self::LessEqual, + 4 => Self::Greater, + 5 => Self::GreaterEqual, + 6 => Self::Add, + 7 => Self::Subtract, + 8 => Self::Multiply, + 9 => Self::Divide, + 10 => Self::Negate, + 11 => Self::Positive, + 12 => Self::Module, + 13 => Self::Power, + 14 => Self::ShiftLeft, + 15 => Self::ShiftRight, + 16 => Self::BitAnd, + 17 => Self::BitOr, + 18 => Self::BitXor, + 19 => Self::BitNegate, + 20 => Self::And, + 21 => Self::Or, + 22 => Self::Xor, + 23 => Self::Not, + 24 => Self::In, + _ => unreachable!("invalid variant operator {}", enumerator), + } + } + #[doc(hidden)] + pub fn sys(self) -> crate::GDNativeVariantOperator { + self as _ + } +} +impl GodotFfi for VariantOperator { + ffi_methods! { type GDNativeTypePtr = * mut Self ; .. } +} diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 4b81d3537..da94445ba 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -16,8 +16,15 @@ deref_nullptr, clippy::redundant_static_lifetimes )] + pub(crate) mod gen { + #[cfg(not(feature = "unit-test"))] + pub mod central; + + #[cfg(feature = "unit-test")] + #[path = "../gen_central_stub.rs"] pub mod central; + pub mod gdnative_interface; } @@ -31,25 +38,26 @@ mod plugins; #[doc(hidden)] pub use paste; -pub use gen::gdnative_interface::*; -pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; // needs `crate::` +pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; -#[cfg(not(test))] +pub use gen::central::*; +pub use gen::gdnative_interface::*; // needs `crate::` + +#[cfg(not(feature = "unit-test"))] #[doc(inline)] pub use real_impl::*; -#[cfg(test)] +#[cfg(feature = "unit-test")] #[doc(inline)] pub use test_impl::*; // ---------------------------------------------------------------------------------------------------------------------------------------------- // Real implementation, when Godot engine is running -#[cfg(not(test))] +#[cfg(not(feature = "unit-test"))] mod real_impl { - pub use super::gen::central::*; - use super::gen::gdnative_interface::*; use super::global_registry::GlobalRegistry; + use super::*; struct GodotBinding { interface: GDNativeInterface, @@ -141,6 +149,7 @@ mod real_impl { } } + #[doc(hidden)] pub fn default_call_error() -> GDNativeCallError { GDNativeCallError { error: GDNATIVE_CALL_OK, @@ -148,12 +157,28 @@ mod real_impl { expected: -1, } } + + #[macro_export] + #[doc(hidden)] + macro_rules! builtin_fn { + ($name:ident $(, @1)?) => { + $crate::method_table().$name + }; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! builtin_call { + ($name:ident ( $($args:expr),* $(,)? )) => { + ($crate::method_table().$name)( $($args),* ) + }; + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- // Stubs when in unit-test (without Godot) -#[cfg(test)] +#[cfg(feature = "unit-test")] mod test_impl { use super::gen::gdnative_interface::*; use super::global_registry::GlobalRegistry; @@ -162,22 +187,64 @@ mod test_impl { #[inline(always)] pub unsafe fn get_interface() -> &'static GDNativeInterface { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") + crate::panic_no_godot!(get_interface) } #[inline(always)] pub unsafe fn get_library() -> GDNativeExtensionClassLibraryPtr { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") + crate::panic_no_godot!(get_library) } #[inline(always)] pub unsafe fn method_table() -> &'static GlobalMethodTable { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") + crate::panic_no_godot!(method_table) } #[inline(always)] pub unsafe fn get_registry() -> &'static mut GlobalRegistry { - unimplemented!("Not available in unit-tests; needs Godot engine to run.") + crate::panic_no_godot!(get_registry) + } + + #[macro_export] + #[doc(hidden)] + macro_rules! builtin_fn { + // Don't use ! because of warnings + ($name:ident) => {{ + #[allow(unreachable_code)] + fn panic2(t: T, u: U) -> () { + panic!("builtin_fn! unavailable in unit-tests; needs Godot engine"); + () + } + panic2 + }}; + ($name:ident @1) => {{ + #[allow(unreachable_code)] + fn panic1(t: T) -> () { + panic!("builtin_fn! unavailable in unit-tests; needs Godot engine"); + () + } + panic1 + }}; + } + + // Possibly interesting: https://stackoverflow.com/a/40234666 + #[macro_export] + #[doc(hidden)] + macro_rules! panic_no_godot { + ($symbol:expr) => { + panic!(concat!( + stringify!($symbol), + " unavailable in unit-tests; needs Godot engine" + )) + }; + } + + #[macro_export] + #[doc(hidden)] + macro_rules! builtin_call { + ($name:ident ( $($args:expr),* $(,)? )) => { + $crate::panic_no_godot!(builtin_call) + }; } } diff --git a/godot-macros/Cargo.toml b/godot-macros/Cargo.toml index 7e13f01ee..b46220766 100644 --- a/godot-macros/Cargo.toml +++ b/godot-macros/Cargo.toml @@ -8,7 +8,7 @@ rust-version = "1.63" proc-macro = true [dev-dependencies] -godot = { path = "../godot" } # doctest +godot = { path = "../godot" } #, features = ["unit-test"] } # doctest [dependencies] quote = "1" diff --git a/godot/Cargo.toml b/godot/Cargo.toml index b4222026b..6a0b0cff0 100644 --- a/godot/Cargo.toml +++ b/godot/Cargo.toml @@ -15,3 +15,5 @@ minimal = ["godot-core/minimal"] godot-core = { path = "../godot-core" } godot-macros = { path = "../godot-macros" } +[dev-dependencies] +godot-core = { path = "../godot-core", features = ["unit-test"] } \ No newline at end of file diff --git a/godot/src/lib.rs b/godot/src/lib.rs index 194f9aebd..a78377d55 100644 --- a/godot/src/lib.rs +++ b/godot/src/lib.rs @@ -36,15 +36,15 @@ pub use godot_core::private; pub mod prelude { pub use super::bind::{godot_api, GodotClass, GodotExt}; pub use super::builtin::*; - pub use super::engine::{ - load, try_load, utilities, AudioStreamPlayer, Camera2D, Camera3D, Input, Node, Node2D, - Node3D, Object, PackedScene, RefCounted, Resource, SceneTree, - }; + // pub use super::engine::{ + // load, try_load, utilities, AudioStreamPlayer, Camera2D, Camera3D, Input, Node, Node2D, + // Node3D, Object, PackedScene, RefCounted, Resource, SceneTree, + // }; pub use super::init::{gdextension, ExtensionLayer, ExtensionLibrary, InitHandle, InitLevel}; pub use super::log::*; pub use super::obj::{Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, Share}; // Make trait methods available - pub use super::engine::NodeExt as _; + // pub use super::engine::NodeExt as _; pub use super::obj::EngineEnum as _; } From 698e21bb65be5b51f7420555cef289cd2016df5f Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 5 Dec 2022 21:41:29 +0100 Subject: [PATCH 07/16] Conditional compilation + test rabbit hole Move conditionally-compiled symbols from unit test into own file. Also disable recursive dev-dependency, which causes following dev-dependency cycle during cargo test. Note that #[cfg(test)] is not carried along dependencies. godot-core [dev] -> godot [non-dev] -> godot-core [non-dev] So, we get a Heisenberg situation where both #[cfg(test)] and #[cfg(not(test))] are simultaneously true. This also happens without any dependencies, just thanks to doctest enabling #[cfg(doctest)] but not #[cfg(test)]. The following code prints *both* errors! #[cfg(test)] compile_error!("test"); #[cfg(not(test))] compile_error!("not(test)"); See comments in godot-core/lib.rs for more horror stories. --- .github/workflows/minimal-ci.yml | 4 +- godot-core/Cargo.toml | 2 +- godot-core/src/builtin/mod.rs | 7 -- godot-core/src/init/mod.rs | 4 +- godot-core/src/lib.rs | 139 +++++------------------------- godot-core/src/test_stubs.rs | 124 ++++++++++++++++++++++++++ godot-ffi/src/gen_central_stub.rs | 14 +-- 7 files changed, 159 insertions(+), 135 deletions(-) create mode 100644 godot-core/src/test_stubs.rs diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index a516eda9e..422423578 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -95,10 +95,10 @@ jobs: if: matrix.name == 'macos' - name: "Compile tests" - run: cargo test --workspace --features ${GDEXT_FEATURES} --no-run + run: cargo test --workspace --features unit-test,${GDEXT_FEATURES} --no-run - name: "Test" - run: cargo test --workspace --features ${GDEXT_FEATURES} ${{ matrix.testflags }} + run: cargo test --workspace --features unit-test,${GDEXT_FEATURES} ${{ matrix.testflags }} integration-test-godot: diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 91caef82c..7aa715aab 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -24,7 +24,7 @@ glam = { version = "0.22", features = ["debug-glam-assert", "scalar-math"] } # Reverse dev dependencies so doctests can use `godot::` prefix [dev-dependencies] -godot = { path = "../godot" } +#godot = { path = "../godot" } # when re-enabling, add unit-test feature to `godot`, or it will mess things up godot-ffi = { path = "../godot-ffi", features = ["unit-test"] } # unit-test [build-dependencies] diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index ec5432323..9f8c83c4a 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -19,14 +19,7 @@ mod vector2; mod vector3; mod vector4; -// #[cfg(not(feature = "unit-test"))] pub mod meta; -// -// #[cfg(feature = "unit-test")] -// pub mod meta { -// mod class_name; -// pub use class_name::*; -// } pub use arrays::*; pub use color::*; diff --git a/godot-core/src/init/mod.rs b/godot-core/src/init/mod.rs index 10ab81b7a..546589d8f 100644 --- a/godot-core/src/init/mod.rs +++ b/godot-core/src/init/mod.rs @@ -7,7 +7,7 @@ use godot_ffi as sys; use std::collections::btree_map::BTreeMap; -#[cfg(feature = "unit-test")] +#[cfg(any(test, feature = "unit-test"))] pub fn __gdext_load_library( interface: *const sys::GDNativeInterface, library: sys::GDNativeExtensionClassLibraryPtr, @@ -16,7 +16,7 @@ pub fn __gdext_load_library( sys::panic_no_godot!(__gdext_load_library) } -#[cfg(not(feature = "unit-test"))] +#[cfg(not(any(test, feature = "unit-test")))] #[doc(hidden)] pub fn __gdext_load_library( interface: *const sys::GDNativeInterface, diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index 319970772..863ec14a0 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -4,22 +4,38 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +// If running in tests, a lot of symbols are unused or panic early +#![cfg_attr(feature = "unit-test", allow(unreachable_code, unused))] + +// More test hacks... +// +// Technically, `cargo test -p godot-core` *could* be supported by this abomination: +// #[cfg(not(any(test, doctest, feature = "unit-test"))] +// which would be necessary because `cargo test` runs both test/doctest, and downstream crates may need the feature as +// workaround https://github.com/rust-lang/rust/issues/59168#issuecomment-962214945. However, this *also* does not work, +// as #[cfg(doctest)] is currently near-useless for conditional compilation: https://github.com/rust-lang/rust/issues/67295. +// As if this weren't enough, even our compile error here does not appear as the first error message, but the 4th or so. +#[cfg(all(test, not(feature = "unit-test")))] +compile_error!("Running `cargo test` requires `--features unit-test`."); + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + mod registry; mod storage; pub mod bind; pub mod builder; pub mod builtin; -#[cfg(not(feature = "unit-test"))] -pub mod engine; pub mod init; pub mod log; pub mod macros; pub mod obj; +pub use godot_ffi as sys; pub use registry::*; -pub use godot_ffi as sys; +#[cfg(not(feature = "unit-test"))] +pub mod engine; // Output of generated code. Mimics the file structure, symbols are re-exported. #[cfg(not(feature = "unit-test"))] @@ -30,119 +46,10 @@ mod gen { pub mod utilities; } -#[cfg(feature = "unit-test")] -mod gen { - pub mod central { - pub mod global {} - } - pub mod classes { - pub struct Node {} - pub struct Resource {} - - pub mod class_macros {} - } - pub mod utilities {} -} - -#[cfg(feature = "unit-test")] -pub mod engine { - use super::sys; - use crate::obj::{Gd, GodotClass}; - - pub struct Object {} - pub struct RefCounted {} - impl RefCounted { - pub fn init_ref(&self) -> bool { - sys::panic_no_godot!(RefCounted::init_ref) - } - pub fn reference(&self) -> bool { - sys::panic_no_godot!(RefCounted::reference) - } - pub fn unreference(&self) -> bool { - sys::panic_no_godot!(RefCounted::unreference) - } - } - - impl GodotClass for Object { - type Base = (); - type Declarer = crate::obj::dom::EngineDomain; - type Mem = crate::obj::mem::DynamicRefCount; - const CLASS_NAME: &'static str = ""; - } - impl GodotClass for RefCounted { - type Base = Object; - type Declarer = crate::obj::dom::EngineDomain; - type Mem = crate::obj::mem::StaticRefCount; - const CLASS_NAME: &'static str = ""; - } - - pub mod utilities { - use super::sys; - - pub fn is_instance_id_valid(id: i64) -> bool { - sys::panic_no_godot!(is_instance_id_valid) - } - } - - #[allow(non_camel_case_types)] - pub mod global { - use super::sys; - - #[derive(Debug)] - pub enum PropertyHint { - PROPERTY_HINT_NONE, - } - impl PropertyHint { - pub fn ord(&self) -> i32 { - sys::panic_no_godot!(PropertyHint::ord) - } - } - - #[derive(Debug)] - pub enum PropertyUsageFlags { - PROPERTY_USAGE_DEFAULT, - } - impl PropertyUsageFlags { - pub fn ord(&self) -> i32 { - sys::panic_no_godot!(PropertyUsageFlags::ord) - } - } - } - - pub(crate) fn debug_string( - ptr: &Gd, - f: &mut std::fmt::Formatter<'_>, - ty: &str, - ) -> std::fmt::Result { - sys::panic_no_godot!(Debug) - } - - pub(crate) fn display_string( - ptr: &Gd, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - sys::panic_no_godot!(Display) - } -} - -pub mod callbacks { - use super::sys; - use crate::obj::{Base, GodotClass}; - - pub unsafe extern "C" fn create( - _class_userdata: *mut std::ffi::c_void, - ) -> sys::GDNativeObjectPtr { - sys::panic_no_godot!(create) - } - - pub(crate) fn create_custom(_make_user_instance: F) -> sys::GDNativeObjectPtr - where - T: GodotClass, - F: FnOnce(Base) -> T, - { - sys::panic_no_godot!(create_custom) - } -} +#[cfg(any(feature = "unit-test"))] +mod test_stubs; +#[cfg(any(feature = "unit-test"))] +use test_stubs::*; #[doc(hidden)] pub mod private { diff --git a/godot-core/src/test_stubs.rs b/godot-core/src/test_stubs.rs new file mode 100644 index 000000000..34d8631f7 --- /dev/null +++ b/godot-core/src/test_stubs.rs @@ -0,0 +1,124 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use super::sys; + +pub mod gen { + pub mod central { + pub mod global {} + } + pub mod classes { + pub struct Node {} + pub struct Resource {} + + pub mod class_macros {} + } + pub mod utilities {} +} + +/*pub mod callbacks { + use super::sys; + use crate::obj::{Base, GodotClass}; + + pub unsafe extern "C" fn create( + _class_userdata: *mut std::ffi::c_void, + ) -> sys::GDNativeObjectPtr { + sys::panic_no_godot!(create) + } + + pub(crate) fn create_custom(_make_user_instance: F) -> sys::GDNativeObjectPtr + where + T: GodotClass, + F: FnOnce(Base) -> T, + { + sys::panic_no_godot!(create_custom) + } +}*/ + +pub mod engine { + use super::sys; + use crate::obj::{Gd, GodotClass}; + + pub struct Object {} + + pub struct RefCounted {} + + impl RefCounted { + pub fn init_ref(&self) -> bool { + sys::panic_no_godot!(RefCounted::init_ref) + } + pub fn reference(&self) -> bool { + sys::panic_no_godot!(RefCounted::reference) + } + pub fn unreference(&self) -> bool { + sys::panic_no_godot!(RefCounted::unreference) + } + } + + impl GodotClass for Object { + type Base = (); + type Declarer = crate::obj::dom::EngineDomain; + type Mem = crate::obj::mem::DynamicRefCount; + const CLASS_NAME: &'static str = ""; + } + + impl GodotClass for RefCounted { + type Base = Object; + type Declarer = crate::obj::dom::EngineDomain; + type Mem = crate::obj::mem::StaticRefCount; + const CLASS_NAME: &'static str = ""; + } + + pub mod utilities { + use super::sys; + + pub fn is_instance_id_valid(id: i64) -> bool { + sys::panic_no_godot!(is_instance_id_valid) + } + } + + #[allow(non_camel_case_types)] + pub mod global { + use super::sys; + + #[derive(Debug)] + pub enum PropertyHint { + PROPERTY_HINT_NONE, + } + + impl PropertyHint { + pub fn ord(&self) -> i32 { + sys::panic_no_godot!(PropertyHint::ord) + } + } + + #[derive(Debug)] + pub enum PropertyUsageFlags { + PROPERTY_USAGE_DEFAULT, + } + + impl PropertyUsageFlags { + pub fn ord(&self) -> i32 { + sys::panic_no_godot!(PropertyUsageFlags::ord) + } + } + } + + pub(crate) fn debug_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, + ty: &str, + ) -> std::fmt::Result { + sys::panic_no_godot!(Debug) + } + + pub(crate) fn display_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + sys::panic_no_godot!(Display) + } +} diff --git a/godot-ffi/src/gen_central_stub.rs b/godot-ffi/src/gen_central_stub.rs index 05f57e9e0..82612c986 100644 --- a/godot-ffi/src/gen_central_stub.rs +++ b/godot-ffi/src/gen_central_stub.rs @@ -8,7 +8,7 @@ // The concrete values matter much less than having a structure at all, to avoid thousands of upstream // conditional compilation differentiations. -use crate::{ffi_methods, GDNativeTypePtr, GDNativeVariantPtr, GodotFfi}; +use crate::{ffi_methods, GDNativeTypePtr, /*GDNativeVariantPtr,*/ GodotFfi}; pub mod types { pub type OpaqueNil = crate::opaque::Opaque<0usize>; pub type OpaqueBool = crate::opaque::Opaque<1usize>; @@ -50,12 +50,12 @@ pub mod types { pub type OpaquePackedColorArray = crate::opaque::Opaque<16usize>; pub type OpaqueVariant = crate::opaque::Opaque<24usize>; } -pub struct GlobalMethodTable {} -impl GlobalMethodTable { - // pub(crate) unsafe fn new(interface: &crate::GDNativeInterface) -> Self { - // Self {} - // } -} +// pub struct GlobalMethodTable {} +// impl GlobalMethodTable { +// pub(crate) unsafe fn new(interface: &crate::GDNativeInterface) -> Self { +// Self {} +// } +// } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[repr(i32)] pub enum VariantType { From bdb2bc7ad48939402f5e8b264683df6bb55b76dc Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 00:12:50 +0100 Subject: [PATCH 08/16] godot-core now depends on godot-codegen with `stubs_only` parameter --- .github/workflows/minimal-ci.yml | 5 +-- godot-codegen/src/central_generator.rs | 43 +++++++++++++++++++++++--- godot-codegen/src/lib.rs | 18 ++++++----- godot-core/Cargo.toml | 4 +-- godot-core/build.rs | 6 ++-- godot-core/src/builtin/string.rs | 2 +- godot-core/src/engine.rs | 7 +++-- godot-core/src/lib.rs | 18 +++++------ godot-core/src/test_stubs.rs | 13 -------- godot/Cargo.toml | 5 +-- godot/src/lib.rs | 12 ++++--- itest/rust/src/lib.rs | 3 ++ 12 files changed, 84 insertions(+), 52 deletions(-) diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 422423578..1731264c4 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -26,6 +26,7 @@ on: env: GDEXT_FEATURES: 'godot-core/convenience' + GDEXT_CRATE_ARGS: '-p godot-codegen -p godot-ffi -p godot-core -p godot-macros -p godot' defaults: run: @@ -95,10 +96,10 @@ jobs: if: matrix.name == 'macos' - name: "Compile tests" - run: cargo test --workspace --features unit-test,${GDEXT_FEATURES} --no-run + run: cargo test $GDEXT_CRATE_ARGS --features unit-test,$GDEXT_FEATURES --no-run - name: "Test" - run: cargo test --workspace --features unit-test,${GDEXT_FEATURES} ${{ matrix.testflags }} + run: cargo test $GDEXT_CRATE_ARGS --features unit-test,$GDEXT_FEATURES ${{ matrix.testflags }} integration-test-godot: diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index afa483236..d2c4ee821 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -61,7 +61,37 @@ pub(crate) fn generate_sys_central_file( let central_items = make_central_items(api, build_config, ctx); let sys_code = make_sys_code(¢ral_items); - write_files(sys_gen_path, sys_code, out_files); + write_file(sys_gen_path, "central.rs", sys_code, out_files); +} + +pub(crate) fn generate_core_mod_file( + core_gen_path: &Path, + out_files: &mut Vec, + stubs_only: bool, +) { + // When invoked by another crate during unit-test (not integration test), don't run generator + let code = if stubs_only { + quote! { + pub mod central { + pub mod global {} + } + pub mod classes { + pub struct Node {} + pub struct Resource {} + + pub mod class_macros {} + } + pub mod utilities {} + } + } else { + quote! { + pub mod central; + pub mod classes; + pub mod utilities; + } + }; + + write_file(core_gen_path, "mod.rs", code.to_string(), out_files); } pub(crate) fn generate_core_central_file( @@ -74,12 +104,17 @@ pub(crate) fn generate_core_central_file( let central_items = make_central_items(api, build_config, ctx); let core_code = make_core_code(¢ral_items); - write_files(core_gen_path, core_code, out_files); + write_file(core_gen_path, "central.rs", core_code, out_files); } -fn write_files(gen_path: &Path, code: String, out_files: &mut Vec) { +pub(crate) fn write_file( + gen_path: &Path, + filename: &str, + code: String, + out_files: &mut Vec, +) { let _ = std::fs::create_dir_all(gen_path); - let out_path = gen_path.join("central.rs"); + let out_path = gen_path.join(filename); std::fs::write(&out_path, code).unwrap_or_else(|e| { panic!( diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index 9ad6599e2..cface43ba 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -19,7 +19,9 @@ mod watch; mod tests; use api_parser::{load_extension_api, ExtensionApi}; -use central_generator::{generate_core_central_file, generate_sys_central_file}; +use central_generator::{ + generate_core_central_file, generate_core_mod_file, generate_sys_central_file, +}; use class_generator::generate_class_files; use context::Context; use util::ident; @@ -52,16 +54,16 @@ pub fn generate_sys_files(sys_gen_path: &Path) { watch.write_stats_to(&sys_gen_path.join("codegen-stats.txt")); } -pub fn generate_core_files(core_gen_path: &Path) { - // When invoked by another crate during unit-test (not integration test), don't run generator - // cfg! is easier to handle than #[cfg] regarding all the imports, and neither code size nor performance matter in unit-test - if cfg!(feature = "codegen-disabled") { - return; - } - +pub fn generate_core_files(core_gen_path: &Path, stubs_only: bool) { let mut out_files = vec![]; let mut watch = StopWatch::start(); + generate_core_mod_file(core_gen_path, &mut out_files, stubs_only); + if stubs_only { + rustfmt_if_needed(out_files); + return; + } + let (api, build_config) = load_extension_api(&mut watch); let mut ctx = Context::build_from_api(&api); watch.record("build_context"); diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 7aa715aab..34194ba19 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -13,7 +13,7 @@ trace = [] convenience = [] codegen-fmt = ["godot-ffi/codegen-fmt"] minimal = ["godot-ffi/minimal"] -unit-test = ["godot-ffi/unit-test", "godot-codegen/codegen-disabled"] # If this crate is built for a downstream unit test +unit-test = ["godot-ffi/unit-test"] # If this crate is built for a downstream unit test [dependencies] godot-ffi = { path = "../godot-ffi" } @@ -25,7 +25,7 @@ glam = { version = "0.22", features = ["debug-glam-assert", "scalar-math"] } # Reverse dev dependencies so doctests can use `godot::` prefix [dev-dependencies] #godot = { path = "../godot" } # when re-enabling, add unit-test feature to `godot`, or it will mess things up -godot-ffi = { path = "../godot-ffi", features = ["unit-test"] } # unit-test +#godot-ffi = { path = "../godot-ffi", features = ["unit-test"] } # unit-test [build-dependencies] godot-codegen = { path = "../godot-codegen" } diff --git a/godot-core/build.rs b/godot-core/build.rs index 7700917a0..3660c0a14 100644 --- a/godot-core/build.rs +++ b/godot-core/build.rs @@ -13,6 +13,8 @@ fn main() { std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); } - #[cfg(not(feature = "unit-test"))] - godot_codegen::generate_core_files(gen_path); + // Note: cannot use cfg!(test) because that isn't recognizable from build files. + // See https://github.com/rust-lang/cargo/issues/1581, which was closed without a solution. + let stubs_only = cfg!(feature = "unit-test"); + godot_codegen::generate_core_files(gen_path, stubs_only); } diff --git a/godot-core/src/builtin/string.rs b/godot-core/src/builtin/string.rs index 9a3ea48a0..7ae4459b7 100644 --- a/godot-core/src/builtin/string.rs +++ b/godot-core/src/builtin/string.rs @@ -61,7 +61,7 @@ impl Default for GodotString { unsafe { let self_ptr = (*uninit.as_mut_ptr()).sys_mut(); - let ctor = sys::builtin_call! { + sys::builtin_call! { string_construct_default(self_ptr, std::ptr::null_mut()) }; diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index 4a6e8c318..5fdd36853 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -144,7 +144,7 @@ where // ---------------------------------------------------------------------------------------------------------------------------------------------- // Utilities for crate -pub(crate) fn debug_string( +pub(crate) fn debug_string( ptr: &Gd, f: &mut std::fmt::Formatter<'_>, ty: &str, @@ -158,7 +158,10 @@ pub(crate) fn debug_string( } } -pub(crate) fn display_string(ptr: &Gd, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +pub(crate) fn display_string( + ptr: &Gd, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { let string: GodotString = ptr.as_object(|obj| Object::to_string(obj)); ::fmt(&string, f) diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index 863ec14a0..6610f0357 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -14,7 +14,7 @@ // which would be necessary because `cargo test` runs both test/doctest, and downstream crates may need the feature as // workaround https://github.com/rust-lang/rust/issues/59168#issuecomment-962214945. However, this *also* does not work, // as #[cfg(doctest)] is currently near-useless for conditional compilation: https://github.com/rust-lang/rust/issues/67295. -// As if this weren't enough, even our compile error here does not appear as the first error message, but the 4th or so. +// Yet even then, our compile error here is only one of many, as the compiler tries to build doctest without hitting this. #[cfg(all(test, not(feature = "unit-test")))] compile_error!("Running `cargo test` requires `--features unit-test`."); @@ -34,22 +34,18 @@ pub mod obj; pub use godot_ffi as sys; pub use registry::*; -#[cfg(not(feature = "unit-test"))] +#[cfg(not(any(test, feature = "unit-test")))] pub mod engine; // Output of generated code. Mimics the file structure, symbols are re-exported. -#[cfg(not(feature = "unit-test"))] +#[rustfmt::skip] #[allow(unused_imports, dead_code, non_upper_case_globals, non_snake_case)] -mod gen { - pub mod central; - pub mod classes; - pub mod utilities; -} +mod gen; -#[cfg(any(feature = "unit-test"))] +#[cfg(feature = "unit-test")] mod test_stubs; -#[cfg(any(feature = "unit-test"))] -use test_stubs::*; +#[cfg(feature = "unit-test")] +pub use test_stubs::*; #[doc(hidden)] pub mod private { diff --git a/godot-core/src/test_stubs.rs b/godot-core/src/test_stubs.rs index 34d8631f7..0a71b5fa1 100644 --- a/godot-core/src/test_stubs.rs +++ b/godot-core/src/test_stubs.rs @@ -6,19 +6,6 @@ use super::sys; -pub mod gen { - pub mod central { - pub mod global {} - } - pub mod classes { - pub struct Node {} - pub struct Resource {} - - pub mod class_macros {} - } - pub mod utilities {} -} - /*pub mod callbacks { use super::sys; use crate::obj::{Base, GodotClass}; diff --git a/godot/Cargo.toml b/godot/Cargo.toml index 6a0b0cff0..37c0d0e28 100644 --- a/godot/Cargo.toml +++ b/godot/Cargo.toml @@ -10,10 +10,11 @@ trace = ["godot-core/trace"] convenience = [] codegen-fmt = ["godot-core/codegen-fmt"] minimal = ["godot-core/minimal"] +unit-test = [] [dependencies] godot-core = { path = "../godot-core" } godot-macros = { path = "../godot-macros" } -[dev-dependencies] -godot-core = { path = "../godot-core", features = ["unit-test"] } \ No newline at end of file +#[dev-dependencies] +#godot-core = { path = "../godot-core", features = ["unit-test"] } \ No newline at end of file diff --git a/godot/src/lib.rs b/godot/src/lib.rs index a78377d55..9b58a9b69 100644 --- a/godot/src/lib.rs +++ b/godot/src/lib.rs @@ -36,15 +36,17 @@ pub use godot_core::private; pub mod prelude { pub use super::bind::{godot_api, GodotClass, GodotExt}; pub use super::builtin::*; - // pub use super::engine::{ - // load, try_load, utilities, AudioStreamPlayer, Camera2D, Camera3D, Input, Node, Node2D, - // Node3D, Object, PackedScene, RefCounted, Resource, SceneTree, - // }; + #[cfg(not(feature = "unit-test"))] + pub use super::engine::{ + load, try_load, utilities, AudioStreamPlayer, Camera2D, Camera3D, Input, Node, Node2D, + Node3D, Object, PackedScene, RefCounted, Resource, SceneTree, + }; pub use super::init::{gdextension, ExtensionLayer, ExtensionLibrary, InitHandle, InitLevel}; pub use super::log::*; pub use super::obj::{Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, Share}; // Make trait methods available - // pub use super::engine::NodeExt as _; + #[cfg(not(feature = "unit-test"))] + pub use super::engine::NodeExt as _; pub use super::obj::EngineEnum as _; } diff --git a/itest/rust/src/lib.rs b/itest/rust/src/lib.rs index af5be2576..7642b2d29 100644 --- a/itest/rust/src/lib.rs +++ b/itest/rust/src/lib.rs @@ -4,6 +4,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#[cfg(test)] +compile_error!("`cargo test` not supported for integration test -- use `cargo run`."); + use godot::bind::{godot_api, GodotClass}; use godot::init::{gdextension, ExtensionLibrary}; use godot::test::itest; From d5eed2af718661d7e103d56d8f7e9c187ea80b14 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 00:32:30 +0100 Subject: [PATCH 09/16] godot-ffi now also depends on godot-codegen with `stubs_only` parameter --- godot-codegen/Cargo.toml | 1 - godot-codegen/src/central_generator.rs | 22 ++++++++++++++++++++++ godot-codegen/src/lib.rs | 15 ++++++++------- godot-ffi/Cargo.toml | 2 +- godot-ffi/build.rs | 4 ++-- godot-ffi/src/lib.rs | 17 +++++------------ itest/rust/src/gdscript_ffi_test.rs | 1 + 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/godot-codegen/Cargo.toml b/godot-codegen/Cargo.toml index ba172ec03..5327ab6f6 100644 --- a/godot-codegen/Cargo.toml +++ b/godot-codegen/Cargo.toml @@ -6,7 +6,6 @@ rust-version = "1.63" # Note: features not additive, but this is an internal crate [features] -codegen-disabled = [] # if active, nothing is generated (for unit tests) codegen-fmt = [] minimal = ["codegen-fmt"] diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index d2c4ee821..0c26b6cbd 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -64,6 +64,28 @@ pub(crate) fn generate_sys_central_file( write_file(sys_gen_path, "central.rs", sys_code, out_files); } +pub(crate) fn generate_sys_mod_file( + core_gen_path: &Path, + out_files: &mut Vec, + stubs_only: bool, +) { + // When invoked by another crate during unit-test (not integration test), don't run generator + let code = if stubs_only { + quote! { + #[path = "../gen_central_stub.rs"] + pub mod central; + pub mod gdnative_interface; + } + } else { + quote! { + pub mod central; + pub mod gdnative_interface; + } + }; + + write_file(core_gen_path, "mod.rs", code.to_string(), out_files); +} + pub(crate) fn generate_core_mod_file( core_gen_path: &Path, out_files: &mut Vec, diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index cface43ba..8622a2e94 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -21,6 +21,7 @@ mod tests; use api_parser::{load_extension_api, ExtensionApi}; use central_generator::{ generate_core_central_file, generate_core_mod_file, generate_sys_central_file, + generate_sys_mod_file, }; use class_generator::generate_class_files; use context::Context; @@ -32,16 +33,16 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use std::path::{Path, PathBuf}; -pub fn generate_sys_files(sys_gen_path: &Path) { - // When invoked by another crate during unit-test (not integration test), don't run generator - // cfg! is easier to handle than #[cfg] regarding all the imports, and neither code size nor performance matter in unit-test - if cfg!(feature = "codegen-disabled") { - return; - } - +pub fn generate_sys_files(sys_gen_path: &Path, stubs_only: bool) { let mut out_files = vec![]; let mut watch = StopWatch::start(); + generate_sys_mod_file(sys_gen_path, &mut out_files, stubs_only); + if stubs_only { + rustfmt_if_needed(out_files); + return; + } + let (api, build_config) = load_extension_api(&mut watch); let mut ctx = Context::build_from_api(&api); watch.record("build_context"); diff --git a/godot-ffi/Cargo.toml b/godot-ffi/Cargo.toml index 039794557..a37f2c670 100644 --- a/godot-ffi/Cargo.toml +++ b/godot-ffi/Cargo.toml @@ -9,7 +9,7 @@ rust-version = "1.63" [features] codegen-fmt = ["godot-codegen/codegen-fmt"] minimal = ["godot-codegen/minimal"] -unit-test = ["godot-codegen/codegen-disabled"] # If this crate is built for a downstream unit test +unit-test = [] # If this crate is built for a downstream unit test [dependencies] paste = "1" diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index 10b1cf000..858bc77ba 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -17,8 +17,8 @@ fn main() { run_bindgen(&gen_path.join("gdnative_interface.rs")); - #[cfg(not(feature = "unit-test"))] - godot_codegen::generate_sys_files(gen_path); + let stubs_only = cfg!(feature = "unit-test"); + godot_codegen::generate_sys_files(gen_path, stubs_only); } fn run_bindgen(out_file: &Path) { diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index da94445ba..052b67921 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -9,6 +9,9 @@ #![cfg_attr(test, allow(unused))] // Output of generated code. Mimics the file structure, symbols are re-exported. +// Note: accessing `gen` *may* still work without explicitly specifying `unit-test` feature, +// but stubs are generated for consistency with how godot-core depends on godot-codegen. +#[rustfmt::skip] #[allow( non_camel_case_types, non_upper_case_globals, @@ -16,17 +19,7 @@ deref_nullptr, clippy::redundant_static_lifetimes )] - -pub(crate) mod gen { - #[cfg(not(feature = "unit-test"))] - pub mod central; - - #[cfg(feature = "unit-test")] - #[path = "../gen_central_stub.rs"] - pub mod central; - - pub mod gdnative_interface; -} +pub(crate) mod gen; mod global_registry; mod godot_ffi; @@ -161,7 +154,7 @@ mod real_impl { #[macro_export] #[doc(hidden)] macro_rules! builtin_fn { - ($name:ident $(, @1)?) => { + ($name:ident $(@1)?) => { $crate::method_table().$name }; } diff --git a/itest/rust/src/gdscript_ffi_test.rs b/itest/rust/src/gdscript_ffi_test.rs index f7ff1ee26..5961b7dc2 100644 --- a/itest/rust/src/gdscript_ffi_test.rs +++ b/itest/rust/src/gdscript_ffi_test.rs @@ -6,6 +6,7 @@ #![allow(dead_code)] +#[rustfmt::skip] #[path = "gen/gen_ffi.rs"] mod gen_ffi; From f415fe70556fbde0259ab093e6e404cc8248e153 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 10:49:00 +0100 Subject: [PATCH 10/16] Escape local variables in generated methods, to avoid naming collisions with parameters --- godot-codegen/src/class_generator.rs | 90 ++++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/godot-codegen/src/class_generator.rs b/godot-codegen/src/class_generator.rs index d031fc726..e040eac2c 100644 --- a/godot-codegen/src/class_generator.rs +++ b/godot-codegen/src/class_generator.rs @@ -69,9 +69,9 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_str: &Literal) -> T quote! { pub fn singleton() -> Gd { unsafe { - let class_name = StringName::from(#class_name_str); - let object_ptr = sys::interface_fn!(global_get_singleton)(class_name.string_sys()); - Gd::from_obj_sys(object_ptr) + let __class_name = StringName::from(#class_name_str); + let __object_ptr = sys::interface_fn!(global_get_singleton)(__class_name.string_sys()); + Gd::from_obj_sys(__object_ptr) } } } @@ -83,10 +83,10 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_str: &Literal) -> T quote! { pub fn new() -> Gd { unsafe { - let class_name = StringName::from(#class_name_str); - let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys()); + let __class_name = StringName::from(#class_name_str); + let __object_ptr = sys::interface_fn!(classdb_construct_object)(__class_name.string_sys()); //let instance = Self { object_ptr }; - Gd::from_obj_sys(object_ptr) + Gd::from_obj_sys(__object_ptr) } } } @@ -96,9 +96,9 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_str: &Literal) -> T #[must_use] pub fn new_alloc() -> Gd { unsafe { - let class_name = StringName::from(#class_name_str); - let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys()); - Gd::from_obj_sys(object_ptr) + let __class_name = StringName::from(#class_name_str); + let __object_ptr = sys::interface_fn!(classdb_construct_object)(__class_name.string_sys()); + Gd::from_obj_sys(__object_ptr) } } } @@ -392,23 +392,23 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) quote! { #vis fn #method_name( #receiver #(, #params )*, varargs: &[Variant]) #return_decl { unsafe { - let class_name = StringName::from(#class_name); - let method_name = StringName::from(#method_name_str); - let method_bind = sys::interface_fn!(classdb_get_method_bind)( - class_name.string_sys(), - method_name.string_sys(), + let __class_name = StringName::from(#class_name); + let __method_name = StringName::from(#method_name_str); + let __method_bind = sys::interface_fn!(classdb_get_method_bind)( + __class_name.string_sys(), + __method_name.string_sys(), #hash ); - let call_fn = sys::interface_fn!(object_method_bind_call); + let __call_fn = sys::interface_fn!(object_method_bind_call); - let explicit_args = [ + let __explicit_args = [ #( #arg_exprs ),* ]; - let mut args = Vec::new(); - args.extend(explicit_args.iter().map(Variant::var_sys)); - args.extend(varargs.iter().map(Variant::var_sys)); + let mut __args = Vec::new(); + __args.extend(__explicit_args.iter().map(Variant::var_sys)); + __args.extend(varargs.iter().map(Variant::var_sys)); - let args_ptr = args.as_ptr(); + let __args_ptr = __args.as_ptr(); #call } @@ -419,19 +419,19 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) quote! { #vis fn #method_name( #receiver, #( #params ),* ) #return_decl { unsafe { - let class_name = StringName::from(#class_name); - let method_name = StringName::from(#method_name_str); - let method_bind = sys::interface_fn!(classdb_get_method_bind)( - class_name.string_sys(), - method_name.string_sys(), + let __class_name = StringName::from(#class_name); + let __method_name = StringName::from(#method_name_str); + let __method_bind = sys::interface_fn!(classdb_get_method_bind)( + __class_name.string_sys(), + __method_name.string_sys(), #hash ); - let call_fn = sys::interface_fn!(object_method_bind_ptrcall); + let __call_fn = sys::interface_fn!(object_method_bind_ptrcall); - let args = [ + let __args = [ #( #arg_exprs ),* ]; - let args_ptr = args.as_ptr(); + let __args_ptr = __args.as_ptr(); #call } @@ -461,14 +461,14 @@ pub(crate) fn make_function_definition( quote! { pub fn #function_name( #( #params ),* ) #return_decl { let result = unsafe { - let function_name = StringName::from(#function_name_str); - let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(function_name.string_sys(), #hash); - let call_fn = call_fn.unwrap_unchecked(); + let __function_name = StringName::from(#function_name_str); + let __call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(__function_name.string_sys(), #hash); + let __call_fn = __call_fn.unwrap_unchecked(); - let args = [ + let __args = [ #( #arg_exprs ),* ]; - let args_ptr = args.as_ptr(); + let __args_ptr = __args.as_ptr(); #call }; @@ -540,9 +540,9 @@ fn make_method_return( // TODO use Result instead of panic on error quote! { let variant = Variant::from_var_sys_init(|return_ptr| { - let mut err = sys::default_call_error(); - call_fn(method_bind, self.object_ptr, args_ptr, args.len() as i64, return_ptr, std::ptr::addr_of_mut!(err)); - assert_eq!(err.error, sys::GDNATIVE_CALL_OK); + let mut __err = sys::default_call_error(); + __call_fn(__method_bind, self.object_ptr, __args_ptr, __args.len() as i64, return_ptr, std::ptr::addr_of_mut!(__err)); + assert_eq!(__err.error, sys::GDNATIVE_CALL_OK); }); #return_expr } @@ -550,28 +550,28 @@ fn make_method_return( (true, None) => { // TODO use Result instead of panic on error quote! { - let mut err = sys::default_call_error(); - call_fn(method_bind, self.object_ptr, args_ptr, args.len() as i64, std::ptr::null_mut(), std::ptr::addr_of_mut!(err)); - assert_eq!(err.error, sys::GDNATIVE_CALL_OK); + let mut __err = sys::default_call_error(); + __call_fn(__method_bind, self.object_ptr, __args_ptr, __args.len() as i64, std::ptr::null_mut(), std::ptr::addr_of_mut!(__err)); + assert_eq!(__err.error, sys::GDNATIVE_CALL_OK); } } (false, Some(RustTy::EngineClass(return_ty))) => { quote! { <#return_ty>::from_sys_init_opt(|return_ptr| { - call_fn(method_bind, self.object_ptr, args_ptr, return_ptr); + __call_fn(__method_bind, self.object_ptr, __args_ptr, return_ptr); }) } } (false, Some(return_ty)) => { quote! { <#return_ty as sys::GodotFfi>::from_sys_init(|return_ptr| { - call_fn(method_bind, self.object_ptr, args_ptr, return_ptr); + __call_fn(__method_bind, self.object_ptr, __args_ptr, return_ptr); }) } } (false, None) => { quote! { - call_fn(method_bind, self.object_ptr, args_ptr, std::ptr::null_mut()); + __call_fn(__method_bind, self.object_ptr, __args_ptr, std::ptr::null_mut()); } } }; @@ -599,20 +599,20 @@ fn make_utility_return( Some(RustTy::EngineClass(return_ty)) => { quote! { <#return_ty>::from_sys_init_opt(|return_ptr| { - call_fn(return_ptr, args_ptr, args.len() as i32); + __call_fn(return_ptr, __args_ptr, __args.len() as i32); }) } } Some(return_ty) => { quote! { <#return_ty as sys::GodotFfi>::from_sys_init(|return_ptr| { - call_fn(return_ptr, args_ptr, args.len() as i32); + __call_fn(return_ptr, __args_ptr, __args.len() as i32); }) } } None => { quote! { - call_fn(std::ptr::null_mut(), args_ptr, args.len() as i32); + __call_fn(std::ptr::null_mut(), __args_ptr, __args.len() as i32); } } }; From e71c622d831fed1a271566fecf097427e0308723 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 16:17:16 +0100 Subject: [PATCH 11/16] Small oversight in godot_ffi! macro --- godot-ffi/src/godot_ffi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index 88c1d0ddd..1d8b463dc 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -133,13 +133,13 @@ macro_rules! ffi_methods_one { }; (SelfPtr $Ptr:ty; $( #[$attr:meta] )? $vis:vis $sys:ident = sys) => { $( #[$attr] )? $vis - fn sys(&self) -> $Ptr { + fn $sys(&self) -> $Ptr { self as *const Self as $Ptr } }; (SelfPtr $Ptr:ty; $( #[$attr:meta] )? $vis:vis $write_sys:ident = write_sys) => { $( #[$attr] )? $vis - unsafe fn write_sys(&self, dst: $Ptr) { + unsafe fn $write_sys(&self, dst: $Ptr) { *(dst as *mut Self) = *self; } }; From ae86ee3d3aebeb9bbe2873dca0434cbffbdaa164 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 15:18:59 +0100 Subject: [PATCH 12/16] Update header: const variants of pointer types --- godot-codegen/input/gdnative_interface.h | 211 ++++++++++++----------- godot-codegen/input/tweak.patch | 24 ++- godot-codegen/src/central_generator.rs | 10 +- godot-codegen/src/class_generator.rs | 16 +- godot-core/src/builtin/arrays.rs | 17 +- godot-core/src/builtin/macros.rs | 8 +- godot-core/src/builtin/meta/signature.rs | 8 +- godot-core/src/builtin/node_path.rs | 8 +- godot-core/src/builtin/others.rs | 4 +- godot-core/src/builtin/string_name.rs | 8 +- godot-core/src/builtin/variant/mod.rs | 5 + godot-core/src/log.rs | 4 +- godot-core/src/macros.rs | 8 +- godot-core/src/obj/as_arg.rs | 6 +- godot-core/src/registry.rs | 6 +- godot-ffi/src/godot_ffi.rs | 8 +- godot-ffi/src/lib.rs | 12 ++ 17 files changed, 204 insertions(+), 159 deletions(-) diff --git a/godot-codegen/input/gdnative_interface.h b/godot-codegen/input/gdnative_interface.h index 860f1de43..304ccdfe8 100644 --- a/godot-codegen/input/gdnative_interface.h +++ b/godot-codegen/input/gdnative_interface.h @@ -141,11 +141,15 @@ typedef enum { } GDNativeVariantOperator; typedef struct TagVariant *GDNativeVariantPtr; +typedef const struct TagVariant *GDNativeConstVariantPtr; typedef struct TagStringName *GDNativeStringNamePtr; +typedef const struct TagStringName *GDNativeConstStringNamePtr; typedef struct TagString *GDNativeStringPtr; +typedef const struct TagString *GDNativeConstStringPtr; typedef struct TagObject *GDNativeObjectPtr; +typedef const struct TagObject *GDNativeConstObjectPtr; typedef struct TagType *GDNativeTypePtr; -typedef struct TagExtension *GDNativeExtensionPtr; +typedef const struct TagType *GDNativeConstTypePtr; typedef struct TagMethodBind *GDNativeMethodBindPtr; typedef int64_t GDNativeInt; typedef uint8_t GDNativeBool; @@ -171,18 +175,18 @@ typedef struct { typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr); typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr); -typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result); -typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); -typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args); +typedef void (*GDNativePtrOperatorEvaluator)(GDNativeConstTypePtr p_left, GDNativeConstTypePtr p_right, GDNativeTypePtr r_result); +typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); +typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args); typedef void (*GDNativePtrDestructor)(GDNativeTypePtr p_base); -typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value); -typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value); -typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value); -typedef void (*GDNativePtrIndexedGetter)(const GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value); -typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_key, const GDNativeTypePtr p_value); -typedef void (*GDNativePtrKeyedGetter)(const GDNativeTypePtr p_base, const GDNativeTypePtr p_key, GDNativeTypePtr r_value); -typedef uint32_t (*GDNativePtrKeyedChecker)(const GDNativeVariantPtr p_base, const GDNativeVariantPtr p_key); -typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeTypePtr *p_arguments, int p_argument_count); +typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_value); +typedef void (*GDNativePtrGetter)(GDNativeConstTypePtr p_base, GDNativeTypePtr r_value); +typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeConstTypePtr p_value); +typedef void (*GDNativePtrIndexedGetter)(GDNativeConstTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value); +typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeConstTypePtr p_value); +typedef void (*GDNativePtrKeyedGetter)(GDNativeConstTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeTypePtr r_value); +typedef uint32_t (*GDNativePtrKeyedChecker)(GDNativeConstVariantPtr p_base, GDNativeConstVariantPtr p_key); +typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, GDNativeConstTypePtr *p_arguments, int p_argument_count); typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); @@ -200,8 +204,8 @@ typedef struct { typedef void *GDExtensionClassInstancePtr; -typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value); -typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); +typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value); +typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret); typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance); typedef struct { @@ -230,16 +234,16 @@ typedef struct { typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDNativePropertyInfo *p_list); -typedef GDNativeBool (*GDNativeExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name); -typedef GDNativeBool (*GDNativeExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); +typedef GDNativeBool (*GDNativeExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name); +typedef GDNativeBool (*GDNativeExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret); typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); typedef void (*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr p_out); typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); -typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); +typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); typedef GDNativeObjectPtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); -typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const GDNativeStringNamePtr p_name); +typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, GDNativeConstStringNamePtr p_name); typedef struct { GDNativeBool is_virtual; @@ -289,8 +293,8 @@ typedef enum { GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE } GDNativeExtensionClassMethodArgumentMetadata; -typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); -typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); +typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); typedef struct { GDNativeStringNamePtr name; @@ -320,25 +324,25 @@ typedef struct { typedef void *GDNativeExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. -typedef GDNativeBool (*GDNativeExtensionScriptInstanceSet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value); -typedef GDNativeBool (*GDNativeExtensionScriptInstanceGet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); +typedef GDNativeBool (*GDNativeExtensionScriptInstanceSet)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value); +typedef GDNativeBool (*GDNativeExtensionScriptInstanceGet)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret); typedef const GDNativePropertyInfo *(*GDNativeExtensionScriptInstanceGetPropertyList)(GDNativeExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); typedef void (*GDNativeExtensionScriptInstanceFreePropertyList)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativePropertyInfo *p_list); -typedef GDNativeVariantType (*GDNativeExtensionScriptInstanceGetPropertyType)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeBool *r_is_valid); +typedef GDNativeVariantType (*GDNativeExtensionScriptInstanceGetPropertyType)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeBool *r_is_valid); -typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyCanRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name); -typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyGetRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); +typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyCanRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name); +typedef GDNativeBool (*GDNativeExtensionScriptInstancePropertyGetRevert)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name, GDNativeVariantPtr r_ret); typedef GDNativeObjectPtr (*GDNativeExtensionScriptInstanceGetOwner)(GDNativeExtensionScriptInstanceDataPtr p_instance); -typedef void (*GDNativeExtensionScriptInstancePropertyStateAdd)(const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value, void *p_userdata); +typedef void (*GDNativeExtensionScriptInstancePropertyStateAdd)(GDNativeConstStringNamePtr p_name, GDNativeConstVariantPtr p_value, void *p_userdata); typedef void (*GDNativeExtensionScriptInstanceGetPropertyState)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata); typedef const GDNativeMethodInfo *(*GDNativeExtensionScriptInstanceGetMethodList)(GDNativeExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); typedef void (*GDNativeExtensionScriptInstanceFreeMethodList)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeMethodInfo *p_list); -typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name); +typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name); -typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); typedef void (*GDNativeExtensionScriptInstanceNotification)(GDNativeExtensionScriptInstanceDataPtr p_instance, int32_t p_what); typedef void (*GDNativeExtensionScriptInstanceToString)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr r_out); @@ -413,41 +417,41 @@ typedef struct { void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); - uint64_t (*get_native_struct_size)(const GDNativeStringNamePtr p_name); + uint64_t (*get_native_struct_size)(GDNativeConstStringNamePtr p_name); /* GODOT VARIANT */ /* variant general */ - void (*variant_new_copy)(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src); + void (*variant_new_copy)(GDNativeVariantPtr r_dest, GDNativeConstVariantPtr p_src); void (*variant_new_nil)(GDNativeVariantPtr r_dest); void (*variant_destroy)(GDNativeVariantPtr p_self); /* variant type */ - void (*variant_call)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); - void (*variant_call_static)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); - void (*variant_evaluate)(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid); - void (*variant_set)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); - void (*variant_set_named)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); - void (*variant_set_keyed)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); - void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob); - void (*variant_get)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); - void (*variant_get_named)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); - void (*variant_get_keyed)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); - void (*variant_get_indexed)(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob); - GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); - GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); - void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); - GDNativeInt (*variant_hash)(const GDNativeVariantPtr p_self); - GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count); - GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other); - GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self); - void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); - void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret); - - GDNativeVariantType (*variant_get_type)(const GDNativeVariantPtr p_self); - GDNativeBool (*variant_has_method)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method); - GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); - GDNativeBool (*variant_has_key)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid); + void (*variant_call)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_call_static)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_evaluate)(GDNativeVariantOperator p_op, GDNativeConstVariantPtr p_a, GDNativeConstVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid); + void (*variant_set)(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_named)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_keyed)(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob); + void (*variant_get)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_named)(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_keyed)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_indexed)(GDNativeConstVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob); + GDNativeBool (*variant_iter_init)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + GDNativeBool (*variant_iter_next)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + void (*variant_iter_get)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + GDNativeInt (*variant_hash)(GDNativeConstVariantPtr p_self); + GDNativeInt (*variant_recursive_hash)(GDNativeConstVariantPtr p_self, GDNativeInt p_recursion_count); + GDNativeBool (*variant_hash_compare)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_other); + GDNativeBool (*variant_booleanize)(GDNativeConstVariantPtr p_self); + void (*variant_duplicate)(GDNativeConstVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); + void (*variant_stringify)(GDNativeConstVariantPtr p_self, GDNativeStringPtr r_ret); + + GDNativeVariantType (*variant_get_type)(GDNativeConstVariantPtr p_self); + GDNativeBool (*variant_has_method)(GDNativeConstVariantPtr p_self, GDNativeConstStringNamePtr p_method); + GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member); + GDNativeBool (*variant_has_key)(GDNativeConstVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeBool *r_valid); void (*variant_get_type_name)(GDNativeVariantType p_type, GDNativeStringPtr r_name); GDNativeBool (*variant_can_convert)(GDNativeVariantType p_from, GDNativeVariantType p_to); GDNativeBool (*variant_can_convert_strict)(GDNativeVariantType p_from, GDNativeVariantType p_to); @@ -456,19 +460,19 @@ typedef struct { GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b); - GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, GDNativeInt p_hash); + GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeInt p_hash); GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type); - void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); - GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); - GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); + void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, GDNativeConstVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); + GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member); + GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member); GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type); GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type); GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type); GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type); GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type); - void (*variant_get_constant_value)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_constant, GDNativeVariantPtr r_ret); - GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const GDNativeStringNamePtr p_function, GDNativeInt p_hash); + void (*variant_get_constant_value)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_constant, GDNativeVariantPtr r_ret); + GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(GDNativeConstStringNamePtr p_function, GDNativeInt p_hash); /* extra utilities */ void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents); @@ -476,12 +480,11 @@ typedef struct { void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents); void (*string_new_with_utf32_chars)(GDNativeStringPtr r_dest, const char32_t *p_contents); void (*string_new_with_wide_chars)(GDNativeStringPtr r_dest, const wchar_t *p_contents); - void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); - void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); - void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size); - void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size); - void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size); - + void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size); + void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, GDNativeInt p_size); + void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, GDNativeInt p_size); + void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, GDNativeInt p_size); + void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, GDNativeInt p_size); /* Information about the following functions: * - The return value is the resulting encoded string length. * - The length returned is in characters, not in bytes. It also does not include a trailing zero. @@ -490,63 +493,63 @@ typedef struct { * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL. * - p_max_write_length argument does not affect the return value, it's only to cap write length. */ - GDNativeInt (*string_to_latin1_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); - GDNativeInt (*string_to_utf8_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); - GDNativeInt (*string_to_utf16_chars)(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length); - GDNativeInt (*string_to_utf32_chars)(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length); - GDNativeInt (*string_to_wide_chars)(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_latin1_chars)(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf8_chars)(GDNativeConstStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf16_chars)(GDNativeConstStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf32_chars)(GDNativeConstStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_wide_chars)(GDNativeConstStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length); char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index); - const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index); + const char32_t *(*string_operator_index_const)(GDNativeConstStringPtr p_self, GDNativeInt p_index); /* Packed array functions */ uint8_t *(*packed_byte_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray - const uint8_t *(*packed_byte_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray + const uint8_t *(*packed_byte_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedByteArray GDNativeTypePtr (*packed_color_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr - GDNativeTypePtr (*packed_color_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr + GDNativeTypePtr (*packed_color_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedColorArray, returns Color ptr float *(*packed_float32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array - const float *(*packed_float32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array + const float *(*packed_float32_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat32Array double *(*packed_float64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array - const double *(*packed_float64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array + const double *(*packed_float64_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedFloat64Array int32_t *(*packed_int32_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array - const int32_t *(*packed_int32_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + const int32_t *(*packed_int32_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array int64_t *(*packed_int64_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array - const int64_t *(*packed_int64_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array + const int64_t *(*packed_int64_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedInt32Array GDNativeStringPtr (*packed_string_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray - GDNativeStringPtr (*packed_string_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray + GDNativeStringPtr (*packed_string_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedStringArray GDNativeTypePtr (*packed_vector2_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr - GDNativeTypePtr (*packed_vector2_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr + GDNativeTypePtr (*packed_vector2_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr GDNativeTypePtr (*packed_vector3_array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr - GDNativeTypePtr (*packed_vector3_array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr + GDNativeTypePtr (*packed_vector3_array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr GDNativeVariantPtr (*array_operator_index)(GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr - GDNativeVariantPtr (*array_operator_index_const)(const GDNativeTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr + GDNativeVariantPtr (*array_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeInt p_index); // p_self should be an Array ptr /* Dictionary functions */ - GDNativeVariantPtr (*dictionary_operator_index)(GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr - GDNativeVariantPtr (*dictionary_operator_index_const)(const GDNativeTypePtr p_self, const GDNativeVariantPtr p_key); // p_self should be an Dictionary ptr + GDNativeVariantPtr (*dictionary_operator_index)(GDNativeTypePtr p_self, GDNativeConstVariantPtr p_key); // p_self should be an Dictionary ptr + GDNativeVariantPtr (*dictionary_operator_index_const)(GDNativeConstTypePtr p_self, GDNativeConstVariantPtr p_key); // p_self should be an Dictionary ptr /* OBJECT */ - void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); - void (*object_method_bind_ptrcall)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); + void (*object_method_bind_call)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); + void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); void (*object_destroy)(GDNativeObjectPtr p_o); - GDNativeObjectPtr (*global_get_singleton)(const GDNativeStringNamePtr p_name); + GDNativeObjectPtr (*global_get_singleton)(GDNativeConstStringNamePtr p_name); void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); - void (*object_set_instance)(GDNativeObjectPtr p_o, const GDNativeStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ + void (*object_set_instance)(GDNativeObjectPtr p_o, GDNativeConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ - GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); + GDNativeObjectPtr (*object_cast_to)(GDNativeConstObjectPtr p_object, void *p_class_tag); GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); - GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object); + GDObjectInstanceID (*object_get_instance_id)(GDNativeConstObjectPtr p_object); /* SCRIPT INSTANCE */ @@ -554,23 +557,23 @@ typedef struct { /* CLASSDB */ - GDNativeObjectPtr (*classdb_construct_object)(const GDNativeStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ - GDNativeMethodBindPtr (*classdb_get_method_bind)(const GDNativeStringNamePtr p_classname, const GDNativeStringNamePtr p_methodname, GDNativeInt p_hash); - void *(*classdb_get_class_tag)(const GDNativeStringNamePtr p_classname); + GDNativeObjectPtr (*classdb_construct_object)(GDNativeConstStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ + GDNativeMethodBindPtr (*classdb_get_method_bind)(GDNativeConstStringNamePtr p_classname, GDNativeConstStringNamePtr p_methodname, GDNativeInt p_hash); + void *(*classdb_get_class_tag)(GDNativeConstStringNamePtr p_classname); /* CLASSDB EXTENSION */ /* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */ - void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); - void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); - void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_enum_name, const GDNativeStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield); - void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, const GDNativeStringNamePtr p_setter, const GDNativeStringNamePtr p_getter); - void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_group_name, const GDNativeStringPtr p_prefix); - void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_subgroup_name, const GDNativeStringPtr p_prefix); - void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); - void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ + void (*classdb_register_extension_class)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + void (*classdb_register_extension_class_method)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + void (*classdb_register_extension_class_integer_constant)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_enum_name, GDNativeConstStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield); + void (*classdb_register_extension_class_property)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, GDNativeConstStringNamePtr p_setter, GDNativeConstStringNamePtr p_getter); + void (*classdb_register_extension_class_property_group)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_group_name, GDNativeConstStringPtr p_prefix); + void (*classdb_register_extension_class_property_subgroup)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringPtr p_subgroup_name, GDNativeConstStringPtr p_prefix); + void (*classdb_register_extension_class_signal)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name, GDNativeConstStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + void (*classdb_unregister_extension_class)(GDNativeExtensionClassLibraryPtr p_library, GDNativeConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ - void (*get_library_path)(const GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path); + void (*get_library_path)(GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path); } GDNativeInterface; @@ -600,7 +603,7 @@ typedef struct { * It can be used to set up different init levels, which are called during various stages of initialization/shutdown. * The function name must be a unique one specified in the .gdextension config file. */ -typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization); +typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization); #ifdef __cplusplus } diff --git a/godot-codegen/input/tweak.patch b/godot-codegen/input/tweak.patch index 0d5179b19..722626253 100644 --- a/godot-codegen/input/tweak.patch +++ b/godot-codegen/input/tweak.patch @@ -1,24 +1,32 @@ -diff --git b/godot-codegen/input/gdnative_interface.h a/godot-codegen/input/gdnative_interface.h -index 041a6e5..c1b932a 100644 ---- b/godot-codegen/input/gdnative_interface.h -+++ a/godot-codegen/input/gdnative_interface.h -@@ -136,13 +136,13 @@ typedef enum { +diff --git a/godot-codegen/input/gdnative_interface.h b/godot-codegen/input/gdnative_interface.h +index 486f5be..304ccdf 100644 +--- a/godot-codegen/input/gdnative_interface.h ++++ b/godot-codegen/input/gdnative_interface.h +@@ -140,17 +140,17 @@ typedef enum { } GDNativeVariantOperator; -typedef void *GDNativeVariantPtr; +-typedef const void *GDNativeConstVariantPtr; -typedef void *GDNativeStringNamePtr; +-typedef const void *GDNativeConstStringNamePtr; -typedef void *GDNativeStringPtr; +-typedef const void *GDNativeConstStringPtr; -typedef void *GDNativeObjectPtr; +-typedef const void *GDNativeConstObjectPtr; -typedef void *GDNativeTypePtr; --typedef void *GDNativeExtensionPtr; --typedef void *GDNativeMethodBindPtr; +-typedef const void *GDNativeConstTypePtr; +-typedef const void *GDNativeMethodBindPtr; +typedef struct TagVariant *GDNativeVariantPtr; ++typedef const struct TagVariant *GDNativeConstVariantPtr; +typedef struct TagStringName *GDNativeStringNamePtr; ++typedef const struct TagStringName *GDNativeConstStringNamePtr; +typedef struct TagString *GDNativeStringPtr; ++typedef const struct TagString *GDNativeConstStringPtr; +typedef struct TagObject *GDNativeObjectPtr; ++typedef const struct TagObject *GDNativeConstObjectPtr; +typedef struct TagType *GDNativeTypePtr; -+typedef struct TagExtension *GDNativeExtensionPtr; ++typedef const struct TagType *GDNativeConstTypePtr; +typedef struct TagMethodBind *GDNativeMethodBindPtr; typedef int64_t GDNativeInt; typedef uint8_t GDNativeBool; diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index 0c26b6cbd..fc026037f 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -161,7 +161,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { } = central_items; let sys_tokens = quote! { - use crate::{GDNativeVariantPtr, GDNativeTypePtr, GodotFfi, ffi_methods}; + use crate::{GDNativeVariantPtr, GDNativeTypePtr, GDNativeConstTypePtr, GodotFfi, ffi_methods}; pub mod types { #(#opaque_types)* @@ -592,8 +592,8 @@ fn make_construct_fns( // Generic signature: fn(base: GDNativeTypePtr, args: *const GDNativeTypePtr) let decls = quote! { - pub #construct_default: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), - pub #construct_copy: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), + pub #construct_default: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), + pub #construct_copy: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), #(#construct_extra_decls)* }; @@ -643,7 +643,7 @@ fn make_extra_constructors( let err = format_load_error(&ident); extra_decls.push(quote! { - pub #ident: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), + pub #ident: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), }); let i = i as i32; @@ -707,7 +707,7 @@ fn make_operator_fns( // Field declaration let decl = quote! { - pub #operator: unsafe extern "C" fn(GDNativeTypePtr, GDNativeTypePtr, GDNativeTypePtr), + pub #operator: unsafe extern "C" fn(GDNativeConstTypePtr, GDNativeConstTypePtr, GDNativeTypePtr), }; // Field initialization in new() diff --git a/godot-codegen/src/class_generator.rs b/godot-codegen/src/class_generator.rs index e040eac2c..4d64298b6 100644 --- a/godot-codegen/src/class_generator.rs +++ b/godot-codegen/src/class_generator.rs @@ -405,10 +405,10 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) #( #arg_exprs ),* ]; let mut __args = Vec::new(); - __args.extend(__explicit_args.iter().map(Variant::var_sys)); - __args.extend(varargs.iter().map(Variant::var_sys)); + __args.extend(__explicit_args.iter().map(Variant::var_sys_const)); + __args.extend(varargs.iter().map(Variant::var_sys_const)); - let __args_ptr = __args.as_ptr(); + let __args_ptr = __args.as_mut_ptr(); #call } @@ -428,10 +428,10 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) ); let __call_fn = sys::interface_fn!(object_method_bind_ptrcall); - let __args = [ + let mut __args = [ #( #arg_exprs ),* ]; - let __args_ptr = __args.as_ptr(); + let __args_ptr = __args.as_mut_ptr(); #call } @@ -465,10 +465,10 @@ pub(crate) fn make_function_definition( let __call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(__function_name.string_sys(), #hash); let __call_fn = __call_fn.unwrap_unchecked(); - let __args = [ + let mut __args = [ #( #arg_exprs ),* ]; - let __args_ptr = __args.as_ptr(); + let __args_ptr = __args.as_mut_ptr(); #call }; @@ -503,7 +503,7 @@ fn make_params( }); } else { arg_exprs.push(quote! { - <#param_ty as sys::GodotFfi>::sys(&#param_name) + <#param_ty as sys::GodotFfi>::sys_const(&#param_name) }); } } diff --git a/godot-core/src/builtin/arrays.rs b/godot-core/src/builtin/arrays.rs index cef0695ed..98d84be25 100644 --- a/godot-core/src/builtin/arrays.rs +++ b/godot-core/src/builtin/arrays.rs @@ -72,16 +72,27 @@ impl TypedArray { impl Clone for TypedArray { fn clone(&self) -> Self { unsafe { - Self::from_sys_init(|opaque_ptr| { - let ctor = sys::builtin_fn!(array_construct_copy); - ctor(opaque_ptr, &self.sys() as *const sys::GDNativeTypePtr); + Self::from_sys_init(|self_ptr| { + let ctor = ::godot_ffi::builtin_fn!(array_construct_copy); + let mut args = [self.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } } + +// TODO enable this: +// impl_builtin_traits! { +// for TypedArray { +// Clone => array_construct_copy; +// Drop => array_destroy; +// } +// } + impl GodotFfi for TypedArray { ffi_methods! { type sys::GDNativeTypePtr = *mut Opaque; .. } } + impl Drop for TypedArray { fn drop(&mut self) { unsafe { diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index de13b9249..b67b7a4ba 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -27,8 +27,8 @@ macro_rules! impl_builtin_traits_inner { unsafe { Self::from_sys_init(|self_ptr| { let ctor = ::godot_ffi::builtin_fn!($gd_method); - let args = [self.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [self.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } @@ -141,9 +141,9 @@ macro_rules! impl_builtin_froms { fn from(other: &$From) -> Self { unsafe { Self::from_sys_init(|ptr| { - let args = [other.sys()]; + let mut args = [other.sys_const()]; ::godot_ffi::builtin_call! { - $from_fn(ptr, args.as_ptr()) + $from_fn(ptr, args.as_mut_ptr()) } }) } diff --git a/godot-core/src/builtin/meta/signature.rs b/godot-core/src/builtin/meta/signature.rs index 2310f01f5..a5f3a07d3 100644 --- a/godot-core/src/builtin/meta/signature.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -18,7 +18,7 @@ pub trait SignatureTuple { fn varcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDNativeVariantPtr, + args_ptr: *mut sys::GDNativeConstVariantPtr, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, func: fn(&mut C, Self::Params) -> Self::Ret, @@ -29,7 +29,7 @@ pub trait SignatureTuple { // We could fall back to varcalls in such cases, and not require GodotFfi categorically. fn ptrcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDNativeTypePtr, + args_ptr: *mut sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, func: fn(&mut C, Self::Params) -> Self::Ret, method_name: &str, @@ -106,7 +106,7 @@ macro_rules! impl_signature_for_tuple { #[inline] fn varcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDNativeVariantPtr, + args_ptr: *mut sys::GDNativeConstVariantPtr, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, func: fn(&mut C, Self::Params) -> Self::Ret, @@ -138,7 +138,7 @@ macro_rules! impl_signature_for_tuple { #[inline] fn ptrcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDNativeTypePtr, + args_ptr: *mut sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, func: fn(&mut C, Self::Params) -> Self::Ret, method_name: &str, diff --git a/godot-core/src/builtin/node_path.rs b/godot-core/src/builtin/node_path.rs index 9a5f9f1a8..b33b69dc8 100644 --- a/godot-core/src/builtin/node_path.rs +++ b/godot-core/src/builtin/node_path.rs @@ -28,8 +28,8 @@ impl From<&GodotString> for NodePath { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(node_path_from_string); - let args = [path.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [path.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } @@ -40,8 +40,8 @@ impl From<&NodePath> for GodotString { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_from_node_path); - let args = [path.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [path.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } diff --git a/godot-core/src/builtin/others.rs b/godot-core/src/builtin/others.rs index caf6b8163..19b261176 100644 --- a/godot-core/src/builtin/others.rs +++ b/godot-core/src/builtin/others.rs @@ -55,8 +55,8 @@ impl Callable { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(callable_from_object_method); - let args = [object.sys(), method.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [object.sys_const(), method.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } diff --git a/godot-core/src/builtin/string_name.rs b/godot-core/src/builtin/string_name.rs index 02859a9f1..e44d5c870 100644 --- a/godot-core/src/builtin/string_name.rs +++ b/godot-core/src/builtin/string_name.rs @@ -108,8 +108,8 @@ impl From<&GodotString> for StringName { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_name_from_string); - let args = [s.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [s.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } @@ -127,8 +127,8 @@ impl From<&StringName> for GodotString { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_from_string_name); - let args = [s.sys()]; - ctor(self_ptr, args.as_ptr()); + let mut args = [s.sys_const()]; + ctor(self_ptr, args.as_mut_ptr()); }) } } diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index 1e52955c9..a9a1f8e1f 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -123,6 +123,11 @@ impl Variant { fn write_var_sys = write_sys; } + #[doc(hidden)] + pub fn var_sys_const(&self) -> sys::GDNativeConstVariantPtr { + sys::to_const_ptr(self.var_sys()) + } + /*#[doc(hidden)] pub unsafe fn from_var_sys_init(init_fn: impl FnOnce(sys::GDNativeVariantPtr)) -> Self { // Can't use uninitialized pointer -- Variant implementation in C++ expects that on assignment, diff --git a/godot-core/src/log.rs b/godot-core/src/log.rs index 56587da93..096393385 100644 --- a/godot-core/src/log.rs +++ b/godot-core/src/log.rs @@ -82,9 +82,9 @@ pub fn print(varargs: &[Variant]) { let call_fn = call_fn.unwrap_unchecked(); let mut args = Vec::new(); - args.extend(varargs.iter().map(Variant::sys)); + args.extend(varargs.iter().map(Variant::sys_const)); - let args_ptr = args.as_ptr(); + let args_ptr = args.as_mut_ptr(); let _variant = Variant::from_sys_init(|return_ptr| { call_fn(return_ptr, args_ptr, args.len() as i32); }); diff --git a/godot-core/src/macros.rs b/godot-core/src/macros.rs index 2a60d0ffa..55677b9f3 100644 --- a/godot-core/src/macros.rs +++ b/godot-core/src/macros.rs @@ -61,7 +61,7 @@ macro_rules! gdext_register_method_inner { unsafe extern "C" fn function( _method_data: *mut std::ffi::c_void, instance_ptr: sys::GDExtensionClassInstancePtr, - args: *const sys::GDNativeVariantPtr, + args: *mut sys::GDNativeConstVariantPtr, _arg_count: sys::GDNativeInt, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, @@ -97,7 +97,7 @@ macro_rules! gdext_register_method_inner { unsafe extern "C" fn function( _method_data: *mut std::ffi::c_void, instance_ptr: sys::GDExtensionClassInstancePtr, - args: *const sys::GDNativeTypePtr, + args: *mut sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, ) { let result = ::std::panic::catch_unwind(|| { @@ -289,7 +289,7 @@ macro_rules! gdext_virtual_method_callback_inner { unsafe extern "C" fn function( instance_ptr: sys::GDExtensionClassInstancePtr, - args: *const sys::GDNativeTypePtr, + args: *const sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, ) { $crate::gdext_ptrcall!( @@ -402,7 +402,7 @@ macro_rules! gdext_ptrcall { let mut idx = 0; $( - let $arg = <$ParamTy as sys::GodotFfi>::from_sys(*$args.offset(idx)); + let $arg = <$ParamTy as sys::GodotFfi>::from_sys(sys::force_mut_ptr(*$args.offset(idx))); // FIXME update refcount, e.g. Gd::ready() or T::Mem::maybe_inc_ref(&result); // possibly in from_sys() directly; what about from_sys_init() and from_{obj|str}_sys()? idx += 1; diff --git a/godot-core/src/obj/as_arg.rs b/godot-core/src/obj/as_arg.rs index 79f21ad0f..5237eb535 100644 --- a/godot-core/src/obj/as_arg.rs +++ b/godot-core/src/obj/as_arg.rs @@ -16,15 +16,15 @@ use private::Sealed; pub trait AsArg: Sealed { #[doc(hidden)] - fn as_arg_ptr(&self) -> sys::GDNativeTypePtr; + fn as_arg_ptr(&self) -> sys::GDNativeConstTypePtr; } impl Sealed for Gd {} impl AsArg for Gd { - fn as_arg_ptr(&self) -> sys::GDNativeTypePtr { + fn as_arg_ptr(&self) -> sys::GDNativeConstTypePtr { // Pass argument to engine: increment refcount ::maybe_inc_ref(self); - self.sys() + self.sys_const() } } diff --git a/godot-core/src/registry.rs b/godot-core/src/registry.rs index 4e1d79eca..9aac194d7 100644 --- a/godot-core/src/registry.rs +++ b/godot-core/src/registry.rs @@ -94,7 +94,7 @@ pub enum PluginComponent { /// Callback for other virtuals get_virtual_fn: unsafe extern "C" fn( p_userdata: *mut std::os::raw::c_void, - p_name: sys::GDNativeStringNamePtr, + p_name: sys::GDNativeConstStringNamePtr, ) -> sys::GDNativeExtensionClassCallVirtual, }, } @@ -312,10 +312,10 @@ pub mod callbacks { pub unsafe extern "C" fn get_virtual( _class_user_data: *mut std::ffi::c_void, - name: sys::GDNativeStringNamePtr, + name: sys::GDNativeConstStringNamePtr, ) -> sys::GDNativeExtensionClassCallVirtual { // This string is not ours, so we cannot call the destructor on it. - let borrowed_string = StringName::from_string_sys(name); + let borrowed_string = StringName::from_string_sys(sys::force_mut_ptr(name)); let method_name = borrowed_string.to_string(); std::mem::forget(borrowed_string); diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index 1d8b463dc..e7e75a7e0 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -31,7 +31,13 @@ pub trait GodotFfi { self.sys() } - unsafe fn write_sys(&self, dst: sys::GDNativeTypePtr); + // TODO check if sys() can take over this + fn sys_const(&self) -> sys::GDNativeConstTypePtr { + self.sys() + } + + + unsafe fn write_sys(&self, dst: sys::GDNativeTypePtr); } /// Trait implemented for all types that can be passed to and from user-defined `#[func]` methods diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 052b67921..4a0aafa9a 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -279,3 +279,15 @@ pub fn unbox(value: Box) -> T { // Deref-move is a Box magic feature; see https://stackoverflow.com/a/42264074 *value } + +/// Explicitly cast away `const` from a pointer, similar to C++ `const_cast`. +/// +/// The `as` conversion simultaneously doing 10 other things, potentially causing unintended transmutations. +pub fn force_mut_ptr(ptr: *const T) -> *mut T { + ptr as *mut T +} + +/// Add `const` to a mut ptr. +pub fn to_const_ptr(ptr: *mut T) -> *const T { + ptr as *const T +} From ba7c1a5339d424101173851e042aa7b0d461a6eb Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 10 Dec 2022 18:04:31 +0100 Subject: [PATCH 13/16] Update const-correctness again, anticipating `T* args` -> `const T* args` fix (not yet in Godot) --- godot-codegen/input/gdnative_interface.h | 24 ++++++++++++------------ godot-codegen/src/central_generator.rs | 6 +++--- godot-codegen/src/class_generator.rs | 10 +++++----- godot-core/src/builtin/arrays.rs | 4 ++-- godot-core/src/builtin/macros.rs | 8 ++++---- godot-core/src/builtin/meta/signature.rs | 14 +++++++++----- godot-core/src/builtin/node_path.rs | 8 ++++---- godot-core/src/builtin/others.rs | 4 ++-- godot-core/src/builtin/string_name.rs | 8 ++++---- godot-core/src/log.rs | 2 +- godot-core/src/macros.rs | 4 ++-- godot-ffi/src/godot_ffi.rs | 13 +++++++------ 12 files changed, 55 insertions(+), 50 deletions(-) diff --git a/godot-codegen/input/gdnative_interface.h b/godot-codegen/input/gdnative_interface.h index 304ccdfe8..f86e0176f 100644 --- a/godot-codegen/input/gdnative_interface.h +++ b/godot-codegen/input/gdnative_interface.h @@ -176,8 +176,8 @@ typedef struct { typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr); typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr); typedef void (*GDNativePtrOperatorEvaluator)(GDNativeConstTypePtr p_left, GDNativeConstTypePtr p_right, GDNativeTypePtr r_result); -typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); -typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, GDNativeConstTypePtr *p_args); +typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeConstTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); +typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeConstTypePtr *p_args); typedef void (*GDNativePtrDestructor)(GDNativeTypePtr p_base); typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_value); typedef void (*GDNativePtrGetter)(GDNativeConstTypePtr p_base, GDNativeTypePtr r_value); @@ -186,7 +186,7 @@ typedef void (*GDNativePtrIndexedGetter)(GDNativeConstTypePtr p_base, GDNativeIn typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeConstTypePtr p_value); typedef void (*GDNativePtrKeyedGetter)(GDNativeConstTypePtr p_base, GDNativeConstTypePtr p_key, GDNativeTypePtr r_value); typedef uint32_t (*GDNativePtrKeyedChecker)(GDNativeConstVariantPtr p_base, GDNativeConstVariantPtr p_key); -typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, GDNativeConstTypePtr *p_arguments, int p_argument_count); +typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeConstTypePtr *p_arguments, int p_argument_count); typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); @@ -240,7 +240,7 @@ typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p typedef void (*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr p_out); typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); -typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); +typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); typedef GDNativeObjectPtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, GDNativeConstStringNamePtr p_name); @@ -293,8 +293,8 @@ typedef enum { GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE } GDNativeExtensionClassMethodArgumentMetadata; -typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); -typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); +typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); typedef struct { GDNativeStringNamePtr name; @@ -342,7 +342,7 @@ typedef void (*GDNativeExtensionScriptInstanceFreeMethodList)(GDNativeExtensionS typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeConstStringNamePtr p_name); -typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, GDNativeConstStringNamePtr p_method, const GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); typedef void (*GDNativeExtensionScriptInstanceNotification)(GDNativeExtensionScriptInstanceDataPtr p_instance, int32_t p_what); typedef void (*GDNativeExtensionScriptInstanceToString)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr r_out); @@ -427,8 +427,8 @@ typedef struct { void (*variant_destroy)(GDNativeVariantPtr p_self); /* variant type */ - void (*variant_call)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); - void (*variant_call_static)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_call)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_method, const GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_call_static)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, const GDNativeConstVariantPtr *p_args, GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); void (*variant_evaluate)(GDNativeVariantOperator p_op, GDNativeConstVariantPtr p_a, GDNativeConstVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid); void (*variant_set)(GDNativeVariantPtr p_self, GDNativeConstVariantPtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid); void (*variant_set_named)(GDNativeVariantPtr p_self, GDNativeConstStringNamePtr p_key, GDNativeConstVariantPtr p_value, GDNativeBool *r_valid); @@ -463,7 +463,7 @@ typedef struct { GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_method, GDNativeInt p_hash); GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type); - void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, GDNativeConstVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); + void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeConstVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member); GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, GDNativeConstStringNamePtr p_member); GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type); @@ -537,8 +537,8 @@ typedef struct { /* OBJECT */ - void (*object_method_bind_call)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); - void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); + void (*object_method_bind_call)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeConstVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); + void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeConstTypePtr *p_args, GDNativeTypePtr r_ret); void (*object_destroy)(GDNativeObjectPtr p_o); GDNativeObjectPtr (*global_get_singleton)(GDNativeConstStringNamePtr p_name); diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index fc026037f..e529757a6 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -592,8 +592,8 @@ fn make_construct_fns( // Generic signature: fn(base: GDNativeTypePtr, args: *const GDNativeTypePtr) let decls = quote! { - pub #construct_default: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), - pub #construct_copy: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), + pub #construct_default: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeConstTypePtr), + pub #construct_copy: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeConstTypePtr), #(#construct_extra_decls)* }; @@ -643,7 +643,7 @@ fn make_extra_constructors( let err = format_load_error(&ident); extra_decls.push(quote! { - pub #ident: unsafe extern "C" fn(GDNativeTypePtr, *mut GDNativeConstTypePtr), + pub #ident: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeConstTypePtr), }); let i = i as i32; diff --git a/godot-codegen/src/class_generator.rs b/godot-codegen/src/class_generator.rs index 4d64298b6..c7a0e997c 100644 --- a/godot-codegen/src/class_generator.rs +++ b/godot-codegen/src/class_generator.rs @@ -408,7 +408,7 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) __args.extend(__explicit_args.iter().map(Variant::var_sys_const)); __args.extend(varargs.iter().map(Variant::var_sys_const)); - let __args_ptr = __args.as_mut_ptr(); + let __args_ptr = __args.as_ptr(); #call } @@ -428,10 +428,10 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) ); let __call_fn = sys::interface_fn!(object_method_bind_ptrcall); - let mut __args = [ + let __args = [ #( #arg_exprs ),* ]; - let __args_ptr = __args.as_mut_ptr(); + let __args_ptr = __args.as_ptr(); #call } @@ -465,10 +465,10 @@ pub(crate) fn make_function_definition( let __call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(__function_name.string_sys(), #hash); let __call_fn = __call_fn.unwrap_unchecked(); - let mut __args = [ + let __args = [ #( #arg_exprs ),* ]; - let __args_ptr = __args.as_mut_ptr(); + let __args_ptr = __args.as_ptr(); #call }; diff --git a/godot-core/src/builtin/arrays.rs b/godot-core/src/builtin/arrays.rs index 98d84be25..3b12236d9 100644 --- a/godot-core/src/builtin/arrays.rs +++ b/godot-core/src/builtin/arrays.rs @@ -74,8 +74,8 @@ impl Clone for TypedArray { unsafe { Self::from_sys_init(|self_ptr| { let ctor = ::godot_ffi::builtin_fn!(array_construct_copy); - let mut args = [self.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [self.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index b67b7a4ba..7a96aecfb 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -27,8 +27,8 @@ macro_rules! impl_builtin_traits_inner { unsafe { Self::from_sys_init(|self_ptr| { let ctor = ::godot_ffi::builtin_fn!($gd_method); - let mut args = [self.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [self.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } @@ -141,9 +141,9 @@ macro_rules! impl_builtin_froms { fn from(other: &$From) -> Self { unsafe { Self::from_sys_init(|ptr| { - let mut args = [other.sys_const()]; + let args = [other.sys_const()]; ::godot_ffi::builtin_call! { - $from_fn(ptr, args.as_mut_ptr()) + $from_fn(ptr, args.as_ptr()) } }) } diff --git a/godot-core/src/builtin/meta/signature.rs b/godot-core/src/builtin/meta/signature.rs index a5f3a07d3..16df2c952 100644 --- a/godot-core/src/builtin/meta/signature.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -18,7 +18,7 @@ pub trait SignatureTuple { fn varcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *mut sys::GDNativeConstVariantPtr, + args_ptr: *const sys::GDNativeConstVariantPtr, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, func: fn(&mut C, Self::Params) -> Self::Ret, @@ -29,7 +29,7 @@ pub trait SignatureTuple { // We could fall back to varcalls in such cases, and not require GodotFfi categorically. fn ptrcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *mut sys::GDNativeConstTypePtr, + args_ptr: *const sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, func: fn(&mut C, Self::Params) -> Self::Ret, method_name: &str, @@ -106,7 +106,7 @@ macro_rules! impl_signature_for_tuple { #[inline] fn varcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *mut sys::GDNativeConstVariantPtr, + args_ptr: *const sys::GDNativeConstVariantPtr, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, func: fn(&mut C, Self::Params) -> Self::Ret, @@ -138,7 +138,7 @@ macro_rules! impl_signature_for_tuple { #[inline] fn ptrcall( instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *mut sys::GDNativeConstTypePtr, + args_ptr: *const sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, func: fn(&mut C, Self::Params) -> Self::Ret, method_name: &str, @@ -149,7 +149,11 @@ macro_rules! impl_signature_for_tuple { let mut instance = storage.get_mut(); let args = ( $( - unsafe { <$Pn as sys::GodotFuncMarshal>::try_from_sys(*args_ptr.offset($n)) } + unsafe { + <$Pn as sys::GodotFuncMarshal>::try_from_sys( + sys::force_mut_ptr(*args_ptr.offset($n)) + ) + } .unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, &e)), )* ); diff --git a/godot-core/src/builtin/node_path.rs b/godot-core/src/builtin/node_path.rs index b33b69dc8..5a2ef9022 100644 --- a/godot-core/src/builtin/node_path.rs +++ b/godot-core/src/builtin/node_path.rs @@ -28,8 +28,8 @@ impl From<&GodotString> for NodePath { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(node_path_from_string); - let mut args = [path.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [path.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } @@ -40,8 +40,8 @@ impl From<&NodePath> for GodotString { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_from_node_path); - let mut args = [path.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [path.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } diff --git a/godot-core/src/builtin/others.rs b/godot-core/src/builtin/others.rs index 19b261176..5e54533b2 100644 --- a/godot-core/src/builtin/others.rs +++ b/godot-core/src/builtin/others.rs @@ -55,8 +55,8 @@ impl Callable { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(callable_from_object_method); - let mut args = [object.sys_const(), method.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [object.sys_const(), method.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } diff --git a/godot-core/src/builtin/string_name.rs b/godot-core/src/builtin/string_name.rs index e44d5c870..e4b66be9c 100644 --- a/godot-core/src/builtin/string_name.rs +++ b/godot-core/src/builtin/string_name.rs @@ -108,8 +108,8 @@ impl From<&GodotString> for StringName { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_name_from_string); - let mut args = [s.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [s.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } @@ -127,8 +127,8 @@ impl From<&StringName> for GodotString { unsafe { Self::from_sys_init(|self_ptr| { let ctor = sys::builtin_fn!(string_from_string_name); - let mut args = [s.sys_const()]; - ctor(self_ptr, args.as_mut_ptr()); + let args = [s.sys_const()]; + ctor(self_ptr, args.as_ptr()); }) } } diff --git a/godot-core/src/log.rs b/godot-core/src/log.rs index 096393385..d6056d1e7 100644 --- a/godot-core/src/log.rs +++ b/godot-core/src/log.rs @@ -84,7 +84,7 @@ pub fn print(varargs: &[Variant]) { let mut args = Vec::new(); args.extend(varargs.iter().map(Variant::sys_const)); - let args_ptr = args.as_mut_ptr(); + let args_ptr = args.as_ptr(); let _variant = Variant::from_sys_init(|return_ptr| { call_fn(return_ptr, args_ptr, args.len() as i32); }); diff --git a/godot-core/src/macros.rs b/godot-core/src/macros.rs index 55677b9f3..6d5102ad8 100644 --- a/godot-core/src/macros.rs +++ b/godot-core/src/macros.rs @@ -61,7 +61,7 @@ macro_rules! gdext_register_method_inner { unsafe extern "C" fn function( _method_data: *mut std::ffi::c_void, instance_ptr: sys::GDExtensionClassInstancePtr, - args: *mut sys::GDNativeConstVariantPtr, + args: *const sys::GDNativeConstVariantPtr, _arg_count: sys::GDNativeInt, ret: sys::GDNativeVariantPtr, err: *mut sys::GDNativeCallError, @@ -97,7 +97,7 @@ macro_rules! gdext_register_method_inner { unsafe extern "C" fn function( _method_data: *mut std::ffi::c_void, instance_ptr: sys::GDExtensionClassInstancePtr, - args: *mut sys::GDNativeConstTypePtr, + args: *const sys::GDNativeConstTypePtr, ret: sys::GDNativeTypePtr, ) { let result = ::std::panic::catch_unwind(|| { diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index e7e75a7e0..321000cae 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -31,13 +31,14 @@ pub trait GodotFfi { self.sys() } - // TODO check if sys() can take over this - fn sys_const(&self) -> sys::GDNativeConstTypePtr { - self.sys() - } - + // TODO check if sys() can take over this + // also, from_sys() might take *const T + // possibly separate 2 pointer types + fn sys_const(&self) -> sys::GDNativeConstTypePtr { + self.sys() + } - unsafe fn write_sys(&self, dst: sys::GDNativeTypePtr); + unsafe fn write_sys(&self, dst: sys::GDNativeTypePtr); } /// Trait implemented for all types that can be passed to and from user-defined `#[func]` methods From dbb20591f9e8184272d21a4afa09442fa2e2ff9b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 12 Dec 2022 21:02:20 +0100 Subject: [PATCH 14/16] Fix Dodge-the-Creeps project setup --- examples/dodge-the-creeps/godot/Main.tscn | 6 +++--- examples/dodge-the-creeps/godot/Mob.tscn | 13 +++++++------ examples/dodge-the-creeps/godot/Player.tscn | 9 +++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/dodge-the-creeps/godot/Main.tscn b/examples/dodge-the-creeps/godot/Main.tscn index bcd8a095d..09599b2b3 100644 --- a/examples/dodge-the-creeps/godot/Main.tscn +++ b/examples/dodge-the-creeps/godot/Main.tscn @@ -2,8 +2,8 @@ [ext_resource type="PackedScene" uid="uid://4vwrqjegqwpj" path="res://Player.tscn" id="3"] [ext_resource type="PackedScene" uid="uid://b6f427lco0mqk" path="res://Hud.tscn" id="4"] -[ext_resource type="AudioStream" uid="uid://q2pf4fr8d0ks" path="res://art/House In a Forest Loop.ogg" id="5"] -[ext_resource type="AudioStream" uid="uid://dw26fpygeag8o" path="res://art/gameover.wav" id="6"] +[ext_resource type="AudioStream" uid="uid://jy3fk3ujnkpl" path="res://art/House In a Forest Loop.ogg" id="5"] +[ext_resource type="AudioStream" uid="uid://cfb6f1t1p6axm" path="res://art/gameover.wav" id="6"] [sub_resource type="Curve2D" id="1"] _data = { @@ -52,4 +52,4 @@ stream = ExtResource("6") [connection signal="timeout" from="MobTimer" to="." method="on_mob_timer_timeout"] [connection signal="timeout" from="ScoreTimer" to="." method="on_score_timer_timeout"] [connection signal="timeout" from="StartTimer" to="." method="on_start_timer_timeout"] -[connection signal="start_game" from="Hud" to="." method="new_game" flags=3] \ No newline at end of file +[connection signal="start_game" from="Hud" to="." method="new_game" flags=3] diff --git a/examples/dodge-the-creeps/godot/Mob.tscn b/examples/dodge-the-creeps/godot/Mob.tscn index 8917e84fe..4dcb00e97 100644 --- a/examples/dodge-the-creeps/godot/Mob.tscn +++ b/examples/dodge-the-creeps/godot/Mob.tscn @@ -1,11 +1,11 @@ [gd_scene load_steps=9 format=3 uid="uid://rkdnhqgf2hpj"] -[ext_resource type="Texture2D" uid="uid://yqglrrsx7j1f" path="res://art/enemyFlyingAlt_1.png" id="2"] -[ext_resource type="Texture2D" uid="uid://bpot8awhdn6ph" path="res://art/enemyFlyingAlt_2.png" id="3"] -[ext_resource type="Texture2D" uid="uid://bu4221t7qpa7d" path="res://art/enemyWalking_1.png" id="4"] -[ext_resource type="Texture2D" uid="uid://booij5t7h4efb" path="res://art/enemyWalking_2.png" id="5"] -[ext_resource type="Texture2D" uid="uid://5lvm88ij4jqn" path="res://art/enemySwimming_1.png" id="6"] -[ext_resource type="Texture2D" uid="uid://bng45cvsgufqc" path="res://art/enemySwimming_2.png" id="7"] +[ext_resource type="Texture2D" uid="uid://ch2bfqohapw00" path="res://art/enemyFlyingAlt_1.png" id="2"] +[ext_resource type="Texture2D" uid="uid://uhy7j2necuj2" path="res://art/enemyFlyingAlt_2.png" id="3"] +[ext_resource type="Texture2D" uid="uid://dd56ab6ouwms7" path="res://art/enemyWalking_1.png" id="4"] +[ext_resource type="Texture2D" uid="uid://dulw5juue3ivn" path="res://art/enemyWalking_2.png" id="5"] +[ext_resource type="Texture2D" uid="uid://cnwoep5enlhrj" path="res://art/enemySwimming_1.png" id="6"] +[ext_resource type="Texture2D" uid="uid://eqshh2v5g0dn" path="res://art/enemySwimming_2.png" id="7"] [sub_resource type="SpriteFrames" id="1"] animations = [{ @@ -37,6 +37,7 @@ gravity_scale = 0.0 scale = Vector2(0.75, 0.75) frames = SubResource("1") animation = &"walk" +playing = true [node name="CollisionShape2D" type="CollisionShape2D" parent="."] rotation = 1.5708 diff --git a/examples/dodge-the-creeps/godot/Player.tscn b/examples/dodge-the-creeps/godot/Player.tscn index 3a7ef7fa1..222f857bf 100644 --- a/examples/dodge-the-creeps/godot/Player.tscn +++ b/examples/dodge-the-creeps/godot/Player.tscn @@ -1,9 +1,9 @@ [gd_scene load_steps=12 format=3 uid="uid://4vwrqjegqwpj"] -[ext_resource type="Texture2D" uid="uid://ftkxr8r4qghp" path="res://art/playerGrey_walk1.png" id="2"] -[ext_resource type="Texture2D" uid="uid://couyhcegeihme" path="res://art/playerGrey_walk2.png" id="3"] -[ext_resource type="Texture2D" uid="uid://b4yyoafu8bi0q" path="res://art/playerGrey_up1.png" id="4"] -[ext_resource type="Texture2D" uid="uid://bko65a0nd66st" path="res://art/playerGrey_up2.png" id="5"] +[ext_resource type="Texture2D" uid="uid://bei50n77gmlml" path="res://art/playerGrey_walk1.png" id="2"] +[ext_resource type="Texture2D" uid="uid://bdkoaa5a3c44c" path="res://art/playerGrey_walk2.png" id="3"] +[ext_resource type="Texture2D" uid="uid://qpg7fsh1wnd6" path="res://art/playerGrey_up1.png" id="4"] +[ext_resource type="Texture2D" uid="uid://c4vx8t665is8j" path="res://art/playerGrey_up2.png" id="5"] [sub_resource type="SpriteFrames" id="1"] animations = [{ @@ -41,6 +41,7 @@ scale_curve = SubResource("6") color_ramp = SubResource("4") [node name="Player" type="Player"] +visible = false z_index = 10 [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] From 36bbd66d1ecde742ad92df4639023babc4cff586 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 12 Dec 2022 22:02:21 +0100 Subject: [PATCH 15/16] CI: enable rustfmt, use ubuntu-20.04, increase timeout --- .github/workflows/minimal-ci.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 1731264c4..c64c05816 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -38,17 +38,17 @@ defaults: # cancel-in-progress: true jobs: -# rustfmt: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v3 -# - name: "Install Rust" -# uses: ./.github/composite/rust -# with: -# rust: stable -# components: rustfmt -# - name: "Check rustfmt" -# run: cargo fmt --all -- --check + rustfmt: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: "Install Rust" + uses: ./.github/composite/rust + with: + rust: stable + components: rustfmt + - name: "Check rustfmt" + run: cargo fmt --all -- --check unit-test: name: test (${{ matrix.name }}) @@ -106,7 +106,7 @@ jobs: name: itest-godot (${{ matrix.name }}) runs-on: ${{ matrix.os }} continue-on-error: false - timeout-minutes: 15 + timeout-minutes: 24 strategy: fail-fast: false # cancel all jobs as soon as one fails? matrix: @@ -142,7 +142,7 @@ jobs: #godot_ver: ${{ matrix.godot }} license-guard: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 if: github.ref != 'refs/heads/master' steps: - uses: actions/checkout@v3 From 7488b2ec4d857994892fb2601e0a4a3488dfdf83 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 12 Dec 2022 22:32:30 +0100 Subject: [PATCH 16/16] Fix Godot version regex parsing --- godot-codegen/src/godot_version.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/godot-codegen/src/godot_version.rs b/godot-codegen/src/godot_version.rs index 4e8914346..45f5f7b26 100644 --- a/godot-codegen/src/godot_version.rs +++ b/godot-codegen/src/godot_version.rs @@ -28,17 +28,11 @@ pub struct GodotVersion { pub fn parse_godot_version(version_str: &str) -> Result> { let regex = Regex::new( - r#"(\d+)\.(\d+)(?:\.(\d+))?\.(alpha|beta|dev|stable)[0-9]*\.(?:mono)\.(?:(?:official|custom_build)\.([a-f0-9]+)|official)"#, + r#"(\d+)\.(\d+)(?:\.(\d+))?\.(alpha|beta|dev|stable)[0-9]*\.(?:mono\.)?(?:(?:official|custom_build)\.([a-f0-9]+)|official)"#, )?; - let caps = regex.captures(version_str).ok_or("Regex capture failed")?; - - let fail = || { - format!( - "Version substring could not be matched in '{}'", - version_str - ) - }; + let fail = || format!("Version substring cannot be parsed: `{}`", version_str); + let caps = regex.captures(version_str).ok_or_else(fail)?; Ok(GodotVersion { full_string: caps.get(0).unwrap().as_str().to_string(),