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 67c737468544bd71d1db1ba174f05ce1893c2f63
Author: [email protected] <[email protected]>
AuthorDate: Thu Apr 30 08:57:46 2026 -0600

    edje: enforce VEC014 transform-form mutual exclusion (Phase 7.3)
    
    Closes the Phase 2.5 carryover (project_edje_vg_phase7_vec014): mixing
    transform.matrix: and any decomposed form (translate/rotate/scale) on the
    same node within a single description's authoring is now a hard VEC014
    error per spec §3.2. Inheritance remains allowed—a child description
    overriding a parent's matrix with a decomposed component is valid.
    
    Implementation uses a per-node bitset tracker (_vg_xform_seen, keyed by
    node pointer via eina_hash_pointer_new) to detect conflicts. Each
    transform handler (translate/rotate/scale/matrix) calls _vg_xform_track
    with its form bit (VG_XFORM_BIT_DECOMPOSED or VG_XFORM_BIT_MATRIX) after
    assignments. The tracker clears at description entry via
    eina_hash_free_buckets in ob_collections_group_parts_part_description,
    ensuring VEC014 only fires for conflicts within the single description's
    own statements.
    
    The has_matrix flag toggling in all four handlers is preserved; it still
    serves Phase 4.4's inheritance arbitration and remains harmless within
    a single description where VEC014 prevents the mixing.
    
    New tests: edje_vg_diag_vec014_transform_conflict (matrix + rotate in
    one description) and edje_vg_diag_vec014_inherit_allowed (parent matrix
    + child rotate across descriptions). All 74/74 tests pass.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/bin/edje/edje_cc_handlers.c          | 48 ++++++++++++++++++++++
 src/tests/edje/edje_test_vector_states.c | 68 ++++++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c
index adf7b29083..f80a843e04 100644
--- a/src/bin/edje/edje_cc_handlers.c
+++ b/src/bin/edje/edje_cc_handlers.c
@@ -200,6 +200,41 @@ static Edje_Vg_Override *current_vg_override = NULL;
    do { if (current_vg_override) \
           current_vg_override->field_mask |= EDJE_VG_OVR_##BIT; } while (0)
 
+/* Phase 7.3 — VEC014 transform-form mutual exclusion.  Tracks which form
+ * ({DECOMPOSED, MATRIX}) of transform statement has fired on each node since
+ * the current description was entered.  Cleared at description entry.
+ * Hash key: node pointer value (passed as &current_vg_node so eina copies the
+ * 8-byte pointer value).  Stored value: (uintptr_t bitset + 1) to distinguish
+ * "no entry" (NULL) from "entry with 0 bits set" (impossible but defensive). */
+static Eina_Hash *_vg_xform_seen = NULL;
+
+#define VG_XFORM_BIT_DECOMPOSED 0x1
+#define VG_XFORM_BIT_MATRIX     0x2
+
+static void
+_vg_xform_track(int new_bit)
+{
+   if (!current_vg_node) return;
+   if (!_vg_xform_seen)
+     _vg_xform_seen = eina_hash_pointer_new(NULL);
+
+   uintptr_t encoded = (uintptr_t)eina_hash_find(_vg_xform_seen,
+                                                  &current_vg_node);
+   uintptr_t prev = encoded ? (encoded - 1) : 0;
+
+   if (((prev & VG_XFORM_BIT_MATRIX)     && (new_bit & VG_XFORM_BIT_DECOMPOSED)) ||
+       ((prev & VG_XFORM_BIT_DECOMPOSED) && (new_bit & VG_XFORM_BIT_MATRIX)))
+     {
+        edje_diag_emit("VEC014", EDJE_DIAG_ERR, file_in, line, NULL, NULL,
+                       "transform.matrix and decomposed transform "
+                       "(translate/rotate/scale) are mutually exclusive on "
+                       "the same node within one description");
+     }
+
+   uintptr_t updated = prev | (uintptr_t)new_bit;
+   eina_hash_set(_vg_xform_seen, &current_vg_node, (void *)(updated + 1));
+}
+
 /* Record a description → tree linkage in the side table. */
 static void
 _vg_record_use(Edje_Part_Description_Vector *ed, Edje_Vg_Tree *t)
@@ -247,11 +282,14 @@ _edje_cc_vg_cleanup(void)
    if (current_vg_node_stack) eina_array_free(current_vg_node_stack);
    if (toplevel_vg_trees) eina_hash_free(toplevel_vg_trees);
    if (_vg_desc_to_tree) eina_hash_free(_vg_desc_to_tree);
+   /* Phase 7.3: free the per-description transform-statement tracker. */
+   if (_vg_xform_seen) eina_hash_free(_vg_xform_seen);
    current_vg_tree = NULL;
    current_vg_node = NULL;
    current_vg_node_stack = NULL;
    toplevel_vg_trees = NULL;
    _vg_desc_to_tree = NULL;
+   _vg_xform_seen = NULL;
 }
 
 Eina_List *po_files;
@@ -2393,6 +2431,7 @@ st_vg_node_transform_translate(void)
    current_vg_node->xform.ty = parse_float(1);
    current_vg_node->xform.has_matrix = 0;
    VG_FLAG(TRANSFORM);
