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] [PR python/18285] Fix.


Hi.

This patch fixes pr 18285 by adding a new get_result_type method
to xmethod workers.  This way we can fetch the result type without
having to call the xmethod (which is important for ptype where
we have EVAL_AVOID_SIDE_EFFECTS).

Regression tested on amd64-linux.

2015-04-24  Doug Evans  <dje@google.com>

	PR python/18285
	* NEWS: Document new gdb.XMethodWorker.get_result_type method.
	* eval.c (evaluate_subexp_standard) <OP_FUNCALL>: Handle
	EVAL_AVOID_SIDE_EFFECTS for xmethods.
	* extension-priv.h (struct extension_language_ops)
	<get_xmethod_result_type>: New member.
	* extension.c (get_xmethod_result_type): New function.
	* extension.h (get_xmethod_result_type): Declare.
	* python/py-xmethods.c (get_result_type_method_name): New static
	global.
	(py_get_result_type_method_name): Ditto.
	(gdbpy_get_xmethod_result_type): New function.
	(gdbpy_initialize_xmethods): Initialize py_get_result_type_method_name.
	* python/python-internal.h (gdbpy_get_xmethod_result_type): Declare.
	* python/python.c (python_extension_ops): Add
	gdbpy_get_xmethod_result_type.
	* valarith.c (value_x_binop): Handle EVAL_AVOID_SIDE_EFFECTS for
	xmethods.
	(value_x_unop): Ditto.
	* value.c (result_type_of_xmethod): New function.
	* value.h (result_type_of_xmethod): Declare.

	testsuite/
	* gdb.python/py-xmethods.exp: Add ptype tests.
	* gdb.python/py-xmethods.py (E_method_char_worker): Add
	get_result_type method.

	doc/
	* python.texi (Xmethod API) <gdb.XMethodWorker.get_result_type>:
	Document.
	(Writing an Xmethod): Add get_result_type to example.

diff --git a/gdb/NEWS b/gdb/NEWS
index 62cbdcb..d2446cc 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -48,6 +48,7 @@
      which is the name of the objfile as specified by the user,
      without, for example, resolving symlinks.
   ** You can now write frame unwinders in Python.
+  ** Xmethods can now specify a result type.
 
 * New commands
 
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 098d718..2d099b4 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2360,7 +2360,7 @@ xmethod worker is found to be equivalent to the winning C@t{++}
 method, then the xmethod worker is treated as a replacement for
 the C@t{++} method.  @value{GDBN} uses the overall winner to invoke the
 method.  If the winning xmethod worker is the overall winner, then
-the corresponding xmethod is invoked via the @code{invoke} method
+the corresponding xmethod is called via the @code{__call__} method
 of the worker object.
 
 If one wants to implement an xmethod as a replacement for an
@@ -2450,6 +2450,13 @@ If the xmethod takes a single argument, then a single
 @code{gdb.Type} object corresponding to it can be returned.
 @end defun
 
+@defun XMethodWorker.get_result_type (self, *args)
+This method returns a @code{gdb.Type} object representing the type
+of the result of invoking this xmethod.
+The @var{args} argument is the same tuple of arguments that would be
+passed to the @code{__call__} method of this worker.
+@end defun
+
 @defun XMethodWorker.__call__ (self, *args)
 This is the method which does the @emph{work} of the xmethod.  The
 @var{args} arguments is the tuple of arguments to the xmethod.  Each
@@ -2566,6 +2573,9 @@ above is as follows:
 class MyClassWorker_geta(gdb.xmethod.XMethodWorker):
     def get_arg_types(self):
         return None
+
+    def get_result_type(self, obj):
+        return gdb.lookup_type('int')
  
     def __call__(self, obj):
         return obj['a_']
@@ -2574,6 +2584,9 @@ class MyClassWorker_geta(gdb.xmethod.XMethodWorker):
 class MyClassWorker_plus(gdb.xmethod.XMethodWorker):
     def get_arg_types(self):
         return gdb.lookup_type('MyClass')
+
+    def get_result_type(self, obj):
+        return gdb.lookup_type('int')
  
     def __call__(self, obj, other):
         return obj['a_'] + other['a_']
@@ -2637,10 +2650,13 @@ of the xmethod workers and xmethod matchers is as follows:
 class MyTemplateWorker_footprint(gdb.xmethod.XMethodWorker):
     def __init__(self, class_type):
         self.class_type = class_type
