Enable chained function calls in C++ expressions.

gdb/ChangeLog:

	* eval.c: Include gdbthread.h.
	(evaluate_subexp): Enable thread stack temporaries before
	evaluating a complete expression and clean them up after the
	evaluation is complete.
	* gdbthread.h: Include common/vec.h.
	(value_ptr): New typedef.
	(VEC (value_ptr)): New vector type.
	(value_vec): New typedef.
	(struct thread_info): Add new fields stack_temporaries_enabled
	and stack_temporaries.
	(enable_thread_stack_temporaries)
	(thread_stack_temporaries_enabled_p, push_thread_stack_temporary)
	(get_last_thread_stack_temporary)
	(value_in_thread_stack_temporaries): Declare.
	* gdbtypes.c (class_or_union_p): New function.
	* gdbtypes.h (class_or_union_p): Declare.
	* infcall.c (call_function_by_hand): Store return values of class
	type as	temporaries on stack.
	* thread.c (enable_thread_stack_temporaries): New function.
	(thread_stack_temporaries_enabled_p, push_thread_stack_temporary)
	(get_last_thread_stack_temporary): Likewise.
	(value_in_thread_stack_temporaries): Likewise.
	* value.c (value_force_lval): New function.
	* value.h (value_force_lval): Declare.

gdb/testsuite/ChangeLog:

	* gdb.cp/chained-calls.cc: New file.
	* gdb.cp/chained-calls.exp: New file.
	* gdb.cp/smartp.exp: Remove KFAIL for "p c2->inta".
This commit is contained in:
Siva Chandra 2014-11-11 05:43:03 -08:00
parent f4f855e84b
commit 6c659fc2c7
13 changed files with 523 additions and 24 deletions

View File

@ -1,3 +1,30 @@
2014-11-28 Siva Chandra Reddy <sivachandra@google.com>
* eval.c: Include gdbthread.h.
(evaluate_subexp): Enable thread stack temporaries before
evaluating a complete expression and clean them up after the
evaluation is complete.
* gdbthread.h: Include common/vec.h.
(value_ptr): New typedef.
(VEC (value_ptr)): New vector type.
(value_vec): New typedef.
(struct thread_info): Add new fields stack_temporaries_enabled
and stack_temporaries.
(enable_thread_stack_temporaries)
(thread_stack_temporaries_enabled_p, push_thread_stack_temporary)
(get_last_thread_stack_temporary)
(value_in_thread_stack_temporaries): Declare.
* gdbtypes.c (class_or_union_p): New function.
* gdbtypes.h (class_or_union_p): Declare.
* infcall.c (call_function_by_hand): Store return values of class
type as temporaries on stack.
* thread.c (enable_thread_stack_temporaries): New function.
(thread_stack_temporaries_enabled_p, push_thread_stack_temporary)
(get_last_thread_stack_temporary): Likewise.
(value_in_thread_stack_temporaries): Likewise.
* value.c (value_force_lval): New function.
* value.h (value_force_lval): Declare.
2014-11-28 Pierre Muller <muller@sourceware.org>
Pushed by Joel Brobecker <brobecker@adacore.com>.

View File

@ -24,6 +24,7 @@
#include "expression.h"
#include "target.h"
#include "frame.h"
#include "gdbthread.h"
#include "language.h" /* For CAST_IS_CONVERSION. */
#include "f-lang.h" /* For array bound stuff. */
#include "cp-abi.h"
@ -63,8 +64,28 @@ struct value *
evaluate_subexp (struct type *expect_type, struct expression *exp,
int *pos, enum noside noside)
{
return (*exp->language_defn->la_exp_desc->evaluate_exp)
struct cleanup *cleanups;
struct value *retval;
int cleanup_temps = 0;
if (*pos == 0 && target_has_execution
&& exp->language_defn->la_language == language_cplus)
{
cleanups = enable_thread_stack_temporaries (inferior_ptid);
cleanup_temps = 1;
}
retval = (*exp->language_defn->la_exp_desc->evaluate_exp)
(expect_type, exp, pos, noside);
if (cleanup_temps)
{
if (value_in_thread_stack_temporaries (retval, inferior_ptid))
retval = value_non_lval (retval);
do_cleanups (cleanups);
}
return retval;
}
/* Parse the string EXP as a C expression, evaluate it,

View File

@ -28,6 +28,7 @@ struct symtab;
#include "ui-out.h"
#include "inferior.h"
#include "btrace.h"
#include "common/vec.h"
/* Frontend view of the thread state. Possible extensions: stepping,
finishing, until(ling),... */
@ -152,6 +153,10 @@ struct thread_suspend_state
enum gdb_signal stop_signal;
};
typedef struct value *value_ptr;
DEF_VEC_P (value_ptr);
typedef VEC (value_ptr) value_vec;
struct thread_info
{
struct thread_info *next;
@ -264,6 +269,14 @@ struct thread_info
/* Branch trace information for this thread. */
struct btrace_thread_info btrace;
/* Flag which indicates that the stack temporaries should be stored while
evaluating expressions. */
int stack_temporaries_enabled;
/* Values that are stored as temporaries on stack while evaluating
expressions. */
value_vec *stack_temporaries;
};
/* Create an empty thread list, or empty the existing one. */
@ -465,6 +478,16 @@ extern void prune_threads (void);
int pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread);
extern struct cleanup *enable_thread_stack_temporaries (ptid_t ptid);
extern int thread_stack_temporaries_enabled_p (ptid_t ptid);
extern void push_thread_stack_temporary (ptid_t ptid, struct value *v);
extern struct value *get_last_thread_stack_temporary (ptid_t);
extern int value_in_thread_stack_temporaries (struct value *, ptid_t);
extern struct thread_info *thread_list;
#endif /* GDBTHREAD_H */

