Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v4] Make chained function calls in expressions work
@ 2014-10-25 14:24 Siva Chandra
  2014-11-03 14:35 ` Siva Chandra
  2014-11-03 14:43 ` Ulrich Weigand
  0 siblings, 2 replies; 20+ messages in thread
From: Siva Chandra @ 2014-10-25 14:24 UTC (permalink / raw)
  To: gdb-patches, Ulrich Weigand

[-- Attachment #1: Type: text/plain, Size: 1667 bytes --]

This is a follow up to the thread here:
https://sourceware.org/ml/gdb-patches/2014-10/msg00000.html

I have made all the suggested changes which now eliminates the need
for two patches in this set. The single patch is attached.

gdb/ChangeLog:

2014-10-25  Siva Chandra Reddy  <sivachandra@google.com>

        * eval.c (evaluate_expression): Cleanup stack mirrors that might
        have been created.
        (add_value_to_expression_stack, skip_current_expression_stack):
        New functions.
        * expression.h (struct expression): New field
        'on_stack_temporaries_vec'.
        * gdbtypes.c (class_or_union_p): New function.
        * gdbtypes.h (class_or_union_p): Declare.
        * infcall.c (call_function_by_hand): New argument EXP of type
        struct expression *. Setup stack temporaries for return values of
        class type.
        (get_return_value_from_memory): New function.
        * infcall.h (call_function_by_hand): Update signature and all
        callers.
        * valarith.c (value_x_binop): New argument EXP of type
        struct expression *.
        (value_x_unop): Likewise.
        * value.c (write_value_to_memory): New function.
        (value_fetch_lazy): Fix its description comment.
        * value.h (add_value_to_expression_stack)
        (skip_current_expression_stack, write_value_to_memory): Declare.
        (value_x_binop, value_x_unop): Update signature and all callers.

gdb/testsuite/ChangeLog:

2014-10-25  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 from c2->inta.

[-- Attachment #2: chained-calls-v4.txt --]
[-- Type: text/plain, Size: 39223 bytes --]

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 5793cd2..32da705 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10297,7 +10297,7 @@ ada_evaluate_subexp (struct type *expect_type, struct expression *exp,
 		return allocate_value (TYPE_TARGET_TYPE (rtype));
 	      return allocate_value (rtype);
 	    }
-          return call_function_by_hand (argvec[0], nargs, argvec + 1);
+          return call_function_by_hand (argvec[0], nargs, argvec + 1, exp);
 	case TYPE_CODE_INTERNAL_FUNCTION:
 	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	    /* We don't know anything about what the internal
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 19aaed3..dee4ab9 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -955,7 +955,7 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   /* STT_GNU_IFUNC resolver functions have no parameters.  FUNCTION is the
      function entry address.  ADDRESS may be a function descriptor.  */
 
-  address_val = call_function_by_hand (function, 0, NULL);
+  address_val = call_function_by_hand (function, 0, NULL, NULL);
   address = value_as_address (address_val);
   address = gdbarch_convert_from_func_ptr_addr (gdbarch, address,
 						&current_target);
diff --git a/gdb/eval.c b/gdb/eval.c
index 5906744..d1cc309 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -38,6 +38,7 @@
 #include "valprint.h"
 #include "gdb_obstack.h"
 #include "objfiles.h"
+#include "common/vec.h"
 #include <ctype.h>
 
 /* This is defined in valops.c */
@@ -136,9 +137,72 @@ parse_to_comma_and_eval (const char **expp)
 struct value *
 evaluate_expression (struct expression *exp)
 {
-  int pc = 0;
+  int i, pc = 0;
+  struct value *res, *val;
+  struct cleanup *cleanups;
+  value_vec *vec = exp->on_stack_temporaries_vec;
+
+  cleanups = make_cleanup (VEC_cleanup (value_ptr),
+			   &exp->on_stack_temporaries_vec);
+  res = evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL);
+  /* If the result is on the expression stack, fetch it and mark it as
+     not_lval.  */
+  for (i = 0; VEC_iterate (value_ptr, vec, 1, val); i++)
+    {
+      if (res == val)
+	{
+	  if (value_lazy (res))
+	    value_fetch_lazy (res);
+	  VALUE_LVAL (res) = not_lval;
+	  break;
+	}
+    }
+  do_cleanups (cleanups);
+
+  return res;
+}
+
+/* Add value V to the expression stack of expression EXP.  */
+
+void
+add_value_to_expression_stack (struct expression *exp, struct value *v)
+{
+  gdb_assert (exp != NULL);
+  VEC_safe_push (value_ptr, exp->on_stack_temporaries_vec, v);
+}
+
+/* Return an address after skipping over the current values on the expression
+   stack of EXP.  SP is the current stack frame pointer.  Non-zero DOWNWARD
+   indicates that the stack grows downwards/backwards.  */
 
-  return evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL);
+CORE_ADDR
+skip_current_expression_stack (struct expression *exp, CORE_ADDR sp,
+			       int downward)
+{
+  CORE_ADDR addr = sp;
+
+  gdb_assert (exp != NULL);
+  if (!VEC_empty (value_ptr, exp->on_stack_temporaries_vec))
+    {
+      struct value *v = VEC_last (value_ptr, exp->on_stack_temporaries_vec);
+      CORE_ADDR val_addr = value_address (v);
+
+      if (downward)
+	{
+	  gdb_assert (sp >= val_addr);
+	  addr = val_addr;
+	}
+      else
+	{
+	  struct type *type;
+
+	  gdb_assert (sp <= val_addr);
+	  type = value_type (v);
+	  addr = val_addr + TYPE_LENGTH (type);
+	}
+    }
+
+  return addr;
 }
 
 /* Evaluate an expression, avoiding all memory references
@@ -1143,12 +1207,12 @@ evaluate_subexp_standard (struct type *expect_type,
 	argvec[3] = value_from_longest (long_type, selector);
 	argvec[4] = 0;
 
-	ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	ret = call_function_by_hand (argvec[0], 3, argvec + 1, exp);
 	if (gnu_runtime)
 	  {
 	    /* Function objc_msg_lookup returns a pointer.  */
 	    argvec[0] = ret;
