aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog19
-rw-r--r--gdb/eval.c280
-rw-r--r--gdb/expop.h64
-rw-r--r--gdb/expression.h20
4 files changed, 380 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 03a7ff1..cbd7c5f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,24 @@
2021-03-08 Tom Tromey <tom@tromey.com>
+ * expression.h (class operation) <evaluate_funcall>: New methods.
+ * expop.h (class scope_operation) <evaluate_funcall>: New method.
+ (class var_value_operation) <evaluate_funcall>: New method.
+ (class structop_base_operation) <evaluate_funcall>: New method.
+ (class var_msym_value_operation) <evaluate_funcall>: 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 <tom@tromey.com>
+
* expop.h (class array_operation): New.
* eval.c (array_operation::evaluate_struct_tuple)
(array_operation::evaluate): New methods.
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<operation_up> &args)
+{
+ std::vector<value *> 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<operation_up> &args)
+{
+ if (!overload_resolution
+ || exp->language_defn->la_language != language_cplus)
+ return operation::evaluate_funcall (expect_type, exp, noside, args);
+
+ std::vector<value *> 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<operation_up> &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<value *> 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<value *> 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<operation_up> &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<value *> vals (args.size () + 1);
+ gdb::array_view<value *> 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<operation_up> &args)
+{
+ std::vector<value *> 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<value *> 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 9b5c4ea..8ac7947 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<operation_up> &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<operation_up> &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<operation_up> &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<operation_up> &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<operation_up, operation_up>
{
public:
using tuple_holding_operation::tuple_holding_operation;
+ value *evaluate_funcall (struct type *expect_type,
+ struct expression *exp,
+ enum noside noside,
+ const std::vector<operation_up> &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<operation_up, operation_up>
+ : 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<operation_up, std::vector<operation_up>>
+{
+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 4d75058..d20857b 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<operation_up> &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<operation_up> &args);
+
/* Called by generate_ax to do the work for this particular
operation. */
virtual void do_generate_ax (struct expression *exp,