| /* Support for printing C++ values for GDB, the GNU debugger. |
| |
| Copyright (C) 1986-2024 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "event-top.h" |
| #include "extract-store-integer.h" |
| #include "gdbsupport/gdb_obstack.h" |
| #include "symtab.h" |
| #include "gdbtypes.h" |
| #include "expression.h" |
| #include "value.h" |
| #include "command.h" |
| #include "cli/cli-cmds.h" |
| #include "demangle.h" |
| #include "annotate.h" |
| #include "c-lang.h" |
| #include "target.h" |
| #include "cp-abi.h" |
| #include "valprint.h" |
| #include "cp-support.h" |
| #include "language.h" |
| #include "extension.h" |
| #include "typeprint.h" |
| #include "gdbsupport/byte-vector.h" |
| #include "gdbarch.h" |
| #include "cli/cli-style.h" |
| #include "gdbsupport/selftest.h" |
| #include "selftest-arch.h" |
| |
| static struct obstack dont_print_vb_obstack; |
| static struct obstack dont_print_statmem_obstack; |
| static struct obstack dont_print_stat_array_obstack; |
| |
| static void cp_print_static_field (struct type *, struct value *, |
| struct ui_file *, int, |
| const struct value_print_options *); |
| |
| static void cp_print_value (struct value *, struct ui_file *, |
| int, const struct value_print_options *, |
| struct type **); |
| |
| |
| /* GCC versions after 2.4.5 use this. */ |
| const char vtbl_ptr_name[] = "__vtbl_ptr_type"; |
| |
| /* Return truth value for assertion that TYPE is of the type |
| "pointer to virtual function". */ |
| |
| int |
| cp_is_vtbl_ptr_type (struct type *type) |
| { |
| const char *type_name = type->name (); |
| |
| return (type_name != NULL && !strcmp (type_name, vtbl_ptr_name)); |
| } |
| |
| /* Return truth value for the assertion that TYPE is of the type |
| "pointer to virtual function table". */ |
| |
| int |
| cp_is_vtbl_member (struct type *type) |
| { |
| /* With older versions of g++, the vtbl field pointed to an array of |
| structures. Nowadays it points directly to the structure. */ |
| if (type->code () == TYPE_CODE_PTR) |
| { |
| type = type->target_type (); |
| if (type->code () == TYPE_CODE_ARRAY) |
| { |
| type = type->target_type (); |
| if (type->code () == TYPE_CODE_STRUCT /* if not using thunks */ |
| || type->code () == TYPE_CODE_PTR) /* if using thunks */ |
| { |
| /* Virtual functions tables are full of pointers |
| to virtual functions. */ |
| return cp_is_vtbl_ptr_type (type); |
| } |
| } |
| else if (type->code () == TYPE_CODE_STRUCT) /* if not using thunks */ |
| { |
| return cp_is_vtbl_ptr_type (type); |
| } |
| else if (type->code () == TYPE_CODE_PTR) /* if using thunks */ |
| { |
| /* The type name of the thunk pointer is NULL when using |
| dwarf2. We could test for a pointer to a function, but |
| there is no type info for the virtual table either, so it |
| wont help. */ |
| return cp_is_vtbl_ptr_type (type); |
| } |
| } |
| return 0; |
| } |
| |
| /* Mutually recursive subroutines of cp_print_value and c_val_print to |
| print out a structure's fields: cp_print_value_fields and |
| cp_print_value. |
| |
| TYPE, VALADDR, ADDRESS, STREAM, RECURSE, and OPTIONS have the same |
| meanings as in cp_print_value and c_val_print. |
| |
| 2nd argument REAL_TYPE is used to carry over the type of the |
| derived class across the recursion to base classes. |
| |
| DONT_PRINT is an array of baseclass types that we should not print, |
| or zero if called from top level. */ |
| |
| void |
| cp_print_value_fields (struct value *val, struct ui_file *stream, |
| int recurse, const struct value_print_options *options, |
| struct type **dont_print_vb, |
| int dont_print_statmem) |
| { |
| int i, len, n_baseclasses; |
| int fields_seen = 0; |
| static int last_set_recurse = -1; |
| |
| struct type *type = check_typedef (val->type ()); |
| |
| if (recurse == 0) |
| { |
| /* Any object can be left on obstacks only during an unexpected |
| error. */ |
| |
| if (obstack_object_size (&dont_print_statmem_obstack) > 0) |
| { |
| obstack_free (&dont_print_statmem_obstack, NULL); |
| obstack_begin (&dont_print_statmem_obstack, |
| 32 * sizeof (CORE_ADDR)); |
| } |
| if (obstack_object_size (&dont_print_stat_array_obstack) > 0) |
| { |
| obstack_free (&dont_print_stat_array_obstack, NULL); |
| obstack_begin (&dont_print_stat_array_obstack, |
| 32 * sizeof (struct type *)); |
| } |
| } |
| |
| gdb_printf (stream, "{"); |
| len = type->num_fields (); |
| n_baseclasses = TYPE_N_BASECLASSES (type); |
| |
| /* First, print out baseclasses such that we don't print |
| duplicates of virtual baseclasses. */ |
| |
| if (n_baseclasses > 0) |
| cp_print_value (val, stream, recurse + 1, options, dont_print_vb); |
| |
| /* Second, print out data fields */ |
| |
| /* If there are no data fields, skip this part */ |
| if (len == n_baseclasses || !len) |
| fprintf_styled (stream, metadata_style.style (), "<No data fields>"); |
| else |
| { |
| size_t statmem_obstack_initial_size = 0; |
| size_t stat_array_obstack_initial_size = 0; |
| struct type *vptr_basetype = NULL; |
| int vptr_fieldno; |
| |
| if (dont_print_statmem == 0) |
| { |
| statmem_obstack_initial_size = |
| obstack_object_size (&dont_print_statmem_obstack); |
| |
| if (last_set_recurse != recurse) |
| { |
| stat_array_obstack_initial_size = |
| obstack_object_size (&dont_print_stat_array_obstack); |
| |
| last_set_recurse = recurse; |
| } |
| } |
| |
| vptr_fieldno = get_vptr_fieldno (type, &vptr_basetype); |
| for (i = n_baseclasses; i < len; i++) |
| { |
| const gdb_byte *valaddr = val->contents_for_printing ().data (); |
| |
| /* If requested, skip printing of static fields. */ |
| if (!options->static_field_print |
| && type->field (i).is_static ()) |
| continue; |
| |
| if (fields_seen) |
| { |
| gdb_puts (",", stream); |
| if (!options->prettyformat) |
| gdb_puts (" ", stream); |
| } |
| else if (n_baseclasses > 0) |
| { |
| if (options->prettyformat) |
| { |
| gdb_printf (stream, "\n"); |
| print_spaces (2 + 2 * recurse, stream); |
| gdb_puts ("members of ", stream); |
| gdb_puts (type->name (), stream); |
| gdb_puts (":", stream); |
| } |
| } |
| fields_seen = 1; |
| |
| if (options->prettyformat) |
| { |
| gdb_printf (stream, "\n"); |
| print_spaces (2 + 2 * recurse, stream); |
| } |
| else |
| { |
| stream->wrap_here (2 + 2 * recurse); |
| } |
| |
| annotate_field_begin (type->field (i).type ()); |
| |
| if (type->field (i).is_static ()) |
| { |
| gdb_puts ("static ", stream); |
| fprintf_symbol (stream, |
| type->field (i).name (), |
| current_language->la_language, |
| DMGL_PARAMS | DMGL_ANSI); |
| } |
| else |
| fputs_styled (type->field (i).name (), |
| variable_name_style.style (), stream); |
| annotate_field_name_end (); |
| |
| /* We tweak various options in a few cases below. */ |
| value_print_options options_copy = *options; |
| value_print_options *opts = &options_copy; |
| |
| /* Do not print leading '=' in case of anonymous |
| unions. */ |
| if (strcmp (type->field (i).name (), "")) |
| gdb_puts (" = ", stream); |
| else |
| { |
| /* If this is an anonymous field then we want to consider it |
| as though it is at its parent's depth when it comes to the |
| max print depth. */ |
| if (opts->max_depth != -1 && opts->max_depth < INT_MAX) |
| ++opts->max_depth; |
| } |
| annotate_field_value (); |
| |
| if (!type->field (i).is_static () |
| && type->field (i).is_packed ()) |
| { |
| struct value *v; |
| |
| /* Bitfields require special handling, especially due to |
| byte order problems. */ |
| if (type->field (i).is_ignored ()) |
| { |
| fputs_styled ("<optimized out or zero length>", |
| metadata_style.style (), stream); |
| } |
| else if (val->bits_synthetic_pointer |
| (type->field (i).loc_bitpos (), |
| type->field (i).bitsize ())) |
| { |
| fputs_styled (_("<synthetic pointer>"), |
| metadata_style.style (), stream); |
| } |
| else |
| { |
| opts->deref_ref = false; |
| |
| v = value_field_bitfield (type, i, valaddr, |
| val->embedded_offset (), val); |
| |
| common_val_print (v, stream, recurse + 1, |
| opts, current_language); |
| } |
| } |
| else |
| { |
| if (type->field (i).is_ignored ()) |
| { |
| fputs_styled ("<optimized out or zero length>", |
| metadata_style.style (), stream); |
| } |
| else if (type->field (i).is_static ()) |
| { |
| try |
| { |
| struct value *v = value_static_field (type, i); |
| |
| cp_print_static_field (type->field (i).type (), |
| v, stream, recurse + 1, |
| opts); |
| } |
| catch (const gdb_exception_error &ex) |
| { |
| fprintf_styled (stream, metadata_style.style (), |
| _("<error reading variable: %s>"), |
| ex.what ()); |
| } |
| } |
| else if (i == vptr_fieldno && type == vptr_basetype) |
| { |
| int i_offset = type->field (i).loc_bitpos () / 8; |
| struct type *i_type = type->field (i).type (); |
| |
| if (valprint_check_validity (stream, i_type, i_offset, val)) |
| { |
| CORE_ADDR addr; |
| |
| i_offset += val->embedded_offset (); |
| addr = extract_typed_address (valaddr + i_offset, i_type); |
| print_function_pointer_address (opts, |
| type->arch (), |
| addr, stream); |
| } |
| } |
| else |
| { |
| struct value *v = val->primitive_field (0, i, type); |
| opts->deref_ref = false; |
| common_val_print (v, stream, recurse + 1, opts, |
| current_language); |
| } |
| } |
| annotate_field_end (); |
| } |
| |
| if (dont_print_statmem == 0) |
| { |
| size_t obstack_final_size = |
| obstack_object_size (&dont_print_statmem_obstack); |
| |
| if (obstack_final_size > statmem_obstack_initial_size) |
| { |
| /* In effect, a pop of the printed-statics stack. */ |
| size_t shrink_bytes |
| = statmem_obstack_initial_size - obstack_final_size; |
| obstack_blank_fast (&dont_print_statmem_obstack, shrink_bytes); |
| } |
| |
| if (last_set_recurse != recurse) |
| { |
| obstack_final_size = |
| obstack_object_size (&dont_print_stat_array_obstack); |
| |
| if (obstack_final_size > stat_array_obstack_initial_size) |
| { |
| void *free_to_ptr = |
| (char *) obstack_next_free (&dont_print_stat_array_obstack) |
| - (obstack_final_size |
| - stat_array_obstack_initial_size); |
| |
| obstack_free (&dont_print_stat_array_obstack, |
| free_to_ptr); |
| } |
| last_set_recurse = -1; |
| } |
| } |
| |
| if (options->prettyformat) |
| { |
| gdb_printf (stream, "\n"); |
| print_spaces (2 * recurse, stream); |
| } |
| } /* if there are data fields */ |
| |
| gdb_printf (stream, "}"); |
| } |
| |
| /* A wrapper for cp_print_value_fields that tries to apply a |
| pretty-printer first. */ |
| |
| static void |
| cp_print_value_fields_pp (struct value *val, |
| struct ui_file *stream, |
| int recurse, |
| const struct value_print_options *options, |
| struct type **dont_print_vb, |
| int dont_print_statmem) |
| { |
| int result = 0; |
| |
| /* Attempt to run an extension language pretty-printer if |
| possible. */ |
| if (!options->raw) |
| result |
| = apply_ext_lang_val_pretty_printer (val, stream, |
| recurse, options, |
| current_language); |
| |
| if (!result) |
| cp_print_value_fields (val, stream, recurse, options, dont_print_vb, |
| dont_print_statmem); |
| } |
| |
| /* Special val_print routine to avoid printing multiple copies of |
| virtual baseclasses. */ |
| |
| static void |
| cp_print_value (struct value *val, struct ui_file *stream, |
| int recurse, const struct value_print_options *options, |
| struct type **dont_print_vb) |
| { |
| struct type *type = check_typedef (val->type ()); |
| CORE_ADDR address = val->address (); |
| struct type **last_dont_print |
| = (struct type **) obstack_next_free (&dont_print_vb_obstack); |
| struct obstack tmp_obstack = dont_print_vb_obstack; |
| int i, n_baseclasses = TYPE_N_BASECLASSES (type); |
| const gdb_byte *valaddr = val->contents_for_printing ().data (); |
| |
| if (dont_print_vb == 0) |
| { |
| /* If we're at top level, carve out a completely fresh chunk of |
| the obstack and use that until this particular invocation |
| returns. */ |
| /* Bump up the high-water mark. Now alpha is omega. */ |
| obstack_finish (&dont_print_vb_obstack); |
| } |
| |
| for (i = 0; i < n_baseclasses; i++) |
| { |
| LONGEST boffset = 0; |
| int skip = 0; |
| struct type *baseclass = check_typedef (TYPE_BASECLASS (type, i)); |
| const char *basename = baseclass->name (); |
| struct value *base_val = NULL; |
| |
| if (BASETYPE_VIA_VIRTUAL (type, i)) |
| { |
| struct type **first_dont_print |
| = (struct type **) obstack_base (&dont_print_vb_obstack); |
| |
| int j = (struct type **) |
| obstack_next_free (&dont_print_vb_obstack) - first_dont_print; |
| |
| while (--j >= 0) |
| if (baseclass == first_dont_print[j]) |
| goto flush_it; |
| |
| obstack_ptr_grow (&dont_print_vb_obstack, baseclass); |
| } |
| |
| try |
| { |
| boffset = baseclass_offset (type, i, valaddr, |
| val->embedded_offset (), |
| address, val); |
| } |
| catch (const gdb_exception_error &ex) |
| { |
| if (ex.error == NOT_AVAILABLE_ERROR) |
| skip = -1; |
| else |
| skip = 1; |
| } |
| |
| if (skip == 0) |
| { |
| if (BASETYPE_VIA_VIRTUAL (type, i)) |
| { |
| /* The virtual base class pointer might have been |
| clobbered by the user program. Make sure that it |
| still points to a valid memory location. */ |
| |
| if (boffset < 0 || boffset >= type->length ()) |
| { |
| gdb::byte_vector buf (baseclass->length ()); |
| |
| if (target_read_memory (address + boffset, buf.data (), |
| baseclass->length ()) != 0) |
| skip = 1; |
| base_val = value_from_contents_and_address (baseclass, |
| buf.data (), |
| address + boffset); |
| baseclass = base_val->type (); |
| boffset = 0; |
| } |
| else |
| { |
| base_val = val; |
| } |
| } |
| else |
| { |
| base_val = val; |
| } |
| } |
| |
| /* Now do the printing. */ |
| if (options->prettyformat) |
| { |
| gdb_printf (stream, "\n"); |
| print_spaces (2 * recurse, stream); |
| } |
| gdb_puts ("<", stream); |
| /* Not sure what the best notation is in the case where there is |
| no baseclass name. */ |
| gdb_puts (basename ? basename : "", stream); |
| gdb_puts ("> = ", stream); |
| |
| if (skip < 0) |
| val_print_unavailable (stream); |
| else if (skip > 0) |
| val_print_invalid_address (stream); |
| else |
| { |
| if (!val_print_check_max_depth (stream, recurse, options, |
| current_language)) |
| { |
| struct value *baseclass_val = val->primitive_field (0, |
| i, type); |
| |
| cp_print_value_fields_pp |
| (baseclass_val, stream, recurse, options, |
| (struct type **) obstack_base (&dont_print_vb_obstack), |
| 0); |
| } |
| } |
| gdb_puts (", ", stream); |
| |
| flush_it: |
| ; |
| } |
| |
| if (dont_print_vb == 0) |
| { |
| /* Free the space used to deal with the printing |
| of this type from top level. */ |
| obstack_free (&dont_print_vb_obstack, last_dont_print); |
| /* Reset watermark so that we can continue protecting |
| ourselves from whatever we were protecting ourselves. */ |
| dont_print_vb_obstack = tmp_obstack; |
| } |
| } |
| |
| /* Print value of a static member. To avoid infinite recursion when |
| printing a class that contains a static instance of the class, we |
| keep the addresses of all printed static member classes in an |
| obstack and refuse to print them more than once. |
| |
| VAL contains the value to print, TYPE, STREAM, RECURSE, and OPTIONS |
| have the same meanings as in c_val_print. */ |
| |
| static void |
| cp_print_static_field (struct type *type, |
| struct value *val, |
| struct ui_file *stream, |
| int recurse, |
| const struct value_print_options *options) |
| { |
| struct value_print_options opts; |
| |
| if (val->entirely_optimized_out ()) |
| { |
| val_print_optimized_out (val, stream); |
| return; |
| } |
| |
| struct type *real_type = check_typedef (type); |
| if (real_type->code () == TYPE_CODE_STRUCT) |
| { |
| CORE_ADDR *first_dont_print; |
| CORE_ADDR addr = val->address (); |
| int i; |
| |
| first_dont_print |
| = (CORE_ADDR *) obstack_base (&dont_print_statmem_obstack); |
| i = obstack_object_size (&dont_print_statmem_obstack) |
| / sizeof (CORE_ADDR); |
| |
| while (--i >= 0) |
| { |
| if (addr == first_dont_print[i]) |
| { |
| fputs_styled (_("<same as static member of an already" |
| " seen type>"), |
| metadata_style.style (), stream); |
| return; |
| } |
| } |
| |
| obstack_grow (&dont_print_statmem_obstack, (char *) &addr, |
| sizeof (CORE_ADDR)); |
| cp_print_value_fields_pp (val, stream, recurse, options, nullptr, 1); |
| return; |
| } |
| |
| if (real_type->code () == TYPE_CODE_ARRAY) |
| { |
| struct type **first_dont_print; |
| int i; |
| struct type *target_type = type->target_type (); |
| |
| first_dont_print |
| = (struct type **) obstack_base (&dont_print_stat_array_obstack); |
| i = obstack_object_size (&dont_print_stat_array_obstack) |
| / sizeof (struct type *); |
| |
| while (--i >= 0) |
| { |
| if (target_type == first_dont_print[i]) |
| { |
| fputs_styled (_("<same as static member of an already" |
| " seen type>"), |
| metadata_style.style (), stream); |
| return; |
| } |
| } |
| |
| obstack_grow (&dont_print_stat_array_obstack, |
| (char *) &target_type, |
| sizeof (struct type *)); |
| } |
| |
| opts = *options; |
| opts.deref_ref = false; |
| common_val_print (val, stream, recurse, &opts, current_language); |
| } |
| |
| /* Find the field in *SELF, or its non-virtual base classes, with |
| bit offset OFFSET. Set *SELF to the containing type and *FIELDNO |
| to the containing field number. If OFFSET is not exactly at the |
| start of some field, set *SELF to NULL. */ |
| |
| static void |
| cp_find_class_member (struct type **self_p, int *fieldno, |
| LONGEST offset) |
| { |
| struct type *self; |
| unsigned int i; |
| unsigned len; |
| |
| *self_p = check_typedef (*self_p); |
| self = *self_p; |
| len = self->num_fields (); |
| |
| for (i = TYPE_N_BASECLASSES (self); i < len; i++) |
| { |
| field &f = self->field (i); |
| if (f.is_static ()) |
| continue; |
| LONGEST bitpos = f.loc_bitpos (); |
| |
| QUIT; |
| if (offset == bitpos) |
| { |
| *fieldno = i; |
| return; |
| } |
| } |
| |
| for (i = 0; i < TYPE_N_BASECLASSES (self); i++) |
| { |
| LONGEST bitpos = self->field (i).loc_bitpos (); |
| LONGEST bitsize = 8 * self->field (i).type ()->length (); |
| |
| if (offset >= bitpos && offset < bitpos + bitsize) |
| { |
| *self_p = self->field (i).type (); |
| cp_find_class_member (self_p, fieldno, offset - bitpos); |
| return; |
| } |
| } |
| |
| *self_p = NULL; |
| } |
| |
| void |
| cp_print_class_member (const gdb_byte *valaddr, struct type *type, |
| struct ui_file *stream, const char *prefix) |
| { |
| enum bfd_endian byte_order = type_byte_order (type); |
| |
| /* VAL is a byte offset into the structure type SELF_TYPE. |
| Find the name of the field for that offset and |
| print it. */ |
| struct type *self_type = TYPE_SELF_TYPE (type); |
| LONGEST val; |
| int fieldno; |
| |
| val = extract_signed_integer (valaddr, |
| type->length (), |
| byte_order); |
| |
| /* Pointers to data members are usually byte offsets into an object. |
| Because a data member can have offset zero, and a NULL pointer to |
| member must be distinct from any valid non-NULL pointer to |
| member, either the value is biased or the NULL value has a |
| special representation; both are permitted by ISO C++. HP aCC |
| used a bias of 0x20000000; HP cfront used a bias of 1; g++ 3.x |
| and other compilers which use the Itanium ABI use -1 as the NULL |
| value. GDB only supports that last form; to add support for |
| another form, make this into a cp-abi hook. */ |
| |
| if (val == -1) |
| { |
| gdb_printf (stream, "NULL"); |
| return; |
| } |
| |
| cp_find_class_member (&self_type, &fieldno, val << 3); |
| |
| if (self_type != NULL) |
| { |
| const char *name; |
| |
| gdb_puts (prefix, stream); |
| name = self_type->name (); |
| if (name) |
| gdb_puts (name, stream); |
| else |
| c_type_print_base (self_type, stream, 0, 0, &type_print_raw_options); |
| gdb_printf (stream, "::"); |
| fputs_styled (self_type->field (fieldno).name (), |
| variable_name_style.style (), stream); |
| } |
| else |
| gdb_printf (stream, "%ld", (long) val); |
| } |
| |
| #if GDB_SELF_TEST |
| |
| /* Test printing of TYPE_CODE_STRUCT values. */ |
| |
| static void |
| test_print_fields (gdbarch *arch) |
| { |
| struct field *f; |
| type *uint8_type = builtin_type (arch)->builtin_uint8; |
| type *bool_type = builtin_type (arch)->builtin_bool; |
| type *the_struct = arch_composite_type (arch, NULL, TYPE_CODE_STRUCT); |
| the_struct->set_length (4); |
| |
| /* Value: 1110 1001 |
| Fields: C-BB B-A- */ |
| if (gdbarch_byte_order (arch) == BFD_ENDIAN_LITTLE) |
| { |
| f = append_composite_type_field_raw (the_struct, "A", bool_type); |
| f->set_loc_bitpos (1); |
| f->set_bitsize (1); |
| f = append_composite_type_field_raw (the_struct, "B", uint8_type); |
| f->set_loc_bitpos (3); |
| f->set_bitsize (3); |
| f = append_composite_type_field_raw (the_struct, "C", bool_type); |
| f->set_loc_bitpos (7); |
| f->set_bitsize (1); |
| } |
| /* According to the logic commented in "make_gdb_type_struct ()" of |
| * target-descriptions.c, bit positions are numbered differently for |
| * little and big endians. */ |
| else |
| { |
| f = append_composite_type_field_raw (the_struct, "A", bool_type); |
| f->set_loc_bitpos (30); |
| f->set_bitsize (1); |
| f = append_composite_type_field_raw (the_struct, "B", uint8_type); |
| f->set_loc_bitpos (26); |
| f->set_bitsize (3); |
| f = append_composite_type_field_raw (the_struct, "C", bool_type); |
| f->set_loc_bitpos (24); |
| f->set_bitsize (1); |
| } |
| |
| value *val = value::allocate (the_struct); |
| gdb_byte *contents = val->contents_writeable ().data (); |
| store_unsigned_integer (contents, val->enclosing_type ()->length (), |
| gdbarch_byte_order (arch), 0xe9); |
| |
| string_file out; |
| struct value_print_options opts; |
| get_no_prettyformat_print_options (&opts); |
| cp_print_value_fields(val, &out, 0, &opts, NULL, 0); |
| SELF_CHECK (out.string () == "{A = false, B = 5, C = true}"); |
| |
| out.clear(); |
| opts.format = 'x'; |
| cp_print_value_fields(val, &out, 0, &opts, NULL, 0); |
| SELF_CHECK (out.string () == "{A = 0x0, B = 0x5, C = 0x1}"); |
| } |
| |
| #endif |
| |
| |
| void _initialize_cp_valprint (); |
| void |
| _initialize_cp_valprint () |
| { |
| #if GDB_SELF_TEST |
| selftests::register_test_foreach_arch ("print-fields", test_print_fields); |
| #endif |
| |
| obstack_begin (&dont_print_stat_array_obstack, |
| 32 * sizeof (struct type *)); |
| obstack_begin (&dont_print_statmem_obstack, |
| 32 * sizeof (CORE_ADDR)); |
| obstack_begin (&dont_print_vb_obstack, |
| 32 * sizeof (struct type *)); |
| } |