| /* Helper routines for D support in GDB. | 
 |  | 
 |    Copyright (C) 2014-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/>.  */ | 
 |  | 
 | #include "symtab.h" | 
 | #include "block.h" | 
 | #include "language.h" | 
 | #include "namespace.h" | 
 | #include "d-lang.h" | 
 | #include "gdbsupport/gdb_obstack.h" | 
 | #include "gdbarch.h" | 
 | #include "inferior.h" | 
 |  | 
 | /* This returns the length of first component of NAME, which should be | 
 |    the demangled name of a D variable/function/method/etc. | 
 |    Specifically, it returns the index of the first dot forming the | 
 |    boundary of the first component: so, given 'A.foo' or 'A.B.foo' | 
 |    it returns the 1, and given 'foo', it returns 0.  */ | 
 |  | 
 | /* The character in NAME indexed by the return value is guaranteed to | 
 |    always be either '.' or '\0'.  */ | 
 |  | 
 | static unsigned int | 
 | d_find_first_component (const char *name) | 
 | { | 
 |   unsigned int index = 0; | 
 |  | 
 |   for (;; ++index) | 
 |     { | 
 |       if (name[index] == '.' || name[index] == '\0') | 
 | 	return index; | 
 |     } | 
 | } | 
 |  | 
 | /* If NAME is the fully-qualified name of a D function/variable/method, | 
 |    this returns the length of its entire prefix: all of the modules and | 
 |    classes that make up its name.  Given 'A.foo', it returns 1, given | 
 |    'A.B.foo', it returns 4, given 'foo', it returns 0.  */ | 
 |  | 
 | static unsigned int | 
 | d_entire_prefix_len (const char *name) | 
 | { | 
 |   unsigned int current_len = d_find_first_component (name); | 
 |   unsigned int previous_len = 0; | 
 |  | 
 |   while (name[current_len] != '\0') | 
 |     { | 
 |       gdb_assert (name[current_len] == '.'); | 
 |       previous_len = current_len; | 
 |       /* Skip the '.'  */ | 
 |       current_len++; | 
 |       current_len += d_find_first_component (name + current_len); | 
 |     } | 
 |  | 
 |   return previous_len; | 
 | } | 
 |  | 
 | /* Look up NAME in BLOCK's static block and in global blocks. | 
 |    If SEARCH is non-zero, search through base classes for a matching | 
 |    symbol.  Other arguments are as in d_lookup_symbol_nonlocal.  */ | 
 |  | 
 | static struct block_symbol | 
 | d_lookup_symbol (const struct language_defn *langdef, | 
 | 		 const char *name, const struct block *block, | 
 | 		 const domain_search_flags domain, int search) | 
 | { | 
 |   struct block_symbol sym; | 
 |  | 
 |   sym = lookup_symbol_in_static_block (name, block, domain); | 
 |   if (sym.symbol != NULL) | 
 |     return sym; | 
 |  | 
 |   /* If we didn't find a definition for a builtin type in the static block, | 
 |      such as "ucent" which is a specialist type, search for it now.  */ | 
 |   if (langdef != nullptr && (domain & SEARCH_TYPE_DOMAIN) != 0) | 
 |     { | 
 |       struct gdbarch *gdbarch; | 
 |  | 
 |       if (block == NULL) | 
 | 	gdbarch = current_inferior ()->arch (); | 
 |       else | 
 | 	gdbarch = block->gdbarch (); | 
 |       sym.symbol | 
 | 	= language_lookup_primitive_type_as_symbol (langdef, gdbarch, name); | 
 |       sym.block = NULL; | 
 |       if (sym.symbol != NULL) | 
 | 	return sym; | 
 |     } | 
 |  | 
 |   sym = lookup_global_symbol (name, block, domain); | 
 |  | 
 |   if (sym.symbol != NULL) | 
 |     return sym; | 
 |  | 
 |   if (search) | 
 |     { | 
 |       std::string classname, nested; | 
 |       unsigned int prefix_len; | 
 |       struct block_symbol class_sym; | 
 |  | 
 |       /* A simple lookup failed.  Check if the symbol was defined in | 
 | 	 a base class.  */ | 
 |  | 
 |       /* Find the name of the class and the name of the method, | 
 | 	 variable, etc.  */ | 
 |       prefix_len = d_entire_prefix_len (name); | 
 |  | 
 |       /* If no prefix was found, search "this".  */ | 
 |       if (prefix_len == 0) | 
 | 	{ | 
 | 	  struct type *type; | 
 | 	  struct block_symbol lang_this; | 
 |  | 
 | 	  lang_this = lookup_language_this (language_def (language_d), block); | 
 | 	  if (lang_this.symbol == NULL) | 
 | 	    return {}; | 
 |  | 
 | 	  type = check_typedef (lang_this.symbol->type ()->target_type ()); | 
 | 	  classname = type->name (); | 
 | 	  nested = name; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* The class name is everything up to and including PREFIX_LEN.  */ | 
 | 	  classname = std::string (name, prefix_len); | 
 |  | 
 | 	  /* The rest of the name is everything else past the initial scope | 
 | 	     operator.  */ | 
 | 	  nested = std::string (name + prefix_len + 1); | 
 | 	} | 
 |  | 
 |       /* Lookup a class named CLASSNAME.  If none is found, there is nothing | 
 | 	 more that can be done.  */ | 
 |       class_sym = lookup_global_symbol (classname.c_str (), block, domain); | 
 |       if (class_sym.symbol == NULL) | 
 | 	return {}; | 
 |  | 
 |       /* Look for a symbol named NESTED in this class.  */ | 
 |       sym = d_lookup_nested_symbol (class_sym.symbol->type (), | 
 | 				    nested.c_str (), block); | 
 |     } | 
 |  | 
 |   return sym; | 
 | } | 
 |  | 
 | /* Look up NAME in the D module MODULE.  Other arguments are as in | 
 |    d_lookup_symbol_nonlocal.  If SEARCH is non-zero, search through | 
 |    base classes for a matching symbol.  */ | 
 |  | 
 | static struct block_symbol | 
 | d_lookup_symbol_in_module (const char *module, const char *name, | 
 | 			   const struct block *block, | 
 | 			   const domain_search_flags domain, int search) | 
 | { | 
 |   char *concatenated_name = NULL; | 
 |  | 
 |   if (module[0] != '\0') | 
 |     { | 
 |       concatenated_name | 
 | 	= (char *) alloca (strlen (module) + strlen (name) + 2); | 
 |       strcpy (concatenated_name, module); | 
 |       strcat (concatenated_name, "."); | 
 |       strcat (concatenated_name, name); | 
 |       name = concatenated_name; | 
 |     } | 
 |  | 
 |   return d_lookup_symbol (NULL, name, block, domain, search); | 
 | } | 
 |  | 
 | /* Lookup NAME at module scope.  SCOPE is the module that the current | 
 |    function is defined within; only consider modules whose length is at | 
 |    least SCOPE_LEN.  Other arguments are as in d_lookup_symbol_nonlocal. | 
 |  | 
 |    For example, if we're within a function A.B.f and looking for a | 
 |    symbol x, this will get called with NAME = "x", SCOPE = "A.B", and | 
 |    SCOPE_LEN = 0.  It then calls itself with NAME and SCOPE the same, | 
 |    but with SCOPE_LEN = 1.  And then it calls itself with NAME and | 
 |    SCOPE the same, but with SCOPE_LEN = 4.  This third call looks for | 
 |    "A.B.x"; if it doesn't find it, then the second call looks for "A.x", | 
 |    and if that call fails, then the first call looks for "x".  */ | 
 |  | 
 | static struct block_symbol | 
 | lookup_module_scope (const struct language_defn *langdef, | 
 | 		     const char *name, const struct block *block, | 
 | 		     const domain_search_flags domain, const char *scope, | 
 | 		     int scope_len) | 
 | { | 
 |   char *module; | 
 |  | 
 |   if (scope[scope_len] != '\0') | 
 |     { | 
 |       /* Recursively search for names in child modules first.  */ | 
 |  | 
 |       struct block_symbol sym; | 
 |       int new_scope_len = scope_len; | 
 |  | 
 |       /* If the current scope is followed by ".", skip past that.  */ | 
 |       if (new_scope_len != 0) | 
 | 	{ | 
 | 	  gdb_assert (scope[new_scope_len] == '.'); | 
 | 	  new_scope_len++; | 
 | 	} | 
 |       new_scope_len += d_find_first_component (scope + new_scope_len); | 
 |       sym = lookup_module_scope (langdef, name, block, domain, | 
 | 				 scope, new_scope_len); | 
 |       if (sym.symbol != NULL) | 
 | 	return sym; | 
 |     } | 
 |  | 
 |   /* Okay, we didn't find a match in our children, so look for the | 
 |      name in the current module. | 
 |  | 
 |      If we there is no scope and we know we have a bare symbol, then short | 
 |      circuit everything and call d_lookup_symbol directly. | 
 |      This isn't an optimization, rather it allows us to pass LANGDEF which | 
 |      is needed for primitive type lookup.  */ | 
 |  | 
 |   if (scope_len == 0 && strchr (name, '.') == NULL) | 
 |     return d_lookup_symbol (langdef, name, block, domain, 1); | 
 |  | 
 |   module = (char *) alloca (scope_len + 1); | 
 |   strncpy (module, scope, scope_len); | 
 |   module[scope_len] = '\0'; | 
 |   return d_lookup_symbol_in_module (module, name, | 
 | 				    block, domain, 1); | 
 | } | 
 |  | 
 | /* Search through the base classes of PARENT_TYPE for a symbol named | 
 |    NAME in block BLOCK.  */ | 
 |  | 
 | static struct block_symbol | 
 | find_symbol_in_baseclass (struct type *parent_type, const char *name, | 
 | 			  const struct block *block) | 
 | { | 
 |   struct block_symbol sym = {}; | 
 |   int i; | 
 |  | 
 |   for (i = 0; i < TYPE_N_BASECLASSES (parent_type); ++i) | 
 |     { | 
 |       struct type *base_type = TYPE_BASECLASS (parent_type, i); | 
 |       const char *base_name = TYPE_BASECLASS_NAME (parent_type, i); | 
 |  | 
 |       if (base_name == NULL) | 
 | 	continue; | 
 |  | 
 |       /* Search this particular base class.  */ | 
 |       sym = d_lookup_symbol_in_module (base_name, name, block, | 
 | 				       SEARCH_VFT, 0); | 
 |       if (sym.symbol != NULL) | 
 | 	break; | 
 |  | 
 |       /* Now search all static file-level symbols.  We have to do this for | 
 | 	 things like typedefs in the class.  First search in this symtab, | 
 | 	 what we want is possibly there.  */ | 
 |       std::string concatenated_name = std::string (base_name) + "." + name; | 
 |       sym = lookup_symbol_in_static_block (concatenated_name.c_str (), block, | 
 | 					   SEARCH_VFT); | 
 |       if (sym.symbol != NULL) | 
 | 	break; | 
 |  | 
 |       /* Nope.  We now have to search all static blocks in all objfiles, | 
 | 	 even if block != NULL, because there's no guarantees as to which | 
 | 	 symtab the symbol we want is in.  */ | 
 |       sym = lookup_static_symbol (concatenated_name.c_str (), SEARCH_VFT); | 
 |       if (sym.symbol != NULL) | 
 | 	break; | 
 |  | 
 |       /* If this class has base classes, search them next.  */ | 
 |       base_type = check_typedef (base_type); | 
 |       if (TYPE_N_BASECLASSES (base_type) > 0) | 
 | 	{ | 
 | 	  sym = find_symbol_in_baseclass (base_type, name, block); | 
 | 	  if (sym.symbol != NULL) | 
 | 	    break; | 
 | 	} | 
 |     } | 
 |  | 
 |   return sym; | 
 | } | 
 |  | 
 | /* Look up a symbol named NESTED_NAME that is nested inside the D | 
 |    class or module given by PARENT_TYPE, from within the context | 
 |    given by BLOCK.  Return NULL if there is no such nested type.  */ | 
 |  | 
 | struct block_symbol | 
 | d_lookup_nested_symbol (struct type *parent_type, | 
 | 			const char *nested_name, | 
 | 			const struct block *block) | 
 | { | 
 |   /* type_name_no_tag_required provides better error reporting using the | 
 |      original type.  */ | 
 |   struct type *saved_parent_type = parent_type; | 
 |  | 
 |   parent_type = check_typedef (parent_type); | 
 |  | 
 |   switch (parent_type->code ()) | 
 |     { | 
 |     case TYPE_CODE_STRUCT: | 
 |     case TYPE_CODE_UNION: | 
 |     case TYPE_CODE_ENUM: | 
 |     case TYPE_CODE_MODULE: | 
 | 	{ | 
 | 	  int size; | 
 | 	  const char *parent_name = type_name_or_error (saved_parent_type); | 
 | 	  struct block_symbol sym | 
 | 	    = d_lookup_symbol_in_module (parent_name, nested_name, | 
 | 					 block, SEARCH_VFT, 0); | 
 | 	  char *concatenated_name; | 
 |  | 
 | 	  if (sym.symbol != NULL) | 
 | 	    return sym; | 
 |  | 
 | 	  /* Now search all static file-level symbols.  We have to do this | 
 | 	     for things like typedefs in the class.  We do not try to | 
 | 	     guess any imported module as even the fully specified | 
 | 	     module search is already not D compliant and more assumptions | 
 | 	     could make it too magic.  */ | 
 | 	  size = strlen (parent_name) + strlen (nested_name) + 2; | 
 | 	  concatenated_name = (char *) alloca (size); | 
 |  | 
 | 	  xsnprintf (concatenated_name, size, "%s.%s", | 
 | 		     parent_name, nested_name); | 
 |  | 
 | 	  sym = lookup_static_symbol (concatenated_name, SEARCH_VFT); | 
 | 	  if (sym.symbol != NULL) | 
 | 	    return sym; | 
 |  | 
 | 	  /* If no matching symbols were found, try searching any | 
 | 	     base classes.  */ | 
 | 	  return find_symbol_in_baseclass (parent_type, nested_name, block); | 
 | 	} | 
 |  | 
 |     case TYPE_CODE_FUNC: | 
 |     case TYPE_CODE_METHOD: | 
 |       return {}; | 
 |  | 
 |     default: | 
 |       gdb_assert_not_reached ("called with non-aggregate type."); | 
 |     } | 
 | } | 
 |  | 
 | /* Search for NAME by applying all import statements belonging to | 
 |    BLOCK which are applicable in SCOPE.  */ | 
 |  | 
 | static struct block_symbol | 
 | d_lookup_symbol_imports (const char *scope, const char *name, | 
 | 			 const struct block *block, | 
 | 			 const domain_search_flags domain) | 
 | { | 
 |   struct using_direct *current; | 
 |   struct block_symbol sym; | 
 |  | 
 |   /* First, try to find the symbol in the given module.  */ | 
 |   sym = d_lookup_symbol_in_module (scope, name, block, domain, 1); | 
 |  | 
 |   if (sym.symbol != NULL) | 
 |     return sym; | 
 |  | 
 |   /* Go through the using directives.  If any of them add new names to | 
 |      the module we're searching in, see if we can find a match by | 
 |      applying them.  */ | 
 |  | 
 |   for (current = block->get_using (); | 
 |        current != NULL; | 
 |        current = current->next) | 
 |     { | 
 |       const char **excludep; | 
 |  | 
 |       /* If the import destination is the current scope then search it.  */ | 
 |       if (!current->searched && strcmp (scope, current->import_dest) == 0) | 
 | 	{ | 
 | 	  /* Mark this import as searched so that the recursive call | 
 | 	     does not search it again.  */ | 
 | 	  scoped_restore restore_searched | 
 | 	    = make_scoped_restore (¤t->searched, 1); | 
 |  | 
 | 	  /* If there is an import of a single declaration, compare the | 
 | 	     imported declaration (after optional renaming by its alias) | 
 | 	     with the sought out name.  If there is a match pass | 
 | 	     current->import_src as MODULE to direct the search towards | 
 | 	     the imported module.  */ | 
 | 	  if (current->declaration | 
 | 	      && strcmp (name, current->alias | 
 | 			 ? current->alias : current->declaration) == 0) | 
 | 	    sym = d_lookup_symbol_in_module (current->import_src, | 
 | 					     current->declaration, | 
 | 					     block, domain, 1); | 
 |  | 
 | 	  /* If a symbol was found or this import statement was an import | 
 | 	     declaration, the search of this import is complete.  */ | 
 | 	  if (sym.symbol != NULL || current->declaration) | 
 | 	    { | 
 | 	      if (sym.symbol != NULL) | 
 | 		return sym; | 
 |  | 
 | 	      continue; | 
 | 	    } | 
 |  | 
 | 	  /* Do not follow CURRENT if NAME matches its EXCLUDES.  */ | 
 | 	  for (excludep = current->excludes; *excludep; excludep++) | 
 | 	    if (strcmp (name, *excludep) == 0) | 
 | 	      break; | 
 | 	  if (*excludep) | 
 | 	    continue; | 
 |  | 
 | 	  /* If the import statement is creating an alias.  */ | 
 | 	  if (current->alias != NULL) | 
 | 	    { | 
 | 	      if (strcmp (name, current->alias) == 0) | 
 | 		{ | 
 | 		  /* If the alias matches the sought name.  Pass | 
 | 		     current->import_src as the NAME to direct the | 
 | 		     search towards the aliased module.  */ | 
 | 		  sym = lookup_module_scope (NULL, current->import_src, block, | 
 | 					     domain, scope, 0); | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  /* If the alias matches the first component of the | 
 | 		     sought name, pass current->import_src as MODULE | 
 | 		     to direct the search, skipping over the aliased | 
 | 		     component in NAME.  */ | 
 | 		  int name_scope = d_find_first_component (name); | 
 |  | 
 | 		  if (name[name_scope] != '\0' | 
 | 		      && strncmp (name, current->alias, name_scope) == 0) | 
 | 		    { | 
 | 		      /* Skip the '.'  */ | 
 | 		      name_scope++; | 
 | 		      sym = d_lookup_symbol_in_module (current->import_src, | 
 | 						       name + name_scope, | 
 | 						       block, domain, 1); | 
 | 		    } | 
 | 		} | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      /* If this import statement creates no alias, pass | 
 | 		 current->import_src as MODULE to direct the search | 
 | 		 towards the imported module.  */ | 
 | 	      sym = d_lookup_symbol_in_module (current->import_src, | 
 | 					       name, block, domain, 1); | 
 | 	    } | 
 |  | 
 | 	  if (sym.symbol != NULL) | 
 | 	    return sym; | 
 | 	} | 
 |     } | 
 |  | 
 |   return {}; | 
 | } | 
 |  | 
 | /* Searches for NAME in the current module, and by applying relevant | 
 |    import statements belonging to BLOCK and its parents.  SCOPE is the | 
 |    module scope of the context in which the search is being evaluated.  */ | 
 |  | 
 | static struct block_symbol | 
 | d_lookup_symbol_module (const char *scope, const char *name, | 
 | 			const struct block *block, | 
 | 			const domain_search_flags domain) | 
 | { | 
 |   struct block_symbol sym; | 
 |  | 
 |   /* First, try to find the symbol in the given module.  */ | 
 |   sym = d_lookup_symbol_in_module (scope, name, | 
 | 				   block, domain, 1); | 
 |   if (sym.symbol != NULL) | 
 |     return sym; | 
 |  | 
 |   /* Search for name in modules imported to this and parent | 
 |      blocks.  */ | 
 |   while (block != NULL) | 
 |     { | 
 |       sym = d_lookup_symbol_imports (scope, name, block, domain); | 
 |  | 
 |       if (sym.symbol != NULL) | 
 | 	return sym; | 
 |  | 
 |       block = block->superblock (); | 
 |     } | 
 |  | 
 |   return {}; | 
 | } | 
 |  | 
 | /* The D-specific version of name lookup for static and global names | 
 |    This makes sure that names get looked for in all modules that are | 
 |    in scope.  NAME is the natural name of the symbol that we're looking | 
 |    looking for, BLOCK is the block that we're searching within, DOMAIN | 
 |    says what kind of symbols we're looking for, and if SYMTAB is non-NULL, | 
 |    we should store the symtab where we found the symbol in it.  */ | 
 |  | 
 | struct block_symbol | 
 | d_lookup_symbol_nonlocal (const struct language_defn *langdef, | 
 | 			  const char *name, | 
 | 			  const struct block *block, | 
 | 			  const domain_search_flags domain) | 
 | { | 
 |   struct block_symbol sym; | 
 |   const char *scope = block == nullptr ? "" : block->scope (); | 
 |  | 
 |   sym = lookup_module_scope (langdef, name, block, domain, scope, 0); | 
 |   if (sym.symbol != NULL) | 
 |     return sym; | 
 |  | 
 |   return d_lookup_symbol_module (scope, name, block, domain); | 
 | } | 
 |  |