This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/2] Implement operator new


I'm going to check this in a couple days, barring comments.

This implements 'new' and 'delete' for the C++ expression parser.

It should work fine in the majority of cases.  There is one known bug,
where array new will fail to work properly if the base type requires
unusual alignment.  I don't think there is any way to compute this
currently in gdb.

A new test case is included.

Built and regtested on x86-64 Fedora 16.

	PR c++/10050:
	* c-exp.y (exp): New productions for "new", "delete".
	(new_operator, new_type, new_type_id, new_placement, new_init)
	(direct_new_declarator, delete_operator): New productions.
	* cp-abi.c (cp_call_array_new, cp_get_vec_elts): New functions.
	* cp-abi.h (cp_call_array_new, cp_get_vec_elts): Declare.
	(struct cp_abi_ops) <call_array_new, get_vec_elts>: New fields.
	* eval.c (evaluate_subexp_standard) <OP_NEW, OP_DELETE>: New
	cases.
	* expprint.c (print_subexp_standard) <OP_NEW, OP_DELETE>: New
	cases.
	* gnu-v3-abi.c (gnuv3_array_padding, gnuv3_call_array_new)
	(gnuv3_get_vec_elts): New functions.
	(init_gnuv3_ops): Initialize new fields.
	* parse.c (operator_length_standard) <OP_NEW, OP_DELETE>: New
	cases.
	* parser-defs.h (enum cxx_new_delete_flags): New.
	* std-operator.def (OP_NEW, OP_DELETE): New.
	* valarith.c (value_operator_new, value_construct)
	(value_operator_delete, value_destruct): New functions.
	* value.h (value_operator_new, value_construct)
	(value_operator_delete, value_destruct): Declare.

	* gdb.cp/new.cc: New file.
	* gdb.cp/new.exp: New file.
---
 gdb/c-exp.y                  |   99 ++++++++++++++++++++++++++
 gdb/cp-abi.c                 |   23 ++++++
 gdb/cp-abi.h                 |   26 +++++++
 gdb/eval.c                   |  159 ++++++++++++++++++++++++++++++++++++++++++
 gdb/expprint.c               |   70 ++++++++++++++++++
 gdb/gnu-v3-abi.c             |  128 +++++++++++++++++++++++++++++++++
 gdb/parse.c                  |    7 ++
 gdb/parser-defs.h            |   12 +++
 gdb/std-operator.def         |    6 ++
 gdb/testsuite/gdb.cp/new.cc  |  153 ++++++++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.cp/new.exp |  156 +++++++++++++++++++++++++++++++++++++++++
 11 files changed, 839 insertions(+), 0 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/new.cc
 create mode 100644 gdb/testsuite/gdb.cp/new.exp

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 7afce77..2b7ca89 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -169,6 +169,13 @@ static struct stoken operator_stoken (const char *);
 %type <lval> rcurly
 %type <tval> type typebase
 %type <tvec> nonempty_typelist
+%type <lval> new_init
+%type <lval> new_placement
+%type <tval> new_type
+%type <tval> new_type_id
+%type <lval> direct_new_declarator
+%type <lval> new_operator
+%type <lval> delete_operator
 /* %type <bval> block */
 
 /* Fancy type parsing.  */
@@ -653,6 +660,98 @@ exp	:	CONST_CAST '<' type '>' '(' exp ')' %prec UNARY
 			  write_exp_elt_opcode (UNOP_CAST); }
 	;
 