+   _vg_xform_track(VG_XFORM_BIT_DECOMPOSED);
 }
 
 /**
@@ -2414,6 +2453,7 @@ st_vg_node_transform_rotate(void)
    current_vg_node->xform.angle = parse_float(0);
    current_vg_node->xform.has_matrix = 0;
    VG_FLAG(TRANSFORM);
+   _vg_xform_track(VG_XFORM_BIT_DECOMPOSED);
 }
 
 /**
@@ -2436,6 +2476,7 @@ st_vg_node_transform_scale(void)
    current_vg_node->xform.sy = parse_float(1);
    current_vg_node->xform.has_matrix = 0;
    VG_FLAG(TRANSFORM);
+   _vg_xform_track(VG_XFORM_BIT_DECOMPOSED);
 }
 
 /**
@@ -2461,6 +2502,7 @@ st_vg_node_transform_matrix(void)
      current_vg_node->xform.m[i] = parse_float(i);
    current_vg_node->xform.has_matrix = 1;
    VG_FLAG(TRANSFORM);
+   _vg_xform_track(VG_XFORM_BIT_MATRIX);
 }
 
 /* -----------------------------------------------------------------------
@@ -10050,6 +10092,12 @@ ob_collections_group_parts_part_description(void)
   ed->minmul.have = 1;
   ed->minmul.w = FROM_DOUBLE(1.0);
   ed->minmul.h = FROM_DOUBLE(1.0);
+
+  /* Phase 7.3: clear the per-node transform-statement bitset so that VEC014
+   * only fires for conflicts within this single description's own statements.
+   * Inheritance is exempt: a child overriding a parent's matrix with a
+   * decomposed form is valid and sees a clean tracker here. */
+  if (_vg_xform_seen) eina_hash_free_buckets(_vg_xform_seen);
 }
 
 static void
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index dc27bf0ae6..fc25a558a2 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -2016,6 +2016,71 @@ EFL_START_TEST(edje_vg_diag_vec012_missing_fill)
 }
 EFL_END_TEST
 
+/* VEC014 — transform.matrix and decomposed form on the same node within one
+ * description: must error (non-zero exit). */
+EFL_START_TEST(edje_vg_diag_vec014_transform_conflict)
+{
+   static const char edc[] =
+      "collections {\n"
+      "   vectors { vector { name: \"t\"; viewbox: 0 0 1 1;\n"
+      "      shape { name: \"s\";\n"
+      "         path: \"M0,0 L1,1\";\n"
+      "         fill.color: 255 0 0 255;\n"
+      "         transform.matrix: 1 0 0  0 1 0  0 0 1;\n"
+      "         transform.rotate: 30;\n"
+      "      }\n"
+      "   } }\n"
+      "   group { name: \"x\"; parts { part { name: \"v\"; type: VECTOR;\n"
+      "      description { state: \"default\" 0.0; vector.use: \"t\"; }\n"
+      "   } } }\n"
+      "}\n";
+   ck_assert_msg(_compile_with_diag(edc, "VEC014", EINA_TRUE),
+                 "VEC014 (transform.matrix + decomposed in same desc) should error");
+}
+EFL_END_TEST
+
+/* VEC014 positive control — parent has matrix, child overrides with decomposed
+ * in a DIFFERENT description.  Inheritance is exempt: must compile cleanly. */
+EFL_START_TEST(edje_vg_diag_vec014_inherit_allowed)
+{
+   static const char edc[] =
+      "collections {\n"
+      "   vectors { vector { name: \"t\"; viewbox: 0 0 1 1;\n"
+      "      shape { name: \"s\";\n"
+      "         path: \"M0,0 L1,1\";\n"
+      "         fill.color: 255 0 0 255;\n"
+      "         transform.matrix: 1 0 0  0 1 0  0 0 1;\n"
+      "      }\n"
+      "   } }\n"
+      "   group { name: \"x\"; parts { part { name: \"v\"; type: VECTOR;\n"
+      "      description { state: \"default\" 0.0; vector.use: \"t\"; }\n"
+      "      description { state: \"alt\" 0.0;\n"
+      "         inherit: \"default\" 0.0;\n"
+      "         vector.shape { target: \"s\"; transform.rotate: 45; }\n"
+      "      }\n"
+      "   } } }\n"
+      "}\n";
+   /* Write EDC and compile; expect zero exit (clean). */
+   char edc_path[PATH_MAX];
+   snprintf(edc_path, sizeof(edc_path), "/tmp/edje_diag_vec014_ok_%d.edc",
+            (int)getpid());
+   FILE *f = fopen(edc_path, "w");
+   fail_if(!f);
+   fputs(edc, f);
+   fclose(f);
+
+   char cmd[PATH_MAX * 2];
+   snprintf(cmd, sizeof(cmd),
+            "EFL_RUN_IN_TREE=1 %s -beta -fd /tmp '%s' /dev/null 2>/dev/null",
+            _EDJE_CC_BIN, edc_path);
+   int ret = system(cmd);
+   unlink(edc_path);
+
+   ck_assert_msg(ret == 0,
+                 "VEC014: cross-description matrix+decomposed should compile clean");
+}
+EFL_END_TEST
+
 void
 edje_test_vector_states(TCase *tc)
 {
@@ -2058,4 +2123,7 @@ edje_test_vector_states(TCase *tc)
    tcase_add_test(tc, edje_vg_diag_vec003_invalid_path);
    tcase_add_test(tc, edje_vg_diag_vec011_undeclared_class);
    tcase_add_test(tc, edje_vg_diag_vec012_missing_fill);
+   /* Phase 7.3 VEC014 transform-form mutual exclusion */
+   tcase_add_test(tc, edje_vg_diag_vec014_transform_conflict);
+   tcase_add_test(tc, edje_vg_diag_vec014_inherit_allowed);
 }

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

Reply via email to