492d29ea1c
This patch splits the TRY_CATCH macro into three, so that we go from this: ~~~ volatile gdb_exception ex; TRY_CATCH (ex, RETURN_MASK_ERROR) { } if (ex.reason < 0) { } ~~~ to this: ~~~ TRY { } CATCH (ex, RETURN_MASK_ERROR) { } END_CATCH ~~~ Thus, we'll be getting rid of the local volatile exception object, and declaring the caught exception in the catch block. This allows reimplementing TRY/CATCH in terms of C++ exceptions when building in C++ mode, while still allowing to build GDB in C mode (using setjmp/longjmp), as a transition step. TBC, after this patch, is it _not_ valid to have code between the TRY and the CATCH blocks, like: TRY { } // some code here. CATCH (ex, RETURN_MASK_ERROR) { } END_CATCH Just like it isn't valid to do that with C++'s native try/catch. By switching to creating the exception object inside the CATCH block scope, we can get rid of all the explicitly allocated volatile exception objects all over the tree, and map the CATCH block more directly to C++'s catch blocks. The majority of the TRY_CATCH -> TRY+CATCH+END_CATCH conversion was done with a script, rerun from scratch at every rebase, no manual editing involved. After the mechanical conversion, a few places needed manual intervention, to fix preexisting cases where we were using the exception object outside of the TRY_CATCH block, and cases where we were using "else" after a 'if (ex.reason) < 0)' [a CATCH after this patch]. The result was folded into this patch so that GDB still builds at each incremental step. END_CATCH is necessary for two reasons: First, because we name the exception object in the CATCH block, which requires creating a scope, which in turn must be closed somewhere. Declaring the exception variable in the initializer field of a for block, like: #define CATCH(EXCEPTION, mask) \ for (struct gdb_exception EXCEPTION; \ exceptions_state_mc_catch (&EXCEPTION, MASK); \ EXCEPTION = exception_none) would avoid needing END_CATCH, but alas, in C mode, we build with C90, which doesn't allow mixed declarations and code. Second, because when TRY/CATCH are wired to real C++ try/catch, as long as we need to handle cleanup chains, even if there's no CATCH block that wants to catch the exception, we need for stop at every frame in the unwind chain and run cleanups, then rethrow. That will be done in END_CATCH. After we require C++, we'll still need TRY/CATCH/END_CATCH until cleanups are completely phased out -- TRY/CATCH in C++ mode will save/restore the current cleanup chain, like in C mode, and END_CATCH catches otherwise uncaugh exceptions, runs cleanups and rethrows, so that C++ cleanups and exceptions can coexist. IMO, this still makes the TRY/CATCH code look a bit more like a newcomer would expect, so IMO worth it even if we weren't considering C++. gdb/ChangeLog. 2015-03-07 Pedro Alves <palves@redhat.com> * common/common-exceptions.c (struct catcher) <exception>: No longer a pointer to volatile exception. Now an exception value. <mask>: Delete field. (exceptions_state_mc_init): Remove all parameters. Adjust. (exceptions_state_mc): No longer pop the catcher here. (exceptions_state_mc_catch): New function. (throw_exception): Adjust. * common/common-exceptions.h (exceptions_state_mc_init): Remove all parameters. (exceptions_state_mc_catch): Declare. (TRY_CATCH): Rename to ... (TRY): ... this. Remove EXCEPTION and MASK parameters. (CATCH, END_CATCH): New. All callers adjusted. gdb/gdbserver/ChangeLog: 2015-03-07 Pedro Alves <palves@redhat.com> Adjust all callers of TRY_CATCH to use TRY/CATCH/END_CATCH instead.
1133 lines
31 KiB
C
1133 lines
31 KiB
C
/* GDB/Scheme pretty-printing.
|
||
|
||
Copyright (C) 2008-2015 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/>. */
|
||
|
||
/* See README file in this directory for implementation notes, coding
|
||
conventions, et.al. */
|
||
|
||
#include "defs.h"
|
||
#include "charset.h"
|
||
#include "symtab.h" /* Needed by language.h. */
|
||
#include "language.h"
|
||
#include "objfiles.h"
|
||
#include "value.h"
|
||
#include "valprint.h"
|
||
#include "guile-internal.h"
|
||
|
||
/* Return type of print_string_repr. */
|
||
|
||
enum string_repr_result
|
||
{
|
||
/* The string method returned None. */
|
||
STRING_REPR_NONE,
|
||
/* The string method had an error. */
|
||
STRING_REPR_ERROR,
|
||
/* Everything ok. */
|
||
STRING_REPR_OK
|
||
};
|
||
|
||
/* Display hints. */
|
||
|
||
enum display_hint
|
||
{
|
||
/* No display hint. */
|
||
HINT_NONE,
|
||
/* The display hint has a bad value. */
|
||
HINT_ERROR,
|
||
/* Print as an array. */
|
||
HINT_ARRAY,
|
||
/* Print as a map. */
|
||
HINT_MAP,
|
||
/* Print as a string. */
|
||
HINT_STRING
|
||
};
|
||
|
||
/* The <gdb:pretty-printer> smob. */
|
||
|
||
typedef struct
|
||
{
|
||
/* This must appear first. */
|
||
gdb_smob base;
|
||
|
||
/* A string representing the name of the printer. */
|
||
SCM name;
|
||
|
||
/* A boolean indicating whether the printer is enabled. */
|
||
SCM enabled;
|
||
|
||
/* A procedure called to look up the printer for the given value.
|
||
The procedure is called as (lookup gdb:pretty-printer value).
|
||
The result should either be a gdb:pretty-printer object that will print
|
||
the value, or #f if the value is not recognized. */
|
||
SCM lookup;
|
||
|
||
/* Note: Attaching subprinters to this smob is left to Scheme. */
|
||
} pretty_printer_smob;
|
||
|
||
/* The <gdb:pretty-printer-worker> smob. */
|
||
|
||
typedef struct
|
||
{
|
||
/* This must appear first. */
|
||
gdb_smob base;
|
||
|
||
/* Either #f or one of the supported display hints: map, array, string.
|
||
If neither of those then the display hint is ignored (treated as #f). */
|
||
SCM display_hint;
|
||
|
||
/* A procedure called to pretty-print the value.
|
||
(lambda (printer) ...) -> string | <gdb:lazy-string> | <gdb:value> */
|
||
SCM to_string;
|
||
|
||
/* A procedure called to print children of the value.
|
||
(lambda (printer) ...) -> <gdb:iterator>
|
||
The iterator returns a pair for each iteration: (name . value),
|
||
where "value" can have the same types as to_string. */
|
||
SCM children;
|
||
} pretty_printer_worker_smob;
|
||
|
||
static const char pretty_printer_smob_name[] =
|
||
"gdb:pretty-printer";
|
||
static const char pretty_printer_worker_smob_name[] =
|
||
"gdb:pretty-printer-worker";
|
||
|
||
/* The tag Guile knows the pretty-printer smobs by. */
|
||
static scm_t_bits pretty_printer_smob_tag;
|
||
static scm_t_bits pretty_printer_worker_smob_tag;
|
||
|
||
/* The global pretty-printer list. */
|
||
static SCM pretty_printer_list;
|
||
|
||
/* gdb:pp-type-error. */
|
||
static SCM pp_type_error_symbol;
|
||
|
||
/* Pretty-printer display hints are specified by strings. */
|
||
static SCM ppscm_map_string;
|
||
static SCM ppscm_array_string;
|
||
static SCM ppscm_string_string;
|
||
|
||
/* Administrivia for pretty-printer matcher smobs. */
|
||
|
||
/* The smob "print" function for <gdb:pretty-printer>. */
|
||
|
||
static int
|
||
ppscm_print_pretty_printer_smob (SCM self, SCM port, scm_print_state *pstate)
|
||
{
|
||
pretty_printer_smob *pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (self);
|
||
|
||
gdbscm_printf (port, "#<%s ", pretty_printer_smob_name);
|
||
scm_write (pp_smob->name, port);
|
||
scm_puts (gdbscm_is_true (pp_smob->enabled) ? " enabled" : " disabled",
|
||
port);
|
||
scm_puts (">", port);
|
||
|
||
scm_remember_upto_here_1 (self);
|
||
|
||
/* Non-zero means success. */
|
||
return 1;
|
||
}
|
||
|
||
/* (make-pretty-printer string procedure) -> <gdb:pretty-printer> */
|
||
|
||
static SCM
|
||
gdbscm_make_pretty_printer (SCM name, SCM lookup)
|
||
{
|
||
pretty_printer_smob *pp_smob = (pretty_printer_smob *)
|
||
scm_gc_malloc (sizeof (pretty_printer_smob),
|
||
pretty_printer_smob_name);
|
||
SCM smob;
|
||
|
||
SCM_ASSERT_TYPE (scm_is_string (name), name, SCM_ARG1, FUNC_NAME,
|
||
_("string"));
|
||
SCM_ASSERT_TYPE (gdbscm_is_procedure (lookup), lookup, SCM_ARG2, FUNC_NAME,
|
||
_("procedure"));
|
||
|
||
pp_smob->name = name;
|
||
pp_smob->lookup = lookup;
|
||
pp_smob->enabled = SCM_BOOL_T;
|
||
smob = scm_new_smob (pretty_printer_smob_tag, (scm_t_bits) pp_smob);
|
||
gdbscm_init_gsmob (&pp_smob->base);
|
||
|
||
return smob;
|
||
}
|
||
|
||
/* Return non-zero if SCM is a <gdb:pretty-printer> object. */
|
||
|
||
static int
|
||
ppscm_is_pretty_printer (SCM scm)
|
||
{
|
||
return SCM_SMOB_PREDICATE (pretty_printer_smob_tag, scm);
|
||
}
|
||
|
||
/* (pretty-printer? object) -> boolean */
|
||
|
||
static SCM
|
||
gdbscm_pretty_printer_p (SCM scm)
|
||
{
|
||
return scm_from_bool (ppscm_is_pretty_printer (scm));
|
||
}
|
||
|
||
/* Returns the <gdb:pretty-printer> object in SELF.
|
||
Throws an exception if SELF is not a <gdb:pretty-printer> object. */
|
||
|
||
static SCM
|
||
ppscm_get_pretty_printer_arg_unsafe (SCM self, int arg_pos,
|
||
const char *func_name)
|
||
{
|
||
SCM_ASSERT_TYPE (ppscm_is_pretty_printer (self), self, arg_pos, func_name,
|
||
pretty_printer_smob_name);
|
||
|
||
return self;
|
||
}
|
||
|
||
/* Returns a pointer to the pretty-printer smob of SELF.
|
||
Throws an exception if SELF is not a <gdb:pretty-printer> object. */
|
||
|
||
static pretty_printer_smob *
|
||
ppscm_get_pretty_printer_smob_arg_unsafe (SCM self, int arg_pos,
|
||
const char *func_name)
|
||
{
|
||
SCM pp_scm = ppscm_get_pretty_printer_arg_unsafe (self, arg_pos, func_name);
|
||
pretty_printer_smob *pp_smob
|
||
= (pretty_printer_smob *) SCM_SMOB_DATA (pp_scm);
|
||
|
||
return pp_smob;
|
||
}
|
||
|
||
/* Pretty-printer methods. */
|
||
|
||
/* (pretty-printer-enabled? <gdb:pretty-printer>) -> boolean */
|
||
|
||
static SCM
|
||
gdbscm_pretty_printer_enabled_p (SCM self)
|
||
{
|
||
pretty_printer_smob *pp_smob
|
||
= ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
|
||
|
||
return pp_smob->enabled;
|
||
}
|
||
|
||
/* (set-pretty-printer-enabled! <gdb:pretty-printer> boolean)
|
||
-> unspecified */
|
||
|
||
static SCM
|
||
gdbscm_set_pretty_printer_enabled_x (SCM self, SCM enabled)
|
||
{
|
||
pretty_printer_smob *pp_smob
|
||
= ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
|
||
|
||
pp_smob->enabled = scm_from_bool (gdbscm_is_true (enabled));
|
||
|
||
return SCM_UNSPECIFIED;
|
||
}
|
||
|
||
/* (pretty-printers) -> list
|
||
Returns the list of global pretty-printers. */
|
||
|
||
static SCM
|
||
gdbscm_pretty_printers (void)
|
||
{
|
||
return pretty_printer_list;
|
||
}
|
||
|
||
/* (set-pretty-printers! list) -> unspecified
|
||
Set the global pretty-printers list. */
|
||
|
||
static SCM
|
||
gdbscm_set_pretty_printers_x (SCM printers)
|
||
{
|
||
SCM_ASSERT_TYPE (gdbscm_is_true (scm_list_p (printers)), printers,
|
||
SCM_ARG1, FUNC_NAME, _("list"));
|
||
|
||
pretty_printer_list = printers;
|
||
|
||
return SCM_UNSPECIFIED;
|
||
}
|
||
|
||
/* Administrivia for pretty-printer-worker smobs.
|
||
These are created when a matcher recognizes a value. */
|
||
|
||
/* The smob "print" function for <gdb:pretty-printer-worker>. */
|
||
|
||
static int
|
||
ppscm_print_pretty_printer_worker_smob (SCM self, SCM port,
|
||
scm_print_state *pstate)
|
||
{
|
||
pretty_printer_worker_smob *w_smob
|
||
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (self);
|
||
|
||
gdbscm_printf (port, "#<%s ", pretty_printer_worker_smob_name);
|
||
scm_write (w_smob->display_hint, port);
|
||
scm_puts (" ", port);
|
||
scm_write (w_smob->to_string, port);
|
||
scm_puts (" ", port);
|
||
scm_write (w_smob->children, port);
|
||
scm_puts (">", port);
|
||
|
||
scm_remember_upto_here_1 (self);
|
||
|
||
/* Non-zero means success. */
|
||
return 1;
|
||
}
|
||
|
||
/* (make-pretty-printer-worker string procedure procedure)
|
||
-> <gdb:pretty-printer-worker> */
|
||
|
||
static SCM
|
||
gdbscm_make_pretty_printer_worker (SCM display_hint, SCM to_string,
|
||
SCM children)
|
||
{
|
||
pretty_printer_worker_smob *w_smob = (pretty_printer_worker_smob *)
|
||
scm_gc_malloc (sizeof (pretty_printer_worker_smob),
|
||
pretty_printer_worker_smob_name);
|
||
SCM w_scm;
|
||
|
||
w_smob->display_hint = display_hint;
|
||
w_smob->to_string = to_string;
|
||
w_smob->children = children;
|
||
w_scm = scm_new_smob (pretty_printer_worker_smob_tag, (scm_t_bits) w_smob);
|
||
gdbscm_init_gsmob (&w_smob->base);
|
||
return w_scm;
|
||
}
|
||
|
||
/* Return non-zero if SCM is a <gdb:pretty-printer-worker> object. */
|
||
|
||
static int
|
||
ppscm_is_pretty_printer_worker (SCM scm)
|
||
{
|
||
return SCM_SMOB_PREDICATE (pretty_printer_worker_smob_tag, scm);
|
||
}
|
||
|
||
/* (pretty-printer-worker? object) -> boolean */
|
||
|
||
static SCM
|
||
gdbscm_pretty_printer_worker_p (SCM scm)
|
||
{
|
||
return scm_from_bool (ppscm_is_pretty_printer_worker (scm));
|
||
}
|
||
|
||
/* Helper function to create a <gdb:exception> object indicating that the
|
||
type of some value returned from a pretty-printer is invalid. */
|
||
|
||
static SCM
|
||
ppscm_make_pp_type_error_exception (const char *message, SCM object)
|
||
{
|
||
char *msg = xstrprintf ("%s: ~S", message);
|
||
struct cleanup *cleanup = make_cleanup (xfree, msg);
|
||
SCM exception
|
||
= gdbscm_make_error (pp_type_error_symbol,
|
||
NULL /* func */, msg,
|
||
scm_list_1 (object), scm_list_1 (object));
|
||
|
||
do_cleanups (cleanup);
|
||
|
||
return exception;
|
||
}
|
||
|
||
/* Print MESSAGE as an exception (meaning it is controlled by
|
||
"guile print-stack").
|
||
Called from the printer code when the Scheme code returns an invalid type
|
||
for something. */
|
||
|
||
static void
|
||
ppscm_print_pp_type_error (const char *message, SCM object)
|
||
{
|
||
SCM exception = ppscm_make_pp_type_error_exception (message, object);
|
||
|
||
gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
|
||
}
|
||
|
||
/* Helper function for find_pretty_printer which iterates over a list,
|
||
calls each function and inspects output. This will return a
|
||
<gdb:pretty-printer> object if one recognizes VALUE. If no printer is
|
||
found, it will return #f. On error, it will return a <gdb:exception>
|
||
object.
|
||
|
||
Note: This has to be efficient and careful.
|
||
We don't want to excessively slow down printing of values, but any kind of
|
||
random crud can appear in the pretty-printer list, and we can't crash
|
||
because of it. */
|
||
|
||
static SCM
|
||
ppscm_search_pp_list (SCM list, SCM value)
|
||
{
|
||
SCM orig_list = list;
|
||
|
||
if (scm_is_null (list))
|
||
return SCM_BOOL_F;
|
||
if (gdbscm_is_false (scm_list_p (list))) /* scm_is_pair? */
|
||
{
|
||
return ppscm_make_pp_type_error_exception
|
||
(_("pretty-printer list is not a list"), list);
|
||
}
|
||
|
||
for ( ; scm_is_pair (list); list = scm_cdr (list))
|
||
{
|
||
SCM matcher = scm_car (list);
|
||
SCM worker;
|
||
pretty_printer_smob *pp_smob;
|
||
int rc;
|
||
|
||
if (!ppscm_is_pretty_printer (matcher))
|
||
{
|
||
return ppscm_make_pp_type_error_exception
|
||
(_("pretty-printer list contains non-pretty-printer object"),
|
||
matcher);
|
||
}
|
||
|
||
pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (matcher);
|
||
|
||
/* Skip if disabled. */
|
||
if (gdbscm_is_false (pp_smob->enabled))
|
||
continue;
|
||
|
||
if (!gdbscm_is_procedure (pp_smob->lookup))
|
||
{
|
||
return ppscm_make_pp_type_error_exception
|
||
(_("invalid lookup object in pretty-printer matcher"),
|
||
pp_smob->lookup);
|
||
}
|
||
|
||
worker = gdbscm_safe_call_2 (pp_smob->lookup, matcher,
|
||
value, gdbscm_memory_error_p);
|
||
if (!gdbscm_is_false (worker))
|
||
{
|
||
if (gdbscm_is_exception (worker))
|
||
return worker;
|
||
if (ppscm_is_pretty_printer_worker (worker))
|
||
return worker;
|
||
return ppscm_make_pp_type_error_exception
|
||
(_("invalid result from pretty-printer lookup"), worker);
|
||
}
|
||
}
|
||
|
||
if (!scm_is_null (list))
|
||
{
|
||
return ppscm_make_pp_type_error_exception
|
||
(_("pretty-printer list is not a list"), orig_list);
|
||
}
|
||
|
||
return SCM_BOOL_F;
|
||
}
|
||
|
||
/* Subroutine of find_pretty_printer to simplify it.
|
||
Look for a pretty-printer to print VALUE in all objfiles.
|
||
If there's an error an exception smob is returned.
|
||
The result is #f, if no pretty-printer was found.
|
||
Otherwise the result is the pretty-printer smob. */
|
||
|
||
static SCM
|
||
ppscm_find_pretty_printer_from_objfiles (SCM value)
|
||
{
|
||
struct objfile *objfile;
|
||
|
||
ALL_OBJFILES (objfile)
|
||
{
|
||
objfile_smob *o_smob = ofscm_objfile_smob_from_objfile (objfile);
|
||
SCM pp = ppscm_search_pp_list (ofscm_objfile_smob_pretty_printers (o_smob),
|
||
value);
|
||
|
||
/* Note: This will return if pp is a <gdb:exception> object,
|
||
which is what we want. */
|
||
if (gdbscm_is_true (pp))
|
||
return pp;
|
||
}
|
||
|
||
return SCM_BOOL_F;
|
||
}
|
||
|
||
/* Subroutine of find_pretty_printer to simplify it.
|
||
Look for a pretty-printer to print VALUE in the current program space.
|
||
If there's an error an exception smob is returned.
|
||
The result is #f, if no pretty-printer was found.
|
||
Otherwise the result is the pretty-printer smob. */
|
||
|
||
static SCM
|
||
ppscm_find_pretty_printer_from_progspace (SCM value)
|
||
{
|
||
pspace_smob *p_smob = psscm_pspace_smob_from_pspace (current_program_space);
|
||
SCM pp
|
||
= ppscm_search_pp_list (psscm_pspace_smob_pretty_printers (p_smob), value);
|
||
|
||
return pp;
|
||
}
|
||
|
||
/* Subroutine of find_pretty_printer to simplify it.
|
||
Look for a pretty-printer to print VALUE in the gdb module.
|
||
If there's an error a Scheme exception is returned.
|
||
The result is #f, if no pretty-printer was found.
|
||
Otherwise the result is the pretty-printer smob. */
|
||
|
||
static SCM
|
||
ppscm_find_pretty_printer_from_gdb (SCM value)
|
||
{
|
||
SCM pp = ppscm_search_pp_list (pretty_printer_list, value);
|
||
|
||
return pp;
|
||
}
|
||
|
||
/* Find the pretty-printing constructor function for VALUE. If no
|
||
pretty-printer exists, return #f. If one exists, return the
|
||
gdb:pretty-printer smob that implements it. On error, an exception smob
|
||
is returned.
|
||
|
||
Note: In the end it may be better to call out to Scheme once, and then
|
||
do all of the lookup from Scheme. TBD. */
|
||
|
||
static SCM
|
||
ppscm_find_pretty_printer (SCM value)
|
||
{
|
||
SCM pp;
|
||
|
||
/* Look at the pretty-printer list for each objfile
|
||
in the current program-space. */
|
||
pp = ppscm_find_pretty_printer_from_objfiles (value);
|
||
/* Note: This will return if function is a <gdb:exception> object,
|
||
which is what we want. */
|
||
if (gdbscm_is_true (pp))
|
||
return pp;
|
||
|
||
/* Look at the pretty-printer list for the current program-space. */
|
||
pp = ppscm_find_pretty_printer_from_progspace (value);
|
||
/* Note: This will return if function is a <gdb:exception> object,
|
||
which is what we want. */
|
||
if (gdbscm_is_true (pp))
|
||
return pp;
|
||
|
||
/* Look at the pretty-printer list in the gdb module. */
|
||
pp = ppscm_find_pretty_printer_from_gdb (value);
|
||
return pp;
|
||
}
|
||
|
||
/* Pretty-print a single value, via the PRINTER, which must be a
|
||
<gdb:pretty-printer-worker> object.
|
||
The caller is responsible for ensuring PRINTER is valid.
|
||
If the function returns a string, an SCM containing the string
|
||
is returned. If the function returns #f that means the pretty
|
||
printer returned #f as a value. Otherwise, if the function returns a
|
||
<gdb:value> object, *OUT_VALUE is set to the value and #t is returned.
|
||
It is an error if the printer returns #t.
|
||
On error, an exception smob is returned. */
|
||
|
||
static SCM
|
||
ppscm_pretty_print_one_value (SCM printer, struct value **out_value,
|
||
struct gdbarch *gdbarch,
|
||
const struct language_defn *language)
|
||
{
|
||
SCM result = SCM_BOOL_F;
|
||
|
||
*out_value = NULL;
|
||
TRY
|
||
{
|
||
int rc;
|
||
pretty_printer_worker_smob *w_smob
|
||
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
|
||
|
||
result = gdbscm_safe_call_1 (w_smob->to_string, printer,
|
||
gdbscm_memory_error_p);
|
||
if (gdbscm_is_false (result))
|
||
; /* Done. */
|
||
else if (scm_is_string (result)
|
||
|| lsscm_is_lazy_string (result))
|
||
; /* Done. */
|
||
else if (vlscm_is_value (result))
|
||
{
|
||
SCM except_scm;
|
||
|
||
*out_value
|
||
= vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
|
||
result, &except_scm,
|
||
gdbarch, language);
|
||
if (*out_value != NULL)
|
||
result = SCM_BOOL_T;
|
||
else
|
||
result = except_scm;
|
||
}
|
||
else if (gdbscm_is_exception (result))
|
||
; /* Done. */
|
||
else
|
||
{
|
||
/* Invalid result from to-string. */
|
||
result = ppscm_make_pp_type_error_exception
|
||
(_("invalid result from pretty-printer to-string"), result);
|
||
}
|
||
}
|
||
CATCH (except, RETURN_MASK_ALL)
|
||
{
|
||
}
|
||
END_CATCH
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Return the display hint for PRINTER as a Scheme object.
|
||
The caller is responsible for ensuring PRINTER is a
|
||
<gdb:pretty-printer-worker> object. */
|
||
|
||
static SCM
|
||
ppscm_get_display_hint_scm (SCM printer)
|
||
{
|
||
pretty_printer_worker_smob *w_smob
|
||
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
|
||
|
||
return w_smob->display_hint;
|
||
}
|
||
|
||
/* Return the display hint for the pretty-printer PRINTER.
|
||
The caller is responsible for ensuring PRINTER is a
|
||
<gdb:pretty-printer-worker> object.
|
||
Returns the display hint or #f if the hint is not a string. */
|
||
|
||
static enum display_hint
|
||
ppscm_get_display_hint_enum (SCM printer)
|
||
{
|
||
SCM hint = ppscm_get_display_hint_scm (printer);
|
||
|
||
if (gdbscm_is_false (hint))
|
||
return HINT_NONE;
|
||
if (scm_is_string (hint))
|
||
{
|
||
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_array_string)))
|
||
return HINT_STRING;
|
||
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_map_string)))
|
||
return HINT_STRING;
|
||
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_string_string)))
|
||
return HINT_STRING;
|
||
return HINT_ERROR;
|
||
}
|
||
return HINT_ERROR;
|
||
}
|
||
|
||
/* A wrapper for gdbscm_print_gdb_exception that ignores memory errors.
|
||
EXCEPTION is a <gdb:exception> object. */
|
||
|
||
static void
|
||
ppscm_print_exception_unless_memory_error (SCM exception,
|
||
struct ui_file *stream)
|
||
{
|
||
if (gdbscm_memory_error_p (gdbscm_exception_key (exception)))
|
||
{
|
||
char *msg = gdbscm_exception_message_to_string (exception);
|
||
struct cleanup *cleanup = make_cleanup (xfree, msg);
|
||
|
||
/* This "shouldn't happen", but play it safe. */
|
||
if (msg == NULL || *msg == '\0')
|
||
fprintf_filtered (stream, _("<error reading variable>"));
|
||
else
|
||
{
|
||
/* Remove the trailing newline. We could instead call a special
|
||
routine for printing memory error messages, but this is easy
|
||
enough for now. */
|
||
size_t len = strlen (msg);
|
||
|
||
if (msg[len - 1] == '\n')
|
||
msg[len - 1] = '\0';
|
||
fprintf_filtered (stream, _("<error reading variable: %s>"), msg);
|
||
}
|
||
|
||
do_cleanups (cleanup);
|
||
}
|
||
else
|
||
gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
|
||
}
|
||
|
||
/* Helper for gdbscm_apply_val_pretty_printer which calls to_string and
|
||
formats the result. */
|
||
|
||
static enum string_repr_result
|
||
ppscm_print_string_repr (SCM printer, enum display_hint hint,
|
||
struct ui_file *stream, int recurse,
|
||
const struct value_print_options *options,
|
||
struct gdbarch *gdbarch,
|
||
const struct language_defn *language)
|
||
{
|
||
struct value *replacement = NULL;
|
||
SCM str_scm;
|
||
enum string_repr_result result = STRING_REPR_ERROR;
|
||
|
||
str_scm = ppscm_pretty_print_one_value (printer, &replacement,
|
||
gdbarch, language);
|
||
if (gdbscm_is_false (str_scm))
|
||
{
|
||
result = STRING_REPR_NONE;
|
||
}
|
||
else if (scm_is_eq (str_scm, SCM_BOOL_T))
|
||
{
|
||
struct value_print_options opts = *options;
|
||
|
||
gdb_assert (replacement != NULL);
|
||
opts.addressprint = 0;
|
||
common_val_print (replacement, stream, recurse, &opts, language);
|
||
result = STRING_REPR_OK;
|
||
}
|
||
else if (scm_is_string (str_scm))
|
||
{
|
||
struct cleanup *cleanup;
|
||
size_t length;
|
||
char *string
|
||
= gdbscm_scm_to_string (str_scm, &length,
|
||
target_charset (gdbarch), 0 /*!strict*/, NULL);
|
||
|
||
cleanup = make_cleanup (xfree, string);
|
||
if (hint == HINT_STRING)
|
||
{
|
||
struct type *type = builtin_type (gdbarch)->builtin_char;
|
||
|
||
LA_PRINT_STRING (stream, type, (gdb_byte *) string,
|
||
length, NULL, 0, options);
|
||
}
|
||
else
|
||
{
|
||
/* Alas scm_to_stringn doesn't nul-terminate the string if we
|
||
ask for the length. */
|
||
size_t i;
|
||
|
||
for (i = 0; i < length; ++i)
|
||
{
|
||
if (string[i] == '\0')
|
||
fputs_filtered ("\\000", stream);
|
||
else
|
||
fputc_filtered (string[i], stream);
|
||
}
|
||
}
|
||
result = STRING_REPR_OK;
|
||
do_cleanups (cleanup);
|
||
}
|
||
else if (lsscm_is_lazy_string (str_scm))
|
||
{
|
||
struct value_print_options local_opts = *options;
|
||
|
||
local_opts.addressprint = 0;
|
||
lsscm_val_print_lazy_string (str_scm, stream, &local_opts);
|
||
result = STRING_REPR_OK;
|
||
}
|
||
else
|
||
{
|
||
gdb_assert (gdbscm_is_exception (str_scm));
|
||
ppscm_print_exception_unless_memory_error (str_scm, stream);
|
||
result = STRING_REPR_ERROR;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Helper for gdbscm_apply_val_pretty_printer that formats children of the
|
||
printer, if any exist.
|
||
The caller is responsible for ensuring PRINTER is a printer smob.
|
||
If PRINTED_NOTHING is true, then nothing has been printed by to_string,
|
||
and format output accordingly. */
|
||
|
||
static void
|
||
ppscm_print_children (SCM printer, enum display_hint hint,
|
||
struct ui_file *stream, int recurse,
|
||
const struct value_print_options *options,
|
||
struct gdbarch *gdbarch,
|
||
const struct language_defn *language,
|
||
int printed_nothing)
|
||
{
|
||
pretty_printer_worker_smob *w_smob
|
||
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
|
||
int is_map, is_array, done_flag, pretty;
|
||
unsigned int i;
|
||
SCM children, status;
|
||
SCM iter = SCM_BOOL_F; /* -Wall */
|
||
struct cleanup *cleanups;
|
||
|
||
if (gdbscm_is_false (w_smob->children))
|
||
return;
|
||
if (!gdbscm_is_procedure (w_smob->children))
|
||
{
|
||
ppscm_print_pp_type_error
|
||
(_("pretty-printer \"children\" object is not a procedure or #f"),
|
||
w_smob->children);
|
||
return;
|
||
}
|
||
|
||
cleanups = make_cleanup (null_cleanup, NULL);
|
||
|
||
/* If we are printing a map or an array, we want special formatting. */
|
||
is_map = hint == HINT_MAP;
|
||
is_array = hint == HINT_ARRAY;
|
||
|
||
children = gdbscm_safe_call_1 (w_smob->children, printer,
|
||
gdbscm_memory_error_p);
|
||
if (gdbscm_is_exception (children))
|
||
{
|
||
ppscm_print_exception_unless_memory_error (children, stream);
|
||
goto done;
|
||
}
|
||
/* We combine two steps here: get children, make an iterator out of them.
|
||
This simplifies things because there's no language means of creating
|
||
iterators, and it's the printer object that knows how it will want its
|
||
children iterated over. */
|
||
if (!itscm_is_iterator (children))
|
||
{
|
||
ppscm_print_pp_type_error
|
||
(_("result of pretty-printer \"children\" procedure is not"
|
||
" a <gdb:iterator> object"), children);
|
||
goto done;
|
||
}
|
||
iter = children;
|
||
|
||
/* Use the prettyformat_arrays option if we are printing an array,
|
||
and the pretty option otherwise. */
|
||
if (is_array)
|
||
pretty = options->prettyformat_arrays;
|
||
else
|
||
{
|
||
if (options->prettyformat == Val_prettyformat)
|
||
pretty = 1;
|
||
else
|
||
pretty = options->prettyformat_structs;
|
||
}
|
||
|
||
done_flag = 0;
|
||
for (i = 0; i < options->print_max; ++i)
|
||
{
|
||
int rc;
|
||
SCM scm_name, v_scm;
|
||
char *name;
|
||
SCM item = itscm_safe_call_next_x (iter, gdbscm_memory_error_p);
|
||
struct cleanup *inner_cleanup = make_cleanup (null_cleanup, NULL);
|
||
|
||
if (gdbscm_is_exception (item))
|
||
{
|
||
ppscm_print_exception_unless_memory_error (item, stream);
|
||
break;
|
||
}
|
||
if (itscm_is_end_of_iteration (item))
|
||
{
|
||
/* Set a flag so we can know whether we printed all the
|
||
available elements. */
|
||
done_flag = 1;
|
||
break;
|
||
}
|
||
|
||
if (! scm_is_pair (item))
|
||
{
|
||
ppscm_print_pp_type_error
|
||
(_("result of pretty-printer children iterator is not a pair"
|
||
" or (end-of-iteration)"),
|
||
item);
|
||
continue;
|
||
}
|
||
scm_name = scm_car (item);
|
||
v_scm = scm_cdr (item);
|
||
if (!scm_is_string (scm_name))
|
||
{
|
||
ppscm_print_pp_type_error
|
||
(_("first element of pretty-printer children iterator is not"
|
||
" a string"), item);
|
||
continue;
|
||
}
|
||
name = gdbscm_scm_to_c_string (scm_name);
|
||
make_cleanup (xfree, name);
|
||
|
||
/* Print initial "{". For other elements, there are three cases:
|
||
1. Maps. Print a "," after each value element.
|
||
2. Arrays. Always print a ",".
|
||
3. Other. Always print a ",". */
|
||
if (i == 0)
|
||
{
|
||
if (printed_nothing)
|
||
fputs_filtered ("{", stream);
|
||
else
|
||
fputs_filtered (" = {", stream);
|
||
}
|
||
|
||
else if (! is_map || i % 2 == 0)
|
||
fputs_filtered (pretty ? "," : ", ", stream);
|
||
|
||
/* In summary mode, we just want to print "= {...}" if there is
|
||
a value. */
|
||
if (options->summary)
|
||
{
|
||
/* This increment tricks the post-loop logic to print what
|
||
we want. */
|
||
++i;
|
||
/* Likewise. */
|
||
pretty = 0;
|
||
break;
|
||
}
|
||
|
||
if (! is_map || i % 2 == 0)
|
||
{
|
||
if (pretty)
|
||
{
|
||
fputs_filtered ("\n", stream);
|
||
print_spaces_filtered (2 + 2 * recurse, stream);
|
||
}
|
||
else
|
||
wrap_here (n_spaces (2 + 2 *recurse));
|
||
}
|
||
|
||
if (is_map && i % 2 == 0)
|
||
fputs_filtered ("[", stream);
|
||
else if (is_array)
|
||
{
|
||
/* We print the index, not whatever the child method
|
||
returned as the name. */
|
||
if (options->print_array_indexes)
|
||
fprintf_filtered (stream, "[%d] = ", i);
|
||
}
|
||
else if (! is_map)
|
||
{
|
||
fputs_filtered (name, stream);
|
||
fputs_filtered (" = ", stream);
|
||
}
|
||
|
||
if (lsscm_is_lazy_string (v_scm))
|
||
{
|
||
struct value_print_options local_opts = *options;
|
||
|
||
local_opts.addressprint = 0;
|
||
lsscm_val_print_lazy_string (v_scm, stream, &local_opts);
|
||
}
|
||
else if (scm_is_string (v_scm))
|
||
{
|
||
char *output = gdbscm_scm_to_c_string (v_scm);
|
||
|
||
fputs_filtered (output, stream);
|
||
xfree (output);
|
||
}
|
||
else
|
||
{
|
||
SCM except_scm;
|
||
struct value *value
|
||
= vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
|
||
v_scm, &except_scm,
|
||
gdbarch, language);
|
||
|
||
if (value == NULL)
|
||
{
|
||
ppscm_print_exception_unless_memory_error (except_scm, stream);
|
||
break;
|
||
}
|
||
common_val_print (value, stream, recurse + 1, options, language);
|
||
}
|
||
|
||
if (is_map && i % 2 == 0)
|
||
fputs_filtered ("] = ", stream);
|
||
|
||
do_cleanups (inner_cleanup);
|
||
}
|
||
|
||
if (i)
|
||
{
|
||
if (!done_flag)
|
||
{
|
||
if (pretty)
|
||
{
|
||
fputs_filtered ("\n", stream);
|
||
print_spaces_filtered (2 + 2 * recurse, stream);
|
||
}
|
||
fputs_filtered ("...", stream);
|
||
}
|
||
if (pretty)
|
||
{
|
||
fputs_filtered ("\n", stream);
|
||
print_spaces_filtered (2 * recurse, stream);
|
||
}
|
||
fputs_filtered ("}", stream);
|
||
}
|
||
|
||
done:
|
||
do_cleanups (cleanups);
|
||
|
||
/* Play it safe, make sure ITER doesn't get GC'd. */
|
||
scm_remember_upto_here_1 (iter);
|
||
}
|
||
|
||
/* This is the extension_language_ops.apply_val_pretty_printer "method". */
|
||
|
||
enum ext_lang_rc
|
||
gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang,
|
||
struct type *type, const gdb_byte *valaddr,
|
||
int embedded_offset, CORE_ADDR address,
|
||
struct ui_file *stream, int recurse,
|
||
const struct value *val,
|
||
const struct value_print_options *options,
|
||
const struct language_defn *language)
|
||
{
|
||
struct gdbarch *gdbarch = get_type_arch (type);
|
||
SCM exception = SCM_BOOL_F;
|
||
SCM printer = SCM_BOOL_F;
|
||
SCM val_obj = SCM_BOOL_F;
|
||
struct value *value;
|
||
enum display_hint hint;
|
||
struct cleanup *cleanups;
|
||
int result = EXT_LANG_RC_NOP;
|
||
enum string_repr_result print_result;
|
||
|
||
/* No pretty-printer support for unavailable values. */
|
||
if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
|
||
return EXT_LANG_RC_NOP;
|
||
|
||
if (!gdb_scheme_initialized)
|
||
return EXT_LANG_RC_NOP;
|
||
|
||
cleanups = make_cleanup (null_cleanup, NULL);
|
||
|
||
/* Instantiate the printer. */
|
||
if (valaddr)
|
||
valaddr += embedded_offset;
|
||
value = value_from_contents_and_address (type, valaddr,
|
||
address + embedded_offset);
|
||
|
||
set_value_component_location (value, val);
|
||
/* set_value_component_location resets the address, so we may
|
||
need to set it again. */
|
||
if (VALUE_LVAL (value) != lval_internalvar
|
||
&& VALUE_LVAL (value) != lval_internalvar_component
|
||
&& VALUE_LVAL (value) != lval_computed)
|
||
set_value_address (value, address + embedded_offset);
|
||
|
||
val_obj = vlscm_scm_from_value (value);
|
||
if (gdbscm_is_exception (val_obj))
|
||
{
|
||
exception = val_obj;
|
||
result = EXT_LANG_RC_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
printer = ppscm_find_pretty_printer (val_obj);
|
||
|
||
if (gdbscm_is_exception (printer))
|
||
{
|
||
exception = printer;
|
||
result = EXT_LANG_RC_ERROR;
|
||
goto done;
|
||
}
|
||
if (gdbscm_is_false (printer))
|
||
{
|
||
result = EXT_LANG_RC_NOP;
|
||
goto done;
|
||
}
|
||
gdb_assert (ppscm_is_pretty_printer_worker (printer));
|
||
|
||
/* If we are printing a map, we want some special formatting. */
|
||
hint = ppscm_get_display_hint_enum (printer);
|
||
if (hint == HINT_ERROR)
|
||
{
|
||
/* Print the error as an exception for consistency. */
|
||
SCM hint_scm = ppscm_get_display_hint_scm (printer);
|
||
|
||
ppscm_print_pp_type_error ("Invalid display hint", hint_scm);
|
||
/* Fall through. A bad hint doesn't stop pretty-printing. */
|
||
hint = HINT_NONE;
|
||
}
|
||
|
||
/* Print the section. */
|
||
print_result = ppscm_print_string_repr (printer, hint, stream, recurse,
|
||
options, gdbarch, language);
|
||
if (print_result != STRING_REPR_ERROR)
|
||
{
|
||
ppscm_print_children (printer, hint, stream, recurse, options,
|
||
gdbarch, language,
|
||
print_result == STRING_REPR_NONE);
|
||
}
|
||
|
||
result = EXT_LANG_RC_OK;
|
||
|
||
done:
|
||
if (gdbscm_is_exception (exception))
|
||
ppscm_print_exception_unless_memory_error (exception, stream);
|
||
do_cleanups (cleanups);
|
||
return result;
|
||
}
|
||
|
||
/* Initialize the Scheme pretty-printer code. */
|
||
|
||
static const scheme_function pretty_printer_functions[] =
|
||
{
|
||
{ "make-pretty-printer", 2, 0, 0, gdbscm_make_pretty_printer,
|
||
"\
|
||
Create a <gdb:pretty-printer> object.\n\
|
||
\n\
|
||
Arguments: name lookup\n\
|
||
name: a string naming the matcher\n\
|
||
lookup: a procedure:\n\
|
||
(pretty-printer <gdb:value>) -> <gdb:pretty-printer-worker> | #f." },
|
||
|
||
{ "pretty-printer?", 1, 0, 0, gdbscm_pretty_printer_p,
|
||
"\
|
||
Return #t if the object is a <gdb:pretty-printer> object." },
|
||
|
||
{ "pretty-printer-enabled?", 1, 0, 0, gdbscm_pretty_printer_enabled_p,
|
||
"\
|
||
Return #t if the pretty-printer is enabled." },
|
||
|
||
{ "set-pretty-printer-enabled!", 2, 0, 0,
|
||
gdbscm_set_pretty_printer_enabled_x,
|
||
"\
|
||
Set the enabled flag of the pretty-printer.\n\
|
||
Returns \"unspecified\"." },
|
||
|
||
{ "make-pretty-printer-worker", 3, 0, 0, gdbscm_make_pretty_printer_worker,
|
||
"\
|
||
Create a <gdb:pretty-printer-worker> object.\n\
|
||
\n\
|
||
Arguments: display-hint to-string children\n\
|
||
display-hint: either #f or one of \"array\", \"map\", or \"string\"\n\
|
||
to-string: a procedure:\n\
|
||
(pretty-printer) -> string | #f | <gdb:value>\n\
|
||
children: either #f or a procedure:\n\
|
||
(pretty-printer) -> <gdb:iterator>" },
|
||
|
||
{ "pretty-printer-worker?", 1, 0, 0, gdbscm_pretty_printer_worker_p,
|
||
"\
|
||
Return #t if the object is a <gdb:pretty-printer-worker> object." },
|
||
|
||
{ "pretty-printers", 0, 0, 0, gdbscm_pretty_printers,
|
||
"\
|
||
Return the list of global pretty-printers." },
|
||
|
||
{ "set-pretty-printers!", 1, 0, 0,
|
||
gdbscm_set_pretty_printers_x,
|
||
"\
|
||
Set the list of global pretty-printers." },
|
||
|
||
END_FUNCTIONS
|
||
};
|
||
|
||
void
|
||
gdbscm_initialize_pretty_printers (void)
|
||
{
|
||
pretty_printer_smob_tag
|
||
= gdbscm_make_smob_type (pretty_printer_smob_name,
|
||
sizeof (pretty_printer_smob));
|
||
scm_set_smob_print (pretty_printer_smob_tag,
|
||
ppscm_print_pretty_printer_smob);
|
||
|
||
pretty_printer_worker_smob_tag
|
||
= gdbscm_make_smob_type (pretty_printer_worker_smob_name,
|
||
sizeof (pretty_printer_worker_smob));
|
||
scm_set_smob_print (pretty_printer_worker_smob_tag,
|
||
ppscm_print_pretty_printer_worker_smob);
|
||
|
||
gdbscm_define_functions (pretty_printer_functions, 1);
|
||
|
||
pretty_printer_list = SCM_EOL;
|
||
|
||
pp_type_error_symbol = scm_from_latin1_symbol ("gdb:pp-type-error");
|
||
|
||
ppscm_map_string = scm_from_latin1_string ("map");
|
||
ppscm_array_string = scm_from_latin1_string ("array");
|
||
ppscm_string_string = scm_from_latin1_string ("string");
|
||
}
|