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.

Reply via email to