From: Daniel Jacobowitz Date: Sat, 14 Sep 2002 02:09:39 +0000 (+0000) Subject: * gdbtypes.c (check_stub_method): Make static. X-Git-Tag: drow-cplus-branchpoint~318 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=de17c821b37ffda295746b31cf47e19ca2303b78;p=external%2Fbinutils.git * gdbtypes.c (check_stub_method): Make static. (check_stub_method_group): New function. * gdbtypes.h: Update prototypes. * cp-support.c: New file. * cp-support.h: New file. * stabsread.c: Include "cp-abi.h" and "cp-support.h". (update_method_name_from_physname): New function. (read_member_functions): Correct method names for operators and v3 constructors/destructors. Separate v2 constructors and destructors. * Makefile.in (stabsread.o): Update dependencies. (SFILES): Add cp-support.c. (COMMON_OBS): Add cp-support.o. (cp_support_h, cp-support.o): Add. * cp-valprint.c (cp_print_class_method): Call check_stub_method_group instead of check_stub_method. Remove extraneous QUITs. * p-valprint.c (pascal_object_print_class_method): Likewise. * valops.c (search_struct_method): Likewise. (find_method_list, value_struct_elt_for_reference): Likewise. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 589e584..78af9f8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,28 @@ +2002-09-13 Daniel Jacobowitz + + * gdbtypes.c (check_stub_method): Make static. + (check_stub_method_group): New function. + * gdbtypes.h: Update prototypes. + * cp-support.c: New file. + * cp-support.h: New file. + + * stabsread.c: Include "cp-abi.h" and "cp-support.h". + (update_method_name_from_physname): New function. + (read_member_functions): Correct method names for operators + and v3 constructors/destructors. Separate v2 constructors and + destructors. + * Makefile.in (stabsread.o): Update dependencies. + (SFILES): Add cp-support.c. + (COMMON_OBS): Add cp-support.o. + (cp_support_h, cp-support.o): Add. + + * cp-valprint.c (cp_print_class_method): Call + check_stub_method_group instead of check_stub_method. Remove + extraneous QUITs. + * p-valprint.c (pascal_object_print_class_method): Likewise. + * valops.c (search_struct_method): Likewise. + (find_method_list, value_struct_elt_for_reference): Likewise. + 2002-09-13 Andrew Cagney * gdbarch.sh (SIGTRAMP_END): Change to a predicate function. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 45739e3..c2c1442 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -558,7 +558,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ ui-file.h ui-file.c \ frame.c doublest.c \ builtin-regs.c std-regs.c \ - gnu-v2-abi.c gnu-v3-abi.c hpacc-abi.c cp-abi.c + gnu-v2-abi.c gnu-v3-abi.c hpacc-abi.c cp-abi.c cp-support.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -626,6 +626,7 @@ command_h = command.h complaints_h = complaints.h completer_h = completer.h cp_abi_h = cp-abi.h +cp_support_h = cp-support.h dcache_h = dcache.h defs_h = defs.h $(config_h) $(gdb_locale_h) $(gdb_signals_h) $(ansidecl_h) \ $(libiberty_h) $(progress_h) $(bfd_h) $(tui_h) $(ui_file_h) $(xm_h) \ @@ -842,7 +843,7 @@ COMMON_OBS = version.o blockframe.o breakpoint.o findvar.o regcache.o \ nlmread.o serial.o mdebugread.o top.o utils.o \ ui-file.o \ frame.o doublest.o \ - gnu-v2-abi.o gnu-v3-abi.o hpacc-abi.o cp-abi.o + gnu-v2-abi.o gnu-v3-abi.o hpacc-abi.o cp-abi.o cp-support.o OBS = $(COMMON_OBS) $(ANNOTATE_OBS) @@ -1588,6 +1589,7 @@ corelow.o: corelow.c $(defs_h) $(gdb_string_h) $(frame_h) $(inferior_h) \ $(symtab_h) $(command_h) $(bfd_h) $(target_h) $(gdbcore_h) \ $(gdbthread_h) $(regcache_h) $(symfile_h) cp-abi.o: cp-abi.c $(defs_h) $(value_h) $(cp_abi_h) $(gdb_string_h) +cp-support.o: cp-support.c $(defs_h) $(cp_support_h) cp-valprint.o: cp-valprint.c $(defs_h) $(gdb_obstack_h) $(symtab_h) \ $(gdbtypes_h) $(expression_h) $(value_h) $(command_h) $(gdbcmd_h) \ $(demangle_h) $(annotate_h) $(gdb_string_h) $(c_lang_h) $(target_h) \ @@ -2174,7 +2176,7 @@ stabsread.o: stabsread.c $(defs_h) $(gdb_string_h) $(bfd_h) $(gdb_obstack_h) \ $(symtab_h) $(gdbtypes_h) $(expression_h) $(symfile_h) $(objfiles_h) \ $(aout_stab_gnu_h) $(libaout_h) $(aout_aout64_h) $(gdb_stabs_h) \ $(buildsym_h) $(complaints_h) $(demangle_h) $(language_h) \ - $(doublest_h) $(stabsread_h) + $(doublest_h) $(stabsread_h) $(cp_abi_h) $(cp_support_h) stack.o: stack.c $(defs_h) $(gdb_string_h) $(value_h) $(symtab_h) \ $(gdbtypes_h) $(expression_h) $(language_h) $(frame_h) $(gdbcmd_h) \ $(gdbcore_h) $(target_h) $(breakpoint_h) $(demangle_h) $(inferior_h) \ diff --git a/gdb/cp-support.c b/gdb/cp-support.c new file mode 100644 index 0000000..46363a8 --- /dev/null +++ b/gdb/cp-support.c @@ -0,0 +1,141 @@ +/* Helper routines for C++ support in GDB. + Copyright 2002 Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "cp-support.h" +#include "gdb_string.h" +#include "demangle.h" + +/* Find the last component of the demangled C++ name NAME. NAME + must be a method name including arguments, in order to correctly + locate the last component. + + This function return a pointer to the first colon before the + last component, or NULL if the name had only one component. */ + +static const char * +find_last_component (const char *name) +{ + const char *p; + int depth; + + /* Functions can have local classes, so we need to find the + beginning of the last argument list, not the end of the first + one. */ + p = name + strlen (name) - 1; + while (p > name && *p != ')') + p--; + + if (p == name) + return NULL; + + /* P now points at the `)' at the end of the argument list. Walk + back to the beginning. */ + p--; + depth = 1; + while (p > name && depth > 0) + { + if (*p == '<' || *p == '(') + depth--; + else if (*p == '>' || *p == ')') + depth++; + p--; + } + + if (p == name) + return NULL; + + while (p > name && *p != ':') + p--; + + if (p == name || p == name + 1 || p[-1] != ':') + return NULL; + + return p - 1; +} + +/* Return the name of the class containing method PHYSNAME. */ + +char * +class_name_from_physname (const char *physname) +{ + char *ret = NULL; + const char *end; + int depth = 0; + char *demangled_name = cplus_demangle (physname, DMGL_ANSI); + + if (demangled_name == NULL) + return NULL; + + end = find_last_component (demangled_name); + if (end != NULL) + { + ret = xmalloc (end - demangled_name + 1); + memcpy (ret, demangled_name, end - demangled_name); + ret[end - demangled_name] = '\0'; + } + + xfree (demangled_name); + return ret; +} + +/* Return the name of the method whose linkage name is PHYSNAME. */ + +char * +method_name_from_physname (const char *physname) +{ + char *ret = NULL; + const char *end; + int depth = 0; + char *demangled_name = cplus_demangle (physname, DMGL_ANSI); + + if (demangled_name == NULL) + return NULL; + + end = find_last_component (demangled_name); + if (end != NULL) + { + char *args; + int len; + + /* Skip "::". */ + end = end + 2; + + /* Find the argument list, if any. */ + args = strchr (end, '('); + if (args == NULL) + len = strlen (end + 2); + else + { + args --; + while (*args == ' ') + args --; + len = args - end + 1; + } + ret = xmalloc (len + 1); + memcpy (ret, end, len); + ret[len] = 0; + } + + xfree (demangled_name); + return ret; +} diff --git a/gdb/cp-support.h b/gdb/cp-support.h new file mode 100644 index 0000000..a7d333f --- /dev/null +++ b/gdb/cp-support.h @@ -0,0 +1,25 @@ +/* Helper routines for C++ support in GDB. + Copyright 2002 Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +extern char *class_name_from_physname (const char *physname); + +extern char *method_name_from_physname (const char *physname); diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c index b7922da..ff4be8c 100644 --- a/gdb/cp-valprint.c +++ b/gdb/cp-valprint.c @@ -97,13 +97,11 @@ cp_print_class_method (char *valaddr, f = TYPE_FN_FIELDLIST1 (domain, i); len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + check_stub_method_group (domain, i); for (j = 0; j < len2; j++) { - QUIT; if (TYPE_FN_FIELD_VOFFSET (f, j) == offset) { - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (domain, i, j); kind = "virtual "; goto common; } @@ -129,15 +127,11 @@ cp_print_class_method (char *valaddr, f = TYPE_FN_FIELDLIST1 (domain, i); len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + check_stub_method_group (f, j); for (j = 0; j < len2; j++) { - QUIT; - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (domain, i, j); if (STREQ (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j))) - { - goto common; - } + goto common; } } } diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 6ebbf2d..a219ab5 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -1672,7 +1672,7 @@ safe_parse_type (char *p, int length) which info used to be in the stab's but was removed to hack back the space required for them. */ -void +static void check_stub_method (struct type *type, int method_id, int signature_id) { struct fn_field *f; @@ -1781,6 +1781,49 @@ check_stub_method (struct type *type, int method_id, int signature_id) xfree (demangled_name); } +/* This is the external interface to check_stub_method, above. This function + unstubs all of the signatures for TYPE's METHOD_ID method name. After + calling this function TYPE_FN_FIELD_STUB will be cleared for each signature + and TYPE_FN_FIELDLIST_NAME will be correct. + + This function unfortunately can not die until stabs do. */ + +void +check_stub_method_group (struct type *type, int method_id) +{ + int len = TYPE_FN_FIELDLIST_LENGTH (type, method_id); + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, method_id); + int j, found_stub; + + for (j = 0; j < len; j++) + if (TYPE_FN_FIELD_STUB (f, j)) + { + found_stub = 1; + check_stub_method (type, method_id, j); + } + + /* GNU v3 methods with incorrect names were corrected when we read in + type information, because it was cheaper to do it then. The only GNU v2 + methods with incorrect method names are operators and destructors; + destructors were also corrected when we read in type information. + + Therefore the only thing we need to handle here are v2 operator + names. */ + if (found_stub && strncmp (TYPE_FN_FIELD_PHYSNAME (f, 0), "_Z", 2) != 0) + { + int ret; + char dem_opname[256]; + + ret = cplus_demangle_opname (TYPE_FN_FIELDLIST_NAME (type, method_id), + dem_opname, DMGL_ANSI); + if (!ret) + ret = cplus_demangle_opname (TYPE_FN_FIELDLIST_NAME (type, method_id), + dem_opname, 0); + if (ret) + TYPE_FN_FIELDLIST_NAME (type, method_id) = xstrdup (dem_opname); + } +} + const struct cplus_struct_type cplus_struct_default; void @@ -3435,7 +3478,6 @@ build_gdbtypes (void) "__bfd_vma", (struct objfile *) NULL); } - extern void _initialize_gdbtypes (void); void _initialize_gdbtypes (void) diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 5c41398..a0b754a 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -1124,7 +1124,7 @@ extern struct type *check_typedef (struct type *); #define CHECK_TYPEDEF(TYPE) (TYPE) = check_typedef (TYPE) -extern void check_stub_method (struct type *, int, int); +extern void check_stub_method_group (struct type *, int); extern struct type *lookup_primitive_typename (char *); diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c index 06a8216..a99d4e9 100644 --- a/gdb/p-valprint.c +++ b/gdb/p-valprint.c @@ -620,13 +620,11 @@ pascal_object_print_class_method (char *valaddr, struct type *type, f = TYPE_FN_FIELDLIST1 (domain, i); len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + check_stub_method_group (domain, i); for (j = 0; j < len2; j++) { - QUIT; if (TYPE_FN_FIELD_VOFFSET (f, j) == offset) { - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (domain, i, j); kind = "virtual "; goto common; } @@ -646,15 +644,11 @@ pascal_object_print_class_method (char *valaddr, struct type *type, f = TYPE_FN_FIELDLIST1 (domain, i); len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + check_stub_method_group (domain, i); for (j = 0; j < len2; j++) { - QUIT; - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (domain, i, j); if (STREQ (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j))) - { - goto common; - } + goto common; } } } diff --git a/gdb/stabsread.c b/gdb/stabsread.c index 6011769..4697b40 100644 --- a/gdb/stabsread.c +++ b/gdb/stabsread.c @@ -44,6 +44,8 @@ #include "demangle.h" #include "language.h" #include "doublest.h" +#include "cp-abi.h" +#include "cp-support.h" #include @@ -3080,6 +3082,27 @@ rs6000_builtin_type (int typenum) /* This page contains subroutines of read_type. */ +/* Replace *OLD_NAME with the method name portion of PHYSNAME. */ + +static void +update_method_name_from_physname (char **old_name, char *physname) +{ + char *method_name; + + method_name = method_name_from_physname (physname); + + if (method_name == NULL) + error ("bad physname %s\n", physname); + + if (strcmp (*old_name, method_name) != 0) + { + xfree (*old_name); + *old_name = method_name; + } + else + xfree (method_name); +} + /* Read member function stabs info for C++ classes. The form of each member function data is: @@ -3377,6 +3400,164 @@ read_member_functions (struct field_info *fip, char **pp, struct type *type, } else { + int has_stub = 0; + int has_destructor = 0, has_other = 0; + int is_v3 = 0; + struct next_fnfield *tmp_sublist; + + /* Various versions of GCC emit various mostly-useless + strings in the name field for special member functions. + + For stub methods, we need to defer correcting the name + until we are ready to unstub the method, because the current + name string is used by gdb_mangle_name. The only stub methods + of concern here are GNU v2 operators; other methods have their + names correct (see caveat below). + + For non-stub methods, in GNU v3, we have a complete physname. + Therefore we can safely correct the name now. This primarily + affects constructors and destructors, whose name will be + __comp_ctor or __comp_dtor instead of Foo or ~Foo. Cast + operators will also have incorrect names; for instance, + "operator int" will be named "operator i" (i.e. the type is + mangled). + + For non-stub methods in GNU v2, we have no easy way to + know if we have a complete physname or not. For most + methods the result depends on the platform (if CPLUS_MARKER + can be `$' or `.', it will use minimal debug information, or + otherwise the full physname will be included). + + Rather than dealing with this, we take a different approach. + For v3 mangled names, we can use the full physname; for v2, + we use cplus_demangle_opname (which is actually v2 specific), + because the only interesting names are all operators - once again + barring the caveat below. Skip this process if any method in the + group is a stub, to prevent our fouling up the workings of + gdb_mangle_name. + + The caveat: GCC 2.95.x (and earlier?) put constructors and + destructors in the same method group. We need to split this + into two groups, because they should have different names. + So for each method group we check whether it contains both + routines whose physname appears to be a destructor (the physnames + for and destructors are always provided, due to quirks in v2 + mangling) and routines whose physname does not appear to be a + destructor. If so then we break up the list into two halves. + Even if the constructors and destructors aren't in the same group + the destructor will still lack the leading tilde, so that also + needs to be fixed. + + So, to summarize what we expect and handle here: + + Given Given Real Real Action + method name physname physname method name + + __opi [none] __opi__3Foo operator int opname + [now or later] + Foo _._3Foo _._3Foo ~Foo separate and + rename + operator i _ZN3FoocviEv _ZN3FoocviEv operator int demangle + __comp_ctor _ZN3FooC1ERKS_ _ZN3FooC1ERKS_ Foo demangle + */ + + tmp_sublist = sublist; + while (tmp_sublist != NULL) + { + if (tmp_sublist->fn_field.is_stub) + has_stub = 1; + if (tmp_sublist->fn_field.physname[0] == '_' + && tmp_sublist->fn_field.physname[1] == 'Z') + is_v3 = 1; + + if (is_destructor_name (tmp_sublist->fn_field.physname)) + has_destructor++; + else + has_other++; + + tmp_sublist = tmp_sublist->next; + } + + if (has_destructor && has_other) + { + struct next_fnfieldlist *destr_fnlist; + struct next_fnfield *last_sublist; + + /* Create a new fn_fieldlist for the destructors. */ + + destr_fnlist = (struct next_fnfieldlist *) + xmalloc (sizeof (struct next_fnfieldlist)); + make_cleanup (xfree, destr_fnlist); + memset (destr_fnlist, 0, sizeof (struct next_fnfieldlist)); + destr_fnlist->fn_fieldlist.name + = obconcat (&objfile->type_obstack, "", "~", + new_fnlist->fn_fieldlist.name); + + destr_fnlist->fn_fieldlist.fn_fields = (struct fn_field *) + obstack_alloc (&objfile->type_obstack, + sizeof (struct fn_field) * has_destructor); + memset (destr_fnlist->fn_fieldlist.fn_fields, 0, + sizeof (struct fn_field) * has_destructor); + tmp_sublist = sublist; + last_sublist = NULL; + i = 0; + while (tmp_sublist != NULL) + { + if (!is_destructor_name (tmp_sublist->fn_field.physname)) + { + tmp_sublist = tmp_sublist->next; + continue; + } + + destr_fnlist->fn_fieldlist.fn_fields[i++] + = tmp_sublist->fn_field; + if (last_sublist) + last_sublist->next = tmp_sublist->next; + else + sublist = tmp_sublist->next; + last_sublist = tmp_sublist; + tmp_sublist = tmp_sublist->next; + } + + destr_fnlist->fn_fieldlist.length = has_destructor; + destr_fnlist->next = fip->fnlist; + fip->fnlist = destr_fnlist; + nfn_fields++; + total_length += has_destructor; + length -= has_destructor; + } + else if (is_v3) + { + /* v3 mangling prevents the use of abbreviated physnames, + so we can do this here. There are stubbed methods in v3 + only: + - in -gstabs instead of -gstabs+ + - or for static methods, which are output as a function type + instead of a method type. */ + + update_method_name_from_physname (&new_fnlist->fn_fieldlist.name, + sublist->fn_field.physname); + } + else if (has_destructor && new_fnlist->fn_fieldlist.name[0] != '~') + { + new_fnlist->fn_fieldlist.name = concat ("~", main_fn_name, NULL); + xfree (main_fn_name); + } + else if (!has_stub) + { + char dem_opname[256]; + int ret; + ret = cplus_demangle_opname (new_fnlist->fn_fieldlist.name, + dem_opname, DMGL_ANSI); + if (!ret) + ret = cplus_demangle_opname (new_fnlist->fn_fieldlist.name, + dem_opname, 0); + if (ret) + new_fnlist->fn_fieldlist.name + = obsavestring (dem_opname, strlen (dem_opname), + &objfile->type_obstack); + } + new_fnlist->fn_fieldlist.fn_fields = (struct fn_field *) obstack_alloc (&objfile->type_obstack, sizeof (struct fn_field) * length); diff --git a/gdb/valops.c b/gdb/valops.c index 4efcd42..2e61e88 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -2302,12 +2302,11 @@ search_struct_method (char *name, struct value **arg1p, struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); name_matched = 1; + check_stub_method_group (type, i); if (j > 0 && args == 0) error ("cannot resolve overloaded method `%s': no arguments supplied", name); else if (j == 0 && args == 0) { - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (type, i, j); v = value_fn_field (arg1p, f, j, type, offset); if (v != NULL) return v; @@ -2315,8 +2314,6 @@ search_struct_method (char *name, struct value **arg1p, else while (j >= 0) { - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (type, i, j); if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j), TYPE_VARARGS (TYPE_FN_FIELD_TYPE (f, j)), TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (f, j)), @@ -2555,20 +2552,15 @@ find_method_list (struct value **argp, char *method, int offset, char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) { - /* Resolve any stub methods. */ int len = TYPE_FN_FIELDLIST_LENGTH (type, i); struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); - int j; *num_fns = len; *basetype = type; *boffset = offset; - for (j = 0; j < len; j++) - { - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (type, i, j); - } + /* Resolve any stub methods. */ + check_stub_method_group (type, i); return f; } @@ -3094,6 +3086,8 @@ value_struct_elt_for_reference (struct type *domain, int offset, int j = TYPE_FN_FIELDLIST_LENGTH (t, i); struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i); + check_stub_method_group (t, i); + if (intype == 0 && j > 1) error ("non-unique member `%s' requires type instantiation", name); if (intype) @@ -3107,8 +3101,6 @@ value_struct_elt_for_reference (struct type *domain, int offset, else j = 0; - if (TYPE_FN_FIELD_STUB (f, j)) - check_stub_method (t, i, j); if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) { return value_from_longest