-	    ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	    ret = call_function_by_hand (argvec[0], 3, argvec + 1, exp);
 	  }
 	if (value_as_long (ret) == 0)
 	  error (_("Target does not respond to this message selector."));
@@ -1165,11 +1229,11 @@ evaluate_subexp_standard (struct type *expect_type,
 	argvec[3] = value_from_longest (long_type, selector);
 	argvec[4] = 0;
 
-	ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	ret = call_function_by_hand (argvec[0], 3, argvec + 1, exp);
 	if (gnu_runtime)
 	  {
 	    argvec[0] = ret;
-	    ret = call_function_by_hand (argvec[0], 3, argvec + 1);
+	    ret = call_function_by_hand (argvec[0], 3, argvec + 1, exp);
 	  }
 
 	/* ret should now be the selector.  */
@@ -1311,10 +1375,10 @@ evaluate_subexp_standard (struct type *expect_type,
 	    deprecated_set_value_type (argvec[0],
 				       lookup_pointer_type (lookup_function_type (value_type (argvec[0]))));
 	    argvec[0]
-	      = call_function_by_hand (argvec[0], nargs + 2, argvec + 1);
+	      = call_function_by_hand (argvec[0], nargs + 2, argvec + 1, exp);
 	  }
 
-	ret = call_function_by_hand (argvec[0], nargs + 2, argvec + 1);
+	ret = call_function_by_hand (argvec[0], nargs + 2, argvec + 1, exp);
 	return ret;
       }
       break;
@@ -1431,7 +1495,7 @@ evaluate_subexp_standard (struct type *expect_type,
 		  struct value *value = NULL;
 		  TRY_CATCH (except, RETURN_MASK_ERROR)
 		    {
-		      value = value_x_unop (arg2, op, noside);
+		      value = value_x_unop (arg2, op, noside, exp);
 		    }
 
 		  if (except.reason < 0)
@@ -1734,7 +1798,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	case TYPE_CODE_XMETHOD:
 	  return call_xmethod (argvec[0], nargs, argvec + 1);
 	default:
-	  return call_function_by_hand (argvec[0], nargs, argvec + 1);
+	  return call_function_by_hand (argvec[0], nargs, argvec + 1, exp);
 	}
       /* pai: FIXME save value from call_function_by_hand, then adjust
 	 pc by adjust_fn_pc if +ve.  */