+exp	:	new_operator
+		new_placement
+		new_type
+		new_init
+			{
+			  write_exp_elt_opcode (OP_NEW);
+			  write_exp_elt_type ($3);
+			  write_exp_elt_longcst ($1);
+			  write_exp_elt_longcst ($2);
+			  write_exp_elt_longcst ($4);
+			  write_exp_elt_opcode (OP_NEW);
+			}
+	|	new_operator
+		new_placement
+		new_type
+		direct_new_declarator
+			{
+			  write_exp_elt_opcode (OP_NEW);
+			  write_exp_elt_type ($3);
+			  write_exp_elt_longcst ($1 | CXX_NEW_ARRAY);
+			  write_exp_elt_longcst ($2);
+			  write_exp_elt_longcst ($4);
+			  write_exp_elt_opcode (OP_NEW);
+			}
+	;
+
+new_operator:	NEW		{ $$ = CXX_NEW_PLAIN; }
+	|	COLONCOLON NEW	{ $$ = CXX_NEW_GLOBAL; }
+	;
+
+new_type:	'(' type ')'	{ $$ = $2; }
+	|	new_type_id	{ $$ = $1; }
+	;
+
+new_type_id:	typebase	{ $$ = $1; }
+	|	new_type_id '*'
+				{
+				  push_type (tp_pointer);
+				  $$ = follow_types ($1);
+				}
+	;
+
+new_placement:	'('
+			{
+			  start_arglist ();
+			}
+		arglist ')'
+			{
+			  $$ = end_arglist ();
+			}
+	|	{ $$ = 0; }
+	;
+
+new_init:	'('
+			{
+			  start_arglist ();
+			}
+		arglist ')'
+			{
+			  $$ = end_arglist ();
+			}
+	|	{ $$ = 0; }
+	;
+
+direct_new_declarator:
+		'[' exp ']'	{ $$ = 1; }
+	|	direct_new_declarator '[' exp ']'
+			{ $$ = $1 + 1; }
+	;
+
+exp	:	delete_operator exp
+			{
+			  write_exp_elt_opcode (OP_DELETE);
+			  write_exp_elt_longcst ($1);
+			  write_exp_elt_opcode (OP_DELETE);
+			}
+	;
+
+exp	:	delete_operator '[' ']' exp
+			{
+			  write_exp_elt_opcode (OP_DELETE);
+			  write_exp_elt_longcst ($1 | CXX_NEW_ARRAY);
+			  write_exp_elt_opcode (OP_DELETE);
+			}
+	;
+
+delete_operator:
+		DELETE	{ $$ = CXX_NEW_PLAIN; }
+	|	COLONCOLON DELETE
+			{ $$ = CXX_NEW_GLOBAL; }
+	;
+
 string_exp:
 		STRING
 			{
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index 16b5356..789dd91 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -187,6 +187,29 @@ cp_pass_by_reference (struct type *type)
   return (*current_cp_abi.pass_by_reference) (type);
 }
 
+/* See cp-abi.h.  */
+
+struct value *
+cp_call_array_new (LONGEST elt_size, LONGEST elt_count,
+		   int global_new, struct type *type,
+		   int argc, struct value **argv)
+{
+  if ((current_cp_abi.call_array_new) == NULL)
+    error (_("GDB cannot call 'new[]' on this target"));
+  return (*current_cp_abi.call_array_new) (elt_size, elt_count, global_new,
+					   type, argc, argv);
+}
+
+/* See cp-abi.h.  */
+
+struct value *
+cp_get_vec_elts (struct value *vec, struct value **new_vec)
+{
+  if ((current_cp_abi.get_vec_elts) == NULL)
+    error (_("GDB cannot call 'delete[]' on this target"));
+  return (*current_cp_abi.get_vec_elts) (vec, new_vec);
+}
+
 /* Set the current C++ ABI to SHORT_NAME.  */
 
 static int
