From a990162fc0e86dfa1a83d262ee9a9dd70925fc35 Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Fri, 12 May 2023 14:35:21 -0700 Subject: [PATCH] Fix crash when using change detection with queries that have no this terms --- flecs.c | 50 +++++++++++++++++++++++++++---------------- src/query.c | 46 +++++++++++++++++++++++---------------- src/table.c | 4 +++- test/api/project.json | 1 + test/api/src/Query.c | 39 +++++++++++++++++++++++++++++++++ test/api/src/main.c | 7 +++++- 6 files changed, 108 insertions(+), 39 deletions(-) diff --git a/flecs.c b/flecs.c index 92e08f614..39f680c18 100644 --- a/flecs.c +++ b/flecs.c @@ -5061,7 +5061,9 @@ void flecs_table_replace_data( int32_t* flecs_table_get_dirty_state( ecs_world_t *world, ecs_table_t *table) -{ +{ + ecs_poly_assert(world, ecs_world_t); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); if (!table->dirty_state) { int32_t column_count = table->storage_count; table->dirty_state = flecs_alloc_n(&world->allocator, @@ -53677,12 +53679,14 @@ void flecs_query_sync_match_monitor( int32_t *monitor = match->monitor; ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - table_dirty_state_t cur; - - monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ + } + table_dirty_state_t cur; int32_t i, term_count = query->filter.term_count; for (i = 0; i < term_count; i ++) { int32_t t = query->filter.terms[i].field_index; @@ -53727,20 +53731,24 @@ bool flecs_query_check_match_monitor_term( } int32_t *monitor = match->monitor; - ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - table_dirty_state_t cur; - int32_t state = monitor[term]; if (state == -1) { return false; } - if (!term) { - return monitor[0] != dirty_state[0]; + ecs_table_t *table = match->node.table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (!term) { + return monitor[0] != dirty_state[0]; + } + } else if (!term) { + return false; } + table_dirty_state_t cur; flecs_query_get_dirty_state(query, match, term - 1, &cur); ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); @@ -53762,14 +53770,17 @@ bool flecs_query_check_match_monitor( int32_t *monitor = match->monitor; ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - bool has_flat = false; - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - - if (monitor[0] != dirty_state[0]) { - return true; + int32_t *dirty_state = NULL; + if (table) { + dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (monitor[0] != dirty_state[0]) { + return true; + } } + bool has_flat = false; ecs_world_t *world = query->filter.world; int32_t i, field_count = query->filter.field_count; int32_t *storage_columns = match->storage_columns; @@ -53787,6 +53798,7 @@ bool flecs_query_check_match_monitor( int32_t column = storage_columns[i]; if (column >= 0) { /* owned component */ + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); if (mon != dirty_state[column + 1]) { return true; } diff --git a/src/query.c b/src/query.c index 54f40da07..cc1691d08 100644 --- a/src/query.c +++ b/src/query.c @@ -535,12 +535,14 @@ void flecs_query_sync_match_monitor( int32_t *monitor = match->monitor; ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - table_dirty_state_t cur; - - monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + monitor[0] = dirty_state[0]; /* Did table gain/lose entities */ + } + table_dirty_state_t cur; int32_t i, term_count = query->filter.term_count; for (i = 0; i < term_count; i ++) { int32_t t = query->filter.terms[i].field_index; @@ -585,20 +587,24 @@ bool flecs_query_check_match_monitor_term( } int32_t *monitor = match->monitor; - ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - table_dirty_state_t cur; - int32_t state = monitor[term]; if (state == -1) { return false; } - if (!term) { - return monitor[0] != dirty_state[0]; + ecs_table_t *table = match->node.table; + if (table) { + int32_t *dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (!term) { + return monitor[0] != dirty_state[0]; + } + } else if (!term) { + return false; } + table_dirty_state_t cur; flecs_query_get_dirty_state(query, match, term - 1, &cur); ecs_assert(cur.column != -1, ECS_INTERNAL_ERROR, NULL); @@ -620,14 +626,17 @@ bool flecs_query_check_match_monitor( int32_t *monitor = match->monitor; ecs_table_t *table = match->node.table; - int32_t *dirty_state = flecs_table_get_dirty_state(query->filter.world, table); - bool has_flat = false; - ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); - - if (monitor[0] != dirty_state[0]) { - return true; + int32_t *dirty_state = NULL; + if (table) { + dirty_state = flecs_table_get_dirty_state( + query->filter.world, table); + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); + if (monitor[0] != dirty_state[0]) { + return true; + } } + bool has_flat = false; ecs_world_t *world = query->filter.world; int32_t i, field_count = query->filter.field_count; int32_t *storage_columns = match->storage_columns; @@ -645,6 +654,7 @@ bool flecs_query_check_match_monitor( int32_t column = storage_columns[i]; if (column >= 0) { /* owned component */ + ecs_assert(dirty_state != NULL, ECS_INTERNAL_ERROR, NULL); if (mon != dirty_state[column + 1]) { return true; } diff --git a/src/table.c b/src/table.c index 140121900..298484396 100644 --- a/src/table.c +++ b/src/table.c @@ -2447,7 +2447,9 @@ void flecs_table_replace_data( int32_t* flecs_table_get_dirty_state( ecs_world_t *world, ecs_table_t *table) -{ +{ + ecs_poly_assert(world, ecs_world_t); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); if (!table->dirty_state) { int32_t column_count = table->storage_count; table->dirty_state = flecs_alloc_n(&world->allocator, diff --git a/test/api/project.json b/test/api/project.json index 5d0178b1d..e26fb081c 100644 --- a/test/api/project.json +++ b/test/api/project.json @@ -1489,6 +1489,7 @@ "query_changed_w_or", "query_changed_or", "query_changed_w_singleton", + "query_changed_w_only_singleton", "subquery_match_existing", "subquery_match_new", "subquery_inactive", diff --git a/test/api/src/Query.c b/test/api/src/Query.c index 4bef64726..143cf0178 100644 --- a/test/api/src/Query.c +++ b/test/api/src/Query.c @@ -3108,6 +3108,45 @@ void Query_query_changed_w_singleton() { ecs_fini(world); } +void Query_query_changed_w_only_singleton() { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_singleton_set(world, Position, {1, 2}); + + ecs_query_t *q = ecs_query(world, { + .filter.terms = {{ + .id = ecs_id(Position), + .src.id = ecs_id(Position) + }} + }); + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(true, ecs_query_changed(NULL, &it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 1); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + { + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_bool(false, ecs_query_changed(NULL, &it)); + test_int(0, it.count); + Position *p = ecs_field(&it, Position, 1); + test_int(p->x, 1); + test_int(p->y, 2); + test_bool(false, ecs_query_next(&it)); + } + + ecs_fini(world); +} + void Query_subquery_match_existing() { ecs_world_t *world = ecs_mini(); diff --git a/test/api/src/main.c b/test/api/src/main.c index fb0b75c49..28952486f 100644 --- a/test/api/src/main.c +++ b/test/api/src/main.c @@ -1426,6 +1426,7 @@ void Query_query_change_skip_non_instanced(void); void Query_query_changed_w_or(void); void Query_query_changed_or(void); void Query_query_changed_w_singleton(void); +void Query_query_changed_w_only_singleton(void); void Query_subquery_match_existing(void); void Query_subquery_match_new(void); void Query_subquery_inactive(void); @@ -8040,6 +8041,10 @@ bake_test_case Query_testcases[] = { "query_changed_w_singleton", Query_query_changed_w_singleton }, + { + "query_changed_w_only_singleton", + Query_query_changed_w_only_singleton + }, { "subquery_match_existing", Query_subquery_match_existing @@ -12586,7 +12591,7 @@ static bake_test_suite suites[] = { "Query", NULL, NULL, - 207, + 208, Query_testcases }, {