@@ -1845,7 +1909,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	  struct value *value = NULL;
 	  TRY_CATCH (except, RETURN_MASK_ERROR)
 	    {
-	      value = value_x_unop (arg1, op, noside);
+	      value = value_x_unop (arg1, op, noside, exp);
 	    }
 
 	  if (except.reason < 0)
@@ -1945,7 +2009,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else
 	return value_concat (arg1, arg2);
 
@@ -1956,7 +2020,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
 	return arg1;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else
 	return value_assign (arg1, arg2);
 
@@ -1968,7 +2032,8 @@ evaluate_subexp_standard (struct type *expect_type,
 	return arg1;
       op = exp->elts[pc + 1].opcode;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op, noside);
+	return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op, noside,
+			      exp);
       else if (op == BINOP_ADD && ptrmath_type_p (exp->language_defn,
 						  value_type (arg1))
 	       && is_integral_type (value_type (arg2)))
@@ -1999,7 +2064,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else if (ptrmath_type_p (exp->language_defn, value_type (arg1))
 	       && is_integral_type (value_type (arg2)))
 	return value_ptradd (arg1, value_as_long (arg2));
@@ -2018,7 +2083,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else if (ptrmath_type_p (exp->language_defn, value_type (arg1))
 	       && ptrmath_type_p (exp->language_defn, value_type (arg2)))
 	{
@@ -2051,7 +2116,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else
 	{
 	  /* If EVAL_AVOID_SIDE_EFFECTS and we're dividing by zero,
@@ -2094,7 +2159,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
-	return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
       else
 	{
 	  /* If the user attempts to subscript something that is not an
@@ -2160,7 +2225,7 @@ evaluate_subexp_standard (struct type *expect_type,
 
 	  if (binop_user_defined_p (op, arg1, arg2))
 	    {
-	      arg1 = value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	      arg1 = value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	    }
 	  else
 	    {
@@ -2244,7 +2309,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2271,7 +2336,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (binop_user_defined_p (op, arg1, arg2))
 	{
 	  arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2290,7 +2355,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2307,7 +2372,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2324,7 +2389,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2341,7 +2406,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2358,7 +2423,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2375,7 +2440,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	goto nosideret;
       if (binop_user_defined_p (op, arg1, arg2))
 	{
-	  return value_x_binop (arg1, arg2, op, OP_NULL, noside);
+	  return value_x_binop (arg1, arg2, op, OP_NULL, noside, exp);
 	}
       else
 	{
@@ -2410,7 +2475,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (unop_user_defined_p (op, arg1))
-	return value_x_unop (arg1, op, noside);
+	return value_x_unop (arg1, op, noside, exp);
       else
 	{
 	  unop_promote (exp->language_defn, exp->gdbarch, &arg1);
@@ -2422,7 +2487,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (unop_user_defined_p (op, arg1))
-	return value_x_unop (arg1, op, noside);
+	return value_x_unop (arg1, op, noside, exp);
       else
 	{
 	  unop_promote (exp->language_defn, exp->gdbarch, &arg1);
@@ -2437,7 +2502,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (unop_user_defined_p (UNOP_COMPLEMENT, arg1))
-	return value_x_unop (arg1, UNOP_COMPLEMENT, noside);
+	return value_x_unop (arg1, UNOP_COMPLEMENT, noside, exp);
       else
 	{
 	  unop_promote (exp->language_defn, exp->gdbarch, &arg1);
@@ -2449,7 +2514,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (unop_user_defined_p (op, arg1))
-	return value_x_unop (arg1, op, noside);
+	return value_x_unop (arg1, op, noside, exp);
       else
 	{
 	  type = language_bool_type (exp->language_defn, exp->gdbarch);
@@ -2468,7 +2533,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
 	goto nosideret;
       if (unop_user_defined_p (op, arg1))
-	return value_x_unop (arg1, op, noside);
+	return value_x_unop (arg1, op, noside, exp);
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
 	  type = check_typedef (value_type (arg1));
@@ -2602,7 +2667,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	return arg1;
       else if (unop_user_defined_p (op, arg1))
 	{
-	  return value_x_unop (arg1, op, noside);
+	  return value_x_unop (arg1, op, noside, exp);
 	}
       else
 	{
@@ -2626,7 +2691,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	return arg1;
       else if (unop_user_defined_p (op, arg1))
 	{
-	  return value_x_unop (arg1, op, noside);
+	  return value_x_unop (arg1, op, noside, exp);
 	}
       else
 	{
@@ -2650,7 +2715,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	return arg1;
       else if (unop_user_defined_p (op, arg1))
 	{
-	  return value_x_unop (arg1, op, noside);
+	  return value_x_unop (arg1, op, noside, exp);
 	}
       else
 	{
@@ -2677,7 +2742,7 @@ evaluate_subexp_standard (struct type *expect_type,
 	return arg1;
       else if (unop_user_defined_p (op, arg1))
 	{
-	  return value_x_unop (arg1, op, noside);
+	  return value_x_unop (arg1, op, noside, exp);
 	}
       else
 	{
@@ -2827,7 +2892,7 @@ evaluate_subexp_for_address (struct expression *exp, int *pos,
       /* We can't optimize out "&*" if there's a user-defined operator*.  */
       if (unop_user_defined_p (op, x))
 	{
-	  x = value_x_unop (x, op, noside);
+	  x = value_x_unop (x, op, noside, exp);
 	  goto default_case_after_eval;
 	}
 
diff --git a/gdb/expression.h b/gdb/expression.h
index 4081a60..05242b6 100644
--- a/gdb/expression.h
+++ b/gdb/expression.h
@@ -76,11 +76,17 @@ union exp_element
     struct objfile *objfile;
   };
 
+struct value;
+typedef struct value *value_ptr;
+DEF_VEC_P (value_ptr);
+typedef VEC (value_ptr) value_vec;
+
 struct expression
   {
     const struct language_defn *language_defn;	/* language it was
 						   entered in.  */
     struct gdbarch *gdbarch;  /* architecture it was parsed in.  */
+    value_vec *on_stack_temporaries_vec;
     int nelts;
     union exp_element elts[1];
   };
diff --git a/gdb/gcore.c b/gdb/gcore.c
index d2adfc8..c4ba961 100644
--- a/gdb/gcore.c
+++ b/gdb/gcore.c
@@ -308,7 +308,7 @@ call_target_sbrk (int sbrk_arg)
   target_sbrk_arg = value_from_longest (builtin_type (gdbarch)->builtin_int, 
 					sbrk_arg);
   gdb_assert (target_sbrk_arg);
-  ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg);
+  ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg, NULL);
   if (ret == NULL)
     return (bfd_vma) 0;
 
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index ee33d77..5388612 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2501,6 +2501,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.  */
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index bd1a0ab..f9d2986 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1751,6 +1751,8 @@ extern int get_array_bounds (struct type *type, LONGEST *low_bound,
 
 extern int class_types_same_p (const struct type *, const struct type *);
 
+extern int class_or_union_p (const struct type *);
+
 extern int is_ancestor (struct type *, struct type *);
 
 extern int is_public_ancestor (struct type *, struct type *);
diff --git a/gdb/infcall.c b/gdb/infcall.c
index bbac693..241ec2e 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -455,6 +455,33 @@ cleanup_delete_std_terminate_breakpoint (void *ignore)
   delete_std_terminate_breakpoint ();
 }
 