diff --git a/gdb/cp-abi.h b/gdb/cp-abi.h
index 8451450..8327662 100644
--- a/gdb/cp-abi.h
+++ b/gdb/cp-abi.h
@@ -189,6 +189,28 @@ CORE_ADDR cplus_skip_trampoline (struct frame_info *frame,
    reference instead of value.  */
 extern int cp_pass_by_reference (struct type *type);
 
+/* Call new[].
+   
+   ELT_SIZE is the size of an element.
+   ELT_COUNT is the total number of elements.
+   GLOBAL_NEW, TYPE, ARGC, and ARGV are as specified by
+   value_operator_new.
+   
+   Returns the new array, or throw exception on error.  */
+
+extern struct value *cp_call_array_new (LONGEST elt_size, LONGEST elt_count,
+					int global_new, struct type *type,
+					int argc, struct value **argv);
+
+/* Given a value, VEC, returned by cp_call_array_new, return a new
+   value which holds the number of elements in VEC.  This can only be
+   called when the array's element type has a destructor.  *NEW_VEC is
+   set to the address actually allocated in the inferior; that is, VEC
+   minus whatever padding was needed.  */
+
+extern struct value *cp_get_vec_elts (struct value *VEC,
+				      struct value **NEW_VEC);
+
 struct cp_abi_ops
 {
   const char *shortname;
@@ -221,6 +243,10 @@ struct cp_abi_ops
   void (*print_vtable) (struct value *);
   CORE_ADDR (*skip_trampoline) (struct frame_info *, CORE_ADDR);
   int (*pass_by_reference) (struct type *type);
+
+  struct value *(*call_array_new) (LONGEST, LONGEST, int, struct type *,
+				   int, struct value **);
+  struct value *(*get_vec_elts) (struct value *, struct value **);
 };
 
 
diff --git a/gdb/eval.c b/gdb/eval.c
index 3d43406..6618dfc 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2861,6 +2861,165 @@ evaluate_subexp_standard (struct type *expect_type,
       else
         error (_("Attempt to use a type name as an expression"));
 
+    case OP_NEW:
+      {
+	struct type *type;
+	int i, n_op_args, n_constr_args, flags;
+	struct value **argvec;
+	struct type *ptr_type, *new_type, *array_type;
+	LONGEST total_len;
+
+	*pos += 5;
+
+	type = check_typedef (exp->elts[pc + 1].type);
+	flags = exp->elts[pc + 2].longconst;
+	n_op_args = exp->elts[pc + 3].longconst;
+	n_constr_args = exp->elts[pc + 4].longconst;
+
+	argvec = alloca ((max (n_op_args, n_constr_args) + 3)
+			 * sizeof (struct value *));
+
+	argvec[0] = NULL;
+
+	/* Evaluate "placement" args.  */
+	for (i = 0; i < n_op_args; ++i)
+	  argvec[i + 2] = evaluate_subexp (NULL, exp, pos, noside);
+	argvec[i + 2] = NULL;
+
+	if ((flags & CXX_NEW_ARRAY) == 0)
+	  {	  
+	    /* Note that the type isn't precisely correct.  */
+	    argvec[1]
+	      = value_from_longest (builtin_type (exp->gdbarch)->builtin_int,
+				    TYPE_LENGTH (type));
+	    new_type = type;
+
+	    /* Allocate memory.  */
+	    ptr_type = lookup_pointer_type (type);
+
+	    if (noside == EVAL_NORMAL)
+	      argvec[0]
+		= value_cast (ptr_type,
+			      value_operator_new ((flags & CXX_NEW_GLOBAL) != 0,
+						  new_type, n_op_args + 2,
+						  argvec));
+
+	    /* Set up for the constructor call.  */
+	    for (i = 0; i < n_constr_args; ++i)
+	      argvec[i + 1] = evaluate_subexp (NULL, exp, pos, noside);
+	    argvec[i + 1] = NULL;
+
+	    if (noside == EVAL_SKIP)
+	      goto nosideret;
+	    if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	      return allocate_value (ptr_type);
+
+	    value_construct (type, n_constr_args + 1, argvec);
+
+	    return argvec[0];
+	  }
+	else
+	  {
+	    LONGEST *array_sizes, iter, elt_size;
+	    CORE_ADDR result_addr;
+
+	    /* Compute the array type.  */
+	    array_type = type;
+	    total_len = 1;
+	    elt_size = TYPE_LENGTH (type);
+
+	    /* Compute each array size.  */
+	    array_sizes = alloca (n_constr_args * sizeof (LONGEST));
+	    for (i = n_constr_args - 1; i >= 0; --i)
+	      {
+		LONGEST len;
+		struct value *this_len = evaluate_subexp (NULL, exp,
+							  pos, noside);
+
+		len = value_as_long (this_len);
+		if (len < 0)
+		  error (_("cannot use 'new' with negative array dimension"));
+		total_len *= len;
+		array_sizes[i] = len;
+	      }
+
+	    if (noside == EVAL_SKIP)
+	      goto nosideret;
+	    
+	    /* FIXME-type-allocation.  */
+	    /* Now compute the array type.  */
+	    for (i = 0; i < n_constr_args; ++i)
+	      {
+		/* Note that array ranges are inclusive.  */
+		array_type = lookup_array_range_type (array_type, 0,
+						      array_sizes[i] - 1);
+	      }
+
+	    if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	      {
+		array_type
+		  = lookup_pointer_type (TYPE_TARGET_TYPE (array_type));
+		return allocate_value (array_type);
+	      }
+
+	    argvec[1] = NULL;	/* Filled in by cp_call_array_new.  */
+	    new_type = array_type;
+
+	    /* Allocate memory.  */
+	    ptr_type = lookup_pointer_type (type);
+	    argvec[0]
+	      = value_cast (ptr_type,
+			    cp_call_array_new (elt_size, total_len,
+					       (flags & CXX_NEW_GLOBAL) != 0,
+					       new_type, n_op_args + 2,
+					       argvec));
+
+	    result_addr = value_as_address (argvec[0]);
+
+	    /* Invoke the zero-arg constructor on each element.  */
+	    for (iter = 0; iter < total_len; ++iter)
+	      {
+		value_construct (type, 1, argvec);
+		argvec[0] = value_ptradd (argvec[0], 1);
+	      }
+
+	    array_type = lookup_pointer_type (TYPE_TARGET_TYPE (array_type));
+	    return value_from_pointer (array_type, result_addr);
+	  }
+      }
+      break;
+
+    case OP_DELETE:
+      {
+	int flags;
+	struct value *arg, **argv, *new_vec;
+	struct type *type;
+
+	*pos += 2;
+	flags = exp->elts[pc + 1].longconst;
+
+	arg = evaluate_subexp (NULL, exp, pos, noside);
+
+	if (noside == EVAL_SKIP)
+	  goto nosideret;
+	if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	  return allocate_value (builtin_type (exp->gdbarch)->builtin_void);
+
+	type = check_typedef (value_type (arg));
+	if (TYPE_CODE (type) != TYPE_CODE_PTR)
+	  error (_("argument to 'delete' not of pointer type"));
+
+	new_vec = value_destruct (arg, (flags & CXX_NEW_ARRAY) != 0);
+	if (new_vec == NULL)
+	  new_vec = arg;
+	value_operator_delete ((flags & CXX_NEW_GLOBAL) != 0,
+			       (flags & CXX_NEW_ARRAY) != 0,
+			       new_vec);
+
+	return allocate_value (builtin_type (exp->gdbarch)->builtin_void);
+      }
+      break;
+
     default:
       /* Removing this case and compiling with gcc -Wall reveals that
          a lot of cases are hitting this case.  Some of these should
diff --git a/gdb/expprint.c b/gdb/expprint.c
index 6915d43..4341464 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -559,6 +559,76 @@ print_subexp_standard (struct expression *exp, int *pos,
 	return;
       }
 
+    case OP_NEW:
+      {
+	struct type *type;
+	int n_op_args, n_constr_args, flags;
+
+    	(*pos) += 5;
+
+	type = exp->elts[pc + 1].type;
+	flags = exp->elts[pc + 2].longconst;
+	n_op_args = exp->elts[pc + 3].longconst;
+	n_constr_args = exp->elts[pc + 4].longconst;
+
+	if ((flags & CXX_NEW_GLOBAL) != 0)
+	  fputs_unfiltered ("::", stream);
+    	fputs_filtered ("new ", stream);
+
+	if (n_op_args > 0)
+	  {
+	    fputs_filtered (" (", stream);
+	    for (tem = 0; tem < n_op_args; ++tem)
+	      {
+		if (tem != 0)
+		  fputs_filtered (", ", stream);
+		print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+	      }
+	    fputs_filtered (")", stream);
+	  }
+
+	fputs_filtered (" ", stream);
+	type_print (type, NULL, stream, 0);
+
+	if ((flags & CXX_NEW_ARRAY) == 0)
+	  {
+	    if (n_constr_args > 0)
+	      {
+		fputs_filtered (" (", stream);
+		for (tem = 0; tem < n_constr_args; ++tem)
+		  {
+		    if (tem != 0)
+		      fputs_filtered (", ", stream);
+		    print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+		  }
+		fputs_filtered (")", stream);
+	      }
+	  }
+	else
+	  {
+	    for (tem = 0; tem < n_constr_args; ++tem)
+	      {
+		fputs_filtered ("[", stream);
+		print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+		fputs_filtered ("]", stream);
+	      }
+	  }
+      }
+      break;
+
+    case OP_DELETE:
+      {
+	int is_array;
+
+	(*pos) += 2;
+
+	is_array = exp->elts[pc + 1].longconst;
+	fputs_filtered (is_array ? "delete[] " : "delete ", stream);
+
+	print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+      }
+      break;
+
       /* Default ops */
 
     default:
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c
index 5eadb3c..4fb47fb 100644
--- a/gdb/gnu-v3-abi.c
+++ b/gdb/gnu-v3-abi.c
@@ -1098,6 +1098,132 @@ gnuv3_pass_by_reference (struct type *type)
   return 0;
 }
 
+/* A helper function that returns the amount of padding needed for
+   new[].  INCLUDE_SIZE is an out parameter which is set to non-zero
+   if the element size should also be put into the padding; this is
+   used by the ARM EABI.  */
+
+static struct value *
+gnuv3_array_padding (int *include_size)
+{
+  struct symbol *sym;
+  struct type *type;
+  int multiplier = 1;
+
+  sym = lookup_symbol ("std::size_t", NULL, VAR_DOMAIN, NULL);
+  if (sym == NULL)
+    sym = lookup_symbol ("size_t", NULL, VAR_DOMAIN, NULL);
+  if (sym == NULL)
+    error (_("couldn't find std::size_t or size_t"));
+
+  type = SYMBOL_TYPE (sym);
+
+  sym = lookup_symbol ("__aeabi_vec_ctor_cookie_nodtor",
+		       NULL, VAR_DOMAIN, NULL);
+  if (sym != NULL)
+    {
+      /* ARM EABI has an extra slot.  */
+      *include_size = 1;
+      multiplier = 2;
+    }
+  else
+    *include_size = 0;
+
+  return value_from_longest (type,
+			     multiplier * TYPE_LENGTH (check_typedef (type)));
+}
+
+/* Implement the call_array_new method.  */
+
+static struct value *
+gnuv3_call_array_new (LONGEST elt_size, LONGEST elt_count,
+		      int global_new, struct type *type,
+		      int argc, struct value **argv)
+{
+  struct value *padding, *memory;
+  int include_size, has_destructor = 0;
+  struct type *elt_type;
+
+
+  for (elt_type = type;
+       TYPE_CODE (elt_type) == TYPE_CODE_ARRAY;
+       elt_type = check_typedef (TYPE_TARGET_TYPE (elt_type)))
+    ;
+  if (TYPE_CODE (elt_type) == TYPE_CODE_STRUCT)
+    {
+      int i;
+
+      for (i = 0; !has_destructor && i < TYPE_NFN_FIELDS (type); ++i)
+	{
+	  struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+	  int j;
+
+	  for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j)
+	    {
+	      if (TYPE_FN_FIELDLIST_NAME (type, i)[0] == '~'
+		  || is_destructor_name (TYPE_FN_FIELD_PHYSNAME (f, j)))
+		{
+		  has_destructor = 1;
+		  break;
+		}
+	    }
+	}
+    }
+
+  padding = gnuv3_array_padding (&include_size);
+  argv[1] = value_from_longest (value_type (padding), elt_size * elt_count);
+
+  if (has_destructor)
+    argv[1] = value_binop (argv[1], padding, BINOP_ADD);
+
+  memory = value_operator_new (global_new, type, argc, argv);
+
+  if (has_destructor)
+    {
+      memory = value_cast (lookup_pointer_type (value_type (padding)), memory);
+
+      /* This logic isn't quite right if we need a bit of extra
+	 padding to make the object alignment work out.  I don't think
+	 there is currently any way to find the required alignment for
+	 a type.  */
+
+      if (include_size)
+	{
+	  value_assign (value_ind (memory),
+			value_from_longest (value_type (padding), elt_size));
+	  memory = value_ptradd (memory, 1);
+	}
+
+      value_assign (value_ind (memory),
+		    value_from_longest (value_type (padding), elt_count));
+      memory = value_ptradd (memory, 1);
+    }
+
+  return memory;
+}
+
+/* Implement the get_vec_elts method.  */
+
+static struct value *
+gnuv3_get_vec_elts (struct value *vec, struct value **new_vec)
+{
+  struct value *padding, *memory, *result;
+  int include_size;
+  struct gdbarch *gdbarch;
+
+  padding = gnuv3_array_padding (&include_size);
+
+  memory = value_cast (lookup_pointer_type (value_type (padding)), vec);
+
+  result = value_ind (value_ptradd (memory, -1));
+
+  gdbarch = get_type_arch (value_type (padding));
+  memory = value_cast (builtin_type (gdbarch)->builtin_data_ptr, vec);
+  *new_vec = value_ptradd (memory, - value_as_long (padding));
+
+  return result;
+}
+
 static void
 init_gnuv3_ops (void)
 {
@@ -1123,6 +1249,8 @@ init_gnuv3_ops (void)
   gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable;
   gnu_v3_abi_ops.skip_trampoline = gnuv3_skip_trampoline;
   gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference;
+  gnu_v3_abi_ops.call_array_new = gnuv3_call_array_new;
+  gnu_v3_abi_ops.get_vec_elts = gnuv3_get_vec_elts;
 }
 
 extern initialize_file_ftype _initialize_gnu_v3_abi; /* -Wmissing-prototypes */
