diff --git a/lib/filterx/expr-getattr.c b/lib/filterx/expr-getattr.c index c44d2fa53..bfe58c34b 100644 --- a/lib/filterx/expr-getattr.c +++ b/lib/filterx/expr-getattr.c @@ -31,47 +31,74 @@ typedef struct _FilterXGetAttr FilterXExpr super; FilterXExpr *operand; FilterXObject *attr; + FilterXObject **path; } FilterXGetAttr; +static FilterXObject * +_eval_operand(FilterXGetAttr *self, FilterXObject **attr) +{ + FilterXObject *o = filterx_expr_eval_typed(self->operand); + + if (!o) + return NULL; + + *attr = self->attr; + if (*attr) + return o; + + FilterXObject *next_o; + *attr = self->path[0]; + for (gint i = 0; self->path[i] && self->path[i+1]; i++) + { + next_o = filterx_object_getattr(o, *attr); + if (!o) + break; + filterx_object_unref(o); + o = next_o; + *attr = self->path[i+1]; + } + return o; +} + static FilterXObject * _eval_getattr(FilterXExpr *s) { FilterXGetAttr *self = (FilterXGetAttr *) s; + FilterXObject *attr; - FilterXObject *variable = filterx_expr_eval_typed(self->operand); - + FilterXObject *variable = _eval_operand(self, &attr); if (!variable) return NULL; - FilterXObject *attr = filterx_object_getattr(variable, self->attr); - if (!attr) + FilterXObject *value = filterx_object_getattr(variable, attr); + if (!value) { - filterx_eval_push_error("Attribute lookup failed", s, self->attr); + filterx_eval_push_error("Attribute lookup failed", s, attr); goto exit; } exit: filterx_object_unref(variable); - return attr; + return value; } static gboolean _unset(FilterXExpr *s) { FilterXGetAttr *self = (FilterXGetAttr *) s; - + FilterXObject *attr; gboolean result = FALSE; - FilterXObject *variable = filterx_expr_eval_typed(self->operand); + FilterXObject *variable = _eval_operand(self, &attr); if (!variable) return FALSE; if (variable->readonly) { - filterx_eval_push_error("Object unset-attr failed, object is readonly", s, self->attr); + filterx_eval_push_error("Object unset-attr failed, object is readonly", s, attr); goto exit; } - result = filterx_object_unset_key(variable, self->attr); + result = filterx_object_unset_key(variable, attr); exit: filterx_object_unref(variable); @@ -82,22 +109,66 @@ static gboolean _isset(FilterXExpr *s) { FilterXGetAttr *self = (FilterXGetAttr *) s; - FilterXObject *variable = filterx_expr_eval_typed(self->operand); + FilterXObject *attr; + FilterXObject *variable = _eval_operand(self, &attr); if (!variable) return FALSE; - gboolean result = filterx_object_is_key_set(variable, self->attr); + gboolean result = filterx_object_is_key_set(variable, attr); filterx_object_unref(variable); return result; } +static void +_rollup_child_getattr(FilterXGetAttr *self, FilterXGetAttr *child) +{ + /* turn a list of getattrs into a single getattr with a path argument */ + GPtrArray *path = g_ptr_array_new(); + + if (child->attr) + { + /* if this is an unoptimized FilterXGetAttr, let's use the "attr" member */ + g_ptr_array_add(path, filterx_object_ref(child->attr)); + g_assert(child->path == NULL); + } + else + { + /* optimized GetAttr, copy the entire path into ours */ + for (gint i = 0; child->path[i]; i++) + { + g_ptr_array_add(path, filterx_object_ref(child->path[i])); + } + } + + /* we are the last getattr so append it to the end */ + g_ptr_array_add(path, self->attr); + /* null termination */ + g_ptr_array_add(path, NULL); + + /* replace self->attr with self->path */ + self->attr = NULL; + self->path = (FilterXObject **) g_ptr_array_free(path, FALSE); +} + static FilterXExpr * _optimize(FilterXExpr *s) { FilterXGetAttr *self = (FilterXGetAttr *) s; self->operand = filterx_expr_optimize(self->operand); + if (filterx_expr_is_getattr(self->operand)) + { + FilterXGetAttr *child = (FilterXGetAttr *) self->operand; + + _rollup_child_getattr(self, child); + self->operand = filterx_expr_ref(child->operand); + + filterx_expr_unref(&child->super); + + /* we need to return a ref */ + return filterx_expr_ref(s); + } return NULL; } @@ -125,7 +196,20 @@ static void _free(FilterXExpr *s) { FilterXGetAttr *self = (FilterXGetAttr *) s; - filterx_object_unref(self->attr); + + if (self->attr) + { + filterx_object_unref(self->attr); + g_assert(self->path == NULL); + } + else + { + for (gint i = 0; self->path[i]; i++) + filterx_object_unref(self->path[i]); + g_free(self->path); + g_assert(self->attr == NULL); + } + filterx_expr_unref(self->operand); filterx_expr_free_method(s); }