+/* Reads a value returned by an inferior function for those return
+   values whose address is passed as the hidden first argument.
+   TYPE is the type of value.  ADDR is the address from where to read it.
+   EXP is the expression whose evaluation lead to calling the inferior
+   function.  It is NULL if the inferior function call was not made while
+   evaluating an expression.  */
+
+static struct value *
+get_return_value_from_memory (struct type *type, CORE_ADDR addr,
+			      struct expression *exp)
+{
+  struct value *retval;
+  if (exp != NULL)
+    {
+      retval = value_from_contents_and_address (type, NULL, addr);
+      add_value_to_expression_stack (exp, retval);
+    }
+  else
+    {
+      retval = allocate_value (type);
+      read_value_memory (retval, 0, 1, addr, value_contents_raw (retval),
+			 TYPE_LENGTH (type));
+    }
+
+  return retval;
+}
+
 /* All this stuff with a dummy frame may seem unnecessarily complicated
    (why not just save registers in GDB?).  The purpose of pushing a dummy
    frame which looks just like a real frame is so that if you call a
@@ -471,10 +498,14 @@ cleanup_delete_std_terminate_breakpoint (void *ignore)
    May fail to return, if a breakpoint or signal is hit
    during the execution of the function.
 
-   ARGS is modified to contain coerced values.  */
+   ARGS is modified to contain coerced values.
+   EXP is the expression whose evaluation lead to calling the inferior
+   function.  It is NULL if the inferior function call was not made while
+   evaluating an expression.  */
 
 struct value *
