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 b231a5f2e7bdf523c45d926fdee67104b34312f9
Author: [email protected] <[email protected]>
AuthorDate: Fri May 1 09:42:29 2026 -0600

    edje: fix stringshare ref bug, gradient override parser, and vector_states demo
    
    ## Stringshare ownership model fix
    
    The edje_vg_tree.c dup helpers (_node_dup, _color_binding_dup, and
    _edje_vg_override_dup) were calling eina_stringshare_ref on input strings.
    The _ref call is only valid when the input is a real stringshare handle —
    it checks a magic field, which fails at runtime (~75725f6c magic error)
    when the input is a borrowed pointer from an Eet-dictionary mapping.
    
    This was not caught by unit tests because the test fixture always loads
    into heap-allocated stringshare-owned trees (parser produces them).
    Production loaded .edj files decode into dictionary-backed trees, which
    failed the magic check on every override-apply operation.
    
    Replaced all 16 _ref calls with eina_stringshare_add, which is
    dictionary-safe: it looks up the bytes and creates a fresh stringshare
    regardless of source ownership. Surfaced during edje_player smoke testing
    on every mouse,in event (override dup on interaction).
    
    Added a String-ownership convention block to edje_vg_tree.h preamble
    documenting the dual-ownership model (Eet-dictionary vs stringshare),
    rules for new code, and the rationale (mmap efficiency). Updated
    _edje_vg_tree_dup doc to explain the dictionary-safe guarantee.
    
    ## Parser override-mode bug fix
    
    The _vg_open_override handler (edje_cc_handlers.c) zero-initialized the
    Edje_Vg_Override struct via calloc but never set payload.type. When a
    description-scope override block targeted a gradient node
    (vector.gradient_linear { target: …; start: …; end: …; }), the
    EXPECT_GRADIENT_LINEAR guards on st_vg_grad_start/end/radius read
    current_vg_node->type (which was 0 = EDJE_VG_NODE_SHAPE) and aborted
    with "linear-gradient-only property on wrong node".
    
    No fixture covered this — the vector_states.edc example originally did
    not override a gradient node. One-line fix sets ovr->payload.type = type
    after calloc to match expected_type.
    
    ## vector_states.edc demo enhancement
    
    Added a gradient_linear node body_grad as a sibling of the body shape,
    with two color_class-bound stops (logo_fill / logo_outline). The body
    shape now references it via fill.gradient: "body_grad" instead of using
    a direct fill.color_class.
    
    The hover state now has a second override block (vector.gradient_linear)
    that sweeps the gradient direction from vertical (top→bottom) to
    horizontal (left→right) AND swaps the leading stop's class from logo_fill
    to logo_hover. Demonstrates four features at once: gradient nodes in the
    toplevel tree, shape-to-gradient reference, gradient endpoint
    interpolation across states, and class-bound gradient stops.
    
    The demo originally did not cover gradient overrides, so the parser bug
    above was not caught by visual testing until gradients were added.
    
    All 91 edje_suite tests pass. edje_cc compiles the new gradient + override
    syntax with zero warnings. edje_player smoke test no longer shows
    stringshare magic errors on interactive events.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/bin/edje/edje_cc_handlers.c     |  6 +++++-
 src/examples/edje/vector_states.edc | 40 +++++++++++++++++++++++++---------
 src/lib/edje/edje_vg_tree.c         | 38 ++++++++++++++++----------------
 src/lib/edje/edje_vg_tree.h         | 43 ++++++++++++++++++++++++++++++++-----
 4 files changed, 93 insertions(+), 34 deletions(-)

diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c
index f75d1d6aa2..bf6e64f7e2 100644
--- a/src/bin/edje/edje_cc_handlers.c
+++ b/src/bin/edje/edje_cc_handlers.c
@@ -2015,9 +2015,13 @@ _vg_open_override(Edje_Vg_Node_Type type)
    ovr = calloc(1, sizeof(Edje_Vg_Override));
    if (!ovr) error_and_abort(NULL, "OOM allocating override");
    ovr->expected_type = type;
