Skip to content

Commit 13923ef

Browse files
committed
rename builtin hash -> hash_u32; added tests
1 parent 4a58210 commit 13923ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1133
-364
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ which = "7"
3939
# * quote: 1.0.37 allows tokenizing CStr.
4040
# * venial: 0.6.1 contains some bugfixes.
4141
heck = "0.5"
42-
litrs = "0.4"
42+
litrs = { version = "1.0", features = ["proc-macro2"] }
4343
markdown = "=1.0.0-alpha.23"
4444
nanoserde = "0.2"
4545
proc-macro2 = "1.0.80"

godot-codegen/src/context.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,14 @@ impl<'a> Context<'a> {
252252
.unwrap_or_else(|| panic!("did not register table index for key {key:?}"))
253253
}
254254

255+
/// Yields cached sys pointer types – various pointer types declared in `gdextension_interface`
256+
/// and used as parameters in exposed Godot APIs.
257+
pub fn cached_sys_pointer_types(&self) -> impl Iterator<Item = &RustTy> {
258+
self.cached_rust_types
259+
.values()
260+
.filter(|rust_ty| rust_ty.is_sys_pointer())
261+
}
262+
255263
/// Whether an interface trait is generated for a class.
256264
///
257265
/// False if the class is "Godot-abstract"/final, thus there are no virtual functions to inherit.

godot-codegen/src/conv/type_conversions.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ fn to_rust_type_uncached(full_ty: &GodotTy, ctx: &mut Context) -> RustTy {
170170
ty = ty.replace("const ", "");
171171
}
172172

