Skip to content

Commit

Permalink
Fix crash when using change detection with queries that have no this …
Browse files Browse the repository at this point in the history
…terms
  • Loading branch information
SanderMertens committed May 12, 2023
1 parent 3ca10b6 commit a990162
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 39 deletions.
50 changes: 31 additions & 19 deletions flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
46 changes: 28 additions & 18 deletions src/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion src/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions test/api/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
39 changes: 39 additions & 0 deletions test/api/src/Query.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
7 changes: 6 additions & 1 deletion test/api/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -12586,7 +12591,7 @@ static bake_test_suite suites[] = {
"Query",
NULL,
NULL,
207,
208,
Query_testcases
},
{
Expand Down

0 comments on commit a990162

Please sign in to comment.