diff --git a/gdb/parse.c b/gdb/parse.c
index c372f40..7ba315f 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -870,6 +870,12 @@ operator_length_standard (const struct expression *expr, int endpos,
       oplen = 5 + BYTES_TO_EXP_ELEM (oplen + 1);
       break;
 
+    case OP_NEW:
+      oplen = 6;
+      args = (longest_to_int (expr->elts[endpos - 2].longconst)
+	      + longest_to_int (expr->elts[endpos - 3].longconst));
+      break;
+
     case OP_LONG:
     case OP_DOUBLE:
     case OP_DECFLOAT:
@@ -916,6 +922,7 @@ operator_length_standard (const struct expression *expr, int endpos,
     case UNOP_DYNAMIC_CAST:
     case UNOP_REINTERPRET_CAST:
     case UNOP_MEMVAL:
+    case OP_DELETE:
       oplen = 3;
       args = 1;
       break;
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index aa600a1..0ab2c56 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -341,4 +341,16 @@ extern void parser_fprintf (FILE *, const char *, ...) ATTRIBUTE_PRINTF (2, 3);
 
 extern int exp_uses_objfile (struct expression *exp, struct objfile *objfile);
 
+/* Flags used when evaluating C++ 'new' or 'delete' expressions.  */
+
+enum cxx_new_delete_flags
+{
+  /* Plain.  */
+  CXX_NEW_PLAIN = 0,
+  /* Set for array new or delete, clear otherwise.  */
+  CXX_NEW_ARRAY = 1,
+  /* Set for ::new or delete, clear otherwise.  */
+  CXX_NEW_GLOBAL = 2
+};
+
 #endif /* PARSER_DEFS_H */
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index f2f650b..4e17035 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -326,3 +326,9 @@ OP (OP_DECFLOAT)
 /* OP_ADL_FUNC specifies that the function is to be looked up in an
    Argument Dependent manner (Koenig lookup).  */
 OP (OP_ADL_FUNC)
+
+/* The C++ 'new' and 'new[]' operations.  */
+OP (OP_NEW)
+
+/* The C++ 'delete' and 'delete[]' operations.  */
+OP (OP_DELETE)
diff --git a/gdb/testsuite/gdb.cp/new.cc b/gdb/testsuite/gdb.cp/new.cc
new file mode 100644
index 0000000..2cf5d6e
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/new.cc
@@ -0,0 +1,153 @@
+#include <new>
+#include <cstdlib>
+
+// For convenience when debugging gdb.
+void *last_alloc;
+
+int counter;
+
+// Our own global operator new which updates counter.
+void *operator new (std::size_t size)
+{
+  ++counter;
+  return last_alloc = malloc (size);
+}
+
+void *operator new[] (std::size_t size)
+{
+  ++counter;
+  return last_alloc = malloc (size);
+}
+
+void operator delete (void *ptr)
+{
+  --counter;
+  free (ptr);
+}
+
+void operator delete[] (void *ptr)
+{
+  --counter;
+  free (ptr);
+}
+
+struct Simple
+{
+  int x;
+  Simple() : x (7) { }
+  Simple (int y) : x (y) { }
+};
+
+struct Derived : public Simple
+{
+};
+
+static int other_counter;
+
+struct HasOps
+{
+  int x;
+
+  void *operator new (std::size_t size)
+  {
+    ++other_counter;
+    return last_alloc = malloc (size);
+  }
+
+  void *operator new (std::size_t size, void *ptr) throw()
+  {
+    return ptr;
+  }
+
+  void *operator new (std::size_t size, int x, int y) throw()
+  {
+    other_counter += x;
+    return last_alloc = malloc (size);
+  }
+
+  void *operator new[] (std::size_t size)
+  {
+    ++other_counter;
+    return last_alloc = malloc (size);
+  }
+
+  void operator delete (void *ptr)
+  {
+    --other_counter;
+    free (ptr);
+  }
+
+  void operator delete[] (void *ptr)
+  {
+    --other_counter;
+    free (ptr);
+  }
+
+  HasOps () : x (7) { }
+  HasOps (int y) : x (y) { }
+};
+
+template<typename T>
+struct WithConstructor
+{
+  T x;
+
+  WithConstructor (T y) : x (y) { }
+};
+
+namespace Name
+{
+  template<typename T>
+  struct InNameSpace
+  {
+    T x;
+
+    InNameSpace (T y) : x (y) { }
+  };
+}
+
+int keep_stuff ()
+{
+  delete new HasOps;
+  delete[] new HasOps[5];
+}
+
+int main ()
+{
+  keep_stuff ();
+
+  int *ip = new int;
+  int *ip2 = new int (5);
+
+  int **y = new int *;
+  int **y2 = new int*[5];
+
+  int (*z)[7] = new int[5][7];
+
+  Simple *s = new Simple;
+  Simple *s2 = new Simple(23);
+  Simple *s3 = new Simple[7];
+
+  Derived *d = new Derived;
+
+  HasOps *h = new HasOps;
+  HasOps *h2 = new HasOps(23);
+  HasOps *h3 = new HasOps[7];
+
+  HasOps *h4 = (HasOps *) HasOps::operator new (sizeof (HasOps));
+  h4 = new (h4) HasOps;
+
+  HasOps *h5 = new (0, 88) HasOps(0);
+
+  HasOps **hp = new HasOps *;
+
+  WithConstructor<int> *w = new WithConstructor<int> (5);
+
+  Name::InNameSpace<int> *ins = new Name::InNameSpace<int> (72);
+
+  // Reset for the test suite.
+  counter = 0;
+  other_counter = 0;
+
+  return 0;			// Stop here
+}
diff --git a/gdb/testsuite/gdb.cp/new.exp b/gdb/testsuite/gdb.cp/new.exp
new file mode 100644
index 0000000..43b3ef4
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/new.exp
@@ -0,0 +1,156 @@
+# Copyright 2012 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/>.
+
+
+if { [skip_stl_tests] } { continue }
+
+set testfile "new"
+set srcfile ${testfile}.cc
+set binfile $objdir/$subdir/$testfile
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+    return -1
+}
+
+if ![runto_main] then {
+    perror "couldn't run to main"
+    continue
+} 
+
+gdb_breakpoint [gdb_get_line_number "Stop here"]
+gdb_continue_to_breakpoint Stop
+
+set counter_count 0
+set counter_value 0
+
+proc check_counter {{value 1}} {
+    global counter_count counter_value
+
+    incr counter_count
+    incr counter_value $value
+    gdb_test "print counter" " = $counter_value" "check counter #$counter_count"
+}
+
+set other_count 0
+set other_count_value 0
+
+proc check_other_counter {{value 1}} {
+    global other_count other_count_value
+
+    incr other_count
+    incr other_count_value $value
+    gdb_test "print other_counter" " = $other_count_value" \
+	"check other_counter #$other_count"
+}
+
+gdb_test "print \$an_int = new int" " = .int .. $hex"
+check_counter
+gdb_test "print *new int(5)" " = 5"
+check_counter
+gdb_test "print *(\$an_int_array = new int\[7\]) @ 7" \
+    " = \\\{$decimal, $decimal, $decimal, $decimal, $decimal, $decimal, $decimal\\\}"
+check_counter
+
+gdb_test "print new int *" " = .int ... $hex"
+check_counter
+gdb_test "print *new int *\[2\] @ 2" " = \\\{$hex, $hex\\\}"
+check_counter
+
+gdb_test "print *(\$another_int_array = new int \[2\]\[3\]) @ 2" " = {\\\{$decimal, $decimal, $decimal}, \\\{$decimal, $decimal, $decimal}}"
+check_counter
+
+gdb_test "print *(\$simple = new Simple)" " = {x = 7}"
+check_counter
+gdb_test "print *(\$simple_array = new Simple\[7\]) @ 2" " = {{x = 7}, {x = 7}}"
+check_counter
+gdb_test "print *new Simple(23)" " = {x = 23}"
+check_counter
+gdb_test "print *new Simple \[2\]\[3\] @ 2" \
+    " = {{{x = 7}, {x = 7}, {x = 7}}, {{x = 7}, {x = 7}, {x = 7}}}"
+check_counter
+
+gdb_test "print *(\$derived = new Derived)" " = {<Simple> = {x = 7}, <No data fields>}"
+check_counter
+gdb_test "print *new Derived\[7\] @ 2" " = {{<Simple> = {x = 7}, <No data fields>}, {<Simple> = {x = 7}, <No data fields>}}"
+check_counter
+
+gdb_test "print *new WithConstructor<int>(5)" " = {x = 5}"
+check_counter
+
+gdb_test "print *new Name::InNameSpace<int>(27)" " = {x = 27}"
+check_counter
+
+gdb_test "print *(\$hasops = new HasOps)" " = {x = 7}"
+check_other_counter
+gdb_test "print *new HasOps(23)" " = {x = 23}"
+check_other_counter
+gdb_test "print *(\$hasops_array = new HasOps\[7\]) @ 2" " = {{x = 7}, {x = 7}}"
+check_other_counter
+
+gdb_test "print \$h = (HasOps *) HasOps::operator new (sizeof (HasOps))" \
+    " = .HasOps .. $hex"
+check_other_counter
+gdb_test "print *new(\$h) HasOps" " = {x = 7}"
+
+gdb_test "print *new (1, 88) HasOps(0)" " = {x = 0}"
+check_other_counter
+
+gdb_test "print new HasOps **" " = .HasOps .... $hex"
+# This should not use HasOps::operator new.
+check_counter
+
+gdb_test "print *(\$global_hasops = ::new HasOps)" " = {x = 7}"
+# This should not use HasOps::operator new.
+check_counter
+
+gdb_test "print delete \$an_int" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] \$an_int_array" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] \$another_int_array" " = void"
+check_counter -1
+
+gdb_test "print delete \$simple" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] \$simple_array" " = void"
+check_counter -1
+
+gdb_test "print delete \$derived" " = void"
+check_counter -1
+
+gdb_test "print delete \$hasops" " = void"
+check_other_counter -1
+
+gdb_test "print ::delete \$global_hasops" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] \$hasops_array" " = void"
+check_other_counter -1
+
+# Interoperability with the real C++ runtime.
+gdb_test "print delete ip" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] y2" " = void"
+check_counter -1
+
+gdb_test "print delete s" " = void"
+check_counter -1
+
+gdb_test "print delete\[\] s2" " = void"
+check_counter -1
-- 
1.7.7.6


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]