-call_function_by_hand (struct value *function, int nargs, struct value **args)
+call_function_by_hand (struct value *function, int nargs, struct value **args,
+		       struct expression *exp)
 {
   CORE_ADDR sp;
   struct type *values_type, *target_values_type;
@@ -532,6 +563,16 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
   {
     CORE_ADDR old_sp = get_frame_sp (frame);
 
+    /* Skip over the stack mirrors that might have been generated during the
+       evaluation of the current expression.  */
+    if (exp != NULL)
+      {
+	if (gdbarch_inner_than (gdbarch, 1, 2))
+	  old_sp = skip_current_expression_stack (exp, old_sp, 1);
+	else
+	  old_sp = skip_current_expression_stack (exp, old_sp, 0);
+      }
+
     if (gdbarch_frame_align_p (gdbarch))
       {
 	sp = gdbarch_frame_align (gdbarch, old_sp);
@@ -719,9 +760,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.
+
+     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)
+  if (struct_return || hidden_first_param_p
+      || (exp != NULL && class_or_union_p (values_type)))
     {
       if (gdbarch_inner_than (gdbarch, 1, 2))
 	{
@@ -1059,13 +1112,8 @@ 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));
+      retval = get_return_value_from_memory (values_type, struct_addr, exp);
     else if (TYPE_CODE (target_values_type) != TYPE_CODE_VOID)
       {
 	/* If the function returns void, don't bother fetching the
@@ -1076,16 +1124,30 @@ When the function is done executing, GDB will silently stop."),
 	  case RETURN_VALUE_REGISTER_CONVENTION:
 	  case RETURN_VALUE_ABI_RETURNS_ADDRESS:
 	  case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
+	    retval = allocate_value (values_type);
 	    gdbarch_return_value (gdbarch, function, values_type,
 				  retbuf, value_contents_raw (retval), NULL);
+	    if (exp != NULL && 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.  */
+		write_value_to_memory (retval, struct_addr);
+		add_value_to_expression_stack (exp, retval);
+	      }
 	    break;
 	  case RETURN_VALUE_STRUCT_CONVENTION:
-	    read_value_memory (retval, 0, 1, struct_addr,
-			       value_contents_raw (retval),
-			       TYPE_LENGTH (values_type));
+	    retval = get_return_value_from_memory (values_type, struct_addr,
+						   exp);
 	    break;
 	  }
       }
+    else
+      retval = allocate_value (values_type);
 
     do_cleanups (retbuf_cleanup);
 
diff --git a/gdb/infcall.h b/gdb/infcall.h
index c6dcdc3..4de931b 100644
--- a/gdb/infcall.h
+++ b/gdb/infcall.h
@@ -22,6 +22,7 @@
 
 struct value;
 struct type;
+struct expression;
 
 extern CORE_ADDR find_function_addr (struct value *function, 
 				     struct type **retval_type);
@@ -36,6 +37,7 @@ extern CORE_ADDR find_function_addr (struct value *function,
    ARGS is modified to contain coerced values.  */
 
 extern struct value *call_function_by_hand (struct value *function, int nargs,
-					    struct value **args);
+					    struct value **args,
+					    struct expression *exp);
 
 #endif
diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c
index 835e612..8ba4741 100644
--- a/gdb/linux-fork.c
+++ b/gdb/linux-fork.c
@@ -468,7 +468,7 @@ inferior_call_waitpid (ptid_t pptid, int pid)
   argv[2] = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
   argv[3] = 0;
 
-  retv = call_function_by_hand (waitpid_fn, 3, argv);
+  retv = call_function_by_hand (waitpid_fn, 3, argv, NULL);
   if (value_as_long (retv) < 0)
     goto out;
 
