Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
More tests, more memory leak fixes, etc
  • Loading branch information
Watermelon914 committed Dec 7, 2025
commit 887ad58dbd38f2fe3e7027d45a43d6754d7faaba
15 changes: 6 additions & 9 deletions flecs_ecs/src/core/utility/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,6 @@ pub(crate) fn set_helper<T: ComponentId>(
//
// For that reason, we could use `ecs_emplace_id`, but then on_replace hooks would not
// be registerable on any of our components.
//
// The set pipeline for default constructed components works like so if deferred:
// Default constructed -> Dropped -> Moved into from value
// This code could be significantly more simple if ecs_emplace_id worked with `on_replace`
// hooks, but that will require modifications to flecs.c, so this will have to suffice for now.
let res = sys::ecs_cpp_set(
Expand All @@ -395,18 +392,18 @@ pub(crate) fn set_helper<T: ComponentId>(
);

let comp = res.ptr as *mut T;
// The set pipeline for default constructed components works like so:
// Default constructed -> Dropped -> Moved into from value
// Default construction happens in sys::ecs_cpp_set.
core::ptr::drop_in_place(comp);
core::ptr::write(comp, value);

if res.call_modified {
sys::ecs_modified_id(world, entity, id);
}
} else {
// If T does not impl default, we need to construct the data ourselves.
// The reason we don't do this with T::IMPLS_DEFAULT is because ecs_emplace_id
// does not work with on_replace hooks and will assert.
//
// This creates a requirement that any component that implements an on_replace hook
// must have a Default constructor.
// If T does not impl Default, we need to construct the data ourselves.
// so that uninitialized data is not dropped.
let mut changed: bool = false;

let comp = sys::ecs_emplace_id(
Expand Down
2 changes: 1 addition & 1 deletion flecs_ecs/src/core/world/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::ptr::NonNull;
use flecs_ecs_sys as sys;

use crate::core::{
FlecsArray, FlecsIdMap, QueryBuilderImpl, SystemAPI, WorldCtx, ecs_os_api, flecs
FlecsArray, FlecsIdMap, QueryBuilderImpl, SystemAPI, WorldCtx, ecs_os_api, flecs, has_default_hook
};

/// The `World` is the container for all ECS data. It stores the entities and
Expand Down
19 changes: 19 additions & 0 deletions flecs_ecs/tests/flecs/component_lifecycle_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ fn component_lifecycle_drop_on_world_delete() {
world.progress();
}

let init_boxes = INITIALIZED_BOXES.lock().unwrap();
assert!(
init_boxes.is_empty(),
"Leaked memory, objects not properly deleted: {:?}",
init_boxes
);
}

#[test]
fn component_lifecycle_set_multiple_times() {
let world = World::new();

let ent = world
.entity_named("object 1")
.set(BoxedNumber::new("Object 1"));
ent.set(BoxedNumber::new("Object 2"));
ent.set(BoxedNumber::new("Object 3"));
ent.destruct();

let init_boxes = INITIALIZED_BOXES.lock().unwrap();
assert!(
init_boxes.is_empty(),
Expand Down