diff options
author | Doug Evans <dje@google.com> | 2012-04-25 14:07:23 +0000 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2012-04-25 14:07:23 +0000 |
commit | a766d390bb857383a5f9ae80a102e1f8705f4c2e (patch) | |
tree | 056e6d8d57aee5d234d9ed4619a27306dfec3dd7 /gdb | |
parent | e65f9ffb8e49431537404712a5b1eed310964cb0 (diff) | |
download | gdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.zip gdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.tar.gz gdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.tar.bz2 |
Initial pass at Go language support.
* NEWS: Mention Go.
* Makefile.in (SFILES): Add go-exp.y, go-lang.c, go-typeprint.c,
go-valprint.c.
(COMMON_OBS): Add go-lang.o, go-val.print.o, go-typeprint.o.
(YYFILES): Add go-exp.c.
(YYOBJ): Add go-exp.o.
(local-maintainer-clean): Delete go-exp.c.
* defs.h (enum language): Add language_go.
* dwarf2read.c: #include "go-lang.h".
(fixup_go_packaging): New function.
(process_full_comp_unit): Call it when processing Go CUs.
(dwarf2_physname): Add Go support.
(read_file_scope): Handle missing language spec for GNU Go.
(set_cu_language): Handle DW_LANG_Go.
* go-exp.y: New file.
* go-lang.h: New file.
* go-lang.c: New file.
* go-typeprint.c: New file.
* go-valprint.c: New file.
* symtab.c: #include "go-lang.h".
(symbol_set_language): Handle language_go.
(symbol_find_demangled_name, symbol_set_names): Ditto.
(symbol_natural_name, demangle_for_lookup, find_main_name): Ditto.
testsuite/
* configure.ac: Create gdb.go/Makefile.
* configure: Regenerate.
* gdb.base/default.exp: Add "go" to "set language" testing.
* gdb.go/Makefile.in: New file.
* gdb.go/basic-types.exp: New file.
* gdb.go/chan.exp: New file.
* gdb.go/chan.go: New file.
* gdb.go/handcall.exp: New file.
* gdb.go/handcall.go: New file.
* gdb.go/hello.exp: New file.
* gdb.go/hello.go: New file.
* gdb.go/integers.exp: New file.
* gdb.go/integers.go: New file.
* gdb.go/methods.exp: New file.
* gdb.go/methods.go: New file.
* gdb.go/package.exp: New file.
* gdb.go/package1.go: New file.
* gdb.go/package2.go: New file.
* gdb.go/print.exp: New file.
* gdb.go/strings.exp: New file.
* gdb.go/strings.go: New file.
* gdb.go/types.exp: New file.
* gdb.go/types.go: New file.
* gdb.go/unsafe.exp: New file.
* gdb.go/unsafe.go: New file.
* lib/future.exp: Add Go support.
(gdb_find_go, gdb_find_go_linker): New procs.
(gdb_default_target_compile): Add Go support.
* lib/gdb.exp (skip_go_tests): New proc.
* lib/go.exp: New file.
doc/
* gdb.texinfo (Supported Languages): Add Go.
(Go): New node.
Diffstat (limited to 'gdb')
42 files changed, 3840 insertions, 21 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 43d590f..df4bacf 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,30 @@ +2012-04-25 Doug Evans <dje@google.com> + + Initial pass at Go language support. + * NEWS: Mention Go. + * Makefile.in (SFILES): Add go-exp.y, go-lang.c, go-typeprint.c, + go-valprint.c. + (COMMON_OBS): Add go-lang.o, go-val.print.o, go-typeprint.o. + (YYFILES): Add go-exp.c. + (YYOBJ): Add go-exp.o. + (local-maintainer-clean): Delete go-exp.c. + * defs.h (enum language): Add language_go. + * dwarf2read.c: #include "go-lang.h". + (fixup_go_packaging): New function. + (process_full_comp_unit): Call it when processing Go CUs. + (dwarf2_physname): Add Go support. + (read_file_scope): Handle missing language spec for GNU Go. + (set_cu_language): Handle DW_LANG_Go. + * go-exp.y: New file. + * go-lang.h: New file. + * go-lang.c: New file. + * go-typeprint.c: New file. + * go-valprint.c: New file. + * symtab.c: #include "go-lang.h". + (symbol_set_language): Handle language_go. + (symbol_find_demangled_name, symbol_set_names): Ditto. + (symbol_natural_name, demangle_for_lookup, find_main_name): Ditto. + 2012-04-24 Jim Meyering <meyering@redhat.com> avoid a few strncpy-induced buffer overruns diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 0e87eeb..fbe5b53 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -704,6 +704,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \ findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \ gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \ + go-exp.y go-lang.c go-typeprint.c go-valprint.c \ inf-loop.c \ infcall.c \ infcmd.c inflow.c infrun.c \ @@ -769,7 +770,7 @@ cli-out.h gdb_expat.h breakpoint.h infcall.h obsd-tdep.h \ exec.h m32r-tdep.h osabi.h gdbcore.h solib-som.h amd64bsd-nat.h \ i386bsd-nat.h xml-support.h xml-tdesc.h alphabsd-tdep.h gdb_obstack.h \ ia64-tdep.h ada-lang.h ada-varobj.h varobj.h frv-tdep.h nto-tdep.h serial.h \ -c-lang.h d-lang.h frame.h event-loop.h block.h cli/cli-setshow.h \ +c-lang.h d-lang.h golang.h frame.h event-loop.h block.h cli/cli-setshow.h \ cli/cli-decode.h cli/cli-cmds.h cli/cli-dump.h cli/cli-utils.h \ cli/cli-script.h macrotab.h symtab.h version.h \ gnulib/import/string.in.h gnulib/import/str-two-way.h \ @@ -889,6 +890,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ ada-tasks.o ada-varobj.o \ ui-out.o cli-out.o \ varobj.o vec.o \ + go-lang.o go-valprint.o go-typeprint.o \ jv-lang.o jv-valprint.o jv-typeprint.o \ m2-lang.o opencl-lang.o p-lang.o p-typeprint.o p-valprint.o \ sentinel-frame.o \ @@ -933,13 +935,13 @@ YYFILES = c-exp.c \ ada-lex.c \ ada-exp.c \ jv-exp.c \ - f-exp.c m2-exp.c p-exp.c + f-exp.c go-exp.c m2-exp.c p-exp.c YYOBJ = c-exp.o \ cp-name-parser.o \ objc-exp.o \ ada-exp.o \ jv-exp.o \ - f-exp.o m2-exp.o p-exp.o + f-exp.o go-exp.o m2-exp.o p-exp.o # Things which need to be built when making a distribution. @@ -1241,7 +1243,7 @@ local-maintainer-clean: ada-lex.c ada-exp.c \ objc-exp.c \ jv-exp.tab \ - f-exp.c m2-exp.c p-exp.c + f-exp.c go-exp.c m2-exp.c p-exp.c rm -f TAGS $(INFOFILES) rm -f $(YYFILES) rm -f nm.h config.status @@ -32,6 +32,10 @@ ** A new method 'referenced_value' on gdb.Value objects which can dereference pointer as well as C++ reference values. +* Go language support. + GDB now supports debugging programs written in the Go programming + language. + * GDBserver now supports stdio connections. E.g. (gdb) target remote | ssh myhost gdbserver - hello @@ -197,6 +197,7 @@ enum language language_c, /* C */ language_cplus, /* C++ */ language_d, /* D */ + language_go, /* Go */ language_objc, /* Objective-C */ language_java, /* Java */ language_fortran, /* Fortran */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 92085c9..0f41571 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2012-04-25 Doug Evans <dje@google.com> + + * gdb.texinfo (Supported Languages): Add Go. + (Go): New node. + 2012-04-25 Yao Qi <yao@codesourcery.com> * gdbint.texinfo (Testsuite): New section `Board settings'. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a2a4eb3..d4d2f60 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -12487,8 +12487,8 @@ being set automatically by @value{GDBN}. @node Supported Languages @section Supported Languages -@value{GDBN} supports C, C@t{++}, D, Objective-C, Fortran, Java, OpenCL C, Pascal, -assembly, Modula-2, and Ada. +@value{GDBN} supports C, C@t{++}, D, Go, Objective-C, Fortran, Java, +OpenCL C, Pascal, assembly, Modula-2, and Ada. @c This is false ... Some @value{GDBN} features may be used in expressions regardless of the language you use: the @value{GDBN} @code{@@} and @code{::} operators, @@ -12507,6 +12507,7 @@ language reference or tutorial. @menu * C:: C and C@t{++} * D:: D +* Go:: Go * Objective-C:: Objective-C * OpenCL C:: OpenCL C * Fortran:: Fortran @@ -13059,6 +13060,55 @@ See @ref{PowerPC,,PowerPC} for more details. GDC, LDC or DMD compilers. Currently @value{GDBN} supports only one D specific feature --- dynamic arrays. +@node Go +@subsection Go + +@cindex Go (programming language) +@value{GDBN} can be used to debug programs written in Go and compiled with +@file{gccgo} or @file{6g} compilers. + +Here is a summary of the Go-specific features and restrictions: + +@table @code +@cindex current Go package +@item The current Go package +The name of the current package does not need to be specified when +specifying global variables and functions. + +For example, given the program: + +@example +package main +var myglob = "Shall we?" +func main () @{ + // ... +@} +@end example + +When stopped inside @code{main} either of these work: + +@example +(gdb) p myglob +(gdb) p main.myglob +@end example + +@cindex builtin Go types +@item Builtin Go types +The @code{string} type is recognized by @value{GDBN} and is printed +as a string. + +@cindex builtin Go functions +@item Builtin Go functions +The @value{GDBN} expression parser recognizes the @code{unsafe.Sizeof} +function and handles it internally. +@end table + +@cindex restrictions on Go expressions +@item Restrictions on Go expressions +All Go operators are supported except @code{&^}. +The Go @code{_} ``blank identifier'' is not supported. +Automatic dereferencing of pointers is not supported. + @node Objective-C @subsection Objective-C diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index c737d41..bd2d6f2 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -59,6 +59,7 @@ #include "completer.h" #include "vec.h" #include "c-lang.h" +#include "go-lang.h" #include "valprint.h" #include <ctype.h> @@ -4822,6 +4823,78 @@ compute_delayed_physnames (struct dwarf2_cu *cu) } } +/* Go objects should be embedded in a DW_TAG_module DIE, + and it's not clear if/how imported objects will appear. + To keep Go support simple until that's worked out, + go back through what we've read and create something usable. + We could do this while processing each DIE, and feels kinda cleaner, + but that way is more invasive. + This is to, for example, allow the user to type "p var" or "b main" + without having to specify the package name, and allow lookups + of module.object to work in contexts that use the expression + parser. */ + +static void +fixup_go_packaging (struct dwarf2_cu *cu) +{ + char *package_name = NULL; + struct pending *list; + int i; + + for (list = global_symbols; list != NULL; list = list->next) + { + for (i = 0; i < list->nsyms; ++i) + { + struct symbol *sym = list->symbol[i]; + + if (SYMBOL_LANGUAGE (sym) == language_go + && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + char *this_package_name = go_symbol_package_name (sym); + + if (this_package_name == NULL) + continue; + if (package_name == NULL) + package_name = this_package_name; + else + { + if (strcmp (package_name, this_package_name) != 0) + complaint (&symfile_complaints, + _("Symtab %s has objects from two different Go packages: %s and %s"), + (sym->symtab && sym->symtab->filename + ? sym->symtab->filename + : cu->objfile->name), + this_package_name, package_name); + xfree (this_package_name); + } + } + } + } + + if (package_name != NULL) + { + struct objfile *objfile = cu->objfile; + struct type *type = init_type (TYPE_CODE_MODULE, 0, 0, + package_name, objfile); + struct symbol *sym; + + TYPE_TAG_NAME (type) = TYPE_NAME (type); + + sym = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct symbol); + SYMBOL_SET_LANGUAGE (sym, language_go); + SYMBOL_SET_NAMES (sym, package_name, strlen (package_name), 1, objfile); + /* This is not VAR_DOMAIN because we want a way to ensure a lookup of, + e.g., "main" finds the "main" module and not C's main(). */ + SYMBOL_DOMAIN (sym) = STRUCT_DOMAIN; + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_TYPE (sym) = type; + + add_symbol_to_list (sym, &global_symbols); + + xfree (package_name); + } +} + /* Generate full symbol information for PER_CU, whose DIEs have already been loaded into memory. */ @@ -4846,6 +4919,10 @@ process_full_comp_unit (struct dwarf2_per_cu_data *per_cu) /* Do line number decoding in read_file_scope () */ process_die (cu->dies, cu); + /* For now fudge the Go package. */ + if (cu->language == language_go) + fixup_go_packaging (cu); + /* Now that we have processed all the DIEs in the CU, all the types should be complete, and it should now be safe to compute all of the physnames. */ @@ -5055,8 +5132,14 @@ do_ui_file_peek_last (void *object, const char *buffer, long length) } /* Compute the fully qualified name of DIE in CU. If PHYSNAME is nonzero, - compute the physname for the object, which include a method's - formal parameters (C++/Java) and return type (Java). + compute the physname for the object, which include a method's: + - formal parameters (C++/Java), + - receiver type (Go), + - return type (Java). + + The term "physname" is a bit confusing. + For C++, for example, it is the demangled name. + For Go, for example, it's the mangled name. For Ada, return the DIE's linkage name rather than the fully qualified name. PHYSNAME is ignored.. @@ -5353,10 +5436,21 @@ dwarf2_physname (char *name, struct die_info *die, struct dwarf2_cu *cu) variant `long name(params)' does not have the proper inferior type. */ - demangled = cplus_demangle (mangled, (DMGL_PARAMS | DMGL_ANSI - | (cu->language == language_java - ? DMGL_JAVA | DMGL_RET_POSTFIX - : DMGL_RET_DROP))); + if (cu->language == language_go) + { + /* This is a lie, but we already lie to the caller new_symbol_full. + new_symbol_full assumes we return the mangled name. + This just undoes that lie until things are cleaned up. */ + demangled = NULL; + } + else + { + demangled = cplus_demangle (mangled, + (DMGL_PARAMS | DMGL_ANSI + | (cu->language == language_java + ? DMGL_JAVA | DMGL_RET_POSTFIX + : DMGL_RET_DROP))); + } if (demangled) { make_cleanup (xfree, demangled); @@ -5684,6 +5778,10 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) if (cu->producer && strstr (cu->producer, "IBM XL C for OpenCL") != NULL) cu->language = language_opencl; + /* Similar hack for Go. */ + if (cu->producer && strstr (cu->producer, "GNU Go ") != NULL) + set_cu_language (DW_LANG_Go, cu); + /* We assume that we're processing GCC output. */ processing_gcc_compilation = 2; @@ -10759,6 +10857,9 @@ set_cu_language (unsigned int lang, struct dwarf2_cu *cu) case DW_LANG_Fortran95: cu->language = language_fortran; break; + case DW_LANG_Go: + cu->language = language_go; + break; case DW_LANG_Mips_Assembler: cu->language = language_asm; break; diff --git a/gdb/go-exp.y b/gdb/go-exp.y new file mode 100644 index 0000000..f720629 --- /dev/null +++ b/gdb/go-exp.y @@ -0,0 +1,1618 @@ +/* YACC parser for Go expressions, for GDB. + + Copyright (C) 2012 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/>. */ + +/* This file is derived from c-exp.y, p-exp.y. */ + +/* Parse a Go expression from text in a string, + and return the result as a struct expression pointer. + That structure contains arithmetic operations in reverse polish, + with constants represented by operations that are followed by special data. + See expression.h for the details of the format. + What is important here is that it can be built up sequentially + during the process of parsing; the lower levels of the tree always + come first in the result. + + Note that malloc's and realloc's in this file are transformed to + xmalloc and xrealloc respectively by the same sed command in the + makefile that remaps any other malloc/realloc inserted by the parser + generator. Doing this with #defines and trying to control the interaction + with include files (<malloc.h> and <stdlib.h> for example) just became + too messy, particularly when such includes can be inserted at random + times by the parser generator. */ + +/* Known bugs or limitations: + + - Unicode + - &^ + - '_' (blank identifier) + - automatic deref of pointers + - method expressions + - interfaces, channels, etc. + + And lots of other things. + I'm sure there's some cleanup to do. +*/ + +%{ + +#include "defs.h" +#include "gdb_string.h" +#include <ctype.h> +#include "expression.h" +#include "value.h" +#include "parser-defs.h" +#include "language.h" +#include "c-lang.h" +#include "go-lang.h" +#include "bfd.h" /* Required by objfiles.h. */ +#include "symfile.h" /* Required by objfiles.h. */ +#include "objfiles.h" /* For have_full_symbols and have_partial_symbols */ +#include "charset.h" +#include "block.h" + +#define parse_type builtin_type (parse_gdbarch) + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in gdb. Note that these are only the variables + produced by yacc. If other parser generators (bison, byacc, etc) produce + additional global names that conflict at link time, then those parser + generators need to be fixed instead of adding those names to this list. */ + +#define yymaxdepth go_maxdepth +#define yyparse go_parse_internal +#define yylex go_lex +#define yyerror go_error +#define yylval go_lval +#define yychar go_char +#define yydebug go_debug +#define yypact go_pact +#define yyr1 go_r1 +#define yyr2 go_r2 +#define yydef go_def +#define yychk go_chk +#define yypgo go_pgo +#define yyact go_act +#define yyexca go_exca +#define yyerrflag go_errflag +#define yynerrs go_nerrs +#define yyps go_ps +#define yypv go_pv +#define yys go_s +#define yy_yys go_yys +#define yystate go_state +#define yytmp go_tmp +#define yyv go_v +#define yy_yyv go_yyv +#define yyval go_val +#define yylloc go_lloc +#define yyreds go_reds /* With YYDEBUG defined */ +#define yytoks go_toks /* With YYDEBUG defined */ +#define yyname go_name /* With YYDEBUG defined */ +#define yyrule go_rule /* With YYDEBUG defined */ +#define yylhs go_yylhs +#define yylen go_yylen +#define yydefred go_yydefred +#define yydgoto go_yydgoto +#define yysindex go_yysindex +#define yyrindex go_yyrindex +#define yygindex go_yygindex +#define yytable go_yytable +#define yycheck go_yycheck + +#ifndef YYDEBUG +#define YYDEBUG 1 /* Default to yydebug support */ +#endif + +#define YYFPRINTF parser_fprintf + +int yyparse (void); + +static int yylex (void); + +void yyerror (char *); + +%} + +/* Although the yacc "value" of an expression is not used, + since the result is stored in the structure being created, + other node types do have values. */ + +%union + { + LONGEST lval; + struct { + LONGEST val; + struct type *type; + } typed_val_int; + struct { + DOUBLEST dval; + struct type *type; + } typed_val_float; + struct stoken sval; + struct symtoken ssym; + struct type *tval; + struct typed_stoken tsval; + struct ttype tsym; + int voidval; + enum exp_opcode opcode; + struct internalvar *ivar; + struct stoken_vector svec; + } + +%{ +/* YYSTYPE gets defined by %union. */ +static int parse_number (char *, int, int, YYSTYPE *); +static int parse_go_float (struct gdbarch *gdbarch, const char *p, int len, + DOUBLEST *d, struct type **t); +%} + +%type <voidval> exp exp1 type_exp start variable lcurly +%type <lval> rcurly +%type <tval> type + +%token <typed_val_int> INT +%token <typed_val_float> FLOAT + +/* Both NAME and TYPENAME tokens represent symbols in the input, + and both convey their data as strings. + But a TYPENAME is a string that happens to be defined as a type + or builtin type name (such as int or char) + and a NAME is any other symbol. + Contexts where this distinction is not important can use the + nonterminal "name", which matches either NAME or TYPENAME. */ + +%token <tsval> RAW_STRING +%token <tsval> STRING +%token <tsval> CHAR +%token <ssym> NAME +%token <tsym> TYPENAME /* Not TYPE_NAME cus already taken. */ +%token <voidval> COMPLETE +/*%type <sval> name*/ +%type <svec> string_exp +%type <ssym> name_not_typename + +/* A NAME_OR_INT is a symbol which is not known in the symbol table, + but which would parse as a valid number in the current input radix. + E.g. "c" when input_radix==16. Depending on the parse, it will be + turned into a name or into a number. */ +%token <ssym> NAME_OR_INT + +%token <lval> TRUE_KEYWORD FALSE_KEYWORD +%token STRUCT_KEYWORD INTERFACE_KEYWORD TYPE_KEYWORD CHAN_KEYWORD +%token SIZEOF_KEYWORD +%token LEN_KEYWORD CAP_KEYWORD +%token NEW_KEYWORD +%token IOTA_KEYWORD NIL_KEYWORD +%token CONST_KEYWORD +%token DOTDOTDOT +%token ENTRY +%token ERROR + +/* Special type cases. */ +%token BYTE_KEYWORD /* An alias of uint8. */ + +%token <sval> DOLLAR_VARIABLE + +%token <opcode> ASSIGN_MODIFY + +%left ',' +%left ABOVE_COMMA +%right '=' ASSIGN_MODIFY +%right '?' +%left OROR +%left ANDAND +%left '|' +%left '^' +%left '&' +%left ANDNOT +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '@' +%left '+' '-' +%left '*' '/' '%' +%right UNARY INCREMENT DECREMENT +%right LEFT_ARROW '.' '[' '(' + + +%% + +start : exp1 + | type_exp + ; + +type_exp: type + { write_exp_elt_opcode(OP_TYPE); + write_exp_elt_type($1); + write_exp_elt_opcode(OP_TYPE); } + ; + +/* Expressions, including the comma operator. */ +exp1 : exp + | exp1 ',' exp + { write_exp_elt_opcode (BINOP_COMMA); } + ; + +/* Expressions, not including the comma operator. */ +exp : '*' exp %prec UNARY + { write_exp_elt_opcode (UNOP_IND); } + ; + +exp : '&' exp %prec UNARY + { write_exp_elt_opcode (UNOP_ADDR); } + ; + +exp : '-' exp %prec UNARY + { write_exp_elt_opcode (UNOP_NEG); } + ; + +exp : '+' exp %prec UNARY + { write_exp_elt_opcode (UNOP_PLUS); } + ; + +exp : '!' exp %prec UNARY + { write_exp_elt_opcode (UNOP_LOGICAL_NOT); } + ; + +exp : '^' exp %prec UNARY + { write_exp_elt_opcode (UNOP_COMPLEMENT); } + ; + +exp : exp INCREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTINCREMENT); } + ; + +exp : exp DECREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTDECREMENT); } + ; + +/* foo->bar is not in Go. May want as a gdb extension. Later. */ + +exp : exp '.' name_not_typename + { write_exp_elt_opcode (STRUCTOP_STRUCT); + write_exp_string ($3.stoken); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : exp '.' name_not_typename COMPLETE + { mark_struct_expression (); + write_exp_elt_opcode (STRUCTOP_STRUCT); + write_exp_string ($3.stoken); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : exp '.' COMPLETE + { struct stoken s; + mark_struct_expression (); + write_exp_elt_opcode (STRUCTOP_STRUCT); + s.ptr = ""; + s.length = 0; + write_exp_string (s); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : exp '[' exp1 ']' + { write_exp_elt_opcode (BINOP_SUBSCRIPT); } + ; + +exp : exp '(' + /* This is to save the value of arglist_len + being accumulated by an outer function call. */ + { start_arglist (); } + arglist ')' %prec LEFT_ARROW + { write_exp_elt_opcode (OP_FUNCALL); + write_exp_elt_longcst ((LONGEST) end_arglist ()); + write_exp_elt_opcode (OP_FUNCALL); } + ; + +lcurly : '{' + { start_arglist (); } + ; + +arglist : + ; + +arglist : exp + { arglist_len = 1; } + ; + +arglist : arglist ',' exp %prec ABOVE_COMMA + { arglist_len++; } + ; + +rcurly : '}' + { $$ = end_arglist () - 1; } + ; + +exp : lcurly type rcurly exp %prec UNARY + { write_exp_elt_opcode (UNOP_MEMVAL); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_MEMVAL); } + ; + +exp : type '(' exp ')' %prec UNARY + { write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type ($1); + write_exp_elt_opcode (UNOP_CAST); } + ; + +exp : '(' exp1 ')' + { } + ; + +/* Binary operators in order of decreasing precedence. */ + +exp : exp '@' exp + { write_exp_elt_opcode (BINOP_REPEAT); } + ; + +exp : exp '*' exp + { write_exp_elt_opcode (BINOP_MUL); } + ; + +exp : exp '/' exp + { write_exp_elt_opcode (BINOP_DIV); } + ; + +exp : exp '%' exp + { write_exp_elt_opcode (BINOP_REM); } + ; + +exp : exp '+' exp + { write_exp_elt_opcode (BINOP_ADD); } + ; + +exp : exp '-' exp + { write_exp_elt_opcode (BINOP_SUB); } + ; + +exp : exp LSH exp + { write_exp_elt_opcode (BINOP_LSH); } + ; + +exp : exp RSH exp + { write_exp_elt_opcode (BINOP_RSH); } + ; + +exp : exp EQUAL exp + { write_exp_elt_opcode (BINOP_EQUAL); } + ; + +exp : exp NOTEQUAL exp + { write_exp_elt_opcode (BINOP_NOTEQUAL); } + ; + +exp : exp LEQ exp + { write_exp_elt_opcode (BINOP_LEQ); } + ; + +exp : exp GEQ exp + { write_exp_elt_opcode (BINOP_GEQ); } + ; + +exp : exp '<' exp + { write_exp_elt_opcode (BINOP_LESS); } + ; + +exp : exp '>' exp + { write_exp_elt_opcode (BINOP_GTR); } + ; + +exp : exp '&' exp + { write_exp_elt_opcode (BINOP_BITWISE_AND); } + ; + +exp : exp '^' exp + { write_exp_elt_opcode (BINOP_BITWISE_XOR); } + ; + +exp : exp '|' exp + { write_exp_elt_opcode (BINOP_BITWISE_IOR); } + ; + +exp : exp ANDAND exp + { write_exp_elt_opcode (BINOP_LOGICAL_AND); } + ; + +exp : exp OROR exp + { write_exp_elt_opcode (BINOP_LOGICAL_OR); } + ; + +exp : exp '?' exp ':' exp %prec '?' + { write_exp_elt_opcode (TERNOP_COND); } + ; + +exp : exp '=' exp + { write_exp_elt_opcode (BINOP_ASSIGN); } + ; + +exp : exp ASSIGN_MODIFY exp + { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); + write_exp_elt_opcode ($2); + write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); } + ; + +exp : INT + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type ($1.type); + write_exp_elt_longcst ((LONGEST)($1.val)); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : CHAR + { + struct stoken_vector vec; + vec.len = 1; + vec.tokens = &$1; + write_exp_string_vector ($1.type, &vec); + } + ; + +exp : NAME_OR_INT + { YYSTYPE val; + parse_number ($1.stoken.ptr, $1.stoken.length, + 0, &val); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (val.typed_val_int.type); + write_exp_elt_longcst ((LONGEST) + val.typed_val_int.val); + write_exp_elt_opcode (OP_LONG); + } + ; + + +exp : FLOAT + { write_exp_elt_opcode (OP_DOUBLE); + write_exp_elt_type ($1.type); + write_exp_elt_dblcst ($1.dval); + write_exp_elt_opcode (OP_DOUBLE); } + ; + +exp : variable + ; + +exp : DOLLAR_VARIABLE + { + write_dollar_variable ($1); + } + ; + +exp : SIZEOF_KEYWORD '(' type ')' %prec UNARY + { + /* TODO(dje): Go objects in structs. */ + write_exp_elt_opcode (OP_LONG); + /* TODO(dje): What's the right type here? */ + write_exp_elt_type (parse_type->builtin_unsigned_int); + CHECK_TYPEDEF ($3); + write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3)); + write_exp_elt_opcode (OP_LONG); + } + ; + +exp : SIZEOF_KEYWORD '(' exp ')' %prec UNARY + { + /* TODO(dje): Go objects in structs. */ + write_exp_elt_opcode (UNOP_SIZEOF); + } + +string_exp: + STRING + { + /* We copy the string here, and not in the + lexer, to guarantee that we do not leak a + string. */ + /* Note that we NUL-terminate here, but just + for convenience. */ + struct typed_stoken *vec = XNEW (struct typed_stoken); + $$.len = 1; + $$.tokens = vec; + + vec->type = $1.type; + vec->length = $1.length; + vec->ptr = malloc ($1.length + 1); + memcpy (vec->ptr, $1.ptr, $1.length + 1); + } + + | string_exp '+' STRING + { + /* Note that we NUL-terminate here, but just + for convenience. */ + char *p; + ++$$.len; + $$.tokens = realloc ($$.tokens, + $$.len * sizeof (struct typed_stoken)); + + p = malloc ($3.length + 1); + memcpy (p, $3.ptr, $3.length + 1); + + $$.tokens[$$.len - 1].type = $3.type; + $$.tokens[$$.len - 1].length = $3.length; + $$.tokens[$$.len - 1].ptr = p; + } + ; + +exp : string_exp %prec ABOVE_COMMA + { + int i; + + write_exp_string_vector (0 /*always utf8*/, &$1); + for (i = 0; i < $1.len; ++i) + free ($1.tokens[i].ptr); + free ($1.tokens); + } + ; + +exp : TRUE_KEYWORD + { write_exp_elt_opcode (OP_BOOL); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_BOOL); } + ; + +exp : FALSE_KEYWORD + { write_exp_elt_opcode (OP_BOOL); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_BOOL); } + ; + +variable: name_not_typename ENTRY + { struct symbol *sym = $1.sym; + + if (sym == NULL + || !SYMBOL_IS_ARGUMENT (sym) + || !symbol_read_needs_frame (sym)) + error (_("@entry can be used only for function " + "parameters, not for \"%s\""), + copy_name ($1.stoken)); + + write_exp_elt_opcode (OP_VAR_ENTRY_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_ENTRY_VALUE); + } + ; + +variable: name_not_typename + { struct symbol *sym = $1.sym; + + if (sym) + { + if (symbol_read_needs_frame (sym)) + { + if (innermost_block == 0 + || contained_in (block_found, + innermost_block)) + innermost_block = block_found; + } + + write_exp_elt_opcode (OP_VAR_VALUE); + /* We want to use the selected frame, not + another more inner frame which happens to + be in the same block. */ + write_exp_elt_block (NULL); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + } + else if ($1.is_a_field_of_this) + { + /* TODO(dje): Can we get here? + E.g., via a mix of c++ and go? */ + gdb_assert_not_reached ("go with `this' field"); + } + else + { + struct minimal_symbol *msymbol; + char *arg = copy_name ($1.stoken); + + msymbol = + lookup_minimal_symbol (arg, NULL, NULL); + if (msymbol != NULL) + write_exp_msymbol (msymbol); + else if (!have_full_symbols () + && !have_partial_symbols ()) + error (_("No symbol table is loaded. " + "Use the \"file\" command.")); + else + error (_("No symbol \"%s\" in current context."), + copy_name ($1.stoken)); + } + } + ; + +/* TODO +method_exp: PACKAGENAME '.' name '.' name + { + } + ; +*/ + +type /* Implements (approximately): [*] type-specifier */ + : '*' type + { $$ = lookup_pointer_type ($2); } + | TYPENAME + { $$ = $1.type; } +/* + | STRUCT_KEYWORD name + { $$ = lookup_struct (copy_name ($2), + expression_context_block); } +*/ + | BYTE_KEYWORD + { $$ = builtin_go_type (parse_gdbarch) + ->builtin_uint8; } + ; + +/* TODO +name : NAME { $$ = $1.stoken; } + | TYPENAME { $$ = $1.stoken; } + | NAME_OR_INT { $$ = $1.stoken; } + ; +*/ + +name_not_typename + : NAME +/* These would be useful if name_not_typename was useful, but it is just + a fake for "variable", so these cause reduce/reduce conflicts because + the parser can't tell whether NAME_OR_INT is a name_not_typename (=variable, + =exp) or just an exp. If name_not_typename was ever used in an lvalue + context where only a name could occur, this might be useful. + | NAME_OR_INT +*/ + ; + +%% + +/* Wrapper on parse_c_float to get the type right for Go. */ + +static int +parse_go_float (struct gdbarch *gdbarch, const char *p, int len, + DOUBLEST *d, struct type **t) +{ + int result = parse_c_float (gdbarch, p, len, d, t); + const struct builtin_type *builtin_types = builtin_type (gdbarch); + const struct builtin_go_type *builtin_go_types = builtin_go_type (gdbarch); + + if (*t == builtin_types->builtin_float) + *t = builtin_go_types->builtin_float32; + else if (*t == builtin_types->builtin_double) + *t = builtin_go_types->builtin_float64; + + return result; +} + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/* FIXME: Needs some error checking for the float case. */ +/* FIXME(dje): IWBN to use c-exp.y's parse_number if we could. + That will require moving the guts into a function that we both call + as our YYSTYPE is different than c-exp.y's */ + +static int +parse_number (char *p, int len, int parsed_float, YYSTYPE *putithere) +{ + /* FIXME: Shouldn't these be unsigned? We don't deal with negative values + here, and we do kind of silly things like cast to unsigned. */ + LONGEST n = 0; + LONGEST prevn = 0; + ULONGEST un; + + int i = 0; + int c; + int base = input_radix; + int unsigned_p = 0; + + /* Number of "L" suffixes encountered. */ + int long_p = 0; + + /* We have found a "L" or "U" suffix. */ + int found_suffix = 0; + + ULONGEST high_bit; + struct type *signed_type; + struct type *unsigned_type; + + if (parsed_float) + { + if (! parse_go_float (parse_gdbarch, p, len, + &putithere->typed_val_float.dval, + &putithere->typed_val_float.type)) + return ERROR; + return FLOAT; + } + + /* Handle base-switching prefixes 0x, 0t, 0d, 0. */ + if (p[0] == '0') + switch (p[1]) + { + case 'x': + case 'X': + if (len >= 3) + { + p += 2; + base = 16; + len -= 2; + } + break; + + case 'b': + case 'B': + if (len >= 3) + { + p += 2; + base = 2; + len -= 2; + } + break; + + case 't': + case 'T': + case 'd': + case 'D': + if (len >= 3) + { + p += 2; + base = 10; + len -= 2; + } + break; + + default: + base = 8; + break; + } + + while (len-- > 0) + { + c = *p++; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != 'l' && c != 'u') + n *= base; + if (c >= '0' && c <= '9') + { + if (found_suffix) + return ERROR; + n += i = c - '0'; + } + else + { + if (base > 10 && c >= 'a' && c <= 'f') + { + if (found_suffix) + return ERROR; + n += i = c - 'a' + 10; + } + else if (c == 'l') + { + ++long_p; + found_suffix = 1; + } + else if (c == 'u') + { + unsigned_p = 1; + found_suffix = 1; + } + else + return ERROR; /* Char not a digit */ + } + if (i >= base) + return ERROR; /* Invalid digit in this base. */ + + /* Portably test for overflow (only works for nonzero values, so make + a second check for zero). FIXME: Can't we just make n and prevn + unsigned and avoid this? */ + if (c != 'l' && c != 'u' && (prevn >= n) && n != 0) + unsigned_p = 1; /* Try something unsigned. */ + + /* Portably test for unsigned overflow. + FIXME: This check is wrong; for example it doesn't find overflow + on 0x123456789 when LONGEST is 32 bits. */ + if (c != 'l' && c != 'u' && n != 0) + { + if ((unsigned_p && (ULONGEST) prevn >= (ULONGEST) n)) + error (_("Numeric constant too large.")); + } + prevn = n; + } + + /* An integer constant is an int, a long, or a long long. An L + suffix forces it to be long; an LL suffix forces it to be long + long. If not forced to a larger size, it gets the first type of + the above that it fits in. To figure out whether it fits, we + shift it right and see whether anything remains. Note that we + can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or more in one + operation, because many compilers will warn about such a shift + (which always produces a zero result). Sometimes gdbarch_int_bit + or gdbarch_long_bit will be that big, sometimes not. To deal with + the case where it is we just always shift the value more than + once, with fewer bits each time. */ + + un = (ULONGEST)n >> 2; + if (long_p == 0 + && (un >> (gdbarch_int_bit (parse_gdbarch) - 2)) == 0) + { + high_bit = ((ULONGEST)1) << (gdbarch_int_bit (parse_gdbarch) - 1); + + /* A large decimal (not hex or octal) constant (between INT_MAX + and UINT_MAX) is a long or unsigned long, according to ANSI, + never an unsigned int, but this code treats it as unsigned + int. This probably should be fixed. GCC gives a warning on + such constants. */ + + unsigned_type = parse_type->builtin_unsigned_int; + signed_type = parse_type->builtin_int; + } + else if (long_p <= 1 + && (un >> (gdbarch_long_bit (parse_gdbarch) - 2)) == 0) + { + high_bit = ((ULONGEST)1) << (gdbarch_long_bit (parse_gdbarch) - 1); + unsigned_type = parse_type->builtin_unsigned_long; + signed_type = parse_type->builtin_long; + } + else + { + int shift; + if (sizeof (ULONGEST) * HOST_CHAR_BIT + < gdbarch_long_long_bit (parse_gdbarch)) + /* A long long does not fit in a LONGEST. */ + shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1); + else + shift = (gdbarch_long_long_bit (parse_gdbarch) - 1); + high_bit = (ULONGEST) 1 << shift; + unsigned_type = parse_type->builtin_unsigned_long_long; + signed_type = parse_type->builtin_long_long; + } + + putithere->typed_val_int.val = n; + + /* If the high bit of the worked out type is set then this number + has to be unsigned. */ + + if (unsigned_p || (n & high_bit)) + { + putithere->typed_val_int.type = unsigned_type; + } + else + { + putithere->typed_val_int.type = signed_type; + } + + return INT; +} + +/* Temporary obstack used for holding strings. */ +static struct obstack tempbuf; +static int tempbuf_init; + +/* Parse a string or character literal from TOKPTR. The string or + character may be wide or unicode. *OUTPTR is set to just after the + end of the literal in the input string. The resulting token is + stored in VALUE. This returns a token value, either STRING or + CHAR, depending on what was parsed. *HOST_CHARS is set to the + number of host characters in the literal. */ + +static int +parse_string_or_char (char *tokptr, char **outptr, struct typed_stoken *value, + int *host_chars) +{ + int quote; + + /* Build the gdb internal form of the input string in tempbuf. Note + that the buffer is null byte terminated *only* for the + convenience of debugging gdb itself and printing the buffer + contents when the buffer contains no embedded nulls. Gdb does + not depend upon the buffer being null byte terminated, it uses + the length string instead. This allows gdb to handle C strings + (as well as strings in other languages) with embedded null + bytes */ + + if (!tempbuf_init) + tempbuf_init = 1; + else + obstack_free (&tempbuf, NULL); + obstack_init (&tempbuf); + + /* Skip the quote. */ + quote = *tokptr; + ++tokptr; + + *host_chars = 0; + + while (*tokptr) + { + char c = *tokptr; + if (c == '\\') + { + ++tokptr; + *host_chars += c_parse_escape (&tokptr, &tempbuf); + } + else if (c == quote) + break; + else + { + obstack_1grow (&tempbuf, c); + ++tokptr; + /* FIXME: this does the wrong thing with multi-byte host + characters. We could use mbrlen here, but that would + make "set host-charset" a bit less useful. */ + ++*host_chars; + } + } + + if (*tokptr != quote) + { + if (quote == '"') + error (_("Unterminated string in expression.")); + else + error (_("Unmatched single quote.")); + } + ++tokptr; + + value->type = C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/ + value->ptr = obstack_base (&tempbuf); + value->length = obstack_object_size (&tempbuf); + + *outptr = tokptr; + + return quote == '\'' ? CHAR : STRING; +} + +struct token +{ + char *operator; + int token; + enum exp_opcode opcode; +}; + +static const struct token tokentab3[] = + { + {">>=", ASSIGN_MODIFY, BINOP_RSH}, + {"<<=", ASSIGN_MODIFY, BINOP_LSH}, + /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */ + {"...", DOTDOTDOT, OP_NULL}, + }; + +static const struct token tokentab2[] = + { + {"+=", ASSIGN_MODIFY, BINOP_ADD}, + {"-=", ASSIGN_MODIFY, BINOP_SUB}, + {"*=", ASSIGN_MODIFY, BINOP_MUL}, + {"/=", ASSIGN_MODIFY, BINOP_DIV}, + {"%=", ASSIGN_MODIFY, BINOP_REM}, + {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR}, + {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND}, + {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR}, + {"++", INCREMENT, BINOP_END}, + {"--", DECREMENT, BINOP_END}, + /*{"->", RIGHT_ARROW, BINOP_END}, Doesn't exist in Go. */ + {"<-", LEFT_ARROW, BINOP_END}, + {"&&", ANDAND, BINOP_END}, + {"||", OROR, BINOP_END}, + {"<<", LSH, BINOP_END}, + {">>", RSH, BINOP_END}, + {"==", EQUAL, BINOP_END}, + {"!=", NOTEQUAL, BINOP_END}, + {"<=", LEQ, BINOP_END}, + {">=", GEQ, BINOP_END}, + /*{"&^", ANDNOT, BINOP_END}, TODO */ + }; + +/* Identifier-like tokens. */ +static const struct token ident_tokens[] = + { + {"true", TRUE_KEYWORD, OP_NULL}, + {"false", FALSE_KEYWORD, OP_NULL}, + {"nil", NIL_KEYWORD, OP_NULL}, + {"const", CONST_KEYWORD, OP_NULL}, + {"struct", STRUCT_KEYWORD, OP_NULL}, + {"type", TYPE_KEYWORD, OP_NULL}, + {"interface", INTERFACE_KEYWORD, OP_NULL}, + {"chan", CHAN_KEYWORD, OP_NULL}, + {"byte", BYTE_KEYWORD, OP_NULL}, /* An alias of uint8. */ + {"len", LEN_KEYWORD, OP_NULL}, + {"cap", CAP_KEYWORD, OP_NULL}, + {"new", NEW_KEYWORD, OP_NULL}, + {"iota", IOTA_KEYWORD, OP_NULL}, + }; + +/* This is set if a NAME token appeared at the very end of the input + string, with no whitespace separating the name from the EOF. This + is used only when parsing to do field name completion. */ +static int saw_name_at_eof; + +/* This is set if the previously-returned token was a structure + operator -- either '.' or ARROW. This is used only when parsing to + do field name completion. */ +static int last_was_structop; + +/* Read one token, getting characters through lexptr. */ + +static int +lex_one_token (void) +{ + int c; + int namelen; + unsigned int i; + char *tokstart; + int saw_structop = last_was_structop; + char *copy; + + last_was_structop = 0; + + retry: + + prev_lexptr = lexptr; + + tokstart = lexptr; + /* See if it is a special token of length 3. */ + for (i = 0; i < sizeof (tokentab3) / sizeof (tokentab3[0]); i++) + if (strncmp (tokstart, tokentab3[i].operator, 3) == 0) + { + lexptr += 3; + yylval.opcode = tokentab3[i].opcode; + return tokentab3[i].token; + } + + /* See if it is a special token of length 2. */ + for (i = 0; i < sizeof (tokentab2) / sizeof (tokentab2[0]); i++) + if (strncmp (tokstart, tokentab2[i].operator, 2) == 0) + { + lexptr += 2; + yylval.opcode = tokentab2[i].opcode; + /* NOTE: -> doesn't exist in Go, so we don't need to watch for + setting last_was_structop here. */ + return tokentab2[i].token; + } + + switch (c = *tokstart) + { + case 0: + if (saw_name_at_eof) + { + saw_name_at_eof = 0; + return COMPLETE; + } + else if (saw_structop) + return COMPLETE; + else + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '[': + case '(': + paren_depth++; + lexptr++; + return c; + + case ']': + case ')': + if (paren_depth == 0) + return 0; + paren_depth--; + lexptr++; + return c; + + case ',': + if (comma_terminates + && paren_depth == 0) + return 0; + lexptr++; + return c; + + case '.': + /* Might be a floating point number. */ + if (lexptr[1] < '0' || lexptr[1] > '9') + { + if (in_parse_field) + last_was_structop = 1; + goto symbol; /* Nope, must be a symbol. */ + } + /* FALL THRU into number case. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + /* It's a number. */ + int got_dot = 0, got_e = 0, toktype; + char *p = tokstart; + int hex = input_radix > 10; + + if (c == '0' && (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + hex = 1; + } + + for (;; ++p) + { + /* This test includes !hex because 'e' is a valid hex digit + and thus does not indicate a floating point number when + the radix is hex. */ + if (!hex && !got_e && (*p == 'e' || *p == 'E')) + got_dot = got_e = 1; + /* This test does not include !hex, because a '.' always indicates + a decimal floating point number regardless of the radix. */ + else if (!got_dot && *p == '.') + got_dot = 1; + else if (got_e && (p[-1] == 'e' || p[-1] == 'E') + && (*p == '-' || *p == '+')) + /* This is the sign of the exponent, not the end of the + number. */ + continue; + /* We will take any letters or digits. parse_number will + complain if past the radix, or if L or U are not final. */ + else if ((*p < '0' || *p > '9') + && ((*p < 'a' || *p > 'z') + && (*p < 'A' || *p > 'Z'))) + break; + } + toktype = parse_number (tokstart, p - tokstart, got_dot|got_e, &yylval); + if (toktype == ERROR) + { + char *err_copy = (char *) alloca (p - tokstart + 1); + + memcpy (err_copy, tokstart, p - tokstart); + err_copy[p - tokstart] = 0; + error (_("Invalid number \"%s\"."), err_copy); + } + lexptr = p; + return toktype; + } + + case '@': + { + char *p = &tokstart[1]; + size_t len = strlen ("entry"); + + while (isspace (*p)) + p++; + if (strncmp (p, "entry", len) == 0 && !isalnum (p[len]) + && p[len] != '_') + { + lexptr = &p[len]; + return ENTRY; + } + } + /* FALLTHRU */ + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '&': + case '^': + case '~': + case '!': + case '<': + case '>': + case '?': + case ':': + case '=': + case '{': + case '}': + symbol: + lexptr++; + return c; + + case '\'': + case '"': + case '`': + { + int host_len; + int result = parse_string_or_char (tokstart, &lexptr, &yylval.tsval, + &host_len); + if (result == CHAR) + { + if (host_len == 0) + error (_("Empty character constant.")); + else if (host_len > 2 && c == '\'') + { + ++tokstart; + namelen = lexptr - tokstart - 1; + goto tryname; + } + else if (host_len > 1) + error (_("Invalid character constant.")); + } + return result; + } + } + + if (!(c == '_' || c == '$' + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) + /* We must have come across a bad character (e.g. ';'). */ + error (_("Invalid character '%c' in expression."), c); + + /* It's a name. See how long it is. */ + namelen = 0; + for (c = tokstart[namelen]; + (c == '_' || c == '$' || (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));) + { + c = tokstart[++namelen]; + } + + /* The token "if" terminates the expression and is NOT removed from + the input stream. It doesn't count if it appears in the + expansion of a macro. */ + if (namelen == 2 + && tokstart[0] == 'i' + && tokstart[1] == 'f') + { + return 0; + } + + /* For the same reason (breakpoint conditions), "thread N" + terminates the expression. "thread" could be an identifier, but + an identifier is never followed by a number without intervening + punctuation. + Handle abbreviations of these, similarly to + breakpoint.c:find_condition_and_thread. + TODO: Watch for "goroutine" here? */ + if (namelen >= 1 + && strncmp (tokstart, "thread", namelen) == 0 + && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t')) + { + char *p = tokstart + namelen + 1; + while (*p == ' ' || *p == '\t') + p++; + if (*p >= '0' && *p <= '9') + return 0; + } + + lexptr += namelen; + + tryname: + + yylval.sval.ptr = tokstart; + yylval.sval.length = namelen; + + /* Catch specific keywords. */ + copy = copy_name (yylval.sval); + for (i = 0; i < sizeof (ident_tokens) / sizeof (ident_tokens[0]); i++) + if (strcmp (copy, ident_tokens[i].operator) == 0) + { + /* It is ok to always set this, even though we don't always + strictly need to. */ + yylval.opcode = ident_tokens[i].opcode; + return ident_tokens[i].token; + } + + if (*tokstart == '$') + return DOLLAR_VARIABLE; + + if (in_parse_field && *lexptr == '\0') + saw_name_at_eof = 1; + return NAME; +} + +/* An object of this type is pushed on a FIFO by the "outer" lexer. */ +typedef struct +{ + int token; + YYSTYPE value; +} token_and_value; + +DEF_VEC_O (token_and_value); + +/* A FIFO of tokens that have been read but not yet returned to the + parser. */ +static VEC (token_and_value) *token_fifo; + +/* Non-zero if the lexer should return tokens from the FIFO. */ +static int popping; + +/* Temporary storage for yylex; this holds symbol names as they are + built up. */ +static struct obstack name_obstack; + +/* Build "package.name" in name_obstack. + For convenience of the caller, the name is NUL-terminated, + but the NUL is not included in the recorded length. */ + +static struct stoken +build_packaged_name (const char *package, int package_len, + const char *name, int name_len) +{ + struct stoken result; + + obstack_free (&name_obstack, obstack_base (&name_obstack)); + obstack_grow (&name_obstack, package, package_len); + obstack_grow_str (&name_obstack, "."); + obstack_grow (&name_obstack, name, name_len); + obstack_grow (&name_obstack, "", 1); + result.ptr = obstack_base (&name_obstack); + result.length = obstack_object_size (&name_obstack) - 1; + + return result; +} + +/* Return non-zero if NAME is a package name. + BLOCK is the scope in which to interpret NAME; this can be NULL + to mean the global scope. */ + +static int +package_name_p (const char *name, struct block *block) +{ + struct symbol *sym; + int is_a_field_of_this; + + sym = lookup_symbol (name, block, STRUCT_DOMAIN, &is_a_field_of_this); + + if (sym + && SYMBOL_CLASS (sym) == LOC_TYPEDEF + && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_MODULE) + return 1; + + return 0; +} + +/* Classify a (potential) function in the "unsafe" package. + We fold these into "keywords" to keep things simple, at least until + something more complex is warranted. */ + +static int +classify_unsafe_function (struct stoken function_name) +{ + char *copy = copy_name (function_name); + + if (strcmp (copy, "Sizeof") == 0) + { + yylval.sval = function_name; + return SIZEOF_KEYWORD; + } + + error (_("Unknown function in `unsafe' package: %s"), copy); +} + +/* Classify token(s) "name1.name2" where name1 is known to be a package. + The contents of the token are in `yylval'. + Updates yylval and returns the new token type. + + The result is one of NAME, NAME_OR_INT, or TYPENAME. */ + +static int +classify_packaged_name (struct block *block) +{ + char *copy; + struct symbol *sym; + int is_a_field_of_this = 0; + + copy = copy_name (yylval.sval); + + sym = lookup_symbol (copy, block, VAR_DOMAIN, &is_a_field_of_this); + + if (sym) + { + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + } + + return NAME; +} + +/* Classify a NAME token. + The contents of the token are in `yylval'. + Updates yylval and returns the new token type. + BLOCK is the block in which lookups start; this can be NULL + to mean the global scope. + + The result is one of NAME, NAME_OR_INT, or TYPENAME. */ + +static int +classify_name (struct block *block) +{ + struct type *type; + struct symbol *sym; + char *copy; + int is_a_field_of_this = 0; + + copy = copy_name (yylval.sval); + + /* Try primitive types first so they win over bad/weird debug info. */ + type = language_lookup_primitive_type_by_name (parse_language, + parse_gdbarch, copy); + if (type != NULL) + { + /* NOTE: We take advantage of the fact that yylval coming in was a + NAME, and that struct ttype is a compatible extension of struct + stoken, so yylval.tsym.stoken is already filled in. */ + yylval.tsym.type = type; + return TYPENAME; + } + + /* TODO: What about other types? */ + + sym = lookup_symbol (copy, block, VAR_DOMAIN, &is_a_field_of_this); + + if (sym) + { + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return NAME; + } + + /* If we didn't find a symbol, look again in the current package. + This is to, e.g., make "p global_var" work without having to specify + the package name. We intentionally only looks for objects in the + current package. */ + + { + char *current_package_name = go_block_package_name (block); + + if (current_package_name != NULL) + { + struct stoken sval = + build_packaged_name (current_package_name, + strlen (current_package_name), + copy, strlen (copy)); + + xfree (current_package_name); + sym = lookup_symbol (sval.ptr, block, VAR_DOMAIN, + &is_a_field_of_this); + if (sym) + { + yylval.sval = sval; + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return NAME; + } + } + } + + /* Input names that aren't symbols but ARE valid hex numbers, when + the input radix permits them, can be names or numbers depending + on the parse. Note we support radixes > 16 here. */ + if ((copy[0] >= 'a' && copy[0] < 'a' + input_radix - 10) + || (copy[0] >= 'A' && copy[0] < 'A' + input_radix - 10)) + { + YYSTYPE newlval; /* Its value is ignored. */ + int hextype = parse_number (copy, yylval.sval.length, 0, &newlval); + if (hextype == INT) + return NAME_OR_INT; + } + + return NAME; +} + +/* This is taken from c-exp.y mostly to get something working. + The basic structure has been kept because we may yet need some of it. */ + +static int +yylex (void) +{ + token_and_value current, next; + + if (popping && !VEC_empty (token_and_value, token_fifo)) + { + token_and_value tv = *VEC_index (token_and_value, token_fifo, 0); + VEC_ordered_remove (token_and_value, token_fifo, 0); + yylval = tv.value; + /* There's no need to fall through to handle package.name + as that can never happen here. In theory. */ + return tv.token; + } + popping = 0; + + current.token = lex_one_token (); + + /* TODO: Need a way to force specifying name1 as a package. + .name1.name2 ? */ + + if (current.token != NAME) + return current.token; + + /* See if we have "name1 . name2". */ + + current.value = yylval; + next.token = lex_one_token (); + next.value = yylval; + + if (next.token == '.') + { + token_and_value name2; + + name2.token = lex_one_token (); + name2.value = yylval; + + if (name2.token == NAME) + { + /* Ok, we have "name1 . name2". */ + int token; + char *copy; + + copy = copy_name (current.value.sval); + + if (strcmp (copy, "unsafe") == 0) + { + popping = 1; + return classify_unsafe_function (name2.value.sval); + } + + if (package_name_p (copy, expression_context_block)) + { + popping = 1; + yylval.sval = build_packaged_name (current.value.sval.ptr, + current.value.sval.length, + name2.value.sval.ptr, + name2.value.sval.length); + return classify_packaged_name (expression_context_block); + } + } + + VEC_safe_push (token_and_value, token_fifo, &next); + VEC_safe_push (token_and_value, token_fifo, &name2); + } + else + { + VEC_safe_push (token_and_value, token_fifo, &next); + } + + /* If we arrive here we don't have a package-qualified name. */ + + popping = 1; + yylval = current.value; + return classify_name (expression_context_block); +} + +int +go_parse (void) +{ + int result; + struct cleanup *back_to = make_cleanup (null_cleanup, NULL); + + make_cleanup_restore_integer (&yydebug); + yydebug = parser_debug; + + /* Initialize some state used by the lexer. */ + last_was_structop = 0; + saw_name_at_eof = 0; + + VEC_free (token_and_value, token_fifo); + popping = 0; + obstack_init (&name_obstack); + make_cleanup_obstack_free (&name_obstack); + + result = yyparse (); + do_cleanups (back_to); + return result; +} + +void +yyerror (char *msg) +{ + if (prev_lexptr) + lexptr = prev_lexptr; + + error (_("A %s in expression, near `%s'."), (msg ? msg : "error"), lexptr); +} diff --git a/gdb/go-lang.c b/gdb/go-lang.c new file mode 100644 index 0000000..147a922 --- /dev/null +++ b/gdb/go-lang.c @@ -0,0 +1,667 @@ +/* Go language support routines for GDB, the GNU debugger. + + Copyright (C) 2012 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/>. */ + +/* TODO: + - split stacks + - printing of native types + - goroutines + - lots more + - gccgo mangling needs redoing + It's too hard, for example, to know whether one is looking at a mangled + Go symbol or not, and their are ambiguities, e.g., the demangler may + get passed *any* symbol, including symbols from other languages + and including symbols that are already demangled. + One thought is to at least add an _G prefix. + - 6g mangling isn't supported yet +*/ + +#include "defs.h" +#include "gdb_assert.h" +#include "gdb_obstack.h" +#include "gdb_string.h" +#include "block.h" +#include "symtab.h" +#include "language.h" +#include "go-lang.h" +#include "c-lang.h" +#include "parser-defs.h" + +#include <ctype.h> + +/* The main function in the main package. */ +static const char GO_MAIN_MAIN[] = "main.main"; + +/* Function returning the special symbol name used by Go for the main + procedure in the main program if it is found in minimal symbol list. + This function tries to find minimal symbols so that it finds them even + if the program was compiled without debugging information. */ + +const char * +go_main_name (void) +{ + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol (GO_MAIN_MAIN, NULL, NULL); + if (msym != NULL) + return GO_MAIN_MAIN; + + /* No known entry procedure found, the main program is probably not Go. */ + return NULL; +} + +/* Return non-zero if TYPE is a gccgo string. + We assume CHECK_TYPEDEF has already been done. */ + +static int +gccgo_string_p (struct type *type) +{ + /* gccgo strings don't necessarily have a name we can use. */ + + if (TYPE_NFIELDS (type) == 2) + { + struct type *type0 = TYPE_FIELD_TYPE (type, 0); + struct type *type1 = TYPE_FIELD_TYPE (type, 1); + + CHECK_TYPEDEF (type0); + CHECK_TYPEDEF (type1); + + if (TYPE_CODE (type0) == TYPE_CODE_PTR + && strcmp (TYPE_FIELD_NAME (type, 0), "__data") == 0 + && TYPE_CODE (type1) == TYPE_CODE_INT + && strcmp (TYPE_FIELD_NAME (type, 1), "__length") == 0) + { + struct type *target_type = TYPE_TARGET_TYPE (type0); + + CHECK_TYPEDEF (target_type); + + if (TYPE_CODE (target_type) == TYPE_CODE_INT + && TYPE_LENGTH (target_type) == 1 + && strcmp (TYPE_NAME (target_type), "uint8") == 0) + return 1; + } + } + + return 0; +} + +/* Return non-zero if TYPE is a 6g string. + We assume CHECK_TYPEDEF has already been done. */ + +static int +sixg_string_p (struct type *type) +{ + if (TYPE_NFIELDS (type) == 2 + && TYPE_TAG_NAME (type) != NULL + && strcmp (TYPE_TAG_NAME (type), "string") == 0) + return 1; + + return 0; +} + +/* Classify the kind of Go object that TYPE is. + TYPE is a TYPE_CODE_STRUCT, used to represent a Go object. */ + +enum go_type +go_classify_struct_type (struct type *type) +{ + CHECK_TYPEDEF (type); + + /* Recognize strings as they're useful to be able to print without + pretty-printers. */ + if (gccgo_string_p (type) + || sixg_string_p (type)) + return GO_TYPE_STRING; + + return GO_TYPE_NONE; +} + +/* Subroutine of unpack_mangled_go_symbol to simplify it. + Given "[foo.]bar.baz", store "bar" in *PACKAGEP and "baz" in *OBJECTP. + We stomp on the last '.' to nul-terminate "bar". + The caller is responsible for memory management. */ + +static void +unpack_package_and_object (char *buf, + const char **packagep, const char **objectp) +{ + char *last_dot; + + last_dot = strrchr (buf, '.'); + gdb_assert (last_dot != NULL); + *objectp = last_dot + 1; + *last_dot = '\0'; + last_dot = strrchr (buf, '.'); + if (last_dot != NULL) + *packagep = last_dot + 1; + else + *packagep = buf; +} + +/* Given a mangled Go symbol, find its package name, object name, and + method type (if present). + E.g., for "libgo_net.textproto.String.N33_libgo_net.textproto.ProtocolError" + *PACKAGEP = "textproto" + *OBJECTP = "String" + *METHOD_TYPE_PACKAGEP = "textproto" + *METHOD_TYPE_OBJECTP = "ProtocolError" + + Space for the resulting strings is malloc'd in one buffer. + PACKAGEP,OBJECTP,METHOD_TYPE* will (typically) point into this buffer. + [There are a few exceptions, but the caller is still responsible for + freeing the resulting pointer.] + A pointer to this buffer is returned, or NULL if symbol isn't a + mangled Go symbol. + The caller is responsible for freeing the result. + + *METHOD_TYPE_IS_POINTERP is set to a boolean indicating if + the method type is a pointer. + + There may be value in returning the outer container, + i.e., "net" in the above example, but for now it's not needed. + Plus it's currently not straightforward to compute, + it comes from -fgo-prefix, and there's no algorithm to compute it. + + If we ever need to unpack the method type, this routine should work + for that too. */ + +static char * +unpack_mangled_go_symbol (const char *mangled_name, + const char **packagep, + const char **objectp, + const char **method_type_packagep, + const char **method_type_objectp, + int *method_type_is_pointerp) +{ + char *buf; + char *p; + int len = strlen (mangled_name); + /* Pointer to last digit in "N<digit(s)>_". */ + char *saw_digit; + /* Pointer to "N" if valid "N<digit(s)>_" found. */ + char *method_type; + /* Pointer to the first '.'. */ + char *first_dot; + /* Pointer to the last '.'. */ + char *last_dot; + /* Non-zero if we saw a pointer indicator. */ + int saw_pointer; + + *packagep = *objectp = NULL; + *method_type_packagep = *method_type_objectp = NULL; + *method_type_is_pointerp = 0; + + /* main.init is mangled specially. */ + if (strcmp (mangled_name, "__go_init_main") == 0) + { + char *package = xstrdup ("main"); + + *packagep = package; + *objectp = "init"; + return package; + } + + /* main.main is mangled specially (missing prefix). */ + if (strcmp (mangled_name, "main.main") == 0) + { + char *package = xstrdup ("main"); + + *packagep = package; + *objectp = "main"; + return package; + } + + /* We may get passed, e.g., "main.T.Foo", which is *not* mangled. + Alas it looks exactly like "prefix.package.object." + To cope for now we only recognize the following prefixes: + + go: the default + libgo_.*: used by gccgo's runtime + + Thus we don't support -fgo-prefix (except as used by the runtime). */ + if (strncmp (mangled_name, "go.", 3) != 0 + && strncmp (mangled_name, "libgo_", 6) != 0) + return NULL; + + /* Quick check for whether a search may be fruitful. */ + /* Ignore anything with @plt, etc. in it. */ + if (strchr (mangled_name, '@') != NULL) + return NULL; + /* It must have at least two dots. */ + first_dot = strchr (mangled_name, '.'); + if (first_dot == NULL) + return NULL; + /* Treat "foo.bar" as unmangled. It can collide with lots of other + languages and it's not clear what the consequences are. + And except for main.main, all gccgo symbols are at least + prefix.package.object. */ + last_dot = strrchr (mangled_name, '.'); + if (last_dot == first_dot) + return NULL; + + /* More quick checks. */ + if (last_dot[1] == '\0' /* foo. */ + || last_dot[-1] == '.') /* foo..bar */ + return NULL; + + /* At this point we've decided we have a mangled Go symbol. */ + + buf = xstrdup (mangled_name); + + /* Search backwards looking for "N<digit(s)>". */ + p = buf + len; + saw_digit = method_type = NULL; + saw_pointer = 0; + while (p > buf) + { + int current = *(const unsigned char *) --p; + int current_is_digit = isdigit (current); + + if (saw_digit) + { + if (current_is_digit) + continue; + if (current == 'N' + && ((p > buf && p[-1] == '.') + || (p > buf + 1 && p[-1] == 'p' && p[-2] == '.'))) + { + if (atoi (p + 1) == strlen (saw_digit + 2)) + { + if (p[-1] == '.') + method_type = p - 1; + else + { + gdb_assert (p[-1] == 'p'); + saw_pointer = 1; + method_type = p - 2; + } + break; + } + } + /* Not what we're looking for, reset and keep looking. */ + saw_digit = NULL; + saw_pointer = 0; + continue; + } + if (current_is_digit && p[1] == '_') + { + /* Possible start of method "this" [sic] type. */ + saw_digit = p; + continue; + } + } + + if (method_type != NULL + /* Ensure not something like "..foo". */ + && (method_type > buf && method_type[-1] != '.')) + { + unpack_package_and_object (saw_digit + 2, + method_type_packagep, method_type_objectp); + *method_type = '\0'; + *method_type_is_pointerp = saw_pointer; + } + + unpack_package_and_object (buf, packagep, objectp); + return buf; +} + +/* Implements the la_demangle language_defn routine for language Go. + + N.B. This may get passed *any* symbol, including symbols from other + languages and including symbols that are already demangled. + Both of these situations are kinda unfortunate, but that's how things + are today. + + N.B. This currently only supports gccgo's mangling. + + N.B. gccgo's mangling needs, I think, changing. + This demangler can't work in all situations, + thus not too much effort is currently put into it. */ + +char * +go_demangle (const char *mangled_name, int options) +{ + struct obstack tempbuf; + char *result; + char *name_buf; + const char *package_name; + const char *object_name; + const char *method_type_package_name; + const char *method_type_object_name; + int method_type_is_pointer; + + if (mangled_name == NULL) + return NULL; + + name_buf = unpack_mangled_go_symbol (mangled_name, + &package_name, &object_name, + &method_type_package_name, + &method_type_object_name, + &method_type_is_pointer); + if (name_buf == NULL) + return NULL; + + obstack_init (&tempbuf); + + /* Print methods as they appear in "method expressions". */ + if (method_type_package_name != NULL) + { + /* FIXME: Seems like we should include package_name here somewhere. */ + if (method_type_is_pointer) + obstack_grow_str (&tempbuf, "(*"); + obstack_grow_str (&tempbuf, method_type_package_name); + obstack_grow_str (&tempbuf, "."); + obstack_grow_str (&tempbuf, method_type_object_name); + if (method_type_is_pointer) + obstack_grow_str (&tempbuf, ")"); + obstack_grow_str (&tempbuf, "."); + obstack_grow_str (&tempbuf, object_name); + } + else + { + obstack_grow_str (&tempbuf, package_name); + obstack_grow_str (&tempbuf, "."); + obstack_grow_str (&tempbuf, object_name); + } + obstack_grow_str0 (&tempbuf, ""); + + result = xstrdup (obstack_finish (&tempbuf)); + obstack_free (&tempbuf, NULL); + xfree (name_buf); + return result; +} + +/* Given a Go symbol, return its package or NULL if unknown. + Space for the result is malloc'd, caller must free. */ + +char * +go_symbol_package_name (const struct symbol *sym) +{ + const char *mangled_name = SYMBOL_LINKAGE_NAME (sym); + const char *package_name; + const char *object_name; + const char *method_type_package_name; + const char *method_type_object_name; + int method_type_is_pointer; + char *name_buf; + char *result; + + gdb_assert (SYMBOL_LANGUAGE (sym) == language_go); + name_buf = unpack_mangled_go_symbol (mangled_name, + &package_name, &object_name, + &method_type_package_name, + &method_type_object_name, + &method_type_is_pointer); + /* Some Go symbols don't have mangled form we interpret (yet). */ + if (name_buf == NULL) + return NULL; + result = xstrdup (package_name); + xfree (name_buf); + return result; +} + +/* Return the package that BLOCK is in, or NULL if there isn't one. + Space for the result is malloc'd, caller must free. */ + +char * +go_block_package_name (const struct block *block) +{ + while (block != NULL) + { + struct symbol *function = BLOCK_FUNCTION (block); + + if (function != NULL) + { + char *package_name = go_symbol_package_name (function); + + if (package_name != NULL) + return package_name; + + /* Stop looking if we find a function without a package name. + We're most likely outside of Go and thus the concept of the + "current" package is gone. */ + return NULL; + } + + block = BLOCK_SUPERBLOCK (block); + } + + return NULL; +} + +/* Table mapping opcodes into strings for printing operators + and precedences of the operators. + TODO(dje): &^ ? */ + +static const struct op_print go_op_print_tab[] = +{ + {",", BINOP_COMMA, PREC_COMMA, 0}, + {"=", BINOP_ASSIGN, PREC_ASSIGN, 1}, + {"||", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0}, + {"&&", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0}, + {"|", BINOP_BITWISE_IOR, PREC_BITWISE_IOR, 0}, + {"^", BINOP_BITWISE_XOR, PREC_BITWISE_XOR, 0}, + {"&", BINOP_BITWISE_AND, PREC_BITWISE_AND, 0}, + {"==", BINOP_EQUAL, PREC_EQUAL, 0}, + {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0}, + {"<=", BINOP_LEQ, PREC_ORDER, 0}, + {">=", BINOP_GEQ, PREC_ORDER, 0}, + {">", BINOP_GTR, PREC_ORDER, 0}, + {"<", BINOP_LESS, PREC_ORDER, 0}, + {">>", BINOP_RSH, PREC_SHIFT, 0}, + {"<<", BINOP_LSH, PREC_SHIFT, 0}, + {"+", BINOP_ADD, PREC_ADD, 0}, + {"-", BINOP_SUB, PREC_ADD, 0}, + {"*", BINOP_MUL, PREC_MUL, 0}, + {"/", BINOP_DIV, PREC_MUL, 0}, + {"%", BINOP_REM, PREC_MUL, 0}, + {"@", BINOP_REPEAT, PREC_REPEAT, 0}, + {"-", UNOP_NEG, PREC_PREFIX, 0}, + {"!", UNOP_LOGICAL_NOT, PREC_PREFIX, 0}, + {"^", UNOP_COMPLEMENT, PREC_PREFIX, 0}, + {"*", UNOP_IND, PREC_PREFIX, 0}, + {"&", UNOP_ADDR, PREC_PREFIX, 0}, + {"unsafe.Sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0}, + {"++", UNOP_POSTINCREMENT, PREC_SUFFIX, 0}, + {"--", UNOP_POSTDECREMENT, PREC_SUFFIX, 0}, + {NULL, 0, 0, 0} +}; + +enum go_primitive_types { + go_primitive_type_void, + go_primitive_type_char, + go_primitive_type_bool, + go_primitive_type_int, + go_primitive_type_uint, + go_primitive_type_uintptr, + go_primitive_type_int8, + go_primitive_type_int16, + go_primitive_type_int32, + go_primitive_type_int64, + go_primitive_type_uint8, + go_primitive_type_uint16, + go_primitive_type_uint32, + go_primitive_type_uint64, + go_primitive_type_float32, + go_primitive_type_float64, + go_primitive_type_complex64, + go_primitive_type_complex128, + nr_go_primitive_types +}; + +static void +go_language_arch_info (struct gdbarch *gdbarch, + struct language_arch_info *lai) +{ + const struct builtin_go_type *builtin = builtin_go_type (gdbarch); + + lai->string_char_type = builtin->builtin_char; + + lai->primitive_type_vector + = GDBARCH_OBSTACK_CALLOC (gdbarch, nr_go_primitive_types + 1, + struct type *); + + lai->primitive_type_vector [go_primitive_type_void] + = builtin->builtin_void; + lai->primitive_type_vector [go_primitive_type_char] + = builtin->builtin_char; + lai->primitive_type_vector [go_primitive_type_bool] + = builtin->builtin_bool; + lai->primitive_type_vector [go_primitive_type_int] + = builtin->builtin_int; + lai->primitive_type_vector [go_primitive_type_uint] + = builtin->builtin_uint; + lai->primitive_type_vector [go_primitive_type_uintptr] + = builtin->builtin_uintptr; + lai->primitive_type_vector [go_primitive_type_int8] + = builtin->builtin_int8; + lai->primitive_type_vector [go_primitive_type_int16] + = builtin->builtin_int16; + lai->primitive_type_vector [go_primitive_type_int32] + = builtin->builtin_int32; + lai->primitive_type_vector [go_primitive_type_int64] + = builtin->builtin_int64; + lai->primitive_type_vector [go_primitive_type_uint8] + = builtin->builtin_uint8; + lai->primitive_type_vector [go_primitive_type_uint16] + = builtin->builtin_uint16; + lai->primitive_type_vector [go_primitive_type_uint32] + = builtin->builtin_uint32; + lai->primitive_type_vector [go_primitive_type_uint64] + = builtin->builtin_uint64; + lai->primitive_type_vector [go_primitive_type_float32] + = builtin->builtin_float32; + lai->primitive_type_vector [go_primitive_type_float64] + = builtin->builtin_float64; + lai->primitive_type_vector [go_primitive_type_complex64] + = builtin->builtin_complex64; + lai->primitive_type_vector [go_primitive_type_complex128] + = builtin->builtin_complex128; + + lai->bool_type_symbol = "bool"; + lai->bool_type_default = builtin->builtin_bool; +} + +static const struct language_defn go_language_defn = +{ + "go", + language_go, + range_check_off, + type_check_off, + case_sensitive_on, + array_row_major, + macro_expansion_no, + &exp_descriptor_c, + go_parse, + go_error, + null_post_parser, + c_printchar, /* Print a character constant. */ + c_printstr, /* Function to print string constant. */ + c_emit_char, /* Print a single char. */ + go_print_type, /* Print a type using appropriate syntax. */ + c_print_typedef, /* Print a typedef using appropriate + syntax. */ + go_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 */ + basic_lookup_symbol_nonlocal, + basic_lookup_transparent_type, + go_demangle, /* Language specific symbol demangler. */ + NULL, /* Language specific + class_name_from_physname. */ + go_op_print_tab, /* Expression operators for printing. */ + 1, /* C-style arrays. */ + 0, /* String lower bound. */ + default_word_break_characters, + default_make_symbol_completion_list, + go_language_arch_info, + default_print_array_index, + default_pass_by_reference, + c_get_string, + NULL, + iterate_over_symbols, + LANG_MAGIC +}; + +static void * +build_go_types (struct gdbarch *gdbarch) +{ + struct builtin_go_type *builtin_go_type + = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct builtin_go_type); + + builtin_go_type->builtin_void + = arch_type (gdbarch, TYPE_CODE_VOID, 1, "void"); + builtin_go_type->builtin_char + = arch_character_type (gdbarch, 8, 1, "char"); + builtin_go_type->builtin_bool + = arch_boolean_type (gdbarch, 8, 0, "bool"); + builtin_go_type->builtin_int + = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch), 0, "int"); + builtin_go_type->builtin_uint + = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch), 1, "uint"); + builtin_go_type->builtin_uintptr + = arch_integer_type (gdbarch, gdbarch_ptr_bit (gdbarch), 1, "uintptr"); + builtin_go_type->builtin_int8 + = arch_integer_type (gdbarch, 8, 0, "int8"); + builtin_go_type->builtin_int16 + = arch_integer_type (gdbarch, 16, 0, "int16"); + builtin_go_type->builtin_int32 + = arch_integer_type (gdbarch, 32, 0, "int32"); + builtin_go_type->builtin_int64 + = arch_integer_type (gdbarch, 64, 0, "int64"); + builtin_go_type->builtin_uint8 + = arch_integer_type (gdbarch, 8, 1, "uint8"); + builtin_go_type->builtin_uint16 + = arch_integer_type (gdbarch, 16, 1, "uint16"); + builtin_go_type->builtin_uint32 + = arch_integer_type (gdbarch, 32, 1, "uint32"); + builtin_go_type->builtin_uint64 + = arch_integer_type (gdbarch, 64, 1, "uint64"); + builtin_go_type->builtin_float32 + = arch_float_type (gdbarch, 32, "float32", NULL); + builtin_go_type->builtin_float64 + = arch_float_type (gdbarch, 64, "float64", NULL); + builtin_go_type->builtin_complex64 + = arch_complex_type (gdbarch, "complex64", + builtin_go_type->builtin_float32); + builtin_go_type->builtin_complex128 + = arch_complex_type (gdbarch, "complex128", + builtin_go_type->builtin_float64); + + return builtin_go_type; +} + +static struct gdbarch_data *go_type_data; + +const struct builtin_go_type * +builtin_go_type (struct gdbarch *gdbarch) +{ + return gdbarch_data (gdbarch, go_type_data); +} + +extern initialize_file_ftype _initialize_go_language; + +void +_initialize_go_language (void) +{ + go_type_data = gdbarch_data_register_post_init (build_go_types); + + add_language (&go_language_defn); +} diff --git a/gdb/go-lang.h b/gdb/go-lang.h new file mode 100644 index 0000000..67b5d93 --- /dev/null +++ b/gdb/go-lang.h @@ -0,0 +1,88 @@ +/* Go language support definitions for GDB, the GNU debugger. + + Copyright (C) 2012 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/>. */ + +#if !defined (GO_LANG_H) +#define GO_LANG_H 1 + +#include "gdbtypes.h" +#include "symtab.h" +#include "value.h" + +struct builtin_go_type +{ + struct type *builtin_void; + struct type *builtin_char; + struct type *builtin_bool; + struct type *builtin_int; + struct type *builtin_uint; + struct type *builtin_uintptr; + struct type *builtin_int8; + struct type *builtin_int16; + struct type *builtin_int32; + struct type *builtin_int64; + struct type *builtin_uint8; + struct type *builtin_uint16; + struct type *builtin_uint32; + struct type *builtin_uint64; + struct type *builtin_float32; + struct type *builtin_float64; + struct type *builtin_complex64; + struct type *builtin_complex128; +}; + +enum go_type +{ + GO_TYPE_NONE, /* Not a Go object. */ + GO_TYPE_STRING +}; + +/* Defined in go-exp.y. */ + +extern int go_parse (void); + +extern void go_error (char *); + +/* Defined in go-lang.c. */ + +extern const char *go_main_name (void); + +extern enum go_type go_classify_struct_type (struct type *type); + +extern char *go_demangle (const char *mangled, int options); + +extern char *go_symbol_package_name (const struct symbol *sym); + +extern char *go_block_package_name (const struct block *block); + +extern const struct builtin_go_type *builtin_go_type (struct gdbarch *); + +/* Defined in go-typeprint.c. */ + +extern void go_print_type (struct type *type, const char *varstring, + struct ui_file *stream, int show, int level); + +/* Defined in go-valprint.c. */ + +extern void go_val_print (struct type *type, const gdb_byte *valaddr, + int embedded_offset, CORE_ADDR address, + struct ui_file *stream, int recurse, + const struct value *val, + const struct value_print_options *options); + +#endif /* !defined (GO_LANG_H) */ diff --git a/gdb/go-typeprint.c b/gdb/go-typeprint.c new file mode 100644 index 0000000..a76c3dc --- /dev/null +++ b/gdb/go-typeprint.c @@ -0,0 +1,62 @@ +/* Support for printing Go types for GDB, the GNU debugger. + + Copyright (C) 2012 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/>. */ + +/* TODO: + - lots + - if the more complex types get Python pretty-printers, we'll + want a Python API for type printing +*/ + +#include "defs.h" +#include "gdbtypes.h" +#include "c-lang.h" +#include "go-lang.h" + +/* Print a description of a type TYPE. + Output goes to STREAM (via stdio). + If VARSTRING is a non-empty string, print as an Ada variable/field + declaration. + SHOW+1 is the maximum number of levels of internal type structure + to show (this applies to record types, enumerated types, and + array types). + SHOW is the number of levels of internal type structure to show + when there is a type name for the SHOWth deepest level (0th is + outer level). + When SHOW<0, no inner structure is shown. + LEVEL indicates level of recursion (for nested definitions). */ + +void +go_print_type (struct type *type, const char *varstring, + struct ui_file *stream, int show, int level) +{ + /* Borrowed from c-typeprint.c. */ + if (show > 0) + CHECK_TYPEDEF (type); + + /* Print the type of "abc" as "string", not char[4]. */ + if (TYPE_CODE (type) == TYPE_CODE_ARRAY + && TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_CHAR) + { + fputs_filtered ("string", stream); + return; + } + + /* Punt the rest to C for now. */ + c_print_type (type, varstring, stream, show, level); +} diff --git a/gdb/go-valprint.c b/gdb/go-valprint.c new file mode 100644 index 0000000..3be4927 --- /dev/null +++ b/gdb/go-valprint.c @@ -0,0 +1,120 @@ +/* Support for printing Go values for GDB, the GNU debugger. + + Copyright (C) 2012 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/>. + + NOTE: This currently only provides special support for printing gccgo + strings. 6g objects are handled in Python. + The remaining gccgo types may also be handled in Python. + Strings are handled specially here, at least for now, in case the Python + support is unavailable. */ + +#include "defs.h" +#include "gdbtypes.h" +#include "gdbcore.h" +#include "go-lang.h" +#include "c-lang.h" +#include "valprint.h" + +/* Print a Go string. + + Note: We assume + gdb_assert (go_classify_struct_type (type) == GO_TYPE_STRING). */ + +static void +print_go_string (struct type *type, const gdb_byte *valaddr, + int embedded_offset, CORE_ADDR address, + struct ui_file *stream, int recurse, + const struct value *val, + const struct value_print_options *options) +{ + struct gdbarch *gdbarch = get_type_arch (type); + struct type *elt_ptr_type = TYPE_FIELD_TYPE (type, 0); + struct type *elt_type = TYPE_TARGET_TYPE (elt_ptr_type); + LONGEST length; + /* TODO(dje): The encapsulation of what a pointer is belongs in value.c. + I.e. If there's going to be unpack_pointer, there should be + unpack_value_field_as_pointer. Do this until we can get + unpack_value_field_as_pointer. */ + LONGEST addr; + + gdb_assert (valaddr == value_contents_for_printing_const (val)); + + if (! unpack_value_field_as_long (type, valaddr, embedded_offset, 0, + val, &addr)) + error (_("Unable to read string address")); + + if (! unpack_value_field_as_long (type, valaddr, embedded_offset, 1, + val, &length)) + error (_("Unable to read string length")); + + /* TODO(dje): Print address of struct or actual string? */ + if (options->addressprint) + fputs_filtered (paddress (gdbarch, addr), stream); + + if (length < 0) + { + fputs_filtered (_("<invalid length: "), stream); + fputs_filtered (plongest (addr), stream); + fputs_filtered (">", stream); + return; + } + + /* TODO(dje): Perhaps we should pass "UTF8" for ENCODING. + The target encoding is a global switch. + Either choice is problematic. */ + val_print_string (elt_type, NULL, addr, length, stream, options); +} + +/* Implements the la_val_print routine for language Go. */ + +void +go_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, + CORE_ADDR address, struct ui_file *stream, int recurse, + const struct value *val, + const struct value_print_options *options) +{ + CHECK_TYPEDEF (type); + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_STRUCT: + { + enum go_type go_type = go_classify_struct_type (type); + + switch (go_type) + { + case GO_TYPE_STRING: + if (! options->raw) + { + print_go_string (type, valaddr, embedded_offset, address, + stream, recurse, val, options); + return; + } + break; + default: + break; + } + } + /* Fall through. */ + + default: + c_val_print (type, valaddr, embedded_offset, address, stream, + recurse, val, options); + break; + } +} diff --git a/gdb/symtab.c b/gdb/symtab.c index af115cd..6c70113 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -39,6 +39,7 @@ #include "objc-lang.h" #include "d-lang.h" #include "ada-lang.h" +#include "go-lang.h" #include "p-lang.h" #include "addrmap.h" @@ -500,6 +501,7 @@ symbol_set_language (struct general_symbol_info *gsymbol, { gsymbol->language = language; if (gsymbol->language == language_d + || gsymbol->language == language_go || gsymbol->language == language_java || gsymbol->language == language_objc || gsymbol->language == language_fortran) @@ -620,6 +622,22 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol, return demangled; } } + /* FIXME(dje): Continually adding languages here is clumsy. + Better to just call la_demangle if !auto, and if auto then call + a utility routine that tries successive languages in turn and reports + which one it finds. I realize the la_demangle options may be different + for different languages but there's already a FIXME for that. */ + if (gsymbol->language == language_go + || gsymbol->language == language_auto) + { + demangled = go_demangle (mangled, 0); + if (demangled != NULL) + { + gsymbol->language = language_go; + return demangled; + } + } + /* We could support `gsymbol->language == language_fortran' here to provide module namespaces also for inferiors with only minimal symbol table (ELF symbols). Just the mangling standard is not standardized across compilers @@ -742,7 +760,11 @@ symbol_set_names (struct general_symbol_info *gsymbol, &entry, INSERT)); /* If this name is not in the hash table, add it. */ - if (*slot == NULL) + if (*slot == NULL + /* A C version of the symbol may have already snuck into the table. + This happens to, e.g., main.init (__go_init_main). Cope. */ + || (gsymbol->language == language_go + && (*slot)->demangled[0] == '\0')) { char *demangled_name = symbol_find_demangled_name (gsymbol, linkage_name_copy); @@ -804,6 +826,7 @@ symbol_natural_name (const struct general_symbol_info *gsymbol) { case language_cplus: case language_d: + case language_go: case language_java: case language_objc: case language_fortran: @@ -832,6 +855,7 @@ symbol_demangled_name (const struct general_symbol_info *gsymbol) { case language_cplus: case language_d: + case language_go: case language_java: case language_objc: case language_fortran: @@ -1123,7 +1147,7 @@ demangle_for_lookup (const char *name, enum language lang, modified_name = name; - /* If we are using C++, D, or Java, demangle the name before doing a + /* If we are using C++, D, Go, or Java, demangle the name before doing a lookup, so we can always binary search. */ if (lang == language_cplus) { @@ -1164,6 +1188,15 @@ demangle_for_lookup (const char *name, enum language lang, make_cleanup (xfree, demangled_name); } } + else if (lang == language_go) + { + demangled_name = go_demangle (name, 0); + if (demangled_name) + { + modified_name = demangled_name; + make_cleanup (xfree, demangled_name); + } + } *result_name = modified_name; return cleanup; @@ -4802,6 +4835,13 @@ find_main_name (void) return; } + new_main_name = go_main_name (); + if (new_main_name != NULL) + { + set_main_name (new_main_name); + return; + } + new_main_name = pascal_main_name (); if (new_main_name != NULL) { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 8c1fd30..6c2ca58 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,36 @@ +2012-04-25 Doug Evans <dje@google.com> + + * configure.ac: Create gdb.go/Makefile. + * configure: Regenerate. + * gdb.base/default.exp: Add "go" to "set language" testing. + * gdb.go/Makefile.in: New file. + * gdb.go/basic-types.exp: New file. + * gdb.go/chan.exp: New file. + * gdb.go/chan.go: New file. + * gdb.go/handcall.exp: New file. + * gdb.go/handcall.go: New file. + * gdb.go/hello.exp: New file. + * gdb.go/hello.go: New file. + * gdb.go/integers.exp: New file. + * gdb.go/integers.go: New file. + * gdb.go/methods.exp: New file. + * gdb.go/methods.go: New file. + * gdb.go/package.exp: New file. + * gdb.go/package1.go: New file. + * gdb.go/package2.go: New file. + * gdb.go/print.exp: New file. + * gdb.go/strings.exp: New file. + * gdb.go/strings.go: New file. + * gdb.go/types.exp: New file. + * gdb.go/types.go: New file. + * gdb.go/unsafe.exp: New file. + * gdb.go/unsafe.go: New file. + * lib/future.exp: Add Go support. + (gdb_find_go, gdb_find_go_linker): New procs. + (gdb_default_target_compile): Add Go support. + * lib/gdb.exp (skip_go_tests): New proc. + * lib/go.exp: New file. + 2012-04-25 Tom Tromey <tromey@redhat.com> * gdb.threads/linux-dp.exp: Unset 'seen' before 'array set'. diff --git a/gdb/testsuite/configure b/gdb/testsuite/configure index fb70b3d..0c8c344 100755 --- a/gdb/testsuite/configure +++ b/gdb/testsuite/configure @@ -3448,7 +3448,7 @@ done -ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.cell/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.fortran/Makefile gdb.server/Makefile gdb.java/Makefile gdb.hp/Makefile gdb.hp/gdb.objdbg/Makefile gdb.hp/gdb.base-hp/Makefile gdb.hp/gdb.aCC/Makefile gdb.hp/gdb.compat/Makefile gdb.hp/gdb.defects/Makefile gdb.linespec/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.multi/Makefile gdb.objc/Makefile gdb.opencl/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.python/Makefile gdb.reverse/Makefile gdb.stabs/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile" +ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.cell/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.fortran/Makefile gdb.go/Makefile gdb.server/Makefile gdb.java/Makefile gdb.hp/Makefile gdb.hp/gdb.objdbg/Makefile gdb.hp/gdb.base-hp/Makefile gdb.hp/gdb.aCC/Makefile gdb.hp/gdb.compat/Makefile gdb.hp/gdb.defects/Makefile gdb.linespec/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.multi/Makefile gdb.objc/Makefile gdb.opencl/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.python/Makefile gdb.reverse/Makefile gdb.stabs/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -4158,6 +4158,7 @@ do "gdb.disasm/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.disasm/Makefile" ;; "gdb.dwarf2/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.dwarf2/Makefile" ;; "gdb.fortran/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.fortran/Makefile" ;; + "gdb.go/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.go/Makefile" ;; "gdb.server/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.server/Makefile" ;; "gdb.java/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.java/Makefile" ;; "gdb.hp/Makefile") CONFIG_FILES="$CONFIG_FILES gdb.hp/Makefile" ;; diff --git a/gdb/testsuite/configure.ac b/gdb/testsuite/configure.ac index 5735805..d9ab9f7 100644 --- a/gdb/testsuite/configure.ac +++ b/gdb/testsuite/configure.ac @@ -91,7 +91,7 @@ AC_OUTPUT([Makefile \ gdb.ada/Makefile \ gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile \ gdb.cell/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile \ - gdb.fortran/Makefile gdb.server/Makefile gdb.java/Makefile \ + gdb.fortran/Makefile gdb.go/Makefile gdb.server/Makefile gdb.java/Makefile \ gdb.hp/Makefile gdb.hp/gdb.objdbg/Makefile gdb.hp/gdb.base-hp/Makefile \ gdb.hp/gdb.aCC/Makefile gdb.hp/gdb.compat/Makefile \ gdb.hp/gdb.defects/Makefile gdb.linespec/Makefile \ diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index c656a20..e2859d5 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -528,7 +528,7 @@ gdb_test "set history size" "Argument required .integer to set it to.*" "set his #test set history gdb_test "set history" "\"set history\" must be followed by the name of a history subcommand.(\[^\r\n\]*\[\r\n\])+List of set history subcommands:(\[^\r\n\]*\[\r\n\])+set history expansion -- Set history expansion on command input(\[^\r\n\]*\[\r\n\])+set history filename -- Set the filename in which to record the command history(\[^\r\n\]*\[\r\n\])+set history save -- Set saving of the history record on exit(\[^\r\n\]*\[\r\n\])+set history size -- Set the size of the command history(\[^\r\n\]*\[\r\n\])+Type \"help set history\" followed by set history subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set history" #test set language -gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, c, c.., asm, minimal, d, fortran, objective-c, java, modula-2, opencl, pascal." "set language" +gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, c, c.., asm, minimal, d, fortran, objective-c, go, java, modula-2, opencl, pascal." "set language" #test set listsize gdb_test "set listsize" "Argument required .integer to set it to.*" "set listsize" #test set print "p" abbreviation diff --git a/gdb/testsuite/gdb.go/Makefile.in b/gdb/testsuite/gdb.go/Makefile.in new file mode 100644 index 0000000..dfce2d6 --- /dev/null +++ b/gdb/testsuite/gdb.go/Makefile.in @@ -0,0 +1,20 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ + +EXECUTABLES = \ + chan handcall hello integers methods package \ + strings types unsafe + +all info install-info dvi install uninstall installcheck check: + @echo "Nothing to be done for $@..." + +clean mostlyclean: + -find . -name '*.o' -print | xargs rm -f + -find . -name '*.ali' -print | xargs rm -f + -rm -f *~ a.out + -rm -f core core.coremaker coremaker.core corefile $(EXECUTABLES) + +distclean maintainer-clean realclean: clean + -rm -f Makefile config.status config.log + -rm -f *-init.exp gdb.log gdb.sum + -rm -fr *.log summary detail *.plog *.sum *.psum site.* diff --git a/gdb/testsuite/gdb.go/basic-types.exp b/gdb/testsuite/gdb.go/basic-types.exp new file mode 100644 index 0000000..d3a542d --- /dev/null +++ b/gdb/testsuite/gdb.go/basic-types.exp @@ -0,0 +1,118 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test basic builtin types. +# NOTE: The tests here intentionally do not require a go compiler. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +proc test_integer_literal_types_accepted {} { + # Test various decimal values. + + gdb_test "pt 123" "type = int" + + gdb_test "pt void(42)" "type = void" + gdb_test "pt byte(42)" "type = uint8" + gdb_test "pt int(42)" "type = int" + gdb_test "pt uint(42)" "type = uint" + gdb_test "pt uintptr(42)" "type = uintptr" + + gdb_test "pt int8(42)" "type = int8" + gdb_test "pt int16(42)" "type = int16" + gdb_test "pt int32(42)" "type = int32" + gdb_test "pt int64(42)" "type = int64" + + gdb_test "pt uint8(42)" "type = uint8" + gdb_test "pt uint16(42)" "type = uint16" + gdb_test "pt uint32(42)" "type = uint32" + gdb_test "pt uint64(42)" "type = uint64" +} + +proc test_logical_literal_types_accepted {} { + # Test the only possible values for a logical, TRUE and FALSE. + + gdb_test "pt true" "type = bool" + gdb_test "pt false" "type = bool" + + gdb_test "pt bool(0)" "type = bool" + gdb_test "pt bool(1)" "type = bool" +} + +proc test_character_literal_types_accepted {} { + # Test various character values. + + gdb_test "pt 'a'" "type = char" + + # FIXME: Need more. +} + +proc test_string_literal_types_accepted {} { + # Test various string values. + + gdb_test "pt \"a simple string\"" "type = string" + gdb_test "pt `a simple raw string`" "type = string" + + # FIXME: Need more. +} + +proc test_float_literal_types_accepted {} { + # Test various floating point formats. + + gdb_test "pt .44" "type = float64" + gdb_test "pt 44.0" "type = float64" + gdb_test "pt 10e20" "type = float64" + gdb_test "pt 10E20" "type = float64" + + gdb_test "pt float32(.42)" "type = float32" + + gdb_test "pt float64(.42)" "type = float64" +} + +proc test_complex_literal_types_accepted {} { + # Test various complex formats. + + gdb_test "pt complex64(.42)" "type = complex64" + setup_xfail "*-*-*" + gdb_test "pt complex64(.42i1.0)" "type = complex64" + setup_xfail "*-*-*" + gdb_test "pt complex64(i1.0)" "type = complex64" + + gdb_test "pt complex128(.42)" "type = complex128" + setup_xfail "*-*-*" + gdb_test "pt complex128(.42i1.0)" "type = complex128" + setup_xfail "*-*-*" + gdb_test "pt complex128(i1.0)" "type = complex128" +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +if [set_lang_go] { + test_integer_literal_types_accepted + test_logical_literal_types_accepted + test_character_literal_types_accepted + test_string_literal_types_accepted + test_float_literal_types_accepted + test_complex_literal_types_accepted +} else { + warning "Go type tests suppressed." +} diff --git a/gdb/testsuite/gdb.go/chan.exp b/gdb/testsuite/gdb.go/chan.exp new file mode 100644 index 0000000..317e214 --- /dev/null +++ b/gdb/testsuite/gdb.go/chan.exp @@ -0,0 +1,52 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Basic channel tests. +# This is very much a work-in-progress. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "chan" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] +set bp_location2 [gdb_get_line_number "set breakpoint 2 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +gdb_test_no_output "disable" + +if { [gdb_breakpoint ${srcfile}:${bp_location2}] } { + pass "setting breakpoint 2" +} + +gdb_test "cont" "Breakpoint .*:${bp_location2}.*" "Going to second breakpoint" diff --git a/gdb/testsuite/gdb.go/chan.go b/gdb/testsuite/gdb.go/chan.go new file mode 100644 index 0000000..f237288 --- /dev/null +++ b/gdb/testsuite/gdb.go/chan.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func generate() chan int { + ch := make(chan int) + go func() { + for i := 0; ; i++ { + ch <- i // set breakpoint 1 here + } + }() + return ch +} + +func main() { + integers := generate() + for i := 0; i < 100; i++ { // Print the first hundred integers. + fmt.Println(<-integers) // set breakpoint 2 here + } +} diff --git a/gdb/testsuite/gdb.go/handcall.exp b/gdb/testsuite/gdb.go/handcall.exp new file mode 100644 index 0000000..077186e --- /dev/null +++ b/gdb/testsuite/gdb.go/handcall.exp @@ -0,0 +1,45 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test hand-calling go functions. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "handcall" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +gdb_test "print add (1, 2)" " = 3" +gdb_test "print main.add (1, 2)" " = 3" diff --git a/gdb/testsuite/gdb.go/handcall.go b/gdb/testsuite/gdb.go/handcall.go new file mode 100644 index 0000000..f32b5e9 --- /dev/null +++ b/gdb/testsuite/gdb.go/handcall.go @@ -0,0 +1,15 @@ +package main + +func add (a,b int) (int) { + return a + b +} + +func sub (a,b int) (int) { + return a - b +} + +var v_int int + +func main () { + v_int = 42 // set breakpoint 1 here +} diff --git a/gdb/testsuite/gdb.go/hello.exp b/gdb/testsuite/gdb.go/hello.exp new file mode 100644 index 0000000..e6abfb9 --- /dev/null +++ b/gdb/testsuite/gdb.go/hello.exp @@ -0,0 +1,60 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Basic tests. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "hello" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] +set bp_location2 [gdb_get_line_number "set breakpoint 2 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +# This used to print "", i.e., the local "st" initialized as "". +setup_xfail "*-*-*" + +gdb_test "print st" \ + ".* = $hex \"\"" \ + "Starting string check" + +if { [gdb_breakpoint ${srcfile}:${bp_location2}] } { + pass "setting breakpoint 2" +} + +gdb_test "cont" "Breakpoint .*:${bp_location2}.*" "Going to second breakpoint" + +gdb_test "print st" \ + ".* = $hex \"Hello, world!\"" \ + "String after assignment check" diff --git a/gdb/testsuite/gdb.go/hello.go b/gdb/testsuite/gdb.go/hello.go new file mode 100644 index 0000000..6e56112 --- /dev/null +++ b/gdb/testsuite/gdb.go/hello.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +var myst = "Shall we?" + +func main () { + fmt.Println ("Before assignment") // set breakpoint 1 here + st := "Hello, world!" // this intentionally shadows the global "st" + fmt.Println (st) // set breakpoint 2 here + fmt.Println (myst) // set breakpoint 2 here +} diff --git a/gdb/testsuite/gdb.go/integers.exp b/gdb/testsuite/gdb.go/integers.exp new file mode 100644 index 0000000..202c56d --- /dev/null +++ b/gdb/testsuite/gdb.go/integers.exp @@ -0,0 +1,116 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test integer expressions. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "integers" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] +set bp_location2 [gdb_get_line_number "set breakpoint 2 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +gdb_test "print i" ".* = 0" "Print i before assigned to 1" + +gdb_test "next" "i = 1" "Next to 'i = 1' line" +gdb_test "next" "j = 2" "Next to 'j = 2' line" +# At that point, +# i should be equal to 1 +gdb_test "print i" " = 1" +# but j should still be equal to zero +gdb_test "print j" " = 0" "Test j value before assignment" + +gdb_test "next" "k = 3" "Next to 'k = 3' line" +gdb_test "next" "l = k" "Next to 'l = k' line" + +#j should be equal to 2 +gdb_test "print j" " = 2" +# k should be equal to 3 +gdb_test "print k" " = 3" +# But l should still be zero +gdb_test "print l" " = 0" + +# Test addition +gdb_test "print i + j" " = 3" +gdb_test "print i + k" " = 4" +gdb_test "print j + k" " = 5" +gdb_test "print i + j + k" " = 6" + +# Test substraction +gdb_test "print j - i" " = 1" +gdb_test "print i - j" "= -1" +gdb_test "print k -i -j" " = 0" +gdb_test "print k -(i + j)" " = 0" + +# Test unany minus +gdb_test "print -i" " = -1" +gdb_test "print (-i)" " = -1" +gdb_test "print -(i)" " = -1" +gdb_test "print -(i+j)" " = -3" + +# Test boolean operators =, <>, <, <=, > and >= +gdb_test "print i + 1 == j" " = true" +gdb_test "print i + 1 != j" " = false" +gdb_test "print i + 1 < j" " = false" +gdb_test "print i + 1 <= j" " = true" +gdb_test "print i + 1 > j" " = false" +gdb_test "print i + 1 >= j" " = true" + +# Test multiplication +gdb_test "print 2 * i" " = 2" +gdb_test "print j * k" " = 6" +gdb_test "print 3000*i" " = 3000" + +#Test div and mod operators +gdb_test "print 35 / 2" " = 17" +gdb_test "print 35 % 2" " = 1" + +# Test several operators together +gdb_test "print i+10*j+100*k" " = 321" +gdb_test " print (i + 5) * (j + 7)" " = 54" + +gdb_test "set var i = 2" " = 2" +gdb_test "print i" " = 2" "Testing new i value" + +if { [gdb_breakpoint ${srcfile}:${bp_location2}] } { + pass "setting breakpoint 2" +} + +gdb_test "cont" \ + "Breakpoint .*:${bp_location2}.*" \ + "Going to second breakpoint" +gdb_test "print i" \ + ".* = 5.*" \ + "Value of i after assignment" diff --git a/gdb/testsuite/gdb.go/integers.go b/gdb/testsuite/gdb.go/integers.go new file mode 100644 index 0000000..4430099 --- /dev/null +++ b/gdb/testsuite/gdb.go/integers.go @@ -0,0 +1,22 @@ +package main + +var i = 0 +var j = 0 +var k = 0 +var l = 0 + +func main () { + i = 0 + j = 0 + k = 0 + l = 0 // set breakpoint 1 here + i = 1 + j = 2 + k = 3 + l = k + + i = j + k + + j = 0 // set breakpoint 2 here + k = 0 +} diff --git a/gdb/testsuite/gdb.go/methods.exp b/gdb/testsuite/gdb.go/methods.exp new file mode 100644 index 0000000..e63870a --- /dev/null +++ b/gdb/testsuite/gdb.go/methods.exp @@ -0,0 +1,50 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test various aspects of methods. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "methods" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 {main.T.Foo} +set bp_location2 {(*main.T).Bar} +set bp_location2_regexp {\(*main.T\).Bar} + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${bp_location1}] } { + pass "setting breakpoint 1" +} +setup_xfail "*-*-*" ;# mangling issues IIRC +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +if { [gdb_breakpoint ${bp_location2}] } { + pass "setting breakpoint 2" +} +setup_xfail "*-*-*" ;# mangling issues IIRC +gdb_test "cont" "Breakpoint .*:${bp_location2_regexp}.*" "Going to second breakpoint" diff --git a/gdb/testsuite/gdb.go/methods.go b/gdb/testsuite/gdb.go/methods.go new file mode 100644 index 0000000..563d0e4 --- /dev/null +++ b/gdb/testsuite/gdb.go/methods.go @@ -0,0 +1,21 @@ +package main + +import "fmt" + +type T struct { i int } + +func (t T) Foo () { + fmt.Println (t.i) +} + +func (t *T) Bar () { + fmt.Println (t.i) +} + +func main () { + fmt.Println ("Shall we?") + var t T + t.Foo () + var pt = new (T) + pt.Bar () +} diff --git a/gdb/testsuite/gdb.go/package.exp b/gdb/testsuite/gdb.go/package.exp new file mode 100644 index 0000000..80d51cc --- /dev/null +++ b/gdb/testsuite/gdb.go/package.exp @@ -0,0 +1,50 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test various aspects of packages. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "package" +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${testfile}2.go" "${binfile}2.o" object {debug go}] != "" } { + untested $testfile + return -1 +} + +if { [gdb_compile "${srcdir}/${subdir}/${testfile}1.go ${binfile}2.o" "${binfile}" executable "debug go libdir=${objdir}/${subdir}"] != "" } { + untested $testfile + return -1 +} + +clean_restart $testfile + +if { [go_runto_main] < 0 } { + untested methods + return -1 +} + +set bp_location1 {package2.Foo} +set bp_location1_regexp {package2[.]Foo.*package2[.]go:} + +if { [gdb_breakpoint ${bp_location1}] } { + pass "setting breakpoint 1" +} +gdb_test "cont" "Breakpoint .*${bp_location1_regexp}.*" "Going to first breakpoint" diff --git a/gdb/testsuite/gdb.go/package1.go b/gdb/testsuite/gdb.go/package1.go new file mode 100644 index 0000000..ec538ba --- /dev/null +++ b/gdb/testsuite/gdb.go/package1.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "package2" +) + +func main () { + fmt.Println ("Shall we?") + package2.Foo () +} diff --git a/gdb/testsuite/gdb.go/package2.go b/gdb/testsuite/gdb.go/package2.go new file mode 100644 index 0000000..de4c49a --- /dev/null +++ b/gdb/testsuite/gdb.go/package2.go @@ -0,0 +1,7 @@ +package package2 + +import "fmt" + +func Foo () { + fmt.Println ("Hi, I'm package2.Foo.") +} diff --git a/gdb/testsuite/gdb.go/print.exp b/gdb/testsuite/gdb.go/print.exp new file mode 100644 index 0000000..84e6166 --- /dev/null +++ b/gdb/testsuite/gdb.go/print.exp @@ -0,0 +1,70 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. + +# 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/>. + +# Test printing of various values. +# NOTE: The tests here intentionally do not require a go compiler. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +proc test_float_accepted {} { + global gdb_prompt + + # Test parsing of fp value with legit text following. + gdb_test "p 1234.5+1" " = 1235.5" "check fp + text" + + # Test all the suffixes (including no suffix). + gdb_test "p 1." " = 1" + gdb_test "p 1.5" " = 1.5" + gdb_test "p 1.f" " = 1" + gdb_test "p 1.5f" " = 1.5" + gdb_test "p 1.l" " = 1" + gdb_test "p 1.5l" " = 1.5" + + # Test hexadecimal floating point. + set test "p 0x1.1" + gdb_test_multiple $test $test { + -re " = 1\\.0625\r\n$gdb_prompt $" { + pass $test + } + -re "Invalid number \"0x1\\.1\"\\.\r\n$gdb_prompt $" { + # Older glibc does not support hex float, newer does. + xfail $test + } + } +} + +proc test_float_rejected {} { + # Test bad suffixes. + test_print_reject "p 1.1x" + test_print_reject "p 1.1ff" + test_print_reject "p 1.1ll" +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +if [set_lang_go] { + test_float_accepted + test_float_rejected +} else { + warning "Go print tests suppressed" +} diff --git a/gdb/testsuite/gdb.go/strings.exp b/gdb/testsuite/gdb.go/strings.exp new file mode 100644 index 0000000..b1b3fed --- /dev/null +++ b/gdb/testsuite/gdb.go/strings.exp @@ -0,0 +1,42 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "strings" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +gdb_test {print "abc" + "def"} {.* = "abcdef"} diff --git a/gdb/testsuite/gdb.go/strings.go b/gdb/testsuite/gdb.go/strings.go new file mode 100644 index 0000000..fc62e39 --- /dev/null +++ b/gdb/testsuite/gdb.go/strings.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +var v_string string = "foo" + +func main () { + fmt.Println ("hello") // set breakpoint 1 here + fmt.Printf ("%s\n", v_string) +} diff --git a/gdb/testsuite/gdb.go/types.exp b/gdb/testsuite/gdb.go/types.exp new file mode 100644 index 0000000..20ac910 --- /dev/null +++ b/gdb/testsuite/gdb.go/types.exp @@ -0,0 +1,51 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Various experiments with types. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "types" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +# It's not clear yet what GCC will emit. +# Pick something and xfail it until GCC solidifies. +# And we still need to finish go-typeprint.c. + +setup_xfail "*-*-*" +gdb_test "ptype T" "type T *T" + +setup_xfail "*-*-*" +gdb_test "ptype T1" "type T1 *T2" +setup_xfail "*-*-*" +gdb_test "ptype T2" "type T2 *T1" + +setup_xfail "*-*-*" +gdb_test "ptype S1" "type S1 struct {.*p_s2 *S2.*}" +setup_xfail "*-*-*" +gdb_test "ptype S2" "type S2 struct {.*p_s1 *S1.*}" diff --git a/gdb/testsuite/gdb.go/types.go b/gdb/testsuite/gdb.go/types.go new file mode 100644 index 0000000..be9179f --- /dev/null +++ b/gdb/testsuite/gdb.go/types.go @@ -0,0 +1,24 @@ +package main + +import "fmt" + +// Self-referential type. +type T *T + +// Mutually recursive types. +type T1 *T2 +type T2 *T1 + +// Mutually recursive struct types. +type S1 struct { p_s2 *S2 } +type S2 struct { p_s1 *S1 } + +func main () { + fmt.Println ("Shall we?") + var t T + fmt.Println (t) + var s1 S1 + var s2 S2 + fmt.Println (s1) + fmt.Println (s2) +} diff --git a/gdb/testsuite/gdb.go/unsafe.exp b/gdb/testsuite/gdb.go/unsafe.exp new file mode 100644 index 0000000..1535ce2 --- /dev/null +++ b/gdb/testsuite/gdb.go/unsafe.exp @@ -0,0 +1,44 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# Test package "unsafe". + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +set testfile "unsafe" +set srcfile ${testfile}.go + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"] + +if { [go_runto_main] < 0 } { + untested $testfile + return -1 +} + +if { [gdb_breakpoint ${srcfile}:${bp_location1}] } { + pass "setting breakpoint 1" +} + +gdb_test "cont" "Breakpoint .*:${bp_location1}.*" "Going to first breakpoint" + +gdb_test "print unsafe.Sizeof(42)" ".* = 4" diff --git a/gdb/testsuite/gdb.go/unsafe.go b/gdb/testsuite/gdb.go/unsafe.go new file mode 100644 index 0000000..95318bd --- /dev/null +++ b/gdb/testsuite/gdb.go/unsafe.go @@ -0,0 +1,11 @@ +package main + +import ("fmt" + "unsafe") + +var mystring = "Shall we?" + +func main () { + fmt.Printf ("%d\n", unsafe.Sizeof (42)) // set breakpoint 1 here + fmt.Printf ("%d\n", unsafe.Sizeof (mystring)) +} diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp index 4e26dcb..40456c0 100644 --- a/gdb/testsuite/lib/future.exp +++ b/gdb/testsuite/lib/future.exp @@ -60,6 +60,30 @@ proc gdb_find_gfortran {} { return $CC } +proc gdb_find_go {} { + global tool_root_dir + + set GO "" + + if {![is_remote host]} { + set file [lookfor_file $tool_root_dir gccgo] + if { $file != "" } { + set root [file dirname $file] + set GO "$file -B$root/gcc/" + } + } + + if { $GO == "" } { + set GO [transform gccgo] + } + + return $GO +} + +proc gdb_find_go_linker {} { + return [find_go] +} + proc gdb_default_target_compile {source destfile type options} { global target_triplet global tool_root_dir @@ -74,6 +98,11 @@ proc gdb_default_target_compile {source destfile type options} { set libs "" set compiler_type "c" set compiler "" + set linker "" + # linker_opts_order is one of "sources-then-flags", "flags-then-sources". + # The order shouldn't matter. It's done this way to preserve + # existing behavior. + set linker_opts_order "sources-then-flags" set ldflags "" set dest [target_info name] @@ -138,6 +167,26 @@ proc gdb_default_target_compile {source destfile type options} { } } + if { $i == "go" } { + set compiler_type "go" + if {[board_info $dest exists goflags]} { + append add_flags " [target_info goflags]" + } + if {[board_info $dest exists gocompiler]} { + set compiler [target_info gocompiler] + } else { + set compiler [find_go] + } + if {[board_info $dest exists golinker]} { + set linker [target_info golinker] + } else { + set linker [find_go_linker] + } + if {[board_info $dest exists golinker_opts_order]} { + set linker_opts_order [target_info golinker_opts_order] + } + } + if {[regexp "^dest=" $i]} { regsub "^dest=" $i "" tmp if {[board_info $tmp exists name]} { @@ -193,6 +242,8 @@ proc gdb_default_target_compile {source destfile type options} { global F77_FOR_TARGET global F90_FOR_TARGET global GNATMAKE_FOR_TARGET + global GO_FOR_TARGET + global GO_LD_FOR_TARGET if {[info exists GNATMAKE_FOR_TARGET]} { if { $compiler_type == "ada" } { @@ -224,6 +275,19 @@ proc gdb_default_target_compile {source destfile type options} { } } + if { $compiler_type == "go" } { + if {[info exists GO_FOR_TARGET]} { + set compiler $GO_FOR_TARGET + } + if {[info exists GO_LD_FOR_TARGET]} { + set linker $GO_LD_FOR_TARGET + } + } + + if { $type == "executable" && $linker != "" } { + set compiler $linker + } + if { $compiler == "" } { set compiler [board_info $dest compiler] if { $compiler == "" } { @@ -366,10 +430,26 @@ proc gdb_default_target_compile {source destfile type options} { # This is obscure: we put SOURCES at the end when building an # object, because otherwise, in some situations, libtool will # become confused about the name of the actual source file. - if {$type == "object"} { - set opts "$add_flags $sources" - } else { - set opts "$sources $add_flags" + switch $type { + "object" { + set opts "$add_flags $sources" + } + "executable" { + switch $linker_opts_order { + "flags-then-sources" { + set opts "$add_flags $sources" + } + "sources-then-flags" { + set opts "$sources $add_flags" + } + default { + error "Invalid value for board_info linker_opts_order" + } + } + } + default { + set opts "$sources $add_flags" + } } if {[is_remote host]} { @@ -431,6 +511,12 @@ if {[info procs find_gfortran] == ""} { set use_gdb_compile 1 } +if {[info procs find_go_linker] == ""} { + rename gdb_find_go find_go + rename gdb_find_go_linker find_go_linker + # No need to set use_gdb_compile. +} + if {$use_gdb_compile} { catch {rename default_target_compile {}} rename gdb_default_target_compile default_target_compile diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index ccf5e98..83faf59 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -1440,6 +1440,12 @@ proc skip_ada_tests {} { return 0 } +# Return a 1 if I don't even want to try to test GO. + +proc skip_go_tests {} { + return 0 +} + # Return a 1 if I don't even want to try to test java. proc skip_java_tests {} { diff --git a/gdb/testsuite/lib/go.exp b/gdb/testsuite/lib/go.exp new file mode 100644 index 0000000..e8466bb --- /dev/null +++ b/gdb/testsuite/lib/go.exp @@ -0,0 +1,37 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# 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/>. + +# FIXME: Presumably skip_go_tests should be defined here, +# but for consistency with other languages it currently lives in gdb.exp. + +# Auxiliary function to set the language to Go. +# The result is 1 (true) for success, 0 (false) for failure. + +proc set_lang_go {} { + if [gdb_test_no_output "set language go"] { + return 0 + } + if [gdb_test "show language" ".* source language is \"go\"." \ + "set language to \"go\""] { + return 0 + } + return 1 +} + +# Go version of runto_main. + +proc go_runto_main { } { + return [runto main.main] +} |