| /* Scheme interface to lazy strings. | 
 |  | 
 |    Copyright (C) 2010-2024 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 "charset.h" | 
 | #include "value.h" | 
 | #include "valprint.h" | 
 | #include "language.h" | 
 | #include "guile-internal.h" | 
 |  | 
 | /* The <gdb:lazy-string> smob.  */ | 
 |  | 
 | struct lazy_string_smob | 
 | { | 
 |   /* This always appears first.  */ | 
 |   gdb_smob base; | 
 |  | 
 |   /*  Holds the address of the lazy string.  */ | 
 |   CORE_ADDR address; | 
 |  | 
 |   /*  Holds the encoding that will be applied to the string when the string | 
 |       is printed by GDB.  If the encoding is set to NULL then GDB will select | 
 |       the most appropriate encoding when the sting is printed. | 
 |       Space for this is malloc'd and will be freed when the object is | 
 |       freed.  */ | 
 |   char *encoding; | 
 |  | 
 |   /* If TYPE is an array: If the length is known, then this value is the | 
 |      array's length, otherwise it is -1. | 
 |      If TYPE is not an array: Then this value represents the string's length. | 
 |      In either case, if the value is -1 then the string will be fetched and | 
 |      encoded up to the first null of appropriate width.  */ | 
 |   int length; | 
 |  | 
 |   /* The type of the string. | 
 |      For example if the lazy string was created from a C "char*" then TYPE | 
 |      represents a C "char*".  To get the type of the character in the string | 
 |      call lsscm_elt_type which handles the different kinds of values for TYPE. | 
 |      This is recorded as an SCM object so that we take advantage of support for | 
 |      preserving the type should its owning objfile go away.  */ | 
 |   SCM type; | 
 | }; | 
 |  | 
 | static const char lazy_string_smob_name[] = "gdb:lazy-string"; | 
 |  | 
 | /* The tag Guile knows the lazy string smob by.  */ | 
 | static scm_t_bits lazy_string_smob_tag; | 
 |  | 
 | /* Administrivia for lazy string smobs.  */ | 
 |  | 
 | /* The smob "free" function for <gdb:lazy-string>.  */ | 
 |  | 
 | static size_t | 
 | lsscm_free_lazy_string_smob (SCM self) | 
 | { | 
 |   lazy_string_smob *v_smob = (lazy_string_smob *) SCM_SMOB_DATA (self); | 
 |  | 
 |   xfree (v_smob->encoding); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* The smob "print" function for <gdb:lazy-string>.  */ | 
 |  | 
 | static int | 
 | lsscm_print_lazy_string_smob (SCM self, SCM port, scm_print_state *pstate) | 
 | { | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (self); | 
 |  | 
 |   gdbscm_printf (port, "#<%s", lazy_string_smob_name); | 
 |   gdbscm_printf (port, " @%s", hex_string (ls_smob->address)); | 
 |   if (ls_smob->length >= 0) | 
 |     gdbscm_printf (port, " length %d", ls_smob->length); | 
 |   if (ls_smob->encoding != NULL) | 
 |     gdbscm_printf (port, " encoding %s", ls_smob->encoding); | 
 |   scm_puts (">", port); | 
 |  | 
 |   scm_remember_upto_here_1 (self); | 
 |  | 
 |   /* Non-zero means success.  */ | 
 |   return 1; | 
 | } | 
 |  | 
 | /* Low level routine to create a <gdb:lazy-string> object. | 
 |    The caller must verify: | 
 |    - length >= -1 | 
 |    - !(address == 0 && length != 0) | 
 |    - type != NULL */ | 
 |  | 
 | static SCM | 
 | lsscm_make_lazy_string_smob (CORE_ADDR address, int length, | 
 | 			     const char *encoding, struct type *type) | 
 | { | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) | 
 |     scm_gc_malloc (sizeof (lazy_string_smob), lazy_string_smob_name); | 
 |   SCM ls_scm; | 
 |  | 
 |   gdb_assert (length >= -1); | 
 |   gdb_assert (!(address == 0 && length != 0)); | 
 |   gdb_assert (type != NULL); | 
 |  | 
 |   ls_smob->address = address; | 
 |   ls_smob->length = length; | 
 |   if (encoding == NULL || strcmp (encoding, "") == 0) | 
 |     ls_smob->encoding = NULL; | 
 |   else | 
 |     ls_smob->encoding = xstrdup (encoding); | 
 |   ls_smob->type = tyscm_scm_from_type (type); | 
 |  | 
 |   ls_scm = scm_new_smob (lazy_string_smob_tag, (scm_t_bits) ls_smob); | 
 |   gdbscm_init_gsmob (&ls_smob->base); | 
 |  | 
 |   return ls_scm; | 
 | } | 
 |  | 
 | /* Return non-zero if SCM is a <gdb:lazy-string> object.  */ | 
 |  | 
 | int | 
 | lsscm_is_lazy_string (SCM scm) | 
 | { | 
 |   return SCM_SMOB_PREDICATE (lazy_string_smob_tag, scm); | 
 | } | 
 |  | 
 | /* (lazy-string? object) -> boolean */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_p (SCM scm) | 
 | { | 
 |   return scm_from_bool (lsscm_is_lazy_string (scm)); | 
 | } | 
 |  | 
 | /* Main entry point to create a <gdb:lazy-string> object. | 
 |    If there's an error a <gdb:exception> object is returned.  */ | 
 |  | 
 | SCM | 
 | lsscm_make_lazy_string (CORE_ADDR address, int length, | 
 | 			const char *encoding, struct type *type) | 
 | { | 
 |   if (length < -1) | 
 |     { | 
 |       return gdbscm_make_out_of_range_error (NULL, 0, | 
 | 					     scm_from_int (length), | 
 | 					     _("invalid length")); | 
 |     } | 
 |  | 
 |   if (address == 0 && length != 0) | 
 |     { | 
 |       return gdbscm_make_out_of_range_error | 
 | 	(NULL, 0, scm_from_int (length), | 
 | 	 _("cannot create a lazy string with address 0x0," | 
 | 	   " and a non-zero length")); | 
 |     } | 
 |  | 
 |   if (type == NULL) | 
 |     { | 
 |       return gdbscm_make_out_of_range_error | 
 | 	(NULL, 0, scm_from_int (0), _("a lazy string's type cannot be NULL")); | 
 |     } | 
 |  | 
 |   return lsscm_make_lazy_string_smob (address, length, encoding, type); | 
 | } | 
 |  | 
 | /* Returns the <gdb:lazy-string> smob in SELF. | 
 |    Throws an exception if SELF is not a <gdb:lazy-string> object.  */ | 
 |  | 
 | static SCM | 
 | lsscm_get_lazy_string_arg_unsafe (SCM self, int arg_pos, const char *func_name) | 
 | { | 
 |   SCM_ASSERT_TYPE (lsscm_is_lazy_string (self), self, arg_pos, func_name, | 
 | 		   lazy_string_smob_name); | 
 |  | 
 |   return self; | 
 | } | 
 |  | 
 | /* Return the type of a character in lazy string LS_SMOB.  */ | 
 |  | 
 | static struct type * | 
 | lsscm_elt_type (lazy_string_smob *ls_smob) | 
 | { | 
 |   struct type *type = tyscm_scm_to_type (ls_smob->type); | 
 |   struct type *realtype; | 
 |  | 
 |   realtype = check_typedef (type); | 
 |  | 
 |   switch (realtype->code ()) | 
 |     { | 
 |     case TYPE_CODE_PTR: | 
 |     case TYPE_CODE_ARRAY: | 
 |       return realtype->target_type (); | 
 |     default: | 
 |       /* This is done to preserve existing behaviour.  PR 20769. | 
 | 	 E.g., gdb.parse_and_eval("my_int_variable").lazy_string().type.  */ | 
 |       return realtype; | 
 |     } | 
 | } | 
 |  | 
 | /* Lazy string methods.  */ | 
 |  | 
 | /* (lazy-string-address <gdb:lazy-string>) -> address */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_address (SCM self) | 
 | { | 
 |   SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm); | 
 |  | 
 |   return gdbscm_scm_from_ulongest (ls_smob->address); | 
 | } | 
 |  | 
 | /* (lazy-string-length <gdb:lazy-string>) -> integer */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_length (SCM self) | 
 | { | 
 |   SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm); | 
 |  | 
 |   return scm_from_int (ls_smob->length); | 
 | } | 
 |  | 
 | /* (lazy-string-encoding <gdb:lazy-string>) -> string */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_encoding (SCM self) | 
 | { | 
 |   SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm); | 
 |  | 
 |   /* An encoding can be set to NULL by the user, so check first. | 
 |      If NULL return #f.  */ | 
 |   if (ls_smob != NULL) | 
 |     return gdbscm_scm_from_c_string (ls_smob->encoding); | 
 |   return SCM_BOOL_F; | 
 | } | 
 |  | 
 | /* (lazy-string-type <gdb:lazy-string>) -> <gdb:type> */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_type (SCM self) | 
 | { | 
 |   SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |   lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm); | 
 |  | 
 |   return ls_smob->type; | 
 | } | 
 |  | 
 | /* (lazy-string->value <gdb:lazy-string>) -> <gdb:value> */ | 
 |  | 
 | static SCM | 
 | gdbscm_lazy_string_to_value (SCM self) | 
 | { | 
 |   SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |   SCM except_scm; | 
 |   struct value *value; | 
 |  | 
 |   value = lsscm_safe_lazy_string_to_value (ls_scm, SCM_ARG1, FUNC_NAME, | 
 | 					   &except_scm); | 
 |   if (value == NULL) | 
 |     gdbscm_throw (except_scm); | 
 |   return vlscm_scm_from_value (value); | 
 | } | 
 |  | 
 | /* A "safe" version of gdbscm_lazy_string_to_value for use by | 
 |    vlscm_convert_typed_value_from_scheme. | 
 |    The result, upon success, is the value of <gdb:lazy-string> STRING. | 
 |    ARG_POS is the argument position of STRING in the original Scheme | 
 |    function call, used in exception text. | 
 |    If there's an error, NULL is returned and a <gdb:exception> object | 
 |    is stored in *except_scmp. | 
 |  | 
 |    Note: The result is still "lazy".  The caller must call value_fetch_lazy | 
 |    to actually fetch the value.  */ | 
 |  | 
 | struct value * | 
 | lsscm_safe_lazy_string_to_value (SCM string, int arg_pos, | 
 | 				 const char *func_name, SCM *except_scmp) | 
 | { | 
 |   lazy_string_smob *ls_smob; | 
 |   struct value *value = NULL; | 
 |  | 
 |   gdb_assert (lsscm_is_lazy_string (string)); | 
 |  | 
 |   ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (string); | 
 |  | 
 |   if (ls_smob->address == 0) | 
 |     { | 
 |       *except_scmp | 
 | 	= gdbscm_make_out_of_range_error (func_name, arg_pos, string, | 
 | 					 _("cannot create a value from NULL")); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   try | 
 |     { | 
 |       struct type *type = tyscm_scm_to_type (ls_smob->type); | 
 |       struct type *realtype = check_typedef (type); | 
 |  | 
 |       switch (realtype->code ()) | 
 | 	{ | 
 | 	case TYPE_CODE_PTR: | 
 | 	  /* If a length is specified we need to convert this to an array | 
 | 	     of the specified size.  */ | 
 | 	  if (ls_smob->length != -1) | 
 | 	    { | 
 | 	      /* PR 20786: There's no way to specify an array of length zero. | 
 | 		 Record a length of [0,-1] which is how Ada does it.  Anything | 
 | 		 we do is broken, but this one possible solution.  */ | 
 | 	      type = lookup_array_range_type (realtype->target_type (), | 
 | 					      0, ls_smob->length - 1); | 
 | 	      value = value_at_lazy (type, ls_smob->address); | 
 | 	    } | 
 | 	  else | 
 | 	    value = value_from_pointer (type, ls_smob->address); | 
 | 	  break; | 
 | 	default: | 
 | 	  value = value_at_lazy (type, ls_smob->address); | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |   catch (const gdb_exception &except) | 
 |     { | 
 |       *except_scmp = gdbscm_scm_from_gdb_exception (unpack (except)); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   return value; | 
 | } | 
 |  | 
 | /* Print a lazy string to STREAM using val_print_string. | 
 |    STRING must be a <gdb:lazy-string> object.  */ | 
 |  | 
 | void | 
 | lsscm_val_print_lazy_string (SCM string, struct ui_file *stream, | 
 | 			     const struct value_print_options *options) | 
 | { | 
 |   lazy_string_smob *ls_smob; | 
 |   struct type *elt_type; | 
 |  | 
 |   gdb_assert (lsscm_is_lazy_string (string)); | 
 |  | 
 |   ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (string); | 
 |   elt_type = lsscm_elt_type (ls_smob); | 
 |  | 
 |   val_print_string (elt_type, ls_smob->encoding, | 
 | 		    ls_smob->address, ls_smob->length, | 
 | 		    stream, options); | 
 | } | 
 |  | 
 | /* Initialize the Scheme lazy-strings code.  */ | 
 |  | 
 | static const scheme_function lazy_string_functions[] = | 
 | { | 
 |   { "lazy-string?", 1, 0, 0, as_a_scm_t_subr (gdbscm_lazy_string_p), | 
 |     "\ | 
 | Return #t if the object is a <gdb:lazy-string> object." }, | 
 |  | 
 |   { "lazy-string-address", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_lazy_string_address), | 
 |     "\ | 
 | Return the address of the lazy-string." }, | 
 |  | 
 |   { "lazy-string-length", 1, 0, 0, as_a_scm_t_subr (gdbscm_lazy_string_length), | 
 |     "\ | 
 | Return the length of the lazy-string.\n\ | 
 | If the length is -1 then the length is determined by the first null\n\ | 
 | of appropriate width." }, | 
 |  | 
 |   { "lazy-string-encoding", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_lazy_string_encoding), | 
 |     "\ | 
 | Return the encoding of the lazy-string." }, | 
 |  | 
 |   { "lazy-string-type", 1, 0, 0, as_a_scm_t_subr (gdbscm_lazy_string_type), | 
 |     "\ | 
 | Return the <gdb:type> of the lazy-string." }, | 
 |  | 
 |   { "lazy-string->value", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_lazy_string_to_value), | 
 |     "\ | 
 | Return the <gdb:value> representation of the lazy-string." }, | 
 |  | 
 |   END_FUNCTIONS | 
 | }; | 
 |  | 
 | void | 
 | gdbscm_initialize_lazy_strings (void) | 
 | { | 
 |   lazy_string_smob_tag = gdbscm_make_smob_type (lazy_string_smob_name, | 
 | 						sizeof (lazy_string_smob)); | 
 |   scm_set_smob_free (lazy_string_smob_tag, lsscm_free_lazy_string_smob); | 
 |   scm_set_smob_print (lazy_string_smob_tag, lsscm_print_lazy_string_smob); | 
 |  | 
 |   gdbscm_define_functions (lazy_string_functions, 1); | 
 | } |