Skip to content

Commit 565bf10

Browse files
bors[bot]lilizoey
andauthored
326: Fix UB related to engine enum layout r=Bromeon a=lilizoey `EngineEnum`s should be represented as 32-bit values, but also should be passed as i64 to/from godot in function parameters/return values. This fixes that by having engine enums not implement `GodotFfi` but instead only implements `GodotFuncMarshal`. To support this change, `GodotFuncMarshal` has been refactored: - `Via` now represents only the intermediate type, and must implement `GodotFfi` - Add error-types for when conversion fails - Add explicit conversion methods to/from `Via`, we can't just use `TryFrom` here since not all types we wanna use have the proper `TryFrom` conversions. like f32 and f64 With this change, a minor change was made to the class generator, such that all arguments are first converted to their `Via` type, before a pointer to them is made. And return pointers are now dereferenced to the `Via` type before they're converted to the proper type. This means that `GodotFuncMarshal` is now the type that represents whether something can be passed as an argument/return value to a ptrcall. `AsArg` was also removed and its method moved into `GodotFfi` as that simplifies codegen. (we can just always call `GodotFfi::as_arg_ptr` to create the pointer, rather than treating classes and other types differently). In the future we can try splitting up `GodotFfi` more, but i do think that `AsArg` belonged more in `godot_ffi` anyway. Co-authored-by: Lili Zoey <[email protected]>
2 parents 560ac01 + 753abf6 commit 565bf10

File tree

8 files changed

+153
-144
lines changed

8 files changed

+153
-144
lines changed

godot-codegen/src/class_generator.rs

+21-38
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
//! Generates a file for each Godot engine + builtin class
88
9-
use proc_macro2::{Ident, TokenStream};
9+
use proc_macro2::{Ident, Literal, TokenStream};
1010
use quote::{format_ident, quote, ToTokens};
1111
use std::path::Path;
1212

