/* * 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; }