This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] D: Support looking up symbols in the current and imported modules
- From: Doug Evans <xdje42 at gmail dot com>
- To: Iain Buclaw <ibuclaw at gdcproject dot org>
- Cc: gdb-patches at sourceware dot org
- Date: Sat, 18 Jul 2015 11:31:34 -0700
- Subject: Re: [PATCH] D: Support looking up symbols in the current and imported modules
- Authentication-results: sourceware.org; auth=none
- References: <CABOHX+cNoaZq1b4Z_8upk-UuBz3kh4-ne6kqteqNzHoh+78eDQ at mail dot gmail dot com>
Iain Buclaw <ibuclaw@gdcproject.org> writes:
> Hi,
>
> D has the notion of importing modules, whether it is public, private,
> or static; basic, selective, or renamed.
>
> This adds support for looking up symbols in both the current and
> imported modules so that it is not a necessity to use the qualified
> name every time.
>
> Example:
>
> module A;
>
> import B; // Basic import
> import R = C; // Renamed import
> import D : funD; // Selective import
> import E : funR = funE; // Renamed selective import
>
> void funA()
> {
> // <- Breakpoint here
> }
>
> From the given breakpoint, the following should work in:
> - All symbols in module 'A' (our current module) can be looked up by name.
> - All symbols in module 'B' can be looked up by name.
> - All symbols in module 'C' can be looked up through it's alias R.name.
> - Only 'funD' in module 'D' can be looked up by name.
> - Only 'funE' in module 'E' can be looked up through it's alias 'funR'.
> - All fully qualified symbol names can be looked up.
>
>
> The implementation of this itself is mostly borrowed from
> cp-namespace.c, but differs in the follow ways:
> - The separator for modules is a single dot '.'
> - Renamed selective imports need special handling for D.
>
>
> This has a dependency on dwarf2read.c being able to handle language_d
> when reading/parsing module/imported declaration symbols, which has
> been raised as a separate patch.
>
> Regards
> Iain
>
> ---
> 2015-07-14 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.
Hi.
I didn't study this too much for d-language correctness.
[If there are issues I'm happy to let them get addressed
in subsequent patches.]
LGTM with the nits below fixed.
>
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -835,7 +835,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 \
> @@ -1070,7 +1070,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 \
> --- a/gdb/d-lang.c
> +++ b/gdb/d-lang.c
> @@ -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
> --- a/gdb/d-lang.h
> +++ b/gdb/d-lang.h
> @@ -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 symbol *d_lookup_symbol_nonlocal (const struct language_defn *,
> + const char *,
> + const struct block *,
> + const domain_enum);
> +
> +extern struct 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,
> --- /dev/null
> +++ b/gdb/d-namespace.c
> @@ -0,0 +1,574 @@
> +/* 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 "d-lang.h"
> +#include "cp-support.h"
Is including cp-support.h necessary?
> +#include "gdb_obstack.h"
> +
> +static struct symbol *lookup_module_scope (const char *,
> + const struct block *,
> + const domain_enum,
> + const char *, int);
> +
> +static struct symbol *lookup_symbol_file (const char *,
> + const struct block *,
> + const domain_enum, int);
> +
> +
> +/* 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;
> + }
> +}
> +
> +/* 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 symbol *
> +d_lookup_symbol_in_module (const char *module, const char *name,
> + const struct block *block,
> + const domain_enum domain, int search)
> +{
> + if (module[0] == '\0')
> + {
> + return lookup_symbol_file (name, block, domain, search);
> + }
> + else
> + {
> + char *concatenated_name = alloca (strlen (module)
> + + strlen (name) + 2);
> +
> + strcpy (concatenated_name, module);
> + strcat (concatenated_name, ".");
> + strcat (concatenated_name, name);
> + return lookup_symbol_file (concatenated_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 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 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 != 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);
> +}
> +
> +/* 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;
> +}
> +
> +/* Search through the base classes of PARENT_TYPE for a symbol named
> + NAME in block BLOCK. */
> +
> +static struct symbol *
> +find_symbol_in_baseclass (struct type *parent_type, const char *name,
> + const struct block *block)
> +{
> + int i;
> + struct symbol *sym;
> + struct cleanup *cleanup;
> + char *concatenated_name;
> +
> + sym = NULL;
> + concatenated_name = 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 != 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 != 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 != 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 != 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 symbol *
Make static.
[Or is there another patch that will use this function?
E.g., c-exp.y also uses cp_lookup_nested_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 symbol *sym
> + = d_lookup_symbol_in_module (parent_name, nested_name,
> + block, VAR_DOMAIN, 0);
> + char *concatenated_name;
> +
> + if (sym != 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 != 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 NULL;
> +
> + default:
> + internal_error (__FILE__, __LINE__,
> + _("d_lookup_nested_symbol called "
> + "on a non-aggregate type."));
gdb_assert_not_reached would be simpler.
> + }
> +}
> +
> +/* 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 symbol *
> +lookup_symbol_file (const char *name, const struct block *block,
> + const domain_enum domain, int search)
> +{
> + struct symbol *sym = NULL;
> +
> + sym = lookup_symbol_in_static_block (name, block, domain);
> + if (sym != NULL)
> + return sym;
> +
> + sym = lookup_global_symbol (name, block, domain);
> +
> + if (sym != NULL)
> + return sym;
> +
> + if (search)
> + {
> + char *classname, *nested;
> + unsigned int prefix_len;
> + struct cleanup *cleanup;
> + struct 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 symbol *this;
> +
> + this = lookup_language_this (language_def (language_d), block);
> + if (this == NULL)
> + {
> + do_cleanups (cleanup);
> + return NULL;
> + }
> +
> + type = check_typedef (TYPE_TARGET_TYPE (SYMBOL_TYPE (this)));
> + 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 == NULL)
> + {
> + do_cleanups (cleanup);
> + return NULL;
> + }
> +
> + /* Look for a symbol named NESTED in this class. */
> + sym = d_lookup_nested_symbol (SYMBOL_TYPE (class_sym), nested, block);
> + do_cleanups (cleanup);
> + }
> +
> + return sym;
> +}
> +
> +/* 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 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 symbol *sym = NULL;
> + 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 != 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 != NULL || current->declaration)
> + {
> + current->searched = 0;
> + discard_cleanups (searched_cleanup);
> +
> + if (sym != 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 != NULL)
> + return sym;
> + }
> + }
> +
> + return 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 symbol*
> +d_lookup_symbol_module (const char *scope, const char *name,
> + const struct block *block,
> + const domain_enum domain)
> +{
> + struct 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 != 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)
> + return sym;
> +
> + block = BLOCK_SUPERBLOCK (block);
> + }
> +
> + return 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 symbol *
> +d_lookup_symbol_nonlocal (const struct language_defn *langdef,
> + const char *name,
> + const struct block *block,
> + const domain_enum domain)
> +{
> + struct symbol *sym;
> + const char *scope = block_scope (block);
> +
> + sym = lookup_module_scope (name, block, domain, scope, 0);
> + if (sym != NULL)
> + return sym;
> +
> + return d_lookup_symbol_module (scope, name, block, domain);
> +}
> +