mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
6b8505468e
GDB computes structure byte offsets using a 32 bit integer. And, first it computes the offset in bits and then converts to bytes. The result is that any offset that if 512K bytes or larger overflows. This patch changes GDB to use LONGEST for such calculations. PR gdb/17520 Structure offset wrong when 1/4 GB or greater. * c-lang.h: Change all parameters, variables, and struct or union members used as struct or union fie3ld offsets from int to LONGEST. * c-valprint.c: Likewise. * cp-abi.c: Likewise. * cp-abi.h: Likewise. * cp-valprint.c: Likewise. * d-valprint.c: Likewise. * dwarf2loc.c: Likewise. * eval.c: Likewise. * extension-priv.h: Likewise. * extension.c: Likewise. * extension.h: Likewise. * findvar.c: Likewise. * gdbtypes.h: Likewise. * gnu-v2-abi.c: Likewise. * gnu-v3-abi.c: Likewise. * go-valprint.c: Likewise. * guile/guile-internal.h: Likewise. * guile/scm-pretty-print.c: Likewise. * jv-valprint.c Likewise. * opencl-lang.c: Likewise. * p-lang.h: Likewise. * python/py-prettyprint.c: Likewise. * python/python-internal.h: Likewise. * spu-tdep.c: Likewise. * typeprint.c: Likewise. * valarith.c: Likewise. * valops.c: Likewise. * valprint.c: Likewise. * valprint.h: Likewise. * value.c: Likewise. * value.h: Likewise. * p-valprint.c: Likewise. * c-typeprint.c (c_type_print_base): When printing offset, use plongest, not %d. * gdbtypes.c (recursive_dump_type): Ditto.
1137 lines
31 KiB
C
1137 lines
31 KiB
C
/* GDB/Scheme pretty-printing.
|
||
|
||
Copyright (C) 2008-2016 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,
|
||
LONGEST 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;
|
||
enum ext_lang_rc 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,
|
||
as_a_scm_t_subr (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, as_a_scm_t_subr (gdbscm_pretty_printer_p),
|
||
"\
|
||
Return #t if the object is a <gdb:pretty-printer> object." },
|
||
|
||
{ "pretty-printer-enabled?", 1, 0, 0,
|
||
as_a_scm_t_subr (gdbscm_pretty_printer_enabled_p),
|
||
"\
|
||
Return #t if the pretty-printer is enabled." },
|
||
|
||
{ "set-pretty-printer-enabled!", 2, 0, 0,
|
||
as_a_scm_t_subr (gdbscm_set_pretty_printer_enabled_x),
|
||
"\
|
||
Set the enabled flag of the pretty-printer.\n\
|
||
Returns \"unspecified\"." },
|
||
|
||
{ "make-pretty-printer-worker", 3, 0, 0,
|
||
as_a_scm_t_subr (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,
|
||
as_a_scm_t_subr (gdbscm_pretty_printer_worker_p),
|
||
"\
|
||
Return #t if the object is a <gdb:pretty-printer-worker> object." },
|
||
|
||
{ "pretty-printers", 0, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printers),
|
||
"\
|
||
Return the list of global pretty-printers." },
|
||
|
||
{ "set-pretty-printers!", 1, 0, 0,
|
||
as_a_scm_t_subr (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");
|
||
}
|