2009-03-05  Tom Tromey  <tromey@redhat.com>

	Add support for convenience functions in Python.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-function.o.
	(SUBDIR_PYTHON_SRCS): Add python-function.c.
	(python-function.o): New target.
	* eval.c: Include "python/python.h" and <ctype.h>.
	(evaluate_subexp_standard): Handle values of type
	TYPE_CODE_INTERNAL_FUNCTION.
	* gdbtypes.h (type_code): Add TYPE_CODE_INTERNAL_FUNCTION.
	* parse.c (write_exp_string): Remove duplicate word in comment.
	* python/python-function.c: New file.
	* python/python-internal.h (gdbpy_initialize_functions): Add
	prototype.
	* python/python.c (_initialize_python): Call
	gdbpy_initialize_functions.
	* valprint.c (value_check_printable): Handle values of type
	TYPE_CODE_INTERNAL_FUNCTION.
	* value.c: Include "cli/cli-decode.h".
	(internal_function): New struct.
	(functionlist, internal_fn_type): New static variables.
	(lookup_only_internalvar,
	lookup_internalvar): Add const qualifier to name argument.
	(create_internalvar): Likewise.  Initialize new field.
	(set_internal_var): Fix typo in comment.  Don't allow assignment
	to canonical variable.
	(value_create_internal_function, value_internal_function_name,
	call_internal_function, function_command, function_destroyer,
	add_internal_function): New functions.
	(_initialize_values): Create `function' placeholder command.
	Initialize internal_fn_type.
	* value.h (lookup_only_internalvar, create_internalvar,
	lookup_internalvar): Add const qualifier to name argument.
	(internal_function_fn, add_internal_function, call_internal_function,
	value_internal_function_name): Add prototypes.
	(struct internalvar) <canonical>: New field.

gdb/doc/
2008-03-05  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Convenience Vars): Document convenience functions.
	(Functions In Python): New node.
	(Python API): Update.

gdb/testsuite/
2009-03-05  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/python-function.exp: New file.
This commit is contained in:
Thiago Jung Bauermann 2009-03-21 03:03:56 +00:00
parent c6dd29cec2
commit bc3b79fd1a
15 changed files with 523 additions and 9 deletions

View file

@ -1,3 +1,40 @@
2009-03-20 Tom Tromey <tromey@redhat.com>
Add support for convenience functions in Python.
* Makefile.in (SUBDIR_PYTHON_OBS): Add python-function.o.
(SUBDIR_PYTHON_SRCS): Add python-function.c.
(python-function.o): New target.
* eval.c: Include "python/python.h" and <ctype.h>.
(evaluate_subexp_standard): Handle values of type
TYPE_CODE_INTERNAL_FUNCTION.
* gdbtypes.h (type_code): Add TYPE_CODE_INTERNAL_FUNCTION.
* parse.c (write_exp_string): Remove duplicate word in comment.
* python/python-function.c: New file.
* python/python-internal.h (gdbpy_initialize_functions): Add
prototype.
* python/python.c (_initialize_python): Call
gdbpy_initialize_functions.
* valprint.c (value_check_printable): Handle values of type
TYPE_CODE_INTERNAL_FUNCTION.
* value.c: Include "cli/cli-decode.h".
(internal_function): New struct.
(functionlist, internal_fn_type): New static variables.
(lookup_only_internalvar,
lookup_internalvar): Add const qualifier to name argument.
(create_internalvar): Likewise. Initialize new field.
(set_internal_var): Fix typo in comment. Don't allow assignment
to canonical variable.
(value_create_internal_function, value_internal_function_name,
call_internal_function, function_command, function_destroyer,
add_internal_function): New functions.
(_initialize_values): Create `function' placeholder command.
Initialize internal_fn_type.
* value.h (lookup_only_internalvar, create_internalvar,
lookup_internalvar): Add const qualifier to name argument.
(internal_function_fn, add_internal_function, call_internal_function,
value_internal_function_name): Add prototypes.
(struct internalvar) <canonical>: New field.
2009-03-20 Tom Tromey <tromey@redhat.com>
* c-lang.c (evaluate_subexp_c): Call check_typedef.