@@ -683,7 +683,7 @@ checkpoint_command (char *args, int from_tty)
   old_chain = make_cleanup_restore_integer (&checkpointing_pid);
   checkpointing_pid = ptid_get_pid (inferior_ptid);
 
-  ret = call_function_by_hand (fork_fn, 0, &ret);
+  ret = call_function_by_hand (fork_fn, 0, &ret, NULL);
   do_cleanups (old_chain);
   if (!ret)	/* Probably can't happen.  */
     error (_("checkpoint: call_function_by_hand returned null."));
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 8a3d21e..10194f0 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -131,7 +131,7 @@ lookup_objc_class (struct gdbarch *gdbarch, char *classname)
   classval = value_string (classname, strlen (classname) + 1, char_type);
   classval = value_coerce_array (classval);
   return (CORE_ADDR) value_as_long (call_function_by_hand (function, 
-							   1, &classval));
+							   1, &classval, NULL));
 }
 
 CORE_ADDR
@@ -160,7 +160,7 @@ lookup_child_selector (struct gdbarch *gdbarch, char *selname)
   selstring = value_coerce_array (value_string (selname, 
 						strlen (selname) + 1,
 						char_type));
-  return value_as_long (call_function_by_hand (function, 1, &selstring));
+  return value_as_long (call_function_by_hand (function, 1, &selstring, NULL));
 }
 
 struct value * 
@@ -181,12 +181,12 @@ value_nsstring (struct gdbarch *gdbarch, char *ptr, int len)
   if (lookup_minimal_symbol("_NSNewStringFromCString", 0, 0).minsym)
     {
       function = find_function_in_inferior("_NSNewStringFromCString", NULL);
-      nsstringValue = call_function_by_hand(function, 1, &stringValue[2]);
+      nsstringValue = call_function_by_hand(function, 1, &stringValue[2], NULL);
     }
   else if (lookup_minimal_symbol("istr", 0, 0).minsym)
     {
       function = find_function_in_inferior("istr", NULL);
-      nsstringValue = call_function_by_hand(function, 1, &stringValue[2]);
+      nsstringValue = call_function_by_hand(function, 1, &stringValue[2], NULL);
     }
   else if (lookup_minimal_symbol("+[NSString stringWithCString:]", 0, 0).minsym)
     {
@@ -198,7 +198,7 @@ value_nsstring (struct gdbarch *gdbarch, char *ptr, int len)
 	(type, lookup_objc_class (gdbarch, "NSString"));
       stringValue[1] = value_from_longest 
 	(type, lookup_child_selector (gdbarch, "stringWithCString:"));
-      nsstringValue = call_function_by_hand(function, 3, &stringValue[0]);
+      nsstringValue = call_function_by_hand(function, 3, &stringValue[0], NULL);
     }
   else
     error (_("NSString: internal error -- no way to create new NSString"));
@@ -1194,7 +1194,7 @@ print_object_command (char *args, int from_tty)
   if (function == NULL)
     error (_("Unable to locate _NSPrintForDebugger in child process"));
 
-  description = call_function_by_hand (function, 1, &object);
+  description = call_function_by_hand (function, 1, &object, NULL);
 
   string_addr = value_as_long (description);
   if (string_addr == 0)
diff --git a/gdb/parse.c b/gdb/parse.c
index 27947e7..a03c266 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -193,6 +193,7 @@ initialize_expout (struct parser_state *ps, size_t initial_size,
 			+ EXP_ELEM_TO_BYTES (ps->expout_size));
   ps->expout->language_defn = lang;
   ps->expout->gdbarch = gdbarch;
+  ps->expout->on_stack_temporaries_vec = NULL;
 }
 
 /* See definition in parser-defs.h.  */
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 4313170..0d65b48 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -795,7 +795,7 @@ valpy_call (PyObject *self, PyObject *args, PyObject *keywords)
       struct cleanup *cleanup = make_cleanup_value_free_to_mark (mark);
       struct value *return_value;
 
-      return_value = call_function_by_hand (function, args_count, vargs);
+      return_value = call_function_by_hand (function, args_count, vargs, NULL);
       result = value_to_value_object (return_value);
       do_cleanups (cleanup);
     }
