| /* GDB/Scheme smobs (gsmob is pronounced "jee smob") | 
 |  | 
 |    Copyright (C) 2014-2021 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.  */ | 
 |  | 
 | /* Smobs are Guile's "small object". | 
 |    They are used to export C structs to Scheme. | 
 |  | 
 |    Note: There's only room in the encoding space for 256, and while we won't | 
 |    come close to that, mixed with other libraries maybe someday we could. | 
 |    We don't worry about it now, except to be aware of the issue. | 
 |    We could allocate just a few smobs and use the unused smob flags field to | 
 |    specify the gdb smob kind, that is left for another day if it ever is | 
 |    needed. | 
 |  | 
 |    Some GDB smobs are "chained gsmobs".  They are used to assist with life-time | 
 |    tracking of GDB objects vs Scheme objects.  Gsmobs can "subclass" | 
 |    chained_gdb_smob, which contains a doubly-linked list to assist with | 
 |    life-time tracking. | 
 |  | 
 |    Some other GDB smobs are "eqable gsmobs".  Gsmob implementations can | 
 |    "subclass" eqable_gdb_smob to make gsmobs eq?-able.  This is done by | 
 |    recording all gsmobs in a hash table and before creating a gsmob first | 
 |    seeing if it's already in the table.  Eqable gsmobs can also be used where | 
 |    lifetime-tracking is required.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "hashtab.h" | 
 | #include "objfiles.h" | 
 | #include "guile-internal.h" | 
 |  | 
 | /* We need to call this.  Undo our hack to prevent others from calling it.  */ | 
 | #undef scm_make_smob_type | 
 |  | 
 | static htab_t registered_gsmobs; | 
 |  | 
 | /* Hash function for registered_gsmobs hash table.  */ | 
 |  | 
 | static hashval_t | 
 | hash_scm_t_bits (const void *item) | 
 | { | 
 |   uintptr_t v = (uintptr_t) item; | 
 |  | 
 |   return v; | 
 | } | 
 |  | 
 | /* Equality function for registered_gsmobs hash table.  */ | 
 |  | 
 | static int | 
 | eq_scm_t_bits (const void *item_lhs, const void *item_rhs) | 
 | { | 
 |   return item_lhs == item_rhs; | 
 | } | 
 |  | 
 | /* Record GSMOB_CODE as being a gdb smob. | 
 |    GSMOB_CODE is the result of scm_make_smob_type.  */ | 
 |  | 
 | static void | 
 | register_gsmob (scm_t_bits gsmob_code) | 
 | { | 
 |   void **slot; | 
 |  | 
 |   slot = htab_find_slot (registered_gsmobs, (void *) gsmob_code, INSERT); | 
 |   gdb_assert (*slot == NULL); | 
 |   *slot = (void *) gsmob_code; | 
 | } | 
 |  | 
 | /* Return non-zero if SCM is any registered gdb smob object.  */ | 
 |  | 
 | static int | 
 | gdbscm_is_gsmob (SCM scm) | 
 | { | 
 |   void **slot; | 
 |  | 
 |   if (SCM_IMP (scm)) | 
 |     return 0; | 
 |   slot = htab_find_slot (registered_gsmobs, (void *) SCM_TYP16 (scm), | 
 | 			 NO_INSERT); | 
 |   return slot != NULL; | 
 | } | 
 |  | 
 | /* Call this to register a smob, instead of scm_make_smob_type. | 
 |    Exports the created smob type from the current module.  */ | 
 |  | 
 | scm_t_bits | 
 | gdbscm_make_smob_type (const char *name, size_t size) | 
 | { | 
 |   scm_t_bits result = scm_make_smob_type (name, size); | 
 |  | 
 |   register_gsmob (result); | 
 |  | 
 | #if SCM_MAJOR_VERSION == 2 && SCM_MINOR_VERSION == 0 | 
 |   /* Prior to Guile 2.1.0, smob classes were only exposed via exports | 
 |      from the (oop goops) module.  */ | 
 |   SCM bound_name = scm_string_append (scm_list_3 (scm_from_latin1_string ("<"), | 
 | 						  scm_from_latin1_string (name), | 
 | 						  scm_from_latin1_string (">"))); | 
 |   bound_name = scm_string_to_symbol (bound_name); | 
 |   SCM smob_type = scm_public_ref (scm_list_2 (scm_from_latin1_symbol ("oop"), | 
 | 					      scm_from_latin1_symbol ("goops")), | 
 | 				  bound_name); | 
 | #elif SCM_MAJOR_VERSION == 2 && SCM_MINOR_VERSION == 1 && SCM_MICRO_VERSION == 0 | 
 |   /* Guile 2.1.0 doesn't provide any API for looking up smob classes. | 
 |      We could try allocating a fake instance and using scm_class_of, | 
 |      but it's probably not worth the trouble for the sake of a single | 
 |      development release.  */ | 
 | #  error "Unsupported Guile version" | 
 | #else | 
 |   /* Guile 2.1.1 and above provides scm_smob_type_class.  */ | 
 |   SCM smob_type = scm_smob_type_class (result); | 
 | #endif | 
 |  | 
 |   SCM smob_type_name = scm_class_name (smob_type); | 
 |   scm_define (smob_type_name, smob_type); | 
 |   scm_module_export (scm_current_module (), scm_list_1 (smob_type_name)); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | /* Initialize a gsmob.  */ | 
 |  | 
 | void | 
 | gdbscm_init_gsmob (gdb_smob *base) | 
 | { | 
 |   base->empty_base_class = 0; | 
 | } | 
 |  | 
 | /* Initialize a chained_gdb_smob. | 
 |    This is the same as gdbscm_init_gsmob except that it also sets prev,next | 
 |    to NULL.  */ | 
 |  | 
 | void | 
 | gdbscm_init_chained_gsmob (chained_gdb_smob *base) | 
 | { | 
 |   gdbscm_init_gsmob ((gdb_smob *) base); | 
 |   base->prev = NULL; | 
 |   base->next = NULL; | 
 | } | 
 |  | 
 | /* Initialize an eqable_gdb_smob. | 
 |    This is the same as gdbscm_init_gsmob except that it also sets | 
 |    BASE->containing_scm to CONTAINING_SCM.  */ | 
 |  | 
 | void | 
 | gdbscm_init_eqable_gsmob (eqable_gdb_smob *base, SCM containing_scm) | 
 | { | 
 |   gdbscm_init_gsmob ((gdb_smob *) base); | 
 |   base->containing_scm = containing_scm; | 
 | } | 
 |  | 
 |  | 
 | /* gsmob accessors */ | 
 |  | 
 | /* Return the gsmob in SELF. | 
 |    Throws an exception if SELF is not a gsmob.  */ | 
 |  | 
 | static SCM | 
 | gsscm_get_gsmob_arg_unsafe (SCM self, int arg_pos, const char *func_name) | 
 | { | 
 |   SCM_ASSERT_TYPE (gdbscm_is_gsmob (self), self, arg_pos, func_name, | 
 | 		   _("any gdb smob")); | 
 |  | 
 |   return self; | 
 | } | 
 |  | 
 | /* (gdb-object-kind gsmob) -> symbol | 
 |  | 
 |    Note: While one might want to name this gdb-object-class-name, it is named | 
 |    "-kind" because smobs aren't real GOOPS classes.  */ | 
 |  | 
 | static SCM | 
 | gdbscm_gsmob_kind (SCM self) | 
 | { | 
 |   SCM smob, result; | 
 |   scm_t_bits smobnum; | 
 |   const char *name; | 
 |  | 
 |   smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |  | 
 |   smobnum = SCM_SMOBNUM (smob); | 
 |   name = SCM_SMOBNAME (smobnum); | 
 |   gdb::unique_xmalloc_ptr<char> kind = xstrprintf ("<%s>", name); | 
 |   result = scm_from_latin1_symbol (kind.get ()); | 
 |   return result; | 
 | } | 
 |  | 
 |  | 
 | /* When underlying gdb data structures are deleted, we need to update any | 
 |    smobs with references to them.  There are several smobs that reference | 
 |    objfile-based data, so we provide helpers to manage this.  */ | 
 |  | 
 | /* Add G_SMOB to the reference chain for OBJFILE specified by DATA_KEY. | 
 |    OBJFILE may be NULL, in which case just set prev,next to NULL.  */ | 
 |  | 
 | void | 
 | gdbscm_add_objfile_ref (struct objfile *objfile, | 
 | 			const struct objfile_data *data_key, | 
 | 			chained_gdb_smob *g_smob) | 
 | { | 
 |   g_smob->prev = NULL; | 
 |   if (objfile != NULL) | 
 |     { | 
 |       g_smob->next = (chained_gdb_smob *) objfile_data (objfile, data_key); | 
 |       if (g_smob->next) | 
 | 	g_smob->next->prev = g_smob; | 
 |       set_objfile_data (objfile, data_key, g_smob); | 
 |     } | 
 |   else | 
 |     g_smob->next = NULL; | 
 | } | 
 |  | 
 | /* Remove G_SMOB from the reference chain for OBJFILE specified | 
 |    by DATA_KEY.  OBJFILE may be NULL.  */ | 
 |  | 
 | void | 
 | gdbscm_remove_objfile_ref (struct objfile *objfile, | 
 | 			   const struct objfile_data *data_key, | 
 | 			   chained_gdb_smob *g_smob) | 
 | { | 
 |   if (g_smob->prev) | 
 |     g_smob->prev->next = g_smob->next; | 
 |   else if (objfile != NULL) | 
 |     set_objfile_data (objfile, data_key, g_smob->next); | 
 |   if (g_smob->next) | 
 |     g_smob->next->prev = g_smob->prev; | 
 | } | 
 |  | 
 | /* Create a hash table for mapping a pointer to a gdb data structure to the | 
 |    gsmob that wraps it.  */ | 
 |  | 
 | htab_t | 
 | gdbscm_create_eqable_gsmob_ptr_map (htab_hash hash_fn, htab_eq eq_fn) | 
 | { | 
 |   htab_t htab = htab_create_alloc (7, hash_fn, eq_fn, | 
 | 				   NULL, xcalloc, xfree); | 
 |  | 
 |   return htab; | 
 | } | 
 |  | 
 | /* Return a pointer to the htab entry for the eq?-able gsmob BASE. | 
 |    If the entry is found, *SLOT is non-NULL. | 
 |    Otherwise *slot is NULL.  */ | 
 |  | 
 | eqable_gdb_smob ** | 
 | gdbscm_find_eqable_gsmob_ptr_slot (htab_t htab, eqable_gdb_smob *base) | 
 | { | 
 |   void **slot = htab_find_slot (htab, base, INSERT); | 
 |  | 
 |   return (eqable_gdb_smob **) slot; | 
 | } | 
 |  | 
 | /* Record BASE in SLOT.  SLOT must be the result of calling | 
 |    gdbscm_find_eqable_gsmob_ptr_slot on BASE (or equivalent for lookup).  */ | 
 |  | 
 | void | 
 | gdbscm_fill_eqable_gsmob_ptr_slot (eqable_gdb_smob **slot, | 
 | 				   eqable_gdb_smob *base) | 
 | { | 
 |   *slot = base; | 
 | } | 
 |  | 
 | /* Remove BASE from HTAB. | 
 |    BASE is a pointer to a gsmob that wraps a pointer to a GDB datum. | 
 |    This is used, for example, when an object is freed. | 
 |  | 
 |    It is an error to call this if PTR is not in HTAB (only because it allows | 
 |    for some consistency checking).  */ | 
 |  | 
 | void | 
 | gdbscm_clear_eqable_gsmob_ptr_slot (htab_t htab, eqable_gdb_smob *base) | 
 | { | 
 |   void **slot = htab_find_slot (htab, base, NO_INSERT); | 
 |  | 
 |   gdb_assert (slot != NULL); | 
 |   htab_clear_slot (htab, slot); | 
 | } | 
 |  | 
 | /* Initialize the Scheme gsmobs code.  */ | 
 |  | 
 | static const scheme_function gsmob_functions[] = | 
 | { | 
 |   /* N.B. There is a general rule of not naming symbols in gdb-guile with a | 
 |      "gdb" prefix.  This symbol does not violate this rule because it is to | 
 |      be read as "gdb-object-foo", not "gdb-foo".  */ | 
 |   { "gdb-object-kind", 1, 0, 0, as_a_scm_t_subr (gdbscm_gsmob_kind), | 
 |     "\ | 
 | Return the kind of the GDB object, e.g., <gdb:breakpoint>, as a symbol." }, | 
 |  | 
 |   END_FUNCTIONS | 
 | }; | 
 |  | 
 | void | 
 | gdbscm_initialize_smobs (void) | 
 | { | 
 |   registered_gsmobs = htab_create_alloc (10, | 
 | 					 hash_scm_t_bits, eq_scm_t_bits, | 
 | 					 NULL, xcalloc, xfree); | 
 |  | 
 |   gdbscm_define_functions (gsmob_functions, 1); | 
 | } |