- 
+
     def get_arg_types(self):
         return None
- 
+
+    def get_result_type(self):
+        return gdb.lookup_type('int')
+
     def __call__(self, obj):
         return (self.class_type.sizeof +
                 obj['dsize_'] *
diff --git a/gdb/eval.c b/gdb/eval.c
index 9804a97..6bbd495 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1742,6 +1742,15 @@ evaluate_subexp_standard (struct type *expect_type,
 	      return value_zero (builtin_type (exp->gdbarch)->builtin_int,
 				 not_lval);
 	    }
+	  else if (TYPE_CODE (ftype) == TYPE_CODE_XMETHOD)
+	    {
+	      struct type *return_type
+		= result_type_of_xmethod (argvec[0], nargs, argvec + 1);
+
+	      if (return_type == NULL)
+		error (_("Xmethod is missing return type."));
+	      return value_zero (return_type, not_lval);
+	    }
 	  else if (TYPE_GNU_IFUNC (ftype))
 	    return allocate_value (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype)));
 	  else if (TYPE_TARGET_TYPE (ftype))
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
index dd2600e..d0242e2 100644
--- a/gdb/extension-priv.h
+++ b/gdb/extension-priv.h
@@ -265,7 +265,7 @@ struct extension_language_ops
   /* xmethod support:
      clone_xmethod_worker_data, free_xmethod_worker_data,
      get_matching_xmethod_workers, get_xmethod_arg_types,
-     invoke_xmethod.
+     get_xmethod_return_type, invoke_xmethod.
      These methods are optional and may be NULL, but if one of them is
      implemented then they all must be.  */
 
@@ -298,6 +298,18 @@ struct extension_language_ops
      int *nargs,
      struct type ***arg_types);
 
+  /* Given a WORKER servicing a particular method, fetch the type of the
+     result of the method.  OBJECT, ARGS, NARGS are the same as for
+     invoke_xmethod.  The result type is stored in *RESULT_TYPE.
+     For backward compatibility with 7.9, which did not support getting the
+     result type, if the get_result_type operation is not provided by WORKER
+     then EXT_LANG_RC_OK is returned and NULL is returned in *RESULT_TYPE.  */
+  enum ext_lang_rc (*get_xmethod_result_type)
+    (const struct extension_language_defn *extlang,
+     struct xmethod_worker *worker,
+     struct value *object, struct value **args, int nargs,
+     struct type **result_type);
+
   /* Invoke the xmethod serviced by WORKER.  The xmethod is invoked
      on OBJECT with arguments in the array ARGS.  NARGS is the length of
      this array.  Returns the value returned by the xmethod.  */
diff --git a/gdb/extension.c b/gdb/extension.c
index 77b62e0..dac203b 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -964,6 +964,31 @@ get_xmethod_arg_types (struct xmethod_worker *worker, int *nargs)
   return type_array;
 }
 
+/* Return the type of the result of the xmethod encapsulated in WORKER.
+   OBJECT, ARGS, NARGS are the same as for invoke_xmethod.  */
+
+struct type *
+get_xmethod_result_type (struct xmethod_worker *worker,
+			 struct value *object, struct value **args, int nargs)
+{
+  enum ext_lang_rc rc;
+  struct type *result_type;
+  const struct extension_language_defn *extlang = worker->extlang;
+
+  gdb_assert (extlang->ops->get_xmethod_arg_types != NULL);
+
+  rc = extlang->ops->get_xmethod_result_type (extlang, worker,
+					      object, args, nargs,
+					      &result_type);
+  if (rc == EXT_LANG_RC_ERROR)
+    {
+      error (_("Error while fetching result type of an xmethod worker "
+	       "defined in %s."), extlang->capitalized_name);
+    }
+
+  return result_type;
+}
+
 /* Invokes the xmethod encapsulated in WORKER and returns the result.
    The method is invoked on OBJ with arguments in the ARGS array.  NARGS is
    the length of the this array.  */
diff --git a/gdb/extension.h b/gdb/extension.h
index e8d7478..ea30035 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -260,4 +260,8 @@ extern xmethod_worker_vec *get_matching_xmethod_workers
 
 extern struct type **get_xmethod_arg_types (struct xmethod_worker *, int *);
 
