Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged] Fix UB related to engine enum layout #326

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 21 additions & 38 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

//! Generates a file for each Godot engine + builtin class

use proc_macro2::{Ident, TokenStream};
use proc_macro2::{Ident, Literal, TokenStream};
use quote::{format_ident, quote, ToTokens};
use std::path::Path;

use crate::central_generator::{collect_builtin_types, BuiltinTypeInfo};
use crate::util::{
ident, option_as_slice, parse_native_structures_format, safe_ident, to_pascal_case,
to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, unmap_meta, NativeStructuresField,
to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, NativeStructuresField,
};
use crate::{api_parser::*, SubmitFn};
use crate::{
Expand Down Expand Up @@ -541,7 +541,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
use crate::engine::notify::*;
use crate::builtin::*;
use crate::engine::native::*;
use crate::obj::{AsArg, Gd};
use crate::obj::Gd;
use sys::GodotFfi as _;
use std::ffi::c_void;

Expand Down Expand Up @@ -787,7 +787,7 @@ fn make_builtin_class(
use godot_ffi as sys;
use crate::builtin::*;
use crate::engine::native::*;
use crate::obj::{AsArg, Gd};
use crate::obj::Gd;
use crate::sys::GodotFfi as _;
use crate::engine::Object;

Expand Down Expand Up @@ -829,7 +829,7 @@ fn make_native_structure(
use godot_ffi as sys;
use crate::builtin::*;
use crate::engine::native::*;
use crate::obj::{AsArg, Gd};
use crate::obj::Gd;
use crate::sys::GodotFfi as _;
use crate::engine::Object;

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

let is_varcall = code.variant_ffi.is_some();
let [params, variant_types, arg_exprs, arg_names, meta_arg_decls] =
let [params, variant_types, arg_exprs, arg_names] =
make_params_and_impl(&sig.params, is_varcall, false);

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

let args_indices = (0..arg_exprs.len()).map(Literal::usize_unsuffixed);

let (prepare_arg_types, error_fn_context);
if code.variant_ffi.is_some() {
// varcall (using varargs)
Expand Down Expand Up @@ -1437,9 +1439,13 @@ fn make_function_definition(sig: &FnSignature, code: &FnCode) -> FnDefinition {
unsafe {
#init_code

#( #meta_arg_decls )*
#[allow(clippy::let_unit_value)]
let __args = (
#( #arg_exprs, )*
);

let __args = [
#( #arg_exprs ),*
#( sys::GodotFfi::as_arg_ptr(&__args.#args_indices) ),*
];

let __args_ptr = __args.as_ptr();
Expand Down Expand Up @@ -1740,12 +1746,11 @@ fn make_params_and_impl(
method_args: &[FnParam],
is_varcall: bool,
skip_defaults: bool,
) -> [Vec<TokenStream>; 5] {
) -> [Vec<TokenStream>; 4] {
let mut params = vec![];
let mut variant_types = vec![];
let mut arg_exprs = vec![];
let mut arg_names = vec![];
let mut meta_arg_decls = vec![];

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

let param_name = &param.name;
let param_ty = &param.type_;
let canonical_ty = unmap_meta(param_ty);

let arg_expr = if is_varcall {
quote! { <#param_ty as ToVariant>::to_variant(&#param_name) }
} else if let RustTy::EngineClass { tokens: path, .. } = &param_ty {
quote! { <#path as AsArg>::as_arg_ptr(&#param_name) }
} else {
let param_ty = if let Some(canonical_ty) = canonical_ty.as_ref() {
canonical_ty.to_token_stream()
} else {
param_ty.to_token_stream()
};
quote! { <#param_ty as sys::GodotFfi>::sys_const(&#param_name) }
};

// Note: could maybe reuse GodotFuncMarshal in the future
let meta_arg_decl = if let Some(canonical) = canonical_ty {
quote! { let #param_name = #param_name as #canonical; }
quote! { <#path as sys::GodotFuncMarshal>::try_into_via(#param_name).unwrap() }
} else {
quote! {}
quote! { <#param_ty as sys::GodotFuncMarshal>::try_into_via(#param_name).unwrap() }
};

params.push(quote! { #param_name: #param_ty });
variant_types.push(quote! { <#param_ty as VariantMetadata>::variant_type() });
arg_exprs.push(arg_expr);
arg_names.push(quote! { #param_name });
meta_arg_decls.push(meta_arg_decl);
}

[params, variant_types, arg_exprs, arg_names, meta_arg_decls]
[params, variant_types, arg_exprs, arg_names]
}

fn make_params_and_args(method_args: &[&FnParam]) -> (Vec<TokenStream>, Vec<TokenStream>) {
Expand Down Expand Up @@ -1867,19 +1858,11 @@ fn make_return_and_impl(
}
}
(false, None, Some(return_ty)) => {
let (ffi_ty, conversion);
if let Some(canonical_ty) = unmap_meta(return_ty) {
ffi_ty = canonical_ty.to_token_stream();
conversion = quote! { as #return_ty };
} else {
ffi_ty = return_ty.to_token_stream();
conversion = quote! {};
};

quote! {
<#ffi_ty as sys::GodotFfi>::from_sys_init_default(|return_ptr| {
let via = <<#return_ty as sys::GodotFuncMarshal>::Via as sys::GodotFfi>::from_sys_init_default(|return_ptr| {
#ptrcall_invocation
}) #conversion
});
<#return_ty as sys::GodotFuncMarshal>::try_from_via(via).unwrap()
}
}
(false, None, None) => {
Expand Down
19 changes: 15 additions & 4 deletions godot-codegen/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,21 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
self.ord
}
}
// SAFETY:
// The enums are transparently represented as an `i32`, so `*mut Self` is sound.
unsafe impl sys::GodotFfi for #enum_name {
sys::ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }

impl sys::GodotFuncMarshal for #enum_name {
type Via = i64;
type FromViaError = sys::PrimitiveConversionError<i64, i32>;
type IntoViaError = std::convert::Infallible;

fn try_from_via(via: Self::Via) -> std::result::Result<Self, Self::FromViaError> {
let err = sys::PrimitiveConversionError::new(via);
let ord = i32::try_from(via).map_err(|_| err)?;
<Self as crate::obj::EngineEnum>::try_from_ord(ord).ok_or(err)
}

fn try_into_via(self) -> std::result::Result<Self::Via, Self::IntoViaError> {
Ok(<Self as crate::obj::EngineEnum>::ord(self).into())
}
}
#bitfield_ops
}
Expand Down
2 changes: 1 addition & 1 deletion godot-core/src/builtin/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use godot_ffi as sys;
use crate::builtin::{inner, ToVariant, Variant};
use crate::engine::Object;
use crate::obj::mem::Memory;
use crate::obj::{AsArg, Gd, GodotClass, InstanceId};
use crate::obj::{Gd, GodotClass, InstanceId};
use std::fmt;
use sys::{ffi_methods, GodotFfi};

Expand Down
49 changes: 0 additions & 49 deletions godot-core/src/obj/as_arg.rs

This file was deleted.

18 changes: 18 additions & 0 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,24 @@ where
// We've passed ownership to caller.
std::mem::forget(self);
}

fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr {
// We're passing a reference to the object to the callee. If the reference count needs to be
// incremented then the callee will do so. We do not need to prematurely do so.
//
// In Rust terms, if `T` is refcounted then we are effectively passing a `&Arc<T>`, and the callee
// would need to call `.clone()` if desired.

// In 4.0, argument pointers are passed to godot as `T*`, except for in virtual method calls. We
// can't perform virtual method calls currently, so they are always `T*`.
//
// In 4.1 argument pointers were standardized to always be `T**`.
if cfg!(gdextension_api = "4.0") {
self.sys_const()
} else {
std::ptr::addr_of!(self.opaque) as sys::GDExtensionConstTypePtr
}
}
}

// SAFETY:
Expand Down
2 changes: 0 additions & 2 deletions godot-core/src/obj/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
//! * [`GodotClass`], which is implemented for every class that Godot can work with (either engine- or user-provided).
//! * [`Gd`], a smart pointer that manages instances of Godot classes.

mod as_arg;
mod base;
mod gd;
mod guards;
mod instance_id;
mod traits;

pub use as_arg::*;
pub use base::*;
pub use gd::*;
pub use guards::*;
Expand Down
Loading