@@ -1039,7 +1039,10 @@ valpy_binop (enum valpy_opcode opcode, PyObject *self, PyObject *other)
       if (!handled)
 	{
 	  if (binop_user_defined_p (op, arg1, arg2))
-	    res_val = value_x_binop (arg1, arg2, op, OP_NULL, EVAL_NORMAL);
+	    {
+	      res_val = value_x_binop (arg1, arg2, op, OP_NULL,
+				       EVAL_NORMAL, NULL);
+	    }
 	  else
 	    res_val = value_binop (arg1, arg2, op);
 	}
diff --git a/gdb/spu-tdep.c b/gdb/spu-tdep.c
index 032f5de..f3bd6c7 100644
--- a/gdb/spu-tdep.c
+++ b/gdb/spu-tdep.c
@@ -2058,7 +2058,7 @@ flush_ea_cache (void)
       type = lookup_pointer_type (type);
       addr = BMSYMBOL_VALUE_ADDRESS (msymbol);
 
-      call_function_by_hand (value_from_pointer (type, addr), 0, NULL);
+      call_function_by_hand (value_from_pointer (type, addr), 0, NULL, NULL);
     }
 }
 
diff --git a/gdb/testsuite/gdb.cp/chained-calls.cc b/gdb/testsuite/gdb.cp/chained-calls.cc
new file mode 100644
index 0000000..a30ec3b
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/chained-calls.cc
@@ -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  */
+}
diff --git a/gdb/testsuite/gdb.cp/chained-calls.exp b/gdb/testsuite/gdb.cp/chained-calls.exp
new file mode 100644
index 0000000..c903bea
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/chained-calls.exp
@@ -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 ()"
diff --git a/gdb/testsuite/gdb.cp/smartp.exp b/gdb/testsuite/gdb.cp/smartp.exp
index 2a1028a..e3d271f 100644
--- a/gdb/testsuite/gdb.cp/smartp.exp
+++ b/gdb/testsuite/gdb.cp/smartp.exp
@@ -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"
 
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 154629b..d80cf62 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -339,11 +339,16 @@ value_user_defined_op (struct value **argp, struct value **args, char *name,
 
    OP is the operatore, and if it is BINOP_ASSIGN_MODIFY, then OTHEROP
    is the opcode saying how to modify it.  Otherwise, OTHEROP is
-   unused.  */
+   unused.
+
+   EXP is the expression whose evaluation requires performing the binary
+   operation.  It is NULL if the operation is not being performed as part
+   of an expression evaluation.  */
 
 struct value *
 value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
-	       enum exp_opcode otherop, enum noside noside)
+	       enum exp_opcode otherop, enum noside noside,
+	       struct expression *exp)
 {
   struct value **argvec;
   char *ptr;
@@ -499,12 +504,13 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	}
       else
 	return call_function_by_hand (argvec[0], 2 - static_memfuncp,
-				      argvec + 1);
+				      argvec + 1, exp);
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
 #ifdef lint
-  return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1);
+  return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1,
+				exp);
 #endif
 }
 
@@ -512,10 +518,15 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
    defined operator that matches the operator in question.
    Create an argument vector that calls arg1.operator @ (arg1)
    and return that value (where '@' is (almost) any unary operator which
-   is legal for GNU C++).  */
+   is legal for GNU C++).
+
+   EXP is the expression whose evaluation requires performing the unary
+   operation.  It is NULL if the operation is not being performed as part
+   of an expression evaluation.  */
 
 struct value *
-value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
+value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside,
+	      struct expression *exp)
 {
   struct gdbarch *gdbarch = get_type_arch (value_type (arg1));
   struct value **argvec;
@@ -609,7 +620,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	  return call_xmethod (argvec[0], 1, argvec + 1);
 	}
       else
-	return call_function_by_hand (argvec[0], nargs, argvec + 1);
+	return call_function_by_hand (argvec[0], nargs, argvec + 1, exp);
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index 7f3e4f5..5fcefc2 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -191,7 +191,7 @@ value_allocate_space_in_inferior (int len)
   struct value *blocklen;
 
   blocklen = value_from_longest (builtin_type (gdbarch)->builtin_int, len);
