Skip to content

Filterx partial container evaluation#869

Merged
alltilla merged 19 commits intoaxoflow:mainfrom
bazsi:filterx-partial-container-evaluation
Jan 30, 2026
Merged

Filterx partial container evaluation#869
alltilla merged 19 commits intoaxoflow:mainfrom
bazsi:filterx-partial-container-evaluation

Conversation

@bazsi
Copy link
Member

@bazsi bazsi commented Dec 5, 2025

This PR allows dict definitions to be partially evaluated even if they have non-literal initializers. A sparse dictionary will
be initialized in this case, literal elements will be completely set, non-literal ones will be stored as nulls, which then will
be completed at execution time.

@bazsi bazsi force-pushed the filterx-partial-container-evaluation branch 2 times, most recently from 82e7d48 to f707716 Compare December 7, 2025 22:00
@bazsi bazsi mentioned this pull request Dec 7, 2025
@bazsi bazsi closed this Dec 29, 2025
@bazsi bazsi reopened this Dec 29, 2025
@bazsi bazsi force-pushed the filterx-partial-container-evaluation branch 2 times, most recently from f85a922 to cbecee3 Compare December 29, 2025 20:38
Copy link
Member

@alltilla alltilla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done one round of review, found no problems. I will do another round.

@alltilla
Copy link
Member

I managed to trigger a jump on uninitialized value with this config:

log {
  source {
    example-msg-generator(num(1));
  };

  filterx {
    key = "foo";
    d = {
      "foo": "fooval",
      key: "newfooval",
    };
    $MSG = d;
  };

  destination {
    file("/dev/stdout" template("$MSG\n"));
  };
};
==280911== Conditional jump or move depends on uninitialised value(s)
==280911==    at 0x49782CC: filterx_ref_unset_parent_container (filterx-ref.h:109)
==280911==    by 0x4979DBD: filterx_dict_set_subscript_by_anchor (object-dict.c:728)
==280911==    by 0x4945111: _literal_dict_store_elem (expr-literal-container.c:285)
==280911==    by 0x49453CD: _literal_dict_eval (expr-literal-container.c:363)
==280911==    by 0x493C16D: filterx_expr_eval (filterx-expr.h:101)
==280911==    by 0x493C345: _assign_eval (expr-assign.c:76)
==280911==    by 0x493EB76: filterx_expr_eval (filterx-expr.h:101)
==280911==    by 0x493EE57: _eval_expr (expr-compound.c:46)
==280911==    by 0x493F18B: _eval_exprs (expr-compound.c:99)
==280911==    by 0x493F23E: _eval_compound_start (expr-compound.c:123)
==280911==    by 0x493F2CC: _eval_compound (expr-compound.c:148)
==280911==    by 0x4952B0A: filterx_expr_eval (filterx-expr.h:101)
==280911== 
==280911== Conditional jump or move depends on uninitialised value(s)
==280911==    at 0x497801F: filterx_object_unref (filterx-object.h:290)
==280911==    by 0x4979DCD: filterx_dict_set_subscript_by_anchor (object-dict.c:729)
==280911==    by 0x4945111: _literal_dict_store_elem (expr-literal-container.c:285)
==280911==    by 0x49453CD: _literal_dict_eval (expr-literal-container.c:363)
==280911==    by 0x493C16D: filterx_expr_eval (filterx-expr.h:101)
==280911==    by 0x493C345: _assign_eval (expr-assign.c:76)
==280911==    by 0x493EB76: filterx_expr_eval (filterx-expr.h:101)
==280911==    by 0x493EE57: _eval_expr (expr-compound.c:46)
==280911==    by 0x493F18B: _eval_exprs (expr-compound.c:99)
==280911==    by 0x493F23E: _eval_compound_start (expr-compound.c:123)
==280911==    by 0x493F2CC: _eval_compound (expr-compound.c:148)
==280911==    by 0x4952B0A: filterx_expr_eval (filterx-expr.h:101)
==280911== 

@alltilla
Copy link
Member

We have a breaking behavior with this config:

  filterx {
    barvalue = "newbarval";
    d = {
      "foo": "fooval",
      "bar": barvalue,
      "bar": "barval",
    };
    $MSG = d;
  };

On main this produces:

{"foo":"fooval","bar":"barval"}

With the PR:

{"foo":"fooval","bar":"newbarval"}

I don't know how to feel about this. I think the behavior on main is more intuitive and better defined. However implementing something that would offer the old behavior seems unreasonably complex in my head.

@bazsi
Copy link
Member Author

bazsi commented Jan 23, 2026

I managed to trigger a jump on uninitialized value with this config:

fixed. thanks for finding this.

@bazsi
Copy link
Member Author

bazsi commented Jan 23, 2026

We have a breaking behavior with this config:

  filterx {
    barvalue = "newbarval";
    d = {
      "foo": "fooval",
      "bar": barvalue,
      "bar": "barval",
    };
    $MSG = d;
  };

On main this produces:

{"foo":"fooval","bar":"barval"}

With the PR:

{"foo":"fooval","bar":"newbarval"}

I don't know how to feel about this. I think the behavior on main is more intuitive and better defined. However implementing something that would offer the old behavior seems unreasonably complex in my head.

I decided to bail out from the optimization step if we have overlapping keys in any way. That basically pushes the evaluation of the dict to runtime, instead of compile time, but I don't think this case is very common, so I don't care if these dicts are not optimized.

@OverOrion OverOrion requested a review from alltilla January 28, 2026 14:34
@bazsi bazsi force-pushed the filterx-partial-container-evaluation branch from e97b6f6 to 14ea32b Compare January 29, 2026 15:53
bazsi added 16 commits January 30, 2026 09:04
Earlier, both the list/dict evaluation was performed by the same set of
functions, which is changed by this patch, as:
  * lists do not have a "nullv" variant
  * lists don't have keys

Even though we can consider this code duplication, each implementation is
somewhat simpler and some of the code can later be refactored to use smaller
chunks of reusable code.

Signed-off-by: Balazs Scheidler <[email protected]>
…ction

Reflect these assumptions in the container specific code:
* dicts always have a key
* lists don't have keys
* lists don't have nullv style initializers

Signed-off-by: Balazs Scheidler <[email protected]>
Previously these were shoveled into the same function with scattered
conditionals, split them up and try to create smaller reusable snippets.

Signed-off-by: Balazs Scheidler <[email protected]>
…tion time

Even if the dict has a non-literal initializer, we can still partially
initialize it, so we can simply clone it at runtime and fill in the rest.

Signed-off-by: Balazs Scheidler <[email protected]>
We should not set the parent container in this case.

Signed-off-by: Balazs Scheidler <[email protected]>
bazsi added 3 commits January 30, 2026 09:04
If we are unable to initialize a dict, the anchors we stored in the elems
array is invalid, reset them to the default value.

Signed-off-by: Balazs Scheidler <[email protected]>
…ded from early eval

As the order of initialization could depend on whether it was early evaluated
or not, with a config like this:

  filterx {
    barvalue = "newbarval";
    d = {
      "foo": "fooval",
      "bar": barvalue,
      "bar": "barval",
    };
    $MSG = d;
  };


Signed-off-by: Balazs Scheidler <[email protected]>
@bazsi bazsi force-pushed the filterx-partial-container-evaluation branch from 14ea32b to 66589ea Compare January 30, 2026 08:04
@alltilla alltilla merged commit 8838f6c into axoflow:main Jan 30, 2026
22 checks passed
@bazsi
Copy link
Member Author

bazsi commented Jan 30, 2026

thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants