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