View file

@ -271,11 +271,13 @@ SUBDIR_TUI_CFLAGS= \
SUBDIR_PYTHON_OBS = \
python.o \
python-cmd.o \
python-function.o \
python-utils.o \
python-value.o
SUBDIR_PYTHON_SRCS = \
python/python.c \
python/python-cmd.c \
python/python-function.c \
python/python-utils.c \
python/python-value.c
SUBDIR_PYTHON_DEPS =
@ -1850,6 +1852,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
$(POSTCOMPILE)
python-function.o: $(srcdir)/python/python-function.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
$(POSTCOMPILE)
python-utils.o: $(srcdir)/python/python-utils.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c
$(POSTCOMPILE)

View file

@ -1,3 +1,9 @@
2008-03-20 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Convenience Vars): Document convenience functions.
(Functions In Python): New node.
(Python API): Update.
2009-03-20 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Character Sets): Remove obsolete text. Document

View file

@ -7436,6 +7436,20 @@ 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
@node Registers
@section Registers
@ -18178,6 +18192,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* Exception Handling::
* Values From Inferior::
* Commands In Python:: Implementing new commands in Python.
* Functions In Python:: Writing new convenience functions.
@end menu
@node Basic Python
@ -18612,6 +18627,65 @@ registration of the command with @value{GDBN}. Depending on how the
Python code is read into @value{GDBN}, you may need to import the
@code{gdb} module explicitly.
@node Functions In Python
@subsubsection Writing new convenience functions
@cindex writing convenience functions
@cindex convenience functions in python
@cindex python convenience functions
@tindex gdb.Function
@tindex Function
You can implement new convenience functions (@pxref{Convenience Vars})
in Python. A convenience function is an instance of a subclass of the
class @code{gdb.Function}.
@defmethod Function __init__ name
The initializer for @code{Function} registers the new function with
@value{GDBN}. The argument @var{name} is the name of the function,
a string. The function will be visible to the user as a convenience
variable of type @code{internal function}, whose name is the same as
the given @var{name}.
The documentation for the new function is taken from the documentation
string for the new class.
@end defmethod
@defmethod Function invoke @var{*args}
When a convenience function is evaluated, its arguments are converted
to instances of @code{gdb.Value}, and then the function's
@code{invoke} method is called. Note that @value{GDBN} does not
predetermine the arity of convenience functions. Instead, all
available arguments are passed to @code{invoke}, following the
standard Python calling convention. In particular, a convenience
function can have default values for parameters without ill effect.
The return value of this method is used as its value in the enclosing
expression. If an ordinary Python value is returned, it is converted
to a @code{gdb.Value} following the usual rules.
@end defmethod
The following code snippet shows how a trivial convenience function can
be implemented in Python:
@smallexample
class Greet (gdb.Function):
"""Return string to greet someone.
Takes a name as argument."""
def __init__ (self):
super (Greet, self).__init__ ("greet")
def invoke (self, name):
return "Hello, %s!" % name.string ()
Greet ()
@end smallexample
The last line instantiates the class, and is necessary to trigger the
registration of the function with @value{GDBN}. Depending on how the
Python code is read into @value{GDBN}, you may need to import the
@code{gdb} module explicitly.
@node Interpreters
@chapter Command Interpreters
@cindex command interpreters

View file

@ -40,9 +40,12 @@
#include "regcache.h"
#include "user-regs.h"
#include "valprint.h"
#include "python/python.h"
#include "gdb_assert.h"
#include <ctype.h>
/* This is defined in valops.c */
extern int overload_resolution;
@ -1512,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 */

View file

@ -134,7 +134,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

View file

@ -306,7 +306,7 @@ write_exp_elt_intern (struct internalvar *expelt)
strings with embedded null bytes, as is required for some languages.
Don't be fooled by the fact that the string is null byte terminated,
this is strictly for the convenience of debugging gdb itself. Gdb
this is strictly for the convenience of debugging gdb itself.
Gdb does not depend up the string being null terminated, since the
actual length is recorded in expression elements at each end of the
string. The null byte is taken into consideration when computing how

View file

@ -0,0 +1,180 @@
/* Convenience functions implemented in Python.
Copyright (C) 2008, 2009 Free Software Foundation, Inc.
This file is part of GDB.
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/>. */
#include "defs.h"
#include "value.h"
#include "exceptions.h"
#include "python-internal.h"
#include "charset.h"
#include "gdbcmd.h"
#include "cli/cli-decode.h"
#include "completer.h"
#include "expression.h"
static PyTypeObject fnpy_object_type;
static PyObject *
convert_values_to_python (int argc, struct value **argv)
{
int i;
PyObject *result = PyTuple_New (argc);
for (i = 0; i < argc; ++i)
{
PyObject *elt = value_to_value_object (argv[i]);
if (! elt)
{
Py_DECREF (result);
error (_("Could not convert value to Python object."));
}
PyTuple_SetItem (result, i, elt);
}
return result;
}
/* Call a Python function object's invoke method. */
static struct value *
fnpy_call (void *cookie, int argc, struct value **argv)
{
int i;
struct value *value = NULL;
PyObject *result, *callable, *args;
struct cleanup *cleanup;
PyGILState_STATE state;
state = PyGILState_Ensure ();
cleanup = make_cleanup_py_restore_gil (&state);
args = convert_values_to_python (argc, argv);
callable = PyObject_GetAttrString ((PyObject *) cookie, "invoke");
if (! callable)
{
Py_DECREF (args);
error (_("No method named 'invoke' in object."));
}
result = PyObject_Call (callable, args, NULL);
Py_DECREF (callable);
Py_DECREF (args);
if (!result)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
value = convert_value_from_python (result);
if (value == NULL)
{
Py_DECREF (result);
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
Py_DECREF (result);
do_cleanups (cleanup);
return value;
}
/* Initializer for a Function object. It takes one argument, the name
of the function. */
static int
fnpy_init (PyObject *self, PyObject *args, PyObject *kwds)
{
char *name, *docstring = NULL;
if (! PyArg_ParseTuple (args, "s", &name))
return -1;
Py_INCREF (self);
if (PyObject_HasAttrString (self, "__doc__"))
{
PyObject *ds_obj = PyObject_GetAttrString (self, "__doc__");
if (ds_obj && gdbpy_is_string (ds_obj))
/* Nothing ever frees this. */
docstring = python_string_to_host_string (ds_obj);
}
if (! docstring)
docstring = _("This function is not documented.");
add_internal_function (name, docstring, fnpy_call, self);
return 0;
}
/* Initialize internal function support. */
void
gdbpy_initialize_functions (void)
{
if (PyType_Ready (&fnpy_object_type) < 0)
return;
Py_INCREF (&fnpy_object_type);
PyModule_AddObject (gdb_module, "Function", (PyObject *) &fnpy_object_type);
}
static PyTypeObject fnpy_object_type =
{
PyObject_HEAD_INIT (NULL)
0, /*ob_size*/
"gdb.Function", /*tp_name*/
sizeof (PyObject), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"GDB function object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
fnpy_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};

View file

@ -71,6 +71,7 @@ struct value *convert_value_from_python (PyObject *obj);
void gdbpy_initialize_values (void);
void gdbpy_initialize_commands (void);
void gdbpy_initialize_functions (void);
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state);

View file

@ -413,6 +413,7 @@ Enables or disables printing of Python stack traces."),
gdbpy_initialize_values ();
gdbpy_initialize_commands ();
gdbpy_initialize_functions ();
PyRun_SimpleString ("import gdb");

View file

@ -1,3 +1,7 @@
2009-03-20 Thiago Jung Bauermann <bauerman@br.ibm.com>
* gdb.python/python-function.exp: New file.
2009-03-20 Tom Tromey <tromey@redhat.com>
* gdb.base/store.exp: Update for change to escape output.

View file