+extern struct type *get_xmethod_result_type (struct xmethod_worker *,
+					     struct value *object,
+					     struct value **args, int nargs);
+
 #endif /* EXTENSION_H */
diff --git a/gdb/python/py-xmethods.c b/gdb/python/py-xmethods.c
index 0a7fc38..91e2691 100644
--- a/gdb/python/py-xmethods.c
+++ b/gdb/python/py-xmethods.c
@@ -30,11 +30,13 @@
 static const char enabled_field_name[] = "enabled";
 static const char match_method_name[] = "match";
 static const char get_arg_types_method_name[] = "get_arg_types";
+static const char get_result_type_method_name[] = "get_result_type";
 static const char invoke_method_name[] = "invoke";
 static const char matchers_attr_str[] = "xmethods";
 
 static PyObject *py_match_method_name = NULL;
 static PyObject *py_get_arg_types_method_name = NULL;
+static PyObject *py_get_result_type_method_name = NULL;
 static PyObject *py_invoke_method_name = NULL;
 
 struct gdbpy_worker_data
@@ -502,6 +504,106 @@ gdbpy_get_xmethod_arg_types (const struct extension_language_defn *extlang,
   return EXT_LANG_RC_OK;
 }
 
+/* Implementation of get_xmethod_result_type for Python.  */
+
+enum ext_lang_rc
+gdbpy_get_xmethod_result_type (const struct extension_language_defn *extlang,
+			       struct xmethod_worker *worker,
+			       struct value *obj,
+			       struct value **args, int nargs,
+			       struct type **result_type_ptr)
+{
+  struct gdbpy_worker_data *worker_data = worker->data;
+  PyObject *py_worker = worker_data->worker;
+  PyObject *py_value_obj, *py_arg_tuple, *py_result_type;
+  PyObject *get_result_type_method;
+  struct type *obj_type, *this_type;
+  struct cleanup *cleanups;
+  int i;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  /* First see if there is a get_result_type method.
+     If not this could be an old xmethod (pre 7.9.1).  */
+  get_result_type_method
+    = PyObject_GetAttrString (py_worker, get_result_type_method_name);
+  if (get_result_type_method == NULL)
+    {
+      PyErr_Clear ();
+      do_cleanups (cleanups);
+      *result_type_ptr = NULL;
+      return EXT_LANG_RC_OK;
+    }
+  make_cleanup_py_decref (get_result_type_method);
+
+  obj_type = check_typedef (value_type (obj));
+  this_type = check_typedef (type_object_to_type (worker_data->this_type));
+  if (TYPE_CODE (obj_type) == TYPE_CODE_PTR)
+    {
+      struct type *this_ptr = lookup_pointer_type (this_type);
+
+      if (!types_equal (obj_type, this_ptr))
+	obj = value_cast (this_ptr, obj);
+    }
+  else if (TYPE_CODE (obj_type) == TYPE_CODE_REF)
+    {
+      struct type *this_ref = lookup_reference_type (this_type);
+
+      if (!types_equal (obj_type, this_ref))
+	obj = value_cast (this_ref, obj);
+    }
+  else
+    {
+      if (!types_equal (obj_type, this_type))
+	obj = value_cast (this_type, obj);
+    }
+  py_value_obj = value_to_value_object (obj);
+  if (py_value_obj == NULL)
+    goto Fail;
+  make_cleanup_py_decref (py_value_obj);
+
+  py_arg_tuple = PyTuple_New (nargs + 1);
+  if (py_arg_tuple == NULL)
+    goto Fail;
+  make_cleanup_py_decref (py_arg_tuple);
+
+  /* PyTuple_SET_ITEM steals the reference of the element.  Hence INCREF the
+     reference to the 'this' object as we have a cleanup to DECREF it.  */
+  Py_INCREF (py_value_obj);
+  PyTuple_SET_ITEM (py_arg_tuple, 0, py_value_obj);
+
+  for (i = 0; i < nargs; i++)
+    {
+      PyObject *py_value_arg = value_to_value_object (args[i]);
+
+      if (py_value_arg == NULL)
+	goto Fail;
+      PyTuple_SET_ITEM (py_arg_tuple, i + 1, py_value_arg);
+    }
+
+  py_result_type = PyObject_CallObject (get_result_type_method, py_arg_tuple);
+  if (py_result_type == NULL)
+    goto Fail;
+  make_cleanup_py_decref (py_result_type);
+
+  *result_type_ptr = type_object_to_type (py_result_type);
+  if (*result_type_ptr == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Type returned by the get_result_type method of an"
+			 " xmethod worker object is not a gdb.Type object."));
+      goto Fail;
+    }
+
+  do_cleanups (cleanups);
+  return EXT_LANG_RC_OK;
+
+ Fail:
+  gdbpy_print_stack ();
+  do_cleanups (cleanups);
+  return EXT_LANG_RC_ERROR;
+}
+
 /* Implementation of invoke_xmethod for Python.  */
 
 struct value *