-   /* payload zeroed; target_name still NULL.
+   /* payload mostly zeroed; target_name still NULL.  Set payload.type so
+    * the EXPECT_GRADIENT_LINEAR / EXPECT_GRADIENT_RADIAL / EXPECT_SHAPE
+    * guards on property handlers (e.g. st_vg_grad_start) accept this
+    * override block — they read current_vg_node->type.
     * Do NOT append to ed->vg.overrides yet — st_vg_override_target will
     * decide whether to merge with an existing inherited entry or append. */
+   ovr->payload.type = type;
 
    current_vg_override = ovr;
    current_vg_node = &ovr->payload;
diff --git a/src/examples/edje/vector_states.edc b/src/examples/edje/vector_states.edc
index a6475b9bbb..06be72a23b 100644
--- a/src/examples/edje/vector_states.edc
+++ b/src/examples/edje/vector_states.edc
@@ -3,9 +3,11 @@
  *
  * Demonstrates:
  *   - A named vector tree in the toplevel vectors {} block
- *   - A VECTOR part referencing it via vector.use:
- *   - Two states (default, hover) with per-node shape override
- *   - fill.color_class binding so the fill colour is themeable at runtime
+ *   - A linear gradient node referenced from a shape's fill
+ *   - A VECTOR part referencing the tree via vector.use:
+ *   - Two states (default, hover) with per-node overrides on BOTH the
+ *     shape (path + stroke width) AND the gradient (endpoint sweep)
+ *   - color_class binding on gradient stops + stroke
  *   - STATE_SET programs wired to mouse,in / mouse,out for live transitions
  *
  * Compile:
