[D] Support looking up symbols in the current and imported modules.
authorIain Buclaw <ibuclaw@gdcproject.org>
Thu, 13 Aug 2015 18:34:15 +0000 (20:34 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Thu, 13 Aug 2015 19:07:09 +0000 (21:07 +0200)
In D, there is the notion of modules, and importing from one to the other,
whether it is a basic, selective or renamed import declaration.

module A;
import X;
void foo() {
  import Y : bar;
}

If the compiler emits DW_TAG_imported_declaration at the appropriate locations,
then we can make use of what gdb stores in using_direct when performing
nonlocal symbol lookups.

gdb/ChangeLog

* Makefile.in (SFILES): Add d-namespace.c.
(COMMON_OBS): Add d-namespace.o.
* d-lang.c (d_language_defn): Use d_lookup_symbol_nonlocal as the
la_lookup_symbol_nonlocal callback function pointer.
* d-lang.h (d_lookup_symbol_nonlocal): New declaration.
(d_lookup_nested_symbol): New declaration.
* d-namespace.c: New file.

gdb/ChangeLog
gdb/Makefile.in
gdb/d-lang.c
gdb/d-lang.h
gdb/d-namespace.c [new file with mode: 0644]

index 7db4487b70ace470de225bc6daec288901a2d126..920a9ddf8bb3e0aa64f4f646297c46b5d927a92b 100644 (file)
@@ -1,3 +1,13 @@
+2015-08-13  Iain Buclaw  <ibuclaw@gdcproject.org>
+
+       * Makefile.in (SFILES): Add d-namespace.c.
+       (COMMON_OBS): Add d-namespace.o.
+       * d-lang.c (d_language_defn): Use d_lookup_symbol_nonlocal as the
+       la_lookup_symbol_nonlocal callback function pointer.
+       * d-lang.h (d_lookup_symbol_nonlocal): New declaration.
+       (d_lookup_nested_symbol): New declaration.
+       * d-namespace.c: New file.
+
 2015-08-13  Pedro Alves  <palves@redhat.com>
 
        * python/py-unwind.c (pyuw_sniffer): Install the invalidate
index d8974d9077f9fad396966626dbabdb061952d7a0..13b9041af2d4a9991a0b7fa1bc9e66bc6fbd99d7 100644 (file)
@@ -838,7 +838,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
        charset.c common/cleanups.c cli-out.c coffread.c coff-pe-read.c \
        complaints.c completer.c continuations.c corefile.c corelow.c \
        cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \
-       d-exp.y d-lang.c d-valprint.c \
+       d-exp.y d-lang.c d-namespace.c d-valprint.c \
        cp-name-parser.y \
        dbxread.c demangle.c dictionary.c disasm.c doublest.c \
        dtrace-probe.c dummy-frame.c \
@@ -1072,7 +1072,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        frame-base.o \
        inline-frame.o \
        gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
-       cp-namespace.o \
+       cp-namespace.o d-namespace.o \
        reggroups.o \
        trad-frame.o \
        tramp-frame.o \
index bfb8e57ef847d33bfe98b8178d767210ea1a370d..2fc5080bee5ef935cc6d0ee3fceab00e3f2aa464 100644 (file)
@@ -214,7 +214,7 @@ static const struct language_defn d_language_defn =
   default_read_var_value,      /* la_read_var_value */
   NULL,                                /* Language specific skip_trampoline.  */
   "this",
-  basic_lookup_symbol_nonlocal, 
+  d_lookup_symbol_nonlocal,
   basic_lookup_transparent_type,
   d_demangle,                  /* Language specific symbol demangler.  */
   NULL,                                /* Language specific
index 4962a8a066e874a944d82b2edc1dd2490ff0baea..8b8b5dc4245979286fba8e89a878a8369a2daca6 100644 (file)
@@ -68,6 +68,16 @@ extern char *d_demangle (const char *mangled, int options);
 
 extern const struct builtin_d_type *builtin_d_type (struct gdbarch *);
 
+/* Defined in d-namespace.c  */
+
+extern struct block_symbol d_lookup_symbol_nonlocal (const struct language_defn *,
+                                                    const char *,
+                                                    const struct block *,
+                                                    const domain_enum);
+
+extern struct block_symbol d_lookup_nested_symbol (struct type *, const char *,
+                                                  const struct block *);
+
 /* Defined in d-valprint.c  */
 
 extern void d_val_print (struct type *type, const gdb_byte *valaddr,
diff --git a/gdb/d-namespace.c b/gdb/d-namespace.c
new file mode 100644 (file)
index 0000000..9a558d1
--- /dev/null
@@ -0,0 +1,562 @@
+/* Helper routines for D support in GDB.
+
+   Copyright (C) 2014-2015 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 "defs.h"
+#include "symtab.h"
+#include "block.h"
+#include "language.h"
+#include "namespace.h"
+#include "d-lang.h"
+#include "gdb_obstack.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 char *name, const struct block *block,
+                const domain_enum domain, int search)
+{
+  struct block_symbol sym;
+
+  sym = lookup_symbol_in_static_block (name, block, domain);
+  if (sym.symbol != NULL)
+    return sym;
+
+  sym = lookup_global_symbol (name, block, domain);
+
+  if (sym.symbol != NULL)
+    return sym;
+
+  if (search)
+    {
+      char *classname, *nested;
+      unsigned int prefix_len;
+      struct cleanup *cleanup;
+      struct block_symbol class_sym;
+
+      /* A simple lookup failed.  Check if the symbol was defined in
+        a base class.  */
+
+      cleanup = make_cleanup (null_cleanup, NULL);
+
+      /* 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)
+           {
+             do_cleanups (cleanup);
+             return (struct block_symbol) {NULL, NULL};
+           }
+
+         type = check_typedef (TYPE_TARGET_TYPE (SYMBOL_TYPE (lang_this.symbol)));
+         classname = xstrdup (TYPE_NAME (type));
+         nested = xstrdup (name);
+       }
+      else
+       {
+         /* The class name is everything up to and including PREFIX_LEN.  */
+         classname = savestring (name, prefix_len);
+
+         /* The rest of the name is everything else past the initial scope
+            operator.  */
+         nested = xstrdup (name + prefix_len + 1);
+       }
+
+      /* Add cleanups to free memory for these strings.  */
+      make_cleanup (xfree, classname);
+      make_cleanup (xfree, nested);
+
+      /* Lookup a class named CLASSNAME.  If none is found, there is nothing
+        more that can be done.  */
+      class_sym = lookup_global_symbol (classname, block, domain);
+      if (class_sym.symbol == NULL)
+       {
+         do_cleanups (cleanup);
+         return (struct block_symbol) {NULL, NULL};
+       }
+
+      /* Look for a symbol named NESTED in this class.  */
+      sym = d_lookup_nested_symbol (SYMBOL_TYPE (class_sym.symbol),
+                                   nested, block);
+      do_cleanups (cleanup);
+    }
+
+  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_enum domain, int search)
+{
+  char *concatenated_name = NULL;
+
+  if (module[0] != '\0')
+    {
+      concatenated_name = alloca (strlen (module)
+                                 + strlen (name) + 2);
+      strcpy (concatenated_name, module);
+      strcat (concatenated_name, ".");
+      strcat (concatenated_name, name);
+      name = concatenated_name;
+    }
+
+  return d_lookup_symbol (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 char *name, const struct block *block,
+                    const domain_enum 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 (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.  */
+
+  module = 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)
+{
+  char *concatenated_name = NULL;
+  struct block_symbol sym;
+  struct cleanup *cleanup;
+  int i;
+
+  sym.symbol = NULL;
+  sym.block = NULL;
+  cleanup = make_cleanup (free_current_contents, &concatenated_name);
+
+  for (i = 0; i < TYPE_N_BASECLASSES (parent_type); ++i)
+    {
+      size_t len;
+      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,
+                                      VAR_DOMAIN, 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.  */
+      len = strlen (base_name) + strlen (name) + 2;
+      concatenated_name = xrealloc (concatenated_name, len);
+      xsnprintf (concatenated_name, len, "%s.%s", base_name, name);
+      sym = lookup_symbol_in_static_block (concatenated_name, block,
+                                          VAR_DOMAIN);
+      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, VAR_DOMAIN);
+      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;
+       }
+    }
+
+  do_cleanups (cleanup);
+  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 (TYPE_CODE (parent_type))
+    {
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+    case TYPE_CODE_MODULE:
+       {
+         int size;
+         const char *parent_name = type_name_no_tag_or_error (saved_parent_type);
+         struct block_symbol sym
+           = d_lookup_symbol_in_module (parent_name, nested_name,
+                                        block, VAR_DOMAIN, 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 = alloca (size);
+
+         xsnprintf (concatenated_name, size, "%s.%s",
+                    parent_name, nested_name);
+
+         sym = lookup_static_symbol (concatenated_name, VAR_DOMAIN);
+         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 (struct block_symbol) {NULL, NULL};
+
+    default:
+      gdb_assert_not_reached ("called with non-aggregate type.");
+    }
+}
+
+/* Used for cleanups to reset the "searched" flag incase
+   of an error.  */
+
+static void
+reset_directive_searched (void *data)
+{
+  struct using_direct *direct = data;
+  direct->searched = 0;
+}
+
+/* Search for NAME by applying all import statements belonging to
+   BLOCK which are applicable in SCOPE.
+
+   If SEARCH_PARENTS the search will include imports which are
+   applicable in parents of SCOPE.
+   Example:
+
+     module A;
+     import X;
+     void B() {
+       import Y;
+     }
+
+   If SCOPE is "A.B" and SEARCH_PARENTS is true, the imports of
+   modules X and Y will be considered.  If SEARCH_PARENTS is false
+   only the import of Y is considered.  */
+
+static struct block_symbol
+d_lookup_symbol_imports (const char *scope, const char *name,
+                        const struct block *block,
+                        const domain_enum domain,
+                        const int search_parents)
+{
+  struct using_direct *current;
+  struct block_symbol sym;
+  int directive_match;
+  struct cleanup *searched_cleanup;
+
+  /* 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_using (block);
+       current != NULL;
+       current = current->next)
+    {
+      const char **excludep;
+      int len = strlen (current->import_dest);
+
+      directive_match = (search_parents
+                        ? (strncmp (scope, current->import_dest, len) == 0
+                           && (len == 0
+                               || scope[len] == '.'
+                               || scope[len] == '\0'))
+                        : strcmp (scope, current->import_dest) == 0);
+
+      /* If the import destination is the current scope or one of its
+        ancestors then it is applicable.  */
+      if (directive_match && !current->searched)
+       {
+         /* Mark this import as searched so that the recursive call
+            does not search it again.  */
+         current->searched = 1;
+         searched_cleanup = make_cleanup (reset_directive_searched,
+                                          current);
+
+         /* 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)
+           {
+             current->searched = 0;
+             discard_cleanups (searched_cleanup);
+
+             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)
+           {
+             discard_cleanups (searched_cleanup);
+             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 (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_imports (current->import_src,
+                                                    name + name_scope,
+                                                    block, domain, 0);
+                   }
+               }
+           }
+         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_imports (current->import_src,
+                                            name, block, domain, 0);
+           }
+         current->searched = 0;
+         discard_cleanups (searched_cleanup);
+
+         if (sym.symbol != NULL)
+           return sym;
+       }
+    }
+
+  return (struct block_symbol) {NULL, NULL};
+}
+
+/* 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_enum 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, 1);
+
+      if (sym.symbol != NULL)
+       return sym;
+
+      block = BLOCK_SUPERBLOCK (block);
+    }
+
+  return (struct block_symbol) {NULL, NULL};
+}
+
+/* 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_enum domain)
+{
+  struct block_symbol sym;
+  const char *scope = block_scope (block);
+
+  sym = lookup_module_scope (name, block, domain, scope, 0);
+  if (sym.symbol != NULL)
+    return sym;
+
+  return d_lookup_symbol_module (scope, name, block, domain);
+}
+