Initial pass at Go language support.
authorDoug Evans <dje@google.com>
Wed, 25 Apr 2012 14:07:23 +0000 (14:07 +0000)
committerDoug Evans <dje@google.com>
Wed, 25 Apr 2012 14:07:23 +0000 (14:07 +0000)
* 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.

42 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/defs.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/dwarf2read.c
gdb/go-exp.y [new file with mode: 0644]
gdb/go-lang.c [new file with mode: 0644]
gdb/go-lang.h [new file with mode: 0644]
gdb/go-typeprint.c [new file with mode: 0644]
gdb/go-valprint.c [new file with mode: 0644]
gdb/symtab.c
gdb/testsuite/ChangeLog
gdb/testsuite/configure
gdb/testsuite/configure.ac
gdb/testsuite/gdb.base/default.exp
gdb/testsuite/gdb.go/Makefile.in [new file with mode: 0644]
gdb/testsuite/gdb.go/basic-types.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/chan.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/chan.go [new file with mode: 0644]
gdb/testsuite/gdb.go/handcall.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/handcall.go [new file with mode: 0644]
gdb/testsuite/gdb.go/hello.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/hello.go [new file with mode: 0644]
gdb/testsuite/gdb.go/integers.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/integers.go [new file with mode: 0644]
gdb/testsuite/gdb.go/methods.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/methods.go [new file with mode: 0644]
gdb/testsuite/gdb.go/package.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/package1.go [new file with mode: 0644]
gdb/testsuite/gdb.go/package2.go [new file with mode: 0644]
gdb/testsuite/gdb.go/print.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/strings.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/strings.go [new file with mode: 0644]
gdb/testsuite/gdb.go/types.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/types.go [new file with mode: 0644]
gdb/testsuite/gdb.go/unsafe.exp [new file with mode: 0644]
gdb/testsuite/gdb.go/unsafe.go [new file with mode: 0644]
gdb/testsuite/lib/future.exp
gdb/testsuite/lib/gdb.exp
gdb/testsuite/lib/go.exp [new file with mode: 0644]

index 43d590f4c3e9313b0c7e9aacf655e0d17b9a92de..df4bacfc490d6abb6f640341485917dd77d2181f 100644 (file)
@@ -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
index 0e87eeb4ac022b0ef7be24a81b447226bc86deaa..fbe5b53a19b18e5470d08b3934445301226aa49c 100644 (file)
@@ -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
index 72ce6b8954ab7322c658a14caaa55a779f358093..777fa434c5e5d8b19a811799b4a6f0b3aedab389 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
   ** 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
 
index f7156cb6132f029113b6e93d649878d892532c02..4d2d2a8a1aa8041c23bcc14a8807bfec6801abcf 100644 (file)
@@ -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 */
index 92085c96237b0d810a5c7650f2dbed1d8c09156c..0f41571887a62ef7511f1a685662cb30c97830e5 100644 (file)
@@ -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'.
index a2a4eb3754955ec3c7a1c9227638207ffbfba028..d4d2f60ef1c3e91ff6a59a202372dafb27549b00 100644 (file)
@@ -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
 
index c737d41ac077ccfba79e31749d0685b95cd7943f..bd2d6f21d3c446d8df7482638bcee910e50bb6a3 100644 (file)
@@ -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 (file)
index 0000000..f720629
--- /dev/null
@@ -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 '.' '[' '('
+
+\f
+%%
+
+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 (file)
index 0000000..147a922
--- /dev/null
@@ -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 (file)
index 0000000..67b5d93
--- /dev/null
@@ -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 (file)
index 0000000..a76c3dc
--- /dev/null
@@ -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 (file)
index 0000000..3be4927
--- /dev/null
@@ -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;
+    }
+}
index af115cdb12840b3bc458e4aea5c0bfc7bb0b3d3c..6c70113e5b91d1660e5512f76b3fae1ee67b18f9 100644 (file)
@@ -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)
     {
index 8c1fd30b71b07c6df0d8a253a25af11711986bea..6c2ca581cb5609e8acb1e6d686219c16cd044a7a 100644 (file)
@@ -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'.
index fb70b3df9bcba18a945e651141f0cef9a5758765..0c8c3447d2937ba7d926b0b5933fad9f2efd1051 100755 (executable)
@@ -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" ;;
index 5735805d5597f642ecd1b2ad3b1690fc286a8697..d9ab9f766f99643531d0558638d275ba6b3d190f 100644 (file)
@@ -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 \
index c656a203a8967c2aa5e4beac0fa8c09894bbcb78..e2859d516b0c574ad8319e70214c9cbe8f543bec 100644 (file)
@@ -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 (file)
index 0000000..dfce2d6
--- /dev/null
@@ -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 (file)
index 0000000..d3a542d
--- /dev/null
@@ -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 (file)
index 0000000..317e214
--- /dev/null
@@ -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 (file)
index 0000000..f237288
--- /dev/null
@@ -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 (file)
index 0000000..077186e
--- /dev/null
@@ -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 (file)
index 0000000..f32b5e9
--- /dev/null
@@ -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 (file)
index 0000000..e6abfb9
--- /dev/null
@@ -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 (file)
index 0000000..6e56112
--- /dev/null
@@ -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 (file)
index 0000000..202c56d
--- /dev/null
@@ -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 (file)
index 0000000..4430099
--- /dev/null
@@ -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 (file)
index 0000000..e63870a
--- /dev/null
@@ -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 (file)
index 0000000..563d0e4
--- /dev/null
@@ -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 (file)
index 0000000..80d51cc
--- /dev/null
@@ -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 (file)
index 0000000..ec538ba
--- /dev/null
@@ -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 (file)
index 0000000..de4c49a
--- /dev/null
@@ -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 (file)
index 0000000..84e6166
--- /dev/null
@@ -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 (file)
index 0000000..b1b3fed
--- /dev/null
@@ -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 (file)
index 0000000..fc62e39
--- /dev/null
@@ -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 (file)
index 0000000..20ac910
--- /dev/null
@@ -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 (file)
index 0000000..be9179f
--- /dev/null
@@ -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 (file)
index 0000000..1535ce2
--- /dev/null
@@ -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 (file)
index 0000000..95318bd
--- /dev/null
@@ -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))
+}
index 4e26dcb0aa16c73e3a28a3b57acefdac237fb99b..40456c01a7310f3db389c9fb4cac8c86942e3524 100644 (file)
@@ -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
index ccf5e987dfb7bc8ff304c45006598728d641e70b..83faf5970bd4a0e155dad71a34980f7be5e6d5e9 100644 (file)
@@ -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 (file)
index 0000000..e8466bb
--- /dev/null
@@ -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]
+}