@@ -24,11 +26,22 @@ collections {
    vectors {
       vector { name: "logo_glyph";
          viewbox: 0 0 100 100;
+         /* The gradient node sits as a sibling of the shape inside the
+          * tree.  Shapes reference it by name via fill.gradient:.
+          * Stops bind to color_class so the gradient retints together
+          * with the rest of the theme. */
+         gradient_linear { name: "body_grad";
+            start: 50 0;
+            end:   50 100;
+            spread: PAD;
+            stop { offset: 0.0; color_class: "logo_fill";    }
+            stop { offset: 1.0; color_class: "logo_outline"; }
+         }
          shape { name: "body";
             /* topology: M + 8×L + Z — identical command sequence in both
              * states, so efl_gfx_path_interpolate() can morph the path. */
             path: "M0,0 L0,0 L100,0 L100,0 L100,100 L100,100 L0,100 L0,100 L0,0";
-            fill.color_class: "logo_fill";
+            fill.gradient: "body_grad";
             stroke.color_class: "logo_outline";
             stroke.width: 2;
          }
@@ -45,17 +58,24 @@ collections {
             }
             description { state: "hover" 0.0;
                inherit: "default" 0.0;
-               /* Override the "body" shape:
-                *  - morph path from square to star (same topology: M + 8×L + Z)
-                *  - swap fill class to logo_hover (smooth crossfade via lerp)
-                *  - widen stroke from 2 to 4
-                * stroke color_class stays logo_outline — no override needed. */
+               /* Two overrides in this state:
+                *  1. Morph the body path from square to star (same topology:
+                *     M + 8×L + Z) and widen its stroke.
+                *  2. Sweep the gradient direction from vertical (top→bottom)
+                *     to horizontal (left→right) AND swap the top stop to
+                *     logo_hover so the warm colour leads the gradient. */
                vector.shape {
                   target: "body";
                   path: "M0,0 L50,-80 L100,0 L180,50 L100,100 L50,180 L0,100 L-80,50 L0,0";
-                  fill.color_class: "logo_hover";
                   stroke.width: 4;
                }
+               vector.gradient_linear {
+                  target: "body_grad";
+                  start: 0  50;
+                  end:   100 50;
+                  stop { offset: 0.0; color_class: "logo_hover";   }
+                  stop { offset: 1.0; color_class: "logo_outline"; }
+               }
             }
          }
       }
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 902ae851c7..cff0088879 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -476,7 +476,9 @@ _edje_vg_tree_equal(const Edje_Vg_Tree *a, const Edje_Vg_Tree *b)
  * Phase 4.1 — Deep-dup and override-apply helpers
  * ========================================================================= */
 
-/* Duplicate a color binding — stringshare_ref the class string. */
+/* Duplicate a color binding — stringshare_add the class string so the
+ * dup owns its own ref regardless of whether src came from an Eet
+ * dictionary or a heap stringshare. */
 static void
 _color_binding_dup(Edje_Vg_Color_Binding *dst,
                    const Edje_Vg_Color_Binding *src)
@@ -486,7 +488,7 @@ _color_binding_dup(Edje_Vg_Color_Binding *dst,
    dst->b = src->b;
    dst->a = src->a;
    dst->color_class = src->color_class
-      ? eina_stringshare_ref(src->color_class) : NULL;
+      ? eina_stringshare_add(src->color_class) : NULL;
 }
 
 static Edje_Vg_Node *_node_dup(const Edje_Vg_Node *src);
@@ -502,7 +504,7 @@ _node_dup(const Edje_Vg_Node *src)
    /* Common fields */
    dst->type    = src->type;
    dst->visible = src->visible;
-   dst->name    = src->name ? eina_stringshare_ref(src->name) : NULL;
+   dst->name    = src->name ? eina_stringshare_add(src->name) : NULL;
    _color_binding_dup(&dst->color, &src->color);
    dst->xform = src->xform;   /* Edje_Vg_Transform has no heap pointers */
 
@@ -510,7 +512,7 @@ _node_dup(const Edje_Vg_Node *src)
      {
       case EDJE_VG_NODE_SHAPE:
          dst->shape.path =
-            src->shape.path ? eina_stringshare_ref(src->shape.path) : NULL;
+            src->shape.path ? eina_stringshare_add(src->shape.path) : NULL;
          _color_binding_dup(&dst->shape.fill, &src->shape.fill);
          _color_binding_dup(&dst->shape.stroke_color, &src->shape.stroke_color);
          dst->shape.stroke_width      = src->shape.stroke_width;
@@ -520,7 +522,7 @@ _node_dup(const Edje_Vg_Node *src)
          dst->shape.stroke_dash_count = src->shape.stroke_dash_count;
          dst->shape.gradient_ref =
             src->shape.gradient_ref
-               ? eina_stringshare_ref(src->shape.gradient_ref) : NULL;
+               ? eina_stringshare_add(src->shape.gradient_ref) : NULL;
          if (src->shape.stroke_dash_count > 0 && src->shape.stroke_dash)
            {
               size_t sz =
@@ -566,7 +568,7 @@ _node_dup(const Edje_Vg_Node *src)
               if (dst->gradient.stops)
                 {
                    memcpy(dst->gradient.stops, src->gradient.stops, sz);
-                   /* stringshare_ref each stop's color_class */
+                   /* stringshare_add each stop's color_class — dictionary-safe */
                    unsigned int i;
                    for (i = 0; i < dst->gradient.stops_count; i++)
                      {
@@ -574,7 +576,7 @@ _node_dup(const Edje_Vg_Node *src)
                            dst->gradient.stops[i].color.color_class;
                         if (cc)
                            dst->gradient.stops[i].color.color_class =
-                              eina_stringshare_ref(cc);
+                              eina_stringshare_add(cc);
                      }
                 }
               else
@@ -597,7 +599,7 @@ _edje_vg_tree_dup(const Edje_Vg_Tree *t)
    Edje_Vg_Tree *dst = calloc(1, sizeof(Edje_Vg_Tree));
    if (!dst) return NULL;
 
-   dst->name = eina_stringshare_ref(t->name);
+   dst->name = eina_stringshare_add(t->name);
    dst->vbx  = t->vbx;
    dst->vby  = t->vby;
    dst->vbw  = t->vbw;
@@ -744,7 +746,7 @@ _edje_vg_tree_apply_override(Edje_Vg_Tree *working,
                           node->gradient.stops[i].color.color_class;
                        if (cc)
                           node->gradient.stops[i].color.color_class =
-                             eina_stringshare_ref(cc);
+                             eina_stringshare_add(cc);
                     }
                }
           }
@@ -772,7 +774,7 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
    if (!dup) return NULL;
 
    dup->target_name   = src->target_name
-                           ? eina_stringshare_ref(src->target_name) : NULL;
+                           ? eina_stringshare_add(src->target_name) : NULL;
    dup->expected_type = src->expected_type;
    dup->field_mask    = src->field_mask;
 
@@ -789,7 +791,7 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
      }
    if (src->field_mask & EDJE_VG_OVR_COLOR_CLASS)
      dup->payload.color.color_class = src->payload.color.color_class
