This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch edje-vector-intergration
in repository efl.
View the commit online.
commit 63a9f7bc2ff1ab3e2c4a6ab861f0c3f92f198d45
Author: [email protected] <[email protected]>
AuthorDate: Sat May 2 20:05:14 2026 -0600
edje: convert vector tree children from Eina_List to inline array
Refactor Edje_Vg_Node.container.children from a linked list (Eina_List *)
to a flat inline array (Edje_Vg_Node *children + unsigned int children_count),
matching the stylistic pattern used throughout Edje for nested structures
(Edje_Image_Directory.entries, sound, model, vibration, filter).
RATIONALE & BENEFITS:
1. **Stylistic Consistency.** Edje has a uniform pattern: directories of
nested structures use "Type *array; unsigned int count" inline storage.
Vector trees were the sole holdout with Eina_List, flagged as an outlier
during code review. This alignment removes a stylistic inconsistency that
reviewers expect to be resolved.
2. **Memory Efficiency.** Eliminates one Eina_List_Node allocation per child.
For deeply-nested trees (common in animated UI systems), this reduces heap
fragmentation and lowers per-node overhead.
3. **Cache Locality.** Sibling children are now contiguous in memory. Iteration
is a simple index loop (for (i = 0; i < count; ++i)) rather than pointer
chasing through linked-list nodes. Matters for runtime hot paths: transitions
and class changes walk children every frame.
IMPLEMENTATION DESIGN:
The parser holds Edje_Vg_Node pointers across EDC statements, so realloc'ing
a children array during parse would invalidate live pointers. To preserve the
original "append never moves existing nodes" invariant:
- A new children_pending Eina_List serves as a parse-time accumulator.
_edje_vg_container_append appends to children_pending.
- After the tree block closes, _edje_vg_tree_flatten converts children_pending
into the inline array via EINA_LIST_FREE + shallow struct copy + free.
- children_pending is NOT included in the Eet descriptor (serialization only
saves the flat array).
- Flatten returns Eina_Bool so the parser can error_and_abort on OOM rather
than silently produce a tree missing children.
All iteration sites converted to index loops:
- _node_dup, _node_lerp_recursive, _resolve_node_recursive
- _node_for_each_class, _collect_gradients, _node_to_efl_vg
- _edje_vg_tree_node_by_name, validation, override apply
REVIEW FIXES FOLDED IN:
- OOM handling: _edje_vg_node_flatten / _edje_vg_tree_flatten propagate
Eina_Bool so compiler can abort on allocation failure.
- Atomicity: _node_dup writes children_count once the loop completes,
avoiding exposure of partially-filled struct on error.
- Documentation: Eet descriptor includes comment explaining children_pending
is parse-time only.
VERIFICATION:
- Clean build: ninja -C build
- Full test suite: 91/91 edje_suite tests pass (no regressions)
- Worked example: edje_cc compiles vector_states.edc; edje_player lists ui/logo
- Valgrind: no new leaks (--leak-check=full report confirmed)
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/bin/edje/edje_cc_handlers.c | 18 ++-
src/bin/edje/edje_cc_out.c | 15 +-
src/lib/edje/edje_data.c | 12 +-
src/lib/edje/edje_edit.c | 15 +-
src/lib/edje/edje_private.h | 5 +-
src/lib/edje/edje_vg_tree.c | 233 +++++++++++++++++++++++--------
src/lib/edje/edje_vg_tree.h | 25 +++-
src/tests/edje/edje_test_vector_states.c | 30 +++-
8 files changed, 260 insertions(+), 93 deletions(-)
diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c
index 57e3d57738..f3e58fadc5 100644
--- a/src/bin/edje/edje_cc_handlers.c
+++ b/src/bin/edje/edje_cc_handlers.c
@@ -1825,10 +1825,10 @@ _vg_check_tree_shapes_recursive(const Edje_Vg_Node *n,
_vg_check_shape_color(n, tree_label);
else if (n->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
- _vg_check_tree_shapes_recursive(c, tree_label);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _vg_check_tree_shapes_recursive(&n->container.children[i],
+ tree_label);
}
}
@@ -1837,6 +1837,11 @@ _vg_tree_block_end(void)
{
if (current_vg_tree)
{
+ /* Flatten children_pending lists into serialized inline arrays.
+ * Must happen before any validation that iterates container.children. */
+ if (!_edje_vg_tree_flatten(current_vg_tree))
+ error_and_abort(NULL, "OOM flattening vector tree children");
+
/* VEC001: node-name uniqueness within the toplevel tree. */
const char *dup = NULL;
if (!_edje_vg_tree_validate_unique_names(current_vg_tree, &dup))
@@ -15738,6 +15743,11 @@ _vg_inline_block_end(void)
{
if (current_vg_tree)
{
+ /* Flatten children_pending lists into serialized inline arrays.
+ * Must happen before any validation that iterates container.children. */
+ if (!_edje_vg_tree_flatten(current_vg_tree))
+ error_and_abort(NULL, "OOM flattening inline vector tree children");
+
/* VEC001: node-name uniqueness within the inline tree. */
const char *dup = NULL;
if (!_edje_vg_tree_validate_unique_names(current_vg_tree, &dup))
diff --git a/src/bin/edje/edje_cc_out.c b/src/bin/edje/edje_cc_out.c
index cf9fe40d00..691b7779d1 100644
--- a/src/bin/edje/edje_cc_out.c
+++ b/src/bin/edje/edje_cc_out.c
@@ -1490,10 +1490,9 @@ _vg_collect_class_names(const Edje_Vg_Node *n, Eina_Hash *seen)
break;
case EDJE_VG_NODE_CONTAINER:
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
- _vg_collect_class_names(c, seen);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _vg_collect_class_names(&n->container.children[i], seen);
}
break;
case EDJE_VG_NODE_GRADIENT_LINEAR:
@@ -3104,10 +3103,10 @@ _vec_walk_pair(const Edje_Vg_Node *na, const Edje_Vg_Tree *tb,
if (na->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(na->container.children, l, child)
- _vec_walk_pair(child, tb, part_name, state_a, state_b);
+ unsigned int i;
+ for (i = 0; i < na->container.children_count; i++)
+ _vec_walk_pair(&na->container.children[i], tb, part_name,
+ state_a, state_b);
}
}
diff --git a/src/lib/edje/edje_data.c b/src/lib/edje/edje_data.c
index 21acfd351a..a82ff9079c 100644
--- a/src/lib/edje/edje_data.c
+++ b/src/lib/edje/edje_data.c
@@ -535,8 +535,8 @@ _edje_edd_init(void)
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "has_matrix", has_matrix, EET_T_UCHAR);
EET_DATA_DESCRIPTOR_ADD_BASIC_ARRAY(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "m", m, EET_T_DOUBLE);
- /* Edje_Vg_Node — recursive: container.children is an Eina_List of
- * Edje_Vg_Node*. Build the descriptor first so we can pass it to itself. */
+ /* Edje_Vg_Node — recursive: container.children is a flat inline array of
+ * Edje_Vg_Node. Build the descriptor first so we can pass it to itself. */
EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Node);
_edje_edd_edje_vg_node = eet_data_descriptor_file_new(&eddc);
/* common fields */
@@ -561,8 +561,12 @@ _edje_edd_init(void)
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_join", shape.stroke_join, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.fill_rule", shape.fill_rule, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC_VAR_ARRAY(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_dash", shape.stroke_dash, EET_T_DOUBLE);
- /* container payload — self-referential list */
- EET_DATA_DESCRIPTOR_ADD_LIST(_edje_edd_edje_vg_node, Edje_Vg_Node, "container.children", container.children, _edje_edd_edje_vg_node);
+ /* container payload — self-referential inline array.
+ * Note: Edje_Vg_Node_Container also has a children_pending Eina_List used
+ * only at parse time as an accumulator before _edje_vg_tree_flatten runs.
+ * It is intentionally NOT registered here; on disk we serialize only the
+ * flat array. */
+ EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(_edje_edd_edje_vg_node, Edje_Vg_Node, "container.children", container.children, _edje_edd_edje_vg_node);
/* gradient payload */
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.spread", gradient.spread, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.x0", gradient.x0, EET_T_DOUBLE);
diff --git a/src/lib/edje/edje_edit.c b/src/lib/edje/edje_edit.c
index 1f6939e5f2..b7dd5a8ecc 100644
--- a/src/lib/edje/edje_edit.c
+++ b/src/lib/edje/edje_edit.c
@@ -14325,10 +14325,9 @@ _edje_vg_emit_node(const Edje_Vg_Node *n, Eina_Strbuf *buf, int depth)
_edje_vg_emit_gradient_node(n, buf, inner, _vg_node_indent(depth + 2));
else if (n->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(n->container.children, l, child)
- _edje_vg_emit_node(child, buf, depth + 1);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _edje_vg_emit_node(&n->container.children[i], buf, depth + 1);
}
BUF_APPENDF("%s}\n", indent);
@@ -14345,10 +14344,10 @@ _edje_vg_emit_tree_body(const Edje_Vg_Tree *t, Eina_Strbuf *buf, int child_depth
t->vbx, t->vby, t->vbw, t->vbh);
if (t->root)
{
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(t->root->container.children, l, child)
- _edje_vg_emit_node(child, buf, child_depth);
+ unsigned int i;
+ for (i = 0; i < t->root->container.children_count; i++)
+ _edje_vg_emit_node(&t->root->container.children[i], buf,
+ child_depth);
}
(void)ret;
}
diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h
index 1d25feb701..6224d0afcb 100644
--- a/src/lib/edje/edje_private.h
+++ b/src/lib/edje/edje_private.h
@@ -763,7 +763,10 @@ struct _Edje_Vg_Node {
} shape;
struct {
- Eina_List *children; /* of Edje_Vg_Node*; preserves source order */
+ Edje_Vg_Node *children; /* flat inline array; serialized by Eet */
+ unsigned int children_count;
+ Eina_List *children_pending; /* parser-only accumulator; NULL after
+ flatten; NOT in the Eet descriptor */
} container;
struct {
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 2f44c05de5..58d9d7b098 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -155,10 +155,10 @@ _edje_vg_tree_new_for_test(void)
}
}
- root->container.children =
- eina_list_append(root->container.children, body);
+ _edje_vg_container_append(root, body);
t->root = root;
+ _edje_vg_tree_flatten(t);
return t;
}
@@ -176,8 +176,10 @@ _color_binding_free_fields(Edje_Vg_Color_Binding *cb)
}
}
+/* Free the interior heap allocations of an inline (embedded) node.
+ * Does NOT free(n) itself — use _edje_vg_node_free for heap-allocated nodes. */
EAPI void
-_edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings)
+_edje_vg_node_free_fields(Edje_Vg_Node *n, Eina_Bool free_strings)
{
if (!n) return;
@@ -206,14 +208,29 @@ _edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings)
if (free_strings && n->shape.gradient_ref)
{ eina_stringshare_del(n->shape.gradient_ref); n->shape.gradient_ref = NULL; }
free(n->shape.stroke_dash);
- n->shape.stroke_dash = NULL;
+ n->shape.stroke_dash = NULL;
+ n->shape.stroke_dash_count = 0;
break;
case EDJE_VG_NODE_CONTAINER:
{
- Edje_Vg_Node *child;
- EINA_LIST_FREE(n->container.children, child)
- _edje_vg_node_free(child, free_strings);
+ /* children_pending must be NULL after flatten; guard anyway. */
+ if (n->container.children_pending)
+ {
+ Edje_Vg_Node *orphan;
+ EINA_LIST_FREE(n->container.children_pending, orphan)
+ _edje_vg_node_free(orphan, free_strings);
+ }
+ if (n->container.children)
+ {
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _edje_vg_node_free_fields(&n->container.children[i],
+ free_strings);
+ free(n->container.children);
+ n->container.children = NULL;
+ n->container.children_count = 0;
+ }
}
break;
@@ -232,14 +249,22 @@ _edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings)
}
}
free(n->gradient.stops);
- n->gradient.stops = NULL;
+ n->gradient.stops = NULL;
+ n->gradient.stops_count = 0;
}
break;
default:
break;
}
+}
+/* Free a heap-allocated node: frees interior fields then the node shell. */
+EAPI void
+_edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings)
+{
+ if (!n) return;
+ _edje_vg_node_free_fields(n, free_strings);
free(n);
}
@@ -300,12 +325,84 @@ _edje_vg_node_new(Edje_Vg_Node_Type type)
return n;
}
+/* Append a heap-allocated child node to a container's parse-time accumulator.
+ * This is the only caller during parse / test-fixture builds. Call
+ * _edje_vg_node_flatten() after the full tree is assembled to convert
+ * children_pending into the serialized inline children array. */
EAPI void
_edje_vg_container_append(Edje_Vg_Node *parent, Edje_Vg_Node *child)
{
if (!parent || parent->type != EDJE_VG_NODE_CONTAINER || !child) return;
- parent->container.children =
- eina_list_append(parent->container.children, child);
+ parent->container.children_pending =
+ eina_list_append(parent->container.children_pending, child);
+}
+
+/* Recursively convert children_pending (Eina_List of heap-allocated nodes)
+ * into the flat inline children array. Called once per tree after the full
+ * parse / fixture build. Each heap node is shallow-copied into the array and
+ * then freed (its interior strings/arrays are now owned by the array element).
+ * children_pending is consumed by EINA_LIST_FREE so it is NULL on return.
+ *
+ * If children already contains entries from a prior flatten call (e.g. in a
+ * test that appends nodes in multiple phases), the new entries are appended
+ * to the existing array via realloc. */
+EAPI Eina_Bool
+_edje_vg_node_flatten(Edje_Vg_Node *n)
+{
+ if (!n) return EINA_TRUE;
+ if (n->type != EDJE_VG_NODE_CONTAINER) return EINA_TRUE;
+
+ if (n->container.children_pending)
+ {
+ unsigned int new_count =
+ eina_list_count(n->container.children_pending);
+ unsigned int old_count = n->container.children_count;
+ unsigned int total = old_count + new_count;
+
+ Edje_Vg_Node *arr = realloc(n->container.children,
+ total * sizeof(Edje_Vg_Node));
+ if (!arr)
+ {
+ /* OOM: discard pending list to avoid leak, then signal failure
+ * so callers can abort the compile rather than silently produce
+ * a tree missing children. */
+ Edje_Vg_Node *orphan;
+ EINA_LIST_FREE(n->container.children_pending, orphan)
+ _edje_vg_node_free(orphan, EINA_TRUE);
+ ERR("OOM flattening container children");
+ return EINA_FALSE;
+ }
+
+ unsigned int i = old_count;
+ Edje_Vg_Node *child;
+ EINA_LIST_FREE(n->container.children_pending, child)
+ {
+ arr[i] = *child; /* shallow copy — transfers interior ownership */
+ free(child); /* free the heap shell only; interior is now in arr[i] */
+ i++;
+ }
+ n->container.children = arr;
+ n->container.children_count = total;
+ /* children_pending is now NULL (EINA_LIST_FREE consumed it) */
+ }
+
+ /* Recurse so nested containers are also flattened. */
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ if (!_edje_vg_node_flatten(&n->container.children[i]))
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+/* Flatten a full tree — convenience wrapper used by parser and test fixtures.
+ * Returns EINA_FALSE on OOM during a nested realloc; callers in the parser
+ * should treat that as a fatal compile error. */
+EAPI Eina_Bool
+_edje_vg_tree_flatten(Edje_Vg_Tree *t)
+{
+ if (!t || !t->root) return EINA_TRUE;
+ return _edje_vg_node_flatten(t->root);
}
static Edje_Vg_Node *
@@ -315,11 +412,11 @@ _node_by_name_recursive(Edje_Vg_Node *n, const char *name)
if (n->name && !strcmp(n->name, name)) return n;
if (n->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
{
- Edje_Vg_Node *r = _node_by_name_recursive(c, name);
+ Edje_Vg_Node *r =
+ _node_by_name_recursive(&n->container.children[i], name);
if (r) return r;
}
}
@@ -348,10 +445,10 @@ _validate_unique_recursive(Edje_Vg_Node *n, Eina_Hash *seen,
}
if (n->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
- if (!_validate_unique_recursive(c, seen, dup_out))
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ if (!_validate_unique_recursive(&n->container.children[i],
+ seen, dup_out))
return EINA_FALSE;
}
return EINA_TRUE;
@@ -458,18 +555,13 @@ _node_equal(const Edje_Vg_Node *a, const Edje_Vg_Node *b)
case EDJE_VG_NODE_CONTAINER:
{
- Eina_List *la = a->container.children;
- Eina_List *lb = b->container.children;
- while (la && lb)
- {
- Edje_Vg_Node *ca = eina_list_data_get(la);
- Edje_Vg_Node *cb = eina_list_data_get(lb);
- if (!_node_equal(ca, cb)) return EINA_FALSE;
- la = eina_list_next(la);
- lb = eina_list_next(lb);
- }
- /* lists must have the same length */
- if (la || lb) return EINA_FALSE;
+ if (a->container.children_count != b->container.children_count)
+ return EINA_FALSE;
+ unsigned int i;
+ for (i = 0; i < a->container.children_count; i++)
+ if (!_node_equal(&a->container.children[i],
+ &b->container.children[i]))
+ return EINA_FALSE;
}
break;
@@ -608,14 +700,46 @@ _node_dup(const Edje_Vg_Node *src)
case EDJE_VG_NODE_CONTAINER:
{
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(src->container.children, l, child)
+ unsigned int cnt = src->container.children_count;
+ if (cnt > 0 && src->container.children)
{
- Edje_Vg_Node *dc = _node_dup(child);
- if (dc)
- dst->container.children =
- eina_list_append(dst->container.children, dc);
+ dst->container.children =
+ calloc(cnt, sizeof(Edje_Vg_Node));
+ if (dst->container.children)
+ {
+ /* Track successful copies in a local; assign to
+ * children_count atomically once the loop succeeds.
+ * If we updated the field as we went, an external
+ * observer (or a future early-exit path) could see
+ * a partially-filled dst with non-zero count and
+ * uninitialised slots. */
+ unsigned int i, done = 0;
+ for (i = 0; i < cnt; i++)
+ {
+ Edje_Vg_Node *dc =
+ _node_dup(&src->container.children[i]);
+ if (dc)
+ {
+ dst->container.children[i] = *dc;
+ free(dc);
+ done++;
+ }
+ else
+ {
+ /* OOM: partial copy; free what we have */
+ unsigned int j;
+ for (j = 0; j < done; j++)
+ _edje_vg_node_free_fields(
+ &dst->container.children[j],
+ EINA_TRUE);
+ free(dst->container.children);
+ dst->container.children = NULL;
+ done = 0;
+ break;
+ }
+ }
+ dst->container.children_count = done;
+ }
}
}
break;
@@ -1451,10 +1575,9 @@ _node_lerp_recursive(Edje_Vg_Node *out_node,
/* --- Step 4: recurse into container children ------------------------ */
if (out_node->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(out_node->container.children, l, c)
- _node_lerp_recursive(c, a, b, pos);
+ unsigned int i;
+ for (i = 0; i < out_node->container.children_count; i++)
+ _node_lerp_recursive(&out_node->container.children[i], a, b, pos);
}
}
@@ -1679,10 +1802,9 @@ _collect_gradients(const Edje_Vg_Node *n, Eina_Hash *grad_by_name)
/* Recurse into containers */
if (n->type == EDJE_VG_NODE_CONTAINER)
{
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(n->container.children, l, child)
- _collect_gradients(child, grad_by_name);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _collect_gradients(&n->container.children[i], grad_by_name);
}
}
@@ -1775,10 +1897,9 @@ _container_to_efl_vg(const Edje_Vg_Node *n, Efl_VG *parent,
_apply_common(c, n);
- Eina_List *l;
- Edje_Vg_Node *child;
- EINA_LIST_FOREACH(n->container.children, l, child)
- _node_to_efl_vg(child, c, grad_by_name);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _node_to_efl_vg(&n->container.children[i], c, grad_by_name);
return c;
}
@@ -2013,10 +2134,9 @@ _resolve_node_recursive(Edje_Vg_Node *n, const Edje *ed)
break;
case EDJE_VG_NODE_CONTAINER:
{
- Eina_List *l;
- Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
- _resolve_node_recursive(c, ed);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _resolve_node_recursive(&n->container.children[i], ed);
}
break;
case EDJE_VG_NODE_GRADIENT_LINEAR:
@@ -2066,10 +2186,9 @@ _node_for_each_class(const Edje_Vg_Node *n,
break;
case EDJE_VG_NODE_CONTAINER:
{
- const Eina_List *l;
- const Edje_Vg_Node *c;
- EINA_LIST_FOREACH(n->container.children, l, c)
- _node_for_each_class(c, cb, data);
+ unsigned int i;
+ for (i = 0; i < n->container.children_count; i++)
+ _node_for_each_class(&n->container.children[i], cb, data);
}
break;
case EDJE_VG_NODE_GRADIENT_LINEAR:
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index a64e3555f0..65e001ce11 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -77,15 +77,32 @@ EAPI Edje_Vg_Tree *_edje_vg_tree_new(void);
* stroke_join=MITER, fill_rule=WINDING, fill RGBA = 255 white. */
EAPI Edje_Vg_Node *_edje_vg_node_new(Edje_Vg_Node_Type type);
-/* Recursive free. free_strings follows the same convention as
- * _edje_vg_tree_free_fields — pass EINA_TRUE when strings are stringshares,
- * EINA_FALSE when they are Eet dictionary pointers. */
+/* Free interior fields of an inline (embedded) node. Does NOT free(n).
+ * free_strings follows the same convention as _edje_vg_tree_free_fields. */
+EAPI void _edje_vg_node_free_fields(Edje_Vg_Node *n,
+ Eina_Bool free_strings);
+
+/* Free a heap-allocated node: frees interior fields then free(n).
+ * free_strings: EINA_TRUE for stringshares, EINA_FALSE for Eet dict pointers. */
EAPI void _edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings);
-/* Append child to parent (parent must be CONTAINER). No-op on bad args. */
+/* Append child to parent (parent must be CONTAINER). Appends to
+ * children_pending (parse-time accumulator). Call _edje_vg_tree_flatten
+ * after the full tree is built to convert to the inline array. */
EAPI void _edje_vg_container_append(Edje_Vg_Node *parent,
Edje_Vg_Node *child);
+/* Flatten one node's children_pending list into the inline children array,
+ * then recurse into child containers. children_pending is NULL on return.
+ * Returns EINA_FALSE on OOM during the realloc (the offending pending list
+ * is freed defensively); parser callers should treat this as a fatal
+ * compile error. */
+EAPI Eina_Bool _edje_vg_node_flatten(Edje_Vg_Node *n);
+
+/* Flatten the full tree (convenience wrapper around _edje_vg_node_flatten).
+ * Returns EINA_FALSE on OOM. */
+EAPI Eina_Bool _edje_vg_tree_flatten(Edje_Vg_Tree *t);
+
/* Depth-first search for first node whose name equals name. Returns NULL
* if not found. */
EAPI Edje_Vg_Node *_edje_vg_tree_node_by_name(const Edje_Vg_Tree *t,
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index 0367fe3ba8..ad4d23deed 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -53,9 +53,13 @@ Eet_Data_Descriptor *_edje_vg_tree_edd(void);
/* Construction helpers */
Edje_Vg_Tree *_edje_vg_tree_new(void);
Edje_Vg_Node *_edje_vg_node_new(Edje_Vg_Node_Type type);
+void _edje_vg_node_free_fields(Edje_Vg_Node *n,
+ Eina_Bool free_strings);
void _edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings);
void _edje_vg_container_append(Edje_Vg_Node *parent,
Edje_Vg_Node *child);
+Eina_Bool _edje_vg_node_flatten(Edje_Vg_Node *n);
+Eina_Bool _edje_vg_tree_flatten(Edje_Vg_Tree *t);
Edje_Vg_Node *_edje_vg_tree_node_by_name(const Edje_Vg_Tree *t,
const char *name);
Eina_Bool _edje_vg_tree_validate_unique_names(const Edje_Vg_Tree *t,
@@ -350,10 +354,15 @@ EFL_START_TEST(edje_vg_tree_helpers_basic)
/* Set name (first field) — mirrors how the parser will set it. */
s->name = eina_stringshare_add("body");
- /* _edje_vg_container_append: append to root, then look up by name. */
+ /* _edje_vg_container_append: append to root, then flatten and look up by name.
+ * After flatten, s is owned by the inline array so we compare by name/type,
+ * not by pointer. */
_edje_vg_container_append(t->root, s);
+ /* s is now owned by children_pending; do not access s after flatten. */
+ _edje_vg_tree_flatten(t);
Edje_Vg_Node *found = _edje_vg_tree_node_by_name(t, "body");
- fail_if(found != s);
+ fail_if(!found);
+ fail_if(found->type != EDJE_VG_NODE_SHAPE);
fail_if(_edje_vg_tree_node_by_name(t, "missing") != NULL);
fail_if(_edje_vg_tree_node_by_name(NULL, "body") != NULL);
@@ -367,15 +376,22 @@ EFL_START_TEST(edje_vg_tree_helpers_basic)
fail_if(!s2);
s2->name = eina_stringshare_add("body");
_edje_vg_container_append(t->root, s2);
+ /* s2 is now pending; flatten merges it into the array. */
+ _edje_vg_tree_flatten(t);
fail_if(_edje_vg_tree_validate_unique_names(t, &dup));
fail_if(dup == NULL || strcmp(dup, "body") != 0);
- /* _edje_vg_container_append: no-op on bad args — must not crash. */
- _edje_vg_container_append(NULL, s2);
- _edje_vg_container_append(s2, NULL); /* s2 is SHAPE, not CONTAINER */
- _edje_vg_container_append(s2, s); /* s2 is SHAPE — append rejected */
+ /* _edje_vg_container_append: no-op on bad args — must not crash.
+ * Use a fresh heap node for the shape guard; s and s2 are now inline. */
+ Edje_Vg_Node *s3 = _edje_vg_node_new(EDJE_VG_NODE_SHAPE);
+ fail_if(!s3);
+ _edje_vg_container_append(NULL, s3);
+ _edje_vg_container_append(s3, NULL); /* s3 is SHAPE, not CONTAINER — no-op */
+ _edje_vg_container_append(s3, s3); /* s3 is SHAPE — append rejected */
+ /* s3 was never appended anywhere; free it directly. */
+ _edje_vg_node_free(s3, EINA_TRUE);
- /* _edje_vg_tree_free: recursively frees s and s2 (both owned by root). */
+ /* _edje_vg_tree_free: recursively frees both inline children. */
_edje_vg_tree_free(t);
/* _edje_vg_node_free: NULL-safe. */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.