/* GDB/Scheme pretty-printing. Copyright (C) 2008-2022 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 . */ /* 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 smob. */ struct pretty_printer_smob { /* 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. */ }; /* The smob. */ struct pretty_printer_worker_smob { /* 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 | | */ SCM to_string; /* A procedure called to print children of the value. (lambda (printer) ...) -> The iterator returns a pair for each iteration: (name . value), where "value" can have the same types as to_string. */ SCM children; }; 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 . */ 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) -> */ 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 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 object in SELF. Throws an exception if SELF is not a 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 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? ) -> 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! 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 . */ 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) -> */ 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 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 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) { std::string msg = string_printf ("%s: ~S", message); return gdbscm_make_error (pp_type_error_symbol, NULL /* func */, msg.c_str (), scm_list_1 (object), scm_list_1 (object)); } /* 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 object if one recognizes VALUE. If no printer is found, it will return #f. On error, it will return a 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; 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) { for (objfile *objfile : current_program_space->objfiles ()) { 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 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 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 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 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 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 { 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 (const gdb_exception &except) { } return result; } /* Return the display hint for PRINTER as a Scheme object. The caller is responsible for ensuring PRINTER is a 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 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 object. */ static void ppscm_print_exception_unless_memory_error (SCM exception, struct ui_file *stream) { if (gdbscm_memory_error_p (gdbscm_exception_key (exception))) { gdb::unique_xmalloc_ptr msg = gdbscm_exception_message_to_string (exception); /* This "shouldn't happen", but play it safe. */ if (msg == NULL || msg.get ()[0] == '\0') fprintf_filtered (stream, _("")); else { /* Remove the trailing newline. We could instead call a special routine for printing memory error messages, but this is easy enough for now. */ char *msg_text = msg.get (); size_t len = strlen (msg_text); if (msg_text[len - 1] == '\n') msg_text[len - 1] = '\0'; fprintf_filtered (stream, _(""), msg_text); } } 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)) { size_t length; gdb::unique_xmalloc_ptr string = gdbscm_scm_to_string (str_scm, &length, target_charset (gdbarch), 0 /*!strict*/, NULL); if (hint == HINT_STRING) { struct type *type = builtin_type (gdbarch)->builtin_char; language->printstr (stream, type, (gdb_byte *) string.get (), 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.get ()[i] == '\0') fputs_filtered ("\\000", stream); else fputc_filtered (string.get ()[i], stream); } } result = STRING_REPR_OK; } 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; SCM iter = SCM_BOOL_F; /* -Wall */ 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; } /* 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 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) { SCM scm_name, v_scm; SCM item = itscm_safe_call_next_x (iter, gdbscm_memory_error_p); 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; } gdb::unique_xmalloc_ptr name = gdbscm_scm_to_c_string (scm_name); /* Print initial "=" to separate print_string_repr output and children. 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 if (! is_map || i % 2 == 0) fputs_filtered (pretty ? "," : ", ", stream); /* Skip printing children if max_depth has been reached. This check is performed after print_string_repr and the "=" separator so that these steps are not skipped if the variable is located within the permitted depth. */ if (val_print_check_max_depth (stream, recurse, options, language)) goto done; else if (i == 0) /* Print initial "{" to bookend children. */ fputs_filtered ("{", 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 stream->wrap_here (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.get (), 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)) { gdb::unique_xmalloc_ptr output = gdbscm_scm_to_c_string (v_scm); fputs_filtered (output.get (), stream); } 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; } else { /* When printing the key of a map we allow one additional level of depth. This means the key will print before the value does. */ struct value_print_options opt = *options; if (is_map && i % 2 == 0 && opt.max_depth != -1 && opt.max_depth < INT_MAX) ++opt.max_depth; common_val_print (value, stream, recurse + 1, &opt, language); } } if (is_map && i % 2 == 0) fputs_filtered ("] = ", stream); } 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: /* 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 value *value, struct ui_file *stream, int recurse, const struct value_print_options *options, const struct language_defn *language) { struct type *type = value_type (value); struct gdbarch *gdbarch = type->arch (); SCM exception = SCM_BOOL_F; SCM printer = SCM_BOOL_F; SCM val_obj = SCM_BOOL_F; enum display_hint hint; enum ext_lang_rc result = EXT_LANG_RC_NOP; enum string_repr_result print_result; if (value_lazy (value)) value_fetch_lazy (value); /* No pretty-printer support for unavailable values. */ if (!value_bytes_available (value, 0, TYPE_LENGTH (type))) return EXT_LANG_RC_NOP; if (!gdb_scheme_initialized) return EXT_LANG_RC_NOP; /* Instantiate the printer. */ val_obj = vlscm_scm_from_value_no_release (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); 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 object.\n\ \n\ Arguments: name lookup\n\ name: a string naming the matcher\n\ lookup: a procedure:\n\ (pretty-printer ) -> | #f." }, { "pretty-printer?", 1, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printer_p), "\ Return #t if the object is a 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 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 | \n\ children: either #f or a procedure:\n\ (pretty-printer) -> " }, { "pretty-printer-worker?", 1, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printer_worker_p), "\ Return #t if the object is a 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"); }