2016-04-27 03:38:08 +02:00
|
|
|
|
/* Rust language support routines for GDB, the GNU debugger.
|
|
|
|
|
|
2018-01-01 05:43:02 +01:00
|
|
|
|
Copyright (C) 2016-2018 Free Software Foundation, Inc.
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
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 <ctype.h>
|
|
|
|
|
|
|
|
|
|
#include "block.h"
|
|
|
|
|
#include "c-lang.h"
|
|
|
|
|
#include "charset.h"
|
|
|
|
|
#include "cp-support.h"
|
2016-05-26 23:04:07 +02:00
|
|
|
|
#include "demangle.h"
|
2016-04-27 03:38:08 +02:00
|
|
|
|
#include "gdbarch.h"
|
|
|
|
|
#include "infcall.h"
|
|
|
|
|
#include "objfiles.h"
|
Handle dereferencing Rust trait objects
In Rust, virtual tables work a bit differently than they do in C++. In
C++, as you know, they are connected to a particular class hierarchy.
Rust, instead, can generate a virtual table for potentially any type --
in fact, one such virtual table for each trait (a trait is similar to an
abstract class or to a Java interface) that a type implements.
Objects that are referenced via a trait can't currently be inspected by
gdb. This patch implements the Rust equivalent of "set print object".
gdb relies heavily on the C++ ABI to decode virtual tables; primarily to
make "set print object" work; but also "info vtbl". However, Rust does
not currently have a specified ABI, so this approach seems unwise to
emulate.
Instead, I've changed the Rust compiler to emit some DWARF that
describes trait objects (previously their internal structure was
opaque), vtables (currently just a size -- but I hope to expand this in
the future), and the concrete type for which a vtable was emitted.
The concrete type is expressed as a DW_AT_containing_type on the
vtable's type. This is a small extension to DWARF.
This patch adds a new entry to quick_symbol_functions to return the
symtab that holds a data address. Previously there was no way in gdb to
look up a full (only minimal) non-text symbol by address. The psymbol
implementation of this method works by lazily filling in a map that is
added to the objfile. This avoids slowing down psymbol reading for a
feature that is likely to not be used too frequently.
I did not update .gdb_index. My thinking here is that the DWARF 5
indices will obsolete .gdb_index soon-ish, meaning that adding a new
feature to them is probably wasted work. If necessary I can update the
DWARF 5 index code when it lands in gdb.
Regression tested on x86-64 Fedora 25.
2017-11-17 Tom Tromey <tom@tromey.com>
* symtab.h (struct symbol) <is_rust_vtable>: New member.
(struct rust_vtable_symbol): New.
(find_symbol_at_address): Declare.
* symtab.c (find_symbol_at_address): New function.
* symfile.h (struct quick_symbol_functions)
<find_compunit_symtab_by_address>: New member.
* symfile-debug.c (debug_qf_find_compunit_symtab_by_address): New
function.
(debug_sym_quick_functions): Link to
debug_qf_find_compunit_symtab_by_address.
* rust-lang.c (rust_get_trait_object_pointer): New function.
(rust_evaluate_subexp) <case UNOP_IND>: New case. Call
rust_get_trait_object_pointer.
* psymtab.c (psym_relocate): Clear psymbol_map.
(psym_fill_psymbol_map, psym_find_compunit_symtab_by_address): New
functions.
(psym_functions): Link to psym_find_compunit_symtab_by_address.
* objfiles.h (struct objfile) <psymbol_map>: New member.
* dwarf2read.c (dwarf2_gdb_index_functions): Update.
(process_die) <DW_TAG_variable>: New case. Call read_variable.
(rust_containing_type, read_variable): New functions.
2017-11-17 Tom Tromey <tom@tromey.com>
* gdb.rust/traits.rs: New file.
* gdb.rust/traits.exp: New file.
2017-07-06 14:44:38 +02:00
|
|
|
|
#include "psymtab.h"
|
2016-04-27 03:38:08 +02:00
|
|
|
|
#include "rust-lang.h"
|
|
|
|
|
#include "valprint.h"
|
|
|
|
|
#include "varobj.h"
|
2016-09-22 17:51:20 +02:00
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
/* Returns the last segment of a Rust path like foo::bar::baz. Will
|
|
|
|
|
not handle cases where the last segment contains generics. This
|
|
|
|
|
will return NULL if the last segment cannot be found. */
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
rust_last_path_segment (const char * path)
|
|
|
|
|
{
|
|
|
|
|
const char *result = strrchr (path, ':');
|
|
|
|
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
return result + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-03 04:58:12 +01:00
|
|
|
|
/* See rust-lang.h. */
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 04:58:12 +01:00
|
|
|
|
std::string
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_crate_for_block (const struct block *block)
|
|
|
|
|
{
|
|
|
|
|
const char *scope = block_scope (block);
|
|
|
|
|
|
|
|
|
|
if (scope[0] == '\0')
|
2017-02-03 04:58:12 +01:00
|
|
|
|
return std::string ();
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 04:58:12 +01:00
|
|
|
|
return std::string (scope, cp_find_first_component (scope));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Information about the discriminant/variant of an enum */
|
|
|
|
|
|
|
|
|
|
struct disr_info
|
|
|
|
|
{
|
2016-11-08 16:26:46 +01:00
|
|
|
|
/* Name of field. */
|
|
|
|
|
std::string name;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
/* Field number in union. Negative on error. For an encoded enum,
|
|
|
|
|
the "hidden" member will always be field 1, and the "real" member
|
|
|
|
|
will always be field 0. */
|
|
|
|
|
int field_no;
|
|
|
|
|
/* True if this is an encoded enum that has a single "real" member
|
|
|
|
|
and a single "hidden" member. */
|
|
|
|
|
unsigned int is_encoded : 1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* The prefix of a specially-encoded enum. */
|
|
|
|
|
|
|
|
|
|
#define RUST_ENUM_PREFIX "RUST$ENCODED$ENUM$"
|
|
|
|
|
|
|
|
|
|
/* The number of the real field. */
|
|
|
|
|
|
|
|
|
|
#define RUST_ENCODED_ENUM_REAL 0
|
|
|
|
|
|
|
|
|
|
/* The number of the hidden field. */
|
|
|
|
|
|
|
|
|
|
#define RUST_ENCODED_ENUM_HIDDEN 1
|
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
/* Whether or not a TYPE_CODE_UNION value is an untagged union
|
|
|
|
|
as opposed to being a regular Rust enum. */
|
|
|
|
|
static bool
|
|
|
|
|
rust_union_is_untagged (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
/* Unions must have at least one field. */
|
|
|
|
|
if (TYPE_NFIELDS (type) == 0)
|
|
|
|
|
return false;
|
|
|
|
|
/* If the first field is named, but the name has the rust enum prefix,
|
2017-02-03 05:01:11 +01:00
|
|
|
|
it is an enum. */
|
2016-10-29 03:00:43 +02:00
|
|
|
|
if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
|
|
|
|
|
strlen (RUST_ENUM_PREFIX)) == 0)
|
|
|
|
|
return false;
|
|
|
|
|
/* Unions only have named fields. */
|
|
|
|
|
for (int i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (strlen (TYPE_FIELD_NAME (type, i)) == 0)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
/* Utility function to get discriminant info for a given value. */
|
|
|
|
|
|
|
|
|
|
static struct disr_info
|
|
|
|
|
rust_get_disr_info (struct type *type, const gdb_byte *valaddr,
|
|
|
|
|
int embedded_offset, CORE_ADDR address,
|
2016-11-08 17:02:42 +01:00
|
|
|
|
struct value *val)
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct disr_info ret;
|
|
|
|
|
struct type *disr_type;
|
|
|
|
|
struct value_print_options opts;
|
|
|
|
|
const char *name_segment;
|
|
|
|
|
|
|
|
|
|
get_no_prettyformat_print_options (&opts);
|
|
|
|
|
|
|
|
|
|
ret.field_no = -1;
|
|
|
|
|
ret.is_encoded = 0;
|
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (type) == 0)
|
|
|
|
|
error (_("Encountered void enum value"));
|
|
|
|
|
|
|
|
|
|
/* If an enum has two values where one is empty and the other holds
|
|
|
|
|
a pointer that cannot be zero; then the Rust compiler optimizes
|
|
|
|
|
away the discriminant and instead uses a zero value in the
|
|
|
|
|
pointer field to indicate the empty variant. */
|
|
|
|
|
if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
|
|
|
|
|
strlen (RUST_ENUM_PREFIX)) == 0)
|
|
|
|
|
{
|
2016-11-09 04:32:50 +01:00
|
|
|
|
char *tail, *token, *saveptr = NULL;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
unsigned long fieldno;
|
|
|
|
|
struct type *member_type;
|
|
|
|
|
LONGEST value;
|
|
|
|
|
|
|
|
|
|
ret.is_encoded = 1;
|
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (type) != 1)
|
|
|
|
|
error (_("Only expected one field in %s type"), RUST_ENUM_PREFIX);
|
|
|
|
|
|
2016-06-25 07:39:23 +02:00
|
|
|
|
/* Optimized enums have only one field. */
|
|
|
|
|
member_type = TYPE_FIELD_TYPE (type, 0);
|
|
|
|
|
|
2017-02-03 04:58:12 +01:00
|
|
|
|
std::string name (TYPE_FIELD_NAME (type, 0));
|
|
|
|
|
tail = &name[0] + strlen (RUST_ENUM_PREFIX);
|
2016-06-25 07:39:23 +02:00
|
|
|
|
|
|
|
|
|
/* The location of the value that doubles as a discriminant is
|
|
|
|
|
stored in the name of the field, as
|
|
|
|
|
RUST$ENCODED$ENUM$<fieldno>$<fieldno>$...$<variantname>
|
|
|
|
|
where the fieldnos are the indices of the fields that should be
|
|
|
|
|
traversed in order to find the field (which may be several fields deep)
|
|
|
|
|
and the variantname is the name of the variant of the case when the
|
|
|
|
|
field is zero. */
|
2016-06-29 12:12:28 +02:00
|
|
|
|
for (token = strtok_r (tail, "$", &saveptr);
|
|
|
|
|
token != NULL;
|
|
|
|
|
token = strtok_r (NULL, "$", &saveptr))
|
2016-06-25 07:39:23 +02:00
|
|
|
|
{
|
2016-06-25 16:41:45 +02:00
|
|
|
|
if (sscanf (token, "%lu", &fieldno) != 1)
|
|
|
|
|
{
|
|
|
|
|
/* We have reached the enum name, which cannot start
|
|
|
|
|
with a digit. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-06-25 07:39:23 +02:00
|
|
|
|
if (fieldno >= TYPE_NFIELDS (member_type))
|
2016-06-25 16:41:45 +02:00
|
|
|
|
error (_("%s refers to field after end of member type"),
|
|
|
|
|
RUST_ENUM_PREFIX);
|
2016-06-25 07:39:23 +02:00
|
|
|
|
|
|
|
|
|
embedded_offset += TYPE_FIELD_BITPOS (member_type, fieldno) / 8;
|
|
|
|
|
member_type = TYPE_FIELD_TYPE (member_type, fieldno);
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-29 12:12:28 +02:00
|
|
|
|
if (token == NULL)
|
2016-04-27 03:38:08 +02:00
|
|
|
|
error (_("Invalid form for %s"), RUST_ENUM_PREFIX);
|
2016-06-25 16:41:45 +02:00
|
|
|
|
value = unpack_long (member_type, valaddr + embedded_offset);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
if (value == 0)
|
|
|
|
|
{
|
|
|
|
|
ret.field_no = RUST_ENCODED_ENUM_HIDDEN;
|
2016-11-08 16:26:46 +01:00
|
|
|
|
ret.name = std::string (TYPE_NAME (type)) + "::" + token;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret.field_no = RUST_ENCODED_ENUM_REAL;
|
2016-11-08 16:26:46 +01:00
|
|
|
|
ret.name = (std::string (TYPE_NAME (type)) + "::"
|
|
|
|
|
+ rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (type, 0))));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
disr_type = TYPE_FIELD_TYPE (type, 0);
|
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (disr_type) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* This is a bounds check and should never be hit unless Rust
|
|
|
|
|
has changed its debuginfo format. */
|
|
|
|
|
error (_("Could not find enum discriminant field"));
|
|
|
|
|
}
|
2016-10-28 01:46:34 +02:00
|
|
|
|
else if (TYPE_NFIELDS (type) == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Sometimes univariant enums are encoded without a
|
|
|
|
|
discriminant. In that case, treating it as an encoded enum
|
|
|
|
|
with the first field being the actual type works. */
|
|
|
|
|
const char *field_name = TYPE_NAME (TYPE_FIELD_TYPE (type, 0));
|
|
|
|
|
const char *last = rust_last_path_segment (field_name);
|
2016-11-09 04:22:29 +01:00
|
|
|
|
ret.name = std::string (TYPE_NAME (type)) + "::" + last;
|
2016-10-28 01:46:34 +02:00
|
|
|
|
ret.field_no = RUST_ENCODED_ENUM_REAL;
|
|
|
|
|
ret.is_encoded = 1;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (disr_type, 0), "RUST$ENUM$DISR") != 0)
|
|
|
|
|
error (_("Rust debug format has changed"));
|
|
|
|
|
|
Eliminate make_cleanup_ui_file_delete / make ui_file a class hierarchy
This patch starts from the desire to eliminate
make_cleanup_ui_file_delete, but then goes beyond. It makes ui_file &
friends a real C++ class hierarchy, and switches temporary
ui_file-like objects to stack-based allocation.
- mem_fileopen -> string_file
mem_fileopen is replaced with a new string_file class that is treated
as a value class created on the stack. This alone eliminates most
make_cleanup_ui_file_delete calls, and, simplifies code a whole lot
(diffstat shows around 1k loc dropped.)
string_file's internal buffer is a std::string, thus the "string" in
the name. This simplifies the implementation much, compared to
mem_fileopen, which managed growing its internal buffer manually.
- ui_file_as_string, ui_file_strdup, ui_file_obsavestring all gone
The new string_file class has a string() method that provides direct
writable access to the internal std::string buffer. This replaced
ui_file_as_string, which forced a copy of the same data the stream had
inside. With direct access via a writable reference, we can instead
move the string out of the string_stream, avoiding deep string
copying.
Related, ui_file_xstrdup calls are replaced with xstrdup'ping the
stream's string, and ui_file_obsavestring is replaced by
obstack_copy0.
With all those out of the way, getting rid of the weird ui_file_put
mechanism was possible.
- New ui_file::printf, ui_file::puts, etc. methods
These simplify / clarify client code. I considered splitting
client-code changes, like these, e.g.:
- stb = mem_fileopen ();
- fprintf_unfiltered (stb, "%s%s%s",
- _("The valid values are:\n"),
- regdesc,
- _("The default is \"std\"."));
+ string_file stb;
+ stb.printf ("%s%s%s",
+ _("The valid values are:\n"),
+ regdesc,
+ _("The default is \"std\"."));
In two steps, with the first step leaving fprintf_unfiltered (etc.)
calls in place, and only afterwards do a pass to change all those to
call stb.printf etc.. I didn't do that split, because (when I tried),
it turned out to be pointless make-work: the first pass would have to
touch the fprintf_unfiltered line anyway, to replace "stb" with
"&stb".
- gdb_fopen replaced with stack-based objects
This avoids the need for cleanups or unique_ptr's. I.e., this:
struct ui_file *file = gdb_fopen (filename, "w");
if (filename == NULL)
perror_with_name (filename);
cleanups = make_cleanup_ui_file_delete (file);
// use file.
do_cleanups (cleanups);
is replaced with this:
stdio_file file;
if (!file.open (filename, "w"))
perror_with_name (filename);
// use file.
- odd contorsions in null_file_write / null_file_fputs around when to
call to_fputs / to_write eliminated.
- Global null_stream object
A few places that were allocating a ui_file in order to print to
"nowhere" are adjusted to instead refer to a new 'null_stream' global
stream.
- TUI's tui_sfileopen eliminated. TUI's ui_file much simplified
The TUI's ui_file was serving a dual purpose. It supported being used
as string buffer, and supported being backed by a stdio FILE. The
string buffer part is gone, replaced by using of string_file. The
'FILE *' support is now much simplified, by making the TUI's ui_file
inherit from stdio_file.
gdb/ChangeLog:
2017-02-02 Pedro Alves <palves@redhat.com>
* ada-lang.c (type_as_string): Use string_file.
* ada-valprint.c (ada_print_floating): Use string_file.
* ada-varobj.c (ada_varobj_scalar_image)
(ada_varobj_get_value_image): Use string_file.
* aix-thread.c (aix_thread_extra_thread_info): Use string_file.
* arm-tdep.c (_initialize_arm_tdep): Use string_printf.
* breakpoint.c (update_inserted_breakpoint_locations)
(insert_breakpoint_locations, reattach_breakpoints)
(print_breakpoint_location, print_one_detail_ranged_breakpoint)
(print_it_watchpoint): Use string_file.
(save_breakpoints): Use stdio_file.
* c-exp.y (oper): Use string_file.
* cli/cli-logging.c (set_logging_redirect): Use ui_file_up and
tee_file.
(pop_output_files): Use delete.
(handle_redirections): Use stdio_file and tee_file.
* cli/cli-setshow.c (do_show_command): Use string_file.
* compile/compile-c-support.c (c_compute_program): Use
string_file.
* compile/compile-c-symbols.c (generate_vla_size): Take a
'string_file &' instead of a 'ui_file *'.
(generate_c_for_for_one_variable): Take a 'string_file &' instead
of a 'ui_file *'. Use string_file.
(generate_c_for_variable_locations): Take a 'string_file &'
instead of a 'ui_file *'.
* compile/compile-internal.h (generate_c_for_for_one_variable):
Take a 'string_file &' instead of a 'ui_file *'.
* compile/compile-loc2c.c (push, pushf, unary, binary)
(print_label, pushf_register_address, pushf_register)
(do_compile_dwarf_expr_to_c): Take a 'string_file &' instead of a
'ui_file *'. Adjust.
* compile/compile.c (compile_to_object): Use string_file.
* compile/compile.h (compile_dwarf_expr_to_c)
(compile_dwarf_bounds_to_c): Take a 'string_file &' instead of a
'ui_file *'.
* cp-support.c (inspect_type): Use string_file and obstack_copy0.
(replace_typedefs_qualified_name): Use string_file and
obstack_copy0.
* disasm.c (gdb_pretty_print_insn): Use string_file.
(gdb_disassembly): Adjust reference the null_stream global.
(do_ui_file_delete): Delete.
(gdb_insn_length): Use null_stream.
* dummy-frame.c (maintenance_print_dummy_frames): Use stdio_file.
* dwarf2loc.c (dwarf2_compile_property_to_c)
(locexpr_generate_c_location, loclist_generate_c_location): Take a
'string_file &' instead of a 'ui_file *'.
* dwarf2loc.h (dwarf2_compile_property_to_c): Likewise.
* dwarf2read.c (do_ui_file_peek_last): Delete.
(dwarf2_compute_name): Use string_file.
* event-top.c (gdb_setup_readline): Use stdio_file.
* gdbarch.sh (verify_gdbarch): Use string_file.
* gdbtypes.c (safe_parse_type): Use null_stream.
* guile/scm-breakpoint.c (gdbscm_breakpoint_commands): Use
string_file.
* guile/scm-disasm.c (gdbscm_print_insn_from_port): Take a
'string_file *' instead of a 'ui_file *'.
(gdbscm_arch_disassemble): Use string_file.
* guile/scm-frame.c (frscm_print_frame_smob): Use string_file.
* guile/scm-ports.c (class ioscm_file_port): Now a class that
inherits from ui_file.
(ioscm_file_port_delete, ioscm_file_port_rewind)
(ioscm_file_port_put): Delete.
(ioscm_file_port_write): Rename to ...
(ioscm_file_port::write): ... this. Remove file_port_magic
checks.
(ioscm_file_port_new): Delete.
(ioscm_with_output_to_port_worker): Use ioscm_file_port and
ui_file_up.
* guile/scm-type.c (tyscm_type_name): Use string_file.
* guile/scm-value.c (vlscm_print_value_smob, gdbscm_value_print):
Use string_file.
* infcmd.c (print_return_value_1): Use string_file.
* infrun.c (print_target_wait_results): Use string_file.
* language.c (add_language): Use string_file.
* location.c (explicit_to_string_internal): Use string_file.
* main.c (captured_main_1): Use null_file.
* maint.c (maintenance_print_architecture): Use stdio_file.
* mi/mi-cmd-stack.c (list_arg_or_local): Use string_file.
* mi/mi-common.h (struct mi_interp) <out, err, log, targ,
event_channel>: Change type to mi_console_file pointer.
* mi/mi-console.c (mi_console_file_fputs, mi_console_file_flush)
(mi_console_file_delete): Delete.
(struct mi_console_file): Delete.
(mi_console_file_magic): Delete.
(mi_console_file_new): Delete.
(mi_console_file::mi_console_file): New.
(mi_console_file_delete): Delete.
(mi_console_file_fputs): Delete.
(mi_console_file::write): New.
(mi_console_raw_packet): Delete.
(mi_console_file::flush): New.
(mi_console_file_flush): Delete.
(mi_console_set_raw): Rename to ...
(mi_console_file::set_raw): ... this.
* mi/mi-console.h (class mi_console_file): New class.
(mi_console_file_new, mi_console_set_raw): Delete.
* mi/mi-interp.c (mi_interpreter_init): Use mi_console_file.
(mi_set_logging): Use delete and tee_file. Adjust.
* mi/mi-main.c (output_register): Use string_file.
(mi_cmd_data_evaluate_expression): Use string_file.
(mi_cmd_data_read_memory): Use string_file.
(mi_cmd_execute, print_variable_or_computed): Use string_file.
* mi/mi-out.c (mi_ui_out::main_stream): New.
(mi_ui_out::rewind): Use main_stream and
string_file.
(mi_ui_out::put): Use main_stream and string_file.
(mi_ui_out::mi_ui_out): Remove 'stream' parameter.
Allocate a 'string_file' instead.
(mi_out_new): Don't allocate a mem_fileopen stream here.
* mi/mi-out.h (mi_ui_out::mi_ui_out): Remove 'stream' parameter.
(mi_ui_out::main_stream): Declare method.
* printcmd.c (eval_command): Use string_file.
* psymtab.c (maintenance_print_psymbols): Use stdio_file.
* python/py-arch.c (archpy_disassemble): Use string_file.
* python/py-breakpoint.c (bppy_get_commands): Use string_file.
* python/py-frame.c (frapy_str): Use string_file.
* python/py-framefilter.c (py_print_type, py_print_single_arg):
Use string_file.
* python/py-type.c (typy_str): Use string_file.
* python/py-unwind.c (unwind_infopy_str): Use string_file.
* python/py-value.c (valpy_str): Use string_file.
* record-btrace.c (btrace_insn_history): Use string_file.
* regcache.c (regcache_print): Use stdio_file.
* reggroups.c (maintenance_print_reggroups): Use stdio_file.
* remote.c (escape_buffer): Use string_file.
* rust-lang.c (rust_get_disr_info): Use string_file.
* serial.c (serial_open_ops_1): Use stdio_file.
(do_serial_close): Use delete.
* stack.c (print_frame_arg): Use string_file.
(print_frame_args): Remove local mem_fileopen stream, not used.
(print_frame): Use string_file.
* symmisc.c (maintenance_print_symbols): Use stdio_file.
* symtab.h (struct symbol_computed_ops) <generate_c_location>:
Take a 'string_file *' instead of a 'ui_file *'.
* top.c (new_ui): Use stdio_file and stderr_file.
(free_ui): Use delete.
(execute_command_to_string): Use string_file.
(quit_confirm): Use string_file.
* tracepoint.c (collection_list::append_exp): Use string_file.
* tui/tui-disasm.c (tui_disassemble): Use string_file.
* tui/tui-file.c: Don't include "ui-file.h".
(enum streamtype, struct tui_stream): Delete.
(tui_file_new, tui_file_delete, tui_fileopen, tui_sfileopen)
(tui_file_isatty, tui_file_rewind, tui_file_put): Delete.
(tui_file::tui_file): New method.
(tui_file_fputs): Delete.
(tui_file_get_strbuf): Delete.
(tui_file::puts): New method.
(tui_file_adjust_strbuf): Delete.
(tui_file_flush): Delete.
(tui_file::flush): New method.
* tui/tui-file.h: Tweak intro comment.
Include ui-file.h.
(tui_fileopen, tui_sfileopen, tui_file_get_strbuf)
(tui_file_adjust_strbuf): Delete declarations.
(class tui_file): New class.
* tui/tui-io.c (tui_initialize_io): Use tui_file.
* tui/tui-regs.c (tui_restore_gdbout): Use delete.
(tui_register_format): Use string_stream.
* tui/tui-stack.c (tui_make_status_line): Use string_file.
(tui_get_function_from_frame): Use string_file.
* typeprint.c (type_to_string): Use string_file.
* ui-file.c (struct ui_file, ui_file_magic, ui_file_new): Delete.
(null_stream): New global.
(ui_file_delete): Delete.
(ui_file::ui_file): New.
(null_file_isatty): Delete.
(ui_file::~ui_file): New.
(null_file_rewind): Delete.
(ui_file::printf): New.
(null_file_put): Delete.
(null_file_flush): Delete.
(ui_file::putstr): New.
(null_file_write): Delete.
(ui_file::putstrn): New.
(null_file_read): Delete.
(ui_file::putc): New.
(null_file_fputs): Delete.
(null_file_write_async_safe): Delete.
(ui_file::vprintf): New.
(null_file_delete): Delete.
(null_file::write): New.
(null_file_fseek): Delete.
(null_file::puts): New.
(ui_file_data): Delete.
(null_file::write_async_safe): New.
(gdb_flush, ui_file_isatty): Adjust.
(ui_file_put, ui_file_rewind): Delete.
(ui_file_write): Adjust.
(ui_file_write_for_put): Delete.
(ui_file_write_async_safe, ui_file_read): Adjust.
(ui_file_fseek): Delete.
(fputs_unfiltered): Adjust.
(set_ui_file_flush, set_ui_file_isatty, set_ui_file_rewind)
(set_ui_file_put, set_ui_file_write, set_ui_file_write_async_safe)
(set_ui_file_read, set_ui_file_fputs, set_ui_file_fseek)
(set_ui_file_data): Delete.
(string_file::~string_file, string_file::write)
(struct accumulated_ui_file, do_ui_file_xstrdup, ui_file_xstrdup)
(do_ui_file_as_string, ui_file_as_string): Delete.
(do_ui_file_obsavestring, ui_file_obsavestring): Delete.
(struct mem_file): Delete.
(mem_file_new): Delete.
(stdio_file::stdio_file): New.
(mem_file_delete): Delete.
(stdio_file::stdio_file): New.
(mem_fileopen): Delete.
(stdio_file::~stdio_file): New.
(mem_file_rewind): Delete.
(stdio_file::set_stream): New.
(mem_file_put): Delete.
(stdio_file::open): New.
(mem_file_write): Delete.
(stdio_file_magic, struct stdio_file): Delete.
(stdio_file_new, stdio_file_delete, stdio_file_flush): Delete.
(stdio_file::flush): New.
(stdio_file_read): Rename to ...
(stdio_file::read): ... this. Adjust.
(stdio_file_write): Rename to ...
(stdio_file::write): ... this. Adjust.
(stdio_file_write_async_safe): Rename to ...
(stdio_file::write_async_safe) ... this. Adjust.
(stdio_file_fputs): Rename to ...
(stdio_file::puts) ... this. Adjust.
(stdio_file_isatty): Delete.
(stdio_file_fseek): Delete.
(stdio_file::isatty): New.
(stderr_file_write): Rename to ...
(stderr_file::write) ... this. Adjust.
(stderr_file_fputs): Rename to ...
(stderr_file::puts) ... this. Adjust.
(stderr_fileopen, stdio_fileopen, gdb_fopen): Delete.
(stderr_file::stderr_file): New.
(tee_file_magic): Delete.
(struct tee_file): Delete.
(tee_file::tee_file): New.
(tee_file_new): Delete.
(tee_file::~tee_file): New.
(tee_file_delete): Delete.
(tee_file_flush): Rename to ...
(tee_file::flush): ... this. Adjust.
(tee_file_write): Rename to ...
(tee_file::write): ... this. Adjust.
(tee_file::write_async_safe): New.
(tee_file_fputs): Rename to ...
(tee_file::puts): ... this. Adjust.
(tee_file_isatty): Rename to ...
(tee_file::isatty): ... this. Adjust.
* ui-file.h (struct obstack, struct ui_file): Don't
forward-declare.
(ui_file_new, ui_file_flush_ftype, set_ui_file_flush)
(ui_file_write_ftype)
(set_ui_file_write, ui_file_fputs_ftype, set_ui_file_fputs)
(ui_file_write_async_safe_ftype, set_ui_file_write_async_safe)
(ui_file_read_ftype, set_ui_file_read, ui_file_isatty_ftype)
(set_ui_file_isatty, ui_file_rewind_ftype, set_ui_file_rewind)
(ui_file_put_method_ftype, ui_file_put_ftype, set_ui_file_put)
(ui_file_delete_ftype, set_ui_file_data, ui_file_fseek_ftype)
(set_ui_file_fseek): Delete.
(ui_file_data, ui_file_delete, ui_file_rewind)
(struct ui_file): New.
(ui_file_up): New.
(class null_file): New.
(null_stream): Declare.
(ui_file_write_for_put, ui_file_put): Delete.
(ui_file_xstrdup, ui_file_as_string, ui_file_obsavestring):
Delete.
(ui_file_fseek, mem_fileopen, stdio_fileopen, stderr_fileopen)
(gdb_fopen, tee_file_new): Delete.
(struct string_file): New.
(struct stdio_file): New.
(stdio_file_up): New.
(struct stderr_file): New.
(class tee_file): New.
* ui-out.c (ui_out::field_stream): Take a 'string_file &' instead
of a 'ui_file *'. Adjust.
* ui-out.h (class ui_out) <field_stream>: Likewise.
* utils.c (do_ui_file_delete, make_cleanup_ui_file_delete)
(null_stream): Delete.
(error_stream): Take a 'string_file &' instead of a 'ui_file *'.
Adjust.
* utils.h (struct ui_file): Delete forward declaration..
(make_cleanup_ui_file_delete, null_stream): Delete declarations.
(error_stream): Take a 'string_file &' instead of a
'ui_file *'.
* varobj.c (varobj_value_get_print_value): Use string_file.
* xtensa-tdep.c (xtensa_verify_config): Use string_file.
* gdbarch.c: Regenerate.
2017-02-02 12:11:47 +01:00
|
|
|
|
string_file temp_file;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
/* The first value of the first field (or any field)
|
|
|
|
|
is the discriminant value. */
|
2016-11-08 17:02:42 +01:00
|
|
|
|
c_val_print (TYPE_FIELD_TYPE (disr_type, 0),
|
2016-04-27 03:38:08 +02:00
|
|
|
|
(embedded_offset + TYPE_FIELD_BITPOS (type, 0) / 8
|
|
|
|
|
+ TYPE_FIELD_BITPOS (disr_type, 0) / 8),
|
Eliminate make_cleanup_ui_file_delete / make ui_file a class hierarchy
This patch starts from the desire to eliminate
make_cleanup_ui_file_delete, but then goes beyond. It makes ui_file &
friends a real C++ class hierarchy, and switches temporary
ui_file-like objects to stack-based allocation.
- mem_fileopen -> string_file
mem_fileopen is replaced with a new string_file class that is treated
as a value class created on the stack. This alone eliminates most
make_cleanup_ui_file_delete calls, and, simplifies code a whole lot
(diffstat shows around 1k loc dropped.)
string_file's internal buffer is a std::string, thus the "string" in
the name. This simplifies the implementation much, compared to
mem_fileopen, which managed growing its internal buffer manually.
- ui_file_as_string, ui_file_strdup, ui_file_obsavestring all gone
The new string_file class has a string() method that provides direct
writable access to the internal std::string buffer. This replaced
ui_file_as_string, which forced a copy of the same data the stream had
inside. With direct access via a writable reference, we can instead
move the string out of the string_stream, avoiding deep string
copying.
Related, ui_file_xstrdup calls are replaced with xstrdup'ping the
stream's string, and ui_file_obsavestring is replaced by
obstack_copy0.
With all those out of the way, getting rid of the weird ui_file_put
mechanism was possible.
- New ui_file::printf, ui_file::puts, etc. methods
These simplify / clarify client code. I considered splitting
client-code changes, like these, e.g.:
- stb = mem_fileopen ();
- fprintf_unfiltered (stb, "%s%s%s",
- _("The valid values are:\n"),
- regdesc,
- _("The default is \"std\"."));
+ string_file stb;
+ stb.printf ("%s%s%s",
+ _("The valid values are:\n"),
+ regdesc,
+ _("The default is \"std\"."));
In two steps, with the first step leaving fprintf_unfiltered (etc.)
calls in place, and only afterwards do a pass to change all those to
call stb.printf etc.. I didn't do that split, because (when I tried),
it turned out to be pointless make-work: the first pass would have to
touch the fprintf_unfiltered line anyway, to replace "stb" with
"&stb".
- gdb_fopen replaced with stack-based objects
This avoids the need for cleanups or unique_ptr's. I.e., this:
struct ui_file *file = gdb_fopen (filename, "w");
if (filename == NULL)
perror_with_name (filename);
cleanups = make_cleanup_ui_file_delete (file);
// use file.
do_cleanups (cleanups);
is replaced with this:
stdio_file file;
if (!file.open (filename, "w"))
perror_with_name (filename);
// use file.
- odd contorsions in null_file_write / null_file_fputs around when to
call to_fputs / to_write eliminated.
- Global null_stream object
A few places that were allocating a ui_file in order to print to
"nowhere" are adjusted to instead refer to a new 'null_stream' global
stream.
- TUI's tui_sfileopen eliminated. TUI's ui_file much simplified
The TUI's ui_file was serving a dual purpose. It supported being used
as string buffer, and supported being backed by a stdio FILE. The
string buffer part is gone, replaced by using of string_file. The
'FILE *' support is now much simplified, by making the TUI's ui_file
inherit from stdio_file.
gdb/ChangeLog:
2017-02-02 Pedro Alves <palves@redhat.com>
* ada-lang.c (type_as_string): Use string_file.
* ada-valprint.c (ada_print_floating): Use string_file.
* ada-varobj.c (ada_varobj_scalar_image)
(ada_varobj_get_value_image): Use string_file.
* aix-thread.c (aix_thread_extra_thread_info): Use string_file.
* arm-tdep.c (_initialize_arm_tdep): Use string_printf.
* breakpoint.c (update_inserted_breakpoint_locations)
(insert_breakpoint_locations, reattach_breakpoints)
(print_breakpoint_location, print_one_detail_ranged_breakpoint)
(print_it_watchpoint): Use string_file.
(save_breakpoints): Use stdio_file.
* c-exp.y (oper): Use string_file.
* cli/cli-logging.c (set_logging_redirect): Use ui_file_up and
tee_file.
(pop_output_files): Use delete.
(handle_redirections): Use stdio_file and tee_file.
* cli/cli-setshow.c (do_show_command): Use string_file.
* compile/compile-c-support.c (c_compute_program): Use
string_file.
* compile/compile-c-symbols.c (generate_vla_size): Take a
'string_file &' instead of a 'ui_file *'.
(generate_c_for_for_one_variable): Take a 'string_file &' instead
of a 'ui_file *'. Use string_file.
(generate_c_for_variable_locations): Take a 'string_file &'
instead of a 'ui_file *'.
* compile/compile-internal.h (generate_c_for_for_one_variable):
Take a 'string_file &' instead of a 'ui_file *'.
* compile/compile-loc2c.c (push, pushf, unary, binary)
(print_label, pushf_register_address, pushf_register)
(do_compile_dwarf_expr_to_c): Take a 'string_file &' instead of a
'ui_file *'. Adjust.
* compile/compile.c (compile_to_object): Use string_file.
* compile/compile.h (compile_dwarf_expr_to_c)
(compile_dwarf_bounds_to_c): Take a 'string_file &' instead of a
'ui_file *'.
* cp-support.c (inspect_type): Use string_file and obstack_copy0.
(replace_typedefs_qualified_name): Use string_file and
obstack_copy0.
* disasm.c (gdb_pretty_print_insn): Use string_file.
(gdb_disassembly): Adjust reference the null_stream global.
(do_ui_file_delete): Delete.
(gdb_insn_length): Use null_stream.
* dummy-frame.c (maintenance_print_dummy_frames): Use stdio_file.
* dwarf2loc.c (dwarf2_compile_property_to_c)
(locexpr_generate_c_location, loclist_generate_c_location): Take a
'string_file &' instead of a 'ui_file *'.
* dwarf2loc.h (dwarf2_compile_property_to_c): Likewise.
* dwarf2read.c (do_ui_file_peek_last): Delete.
(dwarf2_compute_name): Use string_file.
* event-top.c (gdb_setup_readline): Use stdio_file.
* gdbarch.sh (verify_gdbarch): Use string_file.
* gdbtypes.c (safe_parse_type): Use null_stream.
* guile/scm-breakpoint.c (gdbscm_breakpoint_commands): Use
string_file.
* guile/scm-disasm.c (gdbscm_print_insn_from_port): Take a
'string_file *' instead of a 'ui_file *'.
(gdbscm_arch_disassemble): Use string_file.
* guile/scm-frame.c (frscm_print_frame_smob): Use string_file.
* guile/scm-ports.c (class ioscm_file_port): Now a class that
inherits from ui_file.
(ioscm_file_port_delete, ioscm_file_port_rewind)
(ioscm_file_port_put): Delete.
(ioscm_file_port_write): Rename to ...
(ioscm_file_port::write): ... this. Remove file_port_magic
checks.
(ioscm_file_port_new): Delete.
(ioscm_with_output_to_port_worker): Use ioscm_file_port and
ui_file_up.
* guile/scm-type.c (tyscm_type_name): Use string_file.
* guile/scm-value.c (vlscm_print_value_smob, gdbscm_value_print):
Use string_file.
* infcmd.c (print_return_value_1): Use string_file.
* infrun.c (print_target_wait_results): Use string_file.
* language.c (add_language): Use string_file.
* location.c (explicit_to_string_internal): Use string_file.
* main.c (captured_main_1): Use null_file.
* maint.c (maintenance_print_architecture): Use stdio_file.
* mi/mi-cmd-stack.c (list_arg_or_local): Use string_file.
* mi/mi-common.h (struct mi_interp) <out, err, log, targ,
event_channel>: Change type to mi_console_file pointer.
* mi/mi-console.c (mi_console_file_fputs, mi_console_file_flush)
(mi_console_file_delete): Delete.
(struct mi_console_file): Delete.
(mi_console_file_magic): Delete.
(mi_console_file_new): Delete.
(mi_console_file::mi_console_file): New.
(mi_console_file_delete): Delete.
(mi_console_file_fputs): Delete.
(mi_console_file::write): New.
(mi_console_raw_packet): Delete.
(mi_console_file::flush): New.
(mi_console_file_flush): Delete.
(mi_console_set_raw): Rename to ...
(mi_console_file::set_raw): ... this.
* mi/mi-console.h (class mi_console_file): New class.
(mi_console_file_new, mi_console_set_raw): Delete.
* mi/mi-interp.c (mi_interpreter_init): Use mi_console_file.
(mi_set_logging): Use delete and tee_file. Adjust.
* mi/mi-main.c (output_register): Use string_file.
(mi_cmd_data_evaluate_expression): Use string_file.
(mi_cmd_data_read_memory): Use string_file.
(mi_cmd_execute, print_variable_or_computed): Use string_file.
* mi/mi-out.c (mi_ui_out::main_stream): New.
(mi_ui_out::rewind): Use main_stream and
string_file.
(mi_ui_out::put): Use main_stream and string_file.
(mi_ui_out::mi_ui_out): Remove 'stream' parameter.
Allocate a 'string_file' instead.
(mi_out_new): Don't allocate a mem_fileopen stream here.
* mi/mi-out.h (mi_ui_out::mi_ui_out): Remove 'stream' parameter.
(mi_ui_out::main_stream): Declare method.
* printcmd.c (eval_command): Use string_file.
* psymtab.c (maintenance_print_psymbols): Use stdio_file.
* python/py-arch.c (archpy_disassemble): Use string_file.
* python/py-breakpoint.c (bppy_get_commands): Use string_file.
* python/py-frame.c (frapy_str): Use string_file.
* python/py-framefilter.c (py_print_type, py_print_single_arg):
Use string_file.
* python/py-type.c (typy_str): Use string_file.
* python/py-unwind.c (unwind_infopy_str): Use string_file.
* python/py-value.c (valpy_str): Use string_file.
* record-btrace.c (btrace_insn_history): Use string_file.
* regcache.c (regcache_print): Use stdio_file.
* reggroups.c (maintenance_print_reggroups): Use stdio_file.
* remote.c (escape_buffer): Use string_file.
* rust-lang.c (rust_get_disr_info): Use string_file.
* serial.c (serial_open_ops_1): Use stdio_file.
(do_serial_close): Use delete.
* stack.c (print_frame_arg): Use string_file.
(print_frame_args): Remove local mem_fileopen stream, not used.
(print_frame): Use string_file.
* symmisc.c (maintenance_print_symbols): Use stdio_file.
* symtab.h (struct symbol_computed_ops) <generate_c_location>:
Take a 'string_file *' instead of a 'ui_file *'.
* top.c (new_ui): Use stdio_file and stderr_file.
(free_ui): Use delete.
(execute_command_to_string): Use string_file.
(quit_confirm): Use string_file.
* tracepoint.c (collection_list::append_exp): Use string_file.
* tui/tui-disasm.c (tui_disassemble): Use string_file.
* tui/tui-file.c: Don't include "ui-file.h".
(enum streamtype, struct tui_stream): Delete.
(tui_file_new, tui_file_delete, tui_fileopen, tui_sfileopen)
(tui_file_isatty, tui_file_rewind, tui_file_put): Delete.
(tui_file::tui_file): New method.
(tui_file_fputs): Delete.
(tui_file_get_strbuf): Delete.
(tui_file::puts): New method.
(tui_file_adjust_strbuf): Delete.
(tui_file_flush): Delete.
(tui_file::flush): New method.
* tui/tui-file.h: Tweak intro comment.
Include ui-file.h.
(tui_fileopen, tui_sfileopen, tui_file_get_strbuf)
(tui_file_adjust_strbuf): Delete declarations.
(class tui_file): New class.
* tui/tui-io.c (tui_initialize_io): Use tui_file.
* tui/tui-regs.c (tui_restore_gdbout): Use delete.
(tui_register_format): Use string_stream.
* tui/tui-stack.c (tui_make_status_line): Use string_file.
(tui_get_function_from_frame): Use string_file.
* typeprint.c (type_to_string): Use string_file.
* ui-file.c (struct ui_file, ui_file_magic, ui_file_new): Delete.
(null_stream): New global.
(ui_file_delete): Delete.
(ui_file::ui_file): New.
(null_file_isatty): Delete.
(ui_file::~ui_file): New.
(null_file_rewind): Delete.
(ui_file::printf): New.
(null_file_put): Delete.
(null_file_flush): Delete.
(ui_file::putstr): New.
(null_file_write): Delete.
(ui_file::putstrn): New.
(null_file_read): Delete.
(ui_file::putc): New.
(null_file_fputs): Delete.
(null_file_write_async_safe): Delete.
(ui_file::vprintf): New.
(null_file_delete): Delete.
(null_file::write): New.
(null_file_fseek): Delete.
(null_file::puts): New.
(ui_file_data): Delete.
(null_file::write_async_safe): New.
(gdb_flush, ui_file_isatty): Adjust.
(ui_file_put, ui_file_rewind): Delete.
(ui_file_write): Adjust.
(ui_file_write_for_put): Delete.
(ui_file_write_async_safe, ui_file_read): Adjust.
(ui_file_fseek): Delete.
(fputs_unfiltered): Adjust.
(set_ui_file_flush, set_ui_file_isatty, set_ui_file_rewind)
(set_ui_file_put, set_ui_file_write, set_ui_file_write_async_safe)
(set_ui_file_read, set_ui_file_fputs, set_ui_file_fseek)
(set_ui_file_data): Delete.
(string_file::~string_file, string_file::write)
(struct accumulated_ui_file, do_ui_file_xstrdup, ui_file_xstrdup)
(do_ui_file_as_string, ui_file_as_string): Delete.
(do_ui_file_obsavestring, ui_file_obsavestring): Delete.
(struct mem_file): Delete.
(mem_file_new): Delete.
(stdio_file::stdio_file): New.
(mem_file_delete): Delete.
(stdio_file::stdio_file): New.
(mem_fileopen): Delete.
(stdio_file::~stdio_file): New.
(mem_file_rewind): Delete.
(stdio_file::set_stream): New.
(mem_file_put): Delete.
(stdio_file::open): New.
(mem_file_write): Delete.
(stdio_file_magic, struct stdio_file): Delete.
(stdio_file_new, stdio_file_delete, stdio_file_flush): Delete.
(stdio_file::flush): New.
(stdio_file_read): Rename to ...
(stdio_file::read): ... this. Adjust.
(stdio_file_write): Rename to ...
(stdio_file::write): ... this. Adjust.
(stdio_file_write_async_safe): Rename to ...
(stdio_file::write_async_safe) ... this. Adjust.
(stdio_file_fputs): Rename to ...
(stdio_file::puts) ... this. Adjust.
(stdio_file_isatty): Delete.
(stdio_file_fseek): Delete.
(stdio_file::isatty): New.
(stderr_file_write): Rename to ...
(stderr_file::write) ... this. Adjust.
(stderr_file_fputs): Rename to ...
(stderr_file::puts) ... this. Adjust.
(stderr_fileopen, stdio_fileopen, gdb_fopen): Delete.
(stderr_file::stderr_file): New.
(tee_file_magic): Delete.
(struct tee_file): Delete.
(tee_file::tee_file): New.
(tee_file_new): Delete.
(tee_file::~tee_file): New.
(tee_file_delete): Delete.
(tee_file_flush): Rename to ...
(tee_file::flush): ... this. Adjust.
(tee_file_write): Rename to ...
(tee_file::write): ... this. Adjust.
(tee_file::write_async_safe): New.
(tee_file_fputs): Rename to ...
(tee_file::puts): ... this. Adjust.
(tee_file_isatty): Rename to ...
(tee_file::isatty): ... this. Adjust.
* ui-file.h (struct obstack, struct ui_file): Don't
forward-declare.
(ui_file_new, ui_file_flush_ftype, set_ui_file_flush)
(ui_file_write_ftype)
(set_ui_file_write, ui_file_fputs_ftype, set_ui_file_fputs)
(ui_file_write_async_safe_ftype, set_ui_file_write_async_safe)
(ui_file_read_ftype, set_ui_file_read, ui_file_isatty_ftype)
(set_ui_file_isatty, ui_file_rewind_ftype, set_ui_file_rewind)
(ui_file_put_method_ftype, ui_file_put_ftype, set_ui_file_put)
(ui_file_delete_ftype, set_ui_file_data, ui_file_fseek_ftype)
(set_ui_file_fseek): Delete.
(ui_file_data, ui_file_delete, ui_file_rewind)
(struct ui_file): New.
(ui_file_up): New.
(class null_file): New.
(null_stream): Declare.
(ui_file_write_for_put, ui_file_put): Delete.
(ui_file_xstrdup, ui_file_as_string, ui_file_obsavestring):
Delete.
(ui_file_fseek, mem_fileopen, stdio_fileopen, stderr_fileopen)
(gdb_fopen, tee_file_new): Delete.
(struct string_file): New.
(struct stdio_file): New.
(stdio_file_up): New.
(struct stderr_file): New.
(class tee_file): New.
* ui-out.c (ui_out::field_stream): Take a 'string_file &' instead
of a 'ui_file *'. Adjust.
* ui-out.h (class ui_out) <field_stream>: Likewise.
* utils.c (do_ui_file_delete, make_cleanup_ui_file_delete)
(null_stream): Delete.
(error_stream): Take a 'string_file &' instead of a 'ui_file *'.
Adjust.
* utils.h (struct ui_file): Delete forward declaration..
(make_cleanup_ui_file_delete, null_stream): Delete declarations.
(error_stream): Take a 'string_file &' instead of a
'ui_file *'.
* varobj.c (varobj_value_get_print_value): Use string_file.
* xtensa-tdep.c (xtensa_verify_config): Use string_file.
* gdbarch.c: Regenerate.
2017-02-02 12:11:47 +01:00
|
|
|
|
address, &temp_file,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
0, val, &opts);
|
|
|
|
|
|
Eliminate make_cleanup_ui_file_delete / make ui_file a class hierarchy
This patch starts from the desire to eliminate
make_cleanup_ui_file_delete, but then goes beyond. It makes ui_file &
friends a real C++ class hierarchy, and switches temporary
ui_file-like objects to stack-based allocation.
- mem_fileopen -> string_file
mem_fileopen is replaced with a new string_file class that is treated
as a value class created on the stack. This alone eliminates most
make_cleanup_ui_file_delete calls, and, simplifies code a whole lot
(diffstat shows around 1k loc dropped.)
string_file's internal buffer is a std::string, thus the "string" in
the name. This simplifies the implementation much, compared to
mem_fileopen, which managed growing its internal buffer manually.
- ui_file_as_string, ui_file_strdup, ui_file_obsavestring all gone
The new string_file class has a string() method that provides direct
writable access to the internal std::string buffer. This replaced
ui_file_as_string, which forced a copy of the same data the stream had
inside. With direct access via a writable reference, we can instead
move the string out of the string_stream, avoiding deep string
copying.
Related, ui_file_xstrdup calls are replaced with xstrdup'ping the
stream's string, and ui_file_obsavestring is replaced by
obstack_copy0.
With all those out of the way, getting rid of the weird ui_file_put
mechanism was possible.
- New ui_file::printf, ui_file::puts, etc. methods
These simplify / clarify client code. I considered splitting
client-code changes, like these, e.g.:
- stb = mem_fileopen ();
- fprintf_unfiltered (stb, "%s%s%s",
- _("The valid values are:\n"),
- regdesc,
- _("The default is \"std\"."));
+ string_file stb;
+ stb.printf ("%s%s%s",
+ _("The valid values are:\n"),
+ regdesc,
+ _("The default is \"std\"."));
In two steps, with the first step leaving fprintf_unfiltered (etc.)
calls in place, and only afterwards do a pass to change all those to
call stb.printf etc.. I didn't do that split, because (when I tried),
it turned out to be pointless make-work: the first pass would have to
touch the fprintf_unfiltered line anyway, to replace "stb" with
"&stb".
- gdb_fopen replaced with stack-based objects
This avoids the need for cleanups or unique_ptr's. I.e., this:
struct ui_file *file = gdb_fopen (filename, "w");
if (filename == NULL)
perror_with_name (filename);
cleanups = make_cleanup_ui_file_delete (file);
// use file.
do_cleanups (cleanups);
is replaced with this:
stdio_file file;
if (!file.open (filename, "w"))
perror_with_name (filename);
// use file.
- odd contorsions in null_file_write / null_file_fputs around when to
call to_fputs / to_write eliminated.
- Global null_stream object
A few places that were allocating a ui_file in order to print to
"nowhere" are adjusted to instead refer to a new 'null_stream' global
stream.
- TUI's tui_sfileopen eliminated. TUI's ui_file much simplified
The TUI's ui_file was serving a dual purpose. It supported being used
as string buffer, and supported being backed by a stdio FILE. The
string buffer part is gone, replaced by using of string_file. The
'FILE *' support is now much simplified, by making the TUI's ui_file
inherit from stdio_file.
gdb/ChangeLog:
2017-02-02 Pedro Alves <palves@redhat.com>
* ada-lang.c (type_as_string): Use string_file.
* ada-valprint.c (ada_print_floating): Use string_file.
* ada-varobj.c (ada_varobj_scalar_image)
(ada_varobj_get_value_image): Use string_file.
* aix-thread.c (aix_thread_extra_thread_info): Use string_file.
* arm-tdep.c (_initialize_arm_tdep): Use string_printf.
* breakpoint.c (update_inserted_breakpoint_locations)
(insert_breakpoint_locations, reattach_breakpoints)
(print_breakpoint_location, print_one_detail_ranged_breakpoint)
(print_it_watchpoint): Use string_file.
(save_breakpoints): Use stdio_file.
* c-exp.y (oper): Use string_file.
* cli/cli-logging.c (set_logging_redirect): Use ui_file_up and
tee_file.
(pop_output_files): Use delete.
(handle_redirections): Use stdio_file and tee_file.
* cli/cli-setshow.c (do_show_command): Use string_file.
* compile/compile-c-support.c (c_compute_program): Use
string_file.
* compile/compile-c-symbols.c (generate_vla_size): Take a
'string_file &' instead of a 'ui_file *'.
(generate_c_for_for_one_variable): Take a 'string_file &' instead
of a 'ui_file *'. Use string_file.
(generate_c_for_variable_locations): Take a 'string_file &'
instead of a 'ui_file *'.
* compile/compile-internal.h (generate_c_for_for_one_variable):
Take a 'string_file &' instead of a 'ui_file *'.
* compile/compile-loc2c.c (push, pushf, unary, binary)
(print_label, pushf_register_address, pushf_register)
(do_compile_dwarf_expr_to_c): Take a 'string_file &' instead of a
'ui_file *'. Adjust.
* compile/compile.c (compile_to_object): Use string_file.
* compile/compile.h (compile_dwarf_expr_to_c)
(compile_dwarf_bounds_to_c): Take a 'string_file &' instead of a
'ui_file *'.
* cp-support.c (inspect_type): Use string_file and obstack_copy0.
(replace_typedefs_qualified_name): Use string_file and
obstack_copy0.
* disasm.c (gdb_pretty_print_insn): Use string_file.
(gdb_disassembly): Adjust reference the null_stream global.
(do_ui_file_delete): Delete.
(gdb_insn_length): Use null_stream.
* dummy-frame.c (maintenance_print_dummy_frames): Use stdio_file.
* dwarf2loc.c (dwarf2_compile_property_to_c)
(locexpr_generate_c_location, loclist_generate_c_location): Take a
'string_file &' instead of a 'ui_file *'.
* dwarf2loc.h (dwarf2_compile_property_to_c): Likewise.
* dwarf2read.c (do_ui_file_peek_last): Delete.
(dwarf2_compute_name): Use string_file.
* event-top.c (gdb_setup_readline): Use stdio_file.
* gdbarch.sh (verify_gdbarch): Use string_file.
* gdbtypes.c (safe_parse_type): Use null_stream.
* guile/scm-breakpoint.c (gdbscm_breakpoint_commands): Use
string_file.
* guile/scm-disasm.c (gdbscm_print_insn_from_port): Take a
'string_file *' instead of a 'ui_file *'.
(gdbscm_arch_disassemble): Use string_file.
* guile/scm-frame.c (frscm_print_frame_smob): Use string_file.
* guile/scm-ports.c (class ioscm_file_port): Now a class that
inherits from ui_file.
(ioscm_file_port_delete, ioscm_file_port_rewind)
(ioscm_file_port_put): Delete.
(ioscm_file_port_write): Rename to ...
(ioscm_file_port::write): ... this. Remove file_port_magic
checks.
(ioscm_file_port_new): Delete.
(ioscm_with_output_to_port_worker): Use ioscm_file_port and
ui_file_up.
* guile/scm-type.c (tyscm_type_name): Use string_file.
* guile/scm-value.c (vlscm_print_value_smob, gdbscm_value_print):
Use string_file.
* infcmd.c (print_return_value_1): Use string_file.
* infrun.c (print_target_wait_results): Use string_file.
* language.c (add_language): Use string_file.
* location.c (explicit_to_string_internal): Use string_file.
* main.c (captured_main_1): Use null_file.
* maint.c (maintenance_print_architecture): Use stdio_file.
* mi/mi-cmd-stack.c (list_arg_or_local): Use string_file.
* mi/mi-common.h (struct mi_interp) <out, err, log, targ,
event_channel>: Change type to mi_console_file pointer.
* mi/mi-console.c (mi_console_file_fputs, mi_console_file_flush)
(mi_console_file_delete): Delete.
(struct mi_console_file): Delete.
(mi_console_file_magic): Delete.
(mi_console_file_new): Delete.
(mi_console_file::mi_console_file): New.
(mi_console_file_delete): Delete.
(mi_console_file_fputs): Delete.
(mi_console_file::write): New.
(mi_console_raw_packet): Delete.
(mi_console_file::flush): New.
(mi_console_file_flush): Delete.
(mi_console_set_raw): Rename to ...
(mi_console_file::set_raw): ... this.
* mi/mi-console.h (class mi_console_file): New class.
(mi_console_file_new, mi_console_set_raw): Delete.
* mi/mi-interp.c (mi_interpreter_init): Use mi_console_file.
(mi_set_logging): Use delete and tee_file. Adjust.
* mi/mi-main.c (output_register): Use string_file.
(mi_cmd_data_evaluate_expression): Use string_file.
(mi_cmd_data_read_memory): Use string_file.
(mi_cmd_execute, print_variable_or_computed): Use string_file.
* mi/mi-out.c (mi_ui_out::main_stream): New.
(mi_ui_out::rewind): Use main_stream and
string_file.
(mi_ui_out::put): Use main_stream and string_file.
(mi_ui_out::mi_ui_out): Remove 'stream' parameter.
Allocate a 'string_file' instead.
(mi_out_new): Don't allocate a mem_fileopen stream here.
* mi/mi-out.h (mi_ui_out::mi_ui_out): Remove 'stream' parameter.
(mi_ui_out::main_stream): Declare method.
* printcmd.c (eval_command): Use string_file.
* psymtab.c (maintenance_print_psymbols): Use stdio_file.
* python/py-arch.c (archpy_disassemble): Use string_file.
* python/py-breakpoint.c (bppy_get_commands): Use string_file.
* python/py-frame.c (frapy_str): Use string_file.
* python/py-framefilter.c (py_print_type, py_print_single_arg):
Use string_file.
* python/py-type.c (typy_str): Use string_file.
* python/py-unwind.c (unwind_infopy_str): Use string_file.
* python/py-value.c (valpy_str): Use string_file.
* record-btrace.c (btrace_insn_history): Use string_file.
* regcache.c (regcache_print): Use stdio_file.
* reggroups.c (maintenance_print_reggroups): Use stdio_file.
* remote.c (escape_buffer): Use string_file.
* rust-lang.c (rust_get_disr_info): Use string_file.
* serial.c (serial_open_ops_1): Use stdio_file.
(do_serial_close): Use delete.
* stack.c (print_frame_arg): Use string_file.
(print_frame_args): Remove local mem_fileopen stream, not used.
(print_frame): Use string_file.
* symmisc.c (maintenance_print_symbols): Use stdio_file.
* symtab.h (struct symbol_computed_ops) <generate_c_location>:
Take a 'string_file *' instead of a 'ui_file *'.
* top.c (new_ui): Use stdio_file and stderr_file.
(free_ui): Use delete.
(execute_command_to_string): Use string_file.
(quit_confirm): Use string_file.
* tracepoint.c (collection_list::append_exp): Use string_file.
* tui/tui-disasm.c (tui_disassemble): Use string_file.
* tui/tui-file.c: Don't include "ui-file.h".
(enum streamtype, struct tui_stream): Delete.
(tui_file_new, tui_file_delete, tui_fileopen, tui_sfileopen)
(tui_file_isatty, tui_file_rewind, tui_file_put): Delete.
(tui_file::tui_file): New method.
(tui_file_fputs): Delete.
(tui_file_get_strbuf): Delete.
(tui_file::puts): New method.
(tui_file_adjust_strbuf): Delete.
(tui_file_flush): Delete.
(tui_file::flush): New method.
* tui/tui-file.h: Tweak intro comment.
Include ui-file.h.
(tui_fileopen, tui_sfileopen, tui_file_get_strbuf)
(tui_file_adjust_strbuf): Delete declarations.
(class tui_file): New class.
* tui/tui-io.c (tui_initialize_io): Use tui_file.
* tui/tui-regs.c (tui_restore_gdbout): Use delete.
(tui_register_format): Use string_stream.
* tui/tui-stack.c (tui_make_status_line): Use string_file.
(tui_get_function_from_frame): Use string_file.
* typeprint.c (type_to_string): Use string_file.
* ui-file.c (struct ui_file, ui_file_magic, ui_file_new): Delete.
(null_stream): New global.
(ui_file_delete): Delete.
(ui_file::ui_file): New.
(null_file_isatty): Delete.
(ui_file::~ui_file): New.
(null_file_rewind): Delete.
(ui_file::printf): New.
(null_file_put): Delete.
(null_file_flush): Delete.
(ui_file::putstr): New.
(null_file_write): Delete.
(ui_file::putstrn): New.
(null_file_read): Delete.
(ui_file::putc): New.
(null_file_fputs): Delete.
(null_file_write_async_safe): Delete.
(ui_file::vprintf): New.
(null_file_delete): Delete.
(null_file::write): New.
(null_file_fseek): Delete.
(null_file::puts): New.
(ui_file_data): Delete.
(null_file::write_async_safe): New.
(gdb_flush, ui_file_isatty): Adjust.
(ui_file_put, ui_file_rewind): Delete.
(ui_file_write): Adjust.
(ui_file_write_for_put): Delete.
(ui_file_write_async_safe, ui_file_read): Adjust.
(ui_file_fseek): Delete.
(fputs_unfiltered): Adjust.
(set_ui_file_flush, set_ui_file_isatty, set_ui_file_rewind)
(set_ui_file_put, set_ui_file_write, set_ui_file_write_async_safe)
(set_ui_file_read, set_ui_file_fputs, set_ui_file_fseek)
(set_ui_file_data): Delete.
(string_file::~string_file, string_file::write)
(struct accumulated_ui_file, do_ui_file_xstrdup, ui_file_xstrdup)
(do_ui_file_as_string, ui_file_as_string): Delete.
(do_ui_file_obsavestring, ui_file_obsavestring): Delete.
(struct mem_file): Delete.
(mem_file_new): Delete.
(stdio_file::stdio_file): New.
(mem_file_delete): Delete.
(stdio_file::stdio_file): New.
(mem_fileopen): Delete.
(stdio_file::~stdio_file): New.
(mem_file_rewind): Delete.
(stdio_file::set_stream): New.
(mem_file_put): Delete.
(stdio_file::open): New.
(mem_file_write): Delete.
(stdio_file_magic, struct stdio_file): Delete.
(stdio_file_new, stdio_file_delete, stdio_file_flush): Delete.
(stdio_file::flush): New.
(stdio_file_read): Rename to ...
(stdio_file::read): ... this. Adjust.
(stdio_file_write): Rename to ...
(stdio_file::write): ... this. Adjust.
(stdio_file_write_async_safe): Rename to ...
(stdio_file::write_async_safe) ... this. Adjust.
(stdio_file_fputs): Rename to ...
(stdio_file::puts) ... this. Adjust.
(stdio_file_isatty): Delete.
(stdio_file_fseek): Delete.
(stdio_file::isatty): New.
(stderr_file_write): Rename to ...
(stderr_file::write) ... this. Adjust.
(stderr_file_fputs): Rename to ...
(stderr_file::puts) ... this. Adjust.
(stderr_fileopen, stdio_fileopen, gdb_fopen): Delete.
(stderr_file::stderr_file): New.
(tee_file_magic): Delete.
(struct tee_file): Delete.
(tee_file::tee_file): New.
(tee_file_new): Delete.
(tee_file::~tee_file): New.
(tee_file_delete): Delete.
(tee_file_flush): Rename to ...
(tee_file::flush): ... this. Adjust.
(tee_file_write): Rename to ...
(tee_file::write): ... this. Adjust.
(tee_file::write_async_safe): New.
(tee_file_fputs): Rename to ...
(tee_file::puts): ... this. Adjust.
(tee_file_isatty): Rename to ...
(tee_file::isatty): ... this. Adjust.
* ui-file.h (struct obstack, struct ui_file): Don't
forward-declare.
(ui_file_new, ui_file_flush_ftype, set_ui_file_flush)
(ui_file_write_ftype)
(set_ui_file_write, ui_file_fputs_ftype, set_ui_file_fputs)
(ui_file_write_async_safe_ftype, set_ui_file_write_async_safe)
(ui_file_read_ftype, set_ui_file_read, ui_file_isatty_ftype)
(set_ui_file_isatty, ui_file_rewind_ftype, set_ui_file_rewind)
(ui_file_put_method_ftype, ui_file_put_ftype, set_ui_file_put)
(ui_file_delete_ftype, set_ui_file_data, ui_file_fseek_ftype)
(set_ui_file_fseek): Delete.
(ui_file_data, ui_file_delete, ui_file_rewind)
(struct ui_file): New.
(ui_file_up): New.
(class null_file): New.
(null_stream): Declare.
(ui_file_write_for_put, ui_file_put): Delete.
(ui_file_xstrdup, ui_file_as_string, ui_file_obsavestring):
Delete.
(ui_file_fseek, mem_fileopen, stdio_fileopen, stderr_fileopen)
(gdb_fopen, tee_file_new): Delete.
(struct string_file): New.
(struct stdio_file): New.
(stdio_file_up): New.
(struct stderr_file): New.
(class tee_file): New.
* ui-out.c (ui_out::field_stream): Take a 'string_file &' instead
of a 'ui_file *'. Adjust.
* ui-out.h (class ui_out) <field_stream>: Likewise.
* utils.c (do_ui_file_delete, make_cleanup_ui_file_delete)
(null_stream): Delete.
(error_stream): Take a 'string_file &' instead of a 'ui_file *'.
Adjust.
* utils.h (struct ui_file): Delete forward declaration..
(make_cleanup_ui_file_delete, null_stream): Delete declarations.
(error_stream): Take a 'string_file &' instead of a
'ui_file *'.
* varobj.c (varobj_value_get_print_value): Use string_file.
* xtensa-tdep.c (xtensa_verify_config): Use string_file.
* gdbarch.c: Regenerate.
2017-02-02 12:11:47 +01:00
|
|
|
|
ret.name = std::move (temp_file.string ());
|
2016-11-08 16:26:46 +01:00
|
|
|
|
name_segment = rust_last_path_segment (ret.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
if (name_segment != NULL)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
/* Sadly, the discriminant value paths do not match the type
|
|
|
|
|
field name paths ('core::option::Option::Some' vs
|
|
|
|
|
'core::option::Some'). However, enum variant names are
|
|
|
|
|
unique in the last path segment and the generics are not
|
|
|
|
|
part of this path, so we can just compare those. This is
|
|
|
|
|
hackish and would be better fixed by improving rustc's
|
|
|
|
|
metadata for enums. */
|
|
|
|
|
const char *field_type = TYPE_NAME (TYPE_FIELD_TYPE (type, i));
|
|
|
|
|
|
|
|
|
|
if (field_type != NULL
|
|
|
|
|
&& strcmp (name_segment,
|
|
|
|
|
rust_last_path_segment (field_type)) == 0)
|
|
|
|
|
{
|
|
|
|
|
ret.field_no = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-08 16:26:46 +01:00
|
|
|
|
if (ret.field_no == -1 && !ret.name.empty ())
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
/* Somehow the discriminant wasn't found. */
|
|
|
|
|
error (_("Could not find variant of %s with discriminant %s"),
|
2016-11-08 16:26:46 +01:00
|
|
|
|
TYPE_TAG_NAME (type), ret.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See rust-lang.h. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_tuple_type_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
/* The current implementation is a bit of a hack, but there's
|
|
|
|
|
nothing else in the debuginfo to distinguish a tuple from a
|
|
|
|
|
struct. */
|
|
|
|
|
return (TYPE_CODE (type) == TYPE_CODE_STRUCT
|
|
|
|
|
&& TYPE_TAG_NAME (type) != NULL
|
|
|
|
|
&& TYPE_TAG_NAME (type)[0] == '(');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return true if all non-static fields of a structlike type are in a
|
|
|
|
|
sequence like __0, __1, __2. OFFSET lets us skip fields. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_underscore_fields (struct type *type, int offset)
|
|
|
|
|
{
|
|
|
|
|
int i, field_number;
|
|
|
|
|
|
|
|
|
|
field_number = 0;
|
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return false;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (!field_is_static (&TYPE_FIELD (type, i)))
|
|
|
|
|
{
|
|
|
|
|
if (offset > 0)
|
|
|
|
|
offset--;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char buf[20];
|
|
|
|
|
|
|
|
|
|
xsnprintf (buf, sizeof (buf), "__%d", field_number);
|
|
|
|
|
if (strcmp (buf, TYPE_FIELD_NAME (type, i)) != 0)
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return false;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
field_number++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return true;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See rust-lang.h. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_tuple_struct_type_p (struct type *type)
|
|
|
|
|
{
|
2016-07-11 23:02:10 +02:00
|
|
|
|
/* This is just an approximation until DWARF can represent Rust more
|
|
|
|
|
precisely. We exclude zero-length structs because they may not
|
|
|
|
|
be tuple structs, and there's no way to tell. */
|
|
|
|
|
return TYPE_NFIELDS (type) > 0 && rust_underscore_fields (type, 0);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return true if a variant TYPE is a tuple variant, false otherwise. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_tuple_variant_type_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
/* First field is discriminant */
|
|
|
|
|
return rust_underscore_fields (type, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return true if TYPE is a slice type, otherwise false. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_slice_type_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
return (TYPE_CODE (type) == TYPE_CODE_STRUCT
|
|
|
|
|
&& TYPE_TAG_NAME (type) != NULL
|
2017-10-02 20:15:52 +02:00
|
|
|
|
&& (strncmp (TYPE_TAG_NAME (type), "&[", 2) == 0
|
|
|
|
|
|| strcmp (TYPE_TAG_NAME (type), "&str") == 0));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return true if TYPE is a range type, otherwise false. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_range_type_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) != TYPE_CODE_STRUCT
|
|
|
|
|
|| TYPE_NFIELDS (type) > 2
|
|
|
|
|
|| TYPE_TAG_NAME (type) == NULL
|
|
|
|
|
|| strstr (TYPE_TAG_NAME (type), "::Range") == NULL)
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return false;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (type) == 0)
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return true;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (type, 0), "start") == 0)
|
|
|
|
|
{
|
|
|
|
|
if (TYPE_NFIELDS (type) == 1)
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return true;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
i = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (TYPE_NFIELDS (type) == 2)
|
|
|
|
|
{
|
|
|
|
|
/* First field had to be "start". */
|
2017-02-03 05:21:19 +01:00
|
|
|
|
return false;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp (TYPE_FIELD_NAME (type, i), "end") == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return true if TYPE seems to be the type "u8", otherwise false. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_u8_type_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
return (TYPE_CODE (type) == TYPE_CODE_INT
|
|
|
|
|
&& TYPE_UNSIGNED (type)
|
|
|
|
|
&& TYPE_LENGTH (type) == 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return true if TYPE is a Rust character type. */
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
static bool
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_chartype_p (struct type *type)
|
|
|
|
|
{
|
|
|
|
|
return (TYPE_CODE (type) == TYPE_CODE_CHAR
|
|
|
|
|
&& TYPE_LENGTH (type) == 4
|
|
|
|
|
&& TYPE_UNSIGNED (type));
|
|
|
|
|
}
|
|
|
|
|
|
Handle dereferencing Rust trait objects
In Rust, virtual tables work a bit differently than they do in C++. In
C++, as you know, they are connected to a particular class hierarchy.
Rust, instead, can generate a virtual table for potentially any type --
in fact, one such virtual table for each trait (a trait is similar to an
abstract class or to a Java interface) that a type implements.
Objects that are referenced via a trait can't currently be inspected by
gdb. This patch implements the Rust equivalent of "set print object".
gdb relies heavily on the C++ ABI to decode virtual tables; primarily to
make "set print object" work; but also "info vtbl". However, Rust does
not currently have a specified ABI, so this approach seems unwise to
emulate.
Instead, I've changed the Rust compiler to emit some DWARF that
describes trait objects (previously their internal structure was
opaque), vtables (currently just a size -- but I hope to expand this in
the future), and the concrete type for which a vtable was emitted.
The concrete type is expressed as a DW_AT_containing_type on the
vtable's type. This is a small extension to DWARF.
This patch adds a new entry to quick_symbol_functions to return the
symtab that holds a data address. Previously there was no way in gdb to
look up a full (only minimal) non-text symbol by address. The psymbol
implementation of this method works by lazily filling in a map that is
added to the objfile. This avoids slowing down psymbol reading for a
feature that is likely to not be used too frequently.
I did not update .gdb_index. My thinking here is that the DWARF 5
indices will obsolete .gdb_index soon-ish, meaning that adding a new
feature to them is probably wasted work. If necessary I can update the
DWARF 5 index code when it lands in gdb.
Regression tested on x86-64 Fedora 25.
2017-11-17 Tom Tromey <tom@tromey.com>
* symtab.h (struct symbol) <is_rust_vtable>: New member.
(struct rust_vtable_symbol): New.
(find_symbol_at_address): Declare.
* symtab.c (find_symbol_at_address): New function.
* symfile.h (struct quick_symbol_functions)
<find_compunit_symtab_by_address>: New member.
* symfile-debug.c (debug_qf_find_compunit_symtab_by_address): New
function.
(debug_sym_quick_functions): Link to
debug_qf_find_compunit_symtab_by_address.
* rust-lang.c (rust_get_trait_object_pointer): New function.
(rust_evaluate_subexp) <case UNOP_IND>: New case. Call
rust_get_trait_object_pointer.
* psymtab.c (psym_relocate): Clear psymbol_map.
(psym_fill_psymbol_map, psym_find_compunit_symtab_by_address): New
functions.
(psym_functions): Link to psym_find_compunit_symtab_by_address.
* objfiles.h (struct objfile) <psymbol_map>: New member.
* dwarf2read.c (dwarf2_gdb_index_functions): Update.
(process_die) <DW_TAG_variable>: New case. Call read_variable.
(rust_containing_type, read_variable): New functions.
2017-11-17 Tom Tromey <tom@tromey.com>
* gdb.rust/traits.rs: New file.
* gdb.rust/traits.exp: New file.
2017-07-06 14:44:38 +02:00
|
|
|
|
/* If VALUE represents a trait object pointer, return the underlying
|
|
|
|
|
pointer with the correct (i.e., runtime) type. Otherwise, return
|
|
|
|
|
NULL. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
rust_get_trait_object_pointer (struct value *value)
|
|
|
|
|
{
|
|
|
|
|
struct type *type = check_typedef (value_type (value));
|
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) != TYPE_CODE_STRUCT || TYPE_NFIELDS (type) != 2)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Try to be a bit resilient if the ABI changes. */
|
|
|
|
|
int vtable_field = 0;
|
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (type, i), "vtable") == 0)
|
|
|
|
|
vtable_field = i;
|
|
|
|
|
else if (strcmp (TYPE_FIELD_NAME (type, i), "pointer") != 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE_ADDR vtable = value_as_address (value_field (value, vtable_field));
|
|
|
|
|
struct symbol *symbol = find_symbol_at_address (vtable);
|
2017-11-17 20:05:58 +01:00
|
|
|
|
if (symbol == NULL || symbol->subclass != SYMBOL_RUST_VTABLE)
|
Handle dereferencing Rust trait objects
In Rust, virtual tables work a bit differently than they do in C++. In
C++, as you know, they are connected to a particular class hierarchy.
Rust, instead, can generate a virtual table for potentially any type --
in fact, one such virtual table for each trait (a trait is similar to an
abstract class or to a Java interface) that a type implements.
Objects that are referenced via a trait can't currently be inspected by
gdb. This patch implements the Rust equivalent of "set print object".
gdb relies heavily on the C++ ABI to decode virtual tables; primarily to
make "set print object" work; but also "info vtbl". However, Rust does
not currently have a specified ABI, so this approach seems unwise to
emulate.
Instead, I've changed the Rust compiler to emit some DWARF that
describes trait objects (previously their internal structure was
opaque), vtables (currently just a size -- but I hope to expand this in
the future), and the concrete type for which a vtable was emitted.
The concrete type is expressed as a DW_AT_containing_type on the
vtable's type. This is a small extension to DWARF.
This patch adds a new entry to quick_symbol_functions to return the
symtab that holds a data address. Previously there was no way in gdb to
look up a full (only minimal) non-text symbol by address. The psymbol
implementation of this method works by lazily filling in a map that is
added to the objfile. This avoids slowing down psymbol reading for a
feature that is likely to not be used too frequently.
I did not update .gdb_index. My thinking here is that the DWARF 5
indices will obsolete .gdb_index soon-ish, meaning that adding a new
feature to them is probably wasted work. If necessary I can update the
DWARF 5 index code when it lands in gdb.
Regression tested on x86-64 Fedora 25.
2017-11-17 Tom Tromey <tom@tromey.com>
* symtab.h (struct symbol) <is_rust_vtable>: New member.
(struct rust_vtable_symbol): New.
(find_symbol_at_address): Declare.
* symtab.c (find_symbol_at_address): New function.
* symfile.h (struct quick_symbol_functions)
<find_compunit_symtab_by_address>: New member.
* symfile-debug.c (debug_qf_find_compunit_symtab_by_address): New
function.
(debug_sym_quick_functions): Link to
debug_qf_find_compunit_symtab_by_address.
* rust-lang.c (rust_get_trait_object_pointer): New function.
(rust_evaluate_subexp) <case UNOP_IND>: New case. Call
rust_get_trait_object_pointer.
* psymtab.c (psym_relocate): Clear psymbol_map.
(psym_fill_psymbol_map, psym_find_compunit_symtab_by_address): New
functions.
(psym_functions): Link to psym_find_compunit_symtab_by_address.
* objfiles.h (struct objfile) <psymbol_map>: New member.
* dwarf2read.c (dwarf2_gdb_index_functions): Update.
(process_die) <DW_TAG_variable>: New case. Call read_variable.
(rust_containing_type, read_variable): New functions.
2017-11-17 Tom Tromey <tom@tromey.com>
* gdb.rust/traits.rs: New file.
* gdb.rust/traits.exp: New file.
2017-07-06 14:44:38 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
struct rust_vtable_symbol *vtable_sym
|
|
|
|
|
= static_cast<struct rust_vtable_symbol *> (symbol);
|
|
|
|
|
struct type *pointer_type = lookup_pointer_type (vtable_sym->concrete_type);
|
|
|
|
|
return value_cast (pointer_type, value_field (value, 1 - vtable_field));
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* la_emitchar implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_emitchar (int c, struct type *type, struct ui_file *stream, int quoter)
|
|
|
|
|
{
|
|
|
|
|
if (!rust_chartype_p (type))
|
|
|
|
|
generic_emit_char (c, type, stream, quoter,
|
|
|
|
|
target_charset (get_type_arch (type)));
|
|
|
|
|
else if (c == '\\' || c == quoter)
|
|
|
|
|
fprintf_filtered (stream, "\\%c", c);
|
|
|
|
|
else if (c == '\n')
|
|
|
|
|
fputs_filtered ("\\n", stream);
|
|
|
|
|
else if (c == '\r')
|
|
|
|
|
fputs_filtered ("\\r", stream);
|
|
|
|
|
else if (c == '\t')
|
|
|
|
|
fputs_filtered ("\\t", stream);
|
|
|
|
|
else if (c == '\0')
|
|
|
|
|
fputs_filtered ("\\0", stream);
|
|
|
|
|
else if (c >= 32 && c <= 127 && isprint (c))
|
|
|
|
|
fputc_filtered (c, stream);
|
|
|
|
|
else if (c <= 255)
|
|
|
|
|
fprintf_filtered (stream, "\\x%02x", c);
|
|
|
|
|
else
|
|
|
|
|
fprintf_filtered (stream, "\\u{%06x}", c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* la_printchar implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_printchar (int c, struct type *type, struct ui_file *stream)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered ("'", stream);
|
|
|
|
|
LA_EMIT_CHAR (c, type, stream, '\'');
|
|
|
|
|
fputs_filtered ("'", stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* la_printstr implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_printstr (struct ui_file *stream, struct type *type,
|
|
|
|
|
const gdb_byte *string, unsigned int length,
|
|
|
|
|
const char *user_encoding, int force_ellipses,
|
|
|
|
|
const struct value_print_options *options)
|
|
|
|
|
{
|
|
|
|
|
/* Rust always uses UTF-8, but let the caller override this if need
|
|
|
|
|
be. */
|
|
|
|
|
const char *encoding = user_encoding;
|
|
|
|
|
if (user_encoding == NULL || !*user_encoding)
|
|
|
|
|
{
|
|
|
|
|
/* In Rust strings, characters are "u8". */
|
|
|
|
|
if (rust_u8_type_p (type))
|
|
|
|
|
encoding = "UTF-8";
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* This is probably some C string, so let's let C deal with
|
|
|
|
|
it. */
|
|
|
|
|
c_printstr (stream, type, string, length, user_encoding,
|
|
|
|
|
force_ellipses, options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is not ideal as it doesn't use our character printer. */
|
|
|
|
|
generic_printstr (stream, type, string, length, encoding, force_ellipses,
|
|
|
|
|
'"', 0, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-02 21:55:42 +02:00
|
|
|
|
/* Helper function to print a string slice. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_val_print_str (struct ui_file *stream, struct value *val,
|
|
|
|
|
const struct value_print_options *options)
|
|
|
|
|
{
|
|
|
|
|
struct value *base = value_struct_elt (&val, NULL, "data_ptr", NULL,
|
|
|
|
|
"slice");
|
|
|
|
|
struct value *len = value_struct_elt (&val, NULL, "length", NULL, "slice");
|
|
|
|
|
|
|
|
|
|
val_print_string (TYPE_TARGET_TYPE (value_type (base)), "UTF-8",
|
|
|
|
|
value_as_address (base), value_as_long (len), stream,
|
|
|
|
|
options);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
/* rust_print_type branch for structs and untagged unions. */
|
|
|
|
|
|
|
|
|
|
static void
|
2016-11-08 17:02:42 +01:00
|
|
|
|
val_print_struct (struct type *type, int embedded_offset,
|
|
|
|
|
CORE_ADDR address, struct ui_file *stream,
|
|
|
|
|
int recurse, struct value *val,
|
2016-11-08 12:32:53 +01:00
|
|
|
|
const struct value_print_options *options)
|
2016-10-29 03:00:43 +02:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int first_field;
|
2017-10-02 21:55:42 +02:00
|
|
|
|
|
|
|
|
|
if (rust_slice_type_p (type) && strcmp (TYPE_NAME (type), "&str") == 0)
|
|
|
|
|
{
|
|
|
|
|
rust_val_print_str (stream, val, options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-03 05:21:19 +01:00
|
|
|
|
bool is_tuple = rust_tuple_type_p (type);
|
|
|
|
|
bool is_tuple_struct = !is_tuple && rust_tuple_struct_type_p (type);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
struct value_print_options opts;
|
|
|
|
|
|
|
|
|
|
if (!is_tuple)
|
|
|
|
|
{
|
|
|
|
|
if (TYPE_TAG_NAME (type) != NULL)
|
|
|
|
|
fprintf_filtered (stream, "%s", TYPE_TAG_NAME (type));
|
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (type) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (TYPE_TAG_NAME (type) != NULL)
|
|
|
|
|
fputs_filtered (" ", stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_tuple || is_tuple_struct)
|
|
|
|
|
fputs_filtered ("(", stream);
|
|
|
|
|
else
|
|
|
|
|
fputs_filtered ("{", stream);
|
|
|
|
|
|
|
|
|
|
opts = *options;
|
|
|
|
|
opts.deref_ref = 0;
|
|
|
|
|
|
|
|
|
|
first_field = 1;
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (field_is_static (&TYPE_FIELD (type, i)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!first_field)
|
|
|
|
|
fputs_filtered (",", stream);
|
|
|
|
|
|
|
|
|
|
if (options->prettyformat)
|
|
|
|
|
{
|
2017-02-03 05:01:11 +01:00
|
|
|
|
fputs_filtered ("\n", stream);
|
|
|
|
|
print_spaces_filtered (2 + 2 * recurse, stream);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
}
|
|
|
|
|
else if (!first_field)
|
|
|
|
|
fputs_filtered (" ", stream);
|
|
|
|
|
|
|
|
|
|
first_field = 0;
|
|
|
|
|
|
|
|
|
|
if (!is_tuple && !is_tuple_struct)
|
|
|
|
|
{
|
2017-02-03 05:01:11 +01:00
|
|
|
|
fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
|
|
|
|
|
fputs_filtered (": ", stream);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val_print (TYPE_FIELD_TYPE (type, i),
|
2017-02-03 05:01:11 +01:00
|
|
|
|
embedded_offset + TYPE_FIELD_BITPOS (type, i) / 8,
|
|
|
|
|
address,
|
|
|
|
|
stream, recurse + 1, val, &opts,
|
|
|
|
|
current_language);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->prettyformat)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered ("\n", stream);
|
|
|
|
|
print_spaces_filtered (2 * recurse, stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_tuple || is_tuple_struct)
|
|
|
|
|
fputs_filtered (")", stream);
|
|
|
|
|
else
|
|
|
|
|
fputs_filtered ("}", stream);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
static const struct generic_val_print_decorations rust_decorations =
|
|
|
|
|
{
|
|
|
|
|
/* Complex isn't used in Rust, but we provide C-ish values just in
|
|
|
|
|
case. */
|
|
|
|
|
"",
|
|
|
|
|
" + ",
|
|
|
|
|
" * I",
|
|
|
|
|
"true",
|
|
|
|
|
"false",
|
2016-06-27 17:46:59 +02:00
|
|
|
|
"()",
|
2016-04-27 03:38:08 +02:00
|
|
|
|
"[",
|
|
|
|
|
"]"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* la_val_print implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
2016-11-08 17:02:42 +01:00
|
|
|
|
rust_val_print (struct type *type, int embedded_offset,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
CORE_ADDR address, struct ui_file *stream, int recurse,
|
2016-11-08 17:02:42 +01:00
|
|
|
|
struct value *val,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
const struct value_print_options *options)
|
|
|
|
|
{
|
2016-11-08 17:02:42 +01:00
|
|
|
|
const gdb_byte *valaddr = value_contents_for_printing (val);
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
type = check_typedef (type);
|
|
|
|
|
switch (TYPE_CODE (type))
|
|
|
|
|
{
|
|
|
|
|
case TYPE_CODE_PTR:
|
|
|
|
|
{
|
|
|
|
|
LONGEST low_bound, high_bound;
|
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_ARRAY
|
|
|
|
|
&& rust_u8_type_p (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)))
|
|
|
|
|
&& get_array_bounds (TYPE_TARGET_TYPE (type), &low_bound,
|
|
|
|
|
&high_bound)) {
|
|
|
|
|
/* We have a pointer to a byte string, so just print
|
|
|
|
|
that. */
|
|
|
|
|
struct type *elttype = check_typedef (TYPE_TARGET_TYPE (type));
|
|
|
|
|
CORE_ADDR addr;
|
|
|
|
|
struct gdbarch *arch = get_type_arch (type);
|
|
|
|
|
int unit_size = gdbarch_addressable_memory_unit_size (arch);
|
|
|
|
|
|
|
|
|
|
addr = unpack_pointer (type, valaddr + embedded_offset * unit_size);
|
|
|
|
|
if (options->addressprint)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered (paddress (arch, addr), stream);
|
|
|
|
|
fputs_filtered (" ", stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("b", stream);
|
|
|
|
|
val_print_string (TYPE_TARGET_TYPE (elttype), "ASCII", addr,
|
|
|
|
|
high_bound - low_bound + 1, stream,
|
|
|
|
|
options);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Fall through. */
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_METHODPTR:
|
|
|
|
|
case TYPE_CODE_MEMBERPTR:
|
2016-11-08 17:02:42 +01:00
|
|
|
|
c_val_print (type, embedded_offset, address, stream,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
recurse, val, options);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_INT:
|
|
|
|
|
/* Recognize the unit type. */
|
|
|
|
|
if (TYPE_UNSIGNED (type) && TYPE_LENGTH (type) == 0
|
|
|
|
|
&& TYPE_NAME (type) != NULL && strcmp (TYPE_NAME (type), "()") == 0)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered ("()", stream);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
goto generic_print;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_STRING:
|
|
|
|
|
{
|
|
|
|
|
struct gdbarch *arch = get_type_arch (type);
|
|
|
|
|
int unit_size = gdbarch_addressable_memory_unit_size (arch);
|
|
|
|
|
LONGEST low_bound, high_bound;
|
|
|
|
|
|
|
|
|
|
if (!get_array_bounds (type, &low_bound, &high_bound))
|
|
|
|
|
error (_("Could not determine the array bounds"));
|
|
|
|
|
|
|
|
|
|
/* If we see a plain TYPE_CODE_STRING, then we're printing a
|
|
|
|
|
byte string, hence the choice of "ASCII" as the
|
|
|
|
|
encoding. */
|
|
|
|
|
fputs_filtered ("b", stream);
|
|
|
|
|
rust_printstr (stream, TYPE_TARGET_TYPE (type),
|
|
|
|
|
valaddr + embedded_offset * unit_size,
|
|
|
|
|
high_bound - low_bound + 1, "ASCII", 0, options);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_ARRAY:
|
|
|
|
|
{
|
|
|
|
|
LONGEST low_bound, high_bound;
|
|
|
|
|
|
|
|
|
|
if (get_array_bounds (type, &low_bound, &high_bound)
|
|
|
|
|
&& high_bound - low_bound + 1 == 0)
|
|
|
|
|
fputs_filtered ("[]", stream);
|
|
|
|
|
else
|
|
|
|
|
goto generic_print;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_UNION:
|
|
|
|
|
{
|
|
|
|
|
int j, nfields, first_field, is_tuple, start;
|
|
|
|
|
struct type *variant_type;
|
|
|
|
|
struct disr_info disr;
|
|
|
|
|
struct value_print_options opts;
|
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
/* Untagged unions are printed as if they are structs.
|
|
|
|
|
Since the field bit positions overlap in the debuginfo,
|
|
|
|
|
the code for printing a union is same as that for a struct,
|
|
|
|
|
the only difference is that the input type will have overlapping
|
|
|
|
|
fields. */
|
|
|
|
|
if (rust_union_is_untagged (type))
|
|
|
|
|
{
|
|
|
|
|
val_print_struct (type, embedded_offset, address, stream,
|
|
|
|
|
recurse, val, options);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
opts = *options;
|
|
|
|
|
opts.deref_ref = 0;
|
|
|
|
|
|
|
|
|
|
disr = rust_get_disr_info (type, valaddr, embedded_offset, address,
|
|
|
|
|
val);
|
|
|
|
|
|
|
|
|
|
if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
|
|
|
|
|
{
|
2016-11-08 16:26:46 +01:00
|
|
|
|
fprintf_filtered (stream, "%s", disr.name.c_str ());
|
|
|
|
|
break;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first_field = 1;
|
|
|
|
|
variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
|
|
|
|
|
nfields = TYPE_NFIELDS (variant_type);
|
|
|
|
|
|
|
|
|
|
is_tuple = (disr.is_encoded
|
|
|
|
|
? rust_tuple_struct_type_p (variant_type)
|
|
|
|
|
: rust_tuple_variant_type_p (variant_type));
|
|
|
|
|
start = disr.is_encoded ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
if (nfields > start)
|
|
|
|
|
{
|
|
|
|
|
/* In case of a non-nullary variant, we output 'Foo(x,y,z)'. */
|
|
|
|
|
if (is_tuple)
|
2016-11-08 16:26:46 +01:00
|
|
|
|
fprintf_filtered (stream, "%s(", disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* struct variant. */
|
2016-11-08 16:26:46 +01:00
|
|
|
|
fprintf_filtered (stream, "%s{", disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* In case of a nullary variant like 'None', just output
|
|
|
|
|
the name. */
|
2016-11-08 16:26:46 +01:00
|
|
|
|
fprintf_filtered (stream, "%s", disr.name.c_str ());
|
|
|
|
|
break;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (j = start; j < TYPE_NFIELDS (variant_type); j++)
|
|
|
|
|
{
|
|
|
|
|
if (!first_field)
|
|
|
|
|
fputs_filtered (", ", stream);
|
|
|
|
|
first_field = 0;
|
|
|
|
|
|
|
|
|
|
if (!is_tuple)
|
|
|
|
|
fprintf_filtered (stream, "%s: ",
|
|
|
|
|
TYPE_FIELD_NAME (variant_type, j));
|
|
|
|
|
|
|
|
|
|
val_print (TYPE_FIELD_TYPE (variant_type, j),
|
|
|
|
|
(embedded_offset
|
|
|
|
|
+ TYPE_FIELD_BITPOS (type, disr.field_no) / 8
|
|
|
|
|
+ TYPE_FIELD_BITPOS (variant_type, j) / 8),
|
|
|
|
|
address,
|
|
|
|
|
stream, recurse + 1, val, &opts,
|
|
|
|
|
current_language);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_tuple)
|
|
|
|
|
fputs_filtered (")", stream);
|
|
|
|
|
else
|
|
|
|
|
fputs_filtered ("}", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_STRUCT:
|
2016-11-08 17:02:42 +01:00
|
|
|
|
val_print_struct (type, embedded_offset, address, stream,
|
|
|
|
|
recurse, val, options);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
break;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
default:
|
|
|
|
|
generic_print:
|
|
|
|
|
/* Nothing special yet. */
|
2016-11-08 17:02:42 +01:00
|
|
|
|
generic_val_print (type, embedded_offset, address, stream,
|
2016-10-29 03:00:43 +02:00
|
|
|
|
recurse, val, options, &rust_decorations);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
static void
|
|
|
|
|
rust_print_type (struct type *type, const char *varstring,
|
|
|
|
|
struct ui_file *stream, int show, int level,
|
|
|
|
|
const struct type_print_options *flags);
|
|
|
|
|
|
|
|
|
|
/* Print a struct or union typedef. */
|
|
|
|
|
static void
|
|
|
|
|
rust_print_struct_def (struct type *type, const char *varstring,
|
2017-02-03 05:01:11 +01:00
|
|
|
|
struct ui_file *stream, int show, int level,
|
|
|
|
|
const struct type_print_options *flags)
|
2016-10-29 03:00:43 +02:00
|
|
|
|
{
|
2017-02-03 05:21:19 +01:00
|
|
|
|
bool is_tuple_struct;
|
|
|
|
|
int i;
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
/* Print a tuple type simply. */
|
|
|
|
|
if (rust_tuple_type_p (type))
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered (TYPE_TAG_NAME (type), stream);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
/* If we see a base class, delegate to C. */
|
|
|
|
|
if (TYPE_N_BASECLASSES (type) > 0)
|
|
|
|
|
c_print_type (type, varstring, stream, show, level, flags);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2016-10-29 03:00:43 +02:00
|
|
|
|
/* This code path is also used by unions. */
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
|
2017-02-03 05:01:11 +01:00
|
|
|
|
fputs_filtered ("struct ", stream);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
else
|
2017-02-03 05:01:11 +01:00
|
|
|
|
fputs_filtered ("union ", stream);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
if (TYPE_TAG_NAME (type) != NULL)
|
|
|
|
|
fputs_filtered (TYPE_TAG_NAME (type), stream);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
is_tuple_struct = rust_tuple_struct_type_p (type);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
if (TYPE_NFIELDS (type) == 0 && !rust_tuple_type_p (type))
|
|
|
|
|
return;
|
|
|
|
|
fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
QUIT;
|
|
|
|
|
if (field_is_static (&TYPE_FIELD (type, i)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* We'd like to print "pub" here as needed, but rustc
|
|
|
|
|
doesn't emit the debuginfo, and our types don't have
|
|
|
|
|
cplus_struct_type attached. */
|
|
|
|
|
|
|
|
|
|
/* For a tuple struct we print the type but nothing
|
|
|
|
|
else. */
|
|
|
|
|
print_spaces_filtered (level + 2, stream);
|
|
|
|
|
if (!is_tuple_struct)
|
|
|
|
|
fprintf_filtered (stream, "%s: ", TYPE_FIELD_NAME (type, i));
|
|
|
|
|
|
|
|
|
|
rust_print_type (TYPE_FIELD_TYPE (type, i), NULL,
|
|
|
|
|
stream, show - 1, level + 2,
|
|
|
|
|
flags);
|
|
|
|
|
fputs_filtered (",\n", stream);
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
fprintfi_filtered (level, stream, is_tuple_struct ? ")" : "}");
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* la_print_typedef implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_print_typedef (struct type *type,
|
|
|
|
|
struct symbol *new_symbol,
|
|
|
|
|
struct ui_file *stream)
|
|
|
|
|
{
|
|
|
|
|
type = check_typedef (type);
|
|
|
|
|
fprintf_filtered (stream, "type %s = ", SYMBOL_PRINT_NAME (new_symbol));
|
|
|
|
|
type_print (type, "", stream, 0);
|
|
|
|
|
fprintf_filtered (stream, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* la_print_type implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_print_type (struct type *type, const char *varstring,
|
|
|
|
|
struct ui_file *stream, int show, int level,
|
|
|
|
|
const struct type_print_options *flags)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
QUIT;
|
|
|
|
|
if (show <= 0
|
|
|
|
|
&& TYPE_NAME (type) != NULL)
|
|
|
|
|
{
|
2016-06-27 17:46:59 +02:00
|
|
|
|
/* Rust calls the unit type "void" in its debuginfo,
|
|
|
|
|
but we don't want to print it as that. */
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_VOID)
|
|
|
|
|
fputs_filtered ("()", stream);
|
|
|
|
|
else
|
|
|
|
|
fputs_filtered (TYPE_NAME (type), stream);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type = check_typedef (type);
|
|
|
|
|
switch (TYPE_CODE (type))
|
|
|
|
|
{
|
2016-06-27 17:46:59 +02:00
|
|
|
|
case TYPE_CODE_VOID:
|
|
|
|
|
fputs_filtered ("()", stream);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
case TYPE_CODE_FUNC:
|
|
|
|
|
/* Delegate varargs to the C printer. */
|
|
|
|
|
if (TYPE_VARARGS (type))
|
|
|
|
|
goto c_printer;
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("fn ", stream);
|
|
|
|
|
if (varstring != NULL)
|
|
|
|
|
fputs_filtered (varstring, stream);
|
|
|
|
|
fputs_filtered ("(", stream);
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
QUIT;
|
|
|
|
|
if (i > 0)
|
|
|
|
|
fputs_filtered (", ", stream);
|
|
|
|
|
rust_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0,
|
|
|
|
|
flags);
|
|
|
|
|
}
|
2016-06-27 17:46:59 +02:00
|
|
|
|
fputs_filtered (")", stream);
|
|
|
|
|
/* If it returns unit, we can omit the return type. */
|
|
|
|
|
if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_VOID)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered (" -> ", stream);
|
|
|
|
|
rust_print_type (TYPE_TARGET_TYPE (type), "", stream, -1, 0, flags);
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_ARRAY:
|
|
|
|
|
{
|
|
|
|
|
LONGEST low_bound, high_bound;
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("[", stream);
|
|
|
|
|
rust_print_type (TYPE_TARGET_TYPE (type), NULL,
|
|
|
|
|
stream, show - 1, level, flags);
|
|
|
|
|
|
|
|
|
|
if (TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCEXPR
|
|
|
|
|
|| TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCLIST)
|
2017-05-22 01:00:10 +02:00
|
|
|
|
fprintf_filtered (stream, "; variable length");
|
2016-04-27 03:38:08 +02:00
|
|
|
|
else if (get_array_bounds (type, &low_bound, &high_bound))
|
2017-05-22 01:00:10 +02:00
|
|
|
|
fprintf_filtered (stream, "; %s",
|
2016-04-27 03:38:08 +02:00
|
|
|
|
plongest (high_bound - low_bound + 1));
|
|
|
|
|
fputs_filtered ("]", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_STRUCT:
|
2017-02-03 05:01:11 +01:00
|
|
|
|
rust_print_struct_def (type, varstring, stream, show, level, flags);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_ENUM:
|
|
|
|
|
{
|
|
|
|
|
int i, len = 0;
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("enum ", stream);
|
|
|
|
|
if (TYPE_TAG_NAME (type) != NULL)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered (TYPE_TAG_NAME (type), stream);
|
|
|
|
|
fputs_filtered (" ", stream);
|
|
|
|
|
len = strlen (TYPE_TAG_NAME (type));
|
|
|
|
|
}
|
|
|
|
|
fputs_filtered ("{\n", stream);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
const char *name = TYPE_FIELD_NAME (type, i);
|
|
|
|
|
|
|
|
|
|
QUIT;
|
|
|
|
|
|
|
|
|
|
if (len > 0
|
|
|
|
|
&& strncmp (name, TYPE_TAG_NAME (type), len) == 0
|
|
|
|
|
&& name[len] == ':'
|
|
|
|
|
&& name[len + 1] == ':')
|
|
|
|
|
name += len + 2;
|
|
|
|
|
fprintfi_filtered (level + 2, stream, "%s,\n", name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("}", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_UNION:
|
|
|
|
|
{
|
2016-06-25 07:39:23 +02:00
|
|
|
|
/* ADT enums. */
|
2017-12-05 22:05:34 +01:00
|
|
|
|
int i;
|
2016-06-25 07:39:23 +02:00
|
|
|
|
/* Skip the discriminant field. */
|
|
|
|
|
int skip_to = 1;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-02-03 05:01:11 +01:00
|
|
|
|
/* Unions and structs have the same syntax in Rust,
|
|
|
|
|
the only difference is that structs are declared with `struct`
|
|
|
|
|
and union with `union`. This difference is handled in the struct
|
|
|
|
|
printer. */
|
|
|
|
|
if (rust_union_is_untagged (type))
|
|
|
|
|
{
|
|
|
|
|
rust_print_struct_def (type, varstring, stream, show, level, flags);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-10-29 03:00:43 +02:00
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
fputs_filtered ("enum ", stream);
|
|
|
|
|
if (TYPE_TAG_NAME (type) != NULL)
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered (TYPE_TAG_NAME (type), stream);
|
|
|
|
|
fputs_filtered (" ", stream);
|
|
|
|
|
}
|
2016-06-25 07:39:23 +02:00
|
|
|
|
fputs_filtered ("{\n", stream);
|
|
|
|
|
|
2016-06-25 16:41:45 +02:00
|
|
|
|
if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
|
|
|
|
|
strlen (RUST_ENUM_PREFIX)) == 0)
|
|
|
|
|
{
|
|
|
|
|
const char *zero_field = strrchr (TYPE_FIELD_NAME (type, 0), '$');
|
|
|
|
|
if (zero_field != NULL && strlen (zero_field) > 1)
|
|
|
|
|
{
|
|
|
|
|
fprintfi_filtered (level + 2, stream, "%s,\n", zero_field + 1);
|
|
|
|
|
/* There is no explicit discriminant field, skip nothing. */
|
|
|
|
|
skip_to = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-04 06:11:46 +01:00
|
|
|
|
else if (TYPE_NFIELDS (type) == 1)
|
|
|
|
|
skip_to = 0;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
struct type *variant_type = TYPE_FIELD_TYPE (type, i);
|
|
|
|
|
const char *name
|
|
|
|
|
= rust_last_path_segment (TYPE_NAME (variant_type));
|
|
|
|
|
|
|
|
|
|
fprintfi_filtered (level + 2, stream, "%s", name);
|
|
|
|
|
|
2016-06-25 07:39:23 +02:00
|
|
|
|
if (TYPE_NFIELDS (variant_type) > skip_to)
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
int first = 1;
|
2017-02-04 06:11:46 +01:00
|
|
|
|
bool is_tuple = (TYPE_NFIELDS (type) == 1
|
|
|
|
|
? rust_tuple_struct_type_p (variant_type)
|
|
|
|
|
: rust_tuple_variant_type_p (variant_type));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
fputs_filtered (is_tuple ? "(" : "{", stream);
|
2016-06-25 07:39:23 +02:00
|
|
|
|
for (j = skip_to; j < TYPE_NFIELDS (variant_type); j++)
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
if (first)
|
|
|
|
|
first = 0;
|
|
|
|
|
else
|
|
|
|
|
fputs_filtered (", ", stream);
|
|
|
|
|
|
|
|
|
|
if (!is_tuple)
|
|
|
|
|
fprintf_filtered (stream, "%s: ",
|
|
|
|
|
TYPE_FIELD_NAME (variant_type, j));
|
|
|
|
|
|
|
|
|
|
rust_print_type (TYPE_FIELD_TYPE (variant_type, j), NULL,
|
|
|
|
|
stream, show - 1, level + 2,
|
|
|
|
|
flags);
|
|
|
|
|
}
|
|
|
|
|
fputs_filtered (is_tuple ? ")" : "}", stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fputs_filtered (",\n", stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fputs_filtered ("}", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
c_printer:
|
|
|
|
|
c_print_type (type, varstring, stream, show, level, flags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Compute the alignment of the type T. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
rust_type_alignment (struct type *t)
|
|
|
|
|
{
|
|
|
|
|
t = check_typedef (t);
|
|
|
|
|
switch (TYPE_CODE (t))
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
error (_("Could not compute alignment of type"));
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_PTR:
|
|
|
|
|
case TYPE_CODE_ENUM:
|
|
|
|
|
case TYPE_CODE_INT:
|
|
|
|
|
case TYPE_CODE_FLT:
|
|
|
|
|
case TYPE_CODE_REF:
|
|
|
|
|
case TYPE_CODE_CHAR:
|
|
|
|
|
case TYPE_CODE_BOOL:
|
|
|
|
|
return TYPE_LENGTH (t);
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_ARRAY:
|
|
|
|
|
case TYPE_CODE_COMPLEX:
|
|
|
|
|
return rust_type_alignment (TYPE_TARGET_TYPE (t));
|
|
|
|
|
|
|
|
|
|
case TYPE_CODE_STRUCT:
|
|
|
|
|
case TYPE_CODE_UNION:
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int align = 1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < TYPE_NFIELDS (t); ++i)
|
|
|
|
|
{
|
|
|
|
|
int a = rust_type_alignment (TYPE_FIELD_TYPE (t, i));
|
|
|
|
|
if (a > align)
|
|
|
|
|
align = a;
|
|
|
|
|
}
|
|
|
|
|
return align;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Like arch_composite_type, but uses TYPE to decide how to allocate
|
|
|
|
|
-- either on an obstack or on a gdbarch. */
|
|
|
|
|
|
|
|
|
|
static struct type *
|
|
|
|
|
rust_composite_type (struct type *original,
|
|
|
|
|
const char *name,
|
|
|
|
|
const char *field1, struct type *type1,
|
|
|
|
|
const char *field2, struct type *type2)
|
|
|
|
|
{
|
|
|
|
|
struct type *result = alloc_type_copy (original);
|
|
|
|
|
int i, nfields, bitpos;
|
|
|
|
|
|
|
|
|
|
nfields = 0;
|
|
|
|
|
if (field1 != NULL)
|
|
|
|
|
++nfields;
|
|
|
|
|
if (field2 != NULL)
|
|
|
|
|
++nfields;
|
|
|
|
|
|
|
|
|
|
TYPE_CODE (result) = TYPE_CODE_STRUCT;
|
|
|
|
|
TYPE_NAME (result) = name;
|
|
|
|
|
TYPE_TAG_NAME (result) = name;
|
|
|
|
|
|
|
|
|
|
TYPE_NFIELDS (result) = nfields;
|
|
|
|
|
TYPE_FIELDS (result)
|
|
|
|
|
= (struct field *) TYPE_ZALLOC (result, nfields * sizeof (struct field));
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
bitpos = 0;
|
|
|
|
|
if (field1 != NULL)
|
|
|
|
|
{
|
|
|
|
|
struct field *field = &TYPE_FIELD (result, i);
|
|
|
|
|
|
|
|
|
|
SET_FIELD_BITPOS (*field, bitpos);
|
|
|
|
|
bitpos += TYPE_LENGTH (type1) * TARGET_CHAR_BIT;
|
|
|
|
|
|
|
|
|
|
FIELD_NAME (*field) = field1;
|
|
|
|
|
FIELD_TYPE (*field) = type1;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
if (field2 != NULL)
|
|
|
|
|
{
|
|
|
|
|
struct field *field = &TYPE_FIELD (result, i);
|
|
|
|
|
int align = rust_type_alignment (type2);
|
|
|
|
|
|
|
|
|
|
if (align != 0)
|
|
|
|
|
{
|
|
|
|
|
int delta;
|
|
|
|
|
|
|
|
|
|
align *= TARGET_CHAR_BIT;
|
|
|
|
|
delta = bitpos % align;
|
|
|
|
|
if (delta != 0)
|
|
|
|
|
bitpos += align - delta;
|
|
|
|
|
}
|
|
|
|
|
SET_FIELD_BITPOS (*field, bitpos);
|
|
|
|
|
|
|
|
|
|
FIELD_NAME (*field) = field2;
|
|
|
|
|
FIELD_TYPE (*field) = type2;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i > 0)
|
|
|
|
|
TYPE_LENGTH (result)
|
|
|
|
|
= (TYPE_FIELD_BITPOS (result, i - 1) / TARGET_CHAR_BIT +
|
|
|
|
|
TYPE_LENGTH (TYPE_FIELD_TYPE (result, i - 1)));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See rust-lang.h. */
|
|
|
|
|
|
|
|
|
|
struct type *
|
|
|
|
|
rust_slice_type (const char *name, struct type *elt_type,
|
|
|
|
|
struct type *usize_type)
|
|
|
|
|
{
|
|
|
|
|
struct type *type;
|
|
|
|
|
|
|
|
|
|
elt_type = lookup_pointer_type (elt_type);
|
|
|
|
|
type = rust_composite_type (elt_type, name,
|
|
|
|
|
"data_ptr", elt_type,
|
|
|
|
|
"length", usize_type);
|
|
|
|
|
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum rust_primitive_types
|
|
|
|
|
{
|
|
|
|
|
rust_primitive_bool,
|
|
|
|
|
rust_primitive_char,
|
|
|
|
|
rust_primitive_i8,
|
|
|
|
|
rust_primitive_u8,
|
|
|
|
|
rust_primitive_i16,
|
|
|
|
|
rust_primitive_u16,
|
|
|
|
|
rust_primitive_i32,
|
|
|
|
|
rust_primitive_u32,
|
|
|
|
|
rust_primitive_i64,
|
|
|
|
|
rust_primitive_u64,
|
|
|
|
|
rust_primitive_isize,
|
|
|
|
|
rust_primitive_usize,
|
|
|
|
|
rust_primitive_f32,
|
|
|
|
|
rust_primitive_f64,
|
|
|
|
|
rust_primitive_unit,
|
|
|
|
|
rust_primitive_str,
|
|
|
|
|
nr_rust_primitive_types
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* la_language_arch_info implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_language_arch_info (struct gdbarch *gdbarch,
|
|
|
|
|
struct language_arch_info *lai)
|
|
|
|
|
{
|
|
|
|
|
const struct builtin_type *builtin = builtin_type (gdbarch);
|
|
|
|
|
struct type *tem;
|
|
|
|
|
struct type **types;
|
|
|
|
|
unsigned int length;
|
|
|
|
|
|
|
|
|
|
types = GDBARCH_OBSTACK_CALLOC (gdbarch, nr_rust_primitive_types + 1,
|
|
|
|
|
struct type *);
|
|
|
|
|
|
|
|
|
|
types[rust_primitive_bool] = arch_boolean_type (gdbarch, 8, 1, "bool");
|
|
|
|
|
types[rust_primitive_char] = arch_character_type (gdbarch, 32, 1, "char");
|
|
|
|
|
types[rust_primitive_i8] = arch_integer_type (gdbarch, 8, 0, "i8");
|
|
|
|
|
types[rust_primitive_u8] = arch_integer_type (gdbarch, 8, 1, "u8");
|
|
|
|
|
types[rust_primitive_i16] = arch_integer_type (gdbarch, 16, 0, "i16");
|
|
|
|
|
types[rust_primitive_u16] = arch_integer_type (gdbarch, 16, 1, "u16");
|
|
|
|
|
types[rust_primitive_i32] = arch_integer_type (gdbarch, 32, 0, "i32");
|
|
|
|
|
types[rust_primitive_u32] = arch_integer_type (gdbarch, 32, 1, "u32");
|
|
|
|
|
types[rust_primitive_i64] = arch_integer_type (gdbarch, 64, 0, "i64");
|
|
|
|
|
types[rust_primitive_u64] = arch_integer_type (gdbarch, 64, 1, "u64");
|
|
|
|
|
|
|
|
|
|
length = 8 * TYPE_LENGTH (builtin->builtin_data_ptr);
|
|
|
|
|
types[rust_primitive_isize] = arch_integer_type (gdbarch, length, 0, "isize");
|
|
|
|
|
types[rust_primitive_usize] = arch_integer_type (gdbarch, length, 1, "usize");
|
|
|
|
|
|
2016-09-06 17:31:03 +02:00
|
|
|
|
types[rust_primitive_f32] = arch_float_type (gdbarch, 32, "f32",
|
|
|
|
|
floatformats_ieee_single);
|
|
|
|
|
types[rust_primitive_f64] = arch_float_type (gdbarch, 64, "f64",
|
|
|
|
|
floatformats_ieee_double);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
types[rust_primitive_unit] = arch_integer_type (gdbarch, 0, 1, "()");
|
|
|
|
|
|
|
|
|
|
tem = make_cv_type (1, 0, types[rust_primitive_u8], NULL);
|
|
|
|
|
types[rust_primitive_str] = rust_slice_type ("&str", tem,
|
|
|
|
|
types[rust_primitive_usize]);
|
|
|
|
|
|
|
|
|
|
lai->primitive_type_vector = types;
|
|
|
|
|
lai->bool_type_default = types[rust_primitive_bool];
|
|
|
|
|
lai->string_char_type = types[rust_primitive_u8];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* A helper for rust_evaluate_subexp that handles OP_FUNCALL. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
rust_evaluate_funcall (struct expression *exp, int *pos, enum noside noside)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int num_args = exp->elts[*pos + 1].longconst;
|
|
|
|
|
const char *method;
|
|
|
|
|
struct value *function, *result, *arg0;
|
|
|
|
|
struct type *type, *fn_type;
|
|
|
|
|
const struct block *block;
|
|
|
|
|
struct block_symbol sym;
|
|
|
|
|
|
|
|
|
|
/* For an ordinary function call we can simply defer to the
|
|
|
|
|
generic implementation. */
|
|
|
|
|
if (exp->elts[*pos + 3].opcode != STRUCTOP_STRUCT)
|
|
|
|
|
return evaluate_subexp_standard (NULL, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
/* Skip over the OP_FUNCALL and the STRUCTOP_STRUCT. */
|
|
|
|
|
*pos += 4;
|
|
|
|
|
method = &exp->elts[*pos + 1].string;
|
|
|
|
|
*pos += 3 + BYTES_TO_EXP_ELEM (exp->elts[*pos].longconst + 1);
|
|
|
|
|
|
|
|
|
|
/* Evaluate the argument to STRUCTOP_STRUCT, then find its
|
|
|
|
|
type in order to look up the method. */
|
|
|
|
|
arg0 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_SKIP)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < num_args; ++i)
|
|
|
|
|
evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
return arg0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 17:51:20 +02:00
|
|
|
|
std::vector<struct value *> args (num_args + 1);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
args[0] = arg0;
|
|
|
|
|
|
|
|
|
|
/* We don't yet implement real Deref semantics. */
|
|
|
|
|
while (TYPE_CODE (value_type (args[0])) == TYPE_CODE_PTR)
|
|
|
|
|
args[0] = value_ind (args[0]);
|
|
|
|
|
|
|
|
|
|
type = value_type (args[0]);
|
|
|
|
|
if ((TYPE_CODE (type) != TYPE_CODE_STRUCT
|
|
|
|
|
&& TYPE_CODE (type) != TYPE_CODE_UNION
|
|
|
|
|
&& TYPE_CODE (type) != TYPE_CODE_ENUM)
|
|
|
|
|
|| rust_tuple_type_p (type))
|
|
|
|
|
error (_("Method calls only supported on struct or enum types"));
|
|
|
|
|
if (TYPE_TAG_NAME (type) == NULL)
|
|
|
|
|
error (_("Method call on nameless type"));
|
|
|
|
|
|
2016-09-22 17:51:20 +02:00
|
|
|
|
std::string name = std::string (TYPE_TAG_NAME (type)) + "::" + method;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
block = get_selected_block (0);
|
2016-09-22 17:51:20 +02:00
|
|
|
|
sym = lookup_symbol (name.c_str (), block, VAR_DOMAIN, NULL);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
if (sym.symbol == NULL)
|
2016-09-22 17:51:20 +02:00
|
|
|
|
error (_("Could not find function named '%s'"), name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
fn_type = SYMBOL_TYPE (sym.symbol);
|
|
|
|
|
if (TYPE_NFIELDS (fn_type) == 0)
|
2016-09-22 17:51:20 +02:00
|
|
|
|
error (_("Function '%s' takes no arguments"), name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (TYPE_FIELD_TYPE (fn_type, 0)) == TYPE_CODE_PTR)
|
|
|
|
|
args[0] = value_addr (args[0]);
|
|
|
|
|
|
|
|
|
|
function = address_of_variable (sym.symbol, block);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_args; ++i)
|
|
|
|
|
args[i + 1] = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
|
|
|
result = value_zero (TYPE_TARGET_TYPE (fn_type), not_lval);
|
|
|
|
|
else
|
Stop assuming no-debug-info functions return int
The fact that GDB defaults to assuming that functions return int, when
it has no debug info for the function has been a recurring source of
user confusion. Recently this came up on the errno pretty printer
discussions. Shortly after, it came up again on IRC, with someone
wondering why does getenv() in GDB return a negative int:
(gdb) p getenv("PATH")
$1 = -6185
This question (with s/getenv/random-other-C-runtime-function) is a FAQ
on IRC.
The reason for the above is:
(gdb) p getenv
$2 = {<text variable, no debug info>} 0x7ffff7751d80 <getenv>
(gdb) ptype getenv
type = int ()
... which means that GDB truncated the 64-bit pointer that is actually
returned from getent to 32-bit, and then sign-extended it:
(gdb) p /x -6185
$6 = 0xffffe7d7
The workaround is to cast the function to the right type, like:
(gdb) p ((char *(*) (const char *)) getenv) ("PATH")
$3 = 0x7fffffffe7d7 "/usr/local/bin:/"...
IMO, we should do better than this.
I see the "assume-int" issue the same way I see printing bogus values
for optimized-out variables instead of "<optimized out>" -- I'd much
rather that the debugger tells me "I don't know" and tells me how to
fix it than showing me bogus misleading results, making me go around
tilting at windmills.
If GDB prints a signed integer when you're expecting a pointer or
aggregate, you at least have some sense that something is off, but
consider the case of the function actually returning a 64-bit integer.
For example, compile this without debug info:
unsigned long long
function ()
{
return 0x7fffffffffffffff;
}
Currently, with pristine GDB, you get:
(gdb) p function ()
$1 = -1 # incorrect
(gdb) p /x function ()
$2 = 0xffffffff # incorrect
maybe after spending a few hours debugging you suspect something is
wrong with that -1, and do:
(gdb) ptype function
type = int ()
and maybe, just maybe, you realize that the function actually returns
unsigned long long. And you try to fix it with:
(gdb) p /x (unsigned long long) function ()
$3 = 0xffffffffffffffff # incorrect
... which still produces the wrong result, because GDB simply applied
int to unsigned long long conversion. Meaning, it sign-extended the
integer that it extracted from the return of the function, to 64-bits.
and then maybe, after asking around on IRC, you realize you have to
cast the function to a pointer of the right type, and call that. It
won't be easy, but after a few missteps, you'll get to it:
..... (gdb) p /x ((unsigned long long(*) ()) function) ()
$666 = 0x7fffffffffffffff # finally! :-)
So to improve on the user experience, this patch does the following
(interrelated) things:
- makes no-debug-info functions no longer default to "int" as return
type. Instead, they're left with NULL/"<unknown return type>"
return type.
(gdb) ptype getenv
type = <unknown return type> ()
- makes calling a function with unknown return type an error.
(gdb) p getenv ("PATH")
'getenv' has unknown return type; cast the call to its declared return type
- and then to make it easier to call the function, makes it possible
to _only_ cast the return of the function to the right type,
instead of having to cast the function to a function pointer:
(gdb) p (char *) getenv ("PATH") # now Just Works
$3 = 0x7fffffffe7d7 "/usr/local/bin:/"...
(gdb) p ((char *(*) (const char *)) getenv) ("PATH") # continues working
$4 = 0x7fffffffe7d7 "/usr/local/bin:/"...
I.e., it makes GDB default the function's return type to the type
of the cast, and the function's parameters to the type of the
arguments passed down.
After this patch, here's what you'll get for the "unsigned long long"
example above:
(gdb) p function ()
'function' has unknown return type; cast the call to its declared return type
(gdb) p /x (unsigned long long) function ()
$4 = 0x7fffffffffffffff # correct!
Note that while with "print" GDB shows the name of the function that
has the problem:
(gdb) p getenv ("PATH")
'getenv' has unknown return type; cast the call to its declared return type
which can by handy in more complicated expressions, "ptype" does not:
(gdb) ptype getenv ("PATH")
function has unknown return type; cast the call to its declared return type
This will be fixed in the next patch.
gdb/ChangeLog:
2017-09-04 Pedro Alves <palves@redhat.com>
* ada-lang.c (ada_evaluate_subexp) <TYPE_CODE_FUNC>: Don't handle
TYPE_GNU_IFUNC specially here. Throw error if return type is
unknown.
* ada-typeprint.c (print_func_type): Handle functions with unknown
return type.
* c-typeprint.c (c_type_print_base): Handle functions and methods
with unknown return type.
* compile/compile-c-symbols.c (convert_symbol_bmsym)
<mst_text_gnu_ifunc>: Use nodebug_text_gnu_ifunc_symbol.
* compile/compile-c-types.c: Include "objfiles.h".
(convert_func): For functions with unknown return type, warn and
default to int.
* compile/compile-object-run.c (compile_object_run): Adjust call
to call_function_by_hand_dummy.
* elfread.c (elf_gnu_ifunc_resolve_addr): Adjust call to
call_function_by_hand.
* eval.c (evaluate_subexp_standard): Adjust calls to
call_function_by_hand. Handle functions and methods with unknown
return type. Pass expect_type to call_function_by_hand.
* f-typeprint.c (f_type_print_base): Handle functions with unknown
return type.
* gcore.c (call_target_sbrk): Adjust call to
call_function_by_hand.
* gdbtypes.c (objfile_type): Leave nodebug text symbol with NULL
return type instead of int. Make nodebug_text_gnu_ifunc_symbol be
an integer address type instead of nodebug.
* guile/scm-value.c (gdbscm_value_call): Adjust call to
call_function_by_hand.
* infcall.c (error_call_unknown_return_type): New function.
(call_function_by_hand): New "default_return_type" parameter.
Pass it down.
(call_function_by_hand_dummy): New "default_return_type"
parameter. Use it instead of defaulting to int. If there's no
default and the return type is unknown, throw an error. If
there's a default return type, and the called function has no
debug info, then assume the function is prototyped.
* infcall.h (call_function_by_hand, call_function_by_hand_dummy):
New "default_return_type" parameter.
(error_call_unknown_return_type): New declaration.
* linux-fork.c (call_lseek): Cast return type of lseek.
(inferior_call_waitpid, checkpoint_command): Adjust calls to
call_function_by_hand.
* linux-tdep.c (linux_infcall_mmap, linux_infcall_munmap): Adjust
calls to call_function_by_hand.
* m2-typeprint.c (m2_procedure): Handle functions with unknown
return type.
* objc-lang.c (lookup_objc_class, lookup_child_selector)
(value_nsstring, print_object_command): Adjust calls to
call_function_by_hand.
* p-typeprint.c (pascal_type_print_varspec_prefix): Handle
functions with unknown return type.
(pascal_type_print_func_varspec_suffix): New function.
(pascal_type_print_varspec_suffix) <TYPE_CODE_FUNC,
TYPE_CODE_METHOD>: Use it.
* python/py-value.c (valpy_call): Adjust call to
call_function_by_hand.
* rust-lang.c (rust_evaluate_funcall): Adjust call to
call_function_by_hand.
* valarith.c (value_x_binop, value_x_unop): Adjust calls to
call_function_by_hand.
* valops.c (value_allocate_space_in_inferior): Adjust call to
call_function_by_hand.
* typeprint.c (type_print_unknown_return_type): New function.
* typeprint.h (type_print_unknown_return_type): New declaration.
gdb/testsuite/ChangeLog:
2017-09-04 Pedro Alves <palves@redhat.com>
* gdb.base/break-main-file-remove-fail.exp (test_remove_bp): Cast
return type of munmap in infcall.
* gdb.base/break-probes.exp: Cast return type of foo in infcall.
* gdb.base/checkpoint.exp: Simplify using for loop. Cast return
type of ftell in infcall.
* gdb.base/dprintf-detach.exp (dprintf_detach_test): Cast return
type of getpid in infcall.
* gdb.base/infcall-exec.exp: Cast return type of execlp in
infcall.
* gdb.base/info-os.exp: Cast return type of getpid in infcall.
Bail on failure to extract the pid.
* gdb.base/nodebug.c: #include <stdint.h>.
(multf, multf_noproto, mult, mult_noproto, add8, add8_noproto):
New functions.
* gdb.base/nodebug.exp (test_call_promotion): New procedure.
Change expected output of print/whatis/ptype with functions with
no debug info. Test all supported languages. Call
test_call_promotion.
* gdb.compile/compile.exp: Adjust expected output to expect
warning.
* gdb.threads/siginfo-threads.exp: Likewise.
2017-09-04 21:21:13 +02:00
|
|
|
|
result = call_function_by_hand (function, NULL, num_args + 1, args.data ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-27 18:28:56 +02:00
|
|
|
|
/* A helper for rust_evaluate_subexp that handles OP_RANGE. */
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
rust_range (struct expression *exp, int *pos, enum noside noside)
|
|
|
|
|
{
|
2016-04-27 18:28:56 +02:00
|
|
|
|
enum range_type kind;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
struct value *low = NULL, *high = NULL;
|
|
|
|
|
struct value *addrval, *result;
|
|
|
|
|
CORE_ADDR addr;
|
|
|
|
|
struct type *range_type;
|
|
|
|
|
struct type *index_type;
|
|
|
|
|
struct type *temp_type;
|
|
|
|
|
const char *name;
|
|
|
|
|
|
2016-04-27 18:28:56 +02:00
|
|
|
|
kind = (enum range_type) longest_to_int (exp->elts[*pos + 1].longconst);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
*pos += 3;
|
|
|
|
|
|
|
|
|
|
if (kind == HIGH_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
|
|
|
|
|
low = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
if (kind == LOW_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
|
|
|
|
|
high = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_SKIP)
|
|
|
|
|
return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1);
|
|
|
|
|
|
|
|
|
|
if (low == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (high == NULL)
|
|
|
|
|
{
|
|
|
|
|
index_type = NULL;
|
|
|
|
|
name = "std::ops::RangeFull";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
index_type = value_type (high);
|
|
|
|
|
name = "std::ops::RangeTo";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (high == NULL)
|
|
|
|
|
{
|
|
|
|
|
index_type = value_type (low);
|
|
|
|
|
name = "std::ops::RangeFrom";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!types_equal (value_type (low), value_type (high)))
|
|
|
|
|
error (_("Range expression with different types"));
|
|
|
|
|
index_type = value_type (low);
|
|
|
|
|
name = "std::ops::Range";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we don't have an index type, just allocate this on the
|
|
|
|
|
arch. Here any type will do. */
|
|
|
|
|
temp_type = (index_type == NULL
|
|
|
|
|
? language_bool_type (exp->language_defn, exp->gdbarch)
|
|
|
|
|
: index_type);
|
|
|
|
|
/* It would be nicer to cache the range type. */
|
|
|
|
|
range_type = rust_composite_type (temp_type, name,
|
|
|
|
|
low == NULL ? NULL : "start", index_type,
|
|
|
|
|
high == NULL ? NULL : "end", index_type);
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
|
|
|
return value_zero (range_type, lval_memory);
|
|
|
|
|
|
|
|
|
|
addrval = value_allocate_space_in_inferior (TYPE_LENGTH (range_type));
|
|
|
|
|
addr = value_as_long (addrval);
|
|
|
|
|
result = value_at_lazy (range_type, addr);
|
|
|
|
|
|
|
|
|
|
if (low != NULL)
|
|
|
|
|
{
|
|
|
|
|
struct value *start = value_struct_elt (&result, NULL, "start", NULL,
|
|
|
|
|
"range");
|
|
|
|
|
|
|
|
|
|
value_assign (start, low);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (high != NULL)
|
|
|
|
|
{
|
|
|
|
|
struct value *end = value_struct_elt (&result, NULL, "end", NULL,
|
|
|
|
|
"range");
|
|
|
|
|
|
|
|
|
|
value_assign (end, high);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = value_at_lazy (range_type, addr);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A helper function to compute the range and kind given a range
|
|
|
|
|
value. TYPE is the type of the range value. RANGE is the range
|
|
|
|
|
value. LOW, HIGH, and KIND are out parameters. The LOW and HIGH
|
|
|
|
|
parameters might be filled in, or might not be, depending on the
|
|
|
|
|
kind of range this is. KIND will always be set to the appropriate
|
|
|
|
|
value describing the kind of range, and this can be used to
|
|
|
|
|
determine whether LOW or HIGH are valid. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_compute_range (struct type *type, struct value *range,
|
|
|
|
|
LONGEST *low, LONGEST *high,
|
2016-04-27 18:28:56 +02:00
|
|
|
|
enum range_type *kind)
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
*low = 0;
|
|
|
|
|
*high = 0;
|
|
|
|
|
*kind = BOTH_BOUND_DEFAULT;
|
|
|
|
|
|
|
|
|
|
if (TYPE_NFIELDS (type) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (type, 0), "start") == 0)
|
|
|
|
|
{
|
|
|
|
|
*kind = HIGH_BOUND_DEFAULT;
|
|
|
|
|
*low = value_as_long (value_field (range, 0));
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
if (TYPE_NFIELDS (type) > i
|
|
|
|
|
&& strcmp (TYPE_FIELD_NAME (type, i), "end") == 0)
|
|
|
|
|
{
|
|
|
|
|
*kind = (*kind == BOTH_BOUND_DEFAULT
|
|
|
|
|
? LOW_BOUND_DEFAULT : NONE_BOUND_DEFAULT);
|
|
|
|
|
*high = value_as_long (value_field (range, i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A helper for rust_evaluate_subexp that handles BINOP_SUBSCRIPT. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
rust_subscript (struct expression *exp, int *pos, enum noside noside,
|
|
|
|
|
int for_addr)
|
|
|
|
|
{
|
|
|
|
|
struct value *lhs, *rhs, *result;
|
|
|
|
|
struct type *rhstype;
|
2016-05-18 18:48:41 +02:00
|
|
|
|
LONGEST low, high_bound;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
/* Initialized to appease the compiler. */
|
2016-04-27 18:28:56 +02:00
|
|
|
|
enum range_type kind = BOTH_BOUND_DEFAULT;
|
2016-05-18 18:48:41 +02:00
|
|
|
|
LONGEST high = 0;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
int want_slice = 0;
|
|
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
|
lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
rhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_SKIP)
|
|
|
|
|
return lhs;
|
|
|
|
|
|
|
|
|
|
rhstype = check_typedef (value_type (rhs));
|
|
|
|
|
if (rust_range_type_p (rhstype))
|
|
|
|
|
{
|
|
|
|
|
if (!for_addr)
|
|
|
|
|
error (_("Can't take slice of array without '&'"));
|
|
|
|
|
rust_compute_range (rhstype, rhs, &low, &high, &kind);
|
|
|
|
|
want_slice = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
low = value_as_long (rhs);
|
|
|
|
|
|
2017-10-02 21:47:15 +02:00
|
|
|
|
struct type *type = check_typedef (value_type (lhs));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
|
|
|
{
|
2017-10-02 21:47:15 +02:00
|
|
|
|
struct type *base_type = nullptr;
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
|
|
|
|
|
base_type = TYPE_TARGET_TYPE (type);
|
|
|
|
|
else if (rust_slice_type_p (type))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < TYPE_NFIELDS (type); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (type, i), "data_ptr") == 0)
|
|
|
|
|
{
|
|
|
|
|
base_type = TYPE_TARGET_TYPE (TYPE_FIELD_TYPE (type, i));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (base_type == nullptr)
|
|
|
|
|
error (_("Could not find 'data_ptr' in slice type"));
|
|
|
|
|
}
|
|
|
|
|
else if (TYPE_CODE (type) == TYPE_CODE_PTR)
|
|
|
|
|
base_type = TYPE_TARGET_TYPE (type);
|
|
|
|
|
else
|
|
|
|
|
error (_("Cannot subscript non-array type"));
|
|
|
|
|
|
|
|
|
|
struct type *new_type;
|
|
|
|
|
if (want_slice)
|
|
|
|
|
{
|
|
|
|
|
if (rust_slice_type_p (type))
|
|
|
|
|
new_type = type;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
struct type *usize
|
|
|
|
|
= language_lookup_primitive_type (exp->language_defn,
|
|
|
|
|
exp->gdbarch,
|
|
|
|
|
"usize");
|
|
|
|
|
new_type = rust_slice_type ("&[*gdb*]", base_type, usize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
new_type = base_type;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
2017-10-02 21:47:15 +02:00
|
|
|
|
return value_zero (new_type, VALUE_LVAL (lhs));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LONGEST low_bound;
|
|
|
|
|
struct value *base;
|
|
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
|
|
|
|
|
{
|
|
|
|
|
base = lhs;
|
|
|
|
|
if (!get_array_bounds (type, &low_bound, &high_bound))
|
|
|
|
|
error (_("Can't compute array bounds"));
|
|
|
|
|
if (low_bound != 0)
|
|
|
|
|
error (_("Found array with non-zero lower bound"));
|
|
|
|
|
++high_bound;
|
|
|
|
|
}
|
|
|
|
|
else if (rust_slice_type_p (type))
|
|
|
|
|
{
|
|
|
|
|
struct value *len;
|
|
|
|
|
|
|
|
|
|
base = value_struct_elt (&lhs, NULL, "data_ptr", NULL, "slice");
|
|
|
|
|
len = value_struct_elt (&lhs, NULL, "length", NULL, "slice");
|
|
|
|
|
low_bound = 0;
|
|
|
|
|
high_bound = value_as_long (len);
|
|
|
|
|
}
|
2016-07-06 07:25:10 +02:00
|
|
|
|
else if (TYPE_CODE (type) == TYPE_CODE_PTR)
|
|
|
|
|
{
|
|
|
|
|
base = lhs;
|
|
|
|
|
low_bound = 0;
|
|
|
|
|
high_bound = LONGEST_MAX;
|
|
|
|
|
}
|
2016-04-27 03:38:08 +02:00
|
|
|
|
else
|
|
|
|
|
error (_("Cannot subscript non-array type"));
|
|
|
|
|
|
|
|
|
|
if (want_slice
|
|
|
|
|
&& (kind == BOTH_BOUND_DEFAULT || kind == LOW_BOUND_DEFAULT))
|
|
|
|
|
low = low_bound;
|
|
|
|
|
if (low < 0)
|
|
|
|
|
error (_("Index less than zero"));
|
|
|
|
|
if (low > high_bound)
|
|
|
|
|
error (_("Index greater than length"));
|
|
|
|
|
|
|
|
|
|
result = value_subscript (base, low);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (for_addr)
|
|
|
|
|
{
|
|
|
|
|
if (want_slice)
|
|
|
|
|
{
|
|
|
|
|
struct type *usize, *slice;
|
|
|
|
|
CORE_ADDR addr;
|
|
|
|
|
struct value *addrval, *tem;
|
|
|
|
|
|
|
|
|
|
if (kind == BOTH_BOUND_DEFAULT || kind == HIGH_BOUND_DEFAULT)
|
|
|
|
|
high = high_bound;
|
|
|
|
|
if (high < 0)
|
|
|
|
|
error (_("High index less than zero"));
|
|
|
|
|
if (low > high)
|
|
|
|
|
error (_("Low index greater than high index"));
|
|
|
|
|
if (high > high_bound)
|
|
|
|
|
error (_("High index greater than length"));
|
|
|
|
|
|
|
|
|
|
usize = language_lookup_primitive_type (exp->language_defn,
|
|
|
|
|
exp->gdbarch,
|
|
|
|
|
"usize");
|
2017-10-02 21:55:42 +02:00
|
|
|
|
const char *new_name = ((type != nullptr
|
|
|
|
|
&& rust_slice_type_p (type))
|
|
|
|
|
? TYPE_NAME (type) : "&[*gdb*]");
|
|
|
|
|
|
|
|
|
|
slice = rust_slice_type (new_name, value_type (result), usize);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
addrval = value_allocate_space_in_inferior (TYPE_LENGTH (slice));
|
|
|
|
|
addr = value_as_long (addrval);
|
|
|
|
|
tem = value_at_lazy (slice, addr);
|
|
|
|
|
|
|
|
|
|
value_assign (value_field (tem, 0), value_addr (result));
|
|
|
|
|
value_assign (value_field (tem, 1),
|
|
|
|
|
value_from_longest (usize, high - low));
|
|
|
|
|
|
|
|
|
|
result = value_at_lazy (slice, addr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
result = value_addr (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* evaluate_exp implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static struct value *
|
|
|
|
|
rust_evaluate_subexp (struct type *expect_type, struct expression *exp,
|
|
|
|
|
int *pos, enum noside noside)
|
|
|
|
|
{
|
|
|
|
|
struct value *result;
|
|
|
|
|
|
|
|
|
|
switch (exp->elts[*pos].opcode)
|
|
|
|
|
{
|
Handle dereferencing Rust trait objects
In Rust, virtual tables work a bit differently than they do in C++. In
C++, as you know, they are connected to a particular class hierarchy.
Rust, instead, can generate a virtual table for potentially any type --
in fact, one such virtual table for each trait (a trait is similar to an
abstract class or to a Java interface) that a type implements.
Objects that are referenced via a trait can't currently be inspected by
gdb. This patch implements the Rust equivalent of "set print object".
gdb relies heavily on the C++ ABI to decode virtual tables; primarily to
make "set print object" work; but also "info vtbl". However, Rust does
not currently have a specified ABI, so this approach seems unwise to
emulate.
Instead, I've changed the Rust compiler to emit some DWARF that
describes trait objects (previously their internal structure was
opaque), vtables (currently just a size -- but I hope to expand this in
the future), and the concrete type for which a vtable was emitted.
The concrete type is expressed as a DW_AT_containing_type on the
vtable's type. This is a small extension to DWARF.
This patch adds a new entry to quick_symbol_functions to return the
symtab that holds a data address. Previously there was no way in gdb to
look up a full (only minimal) non-text symbol by address. The psymbol
implementation of this method works by lazily filling in a map that is
added to the objfile. This avoids slowing down psymbol reading for a
feature that is likely to not be used too frequently.
I did not update .gdb_index. My thinking here is that the DWARF 5
indices will obsolete .gdb_index soon-ish, meaning that adding a new
feature to them is probably wasted work. If necessary I can update the
DWARF 5 index code when it lands in gdb.
Regression tested on x86-64 Fedora 25.
2017-11-17 Tom Tromey <tom@tromey.com>
* symtab.h (struct symbol) <is_rust_vtable>: New member.
(struct rust_vtable_symbol): New.
(find_symbol_at_address): Declare.
* symtab.c (find_symbol_at_address): New function.
* symfile.h (struct quick_symbol_functions)
<find_compunit_symtab_by_address>: New member.
* symfile-debug.c (debug_qf_find_compunit_symtab_by_address): New
function.
(debug_sym_quick_functions): Link to
debug_qf_find_compunit_symtab_by_address.
* rust-lang.c (rust_get_trait_object_pointer): New function.
(rust_evaluate_subexp) <case UNOP_IND>: New case. Call
rust_get_trait_object_pointer.
* psymtab.c (psym_relocate): Clear psymbol_map.
(psym_fill_psymbol_map, psym_find_compunit_symtab_by_address): New
functions.
(psym_functions): Link to psym_find_compunit_symtab_by_address.
* objfiles.h (struct objfile) <psymbol_map>: New member.
* dwarf2read.c (dwarf2_gdb_index_functions): Update.
(process_die) <DW_TAG_variable>: New case. Call read_variable.
(rust_containing_type, read_variable): New functions.
2017-11-17 Tom Tromey <tom@tromey.com>
* gdb.rust/traits.rs: New file.
* gdb.rust/traits.exp: New file.
2017-07-06 14:44:38 +02:00
|
|
|
|
case UNOP_IND:
|
|
|
|
|
{
|
|
|
|
|
if (noside != EVAL_NORMAL)
|
|
|
|
|
result = evaluate_subexp_standard (expect_type, exp, pos, noside);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++*pos;
|
|
|
|
|
struct value *value = evaluate_subexp (expect_type, exp, pos,
|
|
|
|
|
noside);
|
|
|
|
|
|
|
|
|
|
struct value *trait_ptr = rust_get_trait_object_pointer (value);
|
|
|
|
|
if (trait_ptr != NULL)
|
|
|
|
|
value = trait_ptr;
|
|
|
|
|
|
|
|
|
|
result = value_ind (value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
case UNOP_COMPLEMENT:
|
|
|
|
|
{
|
|
|
|
|
struct value *value;
|
|
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
|
value = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
if (noside == EVAL_SKIP)
|
|
|
|
|
{
|
|
|
|
|
/* Preserving the type is enough. */
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
if (TYPE_CODE (value_type (value)) == TYPE_CODE_BOOL)
|
|
|
|
|
result = value_from_longest (value_type (value),
|
|
|
|
|
value_logical_not (value));
|
|
|
|
|
else
|
|
|
|
|
result = value_complement (value);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BINOP_SUBSCRIPT:
|
|
|
|
|
result = rust_subscript (exp, pos, noside, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_FUNCALL:
|
|
|
|
|
result = rust_evaluate_funcall (exp, pos, noside);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
{
|
|
|
|
|
int pc = (*pos)++;
|
|
|
|
|
struct type *type = exp->elts[pc + 1].type;
|
|
|
|
|
int arglen = longest_to_int (exp->elts[pc + 2].longconst);
|
|
|
|
|
int i;
|
|
|
|
|
CORE_ADDR addr = 0;
|
|
|
|
|
struct value *addrval = NULL;
|
|
|
|
|
|
|
|
|
|
*pos += 3;
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
addrval = value_allocate_space_in_inferior (TYPE_LENGTH (type));
|
|
|
|
|
addr = value_as_long (addrval);
|
|
|
|
|
result = value_at_lazy (type, addr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arglen > 0 && exp->elts[*pos].opcode == OP_OTHERS)
|
|
|
|
|
{
|
|
|
|
|
struct value *init;
|
|
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
|
init = rust_evaluate_subexp (NULL, exp, pos, noside);
|
|
|
|
|
if (noside == EVAL_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
/* This isn't quite right but will do for the time
|
|
|
|
|
being, seeing that we can't implement the Copy
|
|
|
|
|
trait anyway. */
|
|
|
|
|
value_assign (result, init);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--arglen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gdb_assert (arglen % 2 == 0);
|
|
|
|
|
for (i = 0; i < arglen; i += 2)
|
|
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
const char *fieldname;
|
|
|
|
|
struct value *value, *field;
|
|
|
|
|
|
|
|
|
|
gdb_assert (exp->elts[*pos].opcode == OP_NAME);
|
|
|
|
|
++*pos;
|
|
|
|
|
len = longest_to_int (exp->elts[*pos].longconst);
|
|
|
|
|
++*pos;
|
|
|
|
|
fieldname = &exp->elts[*pos].string;
|
|
|
|
|
*pos += 2 + BYTES_TO_EXP_ELEM (len + 1);
|
|
|
|
|
|
|
|
|
|
value = rust_evaluate_subexp (NULL, exp, pos, noside);
|
|
|
|
|
if (noside == EVAL_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
field = value_struct_elt (&result, NULL, fieldname, NULL,
|
|
|
|
|
"structure");
|
|
|
|
|
value_assign (field, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_SKIP)
|
|
|
|
|
return value_from_longest (builtin_type (exp->gdbarch)->builtin_int,
|
|
|
|
|
1);
|
|
|
|
|
else if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
|
|
|
result = allocate_value (type);
|
|
|
|
|
else
|
|
|
|
|
result = value_at_lazy (type, addr);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_RUST_ARRAY:
|
|
|
|
|
{
|
|
|
|
|
int pc = (*pos)++;
|
|
|
|
|
int copies;
|
|
|
|
|
struct value *elt;
|
|
|
|
|
struct value *ncopies;
|
|
|
|
|
|
|
|
|
|
elt = rust_evaluate_subexp (NULL, exp, pos, noside);
|
|
|
|
|
ncopies = rust_evaluate_subexp (NULL, exp, pos, noside);
|
|
|
|
|
copies = value_as_long (ncopies);
|
|
|
|
|
if (copies < 0)
|
|
|
|
|
error (_("Array with negative number of elements"));
|
|
|
|
|
|
|
|
|
|
if (noside == EVAL_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2016-09-22 17:51:20 +02:00
|
|
|
|
std::vector<struct value *> eltvec (copies);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < copies; ++i)
|
|
|
|
|
eltvec[i] = elt;
|
2016-09-22 17:51:20 +02:00
|
|
|
|
result = value_array (0, copies - 1, eltvec.data ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
struct type *arraytype
|
|
|
|
|
= lookup_array_range_type (value_type (elt), 0, copies - 1);
|
|
|
|
|
result = allocate_value (arraytype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STRUCTOP_ANONYMOUS:
|
|
|
|
|
{
|
|
|
|
|
/* Anonymous field access, i.e. foo.1. */
|
|
|
|
|
struct value *lhs;
|
|
|
|
|
int pc, field_number, nfields;
|
|
|
|
|
struct type *type, *variant_type;
|
|
|
|
|
struct disr_info disr;
|
|
|
|
|
|
|
|
|
|
pc = (*pos)++;
|
|
|
|
|
field_number = longest_to_int (exp->elts[pc + 1].longconst);
|
|
|
|
|
(*pos) += 2;
|
|
|
|
|
lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
|
|
|
|
type = value_type (lhs);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
/* Untagged unions can't have anonymous field access since
|
|
|
|
|
they can only have named fields. */
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_UNION
|
|
|
|
|
&& !rust_union_is_untagged (type))
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
disr = rust_get_disr_info (type, value_contents (lhs),
|
|
|
|
|
value_embedded_offset (lhs),
|
|
|
|
|
value_address (lhs), lhs);
|
|
|
|
|
|
|
|
|
|
if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
|
|
|
|
|
{
|
|
|
|
|
variant_type = NULL;
|
|
|
|
|
nfields = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
|
|
|
|
|
nfields = TYPE_NFIELDS (variant_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!disr.is_encoded)
|
|
|
|
|
++field_number;
|
|
|
|
|
|
|
|
|
|
if (field_number >= nfields || field_number < 0)
|
|
|
|
|
error(_("Cannot access field %d of variant %s, \
|
|
|
|
|
there are only %d fields"),
|
|
|
|
|
disr.is_encoded ? field_number : field_number - 1,
|
2016-11-08 16:26:46 +01:00
|
|
|
|
disr.name.c_str (),
|
2016-04-27 03:38:08 +02:00
|
|
|
|
disr.is_encoded ? nfields : nfields - 1);
|
|
|
|
|
|
|
|
|
|
if (!(disr.is_encoded
|
|
|
|
|
? rust_tuple_struct_type_p (variant_type)
|
|
|
|
|
: rust_tuple_variant_type_p (variant_type)))
|
2016-11-08 16:26:46 +01:00
|
|
|
|
error(_("Variant %s is not a tuple variant"), disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
result = value_primitive_field (lhs, 0, field_number,
|
|
|
|
|
variant_type);
|
|
|
|
|
}
|
|
|
|
|
else if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
|
|
|
|
|
{
|
|
|
|
|
/* Tuples and tuple structs */
|
|
|
|
|
nfields = TYPE_NFIELDS(type);
|
|
|
|
|
|
|
|
|
|
if (field_number >= nfields || field_number < 0)
|
|
|
|
|
error(_("Cannot access field %d of %s, there are only %d fields"),
|
|
|
|
|
field_number, TYPE_TAG_NAME (type), nfields);
|
|
|
|
|
|
|
|
|
|
/* Tuples are tuple structs too. */
|
|
|
|
|
if (!rust_tuple_struct_type_p (type))
|
|
|
|
|
error(_("Attempting to access anonymous field %d of %s, which is \
|
|
|
|
|
not a tuple, tuple struct, or tuple-like variant"),
|
|
|
|
|
field_number, TYPE_TAG_NAME (type));
|
|
|
|
|
|
|
|
|
|
result = value_primitive_field (lhs, 0, field_number, type);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
error(_("Anonymous field access is only allowed on tuples, \
|
|
|
|
|
tuple structs, and tuple-like enum variants"));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STRUCTOP_STRUCT:
|
|
|
|
|
{
|
2017-05-12 06:22:36 +02:00
|
|
|
|
struct value *lhs;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
struct type *type;
|
|
|
|
|
int tem, pc;
|
|
|
|
|
|
|
|
|
|
pc = (*pos)++;
|
|
|
|
|
tem = longest_to_int (exp->elts[pc + 1].longconst);
|
|
|
|
|
(*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
|
|
|
|
|
lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
|
|
|
|
|
2017-05-12 06:22:36 +02:00
|
|
|
|
const char *field_name = &exp->elts[pc + 2].string;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
type = value_type (lhs);
|
2016-10-29 03:00:43 +02:00
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_UNION
|
|
|
|
|
&& !rust_union_is_untagged (type))
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
int i, start;
|
|
|
|
|
struct disr_info disr;
|
2017-05-12 06:22:36 +02:00
|
|
|
|
struct type *variant_type;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
disr = rust_get_disr_info (type, value_contents (lhs),
|
|
|
|
|
value_embedded_offset (lhs),
|
|
|
|
|
value_address (lhs), lhs);
|
|
|
|
|
|
|
|
|
|
if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
|
|
|
|
|
error(_("Could not find field %s of struct variant %s"),
|
2016-11-08 16:26:46 +01:00
|
|
|
|
field_name, disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
|
|
|
|
|
|
|
|
|
|
if (variant_type == NULL
|
2016-10-28 01:46:34 +02:00
|
|
|
|
|| (disr.is_encoded
|
|
|
|
|
? rust_tuple_struct_type_p (variant_type)
|
|
|
|
|
: rust_tuple_variant_type_p (variant_type)))
|
2016-04-27 03:38:08 +02:00
|
|
|
|
error(_("Attempting to access named field %s of tuple variant %s, \
|
|
|
|
|
which has only anonymous fields"),
|
2016-11-08 16:26:46 +01:00
|
|
|
|
field_name, disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
start = disr.is_encoded ? 0 : 1;
|
|
|
|
|
for (i = start; i < TYPE_NFIELDS (variant_type); i++)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (TYPE_FIELD_NAME (variant_type, i),
|
|
|
|
|
field_name) == 0) {
|
|
|
|
|
result = value_primitive_field (lhs, 0, i, variant_type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == TYPE_NFIELDS (variant_type))
|
|
|
|
|
/* We didn't find it. */
|
|
|
|
|
error(_("Could not find field %s of struct variant %s"),
|
2016-11-08 16:26:46 +01:00
|
|
|
|
field_name, disr.name.c_str ());
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-05-12 06:22:36 +02:00
|
|
|
|
result = value_struct_elt (&lhs, NULL, field_name, NULL,
|
|
|
|
|
"structure");
|
|
|
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
|
|
|
result = value_zero (value_type (result), VALUE_LVAL (result));
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2016-04-27 18:28:56 +02:00
|
|
|
|
case OP_RANGE:
|
2016-04-27 03:38:08 +02:00
|
|
|
|
result = rust_range (exp, pos, noside);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNOP_ADDR:
|
|
|
|
|
/* We might have &array[range], in which case we need to make a
|
|
|
|
|
slice. */
|
|
|
|
|
if (exp->elts[*pos + 1].opcode == BINOP_SUBSCRIPT)
|
|
|
|
|
{
|
|
|
|
|
++*pos;
|
|
|
|
|
result = rust_subscript (exp, pos, noside, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Fall through. */
|
|
|
|
|
default:
|
|
|
|
|
result = evaluate_subexp_standard (expect_type, exp, pos, noside);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* operator_length implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_operator_length (const struct expression *exp, int pc, int *oplenp,
|
|
|
|
|
int *argsp)
|
|
|
|
|
{
|
|
|
|
|
int oplen = 1;
|
|
|
|
|
int args = 0;
|
|
|
|
|
|
|
|
|
|
switch (exp->elts[pc - 1].opcode)
|
|
|
|
|
{
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
/* We handle aggregate as a type and argument count. The first
|
|
|
|
|
argument might be OP_OTHERS. After that the arguments
|
|
|
|
|
alternate: first an OP_NAME, then an expression. */
|
|
|
|
|
oplen = 4;
|
|
|
|
|
args = longest_to_int (exp->elts[pc - 2].longconst);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_OTHERS:
|
|
|
|
|
oplen = 1;
|
|
|
|
|
args = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STRUCTOP_ANONYMOUS:
|
|
|
|
|
oplen = 3;
|
|
|
|
|
args = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_RUST_ARRAY:
|
|
|
|
|
oplen = 1;
|
|
|
|
|
args = 2;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
operator_length_standard (exp, pc, oplenp, argsp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*oplenp = oplen;
|
|
|
|
|
*argsp = args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* op_name implementation for Rust. */
|
|
|
|
|
|
-Wwrite-strings: The Rest
This is the remainder boring constification that all looks more of less
borderline obvious IMO.
gdb/ChangeLog:
2017-04-05 Pedro Alves <palves@redhat.com>
* ada-exp.y (yyerror): Constify.
* ada-lang.c (bound_name, get_selections)
(ada_variant_discrim_type)
(ada_variant_discrim_name, ada_value_struct_elt)
(ada_lookup_struct_elt_type, is_unchecked_variant)
(ada_which_variant_applies, standard_exc, ada_get_next_arg)
(catch_ada_exception_command_split)
(catch_ada_assert_command_split, catch_assert_command)
(ada_op_name): Constify.
* ada-lang.h (ada_yyerror, get_selections)
(ada_variant_discrim_name, ada_value_struct_elt): Constify.
* arc-tdep.c (arc_print_frame_cache): Constify.
* arm-tdep.c (arm_skip_stub): Constify.
* ax-gdb.c (gen_binop, gen_struct_ref_recursive, gen_struct_ref)
(gen_aggregate_elt_ref): Constify.
* bcache.c (print_bcache_statistics): Constify.
* bcache.h (print_bcache_statistics): Constify.
* break-catch-throw.c (catch_exception_command_1):
* breakpoint.c (struct ep_type_description::description):
Constify.
(add_solib_catchpoint): Constify.
(catch_fork_command_1): Add cast.
(add_catch_command): Constify.
* breakpoint.h (add_catch_command, add_solib_catchpoint):
Constify.
* bsd-uthread.c (bsd_uthread_state): Constify.
* buildsym.c (patch_subfile_names): Constify.
* buildsym.h (next_symbol_text_func, patch_subfile_names):
Constify.
* c-exp.y (yyerror): Constify.
(token::oper): Constify.
* c-lang.h (c_yyerror, cp_print_class_member): Constify.
* c-varobj.c (cplus_describe_child): Constify.
* charset.c (find_charset_names): Add cast.
(find_charset_names): Constify array and add const_cast.
* cli/cli-cmds.c (complete_command, cd_command): Constify.
(edit_command): Constify.
* cli/cli-decode.c (lookup_cmd): Constify.
* cli/cli-dump.c (dump_memory_command, dump_value_command):
Constify.
(struct dump_context): Constify.
(add_dump_command, restore_command): Constify.
* cli/cli-script.c (get_command_line): Constify.
* cli/cli-script.h (get_command_line): Constify.
* cli/cli-utils.c (check_for_argument): Constify.
* cli/cli-utils.h (check_for_argument): Constify.
* coff-pe-read.c (struct read_pe_section_data): Constify.
* command.h (lookup_cmd): Constify.
* common/print-utils.c (decimal2str): Constify.
* completer.c (gdb_print_filename): Constify.
* corefile.c (set_gnutarget): Constify.
* cp-name-parser.y (yyerror): Constify.
* cp-valprint.c (cp_print_class_member): Constify.
* cris-tdep.c (cris_register_name, crisv32_register_name):
Constify.
* d-exp.y (yyerror): Constify.
(struct token::oper): Constify.
* d-lang.h (d_yyerror): Constify.
* dbxread.c (struct header_file_location::name): Constify.
(add_old_header_file, add_new_header_file, last_function_name)
(dbx_next_symbol_text, add_bincl_to_list)
(find_corresponding_bincl_psymtab, set_namestring)
(find_stab_function_addr, read_dbx_symtab, start_psymtab)
(dbx_end_psymtab, read_ofile_symtab, process_one_symbol):
* defs.h (command_line_input, print_address_symbolic)
(deprecated_readline_begin_hook): Constify.
* dwarf2read.c (anonymous_struct_prefix, dwarf_bool_name):
Constify.
* event-top.c (handle_line_of_input): Constify and add cast.
* exceptions.c (catch_errors): Constify.
* exceptions.h (catch_errors): Constify.
* expprint.c (print_subexp_standard, op_string, op_name)
(op_name_standard, dump_raw_expression, dump_raw_expression):
* expression.h (op_name, op_string, dump_raw_expression):
Constify.
* f-exp.y (yyerror): Constify.
(struct token::oper): Constify.
(struct f77_boolean_val::name): Constify.
* f-lang.c (f_word_break_characters): Constify.
* f-lang.h (f_yyerror): Constify.
* fork-child.c (fork_inferior): Add cast.
* frv-tdep.c (struct gdbarch_tdep::register_names): Constify.
(new_variant): Constify.
* gdbarch.sh (pstring_ptr, pstring_list): Constify.
* gdbarch.c: Regenerate.
* gdbcore.h (set_gnutarget): Constify.
* go-exp.y (yyerror): Constify.
(token::oper): Constify.
* go-lang.h (go_yyerror): Constify.
* go32-nat.c (go32_sysinfo): Constify.
* guile/scm-breakpoint.c (gdbscm_breakpoint_expression): Constify.
* guile/scm-cmd.c (cmdscm_function): Constify.
* guile/scm-param.c (pascm_param_value): Constify.
* h8300-tdep.c (h8300_register_name, h8300s_register_name)
(h8300sx_register_name): Constify.
* hppa-tdep.c (hppa32_register_name, hppa64_register_name):
Constify.
* ia64-tdep.c (ia64_register_names): Constify.
* infcmd.c (construct_inferior_arguments): Constify.
(path_command, attach_post_wait): Constify.
* language.c (show_range_command, show_case_command)
(unk_lang_error): Constify.
* language.h (language_defn::la_error)
(language_defn::la_name_of_this): Constify.
* linespec.c (decode_line_2): Constify.
* linux-thread-db.c (thread_db_err_str): Constify.
* lm32-tdep.c (lm32_register_name): Constify.
* m2-exp.y (yyerror): Constify.
* m2-lang.h (m2_yyerror): Constify.
* m32r-tdep.c (m32r_register_names): Constify and make static.
* m68hc11-tdep.c (m68hc11_register_names): Constify.
* m88k-tdep.c (m88k_register_name): Constify.
* macroexp.c (appendmem): Constify.
* mdebugread.c (fdr_name, add_data_symbol, parse_type)
(upgrade_type, parse_external, parse_partial_symbols)
(mdebug_next_symbol_text, cross_ref, mylookup_symbol, new_psymtab)
(new_symbol): Constify.
* memattr.c (mem_info_command): Constify.
* mep-tdep.c (register_name_from_keyword): Constify.
* mi/mi-cmd-env.c (mi_cmd_env_path, _initialize_mi_cmd_env):
Constify.
* mi/mi-cmd-stack.c (list_args_or_locals): Constify.
* mi/mi-cmd-var.c (mi_cmd_var_show_attributes): Constify.
* mi/mi-main.c (captured_mi_execute_command): Constify and add
cast.
(mi_execute_async_cli_command): Constify.
* mips-tdep.c (mips_register_name): Constify.
* mn10300-tdep.c (register_name, mn10300_generic_register_name)
(am33_register_name, am33_2_register_name)
* moxie-tdep.c (moxie_register_names): Constify.
* nat/linux-osdata.c (osdata_type): Constify fields.
* nto-tdep.c (nto_parse_redirection): Constify.
* objc-lang.c (lookup_struct_typedef, lookup_objc_class)
(lookup_child_selector): Constify.
(objc_methcall::name): Constify.
* objc-lang.h (lookup_objc_class, lookup_child_selector)
(lookup_struct_typedef): Constify.
* objfiles.c (pc_in_section): Constify.
* objfiles.h (pc_in_section): Constify.
* p-exp.y (struct token::oper): Constify.
(yyerror): Constify.
* p-lang.h (pascal_yyerror): Constify.
* parser-defs.h (op_name_standard): Constify.
(op_print::string): Constify.
(exp_descriptor::op_name): Constify.
* printcmd.c (print_address_symbolic): Constify.
* psymtab.c (print_partial_symbols): Constify.
* python/py-breakpoint.c (stop_func): Constify.
(bppy_get_expression): Constify.
* python/py-cmd.c (cmdpy_completer::name): Constify.
(cmdpy_function): Constify.
* python/py-event.c (evpy_add_attribute)
(gdbpy_initialize_event_generic): Constify.
* python/py-event.h (evpy_add_attribute)
(gdbpy_initialize_event_generic): Constify.
* python/py-evts.c (add_new_registry): Constify.
* python/py-finishbreakpoint.c (outofscope_func): Constify.
* python/py-framefilter.c (get_py_iter_from_func): Constify.
* python/py-inferior.c (get_buffer): Add cast.
* python/py-param.c (parm_constant::name): Constify.
* python/py-unwind.c (fprint_frame_id): Constify.
* python/python.c (gdbpy_parameter_value): Constify.
* remote-fileio.c (remote_fio_func_map): Make 'name' const.
* remote.c (memory_packet_config::name): Constify.
(show_packet_config_cmd, remote_write_bytes)
(remote_buffer_add_string):
* reverse.c (exec_reverse_once): Constify.
* rs6000-tdep.c (variant::name, variant::description): Constify.
* rust-exp.y (rustyyerror): Constify.
* rust-lang.c (rust_op_name): Constify.
* rust-lang.h (rustyyerror): Constify.
* serial.h (serial_ops::name): Constify.
* sh-tdep.c (sh_sh_register_name, sh_sh3_register_name)
(sh_sh3e_register_name, sh_sh2e_register_name)
(sh_sh2a_register_name, sh_sh2a_nofpu_register_name)
(sh_sh_dsp_register_name, sh_sh3_dsp_register_name)
(sh_sh4_register_name, sh_sh4_nofpu_register_name)
(sh_sh4al_dsp_register_name): Constify.
* sh64-tdep.c (sh64_register_name): Constify.
* solib-darwin.c (lookup_symbol_from_bfd): Constify.
* spu-tdep.c (spu_register_name, info_spu_dma_cmdlist): Constify.
* stabsread.c (patch_block_stabs, read_type_number)
(ref_map::stabs, ref_add, process_reference)
(symbol_reference_defined, define_symbol, define_symbol)
(error_type, read_type, read_member_functions, read_cpp_abbrev)
(read_one_struct_field, read_struct_fields, read_baseclasses)
(read_tilde_fields, read_struct_type, read_array_type)
(read_enum_type, read_sun_builtin_type, read_sun_floating_type)
(read_huge_number, read_range_type, read_args, common_block_start)
(find_name_end): Constify.
* stabsread.h (common_block_start, define_symbol)
(process_one_symbol, symbol_reference_defined, ref_add):
* symfile.c (get_section_index, add_symbol_file_command):
* symfile.h (get_section_index): Constify.
* target-descriptions.c (tdesc_type::name): Constify.
(tdesc_free_type): Add cast.
* target.c (find_default_run_target):
(add_deprecated_target_alias, find_default_run_target)
(target_announce_detach): Constify.
(do_option): Constify.
* target.h (add_deprecated_target_alias): Constify.
* thread.c (print_thread_info_1): Constify.
* top.c (deprecated_readline_begin_hook, command_line_input):
Constify.
(init_main): Add casts.
* top.h (handle_line_of_input): Constify.
* tracefile-tfile.c (tfile_write_uploaded_tsv): Constify.
* tracepoint.c (tvariables_info_1, trace_status_mi): Constify.
(tfind_command): Rename to ...
(tfind_command_1): ... this and constify.
(tfind_command): New function.
(tfind_end_command, tfind_start_command): Adjust.
(encode_source_string): Constify.
* tracepoint.h (encode_source_string): Constify.
* tui/tui-data.c (tui_partial_win_by_name): Constify.
* tui/tui-data.h (tui_partial_win_by_name): Constify.
* tui/tui-source.c (tui_set_source_content_nil): Constify.
* tui/tui-source.h (tui_set_source_content_nil): Constify.
* tui/tui-win.c (parse_scrolling_args): Constify.
* tui/tui-windata.c (tui_erase_data_content): Constify.
* tui/tui-windata.h (tui_erase_data_content): Constify.
* tui/tui-winsource.c (tui_erase_source_content): Constify.
* tui/tui.c (tui_enable): Add cast.
* utils.c (defaulted_query): Constify.
(init_page_info): Add cast.
(puts_debug, subset_compare): Constify.
* utils.h (subset_compare): Constify.
* varobj.c (varobj_format_string): Constify.
* varobj.h (varobj_format_string): Constify.
* vax-tdep.c (vax_register_name): Constify.
* windows-nat.c (windows_detach): Constify.
* xcoffread.c (process_linenos, xcoff_next_symbol_text): Constify.
* xml-support.c (gdb_xml_end_element): Constify.
* xml-tdesc.c (tdesc_start_reg): Constify.
* xstormy16-tdep.c (xstormy16_register_name): Constify.
* xtensa-tdep.c (xtensa_find_register_by_name): Constify.
* xtensa-tdep.h (xtensa_register_t::name): Constify.
gdb/gdbserver/ChangeLog:
2017-04-05 Pedro Alves <palves@redhat.com>
* gdbreplay.c (sync_error): Constify.
* linux-x86-low.c (push_opcode): Constify.
2017-04-05 20:21:37 +02:00
|
|
|
|
static const char *
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_op_name (enum exp_opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
switch (opcode)
|
|
|
|
|
{
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
return "OP_AGGREGATE";
|
|
|
|
|
case OP_OTHERS:
|
|
|
|
|
return "OP_OTHERS";
|
|
|
|
|
default:
|
|
|
|
|
return op_name_standard (opcode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* dump_subexp_body implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
rust_dump_subexp_body (struct expression *exp, struct ui_file *stream,
|
|
|
|
|
int elt)
|
|
|
|
|
{
|
|
|
|
|
switch (exp->elts[elt].opcode)
|
|
|
|
|
{
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
{
|
|
|
|
|
int length = longest_to_int (exp->elts[elt + 2].longconst);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
fprintf_filtered (stream, "Type @");
|
|
|
|
|
gdb_print_host_address (exp->elts[elt + 1].type, stream);
|
|
|
|
|
fprintf_filtered (stream, " (");
|
|
|
|
|
type_print (exp->elts[elt + 1].type, NULL, stream, 0);
|
|
|
|
|
fprintf_filtered (stream, "), length %d", length);
|
|
|
|
|
|
|
|
|
|
elt += 4;
|
|
|
|
|
for (i = 0; i < length; ++i)
|
|
|
|
|
elt = dump_subexp (exp, stream, elt);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_STRING:
|
|
|
|
|
case OP_NAME:
|
|
|
|
|
{
|
|
|
|
|
LONGEST len = exp->elts[elt + 1].longconst;
|
|
|
|
|
|
|
|
|
|
fprintf_filtered (stream, "%s: %s",
|
|
|
|
|
(exp->elts[elt].opcode == OP_STRING
|
|
|
|
|
? "string" : "name"),
|
|
|
|
|
&exp->elts[elt + 2].string);
|
|
|
|
|
elt += 4 + BYTES_TO_EXP_ELEM (len + 1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_OTHERS:
|
|
|
|
|
elt = dump_subexp (exp, stream, elt + 1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STRUCTOP_ANONYMOUS:
|
|
|
|
|
{
|
|
|
|
|
int field_number;
|
|
|
|
|
|
2017-05-12 03:50:47 +02:00
|
|
|
|
field_number = longest_to_int (exp->elts[elt + 1].longconst);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
fprintf_filtered (stream, "Field number: %d", field_number);
|
2017-05-12 03:50:47 +02:00
|
|
|
|
elt = dump_subexp (exp, stream, elt + 3);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_RUST_ARRAY:
|
2017-05-12 03:50:47 +02:00
|
|
|
|
++elt;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
elt = dump_subexp_body_standard (exp, stream, elt);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return elt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print_subexp implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rust_print_subexp (struct expression *exp, int *pos, struct ui_file *stream,
|
|
|
|
|
enum precedence prec)
|
|
|
|
|
{
|
|
|
|
|
switch (exp->elts[*pos].opcode)
|
|
|
|
|
{
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
{
|
|
|
|
|
int length = longest_to_int (exp->elts[*pos + 2].longconst);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
type_print (exp->elts[*pos + 1].type, "", stream, 0);
|
|
|
|
|
fputs_filtered (" { ", stream);
|
|
|
|
|
|
|
|
|
|
*pos += 4;
|
|
|
|
|
for (i = 0; i < length; ++i)
|
|
|
|
|
{
|
|
|
|
|
rust_print_subexp (exp, pos, stream, prec);
|
|
|
|
|
fputs_filtered (", ", stream);
|
|
|
|
|
}
|
|
|
|
|
fputs_filtered (" }", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_NAME:
|
|
|
|
|
{
|
|
|
|
|
LONGEST len = exp->elts[*pos + 1].longconst;
|
|
|
|
|
|
|
|
|
|
fputs_filtered (&exp->elts[*pos + 2].string, stream);
|
|
|
|
|
*pos += 4 + BYTES_TO_EXP_ELEM (len + 1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_OTHERS:
|
|
|
|
|
{
|
|
|
|
|
fputs_filtered ("<<others>> (", stream);
|
|
|
|
|
++*pos;
|
|
|
|
|
rust_print_subexp (exp, pos, stream, prec);
|
|
|
|
|
fputs_filtered (")", stream);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case STRUCTOP_ANONYMOUS:
|
|
|
|
|
{
|
|
|
|
|
int tem = longest_to_int (exp->elts[*pos + 1].longconst);
|
|
|
|
|
|
|
|
|
|
(*pos) += 3;
|
|
|
|
|
print_subexp (exp, pos, stream, PREC_SUFFIX);
|
|
|
|
|
fprintf_filtered (stream, ".%d", tem);
|
|
|
|
|
}
|
2017-05-12 03:44:43 +02:00
|
|
|
|
break;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
|
|
|
|
|
case OP_RUST_ARRAY:
|
|
|
|
|
++*pos;
|
|
|
|
|
fprintf_filtered (stream, "[");
|
|
|
|
|
rust_print_subexp (exp, pos, stream, prec);
|
|
|
|
|
fprintf_filtered (stream, "; ");
|
|
|
|
|
rust_print_subexp (exp, pos, stream, prec);
|
|
|
|
|
fprintf_filtered (stream, "]");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
print_subexp_standard (exp, pos, stream, prec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* operator_check implementation for Rust. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
rust_operator_check (struct expression *exp, int pos,
|
|
|
|
|
int (*objfile_func) (struct objfile *objfile,
|
|
|
|
|
void *data),
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
switch (exp->elts[pos].opcode)
|
|
|
|
|
{
|
|
|
|
|
case OP_AGGREGATE:
|
|
|
|
|
{
|
|
|
|
|
struct type *type = exp->elts[pos + 1].type;
|
|
|
|
|
struct objfile *objfile = TYPE_OBJFILE (type);
|
|
|
|
|
|
|
|
|
|
if (objfile != NULL && (*objfile_func) (objfile, data))
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OP_OTHERS:
|
|
|
|
|
case OP_NAME:
|
|
|
|
|
case OP_RUST_ARRAY:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return operator_check_standard (exp, pos, objfile_func, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Implementation of la_lookup_symbol_nonlocal for Rust. */
|
|
|
|
|
|
|
|
|
|
static struct block_symbol
|
|
|
|
|
rust_lookup_symbol_nonlocal (const struct language_defn *langdef,
|
|
|
|
|
const char *name,
|
|
|
|
|
const struct block *block,
|
|
|
|
|
const domain_enum domain)
|
|
|
|
|
{
|
|
|
|
|
struct block_symbol result = {NULL, NULL};
|
|
|
|
|
|
|
|
|
|
if (symbol_lookup_debug)
|
|
|
|
|
{
|
|
|
|
|
fprintf_unfiltered (gdb_stdlog,
|
|
|
|
|
"rust_lookup_symbol_non_local"
|
|
|
|
|
" (%s, %s (scope %s), %s)\n",
|
|
|
|
|
name, host_address_to_string (block),
|
|
|
|
|
block_scope (block), domain_name (domain));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Look up bare names in the block's scope. */
|
2018-01-19 23:25:19 +01:00
|
|
|
|
std::string scopedname;
|
2016-04-27 03:38:08 +02:00
|
|
|
|
if (name[cp_find_first_component (name)] == '\0')
|
|
|
|
|
{
|
|
|
|
|
const char *scope = block_scope (block);
|
|
|
|
|
|
|
|
|
|
if (scope[0] != '\0')
|
|
|
|
|
{
|
2018-01-19 23:25:19 +01:00
|
|
|
|
scopedname = std::string (scope) + "::" + name;
|
|
|
|
|
name = scopedname.c_str ();
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
2018-01-19 23:25:19 +01:00
|
|
|
|
else
|
|
|
|
|
name = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
|
{
|
|
|
|
|
result = lookup_symbol_in_static_block (name, block, domain);
|
|
|
|
|
if (result.symbol == NULL)
|
|
|
|
|
result = lookup_global_symbol (name, block, domain);
|
2016-04-27 03:38:08 +02:00
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-26 23:04:07 +02:00
|
|
|
|
/* la_sniff_from_mangled_name for Rust. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
rust_sniff_from_mangled_name (const char *mangled, char **demangled)
|
|
|
|
|
{
|
|
|
|
|
*demangled = gdb_demangle (mangled, DMGL_PARAMS | DMGL_ANSI);
|
|
|
|
|
return *demangled != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Use watchpoint's language when re-parsing expression
PR rust/21484 notes that watch -location does not work with Rust:
(gdb) watch -location a
syntax error in expression, near `) 0x00007fffffffe0f4'.
update_watchpoint tries to tell gdb that the new expression it creates
has C syntax:
/* The above expression is in C. */
b->language = language_c;
However, update_watchpoint doesn't actually use this language when
re-parsing the expression.
Originally I was going to fix this by saving and restoring the
language in update_watchpoint, but this regressed
gdb.dlang/watch-loc.exp, because the constructed expression actually
has D syntax (specifically the name is not parseable by C).
Next I looked at directly constructing an expression, and not relying
on the parser at all; but it seemed to me that upon a re-set, we'd
want to reparse the type, and there is no existing API to do this
correctly.
So, in the end I made a hook to let each language choose what
expression to use. I made all the languages other than Rust use the C
expression, because that is the status quo ante. However, this is
probably not truly correct. After this patch, at least, it is easy to
correct by someone who knows the language(s) in question.
Regtested by the buildbot.
ChangeLog
2017-05-19 Tom Tromey <tom@tromey.com>
PR rust/21484:
* rust-lang.c (exp_descriptor_rust): New function.
(rust_language_defn): Use it.
* p-lang.c (pascal_language_defn): Update.
* opencl-lang.c (opencl_language_defn): Update.
* objc-lang.c (objc_language_defn): Update.
* m2-lang.c (m2_language_defn): Update.
* language.h (struct language_defn)
<la_watch_location_expression>: New member.
* language.c (unknown_language_defn, auto_language_defn)
(local_language_defn): Update.
* go-lang.c (go_language_defn): Update.
* f-lang.c (f_language_defn): Update.
* d-lang.c (d_language_defn): Update.
* c-lang.h (c_watch_location_expression): Declare.
* c-lang.c (c_watch_location_expression): New function.
(c_language_defn, cplus_language_defn, asm_language_defn)
(minimal_language_defn): Use it.
* breakpoint.c (watch_command_1): Call
la_watch_location_expression.
* ada-lang.c (ada_language_defn): Update.
testsuite/ChangeLog
2017-05-19 Tom Tromey <tom@tromey.com>
PR rust/21484:
* gdb.rust/watch.exp: New file.
* gdb.rust/watch.rs: New file.
2017-05-14 19:12:14 +02:00
|
|
|
|
/* la_watch_location_expression for Rust. */
|
|
|
|
|
|
|
|
|
|
static gdb::unique_xmalloc_ptr<char>
|
|
|
|
|
rust_watch_location_expression (struct type *type, CORE_ADDR addr)
|
|
|
|
|
{
|
|
|
|
|
type = check_typedef (TYPE_TARGET_TYPE (check_typedef (type)));
|
|
|
|
|
std::string name = type_to_string (type);
|
|
|
|
|
return gdb::unique_xmalloc_ptr<char>
|
|
|
|
|
(xstrprintf ("*(%s as *mut %s)", core_addr_to_string (addr),
|
|
|
|
|
name.c_str ()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-27 03:38:08 +02:00
|
|
|
|
static const struct exp_descriptor exp_descriptor_rust =
|
|
|
|
|
{
|
|
|
|
|
rust_print_subexp,
|
|
|
|
|
rust_operator_length,
|
|
|
|
|
rust_operator_check,
|
|
|
|
|
rust_op_name,
|
|
|
|
|
rust_dump_subexp_body,
|
|
|
|
|
rust_evaluate_subexp
|
|
|
|
|
};
|
|
|
|
|
|
2016-05-26 18:33:28 +02:00
|
|
|
|
static const char *rust_extensions[] =
|
|
|
|
|
{
|
|
|
|
|
".rs", NULL
|
|
|
|
|
};
|
|
|
|
|
|
Make language_def O(1)
Profiling GDB with the rest of series applied, I saw calls to
language_def showing up high in some runs. The problem is that
language_def is O(N) currently, since walk the languages vector each
time to find the matching language_defn.
IMO, the add_language mechanism is pointless, because "enum language"
implies the core of GDB needs to know about all languages anyway. So
simply make the languages vector array be an array where each
element's index is the corresponding enum language enumerator. Note
that "local_language_defn" is gone along the way. It's just a copy of
"auto", so the new code simply maps one to the other. One fewer place
to update when we need to change the language vector...
Also, a while ago the output of "set language" was made out of order
as side effect of some other change. While I was at it, I made them
sorted again.
gdb/ChangeLog:
2017-07-20 Pedro Alves <palves@redhat.com>
* ada-lang.c (ada_language_defn): Make extern.
(_initialize_ada_language): Remove add_language call.
* c-lang.c (c_language_defn, cplus_language_defn)
(asm_language_defn, minimal_language_defn): Make extern.
(_initialize_c_language): Delete.
* completer.c (compare_cstrings): Delete, moved to utils.h.
* d-lang.c (d_language_defn): Make extern.
(_initialize_d_language): Remove add_language calls.
* defs.h (enum language): Add comment.
* f-lang.c (f_language_defn): Make extern.
(_initialize_f_language): Remove add_language call.
* go-lang.c (go_language_defn): Make extern.
(_initialize_go_language): Remove add_language call.
* language.c: Include <algorithm>.
(languages): Redefine as const array.
(languages_size, languages_allocsize, DEFAULT_ALLOCSIZE): Delete.
(set_language_command): Handle "local". Use for-range loop.
(set_language): Remove loop.
(language_enum): Rewrite.
(language_def, language_str): Remove loops.
(add_language): Delete.
(add_set_language_command): New, based on add_languages.
(skip_language_trampoline): Adjust.
(local_language_defn): Delete.
(language_gdbarch_post_init): Adjust.
(_initialize_language): Remove add_language calls. Call
add_set_language_command.
* language.h (add_language): Delete.
(auto_language_defn)
(unknown_language_defn, minimal_language_defn, ada_language_defn)
(asm_language_defn, c_language_defn, cplus_language_defn)
(d_language_defn, f_language_defn, go_language_defn)
(m2_language_defn, objc_language_defn, opencl_language_defn)
(pascal_language_defn, rust_language_defn): Declare.
* m2-lang.c (m2_language_defn): Make extern.
(_initialize_m2_language): Remove add_language call.
* objc-lang.c (objc_language_defn): Make extern.
(_initialize_objc_language): Remove add_language call.
* opencl-lang.c (opencl_language_defn): Make extern.
(_initialize_opencl_language): Remove add_language call.
* p-lang.c (pascal_language_defn): Make extern.
(_initialize_pascal_language): Delete.
* rust-lang.c (rust_language_defn): Make extern.
(_initialize_rust_language): Delete.
* utils.h (compare_cstrings): New static inline function.
gdb/testsuite/ChangeLog:
2017-07-20 Pedro Alves <palves@redhat.com>
* gdb.base/default.exp (set language): Adjust expected output.
2017-07-20 19:28:01 +02:00
|
|
|
|
extern const struct language_defn rust_language_defn =
|
2016-04-27 03:38:08 +02:00
|
|
|
|
{
|
|
|
|
|
"rust",
|
|
|
|
|
"Rust",
|
|
|
|
|
language_rust,
|
|
|
|
|
range_check_on,
|
|
|
|
|
case_sensitive_on,
|
|
|
|
|
array_row_major,
|
|
|
|
|
macro_expansion_no,
|
2016-05-26 18:33:28 +02:00
|
|
|
|
rust_extensions,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
&exp_descriptor_rust,
|
|
|
|
|
rust_parse,
|
|
|
|
|
rustyyerror,
|
|
|
|
|
null_post_parser,
|
|
|
|
|
rust_printchar, /* Print a character constant */
|
|
|
|
|
rust_printstr, /* Function to print string constant */
|
|
|
|
|
rust_emitchar, /* Print a single char */
|
|
|
|
|
rust_print_type, /* Print a type using appropriate syntax */
|
|
|
|
|
rust_print_typedef, /* Print a typedef using appropriate syntax */
|
|
|
|
|
rust_val_print, /* Print a value using appropriate syntax */
|
|
|
|
|
c_value_print, /* Print a top-level value */
|
|
|
|
|
default_read_var_value, /* la_read_var_value */
|
|
|
|
|
NULL, /* Language specific skip_trampoline */
|
|
|
|
|
NULL, /* name_of_this */
|
|
|
|
|
rust_lookup_symbol_nonlocal, /* lookup_symbol_nonlocal */
|
|
|
|
|
basic_lookup_transparent_type,/* lookup_transparent_type */
|
|
|
|
|
gdb_demangle, /* Language specific symbol demangler */
|
2016-05-26 23:04:07 +02:00
|
|
|
|
rust_sniff_from_mangled_name,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
NULL, /* Language specific
|
|
|
|
|
class_name_from_physname */
|
|
|
|
|
c_op_print_tab, /* expression operators for printing */
|
|
|
|
|
1, /* c-style arrays */
|
|
|
|
|
0, /* String lower bound */
|
|
|
|
|
default_word_break_characters,
|
Introduce class completion_tracker & rewrite completion<->readline interaction
This patch reworks the whole completion machinery, and prepares it
for later enhancements.
Adds a new "completion_tracker" class that is meant to hold everything
about the state of the current completion operation.
This class now has the responsibility of tracking the list of
completion matches, and checking whether the max completions limit has
been reached. You can look at this as this patch starting out by
C++fying the existing "completion_tracker" in symtab.c (it's just an
htab_t typedef currently), moving it to completer.h/c, and then making
it a class/generalizing/enhancing it.
Unlike with the current tracking, completion_tracker now checks
whether the limit has been reached on each completion match list
insertion. This both simplifies the max-completions handling code
(maybe_add_completion_enum is gone, for example), and is a
prerequisite for follow up patches.
The current completion_tracker is only used for symbol completions,
and the symbol code gets at the current instance via globals. This
patch cleans that up by adding a completion_tracker reference to the
signature of the completion functions, and passing the tracker around
everywhere necessary.
Then, the patch changes how the completion match list is handed over
to readline. Currently, we're using the rl_completion_entry_function
readline entry point, and the patch switches to
rl_attempted_completion_function. A following patch will want to let
GDB itself decide the common completion prefix between all matches
(what readline calls the "lowest common denominator"), instead of
having readline compute it, and that's not possible with the
rl_completion_entry_function entry point. Also,
rl_attempted_completion_function lets GDB hand over the match list to
readline as an array in one go instead of passing down matches one by
one, so from that angle it's a nicer entry point anyway.
Lastly, the patch catches exceptions around the readline entry points,
because we can't let C++ exceptions cross readline. We handle that in
the readline input entry point, but the completion entry point isn't
guarded, so GDB can abort if completion throws. E.g., in current
master:
(gdb) b -function "fun<tab>
terminate called after throwing an instance of 'gdb_exception_RETURN_MASK_ERROR'
Aborted (core dumped)
This patch fixes that. This will be exercised in the new tests added
later on in the series.
gdb/ChangeLog:
2017-07-17 Pedro Alves <palves@redhat.com>
* ada-lang.c (symbol_completion_match): Adjust comments.
(symbol_completion_add): Replace vector parameter with
completion_tracker parameter. Use it.
(ada_make_symbol_completion_list): Rename to...
(ada_collect_symbol_completion_matches): ... this. Add
completion_tracker parameter and use it.
(ada_language_defn): Adjust.
* break-catch-syscall.c (catch_syscall_completer): Adjust
prototype and work with completion_tracker instead of VEC.
* breakpoint.c (condition_completer): Adjust prototype and work
with completion_tracker instead of VEC.
* c-lang.c (c_language_defn, cplus_language_defn)
(asm_language_defn, minimal_language_defn): Adjust to renames.
* cli/cli-cmds.c (complete_command): Rework using
completion_tracker. Catch exceptions when completing.
* cli/cli-decode.c (integer_unlimited_completer)
(complete_on_cmdlist, complete_on_enum): Adjust prototype and work
with completion_tracker instead of VEC.
* command.h (struct completion_tracker): Forward declare.
(completer_ftype, completer_handle_brkchars_ftype): Change
types.
(complete_on_cmdlist, complete_on_enum): Adjust.
* completer.c: Include <algorithm>.
(struct gdb_completer_state): New.
(current_completion): New global.
(readline_line_completion_function): Delete.
(noop_completer, filename_completer)
(filename_completer_handle_brkchars, complete_files_symbols)
(linespec_location_completer): Adjust to work with a
completion_tracker instead of a VEC.
(string_or_empty): New.
(collect_explicit_location_matches): Adjust to work with a
completion_tracker instead of a VEC.
(explicit_location_completer): Rename to ...
(complete_explicit_location): ... this and adjust to work with a
completion_tracker instead of a VEC.
(location_completer): Adjust to work with a completion_tracker
instead of a VEC.
(add_struct_fields): Adjust to work with a completion_list instead
of VEC.
(expression_completer): Rename to ...
(complete_expression): ... this and adjust to work with a
completion_tracker instead of a VEC. Use complete_files_symbols.
(expression_completer): Reimplement on top of complete_expression.
(symbol_completer): Adjust to work with a completion_tracker
instead of a VEC.
(enum complete_line_internal_reason): Add describing comments.
(complete_line_internal_normal_command): Adjust to work with a
completion_tracker instead of a VEC.
(complete_line_internal): Rename to ...
(complete_line_internal_1): ... this and adjust to work with a
completion_tracker instead of a VEC. Assert TEXT is NULL in the
handle_brkchars phase.
(new_completion_tracker): Delete.
(complete_line_internal): Reimplement as TRY/CATCH wrapper around
complete_line_internal_1.
(free_completion_tracker): Delete.
(INITIAL_COMPLETION_HTAB_SIZE): New.
(completion_tracker::completion_tracker)
(completion_tracker::~completion_tracker): New.
(maybe_add_completion): Delete.
(completion_tracker::maybe_add_completion)
(completion_tracker::add_completion)
(completion_tracker::add_completions): New.
(throw_max_completions_reached_error): Delete.
(complete_line): Adjust to work with a completion_tracker instead
of a VEC. Don't create a completion_tracker_t or check for max
completions here.
(command_completer, command_completer_handle_brkchars)
(signal_completer, reg_or_group_completer_1)
(reg_or_group_completer, default_completer_handle_brkchars):
Adjust to work with a completion_tracker.
(gdb_completion_word_break_characters_throw): New.
(gdb_completion_word_break_characters): Reimplement.
(line_completion_function): Delete.
(completion_tracker::recompute_lowest_common_denominator)
(expand_preserving_ws)
(completion_tracker::build_completion_result)
(completion_result::completion_result)
(completion_result::completion_result)
(completion_result::~completion_result)
(completion_result::completion_result)
(completion_result::release_match_list, compare_cstrings)
(completion_result::sort_match_list)
(completion_result::reset_match_list)
(gdb_rl_attempted_completion_function_throw)
(gdb_rl_attempted_completion_function): New.
* completer.h (completion_list, struct completion_result)
(class completion_tracker): New.
(complete_line): Add completion_tracker parameter.
(readline_line_completion_function): Delete.
(gdb_rl_attempted_completion_function): New.
(noop_completer, filename_completer, expression_completer)
(location_completer, symbol_completer, command_completer)
(signal_completer, reg_or_group_completer): Update prototypes.
(completion_tracker_t, new_completion_tracker)
(make_cleanup_free_completion_tracker): Delete.
(enum maybe_add_completion_enum): Delete.
(maybe_add_completion): Delete.
(throw_max_completions_reached_error): Delete.
* corefile.c (complete_set_gnutarget): Adjust to work with a
completion_tracker instead of a VEC.
* cp-abi.c (cp_abi_completer): Adjust to work with a
completion_tracker instead of a VEC.
* d-lang.c (d_language_defn): Adjust.
* disasm.c (disassembler_options_completer): Adjust to work with a
completion_tracker instead of a VEC.
* f-lang.c (f_make_symbol_completion_list): Rename to ...
(f_collect_symbol_completion_matches): ... this. Adjust to work
with a completion_tracker instead of a VEC.
(f_language_defn): Adjust.
* go-lang.c (go_language_defn): Adjust.
* guile/scm-cmd.c (cmdscm_add_completion, cmdscm_completer):
Adjust to work with a completion_tracker instead of a VEC.
* infrun.c (handle_completer): Likewise.
* interps.c (interpreter_completer): Likewise.
* interps.h (interpreter_completer): Likewise.
* language.c (unknown_language_defn, auto_language_defn)
(local_language_defn): Adjust.
* language.h (language_defn::la_make_symbol_completion_list):
Rename to ...
(language_defn::la_collect_symbol_completion_matches): ... this
and adjust to work with a completion_tracker instead of a VEC.
* m2-lang.c (m2_language_defn): Adjust.
* objc-lang.c (objc_language_defn): Adjust.
* opencl-lang.c (opencl_language_defn): Adjust.
* p-lang.c (pascal_language_defn): Adjust.
* python/py-cmd.c (cmdpy_completer_helper): Handle NULL word.
(cmdpy_completer_handle_brkchars, cmdpy_completer): Adjust to work
with a completion_tracker.
* rust-lang.c (rust_language_defn): Adjust.
* symtab.c (free_completion_list, do_free_completion_list)
(return_val, completion_tracker): Delete.
(completion_list_add_name, completion_list_add_symbol)
(completion_list_add_msymbol, completion_list_objc_symbol)
(completion_list_add_fields, add_symtab_completions): Add
completion_tracker parameter and use it.
(default_make_symbol_completion_list_break_on_1): Rename to...
(default_collect_symbol_completion_matches_break_on): ... this.
Add completion_tracker parameter and use it instead of allocating
a completion tracker here.
(default_make_symbol_completion_list_break_on): Delete old
implementation.
(default_make_symbol_completion_list): Delete.
(default_collect_symbol_completion_matches): New.
(make_symbol_completion_list): Delete.
(collect_symbol_completion_matches): New.
(make_symbol_completion_type): Rename to ...
(collect_symbol_completion_matches_type): ... this. Add
completion_tracker parameter and use it instead of VEC.
(make_file_symbol_completion_list_1): Rename to...
(collect_file_symbol_completion_matches): ... this. Add
completion_tracker parameter and use it instead of VEC.
(make_file_symbol_completion_list): Delete.
(add_filename_to_list): Use completion_list instead of a VEC.
(add_partial_filename_data::list): Now a completion_list.
(make_source_files_completion_list): Work with a completion_list
instead of a VEC.
* symtab.h: Include "completer.h".
(default_make_symbol_completion_list_break_on)
(default_make_symbol_completion_list, make_symbol_completion_list)
(make_symbol_completion_type, make_file_symbol_completion_list)
(make_source_files_completion_list): Delete.
(default_collect_symbol_completion_matches_break_on)
(default_collect_symbol_completion_matches)
(collect_symbol_completion_matches)
(collect_symbol_completion_matches_type)
(collect_file_symbol_completion_matches)
(make_source_files_completion_list): New.
* top.c (init_main): Don't install a rl_completion_entry_function
hook. Install a rl_attempted_completion_function hook instead.
* tui/tui-layout.c (layout_completer): Adjust to work with a
completion_tracker.
* tui/tui-regs.c (tui_reggroup_completer):
* tui/tui-win.c (window_name_completer, focus_completer)
(winheight_completer): Adjust to work with a completion_tracker.
* value.c: Include "completer.h".
(complete_internalvar): Adjust to work with a completion_tracker.
* value.h (complete_internalvar): Likewise.
2017-07-17 15:45:59 +02:00
|
|
|
|
default_collect_symbol_completion_matches,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
rust_language_arch_info,
|
|
|
|
|
default_print_array_index,
|
|
|
|
|
default_pass_by_reference,
|
|
|
|
|
c_get_string,
|
Use watchpoint's language when re-parsing expression
PR rust/21484 notes that watch -location does not work with Rust:
(gdb) watch -location a
syntax error in expression, near `) 0x00007fffffffe0f4'.
update_watchpoint tries to tell gdb that the new expression it creates
has C syntax:
/* The above expression is in C. */
b->language = language_c;
However, update_watchpoint doesn't actually use this language when
re-parsing the expression.
Originally I was going to fix this by saving and restoring the
language in update_watchpoint, but this regressed
gdb.dlang/watch-loc.exp, because the constructed expression actually
has D syntax (specifically the name is not parseable by C).
Next I looked at directly constructing an expression, and not relying
on the parser at all; but it seemed to me that upon a re-set, we'd
want to reparse the type, and there is no existing API to do this
correctly.
So, in the end I made a hook to let each language choose what
expression to use. I made all the languages other than Rust use the C
expression, because that is the status quo ante. However, this is
probably not truly correct. After this patch, at least, it is easy to
correct by someone who knows the language(s) in question.
Regtested by the buildbot.
ChangeLog
2017-05-19 Tom Tromey <tom@tromey.com>
PR rust/21484:
* rust-lang.c (exp_descriptor_rust): New function.
(rust_language_defn): Use it.
* p-lang.c (pascal_language_defn): Update.
* opencl-lang.c (opencl_language_defn): Update.
* objc-lang.c (objc_language_defn): Update.
* m2-lang.c (m2_language_defn): Update.
* language.h (struct language_defn)
<la_watch_location_expression>: New member.
* language.c (unknown_language_defn, auto_language_defn)
(local_language_defn): Update.
* go-lang.c (go_language_defn): Update.
* f-lang.c (f_language_defn): Update.
* d-lang.c (d_language_defn): Update.
* c-lang.h (c_watch_location_expression): Declare.
* c-lang.c (c_watch_location_expression): New function.
(c_language_defn, cplus_language_defn, asm_language_defn)
(minimal_language_defn): Use it.
* breakpoint.c (watch_command_1): Call
la_watch_location_expression.
* ada-lang.c (ada_language_defn): Update.
testsuite/ChangeLog
2017-05-19 Tom Tromey <tom@tromey.com>
PR rust/21484:
* gdb.rust/watch.exp: New file.
* gdb.rust/watch.rs: New file.
2017-05-14 19:12:14 +02:00
|
|
|
|
rust_watch_location_expression,
|
Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
Summary:
- This is preparation for supporting wild name matching on C++ too.
- This is also preparation for TAB-completion fixes.
- Makes symbol name matching (think strcmp_iw) be based on a per-language method.
- Merges completion and non-completion name comparison (think
language_ops::la_get_symbol_name_cmp generalized).
- Avoid re-hashing lookup name multiple times
- Centralizes preparing a name for lookup (Ada name encoding / C++ Demangling),
both completion and non-completion.
- Fixes Ada latent bug with verbatim name matches in expressions
- Makes ada-lang.c use common|symtab.c completion code a bit more.
Ada's wild matching basically means that
"(gdb) break foo"
will find all methods named "foo" in all packages. Translating to
C++, it's roughly the same as saying that "break klass::method" sets
breakpoints on all "klass::method" methods of all classes, no matter
the namespace. A following patch will teach GDB about fullname vs
wild matching for C++ too. This patch is preparatory work to get
there.
Another idea here is to do symbol name matching based on the symbol
language's algorithm. I.e., avoid dependency on current language set.
This allows for example doing
(gdb) b foo::bar< int > (<tab>
and having gdb name match the C++ symbols correctly even if the
current language is C or Assembly (or Rust, or Ada, or ...), which can
easily happen if you step into an Assembly/C runtime library frame.
By encapsulating all the information related to a lookup name in a
class, we can also cache hash computation for a given language in the
lookup name object, to avoid recomputing it over and over.
Similarly, because we don't really know upfront which languages the
lookup name will be matched against, for each language we store the
lookup name transformed into a search name. E.g., for C++, that means
demangling the name. But for Ada, it means encoding the name. This
actually forces us to centralize all the different lookup name
encoding in a central place, resulting in clearer code, IMO. See
e.g., the new ada_lookup_name_info class.
The lookup name -> symbol search name computation is also done only
once per language.
The old language->la_get_symbol_name_cmp / symbol_name_cmp_ftype are
generalized to work with both completion, and normal symbol look up.
At some point early on, I had separate completion vs non-completion
language vector entry points, but a single method ends up being better
IMO for simplifying things -- the more we merge the completion /
non-completion name lookup code paths, the less changes for bugs
causing completion vs normal lookup finding different symbols.
The ada-lex.l change is necessary because when doing
(gdb) p <UpperCase>
then the name that is passed to write_ write_var_or_type ->
ada_lookup_symbol_list misses the "<>", i.e., it's just "UpperCase",
and we end up doing a wild match against "UpperCase" lowercased by
ada_lookup_name_info's constructor. I.e., "uppercase" wouldn't ever
match "UpperCase", and the symbol lookup fails.
This wouldn't cause any regression in the testsuite, but I added a new
test that would pass before the patch and fail after, if it weren't
for that fix.
This is latent bug that happens to go unnoticed because that
particular path was inconsistent with the rest of Ada symbol lookup by
not lowercasing the lookup name.
Ada's symbol_completion_add is deleted, replaced by using common
code's completion_list_add_name. To make the latter work for Ada, we
needed to add a new output parameter, because Ada wants to return back
a custom completion candidates that are not the symbol name.
With this patch, minimal symbol demangled name hashing is made
consistent with regular symbol hashing. I.e., it now goes via the
language vector's search_name_hash method too, as I had suggested in a
previous patch.
dw2_expand_symtabs_matching / .gdb_index symbol names were a
challenge. The problem is that we have no way to telling what is the
language of each symbol name found in the index, until we expand the
corresponding full symbol, which is off course what we're trying to
avoid. Language information is simply not considered in the index
format... Since the symbol name hashing and comparison routines are
per-language, we now have a problem. The patch sorts this out by
matching each name against all languages. This is inneficient, and
indeed slows down completion several times. E.g., with:
$ cat script.cmd
set pagination off
set $count = 0
while $count < 400
complete b string_prin
printf "count = %d\n", $count
set $count = $count + 1
end
$ time gdb --batch -q ./gdb-with-index -ex "source script-string_printf.cmd"
I get, before patch (-O2, x86-64):
real 0m1.773s
user 0m1.737s
sys 0m0.040s
While after patch (-O2, x86-64):
real 0m9.843s
user 0m9.482s
sys 0m0.034s
However, the following patch will optimize this, and will actually
make this use case faster compared to the "before patch" above:
real 0m1.321s
user 0m1.285s
sys 0m0.039s
gdb/ChangeLog:
2017-11-08 Pedro Alves <palves@redhat.com>
* ada-lang.c (ada_encode): Rename to ..
(ada_encode_1): ... this. Add throw_errors parameter and handle
it.
(ada_encode): Reimplement.
(match_name): Delete, folded into full_name.
(resolve_subexp): No longer pass the encoded name to
ada_lookup_symbol_list.
(should_use_wild_match): Delete.
(name_match_type_from_name): New.
(ada_lookup_simple_minsym): Use lookup_name_info and the
language's symbol_name_matcher_ftype.
(add_symbols_from_enclosing_procs, ada_add_local_symbols)
(ada_add_block_renamings): Adjust to use lookup_name_info.
(ada_lookup_name): New.
(add_nonlocal_symbols, ada_add_all_symbols)
(ada_lookup_symbol_list_worker, ada_lookup_symbol_list)
(ada_iterate_over_symbols): Adjust to use lookup_name_info.
(ada_name_for_lookup): Delete.
(ada_lookup_encoded_symbol): Construct a verbatim name.
(wild_match): Reverse sense of return type. Use bool.
(full_match): Reverse sense of return type. Inline bits of old
match_name here.
(ada_add_block_symbols): Adjust to use lookup_name_info.
(symbol_completion_match): Delete, folded into...
(ada_lookup_name_info::matches): ... .this new method.
(symbol_completion_add): Delete.
(ada_collect_symbol_completion_matches): Add name_match_type
parameter. Adjust to use lookup_name_info and
completion_list_add_name.
(get_var_value, ada_add_global_exceptions): Adjust to use
lookup_name_info.
(ada_get_symbol_name_cmp): Delete.
(do_wild_match, do_full_match): New functions.
(ada_lookup_name_info::ada_lookup_name_info): New method.
(ada_symbol_name_matches, ada_get_symbol_name_matcher): New
functions.
(ada_language_defn): Install ada_get_symbol_name_matcher.
* ada-lex.l (processId): If name starts with '<', copy it
verbatim.
* block.c (block_iter_match_step, block_iter_match_first)
(block_iter_match_next, block_lookup_symbol)
(block_lookup_symbol_primary, block_find_symbol): Adjust to use
lookup_name_info.
* block.h (block_iter_match_first, block_iter_match_next)
(ALL_BLOCK_SYMBOLS_WITH_NAME): Adjust to use lookup_name_info.
* c-lang.c (c_language_defn, cplus_language_defn)
(asm_language_defn, minimal_language_defn): Adjust comments to
refer to la_get_symbol_name_matcher.
* completer.c (complete_files_symbols)
(collect_explicit_location_matches, symbol_completer): Pass a
symbol_name_match_type down.
* completer.h (class completion_match, completion_match_result):
New classes.
(completion_tracker::reset_completion_match_result): New method.
(completion_tracker::m_completion_match_result): New field.
* cp-support.c (make_symbol_overload_list_block): Adjust to use
lookup_name_info.
(cp_fq_symbol_name_matches, cp_get_symbol_name_matcher): New
functions.
* cp-support.h (cp_get_symbol_name_matcher): New declaration.
* d-lang.c: Adjust comments to refer to
la_get_symbol_name_matcher.
* dictionary.c (dict_vector) <iter_match_first, iter_match_next>:
Adjust to use lookup_name_info.
(dict_iter_match_first, dict_iter_match_next)
(iter_match_first_hashed, iter_match_next_hashed)
(iter_match_first_linear, iter_match_next_linear): Adjust to work
with a lookup_name_info.
* dictionary.h (dict_iter_match_first, dict_iter_match_next):
Likewise.
* dwarf2read.c (dw2_lookup_symbol): Adjust to use lookup_name_info.
(dw2_map_matching_symbols): Adjust to use symbol_name_match_type.
(gdb_index_symbol_name_matcher): New class.
(dw2_expand_symtabs_matching) Adjust to use lookup_name_info and
gdb_index_symbol_name_matcher. Accept a NULL symbol_matcher.
* f-lang.c (f_collect_symbol_completion_matches): Adjust to work
with a symbol_name_match_type.
(f_language_defn): Adjust comments to refer to
la_get_symbol_name_matcher.
* go-lang.c (go_language_defn): Adjust comments to refer to
la_get_symbol_name_matcher.
* language.c (default_symbol_name_matcher)
(language_get_symbol_name_matcher): New functions.
(unknown_language_defn, auto_language_defn): Adjust comments to
refer to la_get_symbol_name_matcher.
* language.h (symbol_name_cmp_ftype): Delete.
(language_defn) <la_collect_symbol_completion_matches>: Add match
type parameter.
<la_get_symbol_name_cmp>: Delete field.
<la_get_symbol_name_matcher>: New field.
<la_iterate_over_symbols>: Adjust to use lookup_name_info.
(default_symbol_name_matcher, language_get_symbol_name_matcher):
Declare.
* linespec.c (iterate_over_all_matching_symtabs)
(iterate_over_file_blocks): Adjust to use lookup_name_info.
(find_methods): Add language parameter, and use lookup_name_info
and the language's symbol_name_matcher_ftype.
(linespec_complete_function): Adjust.
(lookup_prefix_sym): Use lookup_name_info.
(add_all_symbol_names_from_pspace): Adjust.
(find_superclass_methods): Add language parameter and pass it
down.
(find_method): Pass symbol language down.
(find_linespec_symbols): Don't demangle or Ada encode here.
(search_minsyms_for_name): Add lookup_name_info parameter.
(add_matching_symbols_to_info): Add name_match_type parameter.
Use lookup_name_info.
* m2-lang.c (m2_language_defn): Adjust comments to refer to
la_get_symbol_name_matcher.
* minsyms.c: Include <algorithm>.
(add_minsym_to_demangled_hash_table): Remove table parameter and
add objfile parameter. Use search_name_hash, and add language to
demangled languages vector.
(struct found_minimal_symbols): New struct.
(lookup_minimal_symbol_mangled, lookup_minimal_symbol_demangled):
New functions.
(lookup_minimal_symbol): Adjust to use them. Don't canonicalize
input names here. Use lookup_name_info instead. Lookup up
demangled names once for each language in the demangled names
vector.
(iterate_over_minimal_symbols): Use lookup_name_info. Lookup up
demangled names once for each language in the demangled names
vector.
(build_minimal_symbol_hash_tables): Adjust.
* minsyms.h (iterate_over_minimal_symbols): Adjust to pass down a
lookup_name_info.
* objc-lang.c (objc_language_defn): Adjust comment to refer to
la_get_symbol_name_matcher.
* objfiles.h: Include <vector>.
(objfile_per_bfd_storage) <demangled_hash_languages>: New field.
* opencl-lang.c (opencl_language_defn): Adjust comment to refer to
la_get_symbol_name_matcher.
* p-lang.c (pascal_language_defn): Adjust comment to refer to
la_get_symbol_name_matcher.
* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
(match_partial_symbol): Use symbol_name_match_type,
lookup_name_info and psymbol_name_matches.
(lookup_partial_symbol): Use lookup_name_info.
(map_block): Use symbol_name_match_type and lookup_name_info.
(psym_map_matching_symbols): Use symbol_name_match_type.
(psymbol_name_matches): New.
(recursively_search_psymtabs): Use lookup_name_info and
psymbol_name_matches. Rename 'kind' parameter to 'domain'.
(psym_expand_symtabs_matching): Use lookup_name_info. Rename
'kind' parameter to 'domain'.
* rust-lang.c (rust_language_defn): Adjust comment to refer to
la_get_symbol_name_matcher.
* symfile-debug.c (debug_qf_map_matching_symbols)
(debug_qf_map_matching_symbols): Use symbol_name_match_type.
(debug_qf_expand_symtabs_matching): Use lookup_name_info.
* symfile.c (expand_symtabs_matching): Use lookup_name_info.
* symfile.h (quick_symbol_functions) <map_matching_symbols>:
Adjust to use symbol_name_match_type.
<expand_symtabs_matching>: Adjust to use lookup_name_info.
(expand_symtabs_matching): Adjust to use lookup_name_info.
* symmisc.c (maintenance_expand_symtabs): Use
lookup_name_info::match_any ().
* symtab.c (symbol_matches_search_name): New.
(eq_symbol_entry): Adjust to use lookup_name_info and the
language's matcher.
(demangle_for_lookup_info::demangle_for_lookup_info): New.
(lookup_name_info::match_any): New.
(iterate_over_symbols, search_symbols): Use lookup_name_info.
(compare_symbol_name): Add language, lookup_name_info and
completion_match_result parameters, and use them.
(completion_list_add_name): Make extern. Add language and
lookup_name_info parameters. Use them.
(completion_list_add_symbol, completion_list_add_msymbol)
(completion_list_objc_symbol): Add lookup_name_info parameters and
adjust. Pass down language.
(completion_list_add_fields): Add lookup_name_info parameters and
adjust. Pass down language.
(add_symtab_completions): Add lookup_name_info parameters and
adjust.
(default_collect_symbol_completion_matches_break_on): Add
name_match_type parameter, and use it. Use lookup_name_info.
(default_collect_symbol_completion_matches)
(collect_symbol_completion_matches): Add name_match_type
parameter, and pass it down.
(collect_symbol_completion_matches_type): Adjust.
(collect_file_symbol_completion_matches): Add name_match_type
parameter, and use lookup_name_info.
* symtab.h: Include <string> and "common/gdb_optional.h".
(enum class symbol_name_match_type): New.
(class ada_lookup_name_info): New.
(struct demangle_for_lookup_info): New.
(class lookup_name_info): New.
(symbol_name_matcher_ftype): New.
(SYMBOL_MATCHES_SEARCH_NAME): Use symbol_matches_search_name.
(symbol_matches_search_name): Declare.
(MSYMBOL_MATCHES_SEARCH_NAME): Delete.
(default_collect_symbol_completion_matches)
(collect_symbol_completion_matches)
(collect_file_symbol_completion_matches): Add name_match_type
parameter.
(iterate_over_symbols): Use lookup_name_info.
(completion_list_add_name): Declare.
* utils.c (enum class strncmp_iw_mode): Moved to utils.h.
(strncmp_iw_with_mode): Now extern.
* utils.h (enum class strncmp_iw_mode): Moved from utils.c.
(strncmp_iw_with_mode): Declare.
gdb/testsuite/ChangeLog:
2017-11-08 Pedro Alves <palves@redhat.com>
* gdb.ada/complete.exp (p <Exported_Capitalized>): New test.
(p Exported_Capitalized): New test.
(p exported_capitalized): New test.
2017-11-08 15:22:32 +01:00
|
|
|
|
NULL, /* la_get_symbol_name_matcher */
|
2016-04-27 03:38:08 +02:00
|
|
|
|
iterate_over_symbols,
|
Per-language symbol name hashing algorithm
Currently, we have a mess of symbol name hashing/comparison routines.
There's msymbol_hash for mangled names, and dict_hash and
msymbol_hash_iw for demangled names. Then there's strcmp_iw,
strcmp_iw_ordered and Ada's full_match/wild_match, which all have to
agree with the hashing routines. That's why dict_hash is really about
Ada names. From the inconsistency department, minimal symbol hashing
doesn't go via dict_hash, so Ada's wild matching can't ever work with
minimal symbols.
This patch starts fixing this, by doing two things:
#1 - adds a language vector method to let each language decide how to
compute a symbol name hash.
#2 - makes dictionaries know the language of the symbols they hold,
and then use the dictionaries language to decide which hashing
method to use.
For now, this is just scaffolding, since all languages install the
default method. The series will make C++ install its own hashing
method later on, and will add per-language symbol name comparison
routines too.
This patch was originally based on a patch that Keith wrote for the
libcc1/C++ WIP support.
gdb/ChangeLog:
2017-11-08 Keith Seitz <keiths@redhat.com>
Pedro Alves <palves@redhat.com>
* ada-lang.c (ada_language_defn): Install
default_search_name_hash.
* buildsym.c (struct buildsym_compunit): <language>: New field.
(finish_block_internal): Pass language when creating dictionaries.
(start_buildsym_compunit, start_symtab): New language parameters.
Use them.
(restart_symtab): Pass down compilation unit's language.
* buildsym.h (enum language): Forward declare.
(start_symtab): New 'language' parameter.
* c-lang.c (c_language_defn, cplus_language_defn)
(asm_language_defn, minimal_language_defn): Install
default_search_name_hash.
* coffread.c (coff_start_symtab): Adjust.
* d-lang.c (d_language_defn): Install default_search_name_hash.
* dbxread.c (struct symloc): Add 'pst_language' field.
(PST_LANGUAGE): Define.
(start_psymtab, read_ofile_symtab): Use it.
(process_one_symbol): New 'language' parameter. Pass it down.
* dictionary.c (struct dictionary) <language>: New field.
(DICT_LANGUAGE): Define.
(dict_create_hashed, dict_create_hashed_expandable)
(dict_create_linear, dict_create_linear_expandable): New parameter
'language'. Set the dictionary's language.
(iter_match_first_hashed): Adjust to rename.
(insert_symbol_hashed): Assert we don't see mismatching
languages. Adjust to rename.
(dict_hash): Rename to ...
(default_search_name_hash): ... this and make extern.
* dictionary.h (struct language_defn): Forward declare.
(dict_create_hashed): New parameter 'language'.
* dwarf2read.c (dwarf2_start_symtab): Pass down language.
* f-lang.c (f_language_defn): Install default_search_name_hash.
* go-lang.c (go_language_defn): Install default_search_name_hash.
* jit.c (finalize_symtab): Pass compunit's language to dictionary
creation.
* language.c (unknown_language_defn, auto_language_defn):
* language.h (language_defn::la_search_name_hash): New field.
(default_search_name_hash): Declare.
* m2-lang.c (m2_language_defn): Install default_search_name_hash.
* mdebugread.c (new_block): New parameter 'language'.
* mdebugread.c (parse_symbol): Pass symbol language to block
allocation.
(psymtab_to_symtab_1): Pass down language.
(new_symtab): Pass compunit's language to block allocation.
* objc-lang.c (objc_language_defn): Install
default_search_name_hash.
* opencl-lang.c (opencl_language_defn):
* p-lang.c (pascal_language_defn): Install
default_search_name_hash.
* rust-lang.c (rust_language_defn): Install
default_search_name_hash.
* stabsread.h (enum language): Forward declare.
(process_one_symbol): Add 'language' parameter.
* symtab.c (search_name_hash): New function.
* symtab.h (search_name_hash): Declare.
* xcoffread.c (read_xcoff_symtab): Pass language to start_symtab.
2017-11-08 16:07:56 +01:00
|
|
|
|
default_search_name_hash,
|
2016-04-27 03:38:08 +02:00
|
|
|
|
&default_varobj_ops,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
LANG_MAGIC
|
|
|
|
|
};
|