| /* |
| * This file is part of ltrace. |
| * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| * 02110-1301 USA |
| */ |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| #include "expr.h" |
| |
| static void |
| expr_init_common(struct expr_node *node, enum expr_node_kind kind) |
| { |
| node->kind = kind; |
| node->lhs = NULL; |
| node->own_lhs = 0; |
| memset(&node->u, 0, sizeof(node->u)); |
| } |
| |
| void |
| expr_init_self(struct expr_node *node) |
| { |
| expr_init_common(node, EXPR_OP_SELF); |
| } |
| |
| void |
| expr_init_named(struct expr_node *node, |
| const char *name, int own_name) |
| { |
| expr_init_common(node, EXPR_OP_NAMED); |
| node->u.name.s = name; |
| node->u.name.own = own_name; |
| } |
| |
| void |
| expr_init_argno(struct expr_node *node, size_t num) |
| { |
| expr_init_common(node, EXPR_OP_ARGNO); |
| node->u.num = num; |
| } |
| |
| void |
| expr_init_const(struct expr_node *node, struct value *val) |
| { |
| expr_init_common(node, EXPR_OP_CONST); |
| node->u.value = *val; |
| } |
| |
| void |
| expr_init_const_word(struct expr_node *node, long l, |
| struct arg_type_info *type, int own_type) |
| { |
| struct value val; |
| value_init_detached(&val, NULL, type, own_type); |
| value_set_word(&val, l); |
| expr_init_const(node, &val); |
| } |
| |
| void |
| expr_init_index(struct expr_node *node, |
| struct expr_node *lhs, int own_lhs, |
| struct expr_node *rhs, int own_rhs) |
| { |
| expr_init_common(node, EXPR_OP_INDEX); |
| node->lhs = lhs; |
| node->own_lhs = own_lhs; |
| node->u.node.n = rhs; |
| node->u.node.own = own_rhs; |
| } |
| |
| void |
| expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs) |
| { |
| assert(lhs != NULL); |
| expr_init_common(node, EXPR_OP_UP); |
| node->lhs = lhs; |
| node->own_lhs = own_lhs; |
| } |
| |
| void |
| expr_init_cb1(struct expr_node *node, |
| int (*cb)(struct value *ret_value, struct value *value, |
| struct value_dict *arguments, void *data), |
| struct expr_node *lhs, int own_lhs, void *data) |
| { |
| expr_init_common(node, EXPR_OP_CALL1); |
| node->lhs = lhs; |
| node->own_lhs = own_lhs; |
| node->u.call.u.cb1 = cb; |
| node->u.call.data = data; |
| } |
| |
| void |
| expr_init_cb2(struct expr_node *node, |
| int (*cb)(struct value *ret_value, |
| struct value *lhs, struct value *rhs, |
| struct value_dict *arguments, void *data), |
| struct expr_node *lhs, int own_lhs, |
| struct expr_node *rhs, int own_rhs, void *data) |
| { |
| expr_init_common(node, EXPR_OP_CALL2); |
| node->lhs = lhs; |
| node->own_lhs = own_lhs; |
| node->u.call.rhs = rhs; |
| node->u.call.own_rhs = own_rhs; |
| node->u.call.u.cb2 = cb; |
| node->u.call.data = data; |
| } |
| |
| static void |
| release_expr(struct expr_node *node, int own) |
| { |
| if (own) { |
| expr_destroy(node); |
| free(node); |
| } |
| } |
| |
| void |
| expr_destroy(struct expr_node *node) |
| { |
| if (node == NULL) |
| return; |
| |
| switch (node->kind) { |
| case EXPR_OP_ARGNO: |
| case EXPR_OP_SELF: |
| return; |
| |
| case EXPR_OP_CONST: |
| value_destroy(&node->u.value); |
| return; |
| |
| case EXPR_OP_NAMED: |
| if (node->u.name.own) |
| free((char *)node->u.name.s); |
| return; |
| |
| case EXPR_OP_INDEX: |
| release_expr(node->lhs, node->own_lhs); |
| release_expr(node->u.node.n, node->u.node.own); |
| return; |
| |
| case EXPR_OP_CALL2: |
| release_expr(node->u.call.rhs, node->u.call.own_rhs); |
| /* Fall through. */ |
| case EXPR_OP_UP: |
| case EXPR_OP_CALL1: |
| release_expr(node->lhs, node->own_lhs); |
| return; |
| } |
| |
| assert(!"Invalid value of node kind"); |
| abort(); |
| } |
| |
| static int |
| expr_alloc_and_clone(struct expr_node **retpp, struct expr_node *node, int own) |
| { |
| *retpp = node; |
| if (own) { |
| *retpp = malloc(sizeof **retpp); |
| if (*retpp == NULL || expr_clone(*retpp, node) < 0) { |
| free(*retpp); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int |
| expr_clone(struct expr_node *retp, const struct expr_node *node) |
| { |
| *retp = *node; |
| |
| switch (node->kind) { |
| struct expr_node *nlhs; |
| struct expr_node *nrhs; |
| |
| case EXPR_OP_ARGNO: |
| case EXPR_OP_SELF: |
| return 0; |
| |
| case EXPR_OP_CONST: |
| return value_clone(&retp->u.value, &node->u.value); |
| |
| case EXPR_OP_NAMED: |
| if (node->u.name.own |
| && (retp->u.name.s = strdup(node->u.name.s)) == NULL) |
| return -1; |
| return 0; |
| |
| case EXPR_OP_INDEX: |
| if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0) |
| return -1; |
| |
| if (expr_alloc_and_clone(&nrhs, node->u.node.n, |
| node->u.node.own) < 0) { |
| if (nlhs != node->lhs) { |
| expr_destroy(nlhs); |
| free(nlhs); |
| } |
| return -1; |
| } |
| |
| retp->lhs = nlhs; |
| retp->u.node.n = nrhs; |
| return 0; |
| |
| case EXPR_OP_CALL2: |
| if (expr_alloc_and_clone(&nrhs, node->u.call.rhs, |
| node->u.call.own_rhs) < 0) |
| return -1; |
| retp->u.call.rhs = nrhs; |
| /* Fall through. */ |
| |
| case EXPR_OP_UP: |
| case EXPR_OP_CALL1: |
| if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0) { |
| if (node->kind == EXPR_OP_CALL2 |
| && node->u.call.own_rhs) { |
| expr_destroy(nrhs); |
| free(nrhs); |
| return -1; |
| } |
| } |
| |
| retp->lhs = nlhs; |
| return 0; |
| } |
| |
| assert(!"Invalid value of node kind"); |
| abort(); |
| } |
| |
| int |
| expr_is_compile_constant(struct expr_node *node) |
| { |
| return node->kind == EXPR_OP_CONST; |
| } |
| |
| static int |
| eval_up(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, struct value *ret_value) |
| { |
| if (expr_eval(node->lhs, context, arguments, ret_value) < 0) |
| return -1; |
| struct value *parent = value_get_parental_struct(ret_value); |
| if (parent == NULL) { |
| value_destroy(ret_value); |
| return -1; |
| } |
| *ret_value = *parent; |
| return 0; |
| } |
| |
| static int |
| eval_cb1(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, struct value *ret_value) |
| { |
| struct value val; |
| if (expr_eval(node->lhs, context, arguments, &val) < 0) |
| return -1; |
| |
| int ret = 0; |
| if (node->u.call.u.cb1(ret_value, &val, arguments, |
| node->u.call.data) < 0) |
| ret = -1; |
| |
| /* N.B. the callback must return its own value, or somehow |
| * clone the incoming argument. */ |
| value_destroy(&val); |
| return ret; |
| } |
| |
| static int |
| eval_cb2(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, struct value *ret_value) |
| { |
| struct value lhs; |
| if (expr_eval(node->lhs, context, arguments, &lhs) < 0) |
| return -1; |
| |
| struct value rhs; |
| if (expr_eval(node->u.call.rhs, context, arguments, &rhs) < 0) { |
| value_destroy(&lhs); |
| return -1; |
| } |
| |
| int ret = 0; |
| if (node->u.call.u.cb2(ret_value, &lhs, &rhs, arguments, |
| node->u.call.data) < 0) |
| ret = -1; |
| |
| /* N.B. the callback must return its own value, or somehow |
| * clone the incoming argument. */ |
| value_destroy(&lhs); |
| value_destroy(&rhs); |
| return ret; |
| } |
| |
| int |
| eval_index(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, struct value *ret_value) |
| { |
| struct value lhs; |
| if (expr_eval(node->lhs, context, arguments, &lhs) < 0) |
| return -1; |
| |
| long l; |
| if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) { |
| fail: |
| value_destroy(&lhs); |
| return -1; |
| } |
| |
| if (value_init_element(ret_value, &lhs, (size_t)l) < 0) |
| goto fail; |
| return 0; |
| } |
| |
| int |
| expr_eval(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, struct value *ret_value) |
| { |
| switch (node->kind) { |
| struct value *valp; |
| case EXPR_OP_ARGNO: |
| valp = val_dict_get_num(arguments, node->u.num); |
| if (valp == NULL) |
| return -1; |
| *ret_value = *valp; |
| return 0; |
| |
| case EXPR_OP_NAMED: |
| valp = val_dict_get_name(arguments, node->u.name.s); |
| if (valp == NULL) |
| return -1; |
| *ret_value = *valp; |
| return 0; |
| |
| case EXPR_OP_SELF: |
| *ret_value = *context; |
| return 0; |
| |
| case EXPR_OP_CONST: |
| *ret_value = node->u.value; |
| return 0; |
| |
| case EXPR_OP_INDEX: |
| return eval_index(node, context, arguments, ret_value); |
| |
| case EXPR_OP_UP: |
| return eval_up(node, context, arguments, ret_value); |
| |
| case EXPR_OP_CALL1: |
| return eval_cb1(node, context, arguments, ret_value); |
| |
| case EXPR_OP_CALL2: |
| return eval_cb2(node, context, arguments, ret_value); |
| } |
| |
| assert(!"Unknown node kind."); |
| abort(); |
| } |
| |
| int |
| expr_eval_word(struct expr_node *node, struct value *context, |
| struct value_dict *arguments, long *ret_value) |
| { |
| struct value val; |
| if (expr_eval(node, context, arguments, &val) < 0) |
| return -1; |
| int ret = 0; |
| if (value_extract_word(&val, ret_value, arguments) < 0) |
| ret = -1; |
| value_destroy(&val); |
| return ret; |
| } |
| |
| int |
| expr_eval_constant(struct expr_node *node, long *valuep) |
| { |
| assert(expr_is_compile_constant(node)); |
| return expr_eval_word(node, NULL, NULL, valuep); |
| } |
| |
| struct expr_node * |
| expr_self(void) |
| { |
| static struct expr_node *nodep = NULL; |
| if (nodep == NULL) { |
| static struct expr_node node; |
| expr_init_self(&node); |
| nodep = &node; |
| } |
| return nodep; |
| } |