173+
// Sys pointer type defined in `gdextension_interface` and used as param for given method, e.g. `GDExtensionInitializationFunction`.
174+
// Note: we branch here to avoid clashes with actual GDExtension classes.
175+
if ty.starts_with("GDExtension") {
176+
let ty = rustify_ty(&ty);
177+
return RustTy::RawPointer {
178+
inner: Box::new(RustTy::SysPointerType {
179+
tokens: quote! { sys::#ty },
180+
}),
181+
is_const,
182+
};
183+
}
184+
173185
// .trim() is necessary here, as Godot places a space between a type and the stars when representing a double pointer.
174186
// Example: "int*" but "int **".
175187
let inner_type = to_rust_type(ty.trim(), None, ctx);

godot-codegen/src/generator/builtins.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,27 @@ fn make_special_builtin_methods(class_name: &TyName, _ctx: &Context) -> TokenStr
197197

198198
/// Get the safety docs of an unsafe method, or `None` if it is safe.
199199
fn method_safety_doc(class_name: &TyName, method: &BuiltinMethod) -> Option<TokenStream> {
200-
if class_name.godot_ty == "Array"
201-
&& &method.return_value().type_tokens().to_string() == "VariantArray"
202-
{
203-
return Some(quote! {
204-
/// # Safety
205-
///
206-
/// You must ensure that the returned array fulfils the safety invariants of [`Array`](crate::builtin::Array).
207-
});
200+
if class_name.godot_ty == "Array" {
201+
if method.is_generic() {
202+
return Some(quote! {
203+
/// # Safety
204+
/// You must ensure that the returned array fulfils the safety invariants of [`Array`](crate::builtin::Array), this being:
205+
/// - Any values written to the array must match the runtime type of the array.
206+
/// - Any values read from the array must be convertible to the type `T`.
207+
///
208+
/// If the safety invariant of `Array` is intact, which it must be for any publicly accessible arrays, then `T` must match
209+
/// the runtime type of the array. This then implies that both of the conditions above hold. This means that you only need
210+
/// to keep the above conditions in mind if you are intentionally violating the safety invariant of `Array`.
211+
///
212+
/// In the current implementation, both cases will produce a panic rather than undefined behavior, but this should not be relied upon.
213+
});
214+
} else if &method.return_value().type_tokens().to_string() == "VariantArray" {
215+
return Some(quote! {
216+
/// # Safety
217+
///
218+
/// You must ensure that the returned array fulfils the safety invariants of [`Array`](crate::builtin::Array).
219+
});
220+
}
208221
}
209222

210223
None
@@ -252,11 +265,13 @@ fn make_builtin_method_definition(
252265
let receiver = functions_common::make_receiver(method.qualifier(), ffi_arg_in);
253266
let object_ptr = &receiver.ffi_arg;
254267

268+
let maybe_generic_params = method.return_value().generic_params();
269+
255270
let ptrcall_invocation = quote! {
256271
let method_bind = sys::builtin_method_table().#fptr_access;
257272

258273

259-
Signature::<CallParams, CallRet>::out_builtin_ptrcall(
274+
Signature::<CallParams, CallRet #maybe_generic_params>::out_builtin_ptrcall(
260275
method_bind,
261276
#builtin_name_str,
262277
#method_name_str,

godot-codegen/src/generator/central_files.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use quote::{format_ident, quote, ToTokens};
1010

1111
use crate::context::Context;
1212
use crate::conv;
13+
use crate::generator::sys_pointer_types::make_godotconvert_for_systypes;
1314
use crate::generator::{enums, gdext_build_struct};
1415
use crate::models::domain::ExtensionApi;
1516
use crate::util::ident;
@@ -60,6 +61,7 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
6061

6162
let (global_enum_defs, global_reexported_enum_defs) = make_global_enums(api);
6263
let variant_type_traits = make_variant_type_enum(api, false);
64+
let sys_types_godotconvert_impl = make_godotconvert_for_systypes(ctx);
6365

6466
// TODO impl Clone, Debug, PartialEq, PartialOrd, Hash for VariantDispatch
6567
// TODO could use try_to().unwrap_unchecked(), since type is already verified. Also directly overload from_variant().
@@ -121,6 +123,11 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
121123
use crate::sys;
122124
#( #global_reexported_enum_defs )*
123125
}
126+
127+
pub mod sys_pointer_types {
128+
use crate::sys;
129+
#( #sys_types_godotconvert_impl )*
130+
}
124131
}
125132
}
126133

godot-codegen/src/generator/default_parameters.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use quote::{format_ident, quote};
1111

1212
use crate::generator::functions_common;
1313
use crate::generator::functions_common::{
14-
make_arg_expr, make_param_or_field_type, FnArgExpr, FnCode, FnKind, FnParamDecl, FnParamTokens,
14+
make_arg_expr, make_param_or_field_type, FnArgExpr, FnCode, FnKind, FnParamDecl,
1515
};
1616
use crate::models::domain::{FnParam, FnQualifier, Function, RustTy, TyName};
1717
use crate::util::{ident, safe_ident};
@@ -59,15 +59,6 @@ pub fn make_function_definition_with_defaults(
5959
&default_fn_params,
6060
);
6161

62-
// ExBuilder::new() constructor signature.
63-
let FnParamTokens {
64-
func_general_lifetime: simple_fn_lifetime,
65-
..
66-
} = fns::make_params_exprs(
67-
required_fn_params.iter().cloned(),
68-
FnKind::ExBuilderConstructor,
69-
);
70-
7162
let return_decl = &sig.return_value().decl;
7263

7364
// If either the builder has a lifetime (non-static/global method), or one of its parameters is a reference,
@@ -119,7 +110,7 @@ pub fn make_function_definition_with_defaults(
119110
// Lifetime is set if any parameter is a reference.
120111
#[doc = #default_parameter_usage]
121112
#[inline]
122-
#vis fn #simple_fn_name #simple_fn_lifetime (
113+
#vis fn #simple_fn_name (
123114
#simple_receiver_param
124115
#( #class_method_required_params, )*
125116
) #return_decl {

godot-codegen/src/generator/functions_common.rs

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ pub struct FnParamTokens {
9898
/// Generic argument list `<'a0, 'a1, ...>` after `type CallSig`, if available.
9999
pub callsig_lifetime_args: Option<TokenStream>,
100100
pub arg_exprs: Vec<TokenStream>,
101-
pub func_general_lifetime: Option<TokenStream>,
102101
}
103102

104103
pub fn make_function_definition(
@@ -142,7 +141,6 @@ pub fn make_function_definition(
142141
callsig_param_types: param_types,
143142
callsig_lifetime_args,
144143
arg_exprs: arg_names,
145-
func_general_lifetime: fn_lifetime,
146144
} = if sig.is_virtual() {
147145
make_params_exprs_virtual(sig.params().iter(), sig)
148146
} else {
@@ -175,11 +173,14 @@ pub fn make_function_definition(
175173
default_structs_code = TokenStream::new();
176174
};
177175

176+
let maybe_func_generic_params = sig.return_value().generic_params();
177+
let maybe_func_generic_bounds = sig.return_value().where_clause();
178+
178179
let call_sig_decl = {
179180
let return_ty = &sig.return_value().type_tokens();
180181

181182
quote! {
182-
type CallRet = #return_ty;
183+
type CallRet #maybe_func_generic_params = #return_ty;
183184
type CallParams #callsig_lifetime_args = (#(#param_types,)*);
184185
}
185186
};
@@ -279,10 +280,12 @@ pub fn make_function_definition(
279280

280281
quote! {
281282
#maybe_safety_doc
282-
#vis #maybe_unsafe fn #primary_fn_name #fn_lifetime (
283+
#vis #maybe_unsafe fn #primary_fn_name #maybe_func_generic_params (
283284
#receiver_param
284285
#( #params, )*
285-
) #return_decl {
286+
) #return_decl
287+
#maybe_func_generic_bounds
288+
{
286289
#call_sig_decl
287290

288291
let args = (#( #arg_names, )*);
@@ -357,10 +360,7 @@ pub(crate) enum FnKind {
357360
/// `call()` forwarding to `try_call()`.
358361
DelegateTry,
359362

360-
/// Default extender `new()` associated function -- optional receiver and required parameters.
361-
ExBuilderConstructor,
362-
363-
/// Same as [`ExBuilderConstructor`], but for a builder with an explicit lifetime.
363+
/// Default extender `new()` associated function -- optional receiver and required parameters. Has an explicit lifetime.
364364
ExBuilderConstructorLifetimed,
365365

366366
/// Default extender `new()` associated function -- only default parameters.
@@ -489,6 +489,7 @@ pub(crate) fn make_param_or_field_type(
489489
..
490490
}
491491
| RustTy::BuiltinArray { .. }
492+
| RustTy::GenericArray
492493
| RustTy::EngineArray { .. } => {
493494
let lft = lifetimes.next();
494495
special_ty = Some(quote! { RefArg<#lft, #ty> });
@@ -572,7 +573,6 @@ pub(crate) fn make_params_exprs<'a>(
572573
// Methods relevant in the context of default parameters. Flow in this order.
573574
// Note that for builder methods of Ex* structs, there's a direct call in default_parameters.rs to the parameter manipulation methods,
574575
// bypassing this method. So one case is missing here.
575-
FnKind::ExBuilderConstructor => (FnParamDecl::FnPublic, FnArgExpr::StoreInField),
576576
FnKind::ExBuilderConstructorLifetimed => {
577577
(FnParamDecl::FnPublicLifetime, FnArgExpr::StoreInField)
578578
}
@@ -603,6 +603,32 @@ pub(crate) fn make_params_exprs<'a>(
603603
ret
604604
}
605605

606+
/// Returns the type for a virtual method parameter.
607+
///
608+
/// Generates `Option<Gd<T>>` instead of `Gd<T>` for object parameters (which are currently all nullable).
609+
///
610+
/// Used for consistency between virtual trait definitions and `type Sig = ...` type-safety declarations
611+
/// (which are used to improve compile-time errors on mismatch).
612+
pub(crate) fn make_virtual_param_type(
613+
param_ty: &RustTy,
614+
param_name: &Ident,
615+
function_sig: &dyn Function,
616+
) -> TokenStream {
617+
match param_ty {
618+
// Virtual methods accept Option<Gd<T>>, since we don't know whether objects are nullable or required.
619+
RustTy::EngineClass { .. }
620+
if !special_cases::is_class_method_param_required(
621+
function_sig.surrounding_class().unwrap(),
622+
function_sig.godot_name(),
623+
param_name,
624+
) =>
625+
{
626+
quote! { Option<#param_ty> }
627+
}
628+
_ => quote! { #param_ty },
629+
}
630+
}
631+
606632
/// For virtual functions, returns the parameter declarations, type tokens, and names.
607633
pub(crate) fn make_params_exprs_virtual<'a>(
608634
method_args: impl Iterator<Item = &'a FnParam>,
@@ -614,30 +640,13 @@ pub(crate) fn make_params_exprs_virtual<'a>(
614640
let param_name = &param.name;
615641
let param_ty = &param.type_;
616642

617-
match &param.type_ {
618-
// Virtual methods accept Option<Gd<T>>, since we don't know whether objects are nullable or required.
619-
RustTy::EngineClass { .. }
620-
if !special_cases::is_class_method_param_required(
621-
function_sig.surrounding_class().unwrap(),
622-
function_sig.godot_name(),
623-
param_name,
624-
) =>
625-
{
626-
ret.param_decls
627-
.push(quote! { #param_name: Option<#param_ty> });
628-
ret.arg_exprs.push(quote! { #param_name });
629-
ret.callsig_param_types.push(quote! { #param_ty });
630-
}
643+
// Map parameter types (e.g. virtual functions need Option<Gd> instead of Gd).
644+
let param_ty_tokens = make_virtual_param_type(param_ty, param_name, function_sig);
631645

632-
// All other methods and parameter types: standard handling.
633-
// For now, virtual methods always receive their parameter by value.
634-
//_ => ret.push_regular(param_name, param_ty, true, false, false),
635-
_ => {
636-
ret.param_decls.push(quote! { #param_name: #param_ty });
637-
ret.arg_exprs.push(quote! { #param_name });
638-
ret.callsig_param_types.push(quote! { #param_ty });
639-
}
640-
}
646+
ret.param_decls
647+
.push(quote! { #param_name: #param_ty_tokens });
648+
ret.arg_exprs.push(quote! { #param_name });
649+
ret.callsig_param_types.push(quote! { #param_ty });
641650
}
642651

643652
ret

godot-codegen/src/generator/mod.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ pub mod method_tables;
2828
pub mod native_structures;
2929
pub mod notifications;
3030
pub mod signals;
31+
pub mod sys_pointer_types;
3132
pub mod utility_functions;
32-
pub mod virtual_definition_consts;
33+
pub mod virtual_definitions;
3334
pub mod virtual_traits;
3435

3536
// ----------------------------------------------------------------------------------------------------------------------------------------------
@@ -56,7 +57,6 @@ pub fn generate_sys_module_file(sys_gen_path: &Path, submit_fn: &mut SubmitFn) {
5657
pub mod central;
5758
pub mod gdextension_interface;
5859
pub mod interface;
59-
pub mod virtual_consts;
6060
};
6161

6262
submit_fn(sys_gen_path.join("mod.rs"), code);
@@ -86,12 +86,6 @@ pub fn generate_sys_classes_file(
8686
submit_fn(sys_gen_path.join(filename), code);
8787
watch.record(format!("generate_classes_{}_file", api_level.lower()));
8888
}
89-
90-
// From 4.4 onward, generate table that maps all virtual methods to their known hashes.
91-
// This allows Godot to fall back to an older compatibility function if one is not supported.
92-
let code = virtual_definition_consts::make_virtual_consts_file(api, ctx);
93-
submit_fn(sys_gen_path.join("virtual_consts.rs"), code);
94-
watch.record("generate_virtual_consts_file");
9589
}
9690

9791
pub fn generate_sys_utilities_file(
@@ -131,6 +125,7 @@ pub fn generate_core_mod_file(gen_path: &Path, submit_fn: &mut SubmitFn) {
131125
pub mod builtin_classes;
132126
pub mod utilities;
133127
pub mod native;
128+
pub mod virtuals;
134129
};
135130

136131
submit_fn(gen_path.join("mod.rs"), code);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use proc_macro2::TokenStream;
9+
use quote::quote;
10+
11+
use crate::context::Context;
12+
13+
/// Creates `GodotConvert`, `ToGodot` and `FromGodot` impl
14+
/// for SysPointerTypes – various pointer types declared in `gdextension_interface`
15+
/// and used as parameters in exposed Godot APIs.
16+
pub fn make_godotconvert_for_systypes(ctx: &mut Context) -> Vec<TokenStream> {
17+
ctx.cached_sys_pointer_types().map(|sys_pointer_type| {
18+
quote! {
19+
impl crate::meta::GodotConvert for #sys_pointer_type {
20+
type Via = i64;
21+
}
22+
23+
impl crate::meta::ToGodot for #sys_pointer_type {
24+
type Pass = crate::meta::ByValue;
25+
fn to_godot(&self) -> Self::Via {
26+
*self as i64
27+
}
28+
}
29+
30+
impl crate::meta::FromGodot for #sys_pointer_type {
31+
fn try_from_godot(via: Self::Via) -> Result <Self, crate::meta::error::ConvertError> {
32+
Ok(via as Self)
33+
}
34+
}
35+
}
36+
}).collect()
37+
}

0 commit comments

Comments
 (0)