-  val = call_function_by_hand (val, 1, &blocklen);
+  val = call_function_by_hand (val, 1, &blocklen, NULL);
   if (value_logical_not (val))
     {
       if (!target_has_execution)
diff --git a/gdb/value.c b/gdb/value.c
index ecfb154..058d04f 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1080,6 +1080,20 @@ set_value_parent (struct value *value, struct value *parent)
   value_free (old);
 }
 
+/* Write contents of V at ADDR.  Also, set lval_type of V to lval_memory.
+   It is as error if V's lval_type is anything other than not_lval.  */
+
+void
+write_value_to_memory (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)));
+  VALUE_LVAL (v) = lval_memory;
+  v->location.address = addr;
+  v->lazy = 0;
+}
+
 gdb_byte *
 value_contents_raw (struct value *value)
 {
@@ -3730,11 +3744,8 @@ value_initialized (struct value *val)
   return val->initialized;
 }
 
-/* Called only from the value_contents and value_contents_all()
-   macros, if the current data for a variable needs to be loaded into
-   value_contents(VAL).  Fetches the data from the user's process, and
-   clears the lazy flag to indicate that the data in the buffer is
-   valid.
+/* Fetches the data from the user's process, and clears the lazy flag
+   to indicate that the data in the buffer is valid.
 
    If the value is zero-length, we avoid calling read_memory, which
    would abort.  We mark the value as fetched anyway -- all 0 bytes of
diff --git a/gdb/value.h b/gdb/value.h
index e3603c3..1e0efee 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -363,6 +363,8 @@ extern const gdb_byte *
 
 extern int value_fetch_lazy (struct value *val);
 
+extern void write_value_to_memory (struct value *v, CORE_ADDR addr);
+
 /* If nonzero, this is the value of a variable which does not actually
    exist in the program, at least partially.  If the value is lazy,
    this may fetch it now.  */
@@ -819,6 +821,12 @@ extern struct value *evaluate_subexp (struct type *expect_type,
 extern struct value *evaluate_subexpression_type (struct expression *exp,
 						  int subexp);
 
+extern void add_value_to_expression_stack (struct expression *exp,
+					   struct value *v);
+
+extern CORE_ADDR skip_current_expression_stack (struct expression *exp,
+						CORE_ADDR sp, int downward);
+
 extern void fetch_subexp_value (struct expression *exp, int *pc,
 				struct value **valp, struct value **resultp,
 				struct value **val_chain,
@@ -939,10 +947,11 @@ extern struct value *value_of_this_silent (const struct language_defn *lang);
 extern struct value *value_x_binop (struct value *arg1, struct value *arg2,
 				    enum exp_opcode op,
 				    enum exp_opcode otherop,
-				    enum noside noside);
+				    enum noside noside,
+				    struct expression *exp);
 
 extern struct value *value_x_unop (struct value *arg1, enum exp_opcode op,
-				   enum noside noside);
+				   enum noside noside, struct expression *exp);
 
 extern struct value *value_fn_field (struct value **arg1p, struct fn_field *f,
 				     int j, struct type *type, int offset);

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2014-11-13  3:00 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-25 14:24 [PATCH v4] Make chained function calls in expressions work Siva Chandra
2014-11-03 14:35 ` Siva Chandra
2014-11-03 14:43 ` Ulrich Weigand
2014-11-03 19:55   ` Siva Chandra
2014-11-03 21:22     ` Siva Chandra
2014-11-04 13:43       ` Ulrich Weigand
2014-11-04 15:08         ` Siva Chandra
2014-11-04 15:40           ` Ulrich Weigand
2014-11-04 13:38     ` Ulrich Weigand
2014-11-04 14:26       ` Siva Chandra
2014-11-04 14:59         ` Ulrich Weigand
2014-11-04 15:23           ` Siva Chandra
2014-11-04 15:40             ` Ulrich Weigand
2014-11-11 14:55               ` Siva Chandra
2014-11-11 15:00                 ` Siva Chandra
2014-11-11 15:21                   ` Ulrich Weigand
2014-11-11 17:05                     ` Siva Chandra
2014-11-12 16:08                     ` Doug Evans
2014-11-12 17:29                       ` Doug Evans
2014-11-13  3:00                       ` Siva Chandra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox