Re: Convenience functions

Tom Tromey
2008-10-23T19:14:01Z

Daniel> Maybe I can convince one of you to submit that pair of patches...

I looked at this a little bit today.

There are two problems.

First, in_scope ought to take a string argument. However, converting
a string value to a char* is a pain. At least, I couldn't find any
predefined way to do this. This area could use a lot of work, I
think.

Second, Rob's in_scope patch uses lookup_minimal_symbol. This
function seems misnamed, in that to me "scope" implies scoping -- but
this is just checking for global symbols.

I did write some docs. And, you can see the rest in the appended.

I wouldn't recommend moving forward with this patch, unless somebody
wants to clean it up. I think a different convenience function would
be a better starting point. I probably won't work on this any more
for the time being.

Tom

2008-10-23 Tom Tromey <tromey@redhat.com>

* symtab.c (in_scope_internal_function): New function.
(_initialize_symtab): Register it.
* value.h (lookup_only_internalvar, create_internalvar,
lookup_internalvar): Update.
(internal_function_fn, add_internal_function,
call_internal_function, value_internal_function_name): Declare.
* value.c (struct internal_function): New struct.
(functionlist): New global.
(internal_fn_type): Likewise.
(lookup_only_internalvar): Made argument const.
(create_internalvar): Likewise.
(lookup_internalvar): Likewise.
(value_create_internal_function): New function.
(value_internal_function_name): Likewise.
(call_internal_function): Likewise.
(function_command): Likewise.
(add_internal_function): Likewise.
(_initialize_values): Add the "function" command. Initialize
internal_fn_type.
* valprint.c (value_check_printable): Handle
TYPE_CODE_INTERNAL_FUNCTION.
* gdbtypes.h (enum type_code) <TYPE_CODE_INTERNAL_FUNCTION>: New
constant.
* eval.c: (evaluate_subexp_standard) <OP_FUNCALL>: Handle
TYPE_CODE_INTERNAL_FUNCTION.

2008-10-23 Tom Tromey <tromey@redhat.com>

* gdb.texinfo (Convenience Vars): Document convenience functions.

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c003cd4..baa5993 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7390,6 +7390,29 @@ On HP-UX systems, if you refer to a function or variable name that
begins with a dollar sign, @value{GDBN} searches for a user or system
name first, before it searches for a convenience variable.

+@cindex convenience functions
+@value{GDBN} also supplies some @dfn{convenience functions}. These
+have a syntax similar to convenience variables. A convenience
+function can be used in an expression just like an ordinary function;
+however, a convenience function is implemented internally to
+@value{GDBN}.
+
+@table @code
+@item help function
+@kindex help function
+@cindex show all convenience functions
+Print a list of all convenience functions.
+@end table
+
+Currently there is a single defined convenience function:
+
+@table @code
+@kindex $in_scope
+@item $in_scope (@var{name})
+Evaluate to @samp{1} if the symbol @var{name} is defined in the
+program, or @samp{0} otherwise.
+@end table
+
@node Registers
@section Registers

diff --git a/gdb/eval.c b/gdb/eval.c
index cf3e876..2d4551d 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1515,6 +1515,9 @@ evaluate_subexp_standard (struct type *expect_type,
else
error (_("Expression of type other than \"Function returning ...\" used as function"));
}
+ if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_INTERNAL_FUNCTION)
+ return call_internal_function (argvec[0], nargs, argvec + 1);
+
return call_function_by_hand (argvec[0], nargs, argvec + 1);
/* pai: FIXME save value from call_function_by_hand, then adjust pc by adjust_fn_pc if +ve */

diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 2a41c5b..dc944b1 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -133,7 +133,10 @@ enum type_code

TYPE_CODE_NAMESPACE, /* C++ namespace. */

- TYPE_CODE_DECFLOAT /* Decimal floating point. */
+ TYPE_CODE_DECFLOAT, /* Decimal floating point. */
+
+ /* Internal function type. */
+ TYPE_CODE_INTERNAL_FUNCTION
};

/* For now allow source to use TYPE_CODE_CLASS for C++ classes, as an
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 3af3e8a..514bc97 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4578,6 +4578,36 @@ expand_line_sal (struct symtab_and_line sal)
}

+/* The implementation of the "in_scope" internal function. */
+static struct value *
+in_scope_internal_function (void *ignore, int argc, struct value **argv)
+{
+ const gdb_byte *contents;
+ struct type *type;
+ struct minimal_symbol *minsym;
+
+ if (argc != 1)
+ error (_("in_scope requires a single argument"));
+
+ type = check_typedef (value_type (argv[0]));
+ if (TYPE_CODE (type) == TYPE_CODE_STRING
+ || (TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_CHAR
+ || (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_INT
+ && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) == 1))))
+ {
+ /* Ok. */
+ }
+ else
+ error (_("in_scope argument must have string type"));
+
+ contents = value_contents (argv[0]);
+ /* FIXME: host/target charset confusion here. */
+ minsym = lookup_minimal_symbol ((char *) contents, NULL, NULL);
+ return value_from_longest (builtin_type (current_gdbarch)->builtin_int,
+ minsym != 0);
+}
+
void
_initialize_symtab (void)
{
@@ -4625,6 +4655,13 @@ Show how the debugger handles ambiguities in expressions."), _("\
Valid values are \"ask\", \"all\", \"cancel\", and the default is \"all\"."),
NULL, NULL, &setlist, &showlist);

+ add_internal_function ("in_scope", _("\
+Test whether a name is defined in the current scope.\n\
+This function takes a single argument, a string. It returns one\n\
+if a symbol of that name is available, or zero if no symbol of that\n\
+name appears."),
+ in_scope_internal_function, NULL);
+
/* Initialize the one built-in type that isn't language dependent... */
builtin_type_error = init_type (TYPE_CODE_ERROR, 0, 0,
"<unknown type>", (struct objfile *) NULL);
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 99c376f..617a3f4 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -259,6 +259,13 @@ value_check_printable (struct value *val, struct ui_file *stream)
return 0;
}

+ if (TYPE_CODE (value_type (val)) == TYPE_CODE_INTERNAL_FUNCTION)
+ {
+ fprintf_filtered (stream, _("<internal function %s>"),
+ value_internal_function_name (val));
+ return 0;
+ }
+
return 1;
}

diff --git a/gdb/value.c b/gdb/value.c
index 0b530f0..b5aa853 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -43,6 +43,24 @@

void _initialize_values (void);

+/* Definition of a user function. */
+struct internal_function
+{
+ /* The name of the function. It is a bit odd to have this in the
+ function itself -- the user might use a differently-named
+ convenience variable to hold the function. */
+ char *name;
+
+ /* The handler. */
+ internal_function_fn handler;
+
+ /* User data for the handler. */
+ void *cookie;
+};
+
+/* Command list holding internal functions. */
+static struct cmd_list_element *functionlist;
+
struct value
{
/* Type of value; either not an lval, or one of the various
@@ -205,6 +223,10 @@ struct value_history_chunk
static struct value_history_chunk *value_history_chain;

static int value_history_count; /* Abs number of last entry stored */
+
+/* The type of internal functions. */
+
+static struct type *internal_fn_type;
\f
/* List of all value objects currently allocated
(except for those released by calls to release_value)
@@ -774,7 +796,7 @@ init_if_undefined_command (char* args, int from_tty)
the return value is NULL. */

struct internalvar *
-lookup_only_internalvar (char *name)
+lookup_only_internalvar (const char *name)
{
struct internalvar *var;

@@ -790,7 +812,7 @@ lookup_only_internalvar (char *name)
NAME should not normally include a dollar sign. */

struct internalvar *
-create_internalvar (char *name)
+create_internalvar (const char *name)
{
struct internalvar *var;
var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
@@ -811,7 +833,7 @@ create_internalvar (char *name)
one is created, with a void value. */

struct internalvar *
-lookup_internalvar (char *name)
+lookup_internalvar (const char *name)
{
struct internalvar *var;

@@ -915,6 +937,81 @@ internalvar_name (struct internalvar *var)
return var->name;
}

+/* Create a value which represents an internal function.
+ NAME is the name of the function.
+ HANDLER is a pointer to a function which is called when this
+ internal function is invoked during expression evaluation.
+ COOKIE is a pointer which is passed verbatim to HANDLER. */
+static struct value *
+value_create_internal_function (const char *name,
+ internal_function_fn handler,
+ void *cookie)
+{
+ struct value *result = allocate_value (internal_fn_type);
+ gdb_byte *addr = value_contents_writeable (result);
+ struct internal_function **fnp = (struct internal_function **) addr;
+ /* The internal_function object is leaked here -- to make it truly
+ deletable, we would have to reference count it and add special
+ code to value_free and value_copy. */
+ struct internal_function *ifn = XNEW (struct internal_function);
+ ifn->name = xstrdup (name);
+ ifn->handler = handler;
+ ifn->cookie = cookie;
+ *fnp = ifn;
+ return result;
+}
+
+/* Return the name of the value VAL, which is assumed to be of
+ "internal function" type. The result is not copied and should not
+ be freed or modified. */
+char *
+value_internal_function_name (struct value *val)
+{
+ gdb_byte *addr = value_contents_writeable (val);
+ struct internal_function *ifn = * (struct internal_function **) addr;
+ return ifn->name;
+}
+
+/* Call an internal function. FUNC is a value which is assumed to be
+ of "internal function" type. ARGC is the number of arguments to
+ pass to it, and ARGV is a vector holding the arguments. */
+struct value *
+call_internal_function (struct value *func, int argc, struct value **argv)
+{
+ gdb_byte *addr = value_contents_writeable (func);
+ struct internal_function *ifn = * (struct internal_function **) addr;
+ return (*ifn->handler) (ifn->cookie, argc, argv);
+}
+
+/* The 'function' command. This does nothing -- it is just a
+ placeholder to let "help function NAME" work. This is also used as
+ the implementation of the sub-command that is created when
+ registering an internal function. */
+static void
+function_command (char *command, int from_tty)
+{
+ /* Do nothing. */
+}
+
+/* Add a new internal function. NAME is the name of the function; DOC
+ is a documentation string describing the function. HANDLER is
+ called when the function is invoked. COOKIE is an arbitrary
+ pointer which is passed to HANDLER and is intended for "user
+ data". */
+void
+add_internal_function (const char *name, const char *doc,
+ internal_function_fn handler,
+ void *cookie)
+{
+ struct internalvar *var = lookup_internalvar (name);
+ struct value *fnval = value_create_internal_function (name, handler, cookie);
+ release_value (fnval);
+ set_internalvar (var, fnval);
+
+ add_cmd (xstrdup (name), no_class, function_command, (char *) doc,
+ &functionlist);
+}
+
/* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to
prevent cycles / duplicates. */

@@ -1795,4 +1892,13 @@ init-if-undefined VARIABLE = EXPRESSION\n\
Set an internal VARIABLE to the result of the EXPRESSION if it does not\n\
exist or does not contain a value. The EXPRESSION is not evaluated if the\n\
VARIABLE is already initialized."));
+
+ add_prefix_cmd ("function", no_class, function_command, _("\
+Placeholder command for showing help on convenience functions."),
+ &functionlist, "function ", 0, &cmdlist);
+
+ internal_fn_type = alloc_type (NULL);
+ TYPE_CODE (internal_fn_type) = TYPE_CODE_INTERNAL_FUNCTION;
+ TYPE_LENGTH (internal_fn_type) = sizeof (struct internal_function *);
+ TYPE_NAME (internal_fn_type) = "<internal function>";
}
diff --git a/gdb/value.h b/gdb/value.h
index f53d333..e1ea5dc 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -465,11 +465,11 @@ extern void set_internalvar_component (struct internalvar *var,
int bitpos, int bitsize,
struct value *newvalue);

-extern struct internalvar *lookup_only_internalvar (char *name);
+extern struct internalvar *lookup_only_internalvar (const char *name);

-extern struct internalvar *create_internalvar (char *name);
+extern struct internalvar *create_internalvar (const char *name);

-extern struct internalvar *lookup_internalvar (char *name);
+extern struct internalvar *lookup_internalvar (const char *name);

extern int value_equal (struct value *arg1, struct value *arg2);

@@ -585,4 +585,19 @@ extern struct value *value_allocate_space_in_inferior (int);
extern struct value *value_of_local (const char *name, int complain);

extern struct value * value_subscripted_rvalue (struct value *array, struct value *idx, int lowerbound);
+
+/* User function handler. */
+
+typedef struct value *(*internal_function_fn) (void *cookie,
+ int argc,
+ struct value **argv);
+
+void add_internal_function (const char *name, const char *doc,
+ internal_function_fn handler, void *cookie);
+
+struct value *call_internal_function (struct value *function,
+ int argc, struct value **argv);
+
+char *value_internal_function_name (struct value *);
+
#endif /* !defined (VALUE_H) */