1313
use crate::central_generator::{collect_builtin_types, BuiltinTypeInfo};
1414
use crate::util::{
1515
ident, option_as_slice, parse_native_structures_format, safe_ident, to_pascal_case,
16-
to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, unmap_meta, NativeStructuresField,
16+
to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, NativeStructuresField,
1717
};
1818
use crate::{api_parser::*, SubmitFn};
1919
use crate::{
@@ -541,7 +541,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
541541
use crate::engine::notify::*;
542542
use crate::builtin::*;
543543
use crate::engine::native::*;
544-
use crate::obj::{AsArg, Gd};
544+
use crate::obj::Gd;
545545
use sys::GodotFfi as _;
546546
use std::ffi::c_void;
547547

@@ -787,7 +787,7 @@ fn make_builtin_class(
787787
use godot_ffi as sys;
788788
use crate::builtin::*;
789789
use crate::engine::native::*;
790-
use crate::obj::{AsArg, Gd};
790+
use crate::obj::Gd;
791791
use crate::sys::GodotFfi as _;
792792
use crate::engine::Object;
793793

@@ -829,7 +829,7 @@ fn make_native_structure(
829829
use godot_ffi as sys;
830830
use crate::builtin::*;
831831
use crate::engine::native::*;
832-
use crate::obj::{AsArg, Gd};
832+
use crate::obj::Gd;
833833
use crate::sys::GodotFfi as _;
834834
use crate::engine::Object;
835835

@@ -1334,7 +1334,7 @@ fn make_function_definition(sig: &FnSignature, code: &FnCode) -> FnDefinition {
13341334
};
13351335

13361336
let is_varcall = code.variant_ffi.is_some();
1337-
let [params, variant_types, arg_exprs, arg_names, meta_arg_decls] =
1337+
let [params, variant_types, arg_exprs, arg_names] =
13381338
make_params_and_impl(&sig.params, is_varcall, false);
13391339

13401340
let primary_fn_name = if has_default_params {
@@ -1349,6 +1349,8 @@ fn make_function_definition(sig: &FnSignature, code: &FnCode) -> FnDefinition {
13491349
(TokenStream::new(), TokenStream::new())
13501350
};
13511351

1352+
let args_indices = (0..arg_exprs.len()).map(Literal::usize_unsuffixed);
1353+
13521354
let (prepare_arg_types, error_fn_context);
13531355
if code.variant_ffi.is_some() {
13541356
// varcall (using varargs)
@@ -1437,9 +1439,13 @@ fn make_function_definition(sig: &FnSignature, code: &FnCode) -> FnDefinition {
14371439
unsafe {
14381440
#init_code
14391441

1440-
#( #meta_arg_decls )*
1442+
#[allow(clippy::let_unit_value)]
1443+
let __args = (
1444+
#( #arg_exprs, )*
1445+
);
1446+
14411447
let __args = [
1442-
#( #arg_exprs ),*
1448+
#( sys::GodotFfi::as_arg_ptr(&__args.#args_indices) ),*
14431449
];
14441450

14451451
let __args_ptr = __args.as_ptr();
@@ -1740,12 +1746,11 @@ fn make_params_and_impl(
17401746
method_args: &[FnParam],
17411747
is_varcall: bool,
17421748
skip_defaults: bool,
1743-
) -> [Vec<TokenStream>; 5] {
1749+
) -> [Vec<TokenStream>; 4] {
17441750
let mut params = vec![];
17451751
let mut variant_types = vec![];
17461752
let mut arg_exprs = vec![];
17471753
let mut arg_names = vec![];
1748-
let mut meta_arg_decls = vec![];
17491754

17501755
for param in method_args.iter() {
17511756
if skip_defaults && param.default_value.is_some() {
@@ -1754,36 +1759,22 @@ fn make_params_and_impl(
17541759

17551760
let param_name = &param.name;
17561761
let param_ty = &param.type_;
1757-
let canonical_ty = unmap_meta(param_ty);
17581762

17591763
let arg_expr = if is_varcall {
17601764
quote! { <#param_ty as ToVariant>::to_variant(&#param_name) }
17611765
} else if let RustTy::EngineClass { tokens: path, .. } = &param_ty {
1762-
quote! { <#path as AsArg>::as_arg_ptr(&#param_name) }
1763-
} else {
1764-
let param_ty = if let Some(canonical_ty) = canonical_ty.as_ref() {
1765-
canonical_ty.to_token_stream()
1766-
} else {
1767-
param_ty.to_token_stream()
1768-
};
1769-
quote! { <#param_ty as sys::GodotFfi>::sys_const(&#param_name) }
1770-
};
1771-
1772-
// Note: could maybe reuse GodotFuncMarshal in the future
1773-
let meta_arg_decl = if let Some(canonical) = canonical_ty {
1774-
quote! { let #param_name = #param_name as #canonical; }
1766+
quote! { <#path as sys::GodotFuncMarshal>::try_into_via(#param_name).unwrap() }
17751767
} else {
1776-
quote! {}
1768+
quote! { <#param_ty as sys::GodotFuncMarshal>::try_into_via(#param_name).unwrap() }
17771769
};
17781770

17791771
params.push(quote! { #param_name: #param_ty });
17801772
variant_types.push(quote! { <#param_ty as VariantMetadata>::variant_type() });
17811773
arg_exprs.push(arg_expr);
17821774
arg_names.push(quote! { #param_name });
1783-
meta_arg_decls.push(meta_arg_decl);
17841775
}
17851776

1786-
[params, variant_types, arg_exprs, arg_names, meta_arg_decls]
1777+
[params, variant_types, arg_exprs, arg_names]
17871778
}
17881779

17891780
fn make_params_and_args(method_args: &[&FnParam]) -> (Vec<TokenStream>, Vec<TokenStream>) {
@@ -1867,19 +1858,11 @@ fn make_return_and_impl(
18671858
}
18681859
}
18691860
(false, None, Some(return_ty)) => {
1870-
let (ffi_ty, conversion);
1871-
if let Some(canonical_ty) = unmap_meta(return_ty) {
1872-
ffi_ty = canonical_ty.to_token_stream();
1873-
conversion = quote! { as #return_ty };
1874-
} else {
1875-
ffi_ty = return_ty.to_token_stream();
1876-
conversion = quote! {};
1877-
};
1878-
18791861
quote! {
1880-
<#ffi_ty as sys::GodotFfi>::from_sys_init_default(|return_ptr| {
1862+
let via = <<#return_ty as sys::GodotFuncMarshal>::Via as sys::GodotFfi>::from_sys_init_default(|return_ptr| {
18811863
#ptrcall_invocation
1882-
}) #conversion
1864+
});
1865+
<#return_ty as sys::GodotFuncMarshal>::try_from_via(via).unwrap()
18831866
}
18841867
}
18851868
(false, None, None) => {

godot-codegen/src/util.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,21 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
110110
self.ord
111111
}
112112
}
113-
// SAFETY:
114-
// The enums are transparently represented as an `i32`, so `*mut Self` is sound.
115-
unsafe impl sys::GodotFfi for #enum_name {
116-
sys::ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
113+
114+
impl sys::GodotFuncMarshal for #enum_name {
115+
type Via = i64;
116+
type FromViaError = sys::PrimitiveConversionError<i64, i32>;
117+
type IntoViaError = std::convert::Infallible;
118+
119+
fn try_from_via(via: Self::Via) -> std::result::Result<Self, Self::FromViaError> {
120+
let err = sys::PrimitiveConversionError::new(via);
121+
let ord = i32::try_from(via).map_err(|_| err)?;
122+
<Self as crate::obj::EngineEnum>::try_from_ord(ord).ok_or(err)
123+
}
124+
125+
fn try_into_via(self) -> std::result::Result<Self::Via, Self::IntoViaError> {
126+
Ok(<Self as crate::obj::EngineEnum>::ord(self).into())
127+
}
117128
}
118129
#bitfield_ops
119130
}

godot-core/src/builtin/callable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use godot_ffi as sys;
99
use crate::builtin::{inner, ToVariant, Variant};
1010
use crate::engine::Object;
1111
use crate::obj::mem::Memory;
12-
use crate::obj::{AsArg, Gd, GodotClass, InstanceId};
12+
use crate::obj::{Gd, GodotClass, InstanceId};
1313
use std::fmt;
1414
use sys::{ffi_methods, GodotFfi};
1515

godot-core/src/obj/as_arg.rs

-49
This file was deleted.

godot-core/src/obj/gd.rs

+18
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,24 @@ where
577577
// We've passed ownership to caller.
578578
std::mem::forget(self);
579579
}
580+
581+
fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr {
582+
// We're passing a reference to the object to the callee. If the reference count needs to be
583+
// incremented then the callee will do so. We do not need to prematurely do so.
584+
//
585+
// In Rust terms, if `T` is refcounted then we are effectively passing a `&Arc<T>`, and the callee
586+
// would need to call `.clone()` if desired.
587+
588+
// In 4.0, argument pointers are passed to godot as `T*`, except for in virtual method calls. We
589+
// can't perform virtual method calls currently, so they are always `T*`.
590+
//
591+
// In 4.1 argument pointers were standardized to always be `T**`.
592+
if cfg!(gdextension_api = "4.0") {
593+
self.sys_const()
594+
} else {
595+
std::ptr::addr_of!(self.opaque) as sys::GDExtensionConstTypePtr
596+
}
597+
}
580598
}
581599

582600
// SAFETY:

godot-core/src/obj/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010
//! * [`GodotClass`], which is implemented for every class that Godot can work with (either engine- or user-provided).
1111
//! * [`Gd`], a smart pointer that manages instances of Godot classes.
1212
13-
mod as_arg;
1413
mod base;
1514
mod gd;
1615
mod guards;
1716
mod instance_id;
1817
mod traits;
1918

20-
pub use as_arg::*;
2119
pub use base::*;
2220
pub use gd::*;
2321
pub use guards::*;

0 commit comments

Comments
 (0)