-                                       ? eina_stringshare_ref(src->payload.color.color_class)
+                                       ? eina_stringshare_add(src->payload.color.color_class)
                                        : NULL;
 
    if (src->field_mask & EDJE_VG_OVR_TRANSFORM)
@@ -800,7 +802,7 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
      {
         if (src->field_mask & EDJE_VG_OVR_PATH)
           dup->payload.shape.path = src->payload.shape.path
-                                     ? eina_stringshare_ref(src->payload.shape.path)
+                                     ? eina_stringshare_add(src->payload.shape.path)
                                      : NULL;
 
         if (src->field_mask & EDJE_VG_OVR_FILL_RAW)
@@ -811,14 +813,14 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
              dup->payload.shape.fill.a = src->payload.shape.fill.a;
              dup->payload.shape.gradient_ref =
                 src->payload.shape.gradient_ref
-                   ? eina_stringshare_ref(src->payload.shape.gradient_ref)
+                   ? eina_stringshare_add(src->payload.shape.gradient_ref)
                    : NULL;
           }
 
         if (src->field_mask & EDJE_VG_OVR_FILL_CLASS)
           dup->payload.shape.fill.color_class =
              src->payload.shape.fill.color_class
-                ? eina_stringshare_ref(src->payload.shape.fill.color_class)
+                ? eina_stringshare_add(src->payload.shape.fill.color_class)
                 : NULL;
 
         if (src->field_mask & EDJE_VG_OVR_STROKE_RAW)
@@ -832,7 +834,7 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
         if (src->field_mask & EDJE_VG_OVR_STROKE_CLASS)
           dup->payload.shape.stroke_color.color_class =
              src->payload.shape.stroke_color.color_class
-                ? eina_stringshare_ref(src->payload.shape.stroke_color.color_class)
+                ? eina_stringshare_add(src->payload.shape.stroke_color.color_class)
                 : NULL;
 
         if (src->field_mask & EDJE_VG_OVR_STROKE_WIDTH)
@@ -900,7 +902,7 @@ _edje_vg_override_dup(const Edje_Vg_Override *src)
                           dup->payload.gradient.stops[i].color.color_class;
                        if (cc)
                           dup->payload.gradient.stops[i].color.color_class =
-                             eina_stringshare_ref(cc);
+                             eina_stringshare_add(cc);
                     }
                }
           }
@@ -1151,7 +1153,7 @@ _color_lerp_with_ref(Edje_Vg_Color_Binding *out_cb,
 
    /* _edje_vg_color_lerp left a borrowed stepped pointer; acquire a ref. */
    if (out_cb->color_class)
-     out_cb->color_class = eina_stringshare_ref(out_cb->color_class);
+     out_cb->color_class = eina_stringshare_add(out_cb->color_class);
 }
 
 static void
