wireshark/epan/dfilter/sttype-function.c
João Valverde fab32ea0cb dfilter: Allow arithmetic expressions as function arguments
This allows writing moderately complex expressions, for example
a float epsilon test (#16483):

Filter: {abs(_ws.ftypes.double - 1) / max(abs(_ws.ftypes.double), abs(1))} < 0.01

Syntax tree:
 0 TEST_LT:
   1 OP_DIVIDE:
     2 FUNCTION(abs#1):
       3 OP_SUBTRACT:
         4 FIELD(_ws.ftypes.double)
         4 FVALUE(1 <FT_DOUBLE>)
     2 FUNCTION(max#2):
       3 FUNCTION(abs#1):
         4 FIELD(_ws.ftypes.double)
       3 FUNCTION(abs#1):
         4 FVALUE(1 <FT_DOUBLE>)
   1 FVALUE(0.01 <FT_DOUBLE>)

Instructions:
00000 READ_TREE		_ws.ftypes.double -> reg#1
00001 IF_FALSE_GOTO	3
00002 SUBRACT		reg#1 - 1 <FT_DOUBLE> -> reg#2
00003 STACK_PUSH	reg#2
00004 CALL_FUNCTION	abs(reg#2) -> reg#0
00005 STACK_POP	1
00006 IF_FALSE_GOTO	24
00007 READ_TREE		_ws.ftypes.double -> reg#1
00008 IF_FALSE_GOTO	9
00009 STACK_PUSH	reg#1
00010 CALL_FUNCTION	abs(reg#1) -> reg#4
00011 STACK_POP	1
00012 IF_FALSE_GOTO	13
00013 STACK_PUSH	reg#4
00014 STACK_PUSH	1 <FT_DOUBLE>
00015 CALL_FUNCTION	abs(1 <FT_DOUBLE>) -> reg#5
00016 STACK_POP	1
00017 IF_FALSE_GOTO	18
00018 STACK_PUSH	reg#5
00019 CALL_FUNCTION	max(reg#5, reg#4) -> reg#3
00020 STACK_POP	2
00021 IF_FALSE_GOTO	24
00022 DIVIDE		reg#0 / reg#3 -> reg#6
00023 ANY_LT		reg#6 < 0.01 <FT_DOUBLE>
00024 RETURN

We now use a stack to pass arguments to the function. The
stack is implemented as a list of lists (list of registers).
Arguments may still be non-existent to functions (this is
a feature). Functions must check for nil arguments (NULL lists)
and handle that case.

It's somewhat complicated to allow literal values and test compatibility
for different types, both because of lack of type information with
unparsed/literal and also because it is an underdeveloped area in the
code. In my limited testing it was good enough and useful, further
enhancements are left for future work.
2022-04-18 17:10:31 +01:00

177 lines
3.3 KiB
C

/*
* Copyright (c) 2006 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* Wireshark - Network traffic analyzer
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include "syntax-tree.h"
#include "sttype-function.h"
#include <wsutil/ws_assert.h>
typedef struct {
guint32 magic;
df_func_def_t *funcdef;
GSList *params;
} function_t;
#define FUNCTION_MAGIC 0xe10f0f99
static gpointer
function_new(gpointer funcdef)
{
function_t *stfuncrec;
stfuncrec = g_new(function_t, 1);
stfuncrec->magic = FUNCTION_MAGIC;
stfuncrec->funcdef = funcdef;
stfuncrec->params = NULL;
return stfuncrec;
}
static gpointer
function_dup(gconstpointer data)
{
const function_t *org = data;
function_t *stfuncrec;
GSList *p;
stfuncrec = function_new(org->funcdef);
for (p = org->params; p; p = p->next) {
const stnode_t *param = p->data;
stfuncrec->params = g_slist_append(stfuncrec->params, stnode_dup(param));
}
return stfuncrec;
}
static char *
function_tostr(const void *data, gboolean pretty)
{
const function_t *stfuncrec = data;
const df_func_def_t *def = stfuncrec->funcdef;
GSList *params = stfuncrec->params;
GString *repr = g_string_new("");
ws_assert(def);
if (pretty) {
g_string_printf(repr, "%s(", def->name);
while (params != NULL) {
ws_assert(params->data);
g_string_append(repr, stnode_tostr(params->data, pretty));
params = params->next;
if (params != NULL) {
g_string_append(repr, ", ");
}
}
g_string_append_c(repr, ')');
}
else {
g_string_printf(repr, "%s#%u", def->name, g_slist_length(params));
}
return g_string_free(repr, FALSE);
}
static void
slist_stnode_free(gpointer data)
{
stnode_free(data);
}
void
st_funcparams_free(GSList *params)
{
g_slist_free_full(params, slist_stnode_free);
}
static void
function_free(gpointer value)
{
function_t *stfuncrec = value;
ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
st_funcparams_free(stfuncrec->params);
g_free(stfuncrec);
}
/* Set the parameters for a function stnode_t. */
void
sttype_function_set_params(stnode_t *node, GSList *params)
{
function_t *stfuncrec;
stfuncrec = stnode_data(node);
ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
stfuncrec->params = params;
}
/* Get the function-definition record for a function stnode_t. */
df_func_def_t*
sttype_function_funcdef(stnode_t *node)
{
function_t *stfuncrec;
stfuncrec = stnode_data(node);
ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
return stfuncrec->funcdef;
}
const char *
sttype_function_name(stnode_t *node)
{
function_t *stfuncrec;
stfuncrec = stnode_data(node);
ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
return stfuncrec->funcdef->name;
}
/* Get the parameters for a function stnode_t. */
GSList*
sttype_function_params(stnode_t *node)
{
function_t *stfuncrec;
stfuncrec = stnode_data(node);
ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
return stfuncrec->params;
}
void
sttype_register_function(void)
{
static sttype_t function_type = {
STTYPE_FUNCTION,
"FUNCTION",
function_new,
function_free,
function_dup,
function_tostr
};
sttype_register(&function_type);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/