From a00b7254fb614af557de7ae7cc0eb39a0ce0e408 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 8 Mar 2021 07:27:57 -0700 Subject: 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. --- gdb/eval.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) (limited to 'gdb/eval.c') diff --git a/gdb/eval.c b/gdb/eval.c index 8120684..f1302ef 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 -- cgit v1.1