From: Tom Tromey Date: Mon, 8 Mar 2021 14:27:57 +0000 (-0700) Subject: Implement function call operations X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a00b7254fb614;p=binutils-gdb.git Implement function call operations This implement function call operations. The current function call code relies on some very lengthy code (evaluate_funcall is 398 lines...) to distinguish between the different opcodes that might appear in the callee position. Rather than try to replicate this, and have a function that tried to dissect many different kinds of operation subclass, this patch instead puts the work into the callee. A new operation::evaluate_funcall method is added, and then this is overridden in the classes that require special treatment. gdb/ChangeLog 2021-03-08 Tom Tromey * expression.h (class operation) : New methods. * expop.h (class scope_operation) : New method. (class var_value_operation) : New method. (class structop_base_operation) : New method. (class var_msym_value_operation) : New method. (class structop_member_base): New class. (class structop_member_operation): Derive from structop_member_base. (class structop_mptr_operation): Derive from structop_member_base. (class funcall_operation): New class. * eval.c (operation::evaluate_funcall) (var_value_operation::evaluate_funcall) (scope_operation::evaluate_funcall) (structop_member_base::evaluate_funcall) (structop_base_operation::evaluate_funcall): New methods. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 03a7ff1fab2..cbd7c5fb988 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,22 @@ +2021-03-08 Tom Tromey + + * expression.h (class operation) : New methods. + * expop.h (class scope_operation) : New method. + (class var_value_operation) : New method. + (class structop_base_operation) : New method. + (class var_msym_value_operation) : New method. + (class structop_member_base): New class. + (class structop_member_operation): Derive from + structop_member_base. + (class structop_mptr_operation): Derive from + structop_member_base. + (class funcall_operation): New class. + * eval.c (operation::evaluate_funcall) + (var_value_operation::evaluate_funcall) + (scope_operation::evaluate_funcall) + (structop_member_base::evaluate_funcall) + (structop_base_operation::evaluate_funcall): New methods. + 2021-03-08 Tom Tromey * expop.h (class array_operation): New. diff --git a/gdb/eval.c b/gdb/eval.c index 812068465d7..f1302ef6f54 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -1205,6 +1205,286 @@ evaluate_funcall (type *expect_type, expression *exp, int *pos, var_func_name, expect_type); } +namespace expr +{ + +value * +operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const char *function_name, + const std::vector &args) +{ + std::vector vals (args.size ()); + + value *callee = evaluate_with_coercion (exp, noside); + for (int i = 0; i < args.size (); ++i) + vals[i] = args[i]->evaluate_with_coercion (exp, noside); + + return evaluate_subexp_do_call (exp, noside, callee, vals, + function_name, expect_type); +} + +value * +var_value_operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + if (!overload_resolution + || exp->language_defn->la_language != language_cplus) + return operation::evaluate_funcall (expect_type, exp, noside, args); + + std::vector argvec (args.size ()); + for (int i = 0; i < args.size (); ++i) + argvec[i] = args[i]->evaluate_with_coercion (exp, noside); + + struct symbol *symp; + find_overload_match (argvec, NULL, NON_METHOD, + NULL, std::get<0> (m_storage), + NULL, &symp, NULL, 0, noside); + + if (SYMBOL_TYPE (symp)->code () == TYPE_CODE_ERROR) + error_unknown_type (symp->print_name ()); + value *callee = evaluate_var_value (noside, std::get<1> (m_storage), symp); + + return evaluate_subexp_do_call (exp, noside, callee, argvec, + nullptr, expect_type); +} + +value * +scope_operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + if (!overload_resolution + || exp->language_defn->la_language != language_cplus) + return operation::evaluate_funcall (expect_type, exp, noside, args); + + /* Unpack it locally so we can properly handle overload + resolution. */ + const std::string &name = std::get<1> (m_storage); + struct type *type = std::get<0> (m_storage); + + symbol *function = NULL; + const char *function_name = NULL; + std::vector argvec (1 + args.size ()); + if (type->code () == TYPE_CODE_NAMESPACE) + { + function = cp_lookup_symbol_namespace (type->name (), + name.c_str (), + get_selected_block (0), + VAR_DOMAIN).symbol; + if (function == NULL) + error (_("No symbol \"%s\" in namespace \"%s\"."), + name.c_str (), type->name ()); + } + else + { + gdb_assert (type->code () == TYPE_CODE_STRUCT + || type->code () == TYPE_CODE_UNION); + function_name = name.c_str (); + + /* We need a properly typed value for method lookup. */ + argvec[0] = value_zero (type, lval_memory); + } + + for (int i = 0; i < args.size (); ++i) + argvec[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + gdb::array_view arg_view = argvec; + + value *callee = nullptr; + if (function_name != nullptr) + { + int static_memfuncp; + + find_overload_match (arg_view, function_name, METHOD, + &argvec[0], nullptr, &callee, nullptr, + &static_memfuncp, 0, noside); + if (!static_memfuncp) + { + /* For the time being, we don't handle this. */ + error (_("Call to overloaded function %s requires " + "`this' pointer"), + function_name); + } + + arg_view = arg_view.slice (1); + } + else + { + symbol *symp; + arg_view = arg_view.slice (1); + find_overload_match (arg_view, nullptr, + NON_METHOD, nullptr, function, + nullptr, &symp, nullptr, 1, noside); + callee = value_of_variable (symp, get_selected_block (0)); + } + + return evaluate_subexp_do_call (exp, noside, callee, arg_view, + nullptr, expect_type); +} + +value * +structop_member_base::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + /* First, evaluate the structure into lhs. */ + value *lhs; + if (opcode () == STRUCTOP_MEMBER) + lhs = std::get<0> (m_storage)->evaluate_for_address (exp, noside); + else + lhs = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + + std::vector vals (args.size () + 1); + gdb::array_view val_view = vals; + /* If the function is a virtual function, then the aggregate + value (providing the structure) plays its part by providing + the vtable. Otherwise, it is just along for the ride: call + the function directly. */ + value *rhs = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); + value *callee; + + type *a1_type = check_typedef (value_type (rhs)); + if (a1_type->code () == TYPE_CODE_METHODPTR) + { + if (noside == EVAL_AVOID_SIDE_EFFECTS) + callee = value_zero (TYPE_TARGET_TYPE (a1_type), not_lval); + else + callee = cplus_method_ptr_to_value (&lhs, rhs); + + vals[0] = lhs; + } + else if (a1_type->code () == TYPE_CODE_MEMBERPTR) + { + struct type *type_ptr + = lookup_pointer_type (TYPE_SELF_TYPE (a1_type)); + struct type *target_type_ptr + = lookup_pointer_type (TYPE_TARGET_TYPE (a1_type)); + + /* Now, convert this value to an address. */ + lhs = value_cast (type_ptr, lhs); + + long mem_offset = value_as_long (rhs); + + callee = value_from_pointer (target_type_ptr, + value_as_long (lhs) + mem_offset); + callee = value_ind (callee); + + val_view = val_view.slice (1); + } + else + error (_("Non-pointer-to-member value used in pointer-to-member " + "construct")); + + for (int i = 0; i < args.size (); ++i) + vals[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + + return evaluate_subexp_do_call (exp, noside, callee, val_view, + nullptr, expect_type); + +} + +value * +structop_base_operation::evaluate_funcall + (struct type *expect_type, struct expression *exp, enum noside noside, + const std::vector &args) +{ + std::vector vals (args.size () + 1); + /* First, evaluate the structure into vals[0]. */ + enum exp_opcode op = opcode (); + if (op == STRUCTOP_STRUCT) + { + /* If v is a variable in a register, and the user types + v.method (), this will produce an error, because v has no + address. + + A possible way around this would be to allocate a copy of + the variable on the stack, copy in the contents, call the + function, and copy out the contents. I.e. convert this + from call by reference to call by copy-return (or + whatever it's called). However, this does not work + because it is not the same: the method being called could + stash a copy of the address, and then future uses through + that address (after the method returns) would be expected + to use the variable itself, not some copy of it. */ + vals[0] = std::get<0> (m_storage)->evaluate_for_address (exp, noside); + } + else + { + vals[0] = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + /* Check to see if the operator '->' has been overloaded. + If the operator has been overloaded replace vals[0] with the + value returned by the custom operator and continue + evaluation. */ + while (unop_user_defined_p (op, vals[0])) + { + struct value *value = nullptr; + try + { + value = value_x_unop (vals[0], op, noside); + } + catch (const gdb_exception_error &except) + { + if (except.error == NOT_FOUND_ERROR) + break; + else + throw; + } + + vals[0] = value; + } + } + + for (int i = 0; i < args.size (); ++i) + vals[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + gdb::array_view arg_view = vals; + + int static_memfuncp; + value *callee; + const char *tstr = std::get<1> (m_storage).c_str (); + if (overload_resolution + && exp->language_defn->la_language == language_cplus) + { + /* Language is C++, do some overload resolution before + evaluation. */ + value *val0 = vals[0]; + find_overload_match (arg_view, tstr, METHOD, + &val0, nullptr, &callee, nullptr, + &static_memfuncp, 0, noside); + vals[0] = val0; + } + else + /* Non-C++ case -- or no overload resolution. */ + { + struct value *temp = vals[0]; + + callee = value_struct_elt (&temp, &vals[1], tstr, + &static_memfuncp, + op == STRUCTOP_STRUCT + ? "structure" : "structure pointer"); + /* value_struct_elt updates temp with the correct value of the + ``this'' pointer if necessary, so modify it to reflect any + ``this'' changes. */ + vals[0] = value_from_longest (lookup_pointer_type (value_type (temp)), + value_address (temp) + + value_embedded_offset (temp)); + } + + /* Take out `this' if needed. */ + if (static_memfuncp) + arg_view = arg_view.slice (1); + + return evaluate_subexp_do_call (exp, noside, callee, arg_view, + nullptr, expect_type); +} + + +} /* namespace expr */ + /* Return true if type is integral or reference to integral */ static bool diff --git a/gdb/expop.h b/gdb/expop.h index 9b5c4ea35e4..8ac7947a68c 100644 --- a/gdb/expop.h +++ b/gdb/expop.h @@ -597,6 +597,11 @@ public: value *evaluate_for_address (struct expression *exp, enum noside noside) override; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + enum exp_opcode opcode () const override { return OP_SCOPE; } @@ -634,6 +639,11 @@ public: value *evaluate_for_address (struct expression *exp, enum noside noside) override; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + enum exp_opcode opcode () const override { return OP_VAR_VALUE; } @@ -702,6 +712,15 @@ public: struct expression *exp, enum noside noside) override; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override + { + const char *name = std::get<0> (m_storage)->print_name (); + return operation::evaluate_funcall (expect_type, exp, noside, name, args); + } + enum exp_opcode opcode () const override { return OP_VAR_MSYM_VALUE; } @@ -974,6 +993,11 @@ public: EVAL_AVOID_SIDE_EFFECTS); } + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + protected: using tuple_holding_operation::tuple_holding_operation; @@ -1047,13 +1071,26 @@ protected: } }; -class structop_member_operation +class structop_member_base : public tuple_holding_operation { public: using tuple_holding_operation::tuple_holding_operation; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; +}; + +class structop_member_operation + : public structop_member_base +{ +public: + + using structop_member_base::structop_member_base; + value *evaluate (struct type *expect_type, struct expression *exp, enum noside noside) override @@ -1070,11 +1107,11 @@ public: }; class structop_mptr_operation - : public tuple_holding_operation + : public structop_member_base { public: - using tuple_holding_operation::tuple_holding_operation; + using structop_member_base::structop_member_base; value *evaluate (struct type *expect_type, struct expression *exp, @@ -2071,6 +2108,27 @@ private: enum noside noside, int nargs); }; +/* A function call. This holds the callee operation and the + arguments. */ +class funcall_operation + : public tuple_holding_operation> +{ +public: + + using tuple_holding_operation::tuple_holding_operation; + + value *evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) override + { + return std::get<0> (m_storage)->evaluate_funcall (expect_type, exp, noside, + std::get<1> (m_storage)); + } + + enum exp_opcode opcode () const override + { return OP_FUNCALL; } +}; + } /* namespace expr */ #endif /* EXPOP_H */ diff --git a/gdb/expression.h b/gdb/expression.h index 4d75058dc22..d20857bf268 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -144,6 +144,19 @@ public: virtual value *evaluate_for_address (struct expression *exp, enum noside noside); + /* Evaluate a function call, with this object as the callee. + EXPECT_TYPE, EXP, and NOSIDE have the same meaning as in + 'evaluate'. ARGS holds the operations that should be evaluated + to get the arguments to the call. */ + virtual value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) + { + /* Defer to the helper overload. */ + return evaluate_funcall (expect_type, exp, noside, nullptr, args); + } + /* True if this is a constant expression. */ virtual bool constant_p () const { return false; } @@ -171,6 +184,13 @@ public: protected: + /* A helper overload that wraps evaluate_subexp_do_call. */ + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const char *function_name, + const std::vector &args); + /* Called by generate_ax to do the work for this particular operation. */ virtual void do_generate_ax (struct expression *exp,