@@ -1310,7 +1312,7 @@ _node_lerp_recursive(Edje_Vg_Node *out_node,
                                 out_node->gradient.stops[i].color.color_class;
                              if (cc)
                                out_node->gradient.stops[i].color.color_class =
-                                  eina_stringshare_ref(cc);
+                                  eina_stringshare_add(cc);
                           }
                      }
                    /* Spread: step — out holds B's value */
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 4bc5bdaf1b..055842e93e 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -9,6 +9,37 @@
  * Only the bare-minimum API needed by the Task 1.3 round-trip test is
  * exposed here.  The full helper surface (lookup, dup, override-apply,
  * colour resolve, materialise) belongs to Phase 2.
+ *
+ * String-ownership convention (Eet-dictionary vs stringshare):
+ *
+ *   The string fields on Edje_Vg_Tree, Edje_Vg_Node, Edje_Vg_Override and
+ *   Edje_Vg_Color_Binding are typed `const char *` because they carry
+ *   either of two ownership models depending on how the containing tree
+ *   was produced:
+ *
+ *     1. Loaded from an .edj file (Eet decode) — strings are borrowed
+ *        pointers into the file's mmap'd dictionary section. The struct
+ *        does NOT own them; freeing them would corrupt the dictionary.
+ *        These trees are freed with `free_strings = EINA_FALSE` (which
+ *        skips the per-field eina_stringshare_del calls).
+ *
+ *     2. Produced by the parser, the dup helpers, the edit API, or any
+ *        other in-memory manipulation — strings are stringshare-owned
+ *        (always created via eina_stringshare_add, never raw strdup).
+ *        These trees are freed with `free_strings = EINA_TRUE`.
+ *
+ *   Rules of thumb for new code:
+ *     * In dup paths, always use `eina_stringshare_add(src->field)` —
+ *       NEVER `eina_stringshare_ref`. The `_ref` call requires the input
+ *       to be a real stringshare handle; a dictionary-backed pointer
+ *       will fail the magic check at runtime.
+ *     * In free paths, the per-field `eina_stringshare_del` calls must
+ *       be gated on `free_strings`. The struct field type cannot tell
+ *       you which ownership applies — only the caller knows.
+ *     * In code that always operates on a stringshare-owned tree (lerp,
+ *       resolve, materialise, edit-API setters), call eina_stringshare_*
+ *       directly without any flag — the invariant is that working trees
+ *       are dups, hence stringshare-owned.
  */
 
 /* --- Test fixture -------------------------------------------------------- */
@@ -77,11 +108,13 @@ EAPI Efl_VG *_edje_vg_tree_to_efl_vg(const Edje_Vg_Tree *t);
 
 /* --- Phase 4.1 helpers --------------------------------------------------- */
 
-/* Deep-clone the in-memory tree. Strings are stringshare-shared with the
- * source via eina_stringshare_ref where the source is already stringshared.
- * Container children are recursively duplicated; gradient stops and
- * stroke_dash arrays are memcpy'd into fresh allocations. The result is
- * fully independent of the source — mutating one does not affect the other. */
+/* Deep-clone the in-memory tree.  Every string is copied into a fresh
+ * eina_stringshare_add'd handle so the dup is safe to free with
+ * free_strings=EINA_TRUE regardless of whether src came from an Eet
+ * dictionary mapping (.edj load) or from heap stringshares (parser /
+ * Edje_Edit).  Container children are recursively duplicated; gradient
+ * stops and stroke_dash arrays are memcpy'd into fresh allocations.
+ * The result is fully independent of the source. */
 EAPI Edje_Vg_Tree *_edje_vg_tree_dup(const Edje_Vg_Tree *t);
 
 /* Apply a single override onto a working tree in place. The override's

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to