View File

@ -2508,6 +2508,15 @@ is_scalar_type_recursive (struct type *t)
return 0;
}
/* Return true is T is a class or a union. False otherwise. */
int
class_or_union_p (const struct type *t)
{
return (TYPE_CODE (t) == TYPE_CODE_STRUCT
|| TYPE_CODE (t) == TYPE_CODE_UNION);
}
/* A helper function which returns true if types A and B represent the
"same" class type. This is true if the types have the same main
type, or the same name. */

View File

@ -1823,6 +1823,8 @@ extern int is_integral_type (struct type *);
extern int is_scalar_type_recursive (struct type *);
extern int class_or_union_p (const struct type *);
extern void maintenance_print_type (char *, int);
extern htab_t create_copied_types_hash (struct objfile *objfile);

View File

@ -495,6 +495,7 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
ptid_t call_thread_ptid;
struct gdb_exception e;
char name_buf[RAW_FUNCTION_ADDRESS_SIZE];
int stack_temporaries = thread_stack_temporaries_enabled_p (inferior_ptid);
if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
@ -593,6 +594,33 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
If the ABI specifies a "Red Zone" (see the doco) the code
below will quietly trash it. */
sp = old_sp;
/* Skip over the stack temporaries that might have been generated during
the evaluation of an expression. */
if (stack_temporaries)
{
struct value *lastval;
lastval = get_last_thread_stack_temporary (inferior_ptid);
if (lastval != NULL)
{
CORE_ADDR lastval_addr = value_address (lastval);
if (gdbarch_inner_than (gdbarch, 1, 2))
{
gdb_assert (sp >= lastval_addr);
sp = lastval_addr;
}
else
{
gdb_assert (sp <= lastval_addr);
sp = lastval_addr + TYPE_LENGTH (value_type (lastval));
}
if (gdbarch_frame_align_p (gdbarch))
sp = gdbarch_frame_align (gdbarch, sp);
}
}
}
funaddr = find_function_addr (function, &values_type);
@ -719,9 +747,21 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
/* Reserve space for the return structure to be written on the
stack, if necessary. Make certain that the value is correctly
aligned. */
aligned.
if (struct_return || hidden_first_param_p)
While evaluating expressions, we reserve space on the stack for
return values of class type even if the language ABI and the target
ABI do not require that the return value be passed as a hidden first
argument. This is because we want to store the return value as an
on-stack temporary while the expression is being evaluated. This
enables us to have chained function calls in expressions.
Keeping the return values as on-stack temporaries while the expression
is being evaluated is OK because the thread is stopped until the
expression is completely evaluated. */
if (struct_return || hidden_first_param_p
|| (stack_temporaries && class_or_union_p (values_type)))
{
if (gdbarch_inner_than (gdbarch, 1, 2))
{
@ -1059,31 +1099,40 @@ When the function is done executing, GDB will silently stop."),
At this stage, leave the RETBUF alone. */
restore_infcall_control_state (inf_status);
/* Figure out the value returned by the function. */
retval = allocate_value (values_type);
if (hidden_first_param_p)
read_value_memory (retval, 0, 1, struct_addr,
value_contents_raw (retval),
TYPE_LENGTH (values_type));
else if (TYPE_CODE (target_values_type) != TYPE_CODE_VOID)
if (TYPE_CODE (values_type) == TYPE_CODE_VOID)
retval = allocate_value (values_type);
else if (struct_return || hidden_first_param_p)
{
/* If the function returns void, don't bother fetching the
return value. */
switch (gdbarch_return_value (gdbarch, function, target_values_type,
NULL, NULL, NULL))
if (stack_temporaries)
{
case RETURN_VALUE_REGISTER_CONVENTION:
case RETURN_VALUE_ABI_RETURNS_ADDRESS:
case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
gdbarch_return_value (gdbarch, function, values_type,
retbuf, value_contents_raw (retval), NULL);
break;
case RETURN_VALUE_STRUCT_CONVENTION:
retval = value_from_contents_and_address (values_type, NULL,
struct_addr);
push_thread_stack_temporary (inferior_ptid, retval);
}
else
{
retval = allocate_value (values_type);
read_value_memory (retval, 0, 1, struct_addr,
value_contents_raw (retval),
TYPE_LENGTH (values_type));
break;
}
}
else
{
retval = allocate_value (values_type);
gdbarch_return_value (gdbarch, function, values_type,
retbuf, value_contents_raw (retval), NULL);
if (stack_temporaries && class_or_union_p (values_type))
{
/* Values of class type returned in registers are copied onto
the stack and their lval_type set to lval_memory. This is
required because further evaluation of the expression
could potentially invoke methods on the return value
requiring GDB to evaluate the "this" pointer. To evaluate
the this pointer, GDB needs the memory address of the
value. */
value_force_lval (retval, struct_addr);
push_thread_stack_temporary (inferior_ptid, retval);
}
}

View File

@ -1,3 +1,9 @@
2014-11-28 Siva Chandra Reddy <sivachandra@google.com>
* gdb.cp/chained-calls.cc: New file.
* gdb.cp/chained-calls.exp: New file.
* gdb.cp/smartp.exp: Remove KFAIL for "p c2->inta".
2014-11-28 Simon Marchi <simon.marchi@ericsson.com>
* gdb.ada/py_range.exp: Add parentheses to python calls to print.

View File

@ -0,0 +1,203 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2014 Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>. */
class S
{
public:
S () { }
S (S &obj);
S operator+ (const S &s);
int a;
};
S::S (S &obj)
{
a = obj.a;
}
S
S::operator+ (const S &s)
{
S res;
res.a = a + s.a;
return res;
}
S
f (int i)
{
S s;
s.a = i;
return s;
}
int
g (const S &s)
{
return s.a;
}
class A
{
public:
A operator+ (const A &);
int a;
};
A
A::operator+ (const A &obj)
{
A n;
n.a = a + obj.a;
return n;
}
A
p ()
{
A a;
a.a = 12345678;
return a;
}
A
r ()
{
A a;
a.a = 10000000;
return a;
}
A
q (const A &a)
{
return a;
}
class B
{
public:
int b[1024];
};
B
makeb ()
{
B b;
int i;
for (i = 0; i < 1024; i++)
b.b[i] = i;
return b;
}
int
getb (const B &b, int i)
{
return b.b[i];
}
class C
{
public:
C ();
~C ();
A operator* ();
A *a_ptr;
};
C::C ()
{
a_ptr = new A;
a_ptr->a = 5678;
}
C::~C ()
{
delete a_ptr;
}
A
C::operator* ()
{
return *a_ptr;
}
#define TYPE_INDEX 1
enum type
{
INT,
CHAR
};
union U
{
public:
U (type t);
type get_type ();
int a;
char c;
type tp[2];
};
U::U (type t)
{
tp[TYPE_INDEX] = t;
}
U
make_int ()
{
return U (INT);
}
U
make_char ()
{
return U (CHAR);
}
type
U::get_type ()
{
return tp[TYPE_INDEX];
}
int
main ()
{
int i = g(f(0));
A a = q(p() + r());
B b = makeb ();
C c;
return i + getb(b, 0); /* Break here */
}

View File

@ -0,0 +1,44 @@
# Copyright 2014 Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>.
# This file is part of the gdb testsuite
if {[skip_cplus_tests]} { continue }
standard_testfile .cc
if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
return -1
}
if {![runto_main]} {
return -1
}
gdb_breakpoint [gdb_get_line_number "Break here"]
gdb_continue_to_breakpoint "Break here"
gdb_test "p g(f(12345))" ".* = 12345" "g(f())"
gdb_test "p q(p())" ".* = {a = 12345678}" "q(p())"
gdb_test "p p() + r()" ".* = {a = 22345678}" "p() + r()"
gdb_test "p q(p() + r())" ".* = {a = 22345678}" "q(p() + r())"
gdb_test "p g(f(6700) + f(89))" ".* = 6789" "g(f() + f())"
gdb_test "p g(f(g(f(300) + f(40))) + f(5))" ".* = 345" \
"g(f(g(f() + f())) + f())"
gdb_test "p getb(makeb(), 789)" ".* = 789" "getb(makeb(), ...)"
gdb_test "p *c" ".* = {a = 5678}" "*c"
gdb_test "p *c + *c" ".* = {a = 11356}" "*c + *c"
gdb_test "P q(*c + *c)" ".* = {a = 11356}" "q(*c + *c)"
gdb_test "p make_int().get_type ()" ".* = INT" "make_int().get_type ()"

View File

@ -72,6 +72,5 @@ gdb_test "p b->foo()" "= 66"
gdb_test "p c->foo()" "= 66"
gdb_test "p c->inta" "= 77"
setup_kfail "gdb/11606" "*-*-*"
gdb_test "p c2->inta" "= 77"

View File

@ -636,6 +636,108 @@ prune_threads (void)
}
}
/* Disable storing stack temporaries for the thread whose id is
stored in DATA. */
static void
disable_thread_stack_temporaries (void *data)
{
ptid_t *pd = data;
struct thread_info *tp = find_thread_ptid (*pd);
if (tp != NULL)
{
tp->stack_temporaries_enabled = 0;
VEC_free (value_ptr, tp->stack_temporaries);
}
xfree (pd);
}
/* Enable storing stack temporaries for thread with id PTID and return a
cleanup which can disable and clear the stack temporaries. */
struct cleanup *
enable_thread_stack_temporaries (ptid_t ptid)
{
struct thread_info *tp = find_thread_ptid (ptid);
ptid_t *data;
struct cleanup *c;
gdb_assert (tp != NULL);
tp->stack_temporaries_enabled = 1;
tp->stack_temporaries = NULL;
data = (ptid_t *) xmalloc (sizeof (ptid_t));
*data = ptid;
c = make_cleanup (disable_thread_stack_temporaries, data);
return c;
}
/* Return non-zero value if stack temporaies are enabled for the thread
with id PTID. */
int
thread_stack_temporaries_enabled_p (ptid_t ptid)
{
struct thread_info *tp = find_thread_ptid (ptid);
if (tp == NULL)
return 0;
else
return tp->stack_temporaries_enabled;
}
/* Push V on to the stack temporaries of the thread with id PTID. */
void
push_thread_stack_temporary (ptid_t ptid, struct value *v)
{
struct thread_info *tp = find_thread_ptid (ptid);
gdb_assert (tp != NULL && tp->stack_temporaries_enabled);
VEC_safe_push (value_ptr, tp->stack_temporaries, v);
}
/* Return 1 if VAL is among the stack temporaries of the thread
with id PTID. Return 0 otherwise. */
int
value_in_thread_stack_temporaries (struct value *val, ptid_t ptid)
{
struct thread_info *tp = find_thread_ptid (ptid);
gdb_assert (tp != NULL && tp->stack_temporaries_enabled);
if (!VEC_empty (value_ptr, tp->stack_temporaries))
{
struct value *v;
int i;
for (i = 0; VEC_iterate (value_ptr, tp->stack_temporaries, i, v); i++)
if (v == val)
return 1;
}
return 0;
}
/* Return the last of the stack temporaries for thread with id PTID.
Return NULL if there are no stack temporaries for the thread. */
struct value *
get_last_thread_stack_temporary (ptid_t ptid)
{
struct value *lastval = NULL;
struct thread_info *tp = find_thread_ptid (ptid);
gdb_assert (tp != NULL);
if (!VEC_empty (value_ptr, tp->stack_temporaries))
lastval = VEC_last (value_ptr, tp->stack_temporaries);
return lastval;
}
void
thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid)
{

View File

@ -1724,6 +1724,18 @@ value_non_lval (struct value *arg)
return arg;
}
/* Write contents of V at ADDR and set its lval type to be LVAL_MEMORY. */
void
value_force_lval (struct value *v, CORE_ADDR addr)
{
gdb_assert (VALUE_LVAL (v) == not_lval);
write_memory (addr, value_contents_raw (v), TYPE_LENGTH (value_type (v)));
v->lval = lval_memory;
v->location.address = addr;
}
void
set_value_component_location (struct value *component,
const struct value *whole)

View File

@ -1038,6 +1038,8 @@ extern struct value *value_copy (struct value *);
extern struct value *value_non_lval (struct value *);
extern void value_force_lval (struct value *, CORE_ADDR);
extern void preserve_one_value (struct value *, struct objfile *, htab_t);
/* From valops.c */