@@ -638,5 +740,10 @@ gdbpy_initialize_xmethods (void)
   if (py_get_arg_types_method_name == NULL)
     return -1;
 
+  py_get_result_type_method_name
+    = PyString_FromString (get_result_type_method_name);
+  if (py_get_result_type_method_name == NULL)
+    return -1;
+
   return 1;
 }
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 0581b33..6fea2cb 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -348,6 +348,11 @@ extern enum ext_lang_rc gdbpy_get_xmethod_arg_types
    struct xmethod_worker *worker,
    int *nargs,
    struct type ***arg_types);
+extern enum ext_lang_rc gdbpy_get_xmethod_result_type
+  (const struct extension_language_defn *extlang,
+   struct xmethod_worker *worker,
+   struct value *object, struct value **args, int nargs,
+   struct type **result_type);
 extern struct value *gdbpy_invoke_xmethod
   (const struct extension_language_defn *extlang,
    struct xmethod_worker *worker,
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 1da63fd..c8dbb11 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -192,6 +192,7 @@ const struct extension_language_ops python_extension_ops =
   gdbpy_free_xmethod_worker_data,
   gdbpy_get_matching_xmethod_workers,
   gdbpy_get_xmethod_arg_types,
+  gdbpy_get_xmethod_result_type,
   gdbpy_invoke_xmethod
 };
 
diff --git a/gdb/testsuite/gdb.python/py-xmethods.exp b/gdb/testsuite/gdb.python/py-xmethods.exp
index a83b14d..8b24fc1 100644
--- a/gdb/testsuite/gdb.python/py-xmethods.exp
+++ b/gdb/testsuite/gdb.python/py-xmethods.exp
@@ -107,8 +107,9 @@ gdb_test "p a_ptr->geta()" "From Python <A_geta>.*30" "After: a_ptr->geta()"
 gdb_test "p e.geta()" "From Python <A_geta>.*100" "After: e.geta()"
 gdb_test "p e_ptr->geta()" "From Python <A_geta>.*100" "After: e_ptr->geta()"
 gdb_test "p e_ref.geta()" "From Python <A_geta>.*100" "After: e_ref.geta()"
-gdb_test "p e.method(10)" "From Python <E_method_int>.*" "After: e.method(10)"
-gdb_test "p e.method('a')" "From Python <E_method_char>.*" \
+gdb_test "p e.method(10)" "From Python <E_method_int>.* = void" \
+  "After: e.method(10)"
+gdb_test "p e.method('a')" "From Python <E_method_char>.* = void" \
   "After: e.method('a')"
 gdb_test "p g.size_diff<float>  ()" "From Python G<>::size_diff.*" \
   "After: g.size_diff<float>()"
@@ -149,3 +150,10 @@ gdb_test_no_output "disable xmethod progspace E_methods;method_int" \
   "disable xmethod progspace E_methods;method_int"
 gdb_test "info xmethod progspace E_methods;method_int" ".* \\\[disabled\\\]" \
   "info xmethod xmethods E_methods;method_int"
+
+# PR 18285
+# First make sure both are enabled.
+gdb_test_no_output "enable xmethod progspace E_methods;method_char"
+gdb_test_no_output "enable xmethod progspace E_methods;method_int"
+gdb_test "pt e.method('a')" "type = void"
+gdb_test "pt e.method(10)" "Xmethod is missing return type."
diff --git a/gdb/testsuite/gdb.python/py-xmethods.py b/gdb/testsuite/gdb.python/py-xmethods.py
index 78935e1..3a2f100 100644
--- a/gdb/testsuite/gdb.python/py-xmethods.py
+++ b/gdb/testsuite/gdb.python/py-xmethods.py
@@ -60,6 +60,9 @@ class E_method_char_worker(XMethodWorker):
     def get_arg_types(self):
         return gdb.lookup_type('char')
 
+    def get_result_type(self, obj, arg):
+        return gdb.lookup_type('void')
+
     def __call__(self, obj, arg):
         print('From Python <E_method_char>')
         return None
@@ -72,6 +75,8 @@ class E_method_int_worker(XMethodWorker):
     def get_arg_types(self):
         return gdb.lookup_type('int')
 
+    # Note: get_result_type method elided on purpose
+
     def __call__(self, obj, arg):
         print('From Python <E_method_int>')
         return None
diff --git a/gdb/valarith.c b/gdb/valarith.c
index fb9ec60..3b175f9 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -482,6 +482,21 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	  argvec[1] = argvec[0];
 	  argvec++;
 	}
+      if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
+	{
+	  /* Static xmethods are not supported yet.  */
+	  gdb_assert (static_memfuncp == 0);
+	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	    {
+	      struct type *return_type
+		= result_type_of_xmethod (argvec[0], 2, argvec + 1);
+
+	      if (return_type == NULL)
+		error (_("Xmethod is missing return type."));
+	      return value_zero (return_type, not_lval);
+	    }
+	  return call_xmethod (argvec[0], 2, argvec + 1);
+	}
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
 	  struct type *return_type;
@@ -490,16 +505,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	    = TYPE_TARGET_TYPE (check_typedef (value_type (argvec[0])));
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
-
-      if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
-	{
-	  /* Static xmethods are not supported yet.  */
-	  gdb_assert (static_memfuncp == 0);
-	  return call_xmethod (argvec[0], 2, argvec + 1);
-	}
-      else
-	return call_function_by_hand (argvec[0], 2 - static_memfuncp,
-				      argvec + 1);
+      return call_function_by_hand (argvec[0], 2 - static_memfuncp,
+				    argvec + 1);
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
@@ -594,6 +601,21 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	  nargs --;
 	  argvec++;
 	}
+      if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
+	{
+	  /* Static xmethods are not supported yet.  */
+	  gdb_assert (static_memfuncp == 0);
+	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	    {
+	      struct type *return_type
+		= result_type_of_xmethod (argvec[0], 1, argvec + 1);
+
+	      if (return_type == NULL)
+		error (_("Xmethod is missing return type."));
+	      return value_zero (return_type, not_lval);
+	    }
+	  return call_xmethod (argvec[0], 1, argvec + 1);
+	}
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
 	  struct type *return_type;
@@ -602,14 +624,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	    = TYPE_TARGET_TYPE (check_typedef (value_type (argvec[0])));
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
-      if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
-	{
-	  /* Static xmethods are not supported yet.  */
-	  gdb_assert (static_memfuncp == 0);
-	  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);
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
diff --git a/gdb/value.c b/gdb/value.c
index cb56849..c36f748 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2600,6 +2600,18 @@ value_of_xmethod (struct xmethod_worker *worker)
   return worker->value;
 }
 
+/* Return the type of the result of TYPE_CODE_XMETHOD value METHOD.  */
+
+struct type *
+result_type_of_xmethod (struct value *method, int argc, struct value **argv)
+{
+  gdb_assert (TYPE_CODE (value_type (method)) == TYPE_CODE_XMETHOD
+	      && method->lval == lval_xcallable && argc > 0);
+
+  return get_xmethod_result_type (method->location.xm_worker,
+				  argv[0], argv + 1, argc - 1);
+}
+
 /* Call the xmethod corresponding to the TYPE_CODE_XMETHOD value METHOD.  */
 
 struct value *
diff --git a/gdb/value.h b/gdb/value.h
index 21baa32..25107a4 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1080,7 +1080,10 @@ char *value_internal_function_name (struct value *);
 
 extern struct value *value_of_xmethod (struct xmethod_worker *);
 
-struct value *call_xmethod (struct value *function,
-			    int argc, struct value **argv);
+extern struct type *result_type_of_xmethod (struct value *method,
+					    int argc, struct value **argv);
+
+extern struct value *call_xmethod (struct value *method,
+				   int argc, struct value **argv);
 
 #endif /* !defined (VALUE_H) */


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