@ -0,0 +1,65 @@
# Copyright (C) 2009 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. It tests the mechanism
# exposing convenience functions to Python.
if $tracelevel then {
strace $tracelevel
}
# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}...
# Run a test named NAME, consisting of multiple lines of input.
# After each input line INPUT, search for result line RESULT.
# Succeed if all results are seen; fail otherwise.
proc gdb_py_test_multiple {name args} {
global gdb_prompt
foreach {input result} $args {
if {[gdb_test_multiple $input "$name - $input" {
-re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" {
pass "$name - $input"
}
}]} {
return 1
}
}
return 0
}
# Start with a fresh gdb.
gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_test_multiple "python print 'hello, world!'" "verify python support" {
-re "not supported.*$gdb_prompt $" {
unsupported "python support is disabled"
return -1
}
-re "$gdb_prompt $" {}
}
gdb_py_test_multiple "input convenience function" \
"python" "" \
"class test_func (gdb.Function):" "" \
" def __init__ (self):" "" \
" super (test_func, self).__init__ (\"test_func\")" "" \
" def invoke (self, arg):" "" \
" return \"test_func output, arg = %s\" % arg.string ()" "" \
"test_func ()" "" \
"end" ""
gdb_test "print \$test_func (\"ugh\")" "= \"test_func output, arg = ugh\"" "call function"

View file

@ -287,6 +287,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;
}

View file

@ -37,6 +37,7 @@
#include "dfp.h"
#include "objfiles.h"
#include "valprint.h"
#include "cli/cli-decode.h"
#include "python/python.h"
@ -44,6 +45,23 @@
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;
};
static struct cmd_list_element *functionlist;
struct value
{
/* Type of value; either not an lval, or one of the various
@ -203,6 +221,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;
/* List of all value objects currently allocated
(except for those released by calls to release_value)
@ -878,7 +900,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;
@ -894,7 +916,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));
@ -902,6 +924,7 @@ create_internalvar (char *name)
var->value = allocate_value (builtin_type_void);
var->endian = gdbarch_byte_order (current_gdbarch);
var->make_value = NULL;
var->canonical = 0;
release_value (var->value);
var->next = internalvars;
internalvars = var;
@ -934,7 +957,7 @@ create_internalvar_type_lazy (char *name, internalvar_make_value fun)
one is created, with a void value. */
struct internalvar *
lookup_internalvar (char *name)
lookup_internalvar (const char *name)
{
struct internalvar *var;
@ -1031,6 +1054,9 @@ set_internalvar (struct internalvar *var, struct value *val)
{
struct value *newval;
if (var->canonical)
error (_("Cannot overwrite convenience function %s"), var->name);
newval = value_copy (val);
newval->modifiable = 1;
@ -1042,7 +1068,7 @@ set_internalvar (struct internalvar *var, struct value *val)
/* Begin code which must not call error(). If var->value points to
something free'd, an error() obviously leaves a dangling pointer.
But we also get a danling pointer if var->value points to
But we also get a dangling pointer if var->value points to
something in the value chain (i.e., before release_value is
called), because after the error free_all_values will get called before
long. */
@ -1059,6 +1085,76 @@ internalvar_name (struct internalvar *var)
return var->name;
}
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;
struct internal_function *ifn = XNEW (struct internal_function);
ifn->name = xstrdup (name);
ifn->handler = handler;
ifn->cookie = cookie;
*fnp = ifn;
return result;
}
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;
}
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. */
}
/* Clean up if an internal function's command is destroyed. */
static void
function_destroyer (struct cmd_list_element *self, void *ignore)
{
xfree (self->name);
xfree (self->doc);
}
/* 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 cmd_list_element *cmd;
struct internalvar *var = lookup_internalvar (name);
struct value *fnval = value_create_internal_function (name, handler, cookie);
set_internalvar (var, fnval);
var->canonical = 1;
cmd = add_cmd (xstrdup (name), no_class, function_command, (char *) doc,
&functionlist);
cmd->destroyer = function_destroyer;
}
/* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to
prevent cycles / duplicates. */
@ -1944,4 +2040,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>";
}

View file

@ -314,6 +314,9 @@ struct internalvar
struct value *value;
internalvar_make_value make_value;
int endian;
/* True if this internalvar is the canonical name for a convenience
function. */
int canonical;
};
@ -535,14 +538,14 @@ 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 *
create_internalvar_type_lazy (char *name, internalvar_make_value fun);
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);
@ -661,4 +664,20 @@ 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) */