aboutsummaryrefslogtreecommitdiff
path: root/gdb/guile
diff options
context:
space:
mode:
authorDoug Evans <xdje42@gmail.com>2014-02-09 19:40:01 -0800
committerDoug Evans <xdje42@gmail.com>2014-02-09 19:40:01 -0800
commited3ef33944c39d9a3cea72b9a7cef3c20f0e3461 (patch)
tree4e67d95b8ea65bb36a9cade5e37df2ad6289052e /gdb/guile
parent7026a7c16ee82d39e84823f8cc3097a9a940ddb2 (diff)
downloadgdb-ed3ef33944c39d9a3cea72b9a7cef3c20f0e3461.zip
gdb-ed3ef33944c39d9a3cea72b9a7cef3c20f0e3461.tar.gz
gdb-ed3ef33944c39d9a3cea72b9a7cef3c20f0e3461.tar.bz2
Add Guile as an extension language.
* NEWS: Mention Guile scripting. * Makefile.in (SUBDIR_GUILE_OBS): New variable. (SUBDIR_GUILE_SRCS, SUBDIR_GUILE_DEPS): New variables (SUBDIR_GUILE_LDFLAGS, SUBDIR_GUILE_CFLAGS): New variables. (INTERNAL_CPPFLAGS): Add GUILE_CPPFLAGS. (CLIBS): Add GUILE_LIBS. (install-guile): New rule. (guile.o): New rule. (scm-arch.o, scm-auto-load.o, scm-block.o): New rules. (scm-breakpoint.o, scm-disasm.o, scm-exception.o): New rules. (scm-frame.o, scm-iterator.o, scm-lazy-string.o): New rules. (scm-math.o, scm-objfile.o, scm-ports.o): New rules. (scm-pretty-print.o, scm-safe-call.o, scm-gsmob.o): New rules. (scm-string.o, scm-symbol.o, scm-symtab.o): New rules. (scm-type.o, scm-utils.o, scm-value.o): New rules. * configure.ac: New option --with-guile. * configure: Regenerate. * config.in: Regenerate. * auto-load.c: Remove #include "python/python.h". Add #include "gdb/section-scripts.h". (source_section_scripts): Handle Guile scripts. (_initialize_auto_load): Add name of Guile objfile script to scripts-directory help text. * breakpoint.c (condition_command): Tweak comment to include Scheme. * breakpoint.h (gdbscm_breakpoint_object): Add forward decl. (struct breakpoint): New member scm_bp_object. * defs.h (enum command_control_type): New value guile_control. * cli/cli-cmds.c: Remove #include "python/python.h". Add #include "extension.h". (show_user): Update comment. (_initialize_cli_cmds): Update help text for "show user". Update help text for max-user-call-depth. * cli/cli-script.c: Remove #include "python/python.h". Add #include "extension.h". (multi_line_command_p): Add guile_control. (print_command_lines): Handle guile_control. (execute_control_command, recurse_read_control_structure): Ditto. (process_next_line): Recognize "guile" commands. * disasm.c (gdb_disassemble_info): Make non-static. * disasm.h: #include "dis-asm.h". (struct gdbarch): Add forward decl. (gdb_disassemble_info): Declare. * extension.c: #include "guile/guile.h". (extension_languages): Add guile. (get_ext_lang_defn): Handle EXT_LANG_GDB. * extension.h (enum extension_language): New value EXT_LANG_GUILE. * gdbtypes.c (get_unsigned_type_max): New function. (get_signed_type_minmax): New function. * gdbtypes.h (get_unsigned_type_max): Declare. (get_signed_type_minmax): Declare. * guile/README: New file. * guile/guile-internal.h: New file. * guile/guile.c: New file. * guile/guile.h: New file. * guile/scm-arch.c: New file. * guile/scm-auto-load.c: New file. * guile/scm-block.c: New file. * guile/scm-breakpoint.c: New file. * guile/scm-disasm.c: New file. * guile/scm-exception.c: New file. * guile/scm-frame.c: New file. * guile/scm-gsmob.c: New file. * guile/scm-iterator.c: New file. * guile/scm-lazy-string.c: New file. * guile/scm-math.c: New file. * guile/scm-objfile.c: New file. * guile/scm-ports.c: New file. * guile/scm-pretty-print.c: New file. * guile/scm-safe-call.c: New file. * guile/scm-string.c: New file. * guile/scm-symbol.c: New file. * guile/scm-symtab.c: New file. * guile/scm-type.c: New file. * guile/scm-utils.c: New file. * guile/scm-value.c: New file. * guile/lib/gdb.scm: New file. * guile/lib/gdb/boot.scm: New file. * guile/lib/gdb/experimental.scm: New file. * guile/lib/gdb/init.scm: New file. * guile/lib/gdb/iterator.scm: New file. * guile/lib/gdb/printing.scm: New file. * guile/lib/gdb/types.scm: New file. * data-directory/Makefile.in (GUILE_SRCDIR): New variable. (VPATH): Add $(GUILE_SRCDIR). (GUILE_DIR): New variable. (GUILE_INSTALL_DIR, GUILE_FILES): New variables. (all): Add stamp-guile dependency. (stamp-guile): New rule. (clean-guile, install-guile, uninstall-guile): New rules. (install-only): Add install-guile dependency. (uninstall): Add uninstall-guile dependency. (clean): Add clean-guile dependency. doc/ * Makefile.in (GDB_DOC_FILES): Add guile.texi. * gdb.texinfo (Auto-loading): Add set/show auto-load guile-scripts. (Extending GDB): New menu entries Guile, Multiple Extension Languages. (Guile docs): Include guile.texi. (objfile-gdbdotext file): Add objfile-gdb.scm. (dotdebug_gdb_scripts section): Mention Guile scripts. (Multiple Extension Languages): New node. * guile.texi: New file. testsuite/ * configure.ac (AC_OUTPUT): Add gdb.guile. * configure: Regenerate. * lib/gdb-guile.exp: New file. * lib/gdb.exp (get_target_charset): New function. * gdb.base/help.exp: Update expected output from "apropos apropos". * gdb.guile/Makefile.in: New file. * gdb.guile/guile.exp: New file. * gdb.guile/scm-arch.c: New file. * gdb.guile/scm-arch.exp: New file. * gdb.guile/scm-block.c: New file. * gdb.guile/scm-block.exp: New file. * gdb.guile/scm-breakpoint.c: New file. * gdb.guile/scm-breakpoint.exp: New file. * gdb.guile/scm-disasm.c: New file. * gdb.guile/scm-disasm.exp: New file. * gdb.guile/scm-equal.c: New file. * gdb.guile/scm-equal.exp: New file. * gdb.guile/scm-error.exp: New file. * gdb.guile/scm-error.scm: New file. * gdb.guile/scm-frame-args.c: New file. * gdb.guile/scm-frame-args.exp: New file. * gdb.guile/scm-frame-args.scm: New file. * gdb.guile/scm-frame-inline.c: New file. * gdb.guile/scm-frame-inline.exp: New file. * gdb.guile/scm-frame.c: New file. * gdb.guile/scm-frame.exp: New file. * gdb.guile/scm-generics.exp: New file. * gdb.guile/scm-gsmob.exp: New file. * gdb.guile/scm-iterator.c: New file. * gdb.guile/scm-iterator.exp: New file. * gdb.guile/scm-math.c: New file. * gdb.guile/scm-math.exp: New file. * gdb.guile/scm-objfile-script-gdb.in: New file. * gdb.guile/scm-objfile-script.c: New file. * gdb.guile/scm-objfile-script.exp: New file. * gdb.guile/scm-objfile.c: New file. * gdb.guile/scm-objfile.exp: New file. * gdb.guile/scm-ports.exp: New file. * gdb.guile/scm-pretty-print.c: New file. * gdb.guile/scm-pretty-print.exp: New file. * gdb.guile/scm-pretty-print.scm: New file. * gdb.guile/scm-section-script.c: New file. * gdb.guile/scm-section-script.exp: New file. * gdb.guile/scm-section-script.scm: New file. * gdb.guile/scm-symbol.c: New file. * gdb.guile/scm-symbol.exp: New file. * gdb.guile/scm-symtab-2.c: New file. * gdb.guile/scm-symtab.c: New file. * gdb.guile/scm-symtab.exp: New file. * gdb.guile/scm-type.c: New file. * gdb.guile/scm-type.exp: New file. * gdb.guile/scm-value-cc.cc: New file. * gdb.guile/scm-value-cc.exp: New file. * gdb.guile/scm-value.c: New file. * gdb.guile/scm-value.exp: New file. * gdb.guile/source2.scm: New file. * gdb.guile/types-module.cc: New file. * gdb.guile/types-module.exp: New file.
Diffstat (limited to 'gdb/guile')
-rw-r--r--gdb/guile/README229
-rw-r--r--gdb/guile/guile-internal.h567
-rw-r--r--gdb/guile/guile.c724
-rw-r--r--gdb/guile/guile.h28
-rw-r--r--gdb/guile/lib/gdb.scm452
-rw-r--r--gdb/guile/lib/gdb/boot.scm31
-rw-r--r--gdb/guile/lib/gdb/experimental.scm35
-rw-r--r--gdb/guile/lib/gdb/init.scm173
-rw-r--r--gdb/guile/lib/gdb/iterator.scm80
-rw-r--r--gdb/guile/lib/gdb/printing.scm52
-rw-r--r--gdb/guile/lib/gdb/types.scm78
-rw-r--r--gdb/guile/scm-arch.c668
-rw-r--r--gdb/guile/scm-auto-load.c81
-rw-r--r--gdb/guile/scm-block.c828
-rw-r--r--gdb/guile/scm-breakpoint.c1200
-rw-r--r--gdb/guile/scm-disasm.c355
-rw-r--r--gdb/guile/scm-exception.c691
-rw-r--r--gdb/guile/scm-frame.c1077
-rw-r--r--gdb/guile/scm-gsmob.c486
-rw-r--r--gdb/guile/scm-iterator.c375
-rw-r--r--gdb/guile/scm-lazy-string.c373
-rw-r--r--gdb/guile/scm-math.c998
-rw-r--r--gdb/guile/scm-objfile.c413
-rw-r--r--gdb/guile/scm-ports.c1372
-rw-r--r--gdb/guile/scm-pretty-print.c1138
-rw-r--r--gdb/guile/scm-safe-call.c464
-rw-r--r--gdb/guile/scm-string.c246
-rw-r--r--gdb/guile/scm-symbol.c777
-rw-r--r--gdb/guile/scm-symtab.c735
-rw-r--r--gdb/guile/scm-type.c1495
-rw-r--r--gdb/guile/scm-utils.c585
-rw-r--r--gdb/guile/scm-value.c1485
32 files changed, 18291 insertions, 0 deletions
diff --git a/gdb/guile/README b/gdb/guile/README
new file mode 100644
index 0000000..81306e5
--- /dev/null
+++ b/gdb/guile/README
@@ -0,0 +1,229 @@
+README for gdb/guile
+====================
+
+This file contains important notes for gdb/guile developers.
+["gdb/guile" refers to the directory you found this file in]
+
+Nomenclature:
+
+ In the implementation we use "Scheme" or "Guile" depending on context.
+ And sometimes it doesn't matter.
+ Guile is Scheme, and for the most part this is what we present to the user
+ as well. However, to highlight the fact that it is Guile, the GDB commands
+ that invoke Scheme functions are named "guile" and "guile-repl",
+ abbreviated "gu" and "gr" respectively.
+
+Co-existence with Python:
+
+ Keep the user interfaces reasonably consistent, but don't shy away from
+ providing a clearer (or more Scheme-friendly/consistent) user interface
+ where appropriate.
+
+ Additions to Python support or Scheme support don't require corresponding
+ changes in the other scripting language.
+
+ Scheme-wrapped breakpoints are created lazily so that if the user
+ doesn't use Scheme s/he doesn't pay any cost.
+
+Importing the gdb module into Scheme:
+
+ To import the gdb module:
+ (gdb) guile (use-modules (gdb))
+
+ If you want to add a prefix to gdb module symbols:
+ (gdb) guile (use-modules ((gdb) #:renamer (symbol-prefix-proc 'gdb:)))
+ This gives every symbol a "gdb:" prefix which is a common convention.
+ OTOH it's more to type.
+
+Implementation/Hacking notes:
+
+ Don't use scm_is_false.
+ For this C function, () == #f (a la Lisp) and it's not clear how treating
+ them as equivalent for truth values will affect the GDB interface.
+ Until the effect is clear avoid them.
+ Instead use gdbscm_is_false, gdbscm_is_true, gdbscm_is_bool.
+ There are macros in guile-internal.h to enforce this.
+
+ Use gdbscm_foo as the name of functions that implement Scheme procedures
+ to provide consistent naming in error messages. The user can see "gdbscm"
+ in the name and immediately know where the function came from.
+
+ All smobs contain gdb_smob or chained_gdb_smob as the first member.
+ This provides a mechanism for extending them in the Scheme side without
+ tying GDB to the details.
+
+ The lifetime of a smob, AIUI, is decided by the containing SCM.
+ When there is no longer a reference to the containing SCM then the
+ smob can be GC'd. Objects that have references from outside of Scheme,
+ e.g., breakpoints, need to be protected from GC.
+
+ Don't do something that can cause a Scheme exception inside a TRY_CATCH,
+ and, in code that can be called from Scheme, don't do something that can
+ cause a GDB exception outside a TRY_CATCH.
+ This makes the code a little tricky to write sometimes, but it is a
+ rule imposed by the programming environment. Bugs often happen because
+ this rule is broken. Learn it, follow it.
+
+Coding style notes:
+
+ - If you find violations to these rules, let's fix the code.
+ Some attempt has been made to be consistent, but it's early.
+ Over time we want things to be more consistent, not less.
+
+ - None of this really needs to be read. Instead, do not be creative:
+ Monkey-See-Monkey-Do hacking should generally Just Work.
+
+ - Absence of the word "typically" means the rule is reasonably strict.
+
+ - The gdbscm_initialize_foo function (e.g., gdbscm_initialize_values)
+ is the last thing to appear in the file, immediately preceded by any
+ tables of exported variables and functions.
+
+ - In addition to these of course, follow GDB coding conventions.
+
+General naming rules:
+
+ - The word "object" absent any modifier (like "GOOPS object") means a
+ Scheme object (of any type), and is never used otherwise.
+ If you want to refer to, e.g., a GOOPS object, say "GOOPS object".
+
+ - Do not begin any function, global variable, etc. name with scm_.
+ That's what the Guile implementation uses.
+ (kinda obvious, just being complete).
+
+ - The word "invalid" carries a specific connotation. Try not to use it
+ in a different way. It means the underlying GDB object has disappeared.
+ For example, a <gdb:objfile> smob becomes "invalid" when the underlying
+ objfile is removed from GDB.
+
+ - We typically use the word "exception" to mean Scheme exceptions,
+ and we typically use the word "error" to mean GDB errors.
+
+Comments:
+
+ - function comments for functions implementing Scheme procedures begin with
+ a description of the Scheme usage. Example:
+ /* (gsmob-aux gsmob) -> object */
+
+ - the following comment appears after the copyright header:
+ /* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+Smob naming:
+
+ - gdb smobs are named, internally, "gdb:foo"
+ - in Guile they become <gdb:foo>, that is the convention for naming classes
+ and smobs have rudimentary GOOPS support (they can't be inherited from,
+ but generics can work with them)
+ - in comments use the Guile naming for smobs,
+ i.e., <gdb:foo> instead of gdb:foo.
+ Note: This only applies to smobs. Exceptions are also named gdb:foo,
+ but since they are not "classes" they are not wrapped in <>.
+ - smob names are stored in a global, and for simplicity we pass this
+ global as the "expected type" parameter to SCM_ASSERT_TYPE, thus in
+ this instance smob types are printed without the <>.
+ [Hmmm, this rule seems dated now. Plus I18N rules in GDB are not always
+ clear, sometimes we pass the smob name through _(), however it's not
+ clear that's actually a good idea.]
+
+Type naming:
+
+ - smob structs are typedefs named foo_smob
+
+Variable naming:
+
+ - "scm" by itself is reserved for arbitrary Scheme objects
+
+ - variables that are pointers to smob structs are named <char>_smob or
+ <char><char>_smob, e.g., f_smob for a pointer to a frame smob
+
+ - variables that are gdb smob objects are typically named <char>_scm or
+ <char><char>_scm, e.g., f_scm for a <gdb:frame> object
+
+ - the name of the first argument for method-like functions is "self"
+
+Function naming:
+
+ General:
+
+ - all non-static functions have a prefix,
+ either gdbscm_ or <char><char>scm_ [or <char><char><char>scm_]
+
+ - all functions that implement Scheme procedures have a gdbscm_ prefix,
+ this is for consistency and readability of Scheme exception text
+
+ - static functions typically have a prefix
+ - the prefix is typically <char><char>scm_ where the first two letters
+ are unique to the file or class the function works with.
+ E.g., the scm-arch.c prefix is arscm_.
+ This follows something used in gdb/python in some places,
+ we make it formal.
+
+ - if the function is of a general nature, or no other prefix works,
+ use gdbscm_
+
+ Conversion functions:
+
+ - the from/to in function names follows from libguile's existing style
+ - conversions from/to Scheme objects are named:
+ prefix_scm_from_foo: converts from foo to scm
+ prefix_scm_to_foo: converts from scm to foo
+
+ Exception handling:
+
+ - functions that may throw a Scheme exception have an _unsafe suffix
+ - This does not apply to functions that implement Scheme procedures.
+ - This does not apply to functions whose explicit job is to throw
+ an exception. Adding _unsafe to gdbscm_throw is kinda superfluous. :-)
+ - functions that can throw a GDB error aren't adorned with _unsafe
+
+ - "_safe" in a function name means it will never throw an exception
+ - Generally unnecessary, since the convention is to mark the ones that
+ *can* throw an exception. But sometimes it's useful to highlight the
+ fact that the function is safe to call without worrying about exception
+ handling.
+
+ - except for functions that implement Scheme procedures, all functions
+ that can throw exceptions (GDB or Scheme) say so in their function comment
+
+ - functions that don't throw an exception, but still need to indicate to
+ the caller that one happened (i.e., "safe" functions), either return
+ a <gdb:exception> smob as a result or pass it back via a parameter.
+ For this reason don't pass back <gdb:exception> smobs for any other
+ reason. There are functions that explicitly construct <gdb:exception>
+ smobs. They're obviously the, umm, exception.
+
+ Internal functions:
+
+ - internal Scheme functions begin with "%" and are intentionally undocumented
+ in the manual
+
+ Standard Guile/Scheme conventions:
+
+ - predicates that return Scheme values have the suffix _p and have suffix "?"
+ in the Scheme procedure's name
+ - functions that implement Scheme procedures that modify state have the
+ suffix _x and have suffix "!" in the Scheme procedure's name
+ - object predicates that return a C truth value are named prefix_is_foo
+ - functions that set something have "set" at the front (except for a prefix)
+ write this: gdbscm_set_gsmob_aux_x implements (set-gsmob-aux! ...)
+ not this: gdbscm_gsmob_set_aux_x implements (gsmob-set-aux! ...)
+
+Doc strings:
+
+ - there are lots of existing examples, they should be pretty consistent,
+ use them as boilerplate/examples
+ - begin with a one line summary (can be multiple lines if necessary)
+ - if the arguments need description:
+ - blank line
+ - " Arguments: arg1 arg2"
+ " arg1: blah ..."
+ " arg2: blah ..."
+ - if the result requires more description:
+ - blank line
+ - " Returns:"
+ " Blah ..."
+ - if it's important to list exceptions that can be thrown:
+ - blank line
+ - " Throws:"
+ " exception-name: blah ..."
diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h
new file mode 100644
index 0000000..dcdd422
--- /dev/null
+++ b/gdb/guile/guile-internal.h
@@ -0,0 +1,567 @@
+/* Internal header for GDB/Scheme code.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#ifndef GDB_GUILE_INTERNAL_H
+#define GDB_GUILE_INTERNAL_H
+
+#include "hashtab.h"
+#include "extension-priv.h"
+#include "symtab.h"
+#include "libguile.h"
+
+struct block;
+struct frame_info;
+struct objfile;
+struct symbol;
+
+/* A function to pass to the safe-call routines to ignore things like
+ memory errors. */
+typedef int excp_matcher_func (SCM key);
+
+/* Scheme variables to define during initialization. */
+
+typedef struct
+{
+ const char *name;
+ SCM value;
+ const char *doc_string;
+} scheme_variable;
+
+/* End of scheme_variable table mark. */
+
+#define END_VARIABLES { NULL, SCM_BOOL_F, NULL }
+
+/* Scheme functions to define during initialization. */
+
+typedef struct
+{
+ const char *name;
+ int required;
+ int optional;
+ int rest;
+ scm_t_subr func;
+ const char *doc_string;
+} scheme_function;
+
+/* End of scheme_function table mark. */
+
+#define END_FUNCTIONS { NULL, 0, 0, 0, NULL, NULL }
+
+/* Useful for defining a set of constants. */
+
+typedef struct
+{
+ const char *name;
+ int value;
+} scheme_integer_constant;
+
+#define END_INTEGER_CONSTANTS { NULL, 0 }
+
+/* Pass this instead of 0 to routines like SCM_ASSERT to indicate the value
+ is not a function argument. */
+#define GDBSCM_ARG_NONE 0
+
+/* Ensure new code doesn't accidentally try to use this. */
+#undef scm_make_smob_type
+#define scm_make_smob_type USE_gdbscm_make_smob_type_INSTEAD
+
+/* They brought over () == #f from lisp.
+ Let's avoid that for now. */
+#undef scm_is_bool
+#undef scm_is_false
+#undef scm_is_true
+#define scm_is_bool USE_gdbscm_is_bool_INSTEAD
+#define scm_is_false USE_gdbscm_is_false_INSTEAD
+#define scm_is_true USE_gdbscm_is_true_INSTEAD
+#define gdbscm_is_bool(scm) \
+ (scm_is_eq ((scm), SCM_BOOL_F) || scm_is_eq ((scm), SCM_BOOL_T))
+#define gdbscm_is_false(scm) scm_is_eq ((scm), SCM_BOOL_F)
+#define gdbscm_is_true(scm) (!gdbscm_is_false (scm))
+
+/* Function name that is passed around in case an error needs to be reported.
+ __func is in C99, but we provide a wrapper "just in case",
+ and because FUNC_NAME is the canonical value used in guile sources.
+ IWBN to use the Scheme version of the name (e.g. foo-bar vs foo_bar),
+ but let's KISS for now. */
+#define FUNC_NAME __func__
+
+extern const char gdbscm_module_name[];
+extern const char gdbscm_init_module_name[];
+
+extern int gdb_scheme_initialized;
+
+extern const char gdbscm_print_excp_none[];
+extern const char gdbscm_print_excp_full[];
+extern const char gdbscm_print_excp_message[];
+extern const char *gdbscm_print_excp;
+
+extern SCM gdbscm_documentation_symbol;
+extern SCM gdbscm_invalid_object_error_symbol;
+
+extern SCM gdbscm_map_string;
+extern SCM gdbscm_array_string;
+extern SCM gdbscm_string_string;
+
+/* scm-utils.c */
+
+extern void gdbscm_define_variables (const scheme_variable *, int public);
+
+extern void gdbscm_define_functions (const scheme_function *, int public);
+
+extern void gdbscm_define_integer_constants (const scheme_integer_constant *,
+ int public);
+
+extern void gdbscm_printf (SCM port, const char *format, ...);
+
+extern void gdbscm_debug_display (SCM obj);
+
+extern void gdbscm_debug_write (SCM obj);
+
+extern void gdbscm_parse_function_args (const char *function_name,
+ int beginning_arg_pos,
+ const SCM *keywords,
+ const char *format, ...);
+
+extern SCM gdbscm_scm_from_longest (LONGEST l);
+
+extern LONGEST gdbscm_scm_to_longest (SCM l);
+
+extern SCM gdbscm_scm_from_ulongest (ULONGEST l);
+
+extern ULONGEST gdbscm_scm_to_ulongest (SCM u);
+
+extern void gdbscm_dynwind_xfree (void *ptr);
+
+extern int gdbscm_is_procedure (SCM proc);
+
+/* GDB smobs, from scm-smob.c */
+
+/* All gdb smobs must contain one of the following as the first member:
+ gdb_smob, chained_gdb_smob, or eqable_gdb_smob.
+
+ The next,prev members of chained_gdb_smob allow for chaining gsmobs
+ together so that, for example, when an objfile is deleted we can clean up
+ all smobs that reference it.
+
+ The containing_scm member of eqable_gdb_smob allows for returning the
+ same gsmob instead of creating a new one, allowing them to be eq?-able.
+
+ IMPORTANT: chained_gdb_smob and eqable_gdb-smob are a "subclasses" of
+ gdb_smob. The layout of chained_gdb_smob,eqable_gdb_smob must match
+ gdb_smob as if it is a subclass. To that end we use macro GDB_SMOB_HEAD
+ to ensure this. */
+
+#define GDB_SMOB_HEAD \
+ /* Property list for externally added fields. */ \
+ SCM properties;
+
+typedef struct
+{
+ GDB_SMOB_HEAD
+} gdb_smob;
+
+typedef struct _chained_gdb_smob
+{
+ GDB_SMOB_HEAD
+
+ struct _chained_gdb_smob *prev;
+ struct _chained_gdb_smob *next;
+} chained_gdb_smob;
+
+typedef struct _eqable_gdb_smob
+{
+ GDB_SMOB_HEAD
+
+ /* The object we are contained in.
+ This can be used for several purposes.
+ This is used by the eq? machinery: We need to be able to see if we have
+ already created an object for a symbol, and if so use that SCM.
+ This may also be used to protect the smob from GC if there is
+ a reference to this smob from outside of GC space (i.e., from gdb).
+ This can also be used in place of chained_gdb_smob where we need to
+ keep track of objfile referencing objects. When the objfile is deleted
+ we need to invalidate the objects: we can do that using the same hashtab
+ used to record the smob for eq-ability. */
+ SCM containing_scm;
+} eqable_gdb_smob;
+
+#undef GDB_SMOB_HEAD
+
+struct objfile;
+struct objfile_data;
+
+/* A predicate that returns non-zero if an object is a particular kind
+ of gsmob. */
+typedef int (gsmob_pred_func) (SCM);
+
+extern scm_t_bits gdbscm_make_smob_type (const char *name, size_t size);
+
+extern void gdbscm_init_gsmob (gdb_smob *base);
+
+extern void gdbscm_init_chained_gsmob (chained_gdb_smob *base);
+
+extern void gdbscm_init_eqable_gsmob (eqable_gdb_smob *base);
+
+extern SCM gdbscm_mark_gsmob (gdb_smob *base);
+
+extern SCM gdbscm_mark_chained_gsmob (chained_gdb_smob *base);
+
+extern SCM gdbscm_mark_eqable_gsmob (eqable_gdb_smob *base);
+
+extern void gdbscm_add_objfile_ref (struct objfile *objfile,
+ const struct objfile_data *data_key,
+ chained_gdb_smob *g_smob);
+
+extern void gdbscm_remove_objfile_ref (struct objfile *objfile,
+ const struct objfile_data *data_key,
+ chained_gdb_smob *g_smob);
+
+extern htab_t gdbscm_create_eqable_gsmob_ptr_map (htab_hash hash_fn,
+ htab_eq eq_fn);
+
+extern eqable_gdb_smob **gdbscm_find_eqable_gsmob_ptr_slot
+ (htab_t htab, eqable_gdb_smob *base);
+
+extern void gdbscm_fill_eqable_gsmob_ptr_slot (eqable_gdb_smob **slot,
+ eqable_gdb_smob *base,
+ SCM containing_scm);
+
+extern void gdbscm_clear_eqable_gsmob_ptr_slot (htab_t htab,
+ eqable_gdb_smob *base);
+
+/* Exceptions and calling out to Guile. */
+
+/* scm-exception.c */
+
+extern SCM gdbscm_make_exception (SCM tag, SCM args);
+
+extern int gdbscm_is_exception (SCM scm);
+
+extern SCM gdbscm_exception_key (SCM excp);
+
+extern SCM gdbscm_exception_args (SCM excp);
+
+extern SCM gdbscm_make_exception_with_stack (SCM key, SCM args, SCM stack);
+
+extern SCM gdbscm_make_error_scm (SCM key, SCM subr, SCM message,
+ SCM args, SCM data);
+
+extern SCM gdbscm_make_error (SCM key, const char *subr, const char *message,
+ SCM args, SCM data);
+
+extern SCM gdbscm_make_type_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *expected_type);
+
+extern SCM gdbscm_make_invalid_object_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *error);
+
+extern SCM gdbscm_invalid_object_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *error)
+ ATTRIBUTE_NORETURN;
+
+extern SCM gdbscm_make_out_of_range_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *error);
+
+extern SCM gdbscm_out_of_range_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *error)
+ ATTRIBUTE_NORETURN;
+
+extern SCM gdbscm_make_misc_error (const char *subr, int arg_pos,
+ SCM bad_value, const char *error);
+
+extern void gdbscm_throw (SCM exception) ATTRIBUTE_NORETURN;
+
+extern SCM gdbscm_scm_from_gdb_exception (struct gdb_exception exception);
+
+extern void gdbscm_throw_gdb_exception (struct gdb_exception exception)
+ ATTRIBUTE_NORETURN;
+
+extern void gdbscm_print_exception_with_stack (SCM port, SCM stack,
+ SCM key, SCM args);
+
+extern void gdbscm_print_gdb_exception (SCM port, SCM exception);
+
+extern char *gdbscm_exception_message_to_string (SCM exception);
+
+extern excp_matcher_func gdbscm_memory_error_p;
+
+extern SCM gdbscm_make_memory_error (const char *subr, const char *msg,
+ SCM args);
+
+extern SCM gdbscm_memory_error (const char *subr, const char *msg, SCM args);
+
+/* scm-safe-call.c */
+
+extern void *gdbscm_with_guile (void *(*func) (void *), void *data);
+
+extern SCM gdbscm_call_guile (SCM (*func) (void *), void *data,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_call_0 (SCM proc, excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_call_1 (SCM proc, SCM arg0,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_call_2 (SCM proc, SCM arg0, SCM arg1,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_call_3 (SCM proc, SCM arg0, SCM arg1, SCM arg2,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_call_4 (SCM proc, SCM arg0, SCM arg1, SCM arg2,
+ SCM arg3,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_safe_apply_1 (SCM proc, SCM arg0, SCM args,
+ excp_matcher_func *ok_excps);
+
+extern SCM gdbscm_unsafe_call_1 (SCM proc, SCM arg0);
+
+extern char *gdbscm_safe_eval_string (const char *string, int display_result);
+
+extern char *gdbscm_safe_source_script (const char *filename);
+
+extern void gdbscm_enter_repl (void);
+
+/* Interface to various GDB objects, in alphabetical order. */
+
+/* scm-arch.c */
+
+typedef struct _arch_smob arch_smob;
+
+extern struct gdbarch *arscm_get_gdbarch (arch_smob *a_smob);
+
+extern arch_smob *arscm_get_arch_smob_arg_unsafe (SCM arch_scm, int arg_pos,
+ const char *func_name);
+
+extern SCM arscm_scm_from_arch (struct gdbarch *gdbarch);
+
+/* scm-block.c */
+
+extern SCM bkscm_scm_from_block (const struct block *block,
+ struct objfile *objfile);
+
+extern const struct block *bkscm_scm_to_block
+ (SCM block_scm, int arg_pos, const char *func_name, SCM *excp);
+
+/* scm-frame.c */
+
+typedef struct _frame_smob frame_smob;
+
+extern int frscm_is_frame (SCM scm);
+
+extern frame_smob *frscm_get_frame_smob_arg_unsafe (SCM frame_scm, int arg_pos,
+ const char *func_name);
+
+extern struct frame_info *frscm_frame_smob_to_frame (frame_smob *);
+
+/* scm-iterator.c */
+
+typedef struct _iterator_smob iterator_smob;
+
+extern SCM itscm_iterator_smob_object (iterator_smob *i_smob);
+
+extern SCM itscm_iterator_smob_progress (iterator_smob *i_smob);
+
+extern void itscm_set_iterator_smob_progress_x (iterator_smob *i_smob,
+ SCM progress);
+
+extern const char *itscm_iterator_smob_name (void);
+
+extern SCM gdbscm_make_iterator (SCM object, SCM progress, SCM next);
+
+extern int itscm_is_iterator (SCM scm);
+
+extern SCM gdbscm_end_of_iteration (void);
+
+extern int itscm_is_end_of_iteration (SCM obj);
+
+extern SCM itscm_safe_call_next_x (SCM iter, excp_matcher_func *ok_excps);
+
+extern SCM itscm_get_iterator_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name);
+
+/* scm-lazy-string.c */
+
+extern int lsscm_is_lazy_string (SCM scm);
+
+extern SCM lsscm_make_lazy_string (CORE_ADDR address, int length,
+ const char *encoding, struct type *type);
+
+extern struct value *lsscm_safe_lazy_string_to_value (SCM string,
+ int arg_pos,
+ const char *func_name,
+ SCM *except_scmp);
+
+extern void lsscm_val_print_lazy_string
+ (SCM string, struct ui_file *stream,
+ const struct value_print_options *options);
+
+/* scm-objfile.c */
+
+typedef struct _objfile_smob objfile_smob;
+
+extern SCM ofscm_objfile_smob_pretty_printers (objfile_smob *o_smob);
+
+extern objfile_smob *ofscm_objfile_smob_from_objfile (struct objfile *objfile);
+
+extern SCM ofscm_scm_from_objfile (struct objfile *objfile);
+
+/* scm-string.c */
+
+extern char *gdbscm_scm_to_c_string (SCM string);
+
+extern SCM gdbscm_scm_from_c_string (const char *string);
+
+extern SCM gdbscm_scm_from_printf (const char *format, ...);
+
+extern char *gdbscm_scm_to_string (SCM string, size_t *lenp,
+ const char *charset,
+ int strict, SCM *except_scmp);
+
+extern SCM gdbscm_scm_from_string (const char *string, size_t len,
+ const char *charset, int strict);
+
+extern char *gdbscm_scm_to_target_string_unsafe (SCM string, size_t *lenp,
+ struct gdbarch *gdbarch);
+
+/* scm-symbol.c */
+
+extern int syscm_is_symbol (SCM scm);
+
+extern SCM syscm_scm_from_symbol (struct symbol *symbol);
+
+extern struct symbol *syscm_get_valid_symbol_arg_unsafe
+ (SCM self, int arg_pos, const char *func_name);
+
+/* scm-symtab.c */
+
+extern SCM stscm_scm_from_symtab (struct symtab *symtab);
+
+extern SCM stscm_scm_from_sal (struct symtab_and_line sal);
+
+/* scm-type.c */
+
+typedef struct _type_smob type_smob;
+
+extern int tyscm_is_type (SCM scm);
+
+extern SCM tyscm_scm_from_type (struct type *type);
+
+extern type_smob *tyscm_get_type_smob_arg_unsafe (SCM type_scm, int arg_pos,
+ const char *func_name);
+
+extern struct type *tyscm_type_smob_type (type_smob *t_smob);
+
+extern SCM tyscm_scm_from_field (SCM type_scm, int field_num);
+
+/* scm-value.c */
+
+extern struct value *vlscm_scm_to_value (SCM scm);
+
+extern int vlscm_is_value (SCM scm);
+
+extern SCM vlscm_scm_from_value (struct value *value);
+
+extern SCM vlscm_scm_from_value_unsafe (struct value *value);
+
+extern struct value *vlscm_convert_typed_value_from_scheme
+ (const char *func_name, int obj_arg_pos, SCM obj,
+ int type_arg_pos, SCM type_scm, struct type *type, SCM *except_scmp,
+ struct gdbarch *gdbarch, const struct language_defn *language);
+
+extern struct value *vlscm_convert_value_from_scheme
+ (const char *func_name, int obj_arg_pos, SCM obj, SCM *except_scmp,
+ struct gdbarch *gdbarch, const struct language_defn *language);
+
+/* stript_lang methods */
+
+extern objfile_script_sourcer_func gdbscm_source_objfile_script;
+
+extern int gdbscm_auto_load_enabled (const struct extension_language_defn *);
+
+extern void gdbscm_preserve_values
+ (const struct extension_language_defn *,
+ struct objfile *, htab_t copied_types);
+
+extern enum ext_lang_rc gdbscm_apply_val_pretty_printer
+ (const struct extension_language_defn *,
+ 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,
+ const struct language_defn *language);
+
+extern int gdbscm_breakpoint_has_cond (const struct extension_language_defn *,
+ struct breakpoint *b);
+
+extern enum ext_lang_bp_stop gdbscm_breakpoint_cond_says_stop
+ (const struct extension_language_defn *, struct breakpoint *b);
+
+/* Initializers for each piece of Scheme support, in alphabetical order. */
+
+extern void gdbscm_initialize_arches (void);
+extern void gdbscm_initialize_auto_load (void);
+extern void gdbscm_initialize_blocks (void);
+extern void gdbscm_initialize_breakpoints (void);
+extern void gdbscm_initialize_disasm (void);
+extern void gdbscm_initialize_exceptions (void);
+extern void gdbscm_initialize_frames (void);
+extern void gdbscm_initialize_iterators (void);
+extern void gdbscm_initialize_lazy_strings (void);
+extern void gdbscm_initialize_math (void);
+extern void gdbscm_initialize_objfiles (void);
+extern void gdbscm_initialize_pretty_printers (void);
+extern void gdbscm_initialize_ports (void);
+extern void gdbscm_initialize_smobs (void);
+extern void gdbscm_initialize_strings (void);
+extern void gdbscm_initialize_symbols (void);
+extern void gdbscm_initialize_symtabs (void);
+extern void gdbscm_initialize_types (void);
+extern void gdbscm_initialize_values (void);
+
+/* Use these after a TRY_CATCH to throw the appropriate Scheme exception
+ if a GDB error occurred. */
+
+#define GDBSCM_HANDLE_GDB_EXCEPTION(exception) \
+ do { \
+ if (exception.reason < 0) \
+ { \
+ gdbscm_throw_gdb_exception (exception); \
+ /*NOTREACHED */ \
+ } \
+ } while (0)
+
+/* If cleanups are establish outside the TRY_CATCH block, use this version. */
+
+#define GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS(exception, cleanups) \
+ do { \
+ if (exception.reason < 0) \
+ { \
+ do_cleanups (cleanups); \
+ gdbscm_throw_gdb_exception (exception); \
+ /*NOTREACHED */ \
+ } \
+ } while (0)
+
+#endif /* GDB_GUILE_INTERNAL_H */
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
new file mode 100644
index 0000000..b7134f7
--- /dev/null
+++ b/gdb/guile/guile.c
@@ -0,0 +1,724 @@
+/* General GDB/Guile code.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include <string.h>
+#include "breakpoint.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-script.h"
+#include "cli/cli-utils.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "interps.h"
+#include "extension-priv.h"
+#include "utils.h"
+#include "version.h"
+#ifdef HAVE_GUILE
+#include "guile.h"
+#include "guile-internal.h"
+#endif
+
+/* Declared constants and enum for guile exception printing. */
+const char gdbscm_print_excp_none[] = "none";
+const char gdbscm_print_excp_full[] = "full";
+const char gdbscm_print_excp_message[] = "message";
+
+/* "set guile print-stack" choices. */
+static const char *const guile_print_excp_enums[] =
+ {
+ gdbscm_print_excp_none,
+ gdbscm_print_excp_full,
+ gdbscm_print_excp_message,
+ NULL
+ };
+
+/* The exception printing variable. 'full' if we want to print the
+ error message and stack, 'none' if we want to print nothing, and
+ 'message' if we only want to print the error message. 'message' is
+ the default. */
+const char *gdbscm_print_excp = gdbscm_print_excp_message;
+
+#ifdef HAVE_GUILE
+/* Forward decls, these are defined later. */
+static const struct extension_language_script_ops guile_extension_script_ops;
+static const struct extension_language_ops guile_extension_ops;
+#endif
+
+/* The main struct describing GDB's interface to the Guile
+ extension language. */
+const struct extension_language_defn extension_language_guile =
+{
+ EXT_LANG_GUILE,
+ "guile",
+ "Guile",
+
+ ".scm",
+ "-gdb.scm",
+
+ guile_control,
+
+#ifdef HAVE_GUILE
+ &guile_extension_script_ops,
+ &guile_extension_ops
+#else
+ NULL,
+ NULL
+#endif
+};
+
+#ifdef HAVE_GUILE
+
+static void gdbscm_finish_initialization
+ (const struct extension_language_defn *);
+static int gdbscm_initialized (const struct extension_language_defn *);
+static void gdbscm_eval_from_control_command
+ (const struct extension_language_defn *, struct command_line *);
+static script_sourcer_func gdbscm_source_script;
+
+int gdb_scheme_initialized;
+
+/* Symbol for setting documentation strings. */
+SCM gdbscm_documentation_symbol;
+
+/* Keywords used by various functions. */
+static SCM from_tty_keyword;
+static SCM to_string_keyword;
+
+/* The name of the various modules (without the surrounding parens). */
+const char gdbscm_module_name[] = "gdb";
+const char gdbscm_init_module_name[] = "gdb init";
+
+/* The name of the bootstrap file. */
+static const char boot_scm_filename[] = "boot.scm";
+
+/* The interface between gdb proper and loading of python scripts. */
+
+static const struct extension_language_script_ops guile_extension_script_ops =
+{
+ gdbscm_source_script,
+ gdbscm_source_objfile_script,
+ gdbscm_auto_load_enabled
+};
+
+/* The interface between gdb proper and guile scripting. */
+
+static const struct extension_language_ops guile_extension_ops =
+{
+ gdbscm_finish_initialization,
+ gdbscm_initialized,
+
+ gdbscm_eval_from_control_command,
+
+ NULL, /* gdbscm_start_type_printers, */
+ NULL, /* gdbscm_apply_type_printers, */
+ NULL, /* gdbscm_free_type_printers, */
+
+ gdbscm_apply_val_pretty_printer,
+
+ NULL, /* gdbscm_apply_frame_filter, */
+
+ gdbscm_preserve_values,
+
+ gdbscm_breakpoint_has_cond,
+ gdbscm_breakpoint_cond_says_stop,
+
+ NULL, /* gdbscm_check_quit_flag, */
+ NULL, /* gdbscm_clear_quit_flag, */
+ NULL, /* gdbscm_set_quit_flag, */
+};
+
+/* Implementation of the gdb "guile-repl" command. */
+
+static void
+guile_repl_command (char *arg, int from_tty)
+{
+ struct cleanup *cleanup;
+
+ cleanup = make_cleanup_restore_integer (&interpreter_async);
+ interpreter_async = 0;
+
+ arg = skip_spaces (arg);
+
+ /* This explicitly rejects any arguments for now.
+ "It is easier to relax a restriction than impose one after the fact."
+ We would *like* to be able to pass arguments to the interactive shell
+ but that's not what python-interactive does. Until there is time to
+ sort it out, we forbid arguments. */
+
+ if (arg && *arg)
+ error (_("guile-repl currently does not take any arguments."));
+ else
+ {
+ dont_repeat ();
+ gdbscm_enter_repl ();
+ }
+
+ do_cleanups (cleanup);
+}
+
+/* Implementation of the gdb "guile" command.
+ Note: Contrary to the Python version this displays the result.
+ Have to see which is better.
+
+ TODO: Add the result to Guile's history? */
+
+static void
+guile_command (char *arg, int from_tty)
+{
+ struct cleanup *cleanup;
+
+ cleanup = make_cleanup_restore_integer (&interpreter_async);
+ interpreter_async = 0;
+
+ arg = skip_spaces (arg);
+
+ if (arg && *arg)
+ {
+ char *msg = gdbscm_safe_eval_string (arg, 1);
+
+ if (msg != NULL)
+ {
+ make_cleanup (xfree, msg);
+ error ("%s", msg);
+ }
+ }
+ else
+ {
+ struct command_line *l = get_command_line (guile_control, "");
+
+ make_cleanup_free_command_lines (&l);
+ execute_control_command_untraced (l);
+ }
+
+ do_cleanups (cleanup);
+}
+
+/* Given a command_line, return a command string suitable for passing
+ to Guile. Lines in the string are separated by newlines. The return
+ value is allocated using xmalloc and the caller is responsible for
+ freeing it. */
+
+static char *
+compute_scheme_string (struct command_line *l)
+{
+ struct command_line *iter;
+ char *script = NULL;
+ int size = 0;
+ int here;
+
+ for (iter = l; iter; iter = iter->next)
+ size += strlen (iter->line) + 1;
+
+ script = xmalloc (size + 1);
+ here = 0;
+ for (iter = l; iter; iter = iter->next)
+ {
+ int len = strlen (iter->line);
+
+ strcpy (&script[here], iter->line);
+ here += len;
+ script[here++] = '\n';
+ }
+ script[here] = '\0';
+ return script;
+}
+
+/* Take a command line structure representing a "guile" command, and
+ evaluate its body using the Guile interpreter.
+ This is the extension_language_ops.eval_from_control_command "method". */
+
+static void
+gdbscm_eval_from_control_command
+ (const struct extension_language_defn *extlang, struct command_line *cmd)
+{
+ char *script, *msg;
+ struct cleanup *cleanup;
+
+ if (cmd->body_count != 1)
+ error (_("Invalid \"guile\" block structure."));
+
+ cleanup = make_cleanup (null_cleanup, NULL);
+
+ script = compute_scheme_string (cmd->body_list[0]);
+ msg = gdbscm_safe_eval_string (script, 0);
+ xfree (script);
+ if (msg != NULL)
+ {
+ make_cleanup (xfree, msg);
+ error ("%s", msg);
+ }
+
+ do_cleanups (cleanup);
+}
+
+/* Read a file as Scheme code.
+ This is the extension_language_script_ops.script_sourcer "method".
+ FILE is the file to run. FILENAME is name of the file FILE.
+ This does not throw any errors. If an exception occurs an error message
+ is printed. */
+
+static void
+gdbscm_source_script (const struct extension_language_defn *extlang,
+ FILE *file, const char *filename)
+{
+ char *msg = gdbscm_safe_source_script (filename);
+
+ if (msg != NULL)
+ {
+ fprintf_filtered (gdb_stderr, "%s\n", msg);
+ xfree (msg);
+ }
+}
+
+/* (execute string [#:from-tty boolean] [#:to-string boolean\
+ A Scheme function which evaluates a string using the gdb CLI. */
+
+static SCM
+gdbscm_execute_gdb_command (SCM command_scm, SCM rest)
+{
+ int from_tty_arg_pos = -1, to_string_arg_pos = -1;
+ int from_tty = 0, to_string = 0;
+ volatile struct gdb_exception except;
+ const SCM keywords[] = { from_tty_keyword, to_string_keyword, SCM_BOOL_F };
+ char *command;
+ char *result = NULL;
+ struct cleanup *cleanups;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#tt",
+ command_scm, &command, rest,
+ &from_tty_arg_pos, &from_tty,
+ &to_string_arg_pos, &to_string);
+
+ /* Note: The contents of "command" may get modified while it is
+ executed. */
+ cleanups = make_cleanup (xfree, command);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *inner_cleanups;
+
+ inner_cleanups = make_cleanup_restore_integer (&interpreter_async);
+ interpreter_async = 0;
+
+ prevent_dont_repeat ();
+ if (to_string)
+ result = execute_command_to_string (command, from_tty);
+ else
+ {
+ execute_command (command, from_tty);
+ result = NULL;
+ }
+
+ /* Do any commands attached to breakpoint we stopped at. */
+ bpstat_do_actions ();
+
+ do_cleanups (inner_cleanups);
+ }
+ do_cleanups (cleanups);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (result)
+ {
+ SCM r = gdbscm_scm_from_c_string (result);
+ xfree (result);
+ return r;
+ }
+ return SCM_UNSPECIFIED;
+}
+
+/* (data-directory) -> string */
+
+static SCM
+gdbscm_data_directory (void)
+{
+ return gdbscm_scm_from_c_string (gdb_datadir);
+}
+
+/* (gdb-version) -> string */
+
+static SCM
+gdbscm_gdb_version (void)
+{
+ return gdbscm_scm_from_c_string (version);
+}
+
+/* (host-config) -> string */
+
+static SCM
+gdbscm_host_config (void)
+{
+ return gdbscm_scm_from_c_string (host_name);
+}
+
+/* (target-config) -> string */
+
+static SCM
+gdbscm_target_config (void)
+{
+ return gdbscm_scm_from_c_string (target_name);
+}
+
+#else /* ! HAVE_GUILE */
+
+/* Dummy implementation of the gdb "guile-repl" and "guile"
+ commands. */
+
+static void
+guile_repl_command (char *arg, int from_tty)
+{
+ arg = skip_spaces (arg);
+ if (arg && *arg)
+ error (_("guile-repl currently does not take any arguments."));
+ error (_("Guile scripting is not supported in this copy of GDB."));
+}
+
+static void
+guile_command (char *arg, int from_tty)
+{
+ arg = skip_spaces (arg);
+ if (arg && *arg)
+ error (_("Guile scripting is not supported in this copy of GDB."));
+ else
+ {
+ /* Even if Guile isn't enabled, we still have to slurp the
+ command list to the corresponding "end". */
+ struct command_line *l = get_command_line (guile_control, "");
+ struct cleanup *cleanups = make_cleanup_free_command_lines (&l);
+
+ execute_control_command_untraced (l);
+ do_cleanups (cleanups);
+ }
+}
+
+#endif /* ! HAVE_GUILE */
+
+/* Lists for 'set,show,info guile' commands. */
+
+static struct cmd_list_element *set_guile_list;
+static struct cmd_list_element *show_guile_list;
+static struct cmd_list_element *info_guile_list;
+
+/* Function for use by 'set guile' prefix command. */
+
+static void
+set_guile_command (char *args, int from_tty)
+{
+ help_list (set_guile_list, "set guile ", all_commands, gdb_stdout);
+}
+
+/* Function for use by 'show guile' prefix command. */
+
+static void
+show_guile_command (char *args, int from_tty)
+{
+ cmd_show_list (show_guile_list, from_tty, "");
+}
+
+/* The "info scheme" command is defined as a prefix, with
+ allow_unknown 0. Therefore, its own definition is called only for
+ "info scheme" with no args. */
+
+static void
+info_guile_command (char *args, int from_tty)
+{
+ printf_unfiltered (_("\"info guile\" must be followed"
+ " by the name of an info command.\n"));
+ help_list (info_guile_list, "info guile ", -1, gdb_stdout);
+}
+
+/* Initialization. */
+
+#ifdef HAVE_GUILE
+
+static const scheme_function misc_guile_functions[] =
+{
+ { "execute", 1, 0, 1, gdbscm_execute_gdb_command,
+ "\
+Execute the given GDB command.\n\
+\n\
+ Arguments: string [#:to-string boolean] [#:from-tty boolean]\n\
+ If #:from-tty is true then the command executes as if entered\n\
+ from the keyboard. The default is false (#f).\n\
+ If #:to-string is true then the result is returned as a string.\n\
+ Otherwise output is sent to the current output port,\n\
+ which is the default.\n\
+ Returns: The result of the command if #:to-string is true.\n\
+ Otherwise returns unspecified." },
+
+ { "data-directory", 0, 0, 0, gdbscm_data_directory,
+ "\
+Return the name of GDB's data directory." },
+
+ { "gdb-version", 0, 0, 0, gdbscm_gdb_version,
+ "\
+Return GDB's version string." },
+
+ { "host-config", 0, 0, 0, gdbscm_host_config,
+ "\
+Return the name of the host configuration." },
+
+ { "target-config", 0, 0, 0, gdbscm_target_config,
+ "\
+Return the name of the target configuration." },
+
+ END_FUNCTIONS
+};
+
+/* Load gdb/boot.scm, the Scheme side of GDB/Guile support.
+ Note: This function assumes it's called within the gdb module. */
+
+static void
+initialize_scheme_side (void)
+{
+ char *gdb_guile_dir = concat (gdb_datadir, SLASH_STRING, "guile", NULL);
+ char *boot_scm_path = concat (gdb_guile_dir, SLASH_STRING, "gdb",
+ SLASH_STRING, boot_scm_filename, NULL);
+ char *msg;
+
+ /* While scm_c_primitive_load works, the loaded code is not compiled,
+ instead it is left to be interpreted. Eh?
+ Anyways, this causes a ~100x slowdown, so we only use it to load
+ gdb/boot.scm, and then let boot.scm do the rest. */
+ msg = gdbscm_safe_source_script (boot_scm_path);
+
+ if (msg != NULL)
+ {
+ fprintf_filtered (gdb_stderr, "%s", msg);
+ xfree (msg);
+ warning (_("\n"
+ "Could not complete Guile gdb module initialization from:\n"
+ "%s.\n"
+ "Limited Guile support is available.\n"
+ "Suggest passing --data-directory=/path/to/gdb/data-directory.\n"),
+ boot_scm_path);
+ }
+
+ xfree (gdb_guile_dir);
+ xfree (boot_scm_path);
+}
+
+/* Install the gdb scheme module.
+ The result is a boolean indicating success.
+ If initializing the gdb module fails an error message is printed.
+ Note: This function runs in the context of the gdb module. */
+
+static void
+initialize_gdb_module (void *data)
+{
+ /* The documentation symbol needs to be defined before any calls to
+ gdbscm_define_{variables,functions}. */
+ gdbscm_documentation_symbol = scm_from_latin1_symbol ("documentation");
+
+ /* The smob and exception support must be initialized early. */
+ gdbscm_initialize_smobs ();
+ gdbscm_initialize_exceptions ();
+
+ /* The rest are initialized in alphabetical order. */
+ gdbscm_initialize_arches ();
+ gdbscm_initialize_auto_load ();
+ gdbscm_initialize_blocks ();
+ gdbscm_initialize_breakpoints ();
+ gdbscm_initialize_disasm ();
+ gdbscm_initialize_frames ();
+ gdbscm_initialize_iterators ();
+ gdbscm_initialize_lazy_strings ();
+ gdbscm_initialize_math ();
+ gdbscm_initialize_objfiles ();
+ gdbscm_initialize_ports ();
+ gdbscm_initialize_pretty_printers ();
+ gdbscm_initialize_strings ();
+ gdbscm_initialize_symbols ();
+ gdbscm_initialize_symtabs ();
+ gdbscm_initialize_types ();
+ gdbscm_initialize_values ();
+
+ gdbscm_define_functions (misc_guile_functions, 1);
+
+ from_tty_keyword = scm_from_latin1_keyword ("from-tty");
+ to_string_keyword = scm_from_latin1_keyword ("to-string");
+
+ initialize_scheme_side ();
+
+ gdb_scheme_initialized = 1;
+}
+
+/* A callback to finish Guile initialization after gdb has finished all its
+ initialization.
+ This is the extension_language_ops.finish_initialization "method". */
+
+static void
+gdbscm_finish_initialization (const struct extension_language_defn *extlang)
+{
+ /* Restore the environment to the user interaction one. */
+ scm_set_current_module (scm_interaction_environment ());
+}
+
+/* The extension_language_ops.initialized "method". */
+
+static int
+gdbscm_initialized (const struct extension_language_defn *extlang)
+{
+ return gdb_scheme_initialized;
+}
+
+/* Enable or disable Guile backtraces. */
+
+static void
+gdbscm_set_backtrace (int enable)
+{
+ static const char disable_bt[] = "(debug-disable 'backtrace)";
+ static const char enable_bt[] = "(debug-enable 'backtrace)";
+
+ if (enable)
+ gdbscm_safe_eval_string (enable_bt, 0);
+ else
+ gdbscm_safe_eval_string (disable_bt, 0);
+}
+
+#endif /* HAVE_GUILE */
+
+/* Install the various gdb commands used by Guile. */
+
+static void
+install_gdb_commands (void)
+{
+ add_com ("guile-repl", class_obscure,
+ guile_repl_command,
+#ifdef HAVE_GUILE
+ _("\
+Start an interactive Guile prompt.\n\
+\n\
+To return to GDB, type the EOF character (e.g., Ctrl-D on an empty\n\
+prompt) or ,quit.")
+#else /* HAVE_GUILE */
+ _("\
+Start a Guile interactive prompt.\n\
+\n\
+Guile scripting is not supported in this copy of GDB.\n\
+This command is only a placeholder.")
+#endif /* HAVE_GUILE */
+ );
+ add_com_alias ("gr", "guile-repl", class_obscure, 1);
+
+ /* Since "help guile" is easy to type, and intuitive, we add general help
+ in using GDB+Guile to this command. */
+ add_com ("guile", class_obscure, guile_command,
+#ifdef HAVE_GUILE
+ _("\
+Evaluate one or more Guile expressions.\n\
+\n\
+The expression(s) can be given as an argument, for instance:\n\
+\n\
+ guile (display 23)\n\
+\n\
+The result of evaluating the last expression is printed.\n\
+\n\
+If no argument is given, the following lines are read and passed\n\
+to Guile for evaluation. Type a line containing \"end\" to indicate\n\
+the end of the set of expressions.\n\
+\n\
+The Guile GDB module must first be imported before it can be used.\n\
+Do this with:\n\
+(gdb) guile (use-modules (gdb))\n\
+or if you want to import the (gdb) module with a prefix, use:\n\
+(gdb) guile (use-modules ((gdb) #:renamer (symbol-prefix-proc 'gdb:)))\n\
+\n\
+The Guile interactive session, started with the \"guile-repl\"\n\
+command, provides extensive help and apropos capabilities.\n\
+Type \",help\" once in a Guile interactive session.")
+#else /* HAVE_GUILE */
+ _("\
+Evaluate a Guile expression.\n\
+\n\
+Guile scripting is not supported in this copy of GDB.\n\
+This command is only a placeholder.")
+#endif /* HAVE_GUILE */
+ );
+ add_com_alias ("gu", "guile", class_obscure, 1);
+
+ add_prefix_cmd ("guile", class_obscure, set_guile_command,
+ _("Prefix command for Guile preference settings."),
+ &set_guile_list, "set guile ", 0,
+ &setlist);
+ add_alias_cmd ("gu", "guile", class_obscure, 1, &setlist);
+
+ add_prefix_cmd ("guile", class_obscure, show_guile_command,
+ _("Prefix command for Guile preference settings."),
+ &show_guile_list, "show guile ", 0,
+ &showlist);
+ add_alias_cmd ("gu", "guile", class_obscure, 1, &showlist);
+
+ add_prefix_cmd ("guile", class_obscure, info_guile_command,
+ _("Prefix command for Guile info displays."),
+ &info_guile_list, "info guile ", 0,
+ &infolist);
+ add_info_alias ("gu", "guile", 1);
+
+ /* The name "print-stack" is carried over from Python.
+ A better name is "print-exception". */
+ add_setshow_enum_cmd ("print-stack", no_class, guile_print_excp_enums,
+ &gdbscm_print_excp, _("\
+Set mode for Guile exception printing on error."), _("\
+Show the mode of Guile exception printing on error."), _("\
+none == no stack or message will be printed.\n\
+full == a message and a stack will be printed.\n\
+message == an error message without a stack will be printed."),
+ NULL, NULL,
+ &set_guile_list, &show_guile_list);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_guile;
+
+void
+_initialize_guile (void)
+{
+ char *msg;
+
+ install_gdb_commands ();
+
+#if HAVE_GUILE
+ /* The Guile docs say scm_init_guile isn't as portable as the other Guile
+ initialization routines. However, this is the easiest to use.
+ We can switch to a more portable routine if/when the need arises
+ and if it can be used with gdb. */
+ scm_init_guile ();
+
+ /* The Python support puts the C side in module "_gdb", leaving the Python
+ side to define module "gdb" which imports "_gdb". There is evidently no
+ similar convention in Guile so we skip this. */
+
+ /* The rest of the initialization is done by initialize_gdb_module.
+ scm_c_define_module is used as it allows us to perform the initialization
+ within the desired module. */
+ scm_c_define_module (gdbscm_module_name, initialize_gdb_module, NULL);
+
+ /* Set Guile's backtrace to match the "set guile print-stack" default.
+ [N.B. The two settings are still separate.]
+ But only do this after we've initialized Guile, it's nice to see a
+ backtrace if there's an error during initialization.
+ OTOH, if the error is that gdb/init.scm wasn't found because gdb is being
+ run from the build tree, the backtrace is more noise than signal.
+ Sigh. */
+ gdbscm_set_backtrace (0);
+#endif
+}
diff --git a/gdb/guile/guile.h b/gdb/guile/guile.h
new file mode 100644
index 0000000..333047d
--- /dev/null
+++ b/gdb/guile/guile.h
@@ -0,0 +1,28 @@
+/* General GDB/Scheme code.
+
+ Copyright (C) 2014 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/>. */
+
+#ifndef GDB_GUILE_H
+#define GDB_GUILE_H
+
+#include "extension.h"
+
+/* This is all that guile exports to gdb. */
+extern const struct extension_language_defn extension_language_guile;
+
+#endif /* GDB_GUILE_H */
diff --git a/gdb/guile/lib/gdb.scm b/gdb/guile/lib/gdb.scm
new file mode 100644
index 0000000..f12769e
--- /dev/null
+++ b/gdb/guile/lib/gdb.scm
@@ -0,0 +1,452 @@
+;; Scheme side of the gdb module.
+;;
+;; Copyright (C) 2014 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 loaded with scm_c_primitive_load, which is ok, but files
+;; loaded with it are not compiled. So we do very little here, and do
+;; most of the initialization in init.scm.
+
+(define-module (gdb)
+ ;; The version of the (gdb) module as (major minor).
+ ;; Incompatible changes bump the major version.
+ ;; Other changes bump the minor version.
+ ;; It's not clear whether we need a patch-level as well, but this can
+ ;; be added later if necessary.
+ ;; This is not the GDB version on purpose. This version tracks the Scheme
+ ;; gdb module version.
+ ;; TODO: Change to (1 0) when ready.
+ #:version (0 1))
+
+;; Export the bits provided by the C side.
+;; This is so that the compiler can see the exports when
+;; other code uses this module.
+;; TODO: Generating this list would be nice, but it would require an addition
+;; to the GDB build system. Still, I think it's worth it.
+
+(export
+
+ ;; guile.c
+
+ execute
+ data-directory
+ gdb-version
+ host-config
+ target-config
+
+ ;; scm-arch.c
+
+ arch?
+ current-arch
+ arch-name
+ arch-charset
+ arch-wide-charset
+
+ arch-void-type
+ arch-char-type
+ arch-short-type
+ arch-int-type
+ arch-long-type
+
+ arch-schar-type
+ arch-uchar-type
+ arch-ushort-type
+ arch-uint-type
+ arch-ulong-type
+ arch-float-type
+ arch-double-type
+ arch-longdouble-type
+ arch-bool-type
+ arch-longlong-type
+ arch-ulonglong-type
+
+ arch-int8-type
+ arch-uint8-type
+ arch-int16-type
+ arch-uint16-type
+ arch-int32-type
+ arch-uint32-type
+ arch-int64-type
+ arch-uint64-type
+
+ ;; scm-block.c
+
+ block?
+ block-valid?
+ block-start
+ block-end
+ block-function
+ block-superblock
+ block-global-block
+ block-static-block
+ block-global?
+ block-static?
+ block-symbols
+ make-block-symbols-iterator
+ block-symbols-progress?
+ lookup-block
+
+ ;; scm-breakpoint.c
+
+ BP_NONE
+ BP_BREAKPOINT
+ BP_WATCHPOINT
+ BP_HARDWARE_WATCHPOINT
+ BP_READ_WATCHPOINT
+ BP_ACCESS_WATCHPOINT
+
+ WP_READ
+ WP_WRITE
+ WP_ACCESS
+
+ make-breakpoint
+ breakpoint-delete!
+ breakpoints
+ breakpoint?
+ breakpoint-valid?
+ breakpoint-number
+ breakpoint-type
+ brekapoint-visible?
+ breakpoint-location
+ breakpoint-expression
+ breakpoint-enabled?
+ set-breakpoint-enabled!
+ breakpoint-silent?
+ set-breakpoint-silent!
+ breakpoint-ignore-count
+ set-breakpoint-ignore-count!
+ breakpoint-hit-count
+ set-breakpoint-hit-count!
+ breakpoint-thread
+ set-breakpoint-thread!
+ breakpoint-task
+ set-breakpoint-task!
+ breakpoint-condition
+ set-breakpoint-condition!
+ breakpoint-stop
+ set-breakpoint-stop!
+ breakpoint-commands
+
+ ;; scm-disasm.c
+
+ arch-disassemble
+
+ ;; scm-exception.c
+
+ make-exception
+ exception?
+ exception-key
+ exception-args
+
+ ;; scm-frame.c
+
+ NORMAL_FRAME
+ DUMMY_FRAME
+ INLINE_FRAME
+ TAILCALL_FRAME
+ SIGTRAMP_FRAME
+ ARCH_FRAME
+ SENTINEL_FRAME
+
+ FRAME_UNWIND_NO_REASON
+ FRAME_UNWIND_NULL_ID
+ FRAME_UNWIND_OUTERMOST
+ FRAME_UNWIND_UNAVAILABLE
+ FRAME_UNWIND_INNER_ID
+ FRAME_UNWIND_SAME_ID
+ FRAME_UNWIND_NO_SAVED_PC
+
+ frame?
+ frame-valid?
+ frame-name
+ frame-type
+ frame-arch
+ frame-unwind-stop-reason
+ frame-pc
+ frame-block
+ frame-function
+ frame-older
+ frame-newer
+ frame-sal
+ frame-read-var
+ frame-select
+ newest-frame
+ selected-frame
+ unwind-stop-reason-string
+
+ ;; scm-iterator.c
+
+ make-iterator
+ iterator?
+ iterator-object
+ iterator-progress
+ set-iterator-progress!
+ iterator-next!
+ end-of-iteration
+ end-of-iteration?
+
+ ;; scm-lazy-string.c
+ ;; FIXME: Where's the constructor?
+
+ lazy-string?
+ lazy-string-address
+ lazy-string-length
+ lazy-string-encoding
+ lazy-string-type
+ lazy-string->value
+
+ ;; scm-math.c
+
+ valid-add
+ value-sub
+ value-mul
+ value-div
+ value-rem
+ value-mod
+ value-pow
+ value-not
+ value-neg
+ value-pos
+ value-abs
+ value-lsh
+ value-rsh
+ value-min
+ value-max
+ value-lognot
+ value-logand
+ value-logior
+ value-logxor
+ value=?
+ value<?
+ value<=?
+ value>?
+ value>=?
+
+ ;; scm-objfile.c
+
+ objfile?
+ objfile-valid?
+ objfile-filename
+ objfile-pretty-printers
+ set-objfile-pretty-printers!
+ current-objfile
+ objfiles
+
+ ;; scm-ports.c
+
+ input-port
+ output-port
+ error-port
+ stdio-port?
+ open-memory
+ memory-port?
+ memory-port-range
+ memory-port-read-buffer-size
+ set-memory-port-read-buffer-size!
+ memory-port-write-buffer-size
+ set-memory-port-write-buffer-size!
+ ;; with-gdb-output-to-port, with-gdb-error-to-port are in experimental.scm.
+
+ ;; scm-pretty-print.c
+
+ make-pretty-printer
+ pretty-printer?
+ pretty-printer-enabled?
+ set-pretty-printer-enabled!
+ make-pretty-printer-worker
+ pretty-printer-worker?
+
+ ;; scm-smob.c
+
+ gsmob-kind
+ gsmob-property
+ set-gsmob-property!
+ gsmob-has-property?
+ gsmob-properties
+
+ ;; scm-string.c
+
+ string->argv
+
+ ;; scm-symbol.c
+
+ SYMBOL_LOC_UNDEF
+ SYMBOL_LOC_CONST
+ SYMBOL_LOC_STATIC
+ SYMBOL_LOC_REGISTER
+ SYMBOL_LOC_ARG
+ SYMBOL_LOC_REF_ARG
+ SYMBOL_LOC_LOCAL
+ SYMBOL_LOC_TYPEDEF
+ SYMBOL_LOC_LABEL
+ SYMBOL_LOC_BLOCK
+ SYMBOL_LOC_CONST_BYTES
+ SYMBOL_LOC_UNRESOLVED
+ SYMBOL_LOC_OPTIMIZED_OUT
+ SYMBOL_LOC_COMPUTED
+ SYMBOL_LOC_REGPARM_ADDR
+
+ SYMBOL_UNDEF_DOMAIN
+ SYMBOL_VAR_DOMAIN
+ SYMBOL_STRUCT_DOMAIN
+ SYMBOL_LABEL_DOMAIN
+ SYMBOL_VARIABLES_DOMAIN
+ SYMBOL_FUNCTIONS_DOMAIN
+ SYMBOL_TYPES_DOMAIN
+
+ symbol?
+ symbol-valid?
+ symbol-type
+ symbol-symtab
+ symbol-line
+ symbol-name
+ symbol-linkage-name
+ symbol-print-name
+ symbol-addr-class
+ symbol-argument?
+ symbol-constant?
+ symbol-function?
+ symbol-variable?
+ symbol-needs-frame?
+ symbol-value
+ lookup-symbol
+ lookup-global-symbol
+
+ ;; scm-symtab.c
+
+ symtab?
+ symtab-valid?
+ symtab-filename
+ symtab-fullname
+ symtab-objfile
+ symtab-global-block
+ symtab-static-block
+ sal?
+ sal-valid?
+ sal-symtab
+ sal-line
+ sal-pc
+ sal-last
+ find-pc-line
+
+ ;; scm-type.c
+
+ TYPE_CODE_BITSTRING
+ TYPE_CODE_PTR
+ TYPE_CODE_ARRAY
+ TYPE_CODE_STRUCT
+ TYPE_CODE_UNION
+ TYPE_CODE_ENUM
+ TYPE_CODE_FLAGS
+ TYPE_CODE_FUNC
+ TYPE_CODE_INT
+ TYPE_CODE_FLT
+ TYPE_CODE_VOID
+ TYPE_CODE_SET
+ TYPE_CODE_RANGE
+ TYPE_CODE_STRING
+ TYPE_CODE_ERROR
+ TYPE_CODE_METHOD
+ TYPE_CODE_METHODPTR
+ TYPE_CODE_MEMBERPTR
+ TYPE_CODE_REF
+ TYPE_CODE_CHAR
+ TYPE_CODE_BOOL
+ TYPE_CODE_COMPLEX
+ TYPE_CODE_TYPEDEF
+ TYPE_CODE_NAMESPACE
+ TYPE_CODE_DECFLOAT
+ TYPE_CODE_INTERNAL_FUNCTION
+
+ type?
+ lookup-type
+ type-code
+ type-fields
+ type-tag
+ type-sizeof
+ type-strip-typedefs
+ type-array
+ type-vector
+ type-pointer
+ type-range
+ type-reference
+ type-target
+ type-const
+ type-volatile
+ type-unqualified
+ type-name
+ type-num-fields
+ type-fields
+ make-field-iterator
+ type-field
+ type-has-field?
+ field?
+ field-name
+ field-type
+ field-enumval
+ field-bitpos
+ field-bitsize
+ field-artificial?
+ field-baseclass?
+
+ ;; scm-value.c
+
+ value?
+ make-value
+ value-optimized-out?
+ value-address
+ value-type
+ value-dynamic-type
+ value-cast
+ value-dynamic-cast
+ value-reinterpret-cast
+ value-dereference
+ value-referenced-value
+ value-field
+ value-subscript
+ value-call
+ value->bool
+ value->integer
+ value->real
+ value->bytevector
+ value->string
+ value->lazy-string
+ value-lazy?
+ make-lazy-value
+ value-fetch-lazy!
+ value-print
+ parse-and-eval
+ history-ref
+)
+
+;; Load the rest of the Scheme side.
+;; data-directory is provided by the C code.
+
+(add-to-load-path
+ (string-append (data-directory) file-name-separator-string "guile"))
+
+(use-modules ((gdb init)))
+
+;; These come from other files, but they're really part of this module.
+
+(re-export
+
+ ;; init.scm
+ orig-input-port
+ orig-output-port
+ orig-error-port
+)
diff --git a/gdb/guile/lib/gdb/boot.scm b/gdb/guile/lib/gdb/boot.scm
new file mode 100644
index 0000000..cf7d305
--- /dev/null
+++ b/gdb/guile/lib/gdb/boot.scm
@@ -0,0 +1,31 @@
+;; Bootstrap the Scheme side of the gdb module.
+;;
+;; Copyright (C) 2014 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 loaded with scm_c_primitive_load, which is ok, but files
+;; loaded with it are not compiled. So we do very little here, and do
+;; most of the initialization elsewhere.
+
+;; data-directory is provided by the C code.
+(load (string-append
+ (data-directory) file-name-separator-string "guile"
+ file-name-separator-string "gdb.scm"))
+
+;; Now that the Scheme side support is loaded, initialize it.
+(let ((init-proc (@@ (gdb init) %initialize!)))
+ (init-proc))
diff --git a/gdb/guile/lib/gdb/experimental.scm b/gdb/guile/lib/gdb/experimental.scm
new file mode 100644
index 0000000..ffded84
--- /dev/null
+++ b/gdb/guile/lib/gdb/experimental.scm
@@ -0,0 +1,35 @@
+;; Various experimental utilities.
+;; Anything in this file can change or disappear.
+;;
+;; Copyright (C) 2014 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 this file up by function?
+;; E.g., (gdb experimental ports), etc.
+
+(define-module (gdb experimental)
+ #:use-module (gdb)
+ #:use-module (gdb init))
+
+;; These are defined in C.
+(define-public with-gdb-output-to-port (@@ (gdb) %with-gdb-output-to-port))
+(define-public with-gdb-error-to-port (@@ (gdb) %with-gdb-error-to-port))
+
+(define-public (with-gdb-output-to-string thunk)
+ "Calls THUNK and returns all GDB output as a string."
+ (call-with-output-string
+ (lambda (p) (with-gdb-output-to-port p thunk))))
diff --git a/gdb/guile/lib/gdb/init.scm b/gdb/guile/lib/gdb/init.scm
new file mode 100644
index 0000000..12ad67d
--- /dev/null
+++ b/gdb/guile/lib/gdb/init.scm
@@ -0,0 +1,173 @@
+;; Scheme side of the gdb module.
+;;
+;; Copyright (C) 2014 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/>.
+
+(define-module (gdb init)
+ #:use-module (gdb))
+
+(define-public SCM_ARG1 1)
+(define-public SCM_ARG2 2)
+
+;; The original i/o ports. In case the user wants them back.
+(define %orig-input-port #f)
+(define %orig-output-port #f)
+(define %orig-error-port #f)
+
+;; %exception-print-style is exported as "private" by gdb.
+(define %exception-print-style (@@ (gdb) %exception-print-style))
+
+;; Keys for GDB-generated exceptions.
+;; gdb:with-stack is handled separately.
+
+(define %exception-keys '(gdb:error
+ gdb:invalid-object-error
+ gdb:memory-error
+ gdb:pp-type-error))
+
+;; Printer for gdb exceptions, used when Scheme tries to print them directly.
+
+(define (%exception-printer port key args default-printer)
+ (apply (case-lambda
+ ((subr msg args . rest)
+ (if subr
+ (format port "In procedure ~a: " subr))
+ (apply format port msg (or args '())))
+ (_ (default-printer)))
+ args))
+
+;; Print the message part of a gdb:with-stack exception.
+;; The arg list is the way it is because it's passed to set-exception-printer!.
+;; We don't print a backtrace here because Guile will have already printed a
+;; backtrace.
+
+(define (%with-stack-exception-printer port key args default-printer)
+ (let ((real-key (car args))
+ (real-args (cddr args)))
+ (%exception-printer port real-key real-args default-printer)))
+
+;; Copy of Guile's print-exception that tweaks the output for our purposes.
+;; TODO: It's not clear the tweaking is still necessary.
+
+(define (%print-exception-message-worker port key args)
+ (define (default-printer)
+ (format port "Throw to key `~a' with args `~s'." key args))
+ (format port "ERROR: ")
+ ;; Pass #t for tag to catch all errors.
+ (catch #t
+ (lambda ()
+ (%exception-printer port key args default-printer))
+ (lambda (k . args)
+ (format port "Error while printing gdb exception: ~a ~s."
+ k args)))
+ (newline port)
+ (force-output port))
+
+;; Called from the C code to print an exception.
+;; Guile prints them a little differently than we want.
+;; See boot-9.scm:print-exception.
+
+(define (%print-exception-message port frame key args)
+ (cond ((memq key %exception-keys)
+ (%print-exception-message-worker port key args))
+ (else
+ (print-exception port frame key args)))
+ *unspecified*)
+
+;; Called from the C code to print an exception according to the setting
+;; of "guile print-stack".
+;;
+;; If PORT is #f, use the standard error port.
+;; If STACK is #f, never print the stack, regardless of whether printing it
+;; is enabled. If STACK is #t, then print it if it is contained in ARGS
+;; (i.e., KEY is gdb:with-stack). Otherwise STACK is the result of calling
+;; scm_make_stack (which will be ignored in favor of the stack in ARGS if
+;; KEY is gdb:with-stack).
+;; KEY, ARGS are the standard arguments to scm_throw, et.al.
+
+(define (%print-exception-with-stack port stack key args)
+ (let ((style (%exception-print-style)))
+ (if (not (eq? style 'none))
+ (let ((error-port (current-error-port))
+ (frame #f))
+ (if (not port)
+ (set! port error-port))
+ (if (eq? port error-port)
+ (begin
+ (force-output (current-output-port))
+ ;; In case the current output port is not gdb's output port.
+ (force-output (output-port))))
+
+ ;; If the exception is gdb:with-stack, unwrap it to get the stack and
+ ;; underlying exception. If the caller happens to pass in a stack,
+ ;; we ignore it and use the one in ARGS instead.
+ (if (eq? key 'gdb:with-stack)
+ (begin
+ (set! key (car args))
+ (if stack
+ (set! stack (cadr args)))
+ (set! args (cddr args))))
+
+ ;; If caller wanted a stack and there isn't one, disable backtracing.
+ (if (eq? stack #t)
+ (set! stack #f))
+ ;; At this point if stack is true, then it is assumed to be a stack.
+ (if stack
+ (set! frame (stack-ref stack 0)))
+
+ (if (and (eq? style 'full) stack)
+ (begin
+ ;; This is derived from libguile/throw.c:handler_message.
+ ;; We include "Guile" in "Guile Backtrace" whereas the Guile
+ ;; version does not so that tests can know it's us printing
+ ;; the backtrace. Plus it could help beginners.
+ (display "Guile Backtrace:\n" port)
+ (display-backtrace stack port #f #f '())
+ (newline port)))
+
+ (%print-exception-message port frame key args)))))
+
+;; Internal utility to check the type of an argument, akin to SCM_ASSERT_TYPE.
+;; It's public so other gdb modules can use it.
+
+(define-public (%assert-type test-result arg pos func-name)
+ (if (not test-result)
+ (scm-error 'wrong-type-arg func-name
+ "Wrong type argument in position ~a: ~s"
+ (list pos arg) (list arg))))
+
+;; Internal utility called during startup to initialize the Scheme side of
+;; GDB+Guile.
+
+(define (%initialize!)
+ (add-to-load-path (string-append (data-directory)
+ file-name-separator-string "guile"))
+
+ (for-each (lambda (key)
+ (set-exception-printer! key %exception-printer))
+ %exception-keys)
+ (set-exception-printer! 'gdb:with-stack %with-stack-exception-printer)
+
+ (set! %orig-input-port (set-current-input-port (input-port)))
+ (set! %orig-output-port (set-current-output-port (output-port)))
+ (set! %orig-error-port (set-current-error-port (error-port))))
+
+;; Public routines.
+
+(define-public (orig-input-port) %orig-input-port)
+(define-public (orig-output-port) %orig-output-port)
+(define-public (orig-error-port) %orig-error-port)
diff --git a/gdb/guile/lib/gdb/iterator.scm b/gdb/guile/lib/gdb/iterator.scm
new file mode 100644
index 0000000..9cfbe85
--- /dev/null
+++ b/gdb/guile/lib/gdb/iterator.scm
@@ -0,0 +1,80 @@
+;; Iteration utilities.
+;; Anything in this file can change or disappear.
+;;
+;; Copyright (C) 2014 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/>.
+
+(define-module (gdb iterator)
+ #:use-module (gdb))
+
+(define-public (make-list-iterator l)
+ "Return a <gdb:iterator> object for a list."
+ (%assert-type (list? l) l SCM_ARG1 'make-list-iterator)
+ (let ((next! (lambda (iter)
+ (let ((l (iterator-progress iter)))
+ (if (eq? l '())
+ (end-of-iteration)
+ (begin
+ (set-iterator-progress! iter (cdr l))
+ (car l)))))))
+ (make-iterator l l next!)))
+
+(define-public (iterator->list iter)
+ "Return the elements of ITER as a list."
+ (let loop ((iter iter)
+ (result '()))
+ (let ((next (iterator-next! iter)))
+ (if (end-of-iteration? next)
+ (reverse! result)
+ (loop iter (cons next result))))))
+
+(define-public (iterator-map proc iter)
+ "Return a list of PROC applied to each element."
+ (let loop ((proc proc)
+ (iter iter)
+ (result '()))
+ (let ((next (iterator-next! iter)))
+ (if (end-of-iteration? next)
+ (reverse! result)
+ (loop proc iter (cons (proc next) result))))))
+
+(define-public (iterator-for-each proc iter)
+ "Apply PROC to each element. The result is unspecified."
+ (let ((next (iterator-next! iter)))
+ (if (not (end-of-iteration? next))
+ (begin
+ (proc next)
+ (iterator-for-each proc iter)))))
+
+(define-public (iterator-filter pred iter)
+ "Return the elements that satify predicate PRED."
+ (let loop ((result '()))
+ (let ((next (iterator-next! iter)))
+ (cond ((end-of-iteration? next) (reverse! result))
+ ((pred next) (loop (cons next result)))
+ (else (loop result))))))
+
+(define-public (iterator-until pred iter)
+ "Run the iterator until the result of (pred element) is true.
+
+ Returns:
+ The result of the first (pred element) call that returns true,
+ or #f if no element matches."
+ (let loop ((next (iterator-next! iter)))
+ (cond ((end-of-iteration? next) #f)
+ ((pred next) => identity)
+ (else (loop (iterator-next! iter))))))
diff --git a/gdb/guile/lib/gdb/printing.scm b/gdb/guile/lib/gdb/printing.scm
new file mode 100644
index 0000000..36e3275
--- /dev/null
+++ b/gdb/guile/lib/gdb/printing.scm
@@ -0,0 +1,52 @@
+;; Additional pretty-printer support.
+;;
+;; Copyright (C) 2014 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/>.
+
+(define-module (gdb printing)
+ #:use-module ((gdb) #:select
+ (*pretty-printers* pretty-printer? objfile?
+ objfile-pretty-printers set-objfile-pretty-printers!))
+ #:use-module (gdb init))
+
+(define-public (prepend-pretty-printer! obj matcher)
+ "Add MATCHER to the beginning of the pretty-printer list for OBJ.
+If OBJ is #f, add MATCHER to the global list."
+ (%assert-type (pretty-printer? matcher) matcher SCM_ARG1
+ 'prepend-pretty-printer!)
+ (cond ((eq? obj #f)
+ (set! *pretty-printers* (cons matcher *pretty-printers*)))
+ ((objfile? obj)
+ (set-objfile-pretty-printers! obj
+ (cons matcher
+ (objfile-pretty-printers obj))))
+ (else
+ (%assert-type #f obj SCM_ARG1 'prepend-pretty-printer!))))
+
+(define-public (append-pretty-printer! obj matcher)
+ "Add MATCHER to the end of the pretty-printer list for OBJ.
+If OBJ is #f, add MATCHER to the global list."
+ (%assert-type (pretty-printer? matcher) matcher SCM_ARG1
+ 'append-pretty-printer!)
+ (cond ((eq? obj #f)
+ (set! *pretty-printers* (append! *pretty-printers* (list matcher))))
+ ((objfile? obj)
+ (set-objfile-pretty-printers! obj
+ (append! (objfile-pretty-printers obj)
+ matcher)))
+ (else
+ (%assert-type #f obj SCM_ARG1 'append-pretty-printer!))))
diff --git a/gdb/guile/lib/gdb/types.scm b/gdb/guile/lib/gdb/types.scm
new file mode 100644
index 0000000..31ea192
--- /dev/null
+++ b/gdb/guile/lib/gdb/types.scm
@@ -0,0 +1,78 @@
+;; Type utilities.
+;; Copyright (C) 2010-2014 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/>.
+
+(define-module (gdb types)
+ #:use-module (gdb)
+ #:use-module (gdb init)
+ #:use-module (gdb iterator))
+
+(define-public (type-has-field-deep? type field-name)
+ "Return #t if the type, including baseclasses, has the specified field.
+
+ Arguments:
+ type: The type to examine. It must be a struct or union.
+ field-name: The name of the field to look up.
+
+ Returns:
+ True if the field is present either in type_ or any baseclass.
+
+ Raises:
+ wrong-type-arg: The type is not a struct or union."
+
+ (define (search-class type)
+ (let ((find-in-baseclass (lambda (field)
+ (if (field-baseclass? field)
+ (search-class (field-type field))
+ ;; Not a baseclass, search ends now.
+ ;; Return #:end to end search.
+ #:end))))
+ (let ((search-baseclasses
+ (lambda (type)
+ (iterator-until find-in-baseclass
+ (make-field-iterator type)))))
+ (or (type-has-field? type field-name)
+ (not (eq? (search-baseclasses type) #:end))))))
+
+ (if (= (type-code type) TYPE_CODE_REF)
+ (set! type (type-target type)))
+ (set! type (type-strip-typedefs type))
+
+ (%assert-type (memq (type-code type) (list TYPE_CODE_STRUCT TYPE_CODE_UNION))
+ type SCM_ARG1 'type-has-field-deep?)
+
+ (search-class type))
+
+(define-public (make-enum-hashtable enum-type)
+ "Return a hash table from a program's enum type.
+
+ Elements in the hash table are fetched with hashq-ref.
+
+ Arguments:
+ enum-type: The enum to compute the hash table for.
+
+ Returns:
+ The hash table of the enum.
+
+ Raises:
+ wrong-type-arg: The type is not an enum."
+
+ (%assert-type (= (type-code enum-type) TYPE_CODE_ENUM)
+ enum-type SCM_ARG1 'make-enum-hashtable)
+ (let ((htab (make-hash-table)))
+ (for-each (lambda (enum)
+ (hash-set! htab (field-name enum) (field-enumval enum)))
+ (type-fields enum-type))
+ htab))
diff --git a/gdb/guile/scm-arch.c b/gdb/guile/scm-arch.c
new file mode 100644
index 0000000..fa578f3
--- /dev/null
+++ b/gdb/guile/scm-arch.c
@@ -0,0 +1,668 @@
+/* Scheme interface to architecture.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "charset.h"
+#include "gdbarch.h"
+#include "arch-utils.h"
+#include "guile-internal.h"
+
+/* The <gdb:arch> smob.
+ The typedef for this struct is in guile-internal.h. */
+
+struct _arch_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ struct gdbarch *gdbarch;
+};
+
+static const char arch_smob_name[] = "gdb:arch";
+
+/* The tag Guile knows the arch smob by. */
+static scm_t_bits arch_smob_tag;
+
+static struct gdbarch_data *arch_object_data = NULL;
+
+static int arscm_is_arch (SCM);
+
+/* Administrivia for arch smobs. */
+
+/* The smob "mark" function for <gdb:arch>. */
+
+static SCM
+arscm_mark_arch_smob (SCM self)
+{
+ arch_smob *a_smob = (arch_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&a_smob->base);
+}
+
+/* The smob "print" function for <gdb:arch>. */
+
+static int
+arscm_print_arch_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ arch_smob *a_smob = (arch_smob *) SCM_SMOB_DATA (self);
+ struct gdbarch *gdbarch = a_smob->gdbarch;
+
+ gdbscm_printf (port, "#<%s", arch_smob_name);
+ gdbscm_printf (port, " %s", gdbarch_bfd_arch_info (gdbarch)->printable_name);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:arch> object for GDBARCH. */
+
+static SCM
+arscm_make_arch_smob (struct gdbarch *gdbarch)
+{
+ arch_smob *a_smob = (arch_smob *)
+ scm_gc_malloc (sizeof (arch_smob), arch_smob_name);
+ SCM a_scm;
+
+ a_smob->gdbarch = gdbarch;
+ a_scm = scm_new_smob (arch_smob_tag, (scm_t_bits) a_smob);
+ gdbscm_init_gsmob (&a_smob->base);
+
+ return a_scm;
+}
+
+/* Return the gdbarch field of A_SMOB. */
+
+struct gdbarch *
+arscm_get_gdbarch (arch_smob *a_smob)
+{
+ return a_smob->gdbarch;
+}
+
+/* Return non-zero if SCM is an architecture smob. */
+
+static int
+arscm_is_arch (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (arch_smob_tag, scm);
+}
+
+/* (arch? object) -> boolean */
+
+static SCM
+gdbscm_arch_p (SCM scm)
+{
+ return scm_from_bool (arscm_is_arch (scm));
+}
+
+/* Associates an arch_object with GDBARCH as gdbarch_data via the gdbarch
+ post init registration mechanism (gdbarch_data_register_post_init). */
+
+static void *
+arscm_object_data_init (struct gdbarch *gdbarch)
+{
+ SCM arch_scm = arscm_make_arch_smob (gdbarch);
+
+ /* This object lasts the duration of the GDB session, so there is no
+ call to scm_gc_unprotect_object for it. */
+ scm_gc_protect_object (arch_scm);
+
+ return (void *) arch_scm;
+}
+
+/* Return the <gdb:arch> object corresponding to GDBARCH.
+ The object is cached in GDBARCH so this is simple. */
+
+SCM
+arscm_scm_from_arch (struct gdbarch *gdbarch)
+{
+ SCM a_scm = (SCM) gdbarch_data (gdbarch, arch_object_data);
+
+ return a_scm;
+}
+
+/* Return the <gdb:arch> smob in SELF.
+ Throws an exception if SELF is not a <gdb:arch> object. */
+
+static SCM
+arscm_get_arch_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (arscm_is_arch (self), self, arg_pos, func_name,
+ arch_smob_name);
+
+ return self;
+}
+
+/* Return a pointer to the arch smob of SELF.
+ Throws an exception if SELF is not a <gdb:arch> object. */
+
+arch_smob *
+arscm_get_arch_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM a_scm = arscm_get_arch_arg_unsafe (self, arg_pos, func_name);
+ arch_smob *a_smob = (arch_smob *) SCM_SMOB_DATA (a_scm);
+
+ return a_smob;
+}
+
+/* Arch methods. */
+
+/* (current-arch) -> <gdb:arch>
+ Return the architecture of the currently selected stack frame,
+ if there is one, or the current target if there isn't. */
+
+static SCM
+gdbscm_current_arch (void)
+{
+ return arscm_scm_from_arch (get_current_arch ());
+}
+
+/* (arch-name <gdb:arch>) -> string
+ Return the name of the architecture as a string value. */
+
+static SCM
+gdbscm_arch_name (SCM self)
+{
+ arch_smob *a_smob
+ = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct gdbarch *gdbarch = a_smob->gdbarch;
+ const char *name;
+
+ name = (gdbarch_bfd_arch_info (gdbarch))->printable_name;
+
+ return gdbscm_scm_from_c_string (name);
+}
+
+/* (arch-charset <gdb:arch>) -> string */
+
+static SCM
+gdbscm_arch_charset (SCM self)
+{
+ arch_smob *a_smob
+ =arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct gdbarch *gdbarch = a_smob->gdbarch;
+
+ return gdbscm_scm_from_c_string (target_charset (gdbarch));
+}
+
+/* (arch-wide-charset <gdb:arch>) -> string */
+
+static SCM
+gdbscm_arch_wide_charset (SCM self)
+{
+ arch_smob *a_smob
+ = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct gdbarch *gdbarch = a_smob->gdbarch;
+
+ return gdbscm_scm_from_c_string (target_wide_charset (gdbarch));
+}
+
+/* Builtin types.
+
+ The order the types are defined here follows the order in
+ struct builtin_type. */
+
+/* Helper routine to return a builtin type for <gdb:arch> object SELF.
+ OFFSET is offsetof (builtin_type, the_type).
+ Throws an exception if SELF is not a <gdb:arch> object. */
+
+static const struct builtin_type *
+gdbscm_arch_builtin_type (SCM self, const char *func_name)
+{
+ arch_smob *a_smob
+ = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, func_name);
+ struct gdbarch *gdbarch = a_smob->gdbarch;
+
+ return builtin_type (gdbarch);
+}
+
+/* (arch-void-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_void_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_void;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-char-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_char_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_char;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-short-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_short_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_short;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-int-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_int_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_int;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-long-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_long_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_long;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-schar-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_schar_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_signed_char;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uchar-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uchar_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_unsigned_char;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-ushort-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_ushort_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_unsigned_short;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uint-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uint_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_unsigned_int;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-ulong-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_ulong_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_unsigned_long;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-float-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_float_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_float;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-double-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_double_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_double;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-longdouble-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_longdouble_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_long_double;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-bool-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_bool_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_bool;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-longlong-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_longlong_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_long_long;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-ulonglong-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_ulonglong_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_unsigned_long_long;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-int8-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_int8_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_int8;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uint8-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uint8_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_uint8;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-int16-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_int16_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_int16;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uint16-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uint16_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_uint16;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-int32-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_int32_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_int32;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uint32-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uint32_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_uint32;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-int64-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_int64_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_int64;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (arch-uint64-type <gdb:arch>) -> <gdb:type> */
+
+static SCM
+gdbscm_arch_uint64_type (SCM self)
+{
+ struct type *type
+ = gdbscm_arch_builtin_type (self, FUNC_NAME)->builtin_uint64;
+
+ return tyscm_scm_from_type (type);
+}
+
+/* Initialize the Scheme architecture support. */
+
+static const scheme_function arch_functions[] =
+{
+ { "arch?", 1, 0, 0, gdbscm_arch_p,
+ "\
+Return #t if the object is a <gdb:arch> object." },
+
+ { "current-arch", 0, 0, 0, gdbscm_current_arch,
+ "\
+Return the <gdb:arch> object representing the architecture of the\n\
+currently selected stack frame, if there is one, or the architecture of the\n\
+current target if there isn't.\n\
+\n\
+ Arguments: none" },
+
+ { "arch-name", 1, 0, 0, gdbscm_arch_name,
+ "\
+Return the name of the architecture." },
+
+ { "arch-charset", 1, 0, 0, gdbscm_arch_charset,
+ "\
+Return name of target character set as a string." },
+
+ { "arch-wide-charset", 1, 0, 0, gdbscm_arch_wide_charset,
+ "\
+Return name of target wide character set as a string." },
+
+ { "arch-void-type", 1, 0, 0, gdbscm_arch_void_type,
+ "\
+Return the <gdb:type> object for the \"void\" type\n\
+of the architecture." },
+
+ { "arch-char-type", 1, 0, 0, gdbscm_arch_char_type,
+ "\
+Return the <gdb:type> object for the \"char\" type\n\
+of the architecture." },
+
+ { "arch-short-type", 1, 0, 0, gdbscm_arch_short_type,
+ "\
+Return the <gdb:type> object for the \"short\" type\n\
+of the architecture." },
+
+ { "arch-int-type", 1, 0, 0, gdbscm_arch_int_type,
+ "\
+Return the <gdb:type> object for the \"int\" type\n\
+of the architecture." },
+
+ { "arch-long-type", 1, 0, 0, gdbscm_arch_long_type,
+ "\
+Return the <gdb:type> object for the \"long\" type\n\
+of the architecture." },
+
+ { "arch-schar-type", 1, 0, 0, gdbscm_arch_schar_type,
+ "\
+Return the <gdb:type> object for the \"signed char\" type\n\
+of the architecture." },
+
+ { "arch-uchar-type", 1, 0, 0, gdbscm_arch_uchar_type,
+ "\
+Return the <gdb:type> object for the \"unsigned char\" type\n\
+of the architecture." },
+
+ { "arch-ushort-type", 1, 0, 0, gdbscm_arch_ushort_type,
+ "\
+Return the <gdb:type> object for the \"unsigned short\" type\n\
+of the architecture." },
+
+ { "arch-uint-type", 1, 0, 0, gdbscm_arch_uint_type,
+ "\
+Return the <gdb:type> object for the \"unsigned int\" type\n\
+of the architecture." },
+
+ { "arch-ulong-type", 1, 0, 0, gdbscm_arch_ulong_type,
+ "\
+Return the <gdb:type> object for the \"unsigned long\" type\n\
+of the architecture." },
+
+ { "arch-float-type", 1, 0, 0, gdbscm_arch_float_type,
+ "\
+Return the <gdb:type> object for the \"float\" type\n\
+of the architecture." },
+
+ { "arch-double-type", 1, 0, 0, gdbscm_arch_double_type,
+ "\
+Return the <gdb:type> object for the \"double\" type\n\
+of the architecture." },
+
+ { "arch-longdouble-type", 1, 0, 0, gdbscm_arch_longdouble_type,
+ "\
+Return the <gdb:type> object for the \"long double\" type\n\
+of the architecture." },
+
+ { "arch-bool-type", 1, 0, 0, gdbscm_arch_bool_type,
+ "\
+Return the <gdb:type> object for the \"bool\" type\n\
+of the architecture." },
+
+ { "arch-longlong-type", 1, 0, 0, gdbscm_arch_longlong_type,
+ "\
+Return the <gdb:type> object for the \"long long\" type\n\
+of the architecture." },
+
+ { "arch-ulonglong-type", 1, 0, 0,
+ gdbscm_arch_ulonglong_type,
+ "\
+Return the <gdb:type> object for the \"unsigned long long\" type\n\
+of the architecture." },
+
+ { "arch-int8-type", 1, 0, 0, gdbscm_arch_int8_type,
+ "\
+Return the <gdb:type> object for the \"int8\" type\n\
+of the architecture." },
+
+ { "arch-uint8-type", 1, 0, 0, gdbscm_arch_uint8_type,
+ "\
+Return the <gdb:type> object for the \"uint8\" type\n\
+of the architecture." },
+
+ { "arch-int16-type", 1, 0, 0, gdbscm_arch_int16_type,
+ "\
+Return the <gdb:type> object for the \"int16\" type\n\
+of the architecture." },
+
+ { "arch-uint16-type", 1, 0, 0, gdbscm_arch_uint16_type,
+ "\
+Return the <gdb:type> object for the \"uint16\" type\n\
+of the architecture." },
+
+ { "arch-int32-type", 1, 0, 0, gdbscm_arch_int32_type,
+ "\
+Return the <gdb:type> object for the \"int32\" type\n\
+of the architecture." },
+
+ { "arch-uint32-type", 1, 0, 0, gdbscm_arch_uint32_type,
+ "\
+Return the <gdb:type> object for the \"uint32\" type\n\
+of the architecture." },
+
+ { "arch-int64-type", 1, 0, 0, gdbscm_arch_int64_type,
+ "\
+Return the <gdb:type> object for the \"int64\" type\n\
+of the architecture." },
+
+ { "arch-uint64-type", 1, 0, 0, gdbscm_arch_uint64_type,
+ "\
+Return the <gdb:type> object for the \"uint64\" type\n\
+of the architecture." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_arches (void)
+{
+ arch_smob_tag = gdbscm_make_smob_type (arch_smob_name, sizeof (arch_smob));
+ scm_set_smob_mark (arch_smob_tag, arscm_mark_arch_smob);
+ scm_set_smob_print (arch_smob_tag, arscm_print_arch_smob);
+
+ gdbscm_define_functions (arch_functions, 1);
+
+ arch_object_data
+ = gdbarch_data_register_post_init (arscm_object_data_init);
+}
diff --git a/gdb/guile/scm-auto-load.c b/gdb/guile/scm-auto-load.c
new file mode 100644
index 0000000..5b9eb23
--- /dev/null
+++ b/gdb/guile/scm-auto-load.c
@@ -0,0 +1,81 @@
+/* GDB routines for supporting auto-loaded Guile scripts.
+
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include <string.h>
+#include "top.h"
+#include "exceptions.h"
+#include "gdbcmd.h"
+#include "objfiles.h"
+#include "cli/cli-cmds.h"
+#include "auto-load.h"
+#include "guile.h"
+#include "guile-internal.h"
+
+/* User-settable option to enable/disable auto-loading of Guile scripts:
+ set auto-load guile-scripts on|off
+ This is true if we should auto-load associated Guile scripts when an
+ objfile is opened, false otherwise. */
+static int auto_load_guile_scripts = 1;
+
+/* "show" command for the auto_load_guile_scripts configuration variable. */
+
+static void
+show_auto_load_guile_scripts (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Auto-loading of Guile scripts is %s.\n"), value);
+}
+
+/* Return non-zero if auto-loading Guile scripts is enabled.
+ This is the extension_language_script_ops.auto_load_enabled "method". */
+
+int
+gdbscm_auto_load_enabled (const struct extension_language_defn *extlang)
+{
+ return auto_load_guile_scripts;
+}
+
+/* Wrapper for "info auto-load guile-scripts". */
+
+static void
+info_auto_load_guile_scripts (char *pattern, int from_tty)
+{
+ auto_load_info_scripts (pattern, from_tty, &extension_language_guile);
+}
+
+void
+gdbscm_initialize_auto_load (void)
+{
+ add_setshow_boolean_cmd ("guile-scripts", class_support,
+ &auto_load_guile_scripts, _("\
+Set the debugger's behaviour regarding auto-loaded Guile scripts."), _("\
+Show the debugger's behaviour regarding auto-loaded Guile scripts."), _("\
+If enabled, auto-loaded Guile scripts are loaded when the debugger reads\n\
+an executable or shared library.\n\
+This options has security implications for untrusted inferiors."),
+ NULL, show_auto_load_guile_scripts,
+ auto_load_set_cmdlist_get (),
+ auto_load_show_cmdlist_get ());
+
+ add_cmd ("guile-scripts", class_info, info_auto_load_guile_scripts,
+ _("Print the list of automatically loaded Guile scripts.\n\
+Usage: info auto-load guile-scripts [REGEXP]"),
+ auto_load_info_cmdlist_get ());
+}
diff --git a/gdb/guile/scm-block.c b/gdb/guile/scm-block.c
new file mode 100644
index 0000000..de41af2
--- /dev/null
+++ b/gdb/guile/scm-block.c
@@ -0,0 +1,828 @@
+/* Scheme interface to blocks.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "block.h"
+#include "dictionary.h"
+#include "objfiles.h"
+#include "source.h"
+#include "symtab.h"
+#include "guile-internal.h"
+
+/* A smob describing a gdb block. */
+
+typedef struct _block_smob
+{
+ /* This always appears first.
+ We want blocks to be eq?-able. And we need to be able to invalidate
+ blocks when the associated objfile is deleted. */
+ eqable_gdb_smob base;
+
+ /* The GDB block structure that represents a frame's code block. */
+ const struct block *block;
+
+ /* The backing object file. There is no direct relationship in GDB
+ between a block and an object file. When a block is created also
+ store a pointer to the object file for later use. */
+ struct objfile *objfile;
+} block_smob;
+
+/* To iterate over block symbols from Scheme we need to store
+ struct block_iterator somewhere. This is stored in the "progress" field
+ of <gdb:iterator>. We store the block object in iterator_smob.object,
+ so we don't store it here.
+
+ Remember: While iterating over block symbols, you must continually check
+ whether the block is still valid. */
+
+typedef struct
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The iterator for that block. */
+ struct block_iterator iter;
+
+ /* Has the iterator been initialized flag. */
+ int initialized_p;
+} block_syms_progress_smob;
+
+static const char block_smob_name[] = "gdb:block";
+static const char block_syms_progress_smob_name[] = "gdb:block-symbols-iterator";
+
+/* The tag Guile knows the block smobs by. */
+static scm_t_bits block_smob_tag;
+static scm_t_bits block_syms_progress_smob_tag;
+
+/* The "next!" block syms iterator method. */
+static SCM bkscm_next_symbol_x_proc;
+
+static const struct objfile_data *bkscm_objfile_data_key;
+
+/* Administrivia for block smobs. */
+
+/* Helper function to hash a block_smob. */
+
+static hashval_t
+bkscm_hash_block_smob (const void *p)
+{
+ const block_smob *b_smob = p;
+
+ return htab_hash_pointer (b_smob->block);
+}
+
+/* Helper function to compute equality of block_smobs. */
+
+static int
+bkscm_eq_block_smob (const void *ap, const void *bp)
+{
+ const block_smob *a = ap;
+ const block_smob *b = bp;
+
+ return (a->block == b->block
+ && a->block != NULL);
+}
+
+/* Return the struct block pointer -> SCM mapping table.
+ It is created if necessary. */
+
+static htab_t
+bkscm_objfile_block_map (struct objfile *objfile)
+{
+ htab_t htab = objfile_data (objfile, bkscm_objfile_data_key);
+
+ if (htab == NULL)
+ {
+ htab = gdbscm_create_eqable_gsmob_ptr_map (bkscm_hash_block_smob,
+ bkscm_eq_block_smob);
+ set_objfile_data (objfile, bkscm_objfile_data_key, htab);
+ }
+
+ return htab;
+}
+
+/* The smob "mark" function for <gdb:block>. */
+
+static SCM
+bkscm_mark_block_smob (SCM self)
+{
+ block_smob *b_smob = (block_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_eqable_gsmob (&b_smob->base);
+}
+
+/* The smob "free" function for <gdb:block>. */
+
+static size_t
+bkscm_free_block_smob (SCM self)
+{
+ block_smob *b_smob = (block_smob *) SCM_SMOB_DATA (self);
+
+ if (b_smob->block != NULL)
+ {
+ htab_t htab = bkscm_objfile_block_map (b_smob->objfile);
+
+ gdbscm_clear_eqable_gsmob_ptr_slot (htab, &b_smob->base);
+ }
+
+ /* Not necessary, done to catch bugs. */
+ b_smob->block = NULL;
+ b_smob->objfile = NULL;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:block>. */
+
+static int
+bkscm_print_block_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ block_smob *b_smob = (block_smob *) SCM_SMOB_DATA (self);
+ const struct block *b = b_smob->block;
+
+ gdbscm_printf (port, "#<%s", block_smob_name);
+
+ if (BLOCK_SUPERBLOCK (b) == NULL)
+ gdbscm_printf (port, " global");
+ else if (BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (b)) == NULL)
+ gdbscm_printf (port, " static");
+
+ if (BLOCK_FUNCTION (b) != NULL)
+ gdbscm_printf (port, " %s", SYMBOL_PRINT_NAME (BLOCK_FUNCTION (b)));
+
+ gdbscm_printf (port, " %s-%s",
+ hex_string (BLOCK_START (b)), hex_string (BLOCK_END (b)));
+
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:block> object. */
+
+static SCM
+bkscm_make_block_smob (void)
+{
+ block_smob *b_smob = (block_smob *)
+ scm_gc_malloc (sizeof (block_smob), block_smob_name);
+ SCM b_scm;
+
+ b_smob->block = NULL;
+ b_smob->objfile = NULL;
+ b_scm = scm_new_smob (block_smob_tag, (scm_t_bits) b_smob);
+ gdbscm_init_eqable_gsmob (&b_smob->base);
+
+ return b_scm;
+}
+
+/* Returns non-zero if SCM is a <gdb:block> object. */
+
+static int
+bkscm_is_block (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (block_smob_tag, scm);
+}
+
+/* (block? scm) -> boolean */
+
+static SCM
+gdbscm_block_p (SCM scm)
+{
+ return scm_from_bool (bkscm_is_block (scm));
+}
+
+/* Return the existing object that encapsulates BLOCK, or create a new
+ <gdb:block> object. */
+
+SCM
+bkscm_scm_from_block (const struct block *block, struct objfile *objfile)
+{
+ htab_t htab;
+ eqable_gdb_smob **slot;
+ block_smob *b_smob, b_smob_for_lookup;
+ SCM b_scm;
+
+ /* If we've already created a gsmob for this block, return it.
+ This makes blocks eq?-able. */
+ htab = bkscm_objfile_block_map (objfile);
+ b_smob_for_lookup.block = block;
+ slot = gdbscm_find_eqable_gsmob_ptr_slot (htab, &b_smob_for_lookup.base);
+ if (*slot != NULL)
+ return (*slot)->containing_scm;
+
+ b_scm = bkscm_make_block_smob ();
+ b_smob = (block_smob *) SCM_SMOB_DATA (b_scm);
+ b_smob->block = block;
+ b_smob->objfile = objfile;
+ gdbscm_fill_eqable_gsmob_ptr_slot (slot, &b_smob->base, b_scm);
+
+ return b_scm;
+}
+
+/* Returns the <gdb:block> object in SELF.
+ Throws an exception if SELF is not a <gdb:block> object. */
+
+static SCM
+bkscm_get_block_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (bkscm_is_block (self), self, arg_pos, func_name,
+ block_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the block smob of SELF.
+ Throws an exception if SELF is not a <gdb:block> object. */
+
+static block_smob *
+bkscm_get_block_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM b_scm = bkscm_get_block_arg_unsafe (self, arg_pos, func_name);
+ block_smob *b_smob = (block_smob *) SCM_SMOB_DATA (b_scm);
+
+ return b_smob;
+}
+
+/* Returns non-zero if block B_SMOB is valid. */
+
+static int
+bkscm_is_valid (block_smob *b_smob)
+{
+ return b_smob->block != NULL;
+}
+
+/* Returns the block smob in SELF, verifying it's valid.
+ Throws an exception if SELF is not a <gdb:block> object or is invalid. */
+
+static block_smob *
+bkscm_get_valid_block_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ block_smob *b_smob
+ = bkscm_get_block_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!bkscm_is_valid (b_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:block>"));
+ }
+
+ return b_smob;
+}
+
+/* Returns the block smob contained in SCM or NULL if SCM is not a
+ <gdb:block> object.
+ If there is an error a <gdb:exception> object is stored in *EXCP. */
+
+static block_smob *
+bkscm_get_valid_block (SCM scm, int arg_pos, const char *func_name, SCM *excp)
+{
+ block_smob *b_smob;
+
+ if (!bkscm_is_block (scm))
+ {
+ *excp = gdbscm_make_type_error (func_name, arg_pos, scm,
+ block_smob_name);
+ return NULL;
+ }
+
+ b_smob = (block_smob *) SCM_SMOB_DATA (scm);
+ if (!bkscm_is_valid (b_smob))
+ {
+ *excp = gdbscm_make_invalid_object_error (func_name, arg_pos, scm,
+ _("<gdb:block>"));
+ return NULL;
+ }
+
+ return b_smob;
+}
+
+/* Returns the struct block that is wrapped by BLOCK_SCM.
+ If BLOCK_SCM is not a block, or is an invalid block, then NULL is returned
+ and a <gdb:exception> object is stored in *EXCP. */
+
+const struct block *
+bkscm_scm_to_block (SCM block_scm, int arg_pos, const char *func_name,
+ SCM *excp)
+{
+ block_smob *b_smob;
+
+ b_smob = bkscm_get_valid_block (block_scm, arg_pos, func_name, excp);
+
+ if (b_smob != NULL)
+ return b_smob->block;
+ return NULL;
+}
+
+/* Helper function for bkscm_del_objfile_blocks to mark the block
+ as invalid. */
+
+static int
+bkscm_mark_block_invalid (void **slot, void *info)
+{
+ block_smob *b_smob = (block_smob *) *slot;
+
+ b_smob->block = NULL;
+ b_smob->objfile = NULL;
+ return 1;
+}
+
+/* This function is called when an objfile is about to be freed.
+ Invalidate the block as further actions on the block would result
+ in bad data. All access to b_smob->block should be gated by
+ checks to ensure the block is (still) valid. */
+
+static void
+bkscm_del_objfile_blocks (struct objfile *objfile, void *datum)
+{
+ htab_t htab = datum;
+
+ if (htab != NULL)
+ {
+ htab_traverse_noresize (htab, bkscm_mark_block_invalid, NULL);
+ htab_delete (htab);
+ }
+}
+
+/* Block methods. */
+
+/* (block-valid? <gdb:block>) -> boolean
+ Returns #t if SELF still exists in GDB. */
+
+static SCM
+gdbscm_block_valid_p (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bkscm_is_valid (b_smob));
+}
+
+/* (block-start <gdb:block>) -> address */
+
+static SCM
+gdbscm_block_start (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+
+ return gdbscm_scm_from_ulongest (BLOCK_START (block));
+}
+
+/* (block-end <gdb:block>) -> address */
+
+static SCM
+gdbscm_block_end (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+
+ return gdbscm_scm_from_ulongest (BLOCK_END (block));
+}
+
+/* (block-function <gdb:block>) -> <gdb:symbol> */
+
+static SCM
+gdbscm_block_function (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ struct symbol *sym;
+
+ sym = BLOCK_FUNCTION (block);
+
+ if (sym != NULL)
+ return syscm_scm_from_symbol (sym);
+ return SCM_BOOL_F;
+}
+
+/* (block-superblock <gdb:block>) -> <gdb:block> */
+
+static SCM
+gdbscm_block_superblock (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ const struct block *super_block;
+
+ super_block = BLOCK_SUPERBLOCK (block);
+
+ if (super_block)
+ return bkscm_scm_from_block (super_block, b_smob->objfile);
+ return SCM_BOOL_F;
+}
+
+/* (block-global-block <gdb:block>) -> <gdb:block>
+ Returns the global block associated to this block. */
+
+static SCM
+gdbscm_block_global_block (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ const struct block *global_block;
+
+ global_block = block_global_block (block);
+
+ return bkscm_scm_from_block (global_block, b_smob->objfile);
+}
+
+/* (block-static-block <gdb:block>) -> <gdb:block>
+ Returns the static block associated to this block.
+ Returns #f if we cannot get the static block (this is the global block). */
+
+static SCM
+gdbscm_block_static_block (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ const struct block *static_block;
+
+ if (BLOCK_SUPERBLOCK (block) == NULL)
+ return SCM_BOOL_F;
+
+ static_block = block_static_block (block);
+
+ return bkscm_scm_from_block (static_block, b_smob->objfile);
+}
+
+/* (block-global? <gdb:block>) -> boolean
+ Returns #t if this block object is a global block. */
+
+static SCM
+gdbscm_block_global_p (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+
+ return scm_from_bool (BLOCK_SUPERBLOCK (block) == NULL);
+}
+
+/* (block-static? <gdb:block>) -> boolean
+ Returns #t if this block object is a static block. */
+
+static SCM
+gdbscm_block_static_p (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+
+ if (BLOCK_SUPERBLOCK (block) != NULL
+ && BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL)
+ return SCM_BOOL_T;
+ return SCM_BOOL_F;
+}
+
+/* (block-symbols <gdb:block>) -> list of <gdb:symbol objects
+ Returns a list of symbols of the block. */
+
+static SCM
+gdbscm_block_symbols (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ struct block_iterator iter;
+ struct symbol *sym;
+ SCM result;
+
+ result = SCM_EOL;
+
+ sym = block_iterator_first (block, &iter);
+
+ while (sym != NULL)
+ {
+ SCM s_scm = syscm_scm_from_symbol (sym);
+
+ result = scm_cons (s_scm, result);
+ sym = block_iterator_next (&iter);
+ }
+
+ return scm_reverse_x (result, SCM_EOL);
+}
+
+/* The <gdb:block-symbols-iterator> object,
+ for iterating over all symbols in a block. */
+
+/* The smob "mark" function for <gdb:block-symbols-iterator>. */
+
+static SCM
+bkscm_mark_block_syms_progress_smob (SCM self)
+{
+ block_syms_progress_smob *i_smob
+ = (block_syms_progress_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&i_smob->base);
+}
+
+/* The smob "print" function for <gdb:block-symbols-iterator>. */
+
+static int
+bkscm_print_block_syms_progress_smob (SCM self, SCM port,
+ scm_print_state *pstate)
+{
+ block_syms_progress_smob *i_smob
+ = (block_syms_progress_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s", block_syms_progress_smob_name);
+
+ if (i_smob->initialized_p)
+ {
+ switch (i_smob->iter.which)
+ {
+ case GLOBAL_BLOCK:
+ case STATIC_BLOCK:
+ {
+ struct symtab *s;
+
+ gdbscm_printf (port, " %s",
+ i_smob->iter.which == GLOBAL_BLOCK
+ ? "global" : "static");
+ if (i_smob->iter.idx != -1)
+ gdbscm_printf (port, " @%d", i_smob->iter.idx);
+ s = (i_smob->iter.idx == -1
+ ? i_smob->iter.d.symtab
+ : i_smob->iter.d.symtab->includes[i_smob->iter.idx]);
+ gdbscm_printf (port, " %s", symtab_to_filename_for_display (s));
+ break;
+ }
+ case FIRST_LOCAL_BLOCK:
+ gdbscm_printf (port, " single block");
+ break;
+ }
+ }
+ else
+ gdbscm_printf (port, " !initialized");
+
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:block-symbols-progress> object. */
+
+static SCM
+bkscm_make_block_syms_progress_smob (void)
+{
+ block_syms_progress_smob *i_smob = (block_syms_progress_smob *)
+ scm_gc_malloc (sizeof (block_syms_progress_smob),
+ block_syms_progress_smob_name);
+ SCM smob;
+
+ memset (&i_smob->iter, 0, sizeof (i_smob->iter));
+ i_smob->initialized_p = 0;
+ smob = scm_new_smob (block_syms_progress_smob_tag, (scm_t_bits) i_smob);
+ gdbscm_init_gsmob (&i_smob->base);
+
+ return smob;
+}
+
+/* Returns non-zero if SCM is a <gdb:block-symbols-progress> object. */
+
+static int
+bkscm_is_block_syms_progress (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (block_syms_progress_smob_tag, scm);
+}
+
+/* (block-symbols-progress? scm) -> boolean */
+
+static SCM
+bkscm_block_syms_progress_p (SCM scm)
+{
+ return scm_from_bool (bkscm_is_block_syms_progress (scm));
+}
+
+/* (make-block-symbols-iterator <gdb:block>) -> <gdb:iterator>
+ Return a <gdb:iterator> object for iterating over the symbols of SELF. */
+
+static SCM
+gdbscm_make_block_syms_iter (SCM self)
+{
+ block_smob *b_smob
+ = bkscm_get_valid_block_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct block *block = b_smob->block;
+ SCM progress, iter;
+
+ progress = bkscm_make_block_syms_progress_smob ();
+
+ iter = gdbscm_make_iterator (self, progress, bkscm_next_symbol_x_proc);
+
+ return iter;
+}
+
+/* Returns the next symbol in the iteration through the block's dictionary,
+ or (end-of-iteration).
+ This is the iterator_smob.next_x method. */
+
+static SCM
+gdbscm_block_next_symbol_x (SCM self)
+{
+ SCM progress, iter_scm, block_scm;
+ iterator_smob *iter_smob;
+ block_smob *b_smob;
+ const struct block *block;
+ block_syms_progress_smob *p_smob;
+ struct symbol *sym;
+
+ iter_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ iter_smob = (iterator_smob *) SCM_SMOB_DATA (iter_scm);
+
+ block_scm = itscm_iterator_smob_object (iter_smob);
+ b_smob = bkscm_get_valid_block_smob_arg_unsafe (block_scm,
+ SCM_ARG1, FUNC_NAME);
+ block = b_smob->block;
+
+ progress = itscm_iterator_smob_progress (iter_smob);
+
+ SCM_ASSERT_TYPE (bkscm_is_block_syms_progress (progress),
+ progress, SCM_ARG1, FUNC_NAME,
+ block_syms_progress_smob_name);
+ p_smob = (block_syms_progress_smob *) SCM_SMOB_DATA (progress);
+
+ if (!p_smob->initialized_p)
+ {
+ sym = block_iterator_first (block, &p_smob->iter);
+ p_smob->initialized_p = 1;
+ }
+ else
+ sym = block_iterator_next (&p_smob->iter);
+
+ if (sym == NULL)
+ return gdbscm_end_of_iteration ();
+
+ return syscm_scm_from_symbol (sym);
+}
+
+/* (lookup-block address) -> <gdb:block>
+ Returns the innermost lexical block containing the specified pc value,
+ or #f if there is none. */
+
+static SCM
+gdbscm_lookup_block (SCM pc_scm)
+{
+ CORE_ADDR pc;
+ struct block *block = NULL;
+ struct obj_section *section = NULL;
+ struct symtab *symtab = NULL;
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "U", pc_scm, &pc);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ section = find_pc_mapped_section (pc);
+ symtab = find_pc_sect_symtab (pc, section);
+
+ if (symtab != NULL && symtab->objfile != NULL)
+ block = block_for_pc (pc);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (symtab == NULL || symtab->objfile == NULL)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, pc_scm,
+ _("cannot locate object file for block"));
+ }
+
+ if (block != NULL)
+ return bkscm_scm_from_block (block, symtab->objfile);
+ return SCM_BOOL_F;
+}
+
+/* Initialize the Scheme block support. */
+
+static const scheme_function block_functions[] =
+{
+ { "block?", 1, 0, 0, gdbscm_block_p,
+ "\
+Return #t if the object is a <gdb:block> object." },
+
+ { "block-valid?", 1, 0, 0, gdbscm_block_valid_p,
+ "\
+Return #t if the block is valid.\n\
+A block becomes invalid when its objfile is freed." },
+
+ { "block-start", 1, 0, 0, gdbscm_block_start,
+ "\
+Return the start address of the block." },
+
+ { "block-end", 1, 0, 0, gdbscm_block_end,
+ "\
+Return the end address of the block." },
+
+ { "block-function", 1, 0, 0, gdbscm_block_function,
+ "\
+Return the gdb:symbol object of the function containing the block\n\
+or #f if the block does not live in any function." },
+
+ { "block-superblock", 1, 0, 0, gdbscm_block_superblock,
+ "\
+Return the superblock (parent block) of the block." },
+
+ { "block-global-block", 1, 0, 0, gdbscm_block_global_block,
+ "\
+Return the global block of the block." },
+
+ { "block-static-block", 1, 0, 0, gdbscm_block_static_block,
+ "\
+Return the static block of the block." },
+
+ { "block-global?", 1, 0, 0, gdbscm_block_global_p,
+ "\
+Return #t if block is a global block." },
+
+ { "block-static?", 1, 0, 0, gdbscm_block_static_p,
+ "\
+Return #t if block is a static block." },
+
+ { "block-symbols", 1, 0, 0, gdbscm_block_symbols,
+ "\
+Return a list of all symbols (as <gdb:symbol> objects) in the block." },
+
+ { "make-block-symbols-iterator", 1, 0, 0, gdbscm_make_block_syms_iter,
+ "\
+Return a <gdb:iterator> object for iterating over all symbols in the block." },
+
+ { "block-symbols-progress?", 1, 0, 0, bkscm_block_syms_progress_p,
+ "\
+Return #t if the object is a <gdb:block-symbols-progress> object." },
+
+ { "lookup-block", 1, 0, 0, gdbscm_lookup_block,
+ "\
+Return the innermost GDB block containing the address or #f if none found.\n\
+\n\
+ Arguments:\n\
+ address: the address to lookup" },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_blocks (void)
+{
+ block_smob_tag
+ = gdbscm_make_smob_type (block_smob_name, sizeof (block_smob));
+ scm_set_smob_mark (block_smob_tag, bkscm_mark_block_smob);
+ scm_set_smob_free (block_smob_tag, bkscm_free_block_smob);
+ scm_set_smob_print (block_smob_tag, bkscm_print_block_smob);
+
+ block_syms_progress_smob_tag
+ = gdbscm_make_smob_type (block_syms_progress_smob_name,
+ sizeof (block_syms_progress_smob));
+ scm_set_smob_mark (block_syms_progress_smob_tag,
+ bkscm_mark_block_syms_progress_smob);
+ scm_set_smob_print (block_syms_progress_smob_tag,
+ bkscm_print_block_syms_progress_smob);
+
+ gdbscm_define_functions (block_functions, 1);
+
+ /* This function is "private". */
+ bkscm_next_symbol_x_proc
+ = scm_c_define_gsubr ("%block-next-symbol!", 1, 0, 0,
+ gdbscm_block_next_symbol_x);
+ scm_set_procedure_property_x (bkscm_next_symbol_x_proc,
+ gdbscm_documentation_symbol,
+ gdbscm_scm_from_c_string ("\
+Internal function to assist the block symbols iterator."));
+
+ /* Register an objfile "free" callback so we can properly
+ invalidate blocks when an object file is about to be deleted. */
+ bkscm_objfile_data_key
+ = register_objfile_data_with_cleanup (NULL, bkscm_del_objfile_blocks);
+}
diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c
new file mode 100644
index 0000000..d022377
--- /dev/null
+++ b/gdb/guile/scm-breakpoint.c
@@ -0,0 +1,1200 @@
+/* Scheme interface to breakpoints.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "value.h"
+#include "exceptions.h"
+#include "breakpoint.h"
+#include "gdbcmd.h"
+#include "gdbthread.h"
+#include "observer.h"
+#include "cli/cli-script.h"
+#include "ada-lang.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "guile-internal.h"
+
+/* The <gdb:breakpoint> smob.
+ N.B.: The name of this struct is known to breakpoint.h. */
+
+typedef struct gdbscm_breakpoint_object
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The breakpoint number according to gdb.
+ This is recorded here because BP will be NULL when deleted. */
+ int number;
+
+ /* The gdb breakpoint object, or NULL if the breakpoint has been deleted. */
+ struct breakpoint *bp;
+
+ /* Backlink to our containing <gdb:breakpoint> smob.
+ This is needed when we are deleted, we need to unprotect the object
+ from GC. */
+ SCM containing_scm;
+
+ /* A stop condition or #f. */
+ SCM stop;
+} breakpoint_smob;
+
+static const char breakpoint_smob_name[] = "gdb:breakpoint";
+
+/* The tag Guile knows the breakpoint smob by. */
+static scm_t_bits breakpoint_smob_tag;
+
+/* Variables used to pass information between the breakpoint_smob
+ constructor and the breakpoint-created hook function. */
+static SCM pending_breakpoint_scm = SCM_BOOL_F;
+
+/* Keywords used by create-breakpoint!. */
+static SCM type_keyword;
+static SCM wp_class_keyword;
+static SCM internal_keyword;
+
+/* Administrivia for breakpoint smobs. */
+
+/* The smob "mark" function for <gdb:breakpoint>. */
+
+static SCM
+bpscm_mark_breakpoint_smob (SCM self)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+
+ /* We don't mark containing_scm here. It is just a backlink to our
+ container, and is gc'protected until the breakpoint is deleted. */
+
+ scm_gc_mark (bp_smob->stop);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&bp_smob->base);
+}
+
+/* The smob "free" function for <gdb:breakpoint>. */
+
+static size_t
+bpscm_free_breakpoint_smob (SCM self)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+
+ if (bp_smob->bp)
+ bp_smob->bp->scm_bp_object = NULL;
+
+ /* Not necessary, done to catch bugs. */
+ bp_smob->bp = NULL;
+ bp_smob->containing_scm = SCM_UNDEFINED;
+ bp_smob->stop = SCM_UNDEFINED;
+
+ return 0;
+}
+
+/* Return the name of TYPE.
+ This doesn't handle all types, just the ones we export. */
+
+static const char *
+bpscm_type_to_string (enum bptype type)
+{
+ switch (type)
+ {
+ case bp_none: return "BP_NONE";
+ case bp_breakpoint: return "BP_BREAKPOINT";
+ case bp_watchpoint: return "BP_WATCHPOINT";
+ case bp_hardware_watchpoint: return "BP_HARDWARE_WATCHPOINT";
+ case bp_read_watchpoint: return "BP_READ_WATCHPOINT";
+ case bp_access_watchpoint: return "BP_ACCESS_WATCHPOINT";
+ default: return "internal/other";
+ }
+}
+
+/* Return the name of ENABLE_STATE. */
+
+static const char *
+bpscm_enable_state_to_string (enum enable_state enable_state)
+{
+ switch (enable_state)
+ {
+ case bp_disabled: return "disabled";
+ case bp_enabled: return "enabled";
+ case bp_call_disabled: return "call_disabled";
+ case bp_permanent: return "permanent";
+ default: return "unknown";
+ }
+}
+
+/* The smob "print" function for <gdb:breakpoint>. */
+
+static int
+bpscm_print_breakpoint_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+ struct breakpoint *b = bp_smob->bp;
+
+ gdbscm_printf (port, "#<%s", breakpoint_smob_name);
+
+ /* Only print what we export to the user.
+ The rest are possibly internal implementation details. */
+
+ gdbscm_printf (port, " #%d", bp_smob->number);
+
+ /* Careful, the breakpoint may be invalid. */
+ if (b != NULL)
+ {
+ gdbscm_printf (port, " %s %s %s",
+ bpscm_type_to_string (b->type),
+ bpscm_enable_state_to_string (b->enable_state),
+ b->silent ? "silent" : "noisy");
+
+ gdbscm_printf (port, " hit:%d", b->hit_count);
+ gdbscm_printf (port, " ignore:%d", b->ignore_count);
+
+ if (b->addr_string != NULL)
+ gdbscm_printf (port, " @%s", b->addr_string);
+ }
+
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:breakpoint> object. */
+
+static SCM
+bpscm_make_breakpoint_smob (void)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *)
+ scm_gc_malloc (sizeof (breakpoint_smob), breakpoint_smob_name);
+ SCM bp_scm;
+
+ bp_smob->number = -1;
+ bp_smob->bp = NULL;
+ bp_smob->stop = SCM_BOOL_F;
+ bp_scm = scm_new_smob (breakpoint_smob_tag, (scm_t_bits) bp_smob);
+ bp_smob->containing_scm = bp_scm;
+ gdbscm_init_gsmob (&bp_smob->base);
+
+ return bp_scm;
+}
+
+/* Return non-zero if we want a Scheme wrapper for breakpoint B.
+ If FROM_SCHEME is non-zero,this is called for a breakpoint created
+ by the user from Scheme. Otherwise it is zero. */
+
+static int
+bpscm_want_scm_wrapper_p (struct breakpoint *bp, int from_scheme)
+{
+ /* Don't create <gdb:breakpoint> objects for internal GDB breakpoints. */
+ if (bp->number < 0 && !from_scheme)
+ return 0;
+
+ /* The others are not supported. */
+ if (bp->type != bp_breakpoint
+ && bp->type != bp_watchpoint
+ && bp->type != bp_hardware_watchpoint
+ && bp->type != bp_read_watchpoint
+ && bp->type != bp_access_watchpoint)
+ return 0;
+
+ return 1;
+}
+
+/* Install the Scheme side of a breakpoint, CONTAINING_SCM, in
+ the gdb side BP. */
+
+static void
+bpscm_attach_scm_to_breakpoint (struct breakpoint *bp, SCM containing_scm)
+{
+ breakpoint_smob *bp_smob;
+
+ bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (containing_scm);
+ bp_smob->number = bp->number;
+ bp_smob->bp = bp;
+ bp_smob->containing_scm = containing_scm;
+ bp_smob->bp->scm_bp_object = bp_smob;
+
+ /* The owner of this breakpoint is not in GC-controlled memory, so we need
+ to protect it from GC until the breakpoint is deleted. */
+ scm_gc_protect_object (containing_scm);
+}
+
+/* Return non-zero if SCM is a breakpoint smob. */
+
+static int
+bpscm_is_breakpoint (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (breakpoint_smob_tag, scm);
+}
+
+/* (breakpoint? scm) -> boolean */
+
+static SCM
+gdbscm_breakpoint_p (SCM scm)
+{
+ return scm_from_bool (bpscm_is_breakpoint (scm));
+}
+
+/* Returns the <gdb:breakpoint> object in SELF.
+ Throws an exception if SELF is not a <gdb:breakpoint> object. */
+
+static SCM
+bpscm_get_breakpoint_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (bpscm_is_breakpoint (self), self, arg_pos, func_name,
+ breakpoint_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the breakpoint smob of SELF.
+ Throws an exception if SELF is not a <gdb:breakpoint> object. */
+
+static breakpoint_smob *
+bpscm_get_breakpoint_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ SCM bp_scm = bpscm_get_breakpoint_arg_unsafe (self, arg_pos, func_name);
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (bp_scm);
+
+ return bp_smob;
+}
+
+/* Return non-zero if breakpoint BP_SMOB is valid. */
+
+static int
+bpscm_is_valid (breakpoint_smob *bp_smob)
+{
+ return bp_smob->bp != NULL;
+}
+
+/* Returns the breakpoint smob in SELF, verifying it's valid.
+ Throws an exception if SELF is not a <gdb:breakpoint> object,
+ or is invalid. */
+
+static breakpoint_smob *
+bpscm_get_valid_breakpoint_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_breakpoint_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!bpscm_is_valid (bp_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:breakpoint>"));
+ }
+
+ return bp_smob;
+}
+
+/* Breakpoint methods. */
+
+/* (create-breakpoint! string [#:type integer] [#:wp-class integer]
+ [#:internal boolean) -> <gdb:breakpoint> */
+
+static SCM
+gdbscm_create_breakpoint_x (SCM spec_scm, SCM rest)
+{
+ const SCM keywords[] = {
+ type_keyword, wp_class_keyword, internal_keyword, SCM_BOOL_F
+ };
+ char *spec;
+ int type_arg_pos = -1, access_type_arg_pos = -1, internal_arg_pos = -1;
+ int type = bp_breakpoint;
+ int access_type = hw_write;
+ int internal = 0;
+ SCM result;
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#iit",
+ spec_scm, &spec, rest,
+ &type_arg_pos, &type,
+ &access_type_arg_pos, &access_type,
+ &internal_arg_pos, &internal);
+
+ result = bpscm_make_breakpoint_smob ();
+ pending_breakpoint_scm = result;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup = make_cleanup (xfree, spec);
+
+ switch (type)
+ {
+ case bp_breakpoint:
+ {
+ create_breakpoint (get_current_arch (),
+ spec, NULL, -1, NULL,
+ 0,
+ 0, bp_breakpoint,
+ 0,
+ AUTO_BOOLEAN_TRUE,
+ &bkpt_breakpoint_ops,
+ 0, 1, internal, 0);
+ break;
+ }
+ case bp_watchpoint:
+ {
+ if (access_type == hw_write)
+ watch_command_wrapper (spec, 0, internal);
+ else if (access_type == hw_access)
+ awatch_command_wrapper (spec, 0, internal);
+ else if (access_type == hw_read)
+ rwatch_command_wrapper (spec, 0, internal);
+ else
+ error (_("Invalid watchpoint access type"));
+ break;
+ }
+ default:
+ error (_("Invalid breakpoint type"));
+ }
+
+ do_cleanups (cleanup);
+ }
+ /* Ensure this gets reset, even if there's an error. */
+ pending_breakpoint_scm = SCM_BOOL_F;
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return result;
+}
+
+/* (breakpoint-delete! <gdb:breakpoint>) -> unspecified
+ Scheme function which deletes the underlying GDB breakpoint. This
+ triggers the breakpoint_deleted observer which will call
+ gdbscm_breakpoint_deleted; that function cleans up the Scheme sections. */
+
+static SCM
+gdbscm_breakpoint_delete_x (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ delete_breakpoint (bp_smob->bp);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* iterate_over_breakpoints function for gdbscm_breakpoints. */
+
+static int
+bpscm_build_bp_list (struct breakpoint *bp, void *arg)
+{
+ SCM *list = arg;
+ breakpoint_smob *bp_smob = bp->scm_bp_object;
+
+ /* Lazily create wrappers for breakpoints created outside Scheme. */
+
+ if (bp_smob == NULL)
+ {
+ if (bpscm_want_scm_wrapper_p (bp, 0))
+ {
+ SCM bp_scm;
+
+ bp_scm = bpscm_make_breakpoint_smob ();
+ bpscm_attach_scm_to_breakpoint (bp, bp_scm);
+ /* Refetch it. */
+ bp_smob = bp->scm_bp_object;
+ }
+ }
+
+ /* Not all breakpoints will have a companion Scheme object.
+ Only breakpoints that trigger the created_breakpoint observer call,
+ and satisfy certain conditions (see bpscm_want_scm_wrapper_p),
+ get a companion object (this includes Scheme-created breakpoints). */
+
+ if (bp_smob != NULL)
+ *list = scm_cons (bp_smob->containing_scm, *list);
+
+ return 0;
+}
+
+/* (breakpoints) -> list
+ Return a list of all breakpoints. */
+
+static SCM
+gdbscm_breakpoints (void)
+{
+ SCM list = SCM_EOL;
+
+ /* If iterate_over_breakpoints returns non-NULL it means the iteration
+ terminated early.
+ In that case abandon building the list and return #f. */
+ if (iterate_over_breakpoints (bpscm_build_bp_list, &list) != NULL)
+ return SCM_BOOL_F;
+
+ return scm_reverse_x (list, SCM_EOL);
+}
+
+/* (breakpoint-valid? <gdb:breakpoint>) -> boolean
+ Returns #t if SELF is still valid. */
+
+static SCM
+gdbscm_breakpoint_valid_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bpscm_is_valid (bp_smob));
+}
+
+/* (breakpoint-enabled? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_enabled_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->enable_state == bp_enabled);
+}
+
+/* (set-breakpoint-enabled? <gdb:breakpoint> boolean) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_enabled_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (gdbscm_is_bool (newvalue), newvalue, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (gdbscm_is_true (newvalue))
+ enable_breakpoint (bp_smob->bp);
+ else
+ disable_breakpoint (bp_smob->bp);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-silent? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_silent_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->silent);
+}
+
+/* (set-breakpoint-silent?! <gdb:breakpoint> boolean) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_silent_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (gdbscm_is_bool (newvalue), newvalue, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ breakpoint_set_silent (bp_smob->bp, gdbscm_is_true (newvalue));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-ignore-count <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_ignore_count (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->ignore_count);
+}
+
+/* (set-breakpoint-ignore-count! <gdb:breakpoint> integer)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_ignore_count_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long value;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX),
+ newvalue, SCM_ARG2, FUNC_NAME, _("integer"));
+
+ value = scm_to_long (newvalue);
+ if (value < 0)
+ value = 0;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ set_ignore_count (bp_smob->number, (int) value, 0);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-hit-count <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_hit_count (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->hit_count);
+}
+
+/* (set-breakpoint-hit-count! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_hit_count_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long value;
+
+ SCM_ASSERT_TYPE (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX),
+ newvalue, SCM_ARG2, FUNC_NAME, _("integer"));
+
+ value = scm_to_long (newvalue);
+ if (value < 0)
+ value = 0;
+
+ if (value != 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("hit-count must be zero"));
+ }
+
+ bp_smob->bp->hit_count = 0;
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-thread <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_thread (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ if (bp_smob->bp->thread == -1)
+ return SCM_BOOL_F;
+
+ return scm_from_long (bp_smob->bp->thread);
+}
+
+/* (set-breakpoint-thread! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_thread_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long id;
+
+ if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
+ {
+ id = scm_to_long (newvalue);
+ if (! valid_thread_id (id))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("invalid thread id"));
+ }
+ }
+ else if (gdbscm_is_false (newvalue))
+ id = -1;
+ else
+ SCM_ASSERT_TYPE (0, newvalue, SCM_ARG2, FUNC_NAME, _("integer or #f"));
+
+ breakpoint_set_thread (bp_smob->bp, id);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-task <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_task (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ if (bp_smob->bp->task == 0)
+ return SCM_BOOL_F;
+
+ return scm_from_long (bp_smob->bp->task);
+}
+
+/* (set-breakpoint-task! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_task_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long id;
+ int valid_id = 0;
+ volatile struct gdb_exception except;
+
+ if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
+ {
+ id = scm_to_long (newvalue);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ valid_id = valid_task_id (id);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (! valid_id)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("invalid task id"));
+ }
+ }
+ else if (gdbscm_is_false (newvalue))
+ id = 0;
+ else
+ SCM_ASSERT_TYPE (0, newvalue, SCM_ARG2, FUNC_NAME, _("integer or #f"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ breakpoint_set_task (bp_smob->bp, id);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-location <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_location (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+
+ if (bp_smob->bp->type != bp_breakpoint)
+ return SCM_BOOL_F;
+
+ str = bp_smob->bp->addr_string;
+ if (! str)
+ str = "";
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (breakpoint-expression <gdb:breakpoint>) -> string
+ This is only valid for watchpoints.
+ Returns #f for non-watchpoints. */
+
+static SCM
+gdbscm_breakpoint_expression (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+ struct watchpoint *wp;
+
+ if (!is_watchpoint (bp_smob->bp))
+ return SCM_BOOL_F;
+
+ wp = (struct watchpoint *) bp_smob->bp;
+
+ str = wp->exp_string;
+ if (! str)
+ str = "";
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (breakpoint-condition <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_condition (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+
+ str = bp_smob->bp->cond_string;
+ if (! str)
+ return SCM_BOOL_F;
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (set-breakpoint-condition! <gdb:breakpoint> string|#f)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_condition_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *exp;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (scm_is_string (newvalue) || gdbscm_is_false (newvalue),
+ newvalue, SCM_ARG2, FUNC_NAME,
+ _("string or #f"));
+
+ if (gdbscm_is_false (newvalue))
+ exp = NULL;
+ else
+ exp = gdbscm_scm_to_c_string (newvalue);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ set_breakpoint_condition (bp_smob->bp, exp ? exp : "", 0);
+ }
+ xfree (exp);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-stop <gdb:breakpoint>) -> procedure or #f */
+
+static SCM
+gdbscm_breakpoint_stop (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return bp_smob->stop;
+}
+
+/* (set-breakpoint-stop! <gdb:breakpoint> procedure|#f)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_stop_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct extension_language_defn *extlang = NULL;
+
+ SCM_ASSERT_TYPE (gdbscm_is_procedure (newvalue)
+ || gdbscm_is_false (newvalue),
+ newvalue, SCM_ARG2, FUNC_NAME,
+ _("procedure or #f"));
+
+ if (bp_smob->bp->cond_string != NULL)
+ extlang = get_ext_lang_defn (EXT_LANG_GDB);
+ if (extlang == NULL)
+ extlang = get_breakpoint_cond_ext_lang (bp_smob->bp, EXT_LANG_GUILE);
+ if (extlang != NULL)
+ {
+ char *error_text
+ = xstrprintf (_("Only one stop condition allowed. There is"
+ " currently a %s stop condition defined for"
+ " this breakpoint."),
+ ext_lang_capitalized_name (extlang));
+
+ scm_dynwind_begin (0);
+ gdbscm_dynwind_xfree (error_text);
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self, error_text);
+ /* The following line, while unnecessary, is present for completeness
+ sake. */
+ scm_dynwind_end ();
+ }
+
+ bp_smob->stop = newvalue;
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-commands <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_commands (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct breakpoint *bp;
+ long length;
+ volatile struct gdb_exception except;
+ struct ui_file *string_file;
+ struct cleanup *chain;
+ SCM result;
+ char *cmdstr;
+
+ bp = bp_smob->bp;
+
+ if (bp->commands == NULL)
+ return SCM_BOOL_F;
+
+ string_file = mem_fileopen ();
+ chain = make_cleanup_ui_file_delete (string_file);
+
+ ui_out_redirect (current_uiout, string_file);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ print_command_lines (current_uiout, breakpoint_commands (bp), 0);
+ }
+ ui_out_redirect (current_uiout, NULL);
+ if (except.reason < 0)
+ {
+ do_cleanups (chain);
+ gdbscm_throw_gdb_exception (except);
+ }
+
+ cmdstr = ui_file_xstrdup (string_file, &length);
+ make_cleanup (xfree, cmdstr);
+ result = gdbscm_scm_from_c_string (cmdstr);
+
+ do_cleanups (chain);
+ return result;
+}
+
+/* (breakpoint-type <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_type (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->type);
+}
+
+/* (breakpoint-visible? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_visible (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->number >= 0);
+}
+
+/* (breakpoint-number <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_number (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->number);
+}
+
+/* Return TRUE if "stop" has been set for this breakpoint.
+
+ This is the extension_language_ops.breakpoint_has_cond "method". */
+
+int
+gdbscm_breakpoint_has_cond (const struct extension_language_defn *extlang,
+ struct breakpoint *b)
+{
+ breakpoint_smob *bp_smob = b->scm_bp_object;
+
+ if (bp_smob == NULL)
+ return 0;
+
+ return gdbscm_is_procedure (bp_smob->stop);
+}
+
+/* Call the "stop" method in the breakpoint class.
+ This must only be called if gdbscm_breakpoint_has_cond returns true.
+ If the stop method returns #t, the inferior will be stopped at the
+ breakpoint. Otherwise the inferior will be allowed to continue
+ (assuming other conditions don't indicate "stop").
+
+ This is the extension_language_ops.breakpoint_cond_says_stop "method". */
+
+enum ext_lang_bp_stop
+gdbscm_breakpoint_cond_says_stop
+ (const struct extension_language_defn *extlang, struct breakpoint *b)
+{
+ breakpoint_smob *bp_smob = b->scm_bp_object;
+ SCM predicate_result;
+ int stop;
+
+ if (bp_smob == NULL)
+ return EXT_LANG_BP_STOP_UNSET;
+ if (!gdbscm_is_procedure (bp_smob->stop))
+ return EXT_LANG_BP_STOP_UNSET;
+
+ stop = 1;
+
+ predicate_result
+ = gdbscm_safe_call_1 (bp_smob->stop, bp_smob->containing_scm, NULL);
+
+ if (gdbscm_is_exception (predicate_result))
+ ; /* Exception already printed. */
+ /* If the "stop" function returns #f that means
+ the Scheme breakpoint wants GDB to continue. */
+ else if (gdbscm_is_false (predicate_result))
+ stop = 0;
+
+ return stop ? EXT_LANG_BP_STOP_YES : EXT_LANG_BP_STOP_NO;
+}
+
+/* Event callback functions. */
+
+/* Callback that is used when a breakpoint is created.
+ For breakpoints created by Scheme, i.e., gdbscm_create_breakpoint_x, finish
+ object creation by connecting the Scheme wrapper to the gdb object.
+ We ignore breakpoints created from gdb or python here, we create the
+ Scheme wrapper for those when there's a need to, e.g.,
+ gdbscm_breakpoints. */
+
+static void
+bpscm_breakpoint_created (struct breakpoint *bp)
+{
+ SCM bp_scm;
+
+ if (gdbscm_is_false (pending_breakpoint_scm))
+ return;
+
+ /* Verify our caller error checked the user's request. */
+ gdb_assert (bpscm_want_scm_wrapper_p (bp, 1));
+
+ bp_scm = pending_breakpoint_scm;
+ pending_breakpoint_scm = SCM_BOOL_F;
+
+ bpscm_attach_scm_to_breakpoint (bp, bp_scm);
+}
+
+/* Callback that is used when a breakpoint is deleted. This will
+ invalidate the corresponding Scheme object. */
+
+static void
+bpscm_breakpoint_deleted (struct breakpoint *b)
+{
+ int num = b->number;
+ struct breakpoint *bp;
+
+ /* TODO: Why the lookup? We have B. */
+
+ bp = get_breakpoint (num);
+ if (bp)
+ {
+ breakpoint_smob *bp_smob = bp->scm_bp_object;
+
+ if (bp_smob)
+ {
+ bp_smob->bp = NULL;
+ scm_gc_unprotect_object (bp_smob->containing_scm);
+ }
+ }
+}
+
+/* Initialize the Scheme breakpoint code. */
+
+static const scheme_integer_constant breakpoint_integer_constants[] =
+{
+ { "BP_NONE", bp_none },
+ { "BP_BREAKPOINT", bp_breakpoint },
+ { "BP_WATCHPOINT", bp_watchpoint },
+ { "BP_HARDWARE_WATCHPOINT", bp_hardware_watchpoint },
+ { "BP_READ_WATCHPOINT", bp_read_watchpoint },
+ { "BP_ACCESS_WATCHPOINT", bp_access_watchpoint },
+
+ { "WP_READ", hw_read },
+ { "WP_WRITE", hw_write },
+ { "WP_ACCESS", hw_access },
+
+ END_INTEGER_CONSTANTS
+};
+
+static const scheme_function breakpoint_functions[] =
+{
+ { "create-breakpoint!", 1, 0, 1, gdbscm_create_breakpoint_x,
+ "\
+Create and install a GDB breakpoint object.\n\
+\n\
+ Arguments:\n\
+ location [#:type <type>] [#:wp-class <wp-class>] [#:internal <bool>]" },
+
+ { "breakpoint-delete!", 1, 0, 0, gdbscm_breakpoint_delete_x,
+ "\
+Delete the breakpoint from GDB." },
+
+ { "breakpoints", 0, 0, 0, gdbscm_breakpoints,
+ "\
+Return a list of all GDB breakpoints.\n\
+\n\
+ Arguments: none" },
+
+ { "breakpoint?", 1, 0, 0, gdbscm_breakpoint_p,
+ "\
+Return #t if the object is a <gdb:breakpoint> object." },
+
+ { "breakpoint-valid?", 1, 0, 0, gdbscm_breakpoint_valid_p,
+ "\
+Return #t if the breakpoint has not been deleted from GDB." },
+
+ { "breakpoint-number", 1, 0, 0, gdbscm_breakpoint_number,
+ "\
+Return the breakpoint's number." },
+
+ { "breakpoint-type", 1, 0, 0, gdbscm_breakpoint_type,
+ "\
+Return the type of the breakpoint." },
+
+ { "breakpoint-visible?", 1, 0, 0, gdbscm_breakpoint_visible,
+ "\
+Return #t if the breakpoint is visible to the user." },
+
+ { "breakpoint-location", 1, 0, 0, gdbscm_breakpoint_location,
+ "\
+Return the location of the breakpoint as specified by the user." },
+
+ { "breakpoint-expression", 1, 0, 0, gdbscm_breakpoint_expression,
+ "\
+Return the expression of the breakpoint as specified by the user.\n\
+Valid for watchpoints only, returns #f for non-watchpoints." },
+
+ { "breakpoint-enabled?", 1, 0, 0, gdbscm_breakpoint_enabled_p,
+ "\
+Return #t if the breakpoint is enabled." },
+
+ { "set-breakpoint-enabled!", 2, 0, 0, gdbscm_set_breakpoint_enabled_x,
+ "\
+Set the breakpoint's enabled state.\n\
+\n\
+ Arguments: <gdb:breakpoint boolean" },
+
+ { "breakpoint-silent?", 1, 0, 0, gdbscm_breakpoint_silent_p,
+ "\
+Return #t if the breakpoint is silent." },
+
+ { "set-breakpoint-silent!", 2, 0, 0, gdbscm_set_breakpoint_silent_x,
+ "\
+Set the breakpoint's silent state.\n\
+\n\
+ Arguments: <gdb:breakpoint> boolean" },
+
+ { "breakpoint-ignore-count", 1, 0, 0, gdbscm_breakpoint_ignore_count,
+ "\
+Return the breakpoint's \"ignore\" count." },
+
+ { "set-breakpoint-ignore-count!", 2, 0, 0,
+ gdbscm_set_breakpoint_ignore_count_x,
+ "\
+Set the breakpoint's \"ignore\" count.\n\
+\n\
+ Arguments: <gdb:breakpoint> count" },
+
+ { "breakpoint-hit-count", 1, 0, 0, gdbscm_breakpoint_hit_count,
+ "\
+Return the breakpoint's \"hit\" count." },
+
+ { "set-breakpoint-hit-count!", 2, 0, 0, gdbscm_set_breakpoint_hit_count_x,
+ "\
+Set the breakpoint's \"hit\" count. The value must be zero.\n\
+\n\
+ Arguments: <gdb:breakpoint> 0" },
+
+ { "breakpoint-thread", 1, 0, 0, gdbscm_breakpoint_thread,
+ "\
+Return the breakpoint's thread id or #f if there isn't one." },
+
+ { "set-breakpoint-thread!", 2, 0, 0, gdbscm_set_breakpoint_thread_x,
+ "\
+Set the thread id for this breakpoint.\n\
+\n\
+ Arguments: <gdb:breakpoint> thread-id" },
+
+ { "breakpoint-task", 1, 0, 0, gdbscm_breakpoint_task,
+ "\
+Return the breakpoint's Ada task-id or #f if there isn't one." },
+
+ { "set-breakpoint-task!", 2, 0, 0, gdbscm_set_breakpoint_task_x,
+ "\
+Set the breakpoint's Ada task-id.\n\
+\n\
+ Arguments: <gdb:breakpoint> task-id" },
+
+ { "breakpoint-condition", 1, 0, 0, gdbscm_breakpoint_condition,
+ "\
+Return the breakpoint's condition as specified by the user.\n\
+Return #f if there isn't one." },
+
+ { "set-breakpoint-condition!", 2, 0, 0, gdbscm_set_breakpoint_condition_x,
+ "\
+Set the breakpoint's condition.\n\
+\n\
+ Arguments: <gdb:breakpoint> condition\n\
+ condition: a string" },
+
+ { "breakpoint-stop", 1, 0, 0, gdbscm_breakpoint_stop,
+ "\
+Return the breakpoint's stop predicate.\n\
+Return #f if there isn't one." },
+
+ { "set-breakpoint-stop!", 2, 0, 0, gdbscm_set_breakpoint_stop_x,
+ "\
+Set the breakpoint's stop predicate.\n\
+\n\
+ Arguments: <gdb:breakpoint> procedure\n\
+ procedure: A procedure of one argument, the breakpoint.\n\
+ Its result is true if program execution should stop." },
+
+ { "breakpoint-commands", 1, 0, 0, gdbscm_breakpoint_commands,
+ "\
+Return the breakpoint's commands." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_breakpoints (void)
+{
+ breakpoint_smob_tag
+ = gdbscm_make_smob_type (breakpoint_smob_name, sizeof (breakpoint_smob));
+ scm_set_smob_mark (breakpoint_smob_tag, bpscm_mark_breakpoint_smob);
+ scm_set_smob_free (breakpoint_smob_tag, bpscm_free_breakpoint_smob);
+ scm_set_smob_print (breakpoint_smob_tag, bpscm_print_breakpoint_smob);
+
+ observer_attach_breakpoint_created (bpscm_breakpoint_created);
+ observer_attach_breakpoint_deleted (bpscm_breakpoint_deleted);
+
+ gdbscm_define_integer_constants (breakpoint_integer_constants, 1);
+ gdbscm_define_functions (breakpoint_functions, 1);
+
+ type_keyword = scm_from_latin1_keyword ("type");
+ wp_class_keyword = scm_from_latin1_keyword ("wp-class");
+ internal_keyword = scm_from_latin1_keyword ("internal");
+}
diff --git a/gdb/guile/scm-disasm.c b/gdb/guile/scm-disasm.c
new file mode 100644
index 0000000..dc76b98
--- /dev/null
+++ b/gdb/guile/scm-disasm.c
@@ -0,0 +1,355 @@
+/* Scheme interface to architecture.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "disasm.h"
+#include "dis-asm.h"
+#include "gdbarch.h"
+#include "gdbcore.h" /* Why is memory_error here? */
+#include "guile-internal.h"
+
+static SCM port_keyword;
+static SCM offset_keyword;
+static SCM size_keyword;
+static SCM count_keyword;
+
+static SCM address_symbol;
+static SCM asm_symbol;
+static SCM length_symbol;
+
+/* Struct used to pass "application data" in disassemble_info. */
+
+struct gdbscm_disasm_data
+{
+ struct gdbarch *gdbarch;
+ SCM port;
+ /* The offset of the address of the first instruction in PORT. */
+ ULONGEST offset;
+};
+
+/* Struct used to pass data from gdbscm_disasm_read_memory to
+ gdbscm_disasm_read_memory_worker. */
+
+struct gdbscm_disasm_read_data
+{
+ bfd_vma memaddr;
+ bfd_byte *myaddr;
+ unsigned int length;
+ struct disassemble_info *dinfo;
+};
+
+/* Subroutine of gdbscm_arch_disassemble to simplify it.
+ Return the result for one instruction. */
+
+static SCM
+dascm_make_insn (CORE_ADDR pc, const char *assembly, int insn_len)
+{
+ return scm_list_3 (scm_cons (address_symbol,
+ gdbscm_scm_from_ulongest (pc)),
+ scm_cons (asm_symbol,
+ gdbscm_scm_from_c_string (assembly)),
+ scm_cons (length_symbol,
+ scm_from_int (insn_len)));
+}
+
+/* Helper function for gdbscm_disasm_read_memory to safely read from a
+ Scheme port. Called via gdbscm_call_guile.
+ The result is a statically allocated error message or NULL if success. */
+
+static void *
+gdbscm_disasm_read_memory_worker (void *datap)
+{
+ struct gdbscm_disasm_read_data *data = datap;
+ struct disassemble_info *dinfo = data->dinfo;
+ struct gdbscm_disasm_data *disasm_data = dinfo->application_data;
+ SCM seekto, newpos, port = disasm_data->port;
+ size_t bytes_read;
+
+ seekto = gdbscm_scm_from_ulongest (data->memaddr - disasm_data->offset);
+ newpos = scm_seek (port, seekto, scm_from_int (SEEK_SET));
+ if (!scm_is_eq (seekto, newpos))
+ return "seek error";
+
+ bytes_read = scm_c_read (port, data->myaddr, data->length);
+
+ if (bytes_read != data->length)
+ return "short read";
+
+ /* If we get here the read succeeded. */
+ return NULL;
+}
+
+/* disassemble_info.read_memory_func for gdbscm_print_insn_from_port. */
+
+static int
+gdbscm_disasm_read_memory (bfd_vma memaddr, bfd_byte *myaddr,
+ unsigned int length,
+ struct disassemble_info *dinfo)
+{
+ struct gdbscm_disasm_read_data data;
+ void *status;
+
+ data.memaddr = memaddr;
+ data.myaddr = myaddr;
+ data.length = length;
+ data.dinfo = dinfo;
+
+ status = gdbscm_with_guile (gdbscm_disasm_read_memory_worker, &data);
+
+ /* TODO: IWBN to distinguish problems reading target memory versus problems
+ with the port (e.g., EOF).
+ We return TARGET_XFER_E_IO here as that's what memory_error looks for. */
+ return status != NULL ? TARGET_XFER_E_IO : 0;
+}
+
+/* disassemble_info.memory_error_func for gdbscm_print_insn_from_port.
+ Technically speaking, we don't need our own memory_error_func,
+ but to not provide one would leave a subtle dependency in the code.
+ This function exists to keep a clear boundary. */
+
+static void
+gdbscm_disasm_memory_error (int status, bfd_vma memaddr,
+ struct disassemble_info *info)
+{
+ memory_error (status, memaddr);
+}
+
+/* disassemble_info.print_address_func for gdbscm_print_insn_from_port.
+ Since we need to use our own application_data value, we need to supply
+ this routine as well. */
+
+static void
+gdbscm_disasm_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+ struct gdbscm_disasm_data *data = info->application_data;
+ struct gdbarch *gdbarch = data->gdbarch;
+
+ print_address (gdbarch, addr, info->stream);
+}
+
+/* Subroutine of gdbscm_arch_disassemble to simplify it.
+ Call gdbarch_print_insn using a port for input.
+ PORT must be seekable.
+ OFFSET is the offset in PORT from which addresses begin.
+ For example, when printing from a bytevector, addresses passed to the
+ bv seek routines must be in the range [0,size). However, the bytevector
+ may represent an instruction at address 0x1234. To handle this case pass
+ 0x1234 for OFFSET.
+ This is based on gdb_print_insn, see it for details. */
+
+static int
+gdbscm_print_insn_from_port (struct gdbarch *gdbarch,
+ SCM port, ULONGEST offset, CORE_ADDR memaddr,
+ struct ui_file *stream, int *branch_delay_insns)
+{
+ struct disassemble_info di;
+ int length;
+ struct gdbscm_disasm_data data;
+
+ di = gdb_disassemble_info (gdbarch, stream);
+ data.gdbarch = gdbarch;
+ data.port = port;
+ data.offset = offset;
+ di.application_data = &data;
+ di.read_memory_func = gdbscm_disasm_read_memory;
+ di.memory_error_func = gdbscm_disasm_memory_error;
+ di.print_address_func = gdbscm_disasm_print_address;
+
+ length = gdbarch_print_insn (gdbarch, memaddr, &di);
+
+ if (branch_delay_insns)
+ {
+ if (di.insn_info_valid)
+ *branch_delay_insns = di.branch_delay_insns;
+ else
+ *branch_delay_insns = 0;
+ }
+
+ return length;
+}
+
+/* (arch-disassemble <gdb:arch> address
+ [#:port port] [#:offset address] [#:size integer] [#:count integer])
+ -> list
+
+ Returns a list of disassembled instructions.
+ If PORT is provided, read bytes from it. Otherwise read target memory.
+ If PORT is #f, read target memory.
+ PORT must be seekable. IWBN to remove this restriction, and a future
+ release may. For now the restriction is in place because it's not clear
+ all disassemblers are strictly sequential.
+ If SIZE is provided, limit the number of bytes read to this amount.
+ If COUNT is provided, limit the number of instructions to this amount.
+
+ Each instruction in the result is an alist:
+ (('address . address) ('asm . disassembly) ('length . length)).
+ We could use a hash table (dictionary) but there aren't that many fields. */
+
+static SCM
+gdbscm_arch_disassemble (SCM self, SCM start_scm, SCM rest)
+{
+ arch_smob *a_smob
+ = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct gdbarch *gdbarch = arscm_get_gdbarch (a_smob);
+ const SCM keywords[] = {
+ port_keyword, offset_keyword, size_keyword, count_keyword, SCM_BOOL_F
+ };
+ int port_arg_pos = -1, offset_arg_pos = -1;
+ int size_arg_pos = -1, count_arg_pos = -1;
+ SCM port = SCM_BOOL_F;
+ ULONGEST offset = 0;
+ unsigned int count = 1;
+ unsigned int size;
+ ULONGEST start_arg;
+ CORE_ADDR start, end;
+ CORE_ADDR pc;
+ unsigned int i;
+ int using_port;
+ SCM result;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "U#OUuu",
+ start_scm, &start_arg, rest,
+ &port_arg_pos, &port,
+ &offset_arg_pos, &offset,
+ &size_arg_pos, &size,
+ &count_arg_pos, &count);
+ /* START is first stored in a ULONGEST because we don't have a format char
+ for CORE_ADDR, and it's not really worth it to have one yet. */
+ start = start_arg;
+
+ if (port_arg_pos > 0)
+ {
+ SCM_ASSERT_TYPE (gdbscm_is_false (port)
+ || gdbscm_is_true (scm_input_port_p (port)),
+ port, port_arg_pos, FUNC_NAME, _("input port"));
+ }
+ using_port = gdbscm_is_true (port);
+
+ if (offset_arg_pos > 0
+ && (port_arg_pos < 0
+ || gdbscm_is_false (port)))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, offset_arg_pos,
+ gdbscm_scm_from_ulongest (offset),
+ _("offset provided but port is missing"));
+ }
+
+ if (size_arg_pos > 0)
+ {
+ if (size == 0)
+ return SCM_EOL;
+ /* For now be strict about start+size overflowing. If it becomes
+ a nuisance we can relax things later. */
+ if (start + size < start)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0,
+ scm_list_2 (gdbscm_scm_from_ulongest (start),
+ gdbscm_scm_from_ulongest (size)),
+ _("start+size overflows"));
+ }
+ end = start + size - 1;
+ }
+ else
+ end = ~(CORE_ADDR) 0;
+
+ if (count == 0)
+ return SCM_EOL;
+
+ result = SCM_EOL;
+
+ for (pc = start, i = 0; pc <= end && i < count; )
+ {
+ int insn_len = 0;
+ char *as = NULL;
+ struct ui_file *memfile = mem_fileopen ();
+ struct cleanup *cleanups = make_cleanup_ui_file_delete (memfile);
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (using_port)
+ {
+ insn_len = gdbscm_print_insn_from_port (gdbarch, port, offset,
+ pc, memfile, NULL);
+ }
+ else
+ insn_len = gdb_print_insn (gdbarch, pc, memfile, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ as = ui_file_xstrdup (memfile, NULL);
+
+ result = scm_cons (dascm_make_insn (pc, as, insn_len),
+ result);
+
+ pc += insn_len;
+ i++;
+ do_cleanups (cleanups);
+ xfree (as);
+ }
+
+ return scm_reverse_x (result, SCM_EOL);
+}
+
+/* Initialize the Scheme architecture support. */
+
+static const scheme_function disasm_functions[] =
+{
+ { "arch-disassemble", 2, 0, 1, gdbscm_arch_disassemble,
+ "\
+Return list of disassembled instructions in memory.\n\
+\n\
+ Arguments: <gdb:arch> start-address\n\
+ [#:port port] [#:offset address]\n\
+ [#:size <integer>] [#:count <integer>]\n\
+ port: If non-#f, it is an input port to read bytes from.\n\
+ offset: Specifies the address offset of the first byte in the port.\n\
+ This is useful if the input is from something other than memory\n\
+ (e.g., a bytevector) and you want the result to be as if the bytes\n\
+ came from that address. The value to pass for start-address is\n\
+ then also the desired disassembly address, not the offset in, e.g.,\n\
+ the bytevector.\n\
+ size: Limit the number of bytes read to this amount.\n\
+ count: Limit the number of instructions to this amount.\n\
+\n\
+ Returns:\n\
+ Each instruction in the result is an alist:\n\
+ (('address . address) ('asm . disassembly) ('length . length))." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_disasm (void)
+{
+ gdbscm_define_functions (disasm_functions, 1);
+
+ port_keyword = scm_from_latin1_keyword ("port");
+ offset_keyword = scm_from_latin1_keyword ("offset");
+ size_keyword = scm_from_latin1_keyword ("size");
+ count_keyword = scm_from_latin1_keyword ("count");
+
+ address_symbol = scm_from_latin1_symbol ("address");
+ asm_symbol = scm_from_latin1_symbol ("asm");
+ length_symbol = scm_from_latin1_symbol ("length");
+}
diff --git a/gdb/guile/scm-exception.c b/gdb/guile/scm-exception.c
new file mode 100644
index 0000000..a96a350
--- /dev/null
+++ b/gdb/guile/scm-exception.c
@@ -0,0 +1,691 @@
+/* GDB/Scheme exception support.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+/* Notes:
+
+ IWBN to support SRFI 34/35. At the moment we follow Guile's own
+ exception mechanism.
+
+ The non-static functions in this file have prefix gdbscm_ and
+ not exscm_ on purpose. */
+
+#include "defs.h"
+#include <signal.h>
+#include "gdb_assert.h"
+#include "guile-internal.h"
+
+/* The <gdb:exception> smob.
+ This is used to record and handle Scheme exceptions.
+ One important invariant is that <gdb:exception> smobs are never a valid
+ result of a function, other than to signify an exception occurred. */
+
+typedef struct
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The key and args parameters to "throw". */
+ SCM key;
+ SCM args;
+} exception_smob;
+
+static const char exception_smob_name[] = "gdb:exception";
+
+/* The tag Guile knows the exception smob by. */
+static scm_t_bits exception_smob_tag;
+
+/* A generic error in struct gdb_exception.
+ I.e., not RETURN_QUIT and not MEMORY_ERROR. */
+static SCM error_symbol;
+
+/* An error occurred accessing inferior memory.
+ This is not a Scheme programming error. */
+static SCM memory_error_symbol;
+
+/* User interrupt, e.g., RETURN_QUIT in struct gdb_exception. */
+static SCM signal_symbol;
+
+/* Printing the stack is done by first capturing the stack and recording it in
+ a <gdb:exception> object with this key and with the ARGS field set to
+ (cons real-key (cons stack real-args)).
+ See gdbscm_make_exception_with_stack. */
+static SCM with_stack_error_symbol;
+
+/* The key to use for an invalid object exception. An invalid object is one
+ where the underlying object has been removed from GDB. */
+SCM gdbscm_invalid_object_error_symbol;
+
+/* Values for "guile print-stack" as symbols. */
+static SCM none_symbol;
+static SCM message_symbol;
+static SCM full_symbol;
+
+static const char percent_print_exception_message_name[] =
+ "%print-exception-message";
+
+/* Variable containing %print-exception-message.
+ It is not defined until late in initialization, after our init routine
+ has run. Cope by looking it up lazily. */
+static SCM percent_print_exception_message_var = SCM_BOOL_F;
+
+static const char percent_print_exception_with_stack_name[] =
+ "%print-exception-with-stack";
+
+/* Variable containing %print-exception-with-stack.
+ It is not defined until late in initialization, after our init routine
+ has run. Cope by looking it up lazily. */
+static SCM percent_print_exception_with_stack_var = SCM_BOOL_F;
+
+/* Counter to keep track of the number of times we create a <gdb:exception>
+ object, for performance monitoring purposes. */
+static unsigned long gdbscm_exception_count = 0;
+
+/* Administrivia for exception smobs. */
+
+/* The smob "mark" function for <gdb:exception>. */
+
+static SCM
+exscm_mark_exception_smob (SCM self)
+{
+ exception_smob *e_smob = (exception_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (e_smob->key);
+ scm_gc_mark (e_smob->args);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&e_smob->base);
+}
+
+/* The smob "print" function for <gdb:exception>. */
+
+static int
+exscm_print_exception_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ exception_smob *e_smob = (exception_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", exception_smob_name);
+ scm_write (e_smob->key, port);
+ scm_puts (" ", port);
+ scm_write (e_smob->args, port);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* (make-exception key args) -> <gdb:exception> */
+
+SCM
+gdbscm_make_exception (SCM key, SCM args)
+{
+ exception_smob *e_smob = (exception_smob *)
+ scm_gc_malloc (sizeof (exception_smob), exception_smob_name);
+ SCM smob;
+
+ e_smob->key = key;
+ e_smob->args = args;
+ smob = scm_new_smob (exception_smob_tag, (scm_t_bits) e_smob);
+ gdbscm_init_gsmob (&e_smob->base);
+
+ ++gdbscm_exception_count;
+
+ return smob;
+}
+
+/* Return non-zero if SCM is a <gdb:exception> object. */
+
+int
+gdbscm_is_exception (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (exception_smob_tag, scm);
+}
+
+/* (exception? scm) -> boolean */
+
+static SCM
+gdbscm_exception_p (SCM scm)
+{
+ return scm_from_bool (gdbscm_is_exception (scm));
+}
+
+/* (exception-key <gdb:exception>) -> key */
+
+SCM
+gdbscm_exception_key (SCM self)
+{
+ exception_smob *e_smob;
+
+ SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME,
+ "gdb:exception");
+
+ e_smob = (exception_smob *) SCM_SMOB_DATA (self);
+ return e_smob->key;
+}
+
+/* (exception-args <gdb:exception>) -> arg-list */
+
+SCM
+gdbscm_exception_args (SCM self)
+{
+ exception_smob *e_smob;
+
+ SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME,
+ "gdb:exception");
+
+ e_smob = (exception_smob *) SCM_SMOB_DATA (self);
+ return e_smob->args;
+}
+
+/* Wrap an exception in a <gdb:exception> object that includes STACK.
+ gdbscm_print_exception_with_stack knows how to unwrap it. */
+
+SCM
+gdbscm_make_exception_with_stack (SCM key, SCM args, SCM stack)
+{
+ return gdbscm_make_exception (with_stack_error_symbol,
+ scm_cons (key, scm_cons (stack, args)));
+}
+
+/* Version of scm_error_scm that creates a gdb:exception object that can later
+ be passed to gdbscm_throw.
+ KEY is a symbol denoting the kind of error.
+ SUBR is either #f or a string marking the function in which the error
+ occurred.
+ MESSAGE is either #f or the error message string. It may contain ~a and ~s
+ modifiers, provided by ARGS.
+ ARGS is a list of args to MESSAGE.
+ DATA is an arbitrary object, its value depends on KEY. The value to pass
+ here is a bit underspecified by Guile. */
+
+SCM
+gdbscm_make_error_scm (SCM key, SCM subr, SCM message, SCM args, SCM data)
+{
+ return gdbscm_make_exception (key, scm_list_4 (subr, message, args, data));
+}
+
+/* Version of scm_error that creates a gdb:exception object that can later
+ be passed to gdbscm_throw.
+ See gdbscm_make_error_scm for a description of the arguments. */
+
+SCM
+gdbscm_make_error (SCM key, const char *subr, const char *message,
+ SCM args, SCM data)
+{
+ return gdbscm_make_error_scm
+ (key,
+ subr == NULL ? SCM_BOOL_F : scm_from_latin1_string (subr),
+ message == NULL ? SCM_BOOL_F : scm_from_latin1_string (message),
+ args, data);
+}
+
+/* Version of SCM_ASSERT_TYPE/scm_wrong_type_arg_msg that creates a
+ gdb:exception object that can later be passed to gdbscm_throw. */
+
+SCM
+gdbscm_make_type_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *expected_type)
+{
+ char *msg;
+ SCM result;
+
+ if (arg_pos > 0)
+ {
+ if (expected_type != NULL)
+ {
+ msg = xstrprintf (_("Wrong type argument in position %d"
+ " (expecting %s): ~S"),
+ arg_pos, expected_type);
+ }
+ else
+ {
+ msg = xstrprintf (_("Wrong type argument in position %d: ~S"),
+ arg_pos);
+ }
+ }
+ else
+ {
+ if (expected_type != NULL)
+ {
+ msg = xstrprintf (_("Wrong type argument (expecting %s): ~S"),
+ expected_type);
+ }
+ else
+ msg = xstrprintf (_("Wrong type argument: ~S"));
+ }
+
+ result = gdbscm_make_error (scm_arg_type_key, subr, msg,
+ scm_list_1 (bad_value), scm_list_1 (bad_value));
+ xfree (msg);
+ return result;
+}
+
+/* A variant of gdbscm_make_type_error for non-type argument errors.
+ ERROR_PREFIX and ERROR are combined to build the error message.
+ Care needs to be taken so that the i18n composed form is still
+ reasonable, but no one is going to translate these anyway so we don't
+ worry too much.
+ ERROR_PREFIX may be NULL, ERROR may not be NULL. */
+
+static SCM
+gdbscm_make_arg_error (SCM key, const char *subr, int arg_pos, SCM bad_value,
+ const char *error_prefix, const char *error)
+{
+ char *msg;
+ SCM result;
+
+ if (error_prefix != NULL)
+ {
+ if (arg_pos > 0)
+ {
+ msg = xstrprintf (_("%s %s in position %d: ~S"),
+ error_prefix, error, arg_pos);
+ }
+ else
+ msg = xstrprintf (_("%s %s: ~S"), error_prefix, error);
+ }
+ else
+ {
+ if (arg_pos > 0)
+ msg = xstrprintf (_("%s in position %d: ~S"), error, arg_pos);
+ else
+ msg = xstrprintf (_("%s: ~S"), error);
+ }
+
+ result = gdbscm_make_error (key, subr, msg,
+ scm_list_1 (bad_value), scm_list_1 (bad_value));
+ xfree (msg);
+ return result;
+}
+
+/* Make an invalid-object error <gdb:exception> object.
+ OBJECT is the name of the kind of object that is invalid. */
+
+SCM
+gdbscm_make_invalid_object_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *object)
+{
+ return gdbscm_make_arg_error (gdbscm_invalid_object_error_symbol,
+ subr, arg_pos, bad_value,
+ _("Invalid object:"), object);
+}
+
+/* Throw an invalid-object error.
+ OBJECT is the name of the kind of object that is invalid. */
+
+SCM
+gdbscm_invalid_object_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *object)
+{
+ SCM exception
+ = gdbscm_make_invalid_object_error (subr, arg_pos, bad_value, object);
+
+ gdbscm_throw (exception);
+}
+
+/* Make an out-of-range error <gdb:exception> object. */
+
+SCM
+gdbscm_make_out_of_range_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *error)
+{
+ return gdbscm_make_arg_error (scm_out_of_range_key,
+ subr, arg_pos, bad_value,
+ _("Out of range:"), error);
+}
+
+/* Throw an out-of-range error.
+ This is the standard Guile out-of-range exception. */
+
+SCM
+gdbscm_out_of_range_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *error)
+{
+ SCM exception
+ = gdbscm_make_out_of_range_error (subr, arg_pos, bad_value, error);
+
+ gdbscm_throw (exception);
+}
+
+/* Make a misc-error <gdb:exception> object. */
+
+SCM
+gdbscm_make_misc_error (const char *subr, int arg_pos, SCM bad_value,
+ const char *error)
+{
+ return gdbscm_make_arg_error (scm_misc_error_key,
+ subr, arg_pos, bad_value, NULL, error);
+}
+
+/* Return a <gdb:exception> object for gdb:memory-error. */
+
+SCM
+gdbscm_make_memory_error (const char *subr, const char *msg, SCM args)
+{
+ return gdbscm_make_error (memory_error_symbol, subr, msg, args,
+ SCM_EOL);
+}
+
+/* Throw a gdb:memory-error exception. */
+
+SCM
+gdbscm_memory_error (const char *subr, const char *msg, SCM args)
+{
+ SCM exception = gdbscm_make_memory_error (subr, msg, args);
+
+ gdbscm_throw (exception);
+}
+
+/* Return non-zero if KEY is gdb:memory-error.
+ Note: This is an excp_matcher_func function. */
+
+int
+gdbscm_memory_error_p (SCM key)
+{
+ return scm_is_eq (key, memory_error_symbol);
+}
+
+/* Wrapper around scm_throw to throw a gdb:exception.
+ This function does not return.
+ This function cannot be called from inside TRY_CATCH. */
+
+void
+gdbscm_throw (SCM exception)
+{
+ scm_throw (gdbscm_exception_key (exception),
+ gdbscm_exception_args (exception));
+ gdb_assert_not_reached ("scm_throw returned");
+}
+
+/* Convert a GDB exception to a <gdb:exception> object. */
+
+SCM
+gdbscm_scm_from_gdb_exception (struct gdb_exception exception)
+{
+ SCM key;
+
+ if (exception.reason == RETURN_QUIT)
+ {
+ /* Handle this specially to be consistent with top-repl.scm. */
+ return gdbscm_make_error (signal_symbol, NULL, _("User interrupt"),
+ SCM_EOL, scm_list_1 (scm_from_int (SIGINT)));
+ }
+
+ if (exception.error == MEMORY_ERROR)
+ key = memory_error_symbol;
+ else
+ key = error_symbol;
+
+ return gdbscm_make_error (key, NULL, "~A",
+ scm_list_1 (gdbscm_scm_from_c_string
+ (exception.message)),
+ SCM_BOOL_F);
+}
+
+/* Convert a GDB exception to the appropriate Scheme exception and throw it.
+ This function does not return. */
+
+void
+gdbscm_throw_gdb_exception (struct gdb_exception exception)
+{
+ gdbscm_throw (gdbscm_scm_from_gdb_exception (exception));
+}
+
+/* Print the error message portion of an exception.
+ If PORT is #f, use the standard error port.
+ KEY cannot be gdb:with-stack.
+
+ Basically this function is just a wrapper around calling
+ %print-exception-message. */
+
+static void
+gdbscm_print_exception_message (SCM port, SCM frame, SCM key, SCM args)
+{
+ SCM printer, status;
+
+ if (gdbscm_is_false (port))
+ port = scm_current_error_port ();
+
+ gdb_assert (!scm_is_eq (key, with_stack_error_symbol));
+
+ /* This does not use scm_print_exception because we tweak the output a bit.
+ Compare Guile's print-exception with our %print-exception-message for
+ details. */
+ if (gdbscm_is_false (percent_print_exception_message_var))
+ {
+ percent_print_exception_message_var
+ = scm_c_private_variable (gdbscm_init_module_name,
+ percent_print_exception_message_name);
+ /* If we can't find %print-exception-message, there's a problem on the
+ Scheme side. Don't kill GDB, just flag an error and leave it at
+ that. */
+ if (gdbscm_is_false (percent_print_exception_message_var))
+ {
+ gdbscm_printf (port, _("Error in Scheme exception printing,"
+ " can't find %s.\n"),
+ percent_print_exception_message_name);
+ return;
+ }
+ }
+ printer = scm_variable_ref (percent_print_exception_message_var);
+
+ status = gdbscm_safe_call_4 (printer, port, frame, key, args, NULL);
+
+ /* If that failed still tell the user something.
+ But don't use the exception printing machinery! */
+ if (gdbscm_is_exception (status))
+ {
+ gdbscm_printf (port, _("Error in Scheme exception printing:\n"));
+ scm_display (status, port);
+ scm_newline (port);
+ }
+}
+
+/* Print the description of exception KEY, ARGS to PORT, according to the
+ setting of "set guile print-stack".
+ If PORT is #f, use the standard error port.
+ If STACK is #f, never print the stack, regardless of whether printing it
+ is enabled. If STACK is #t, then print it if it is contained in ARGS
+ (i.e., KEY is gdb:with-stack). Otherwise STACK is the result of calling
+ scm_make_stack (which will be ignored in favor of the stack in ARGS if
+ KEY is gdb:with-stack).
+ KEY, ARGS are the standard arguments to scm_throw, et.al.
+
+ Basically this function is just a wrapper around calling
+ %print-exception-with-args. */
+
+void
+gdbscm_print_exception_with_stack (SCM port, SCM stack, SCM key, SCM args)
+{
+ SCM printer, status;
+
+ if (gdbscm_is_false (port))
+ port = scm_current_error_port ();
+
+ if (gdbscm_is_false (percent_print_exception_with_stack_var))
+ {
+ percent_print_exception_with_stack_var
+ = scm_c_private_variable (gdbscm_init_module_name,
+ percent_print_exception_with_stack_name);
+ /* If we can't find %print-exception-with-args, there's a problem on the
+ Scheme side. Don't kill GDB, just flag an error and leave it at
+ that. */
+ if (gdbscm_is_false (percent_print_exception_with_stack_var))
+ {
+ gdbscm_printf (port, _("Error in Scheme exception printing,"
+ " can't find %s.\n"),
+ percent_print_exception_with_stack_name);
+ return;
+ }
+ }
+ printer = scm_variable_ref (percent_print_exception_with_stack_var);
+
+ status = gdbscm_safe_call_4 (printer, port, stack, key, args, NULL);
+
+ /* If that failed still tell the user something.
+ But don't use the exception printing machinery! */
+ if (gdbscm_is_exception (status))
+ {
+ gdbscm_printf (port, _("Error in Scheme exception printing:\n"));
+ scm_display (status, port);
+ scm_newline (port);
+ }
+}
+
+/* Print EXCEPTION, a <gdb:exception> object, to PORT.
+ If PORT is #f, use the standard error port. */
+
+void
+gdbscm_print_gdb_exception (SCM port, SCM exception)
+{
+ gdb_assert (gdbscm_is_exception (exception));
+
+ gdbscm_print_exception_with_stack (port, SCM_BOOL_T,
+ gdbscm_exception_key (exception),
+ gdbscm_exception_args (exception));
+}
+
+/* Return a string description of <gdb:exception> EXCEPTION.
+ If EXCEPTION is a gdb:with-stack exception, unwrap it, a backtrace
+ is never returned as part of the result.
+
+ Space for the result is malloc'd, the caller must free. */
+
+char *
+gdbscm_exception_message_to_string (SCM exception)
+{
+ SCM port = scm_open_output_string ();
+ SCM key, args;
+ char *result;
+
+ gdb_assert (gdbscm_is_exception (exception));
+
+ key = gdbscm_exception_key (exception);
+ args = gdbscm_exception_args (exception);
+
+ if (scm_is_eq (key, with_stack_error_symbol)
+ /* Don't crash on a badly generated gdb:with-stack exception. */
+ && scm_is_pair (args)
+ && scm_is_pair (scm_cdr (args)))
+ {
+ key = scm_car (args);
+ args = scm_cddr (args);
+ }
+
+ gdbscm_print_exception_message (port, SCM_BOOL_F, key, args);
+ result = gdbscm_scm_to_c_string (scm_get_output_string (port));
+ scm_close_port (port);
+
+ return result;
+}
+
+/* Return the value of the "guile print-stack" option as one of:
+ 'none, 'message, 'full. */
+
+static SCM
+gdbscm_percent_exception_print_style (void)
+{
+ if (gdbscm_print_excp == gdbscm_print_excp_none)
+ return none_symbol;
+ if (gdbscm_print_excp == gdbscm_print_excp_message)
+ return message_symbol;
+ if (gdbscm_print_excp == gdbscm_print_excp_full)
+ return full_symbol;
+ gdb_assert_not_reached ("bad value for \"guile print-stack\"");
+}
+
+/* Return the current <gdb:exception> counter.
+ This is for debugging purposes. */
+
+static SCM
+gdbscm_percent_exception_count (void)
+{
+ return scm_from_ulong (gdbscm_exception_count);
+}
+
+/* Initialize the Scheme exception support. */
+
+static const scheme_function exception_functions[] =
+{
+ { "make-exception", 2, 0, 0, gdbscm_make_exception,
+ "\
+Create a <gdb:exception> object.\n\
+\n\
+ Arguments: key args\n\
+ These are the standard key,args arguments of \"throw\"." },
+
+ { "exception?", 1, 0, 0, gdbscm_exception_p,
+ "\
+Return #t if the object is a <gdb:exception> object." },
+
+ { "exception-key", 1, 0, 0, gdbscm_exception_key,
+ "\
+Return the exception's key." },
+
+ { "exception-args", 1, 0, 0, gdbscm_exception_args,
+ "\
+Return the exception's arg list." },
+
+ END_FUNCTIONS
+};
+
+static const scheme_function private_exception_functions[] =
+{
+ { "%exception-print-style", 0, 0, 0, gdbscm_percent_exception_print_style,
+ "\
+Return the value of the \"guile print-stack\" option." },
+
+ { "%exception-count", 0, 0, 0, gdbscm_percent_exception_count,
+ "\
+Return a count of the number of <gdb:exception> objects created.\n\
+This is for debugging purposes." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_exceptions (void)
+{
+ exception_smob_tag = gdbscm_make_smob_type (exception_smob_name,
+ sizeof (exception_smob));
+ scm_set_smob_mark (exception_smob_tag, exscm_mark_exception_smob);
+ scm_set_smob_print (exception_smob_tag, exscm_print_exception_smob);
+
+ gdbscm_define_functions (exception_functions, 1);
+ gdbscm_define_functions (private_exception_functions, 0);
+
+ error_symbol = scm_from_latin1_symbol ("gdb:error");
+
+ memory_error_symbol = scm_from_latin1_symbol ("gdb:memory-error");
+
+ gdbscm_invalid_object_error_symbol
+ = scm_from_latin1_symbol ("gdb:invalid-object-error");
+
+ with_stack_error_symbol = scm_from_latin1_symbol ("gdb:with-stack");
+
+ /* The text of this symbol is taken from Guile's top-repl.scm. */
+ signal_symbol = scm_from_latin1_symbol ("signal");
+
+ none_symbol = scm_from_latin1_symbol ("none");
+ message_symbol = scm_from_latin1_symbol ("message");
+ full_symbol = scm_from_latin1_symbol ("full");
+}
diff --git a/gdb/guile/scm-frame.c b/gdb/guile/scm-frame.c
new file mode 100644
index 0000000..a46d1e3
--- /dev/null
+++ b/gdb/guile/scm-frame.c
@@ -0,0 +1,1077 @@
+/* Scheme interface to stack frames.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "block.h"
+#include "frame.h"
+#include "exceptions.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "stack.h"
+#include "value.h"
+#include "guile-internal.h"
+
+/* The <gdb:frame> smob.
+ The typedef for this struct is in guile-internal.h. */
+
+struct _frame_smob
+{
+ /* This always appears first. */
+ eqable_gdb_smob base;
+
+ struct frame_id frame_id;
+ struct gdbarch *gdbarch;
+
+ /* Frames are tracked by inferior.
+ We need some place to put the eq?-able hash table, and this feels as
+ good a place as any. Frames in one inferior shouldn't be considered
+ equal to frames in a different inferior. The frame becomes invalid if
+ this becomes NULL (the inferior has been deleted from gdb).
+ It's easier to relax restrictions than impose them after the fact.
+ N.B. It is an outstanding question whether a frame survives reruns of
+ the inferior. Intuitively the answer is "No", but currently a frame
+ also survives, e.g., multiple invocations of the same function from
+ the same point. Even different threads can have the same frame, e.g.,
+ if a thread dies and a new thread gets the same stack. */
+ struct inferior *inferior;
+
+ /* Marks that the FRAME_ID member actually holds the ID of the frame next
+ to this, and not this frame's ID itself. This is a hack to permit Scheme
+ frame objects which represent invalid frames (i.e., the last frame_info
+ in a corrupt stack). The problem arises from the fact that this code
+ relies on FRAME_ID to uniquely identify a frame, which is not always true
+ for the last "frame" in a corrupt stack (it can have a null ID, or the
+ same ID as the previous frame). Whenever get_prev_frame returns NULL, we
+ record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1. */
+ int frame_id_is_next;
+};
+
+static const char frame_smob_name[] = "gdb:frame";
+
+/* The tag Guile knows the frame smob by. */
+static scm_t_bits frame_smob_tag;
+
+/* Keywords used in argument passing. */
+static SCM block_keyword;
+
+static const struct inferior_data *frscm_inferior_data_key;
+
+/* Administrivia for frame smobs. */
+
+/* Helper function to hash a frame_smob. */
+
+static hashval_t
+frscm_hash_frame_smob (const void *p)
+{
+ const frame_smob *f_smob = p;
+ const struct frame_id *fid = &f_smob->frame_id;
+ hashval_t hash = htab_hash_pointer (f_smob->inferior);
+
+ if (fid->stack_status == FID_STACK_VALID)
+ hash = iterative_hash (&fid->stack_addr, sizeof (fid->stack_addr), hash);
+ if (fid->code_addr_p)
+ hash = iterative_hash (&fid->code_addr, sizeof (fid->code_addr), hash);
+ if (fid->special_addr_p)
+ hash = iterative_hash (&fid->special_addr, sizeof (fid->special_addr),
+ hash);
+
+ return hash;
+}
+
+/* Helper function to compute equality of frame_smobs. */
+
+static int
+frscm_eq_frame_smob (const void *ap, const void *bp)
+{
+ const frame_smob *a = ap;
+ const frame_smob *b = bp;
+
+ return (frame_id_eq (a->frame_id, b->frame_id)
+ && a->inferior == b->inferior
+ && a->inferior != NULL);
+}
+
+/* Return the frame -> SCM mapping table.
+ It is created if necessary. */
+
+static htab_t
+frscm_inferior_frame_map (struct inferior *inferior)
+{
+ htab_t htab = inferior_data (inferior, frscm_inferior_data_key);
+
+ if (htab == NULL)
+ {
+ htab = gdbscm_create_eqable_gsmob_ptr_map (frscm_hash_frame_smob,
+ frscm_eq_frame_smob);
+ set_inferior_data (inferior, frscm_inferior_data_key, htab);
+ }
+
+ return htab;
+}
+
+/* The smob "mark" function for <gdb:frame>. */
+
+static SCM
+frscm_mark_frame_smob (SCM self)
+{
+ frame_smob *f_smob = (frame_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_eqable_gsmob (&f_smob->base);
+}
+
+/* The smob "free" function for <gdb:frame>. */
+
+static size_t
+frscm_free_frame_smob (SCM self)
+{
+ frame_smob *f_smob = (frame_smob *) SCM_SMOB_DATA (self);
+
+ if (f_smob->inferior != NULL)
+ {
+ htab_t htab = frscm_inferior_frame_map (f_smob->inferior);
+
+ gdbscm_clear_eqable_gsmob_ptr_slot (htab, &f_smob->base);
+ }
+
+ /* Not necessary, done to catch bugs. */
+ f_smob->inferior = NULL;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:frame>. */
+
+static int
+frscm_print_frame_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ frame_smob *f_smob = (frame_smob *) SCM_SMOB_DATA (self);
+ struct ui_file *strfile;
+ char *s;
+
+ gdbscm_printf (port, "#<%s ", frame_smob_name);
+
+ strfile = mem_fileopen ();
+ fprint_frame_id (strfile, f_smob->frame_id);
+ s = ui_file_xstrdup (strfile, NULL);
+ gdbscm_printf (port, "%s", s);
+ ui_file_delete (strfile);
+ xfree (s);
+
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:frame> object. */
+
+static SCM
+frscm_make_frame_smob (void)
+{
+ frame_smob *f_smob = (frame_smob *)
+ scm_gc_malloc (sizeof (frame_smob), frame_smob_name);
+ SCM f_scm;
+
+ f_smob->frame_id = null_frame_id;
+ f_smob->gdbarch = NULL;
+ f_smob->inferior = NULL;
+ f_smob->frame_id_is_next = 0;
+ f_scm = scm_new_smob (frame_smob_tag, (scm_t_bits) f_smob);
+ gdbscm_init_eqable_gsmob (&f_smob->base);
+
+ return f_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:frame> object. */
+
+int
+frscm_is_frame (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (frame_smob_tag, scm);
+}
+
+/* (frame? object) -> boolean */
+
+static SCM
+gdbscm_frame_p (SCM scm)
+{
+ return scm_from_bool (frscm_is_frame (scm));
+}
+
+/* Create a new <gdb:frame> object that encapsulates FRAME.
+ Returns a <gdb:exception> object if there is an error. */
+
+static SCM
+frscm_scm_from_frame (struct frame_info *frame, struct inferior *inferior)
+{
+ frame_smob *f_smob, f_smob_for_lookup;
+ SCM f_scm;
+ htab_t htab;
+ eqable_gdb_smob **slot;
+ volatile struct gdb_exception except;
+ struct frame_id frame_id = null_frame_id;
+ struct gdbarch *gdbarch = NULL;
+ int frame_id_is_next = 0;
+
+ /* If we've already created a gsmob for this frame, return it.
+ This makes frames eq?-able. */
+ htab = frscm_inferior_frame_map (inferior);
+ f_smob_for_lookup.frame_id = get_frame_id (frame);
+ f_smob_for_lookup.inferior = inferior;
+ slot = gdbscm_find_eqable_gsmob_ptr_slot (htab, &f_smob_for_lookup.base);
+ if (*slot != NULL)
+ return (*slot)->containing_scm;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* Try to get the previous frame, to determine if this is the last frame
+ in a corrupt stack. If so, we need to store the frame_id of the next
+ frame and not of this one (which is possibly invalid). */
+ if (get_prev_frame (frame) == NULL
+ && get_frame_unwind_stop_reason (frame) != UNWIND_NO_REASON
+ && get_next_frame (frame) != NULL)
+ {
+ frame_id = get_frame_id (get_next_frame (frame));
+ frame_id_is_next = 1;
+ }
+ else
+ {
+ frame_id = get_frame_id (frame);
+ frame_id_is_next = 0;
+ }
+ gdbarch = get_frame_arch (frame);
+ }
+ if (except.reason < 0)
+ return gdbscm_scm_from_gdb_exception (except);
+
+ f_scm = frscm_make_frame_smob ();
+ f_smob = (frame_smob *) SCM_SMOB_DATA (f_scm);
+ f_smob->frame_id = frame_id;
+ f_smob->gdbarch = gdbarch;
+ f_smob->inferior = inferior;
+ f_smob->frame_id_is_next = frame_id_is_next;
+
+ gdbscm_fill_eqable_gsmob_ptr_slot (slot, &f_smob->base, f_scm);
+
+ return f_scm;
+}
+
+/* Create a new <gdb:frame> object that encapsulates FRAME.
+ A Scheme exception is thrown if there is an error. */
+
+static SCM
+frscm_scm_from_frame_unsafe (struct frame_info *frame,
+ struct inferior *inferior)
+{
+ SCM f_scm = frscm_scm_from_frame (frame, inferior);
+
+ if (gdbscm_is_exception (f_scm))
+ gdbscm_throw (f_scm);
+
+ return f_scm;
+}
+
+/* Returns the <gdb:frame> object in SELF.
+ Throws an exception if SELF is not a <gdb:frame> object. */
+
+static SCM
+frscm_get_frame_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (frscm_is_frame (self), self, arg_pos, func_name,
+ frame_smob_name);
+
+ return self;
+}
+
+/* There is no gdbscm_scm_to_frame function because translating
+ a frame SCM object to a struct frame_info * can throw a GDB error.
+ Thus code working with frames has to handle both Scheme errors (e.g., the
+ object is not a frame) and GDB errors (e.g., the frame lookup failed).
+
+ To help keep things clear we split gdbscm_scm_to_frame into two:
+
+ gdbscm_get_frame_smob_arg_unsafe
+ - throws a Scheme error if object is not a frame,
+ or if the inferior is gone or is no longer current
+
+ gdbscm_frame_smob_to_frame
+ - may throw a gdb error if the conversion fails
+ - it's not clear when it will and won't throw a GDB error,
+ but for robustness' sake we assume that whenever we call out to GDB
+ a GDB error may get thrown (and thus the call must be wrapped in a
+ TRY_CATCH) */
+
+/* Returns the frame_smob for the object wrapped by FRAME_SCM.
+ A Scheme error is thrown if FRAME_SCM is not a frame. */
+
+frame_smob *
+frscm_get_frame_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM f_scm = frscm_get_frame_arg_unsafe (self, arg_pos, func_name);
+ frame_smob *f_smob = (frame_smob *) SCM_SMOB_DATA (f_scm);
+
+ if (f_smob->inferior == NULL)
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("inferior"));
+ }
+ if (f_smob->inferior != current_inferior ())
+ scm_misc_error (func_name, _("inferior has changed"), SCM_EOL);
+
+ return f_smob;
+}
+
+/* Returns the frame_info object wrapped by F_SMOB.
+ If the frame doesn't exist anymore (the frame id doesn't
+ correspond to any frame in the inferior), returns NULL.
+ This function calls GDB routines, so don't assume a GDB error will
+ not be thrown. */
+
+struct frame_info *
+frscm_frame_smob_to_frame (frame_smob *f_smob)
+{
+ struct frame_info *frame;
+
+ frame = frame_find_by_id (f_smob->frame_id);
+ if (frame == NULL)
+ return NULL;
+
+ if (f_smob->frame_id_is_next)
+ frame = get_prev_frame (frame);
+
+ return frame;
+}
+
+/* Helper function for frscm_del_inferior_frames to mark the frame
+ as invalid. */
+
+static int
+frscm_mark_frame_invalid (void **slot, void *info)
+{
+ frame_smob *f_smob = (frame_smob *) *slot;
+
+ f_smob->inferior = NULL;
+ return 1;
+}
+
+/* This function is called when an inferior is about to be freed.
+ Invalidate the frame as further actions on the frame could result
+ in bad data. All access to the frame should be gated by
+ frscm_get_frame_smob_arg_unsafe which will raise an exception on
+ invalid frames. */
+
+static void
+frscm_del_inferior_frames (struct inferior *inferior, void *datum)
+{
+ htab_t htab = datum;
+
+ if (htab != NULL)
+ {
+ htab_traverse_noresize (htab, frscm_mark_frame_invalid, NULL);
+ htab_delete (htab);
+ }
+}
+
+/* Frame methods. */
+
+/* (frame-valid? <gdb:frame>) -> bool
+ Returns #t if the frame corresponding to the frame_id of this
+ object still exists in the inferior. */
+
+static SCM
+gdbscm_frame_valid_p (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (frame != NULL);
+}
+
+/* (frame-name <gdb:frame>) -> string
+ Returns the name of the function corresponding to this frame,
+ or #f if there is no function. */
+
+static SCM
+gdbscm_frame_name (SCM self)
+{
+ frame_smob *f_smob;
+ char *name = NULL;
+ enum language lang = language_minimal;
+ struct frame_info *frame = NULL;
+ SCM result;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ find_frame_funname (frame, &name, &lang, NULL);
+ }
+ if (except.reason < 0)
+ xfree (name);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ if (name != NULL)
+ {
+ result = gdbscm_scm_from_c_string (name);
+ xfree (name);
+ }
+ else
+ result = SCM_BOOL_F;
+
+ return result;
+}
+
+/* (frame-type <gdb:frame>) -> integer
+ Returns the frame type, namely one of the gdb:*_FRAME constants. */
+
+static SCM
+gdbscm_frame_type (SCM self)
+{
+ frame_smob *f_smob;
+ enum frame_type type = NORMAL_FRAME;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ type = get_frame_type (frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ return scm_from_int (type);
+}
+
+/* (frame-arch <gdb:frame>) -> <gdb:architecture>
+ Returns the frame's architecture as a gdb:architecture object. */
+
+static SCM
+gdbscm_frame_arch (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ return arscm_scm_from_arch (f_smob->gdbarch);
+}
+
+/* (frame-unwind-stop-reason <gdb:frame>) -> integer
+ Returns one of the gdb:FRAME_UNWIND_* constants. */
+
+static SCM
+gdbscm_frame_unwind_stop_reason (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+ enum unwind_stop_reason stop_reason;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ stop_reason = get_frame_unwind_stop_reason (frame);
+
+ return scm_from_int (stop_reason);
+}
+
+/* (frame-pc <gdb:frame>) -> integer
+ Returns the frame's resume address. */
+
+static SCM
+gdbscm_frame_pc (SCM self)
+{
+ frame_smob *f_smob;
+ CORE_ADDR pc = 0;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ pc = get_frame_pc (frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ return gdbscm_scm_from_ulongest (pc);
+}
+
+/* (frame-block <gdb:frame>) -> <gdb:block>
+ Returns the frame's code block, or #f if one cannot be found. */
+
+static SCM
+gdbscm_frame_block (SCM self)
+{
+ frame_smob *f_smob;
+ struct block *block = NULL, *fn_block;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ block = get_frame_block (frame, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ for (fn_block = block;
+ fn_block != NULL && BLOCK_FUNCTION (fn_block) == NULL;
+ fn_block = BLOCK_SUPERBLOCK (fn_block))
+ continue;
+
+ if (block == NULL || fn_block == NULL || BLOCK_FUNCTION (fn_block) == NULL)
+ {
+ scm_misc_error (FUNC_NAME, _("cannot find block for frame"),
+ scm_list_1 (self));
+ }
+
+ if (block != NULL)
+ {
+ struct symtab *st;
+ SCM block_scm;
+
+ st = SYMBOL_SYMTAB (BLOCK_FUNCTION (fn_block));
+ return bkscm_scm_from_block (block, st->objfile);
+ }
+
+ return SCM_BOOL_F;
+}
+
+/* (frame-function <gdb:frame>) -> <gdb:symbol>
+ Returns the symbol for the function corresponding to this frame,
+ or #f if there isn't one. */
+
+static SCM
+gdbscm_frame_function (SCM self)
+{
+ frame_smob *f_smob;
+ struct symbol *sym = NULL;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ sym = find_pc_function (get_frame_address_in_block (frame));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ if (sym != NULL)
+ return syscm_scm_from_symbol (sym);
+
+ return SCM_BOOL_F;
+}
+
+/* (frame-older <gdb:frame>) -> <gdb:frame>
+ Returns the frame immediately older (outer) to this frame,
+ or #f if there isn't one. */
+
+static SCM
+gdbscm_frame_older (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *prev = NULL;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ prev = get_prev_frame (frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ if (prev != NULL)
+ return frscm_scm_from_frame_unsafe (prev, f_smob->inferior);
+
+ return SCM_BOOL_F;
+}
+
+/* (frame-newer <gdb:frame>) -> <gdb:frame>
+ Returns the frame immediately newer (inner) to this frame,
+ or #f if there isn't one. */
+
+static SCM
+gdbscm_frame_newer (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *next = NULL;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ next = get_next_frame (frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ if (next != NULL)
+ return frscm_scm_from_frame_unsafe (next, f_smob->inferior);
+
+ return SCM_BOOL_F;
+}
+
+/* (frame-sal <gdb:frame>) -> <gdb:sal>
+ Returns the frame's symtab and line. */
+
+static SCM
+gdbscm_frame_sal (SCM self)
+{
+ frame_smob *f_smob;
+ struct symtab_and_line sal;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ find_frame_sal (frame, &sal);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ return stscm_scm_from_sal (sal);
+}
+
+/* (frame-read-var <gdb:frame> <gdb:symbol>) -> <gdb:value>
+ (frame-read-var <gdb:frame> string [#:block <gdb:block>]) -> <gdb:value>
+ If the optional block argument is provided start the search from that block,
+ otherwise search from the frame's current block (determined by examining
+ the resume address of the frame). The variable argument must be a string
+ or an instance of a <gdb:symbol>. The block argument must be an instance of
+ <gdb:block>. */
+
+static SCM
+gdbscm_frame_read_var (SCM self, SCM symbol_scm, SCM rest)
+{
+ SCM keywords[] = { block_keyword, SCM_BOOL_F };
+ int rc;
+ frame_smob *f_smob;
+ int block_arg_pos = -1;
+ SCM block_scm = SCM_UNDEFINED;
+ struct frame_info *frame = NULL;
+ struct symbol *var = NULL;
+ struct value *value = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG3, keywords, "#O",
+ rest, &block_arg_pos, &block_scm);
+
+ if (syscm_is_symbol (symbol_scm))
+ {
+ var = syscm_get_valid_symbol_arg_unsafe (symbol_scm, SCM_ARG2,
+ FUNC_NAME);
+ SCM_ASSERT (SCM_UNBNDP (block_scm), block_scm, SCM_ARG3, FUNC_NAME);
+ }
+ else if (scm_is_string (symbol_scm))
+ {
+ char *var_name;
+ const struct block *block = NULL;
+ struct cleanup *cleanup;
+ volatile struct gdb_exception except;
+
+ if (! SCM_UNBNDP (block_scm))
+ {
+ SCM except_scm;
+
+ gdb_assert (block_arg_pos > 0);
+ block = bkscm_scm_to_block (block_scm, block_arg_pos, FUNC_NAME,
+ &except_scm);
+ if (block == NULL)
+ gdbscm_throw (except_scm);
+ }
+
+ var_name = gdbscm_scm_to_c_string (symbol_scm);
+ cleanup = make_cleanup (xfree, var_name);
+ /* N.B. Between here and the call to do_cleanups, don't do anything
+ to cause a Scheme exception without performing the cleanup. */
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (block == NULL)
+ block = get_frame_block (frame, NULL);
+ var = lookup_symbol (var_name, block, VAR_DOMAIN, NULL);
+ }
+ if (except.reason < 0)
+ do_cleanups (cleanup);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (var == NULL)
+ {
+ do_cleanups (cleanup);
+ gdbscm_out_of_range_error (FUNC_NAME, 0, symbol_scm,
+ _("variable not found"));
+ }
+
+ do_cleanups (cleanup);
+ }
+ else
+ {
+ /* Use SCM_ASSERT_TYPE for more consistent error messages. */
+ SCM_ASSERT_TYPE (0, symbol_scm, SCM_ARG1, FUNC_NAME,
+ _("gdb:symbol or string"));
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ value = read_var_value (var, frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return vlscm_scm_from_value (value);
+}
+
+/* (frame-select <gdb:frame>) -> unspecified
+ Select this frame. */
+
+static SCM
+gdbscm_frame_select (SCM self)
+{
+ frame_smob *f_smob;
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ f_smob = frscm_get_frame_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = frscm_frame_smob_to_frame (f_smob);
+ if (frame != NULL)
+ select_frame (frame);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (frame == NULL)
+ {
+ gdbscm_invalid_object_error (FUNC_NAME, SCM_ARG1, self,
+ _("<gdb:frame>"));
+ }
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (newest-frame) -> <gdb:frame>
+ Returns the newest frame. */
+
+static SCM
+gdbscm_newest_frame (void)
+{
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = get_current_frame ();
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return frscm_scm_from_frame_unsafe (frame, current_inferior ());
+}
+
+/* (selected-frame) -> <gdb:frame>
+ Returns the selected frame. */
+
+static SCM
+gdbscm_selected_frame (void)
+{
+ struct frame_info *frame = NULL;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ frame = get_selected_frame (_("No frame is currently selected"));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return frscm_scm_from_frame_unsafe (frame, current_inferior ());
+}
+
+/* (unwind-stop-reason-string integer) -> string
+ Return a string explaining the unwind stop reason. */
+
+static SCM
+gdbscm_unwind_stop_reason_string (SCM reason_scm)
+{
+ int reason;
+ const char *str;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "i",
+ reason_scm, &reason);
+
+ if (reason < UNWIND_FIRST || reason > UNWIND_LAST)
+ scm_out_of_range (FUNC_NAME, reason_scm);
+
+ str = frame_stop_reason_string (reason);
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* Initialize the Scheme frame support. */
+
+static const scheme_integer_constant frame_integer_constants[] =
+{
+#define ENTRY(X) { #X, X }
+
+ ENTRY (NORMAL_FRAME),
+ ENTRY (DUMMY_FRAME),
+ ENTRY (INLINE_FRAME),
+ ENTRY (TAILCALL_FRAME),
+ ENTRY (SIGTRAMP_FRAME),
+ ENTRY (ARCH_FRAME),
+ ENTRY (SENTINEL_FRAME),
+
+#undef ENTRY
+
+#define SET(name, description) \
+ { "FRAME_" #name, name },
+#include "unwind_stop_reasons.def"
+#undef SET
+
+ END_INTEGER_CONSTANTS
+};
+
+static const scheme_function frame_functions[] =
+{
+ { "frame?", 1, 0, 0, gdbscm_frame_p,
+ "\
+Return #t if the object is a <gdb:frame> object." },
+
+ { "frame-valid?", 1, 0, 0, gdbscm_frame_valid_p,
+ "\
+Return #t if the object is a valid <gdb:frame> object.\n\
+Frames become invalid when the inferior returns to its caller." },
+
+ { "frame-name", 1, 0, 0, gdbscm_frame_name,
+ "\
+Return the name of the function corresponding to this frame,\n\
+or #f if there is no function." },
+
+ { "frame-arch", 1, 0, 0, gdbscm_frame_arch,
+ "\
+Return the frame's architecture as a <gdb:arch> object." },
+
+ { "frame-type", 1, 0, 0, gdbscm_frame_type,
+ "\
+Return the frame type, namely one of the gdb:*_FRAME constants." },
+
+ { "frame-unwind-stop-reason", 1, 0, 0, gdbscm_frame_unwind_stop_reason,
+ "\
+Return one of the gdb:FRAME_UNWIND_* constants explaining why\n\
+it's not possible to find frames older than this." },
+
+ { "frame-pc", 1, 0, 0, gdbscm_frame_pc,
+ "\
+Return the frame's resume address." },
+
+ { "frame-block", 1, 0, 0, gdbscm_frame_block,
+ "\
+Return the frame's code block, or #f if one cannot be found." },
+
+ { "frame-function", 1, 0, 0, gdbscm_frame_function,
+ "\
+Return the <gdb:symbol> for the function corresponding to this frame,\n\
+or #f if there isn't one." },
+
+ { "frame-older", 1, 0, 0, gdbscm_frame_older,
+ "\
+Return the frame immediately older (outer) to this frame,\n\
+or #f if there isn't one." },
+
+ { "frame-newer", 1, 0, 0, gdbscm_frame_newer,
+ "\
+Return the frame immediately newer (inner) to this frame,\n\
+or #f if there isn't one." },
+
+ { "frame-sal", 1, 0, 0, gdbscm_frame_sal,
+ "\
+Return the frame's symtab-and-line <gdb:sal> object." },
+
+ { "frame-read-var", 2, 0, 1, gdbscm_frame_read_var,
+ "\
+Return the value of the symbol in the frame.\n\
+\n\
+ Arguments: <gdb:frame> <gdb:symbol>\n\
+ Or: <gdb:frame> string [#:block <gdb:block>]" },
+
+ { "frame-select", 1, 0, 0, gdbscm_frame_select,
+ "\
+Select this frame." },
+
+ { "newest-frame", 0, 0, 0, gdbscm_newest_frame,
+ "\
+Return the newest frame." },
+
+ { "selected-frame", 0, 0, 0, gdbscm_selected_frame,
+ "\
+Return the selected frame." },
+
+ { "unwind-stop-reason-string", 1, 0, 0, gdbscm_unwind_stop_reason_string,
+ "\
+Return a string explaining the unwind stop reason.\n\
+\n\
+ Arguments: integer (the result of frame-unwind-stop-reason)" },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_frames (void)
+{
+ frame_smob_tag
+ = gdbscm_make_smob_type (frame_smob_name, sizeof (frame_smob));
+ scm_set_smob_mark (frame_smob_tag, frscm_mark_frame_smob);
+ scm_set_smob_free (frame_smob_tag, frscm_free_frame_smob);
+ scm_set_smob_print (frame_smob_tag, frscm_print_frame_smob);
+
+ gdbscm_define_integer_constants (frame_integer_constants, 1);
+ gdbscm_define_functions (frame_functions, 1);
+
+ block_keyword = scm_from_latin1_keyword ("block");
+
+ /* Register an inferior "free" callback so we can properly
+ invalidate frames when an inferior file is about to be deleted. */
+ frscm_inferior_data_key
+ = register_inferior_data_with_cleanup (NULL, frscm_del_inferior_frames);
+}
diff --git a/gdb/guile/scm-gsmob.c b/gdb/guile/scm-gsmob.c
new file mode 100644
index 0000000..5f9e856
--- /dev/null
+++ b/gdb/guile/scm-gsmob.c
@@ -0,0 +1,486 @@
+/* GDB/Scheme smobs (gsmob is pronounced "jee smob")
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+/* Smobs are Guile's "small object".
+ They are used to export C structs to Scheme.
+
+ Note: There's only room in the encoding space for 256, and while we won't
+ come close to that, mixed with other libraries maybe someday we could.
+ We don't worry about it now, except to be aware of the issue.
+ We could allocate just a few smobs and use the unused smob flags field to
+ specify the gdb smob kind, that is left for another day if it ever is
+ needed.
+
+ We want the objects we export to Scheme to be extensible by the user.
+ A gsmob (gdb smob) adds a simple API on top of smobs to support this.
+ This allows GDB objects to be easily extendable in a useful manner.
+ To that end, all smobs in gdb have gdb_smob as the first member.
+
+ On top of gsmobs there are "chained gsmobs". They are used to assist with
+ life-time tracking of GDB objects vs Scheme objects. Gsmobs can "subclass"
+ chained_gdb_smob, which contains a doubly-linked list to assist with
+ life-time tracking.
+
+ On top of gsmobs there are also "eqable gsmobs". Gsmobs can "subclass"
+ eqable_gdb_smob instead of gdb_smob, and is used to make gsmobs eq?-able.
+ This is done by recording all gsmobs in a hash table and before creating a
+ gsmob first seeing if it's already in the table. Eqable gsmobs can also be
+ used where lifetime-tracking is required.
+
+ Gsmobs (and chained/eqable gsmobs) add an extra field that is used to
+ record extra data: "properties". It is a table of key/value pairs
+ that can be set with set-gsmob-property!, gsmob-property. */
+
+#include "defs.h"
+#include "hashtab.h"
+#include "gdb_assert.h"
+#include "objfiles.h"
+#include "guile-internal.h"
+
+/* We need to call this. Undo our hack to prevent others from calling it. */
+#undef scm_make_smob_type
+
+static htab_t registered_gsmobs;
+
+/* Gsmob properties are initialize stored as an alist to minimize space
+ usage: GDB can be used to debug some really big programs, and property
+ lists generally have very few elements. Once the list grows to this
+ many elements then we switch to a hash table.
+ The smallest Guile hashtable in 2.0 uses a vector of 31 elements.
+ The value we use here is large enough to hold several expected uses,
+ without being so large that we might as well just use a hashtable. */
+#define SMOB_PROP_HTAB_THRESHOLD 7
+
+/* Hash function for registered_gsmobs hash table. */
+
+static hashval_t
+hash_scm_t_bits (const void *item)
+{
+ uintptr_t v = (uintptr_t) item;
+
+ return v;
+}
+
+/* Equality function for registered_gsmobs hash table. */
+
+static int
+eq_scm_t_bits (const void *item_lhs, const void *item_rhs)
+{
+ return item_lhs == item_rhs;
+}
+
+/* Record GSMOB_CODE as being a gdb smob.
+ GSMOB_CODE is the result of scm_make_smob_type. */
+
+static void
+register_gsmob (scm_t_bits gsmob_code)
+{
+ void **slot;
+
+ slot = htab_find_slot (registered_gsmobs, (void *) gsmob_code, INSERT);
+ gdb_assert (*slot == NULL);
+ *slot = (void *) gsmob_code;
+}
+
+/* Return non-zero if SCM is any registered gdb smob object. */
+
+static int
+gdbscm_is_gsmob (SCM scm)
+{
+ void **slot;
+
+ if (SCM_IMP (scm))
+ return 0;
+ slot = htab_find_slot (registered_gsmobs, (void *) SCM_TYP16 (scm),
+ NO_INSERT);
+ return slot != NULL;
+}
+
+/* Call this to register a smob, instead of scm_make_smob_type. */
+
+scm_t_bits
+gdbscm_make_smob_type (const char *name, size_t size)
+{
+ scm_t_bits result = scm_make_smob_type (name, size);
+
+ register_gsmob (result);
+ return result;
+}
+
+/* Initialize a gsmob. */
+
+void
+gdbscm_init_gsmob (gdb_smob *base)
+{
+ base->properties = SCM_EOL;
+}
+
+/* Initialize a chained_gdb_smob.
+ This is the same as gdbscm_init_gsmob except that it also sets prev,next
+ to NULL. */
+
+void
+gdbscm_init_chained_gsmob (chained_gdb_smob *base)
+{
+ gdbscm_init_gsmob ((gdb_smob *) base);
+ base->prev = NULL;
+ base->next = NULL;
+}
+
+/* Initialize an eqable_gdb_smob.
+ This is the same as gdbscm_init_gsmob except that it also sets
+ containing_scm to #f. */
+
+void
+gdbscm_init_eqable_gsmob (eqable_gdb_smob *base)
+{
+ gdbscm_init_gsmob ((gdb_smob *) base);
+ base->containing_scm = SCM_BOOL_F;
+}
+
+/* Call this from each smob's "mark" routine.
+ In general, this should be called as:
+ return gdbscm_mark_gsmob (base); */
+
+SCM
+gdbscm_mark_gsmob (gdb_smob *base)
+{
+ /* Return the last one to mark as an optimization.
+ The marking infrastructure will mark it for us. */
+ return base->properties;
+}
+
+/* Call this from each smob's "mark" routine.
+ In general, this should be called as:
+ return gdbscm_mark_chained_gsmob (base); */
+
+SCM
+gdbscm_mark_chained_gsmob (chained_gdb_smob *base)
+{
+ /* Return the last one to mark as an optimization.
+ The marking infrastructure will mark it for us. */
+ return base->properties;
+}
+
+/* Call this from each smob's "mark" routine.
+ In general, this should be called as:
+ return gdbscm_mark_eqable_gsmob (base); */
+
+SCM
+gdbscm_mark_eqable_gsmob (eqable_gdb_smob *base)
+{
+ /* There's no need to mark containing_scm.
+ Any references to it either come from Scheme in which case it will be
+ marked through them, or there's a reference to the smob from gdb in
+ which case the smob is GC-protected. */
+
+ /* Return the last one to mark as an optimization.
+ The marking infrastructure will mark it for us. */
+ return base->properties;
+}
+
+/* gsmob accessors */
+
+/* Return the gsmob in SELF.
+ Throws an exception if SELF is not a gsmob. */
+
+static SCM
+gsscm_get_gsmob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (gdbscm_is_gsmob (self), self, arg_pos, func_name,
+ _("any gdb smob"));
+
+ return self;
+}
+
+/* (gsmob-kind gsmob) -> symbol
+
+ Note: While one might want to name this gsmob-class-name, it is named
+ "-kind" because smobs aren't real GOOPS classes. */
+
+static SCM
+gdbscm_gsmob_kind (SCM self)
+{
+ SCM smob, result;
+ scm_t_bits smobnum;
+ const char *name;
+ char *kind;
+
+ smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ smobnum = SCM_SMOBNUM (smob);
+ name = SCM_SMOBNAME (smobnum);
+ kind = xstrprintf ("<%s>", name);
+ result = scm_from_latin1_symbol (kind);
+ xfree (kind);
+
+ return result;
+}
+
+/* (gsmob-property gsmob property) -> object
+ If property isn't present then #f is returned. */
+
+static SCM
+gdbscm_gsmob_property (SCM self, SCM property)
+{
+ SCM smob;
+ gdb_smob *base;
+
+ smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ base = (gdb_smob *) SCM_SMOB_DATA (self);
+
+ /* Have we switched to a hash table? */
+ if (gdbscm_is_true (scm_hash_table_p (base->properties)))
+ return scm_hashq_ref (base->properties, property, SCM_BOOL_F);
+
+ return scm_assq_ref (base->properties, property);
+}
+
+/* (set-gsmob-property! gsmob property new-value) -> unspecified */
+
+static SCM
+gdbscm_set_gsmob_property_x (SCM self, SCM property, SCM new_value)
+{
+ SCM smob, alist;
+ gdb_smob *base;
+
+ smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ base = (gdb_smob *) SCM_SMOB_DATA (self);
+
+ /* Have we switched to a hash table? */
+ if (gdbscm_is_true (scm_hash_table_p (base->properties)))
+ {
+ scm_hashq_set_x (base->properties, property, new_value);
+ return SCM_UNSPECIFIED;
+ }
+
+ alist = scm_assq_set_x (base->properties, property, new_value);
+
+ /* Did we grow the list? */
+ if (!scm_is_eq (alist, base->properties))
+ {
+ /* If we grew the list beyond a threshold in size,
+ switch to a hash table. */
+ if (scm_ilength (alist) >= SMOB_PROP_HTAB_THRESHOLD)
+ {
+ SCM elm, htab;
+
+ htab = scm_c_make_hash_table (SMOB_PROP_HTAB_THRESHOLD);
+ for (elm = alist; elm != SCM_EOL; elm = scm_cdr (elm))
+ scm_hashq_set_x (htab, scm_caar (elm), scm_cdar (elm));
+ base->properties = htab;
+ return SCM_UNSPECIFIED;
+ }
+ }
+
+ base->properties = alist;
+ return SCM_UNSPECIFIED;
+}
+
+/* (gsmob-has-property? gsmob property) -> boolean */
+
+static SCM
+gdbscm_gsmob_has_property_p (SCM self, SCM property)
+{
+ SCM smob, handle;
+ gdb_smob *base;
+
+ smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ base = (gdb_smob *) SCM_SMOB_DATA (self);
+
+ if (gdbscm_is_true (scm_hash_table_p (base->properties)))
+ handle = scm_hashq_get_handle (base->properties, property);
+ else
+ handle = scm_assq (property, base->properties);
+
+ return scm_from_bool (gdbscm_is_true (handle));
+}
+
+/* Helper function for gdbscm_gsmob_properties. */
+
+static SCM
+add_property_name (void *closure, SCM handle)
+{
+ SCM *resultp = closure;
+
+ *resultp = scm_cons (scm_car (handle), *resultp);
+ return SCM_UNSPECIFIED;
+}
+
+/* (gsmob-properties gsmob) -> list
+ The list is unsorted. */
+
+static SCM
+gdbscm_gsmob_properties (SCM self)
+{
+ SCM smob, handle, result;
+ gdb_smob *base;
+
+ smob = gsscm_get_gsmob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ base = (gdb_smob *) SCM_SMOB_DATA (self);
+
+ result = SCM_EOL;
+ if (gdbscm_is_true (scm_hash_table_p (base->properties)))
+ {
+ scm_internal_hash_for_each_handle (add_property_name, &result,
+ base->properties);
+ }
+ else
+ {
+ SCM elm;
+
+ for (elm = base->properties; elm != SCM_EOL; elm = scm_cdr (elm))
+ result = scm_cons (scm_caar (elm), result);
+ }
+
+ return result;
+}
+
+/* When underlying gdb data structures are deleted, we need to update any
+ smobs with references to them. There are several smobs that reference
+ objfile-based data, so we provide helpers to manage this. */
+
+/* Add G_SMOB to the reference chain for OBJFILE specified by DATA_KEY.
+ OBJFILE may be NULL, in which case just set prev,next to NULL. */
+
+void
+gdbscm_add_objfile_ref (struct objfile *objfile,
+ const struct objfile_data *data_key,
+ chained_gdb_smob *g_smob)
+{
+ g_smob->prev = NULL;
+ if (objfile != NULL)
+ {
+ g_smob->next = objfile_data (objfile, data_key);
+ if (g_smob->next)
+ g_smob->next->prev = g_smob;
+ set_objfile_data (objfile, data_key, g_smob);
+ }
+ else
+ g_smob->next = NULL;
+}
+
+/* Remove G_SMOB from the reference chain for OBJFILE specified
+ by DATA_KEY. OBJFILE may be NULL. */
+
+void
+gdbscm_remove_objfile_ref (struct objfile *objfile,
+ const struct objfile_data *data_key,
+ chained_gdb_smob *g_smob)
+{
+ if (g_smob->prev)
+ g_smob->prev->next = g_smob->next;
+ else if (objfile != NULL)
+ set_objfile_data (objfile, data_key, g_smob->next);
+ if (g_smob->next)
+ g_smob->next->prev = g_smob->prev;
+}
+
+/* Create a hash table for mapping a pointer to a gdb data structure to the
+ gsmob that wraps it. */
+
+htab_t
+gdbscm_create_eqable_gsmob_ptr_map (htab_hash hash_fn, htab_eq eq_fn)
+{
+ htab_t htab = htab_create_alloc (7, hash_fn, eq_fn,
+ NULL, xcalloc, xfree);
+
+ return htab;
+}
+
+/* Return a pointer to the htab entry for the eq?-able gsmob BASE.
+ If the entry is found, *SLOT is non-NULL.
+ Otherwise *slot is NULL. */
+
+eqable_gdb_smob **
+gdbscm_find_eqable_gsmob_ptr_slot (htab_t htab, eqable_gdb_smob *base)
+{
+ void **slot = htab_find_slot (htab, base, INSERT);
+
+ return (eqable_gdb_smob **) slot;
+}
+
+/* Record CONTAINING_SCM as the object containing BASE, and record it in
+ SLOT. SLOT must be the result of calling gdbscm_find_eqable_gsmob_ptr_slot
+ on BASE (or equivalent for lookup). */
+
+void
+gdbscm_fill_eqable_gsmob_ptr_slot (eqable_gdb_smob **slot,
+ eqable_gdb_smob *base,
+ SCM containing_scm)
+{
+ base->containing_scm = containing_scm;
+ *slot = base;
+}
+
+/* Remove BASE from HTAB.
+ BASE is a pointer to a gsmob that wraps a pointer to a GDB datum.
+ This is used, for example, when an object is freed.
+
+ It is an error to call this if PTR is not in HTAB (only because it allows
+ for some consistency checking). */
+
+void
+gdbscm_clear_eqable_gsmob_ptr_slot (htab_t htab, eqable_gdb_smob *base)
+{
+ void **slot = htab_find_slot (htab, base, NO_INSERT);
+
+ gdb_assert (slot != NULL);
+ htab_clear_slot (htab, slot);
+}
+
+/* Initialize the Scheme gsmobs code. */
+
+static const scheme_function gsmob_functions[] =
+{
+ { "gsmob-kind", 1, 0, 0, gdbscm_gsmob_kind,
+ "\
+Return the kind of the smob, e.g., <gdb:breakpoint>, as a symbol." },
+
+ { "gsmob-property", 2, 0, 0, gdbscm_gsmob_property,
+ "\
+Return the specified property of the gsmob." },
+
+ { "set-gsmob-property!", 3, 0, 0, gdbscm_set_gsmob_property_x,
+ "\
+Set the specified property of the gsmob." },
+
+ { "gsmob-has-property?", 2, 0, 0, gdbscm_gsmob_has_property_p,
+ "\
+Return #t if the specified property is present." },
+
+ { "gsmob-properties", 1, 0, 0, gdbscm_gsmob_properties,
+ "\
+Return an unsorted list of names of properties." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_smobs (void)
+{
+ registered_gsmobs = htab_create_alloc (10,
+ hash_scm_t_bits, eq_scm_t_bits,
+ NULL, xcalloc, xfree);
+
+ gdbscm_define_functions (gsmob_functions, 1);
+}
diff --git a/gdb/guile/scm-iterator.c b/gdb/guile/scm-iterator.c
new file mode 100644
index 0000000..a6deb84
--- /dev/null
+++ b/gdb/guile/scm-iterator.c
@@ -0,0 +1,375 @@
+/* Simple iterators for GDB/Scheme.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+/* These are *simple* iterators, used to implement iterating over a collection
+ of objects. They are implemented as a smob containing three objects:
+
+ 1) the object being iterated over,
+ 2) an object to record the progress of the iteration,
+ 3) a procedure of one argument (the iterator object) that returns the next
+ object in the iteration or a pre-determined end marker.
+
+ Simple example:
+
+ (define-public (make-list-iterator l end-marker)
+ "Return a <gdb:iterator> object for a list."
+ (let ((next! (lambda (iter)
+ (let ((l (iterator-progress iter)))
+ (if (eq? l '())
+ end-marker
+ (begin
+ (set-iterator-progress! iter (cdr l))
+ (car l)))))))
+ (make-iterator l l next!)))
+
+ (define l '(1 2))
+ (define i (make-list-iterator l #:eoi))
+ (iterator-next! i) -> 1
+ (iterator-next! i) -> 2
+ (iterator-next! i) -> #:eoi
+
+ There is SRFI 41, Streams. We might support that too eventually (not with
+ this interface of course). */
+
+#include "defs.h"
+#include "guile-internal.h"
+
+/* A smob for iterating over something.
+ Typically this is used when computing a list of everything is
+ too expensive.
+ The typedef for this struct is in guile-internal.h. */
+
+struct _iterator_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The object being iterated over. */
+ SCM object;
+
+ /* An arbitrary object describing the progress of the iteration.
+ This is used by next_x to track progress. */
+ SCM progress;
+
+ /* A procedure of one argument, the iterator.
+ It returns the next object in the iteration.
+ How to signal "end of iteration" is up to next_x. */
+ SCM next_x;
+};
+
+static const char iterator_smob_name[] = "gdb:iterator";
+
+/* The tag Guile knows the iterator smob by. */
+static scm_t_bits iterator_smob_tag;
+
+/* A unique-enough marker to denote "end of iteration". */
+static SCM end_of_iteration;
+
+const char *
+itscm_iterator_smob_name (void)
+{
+ return iterator_smob_name;
+}
+
+SCM
+itscm_iterator_smob_object (iterator_smob *i_smob)
+{
+ return i_smob->object;
+}
+
+SCM
+itscm_iterator_smob_progress (iterator_smob *i_smob)
+{
+ return i_smob->progress;
+}
+
+void
+itscm_set_iterator_smob_progress_x (iterator_smob *i_smob, SCM progress)
+{
+ i_smob->progress = progress;
+}
+
+/* Administrivia for iterator smobs. */
+
+/* The smob "mark" function for <gdb:iterator>. */
+
+static SCM
+itscm_mark_iterator_smob (SCM self)
+{
+ iterator_smob *i_smob = (iterator_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (i_smob->object);
+ scm_gc_mark (i_smob->progress);
+ scm_gc_mark (i_smob->next_x);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&i_smob->base);
+}
+
+/* The smob "print" function for <gdb:iterator>. */
+
+static int
+itscm_print_iterator_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ iterator_smob *i_smob = (iterator_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", iterator_smob_name);
+ scm_write (i_smob->object, port);
+ scm_puts (" ", port);
+ scm_write (i_smob->progress, port);
+ scm_puts (" ", port);
+ scm_write (i_smob->next_x, port);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to make a <gdb:iterator> object.
+ Caller must verify correctness of arguments.
+ No exceptions are thrown. */
+
+static SCM
+itscm_make_iterator_smob (SCM object, SCM progress, SCM next)
+{
+ iterator_smob *i_smob = (iterator_smob *)
+ scm_gc_malloc (sizeof (iterator_smob), iterator_smob_name);
+ SCM i_scm;
+
+ i_smob->object = object;
+ i_smob->progress = progress;
+ i_smob->next_x = next;
+ i_scm = scm_new_smob (iterator_smob_tag, (scm_t_bits) i_smob);
+ gdbscm_init_gsmob (&i_smob->base);
+
+ return i_scm;
+}
+
+/* (make-iterator object object procedure) -> <gdb:iterator> */
+
+SCM
+gdbscm_make_iterator (SCM object, SCM progress, SCM next)
+{
+ SCM i_scm;
+
+ SCM_ASSERT_TYPE (gdbscm_is_procedure (next), next, SCM_ARG3, FUNC_NAME,
+ _("procedure"));
+
+ i_scm = itscm_make_iterator_smob (object, progress, next);
+
+ return i_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:iterator> object. */
+
+int
+itscm_is_iterator (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (iterator_smob_tag, scm);
+}
+
+/* (iterator? object) -> boolean */
+
+static SCM
+gdbscm_iterator_p (SCM scm)
+{
+ return scm_from_bool (itscm_is_iterator (scm));
+}
+
+/* (end-of-iteration) -> an "end-of-iteration" marker
+ We rely on this not being used as a data result of an iterator. */
+
+SCM
+gdbscm_end_of_iteration (void)
+{
+ return end_of_iteration;
+}
+
+/* Return non-zero if OBJ is the end-of-iteration marker. */
+
+int
+itscm_is_end_of_iteration (SCM obj)
+{
+ return scm_is_eq (obj, end_of_iteration);
+}
+
+/* (end-of-iteration? obj) -> boolean */
+
+static SCM
+gdbscm_end_of_iteration_p (SCM obj)
+{
+ return scm_from_bool (itscm_is_end_of_iteration (obj));
+}
+
+/* Call the next! method on ITER, which must be a <gdb:iterator> object.
+ Returns a <gdb:exception> object if an exception is thrown.
+ OK_EXCPS is passed to gdbscm_safe_call_1. */
+
+SCM
+itscm_safe_call_next_x (SCM iter, excp_matcher_func *ok_excps)
+{
+ iterator_smob *i_smob;
+
+ gdb_assert (itscm_is_iterator (iter));
+
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (iter);
+ return gdbscm_safe_call_1 (i_smob->next_x, iter, ok_excps);
+}
+
+/* Iterator methods. */
+
+/* Returns the <gdb:iterator> smob in SELF.
+ Throws an exception if SELF is not an iterator smob. */
+
+SCM
+itscm_get_iterator_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (itscm_is_iterator (self), self, arg_pos, func_name,
+ iterator_smob_name);
+
+ return self;
+}
+
+/* (iterator-object <gdb:iterator>) -> object */
+
+static SCM
+gdbscm_iterator_object (SCM self)
+{
+ SCM i_scm;
+ iterator_smob *i_smob;
+
+ i_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (i_scm);
+
+ return i_smob->object;
+}
+
+/* (iterator-progress <gdb:iterator>) -> object */
+
+static SCM
+gdbscm_iterator_progress (SCM self)
+{
+ SCM i_scm;
+ iterator_smob *i_smob;
+
+ i_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (i_scm);
+
+ return i_smob->progress;
+}
+
+/* (set-iterator-progress! <gdb:iterator> object) -> unspecified */
+
+static SCM
+gdbscm_set_iterator_progress_x (SCM self, SCM value)
+{
+ SCM i_scm;
+ iterator_smob *i_smob;
+
+ i_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (i_scm);
+
+ i_smob->progress = value;
+ return SCM_UNSPECIFIED;
+}
+
+/* (iterator-next! <gdb:iterator>) -> object
+ The result is the next value in the iteration or some "end" marker.
+ It is up to each iterator's next! function to specify what its end
+ marker is. */
+
+static SCM
+gdbscm_iterator_next_x (SCM self)
+{
+ SCM i_scm;
+ iterator_smob *i_smob;
+
+ i_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (i_scm);
+ /* We leave type-checking of the procedure to gdbscm_safe_call_1. */
+
+ return gdbscm_safe_call_1 (i_smob->next_x, self, NULL);
+}
+
+/* Initialize the Scheme iterator code. */
+
+static const scheme_function iterator_functions[] =
+{
+ { "make-iterator", 3, 0, 0, gdbscm_make_iterator,
+ "\
+Create a <gdb:iterator> object.\n\
+\n\
+ Arguments: object progress next!\n\
+ object: The object to iterate over.\n\
+ progress: An object to use to track progress of the iteration.\n\
+ next!: A procedure of one argument, the iterator.\n\
+ Returns the next element in the iteration or an implementation-chosen\n\
+ value to signify iteration is complete.\n\
+ By convention end-of-iteration should be marked with (end-of-iteration)\n\
+ from module (gdb iterator)." },
+
+ { "iterator?", 1, 0, 0, gdbscm_iterator_p,
+ "\
+Return #t if the object is a <gdb:iterator> object." },
+
+ { "iterator-object", 1, 0, 0, gdbscm_iterator_object,
+ "\
+Return the object being iterated over." },
+
+ { "iterator-progress", 1, 0, 0, gdbscm_iterator_progress,
+ "\
+Return the progress object of the iterator." },
+
+ { "set-iterator-progress!", 2, 0, 0, gdbscm_set_iterator_progress_x,
+ "\
+Set the progress object of the iterator." },
+
+ { "iterator-next!", 1, 0, 0, gdbscm_iterator_next_x,
+ "\
+Invoke the next! procedure of the iterator and return its result." },
+
+ { "end-of-iteration", 0, 0, 0, gdbscm_end_of_iteration,
+ "\
+Return the end-of-iteration marker." },
+
+ { "end-of-iteration?", 1, 0, 0, gdbscm_end_of_iteration_p,
+ "\
+Return #t if the object is the end-of-iteration marker." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_iterators (void)
+{
+ iterator_smob_tag = gdbscm_make_smob_type (iterator_smob_name,
+ sizeof (iterator_smob));
+ scm_set_smob_mark (iterator_smob_tag, itscm_mark_iterator_smob);
+ scm_set_smob_print (iterator_smob_tag, itscm_print_iterator_smob);
+
+ gdbscm_define_functions (iterator_functions, 1);
+
+ /* We can make this more unique if it's necessary,
+ but this is good enough for now. */
+ end_of_iteration = scm_from_latin1_keyword ("end-of-iteration");
+}
diff --git a/gdb/guile/scm-lazy-string.c b/gdb/guile/scm-lazy-string.c
new file mode 100644
index 0000000..e965d01
--- /dev/null
+++ b/gdb/guile/scm-lazy-string.c
@@ -0,0 +1,373 @@
+/* Scheme interface to lazy strings.
+
+ Copyright (C) 2010-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "charset.h"
+#include "value.h"
+#include "exceptions.h"
+#include "valprint.h"
+#include "language.h"
+#include "gdb_assert.h"
+#include "guile-internal.h"
+
+/* The <gdb:lazy-string> smob. */
+
+typedef struct
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Holds the address of the lazy string. */
+ CORE_ADDR address;
+
+ /* Holds the encoding that will be applied to the string when the string
+ is printed by GDB. If the encoding is set to NULL then GDB will select
+ the most appropriate encoding when the sting is printed.
+ Space for this is malloc'd and will be freed when the object is
+ freed. */
+ char *encoding;
+
+ /* Holds the length of the string in characters. If the length is -1,
+ then the string will be fetched and encoded up to the first null of
+ appropriate width. */
+ int length;
+
+ /* This attribute holds the type that is represented by the lazy
+ string's type. */
+ struct type *type;
+} lazy_string_smob;
+
+static const char lazy_string_smob_name[] = "gdb:lazy-string";
+
+/* The tag Guile knows the lazy string smob by. */
+static scm_t_bits lazy_string_smob_tag;
+
+/* Administrivia for lazy string smobs. */
+
+/* The smob "mark" function for <gdb:lazy-string>. */
+
+static SCM
+lsscm_mark_lazy_string_smob (SCM self)
+{
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&ls_smob->base);
+}
+
+/* The smob "free" function for <gdb:lazy-string>. */
+
+static size_t
+lsscm_free_lazy_string_smob (SCM self)
+{
+ lazy_string_smob *v_smob = (lazy_string_smob *) SCM_SMOB_DATA (self);
+
+ xfree (v_smob->encoding);
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:lazy-string>. */
+
+static int
+lsscm_print_lazy_string_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s", lazy_string_smob_name);
+ gdbscm_printf (port, " @%s", hex_string (ls_smob->address));
+ if (ls_smob->length >= 0)
+ gdbscm_printf (port, " length %d", ls_smob->length);
+ if (ls_smob->encoding != NULL)
+ gdbscm_printf (port, " encoding %s", ls_smob->encoding);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:lazy-string> object.
+ The caller must verify !(address == 0 && length != 0). */
+
+static SCM
+lsscm_make_lazy_string_smob (CORE_ADDR address, int length,
+ const char *encoding, struct type *type)
+{
+ lazy_string_smob *ls_smob = (lazy_string_smob *)
+ scm_gc_malloc (sizeof (lazy_string_smob), lazy_string_smob_name);
+ SCM ls_scm;
+
+ /* Caller must verify this. */
+ gdb_assert (!(address == 0 && length != 0));
+ gdb_assert (type != NULL);
+
+ ls_smob->address = address;
+ /* Coerce all values < 0 to -1. */
+ ls_smob->length = length < 0 ? -1 : length;
+ if (encoding == NULL || strcmp (encoding, "") == 0)
+ ls_smob->encoding = NULL;
+ else
+ ls_smob->encoding = xstrdup (encoding);
+ ls_smob->type = type;
+
+ ls_scm = scm_new_smob (lazy_string_smob_tag, (scm_t_bits) ls_smob);
+ gdbscm_init_gsmob (&ls_smob->base);
+
+ return ls_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:lazy-string> object. */
+
+int
+lsscm_is_lazy_string (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (lazy_string_smob_tag, scm);
+}
+
+/* (lazy-string? object) -> boolean */
+
+static SCM
+gdbscm_lazy_string_p (SCM scm)
+{
+ return scm_from_bool (lsscm_is_lazy_string (scm));
+}
+
+/* Main entry point to create a <gdb:lazy-string> object.
+ If there's an error a <gdb:exception> object is returned. */
+
+SCM
+lsscm_make_lazy_string (CORE_ADDR address, int length,
+ const char *encoding, struct type *type)
+{
+ if (address == 0 && length != 0)
+ {
+ return gdbscm_make_out_of_range_error
+ (NULL, 0, scm_from_int (length),
+ _("cannot create a lazy string with address 0x0"
+ " and a non-zero length"));
+ }
+
+ if (type == NULL)
+ {
+ return gdbscm_make_out_of_range_error
+ (NULL, 0, scm_from_int (0), _("a lazy string's type cannot be NULL"));
+ }
+
+ return lsscm_make_lazy_string_smob (address, length, encoding, type);
+}
+
+/* Returns the <gdb:lazy-string> smob in SELF.
+ Throws an exception if SELF is not a <gdb:lazy-string> object. */
+
+static SCM
+lsscm_get_lazy_string_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (lsscm_is_lazy_string (self), self, arg_pos, func_name,
+ lazy_string_smob_name);
+
+ return self;
+}
+
+/* Lazy string methods. */
+
+/* (lazy-string-address <gdb:lazy-string>) -> address */
+
+static SCM
+gdbscm_lazy_string_address (SCM self)
+{
+ SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm);
+
+ return gdbscm_scm_from_ulongest (ls_smob->address);
+}
+
+/* (lazy-string-length <gdb:lazy-string>) -> integer */
+
+static SCM
+gdbscm_lazy_string_length (SCM self)
+{
+ SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm);
+
+ return scm_from_int (ls_smob->length);
+}
+
+/* (lazy-string-encoding <gdb:lazy-string>) -> string */
+
+static SCM
+gdbscm_lazy_string_encoding (SCM self)
+{
+ SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm);
+
+ /* An encoding can be set to NULL by the user, so check first.
+ If NULL return #f. */
+ if (ls_smob != NULL)
+ return gdbscm_scm_from_c_string (ls_smob->encoding);
+ return SCM_BOOL_F;
+}
+
+/* (lazy-string-type <gdb:lazy-string>) -> <gdb:type> */
+
+static SCM
+gdbscm_lazy_string_type (SCM self)
+{
+ SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm);
+
+ return tyscm_scm_from_type (ls_smob->type);
+}
+
+/* (lazy-string->value <gdb:lazy-string>) -> <gdb:value> */
+
+static SCM
+gdbscm_lazy_string_to_value (SCM self)
+{
+ SCM ls_scm = lsscm_get_lazy_string_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ lazy_string_smob *ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (ls_scm);
+ struct value *value = NULL;
+ volatile struct gdb_exception except;
+
+ if (ls_smob->address == 0)
+ {
+ gdbscm_throw (gdbscm_make_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _("cannot create a value from NULL")));
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ value = value_at_lazy (ls_smob->type, ls_smob->address);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return vlscm_scm_from_value (value);
+}
+
+/* A "safe" version of gdbscm_lazy_string_to_value for use by
+ vlscm_convert_typed_value_from_scheme.
+ The result, upon success, is the value of <gdb:lazy-string> STRING.
+ ARG_POS is the argument position of STRING in the original Scheme
+ function call, used in exception text.
+ If there's an error, NULL is returned and a <gdb:exception> object
+ is stored in *except_scmp.
+
+ Note: The result is still "lazy". The caller must call value_fetch_lazy
+ to actually fetch the value. */
+
+struct value *
+lsscm_safe_lazy_string_to_value (SCM string, int arg_pos,
+ const char *func_name, SCM *except_scmp)
+{
+ lazy_string_smob *ls_smob;
+ struct value *value = NULL;
+ volatile struct gdb_exception except;
+
+ gdb_assert (lsscm_is_lazy_string (string));
+
+ ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (string);
+ *except_scmp = SCM_BOOL_F;
+
+ if (ls_smob->address == 0)
+ {
+ *except_scmp
+ = gdbscm_make_out_of_range_error (FUNC_NAME, SCM_ARG1, string,
+ _("cannot create a value from NULL"));
+ return NULL;
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ value = value_at_lazy (ls_smob->type, ls_smob->address);
+ }
+ if (except.reason < 0)
+ {
+ *except_scmp = gdbscm_scm_from_gdb_exception (except);
+ return NULL;
+ }
+
+ return value;
+}
+
+/* Print a lazy string to STREAM using val_print_string.
+ STRING must be a <gdb:lazy-string> object. */
+
+void
+lsscm_val_print_lazy_string (SCM string, struct ui_file *stream,
+ const struct value_print_options *options)
+{
+ lazy_string_smob *ls_smob;
+
+ gdb_assert (lsscm_is_lazy_string (string));
+
+ ls_smob = (lazy_string_smob *) SCM_SMOB_DATA (string);
+
+ val_print_string (ls_smob->type, ls_smob->encoding,
+ ls_smob->address, ls_smob->length,
+ stream, options);
+}
+
+/* Initialize the Scheme lazy-strings code. */
+
+static const scheme_function lazy_string_functions[] =
+{
+ { "lazy-string?", 1, 0, 0, gdbscm_lazy_string_p,
+ "\
+Return #t if the object is a <gdb:lazy-string> object." },
+
+ { "lazy-string-address", 1, 0, 0, gdbscm_lazy_string_address,
+ "\
+Return the address of the lazy-string." },
+
+ { "lazy-string-length", 1, 0, 0, gdbscm_lazy_string_length,
+ "\
+Return the length of the lazy-string.\n\
+If the length is -1 then the length is determined by the first null\n\
+of appropriate width." },
+
+ { "lazy-string-encoding", 1, 0, 0, gdbscm_lazy_string_encoding,
+ "\
+Return the encoding of the lazy-string." },
+
+ { "lazy-string-type", 1, 0, 0, gdbscm_lazy_string_type,
+ "\
+Return the <gdb:type> of the lazy-string." },
+
+ { "lazy-string->value", 1, 0, 0, gdbscm_lazy_string_to_value,
+ "\
+Return the <gdb:value> representation of the lazy-string." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_lazy_strings (void)
+{
+ lazy_string_smob_tag = gdbscm_make_smob_type (lazy_string_smob_name,
+ sizeof (lazy_string_smob));
+ scm_set_smob_mark (lazy_string_smob_tag, lsscm_mark_lazy_string_smob);
+ scm_set_smob_free (lazy_string_smob_tag, lsscm_free_lazy_string_smob);
+ scm_set_smob_print (lazy_string_smob_tag, lsscm_print_lazy_string_smob);
+
+ gdbscm_define_functions (lazy_string_functions, 1);
+}
diff --git a/gdb/guile/scm-math.c b/gdb/guile/scm-math.c
new file mode 100644
index 0000000..80e1673
--- /dev/null
+++ b/gdb/guile/scm-math.c
@@ -0,0 +1,998 @@
+/* GDB/Scheme support for math operations on values.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "charset.h"
+#include "cp-abi.h"
+#include "doublest.h" /* Needed by dfp.h. */
+#include "expression.h" /* Needed by dfp.h. */
+#include "dfp.h"
+#include "gdb_assert.h"
+#include "symtab.h" /* Needed by language.h. */
+#include "language.h"
+#include "valprint.h"
+#include "value.h"
+#include "guile-internal.h"
+
+/* Note: Use target types here to remain consistent with the values system in
+ GDB (which uses target arithmetic). */
+
+enum valscm_unary_opcode
+{
+ VALSCM_NOT,
+ VALSCM_NEG,
+ VALSCM_NOP,
+ VALSCM_ABS,
+ /* Note: This is Scheme's "logical not", not GDB's.
+ GDB calls this UNOP_COMPLEMENT. */
+ VALSCM_LOGNOT
+};
+
+enum valscm_binary_opcode
+{
+ VALSCM_ADD,
+ VALSCM_SUB,
+ VALSCM_MUL,
+ VALSCM_DIV,
+ VALSCM_REM,
+ VALSCM_MOD,
+ VALSCM_POW,
+ VALSCM_LSH,
+ VALSCM_RSH,
+ VALSCM_MIN,
+ VALSCM_MAX,
+ VALSCM_BITAND,
+ VALSCM_BITOR,
+ VALSCM_BITXOR
+};
+
+/* If TYPE is a reference, return the target; otherwise return TYPE. */
+#define STRIP_REFERENCE(TYPE) \
+ ((TYPE_CODE (TYPE) == TYPE_CODE_REF) ? (TYPE_TARGET_TYPE (TYPE)) : (TYPE))
+
+/* Returns a value object which is the result of applying the operation
+ specified by OPCODE to the given argument.
+ If there's an error a Scheme exception is thrown. */
+
+static SCM
+vlscm_unop (enum valscm_unary_opcode opcode, SCM x, const char *func_name)
+{
+ struct gdbarch *gdbarch = get_current_arch ();
+ const struct language_defn *language = current_language;
+ struct value *arg1;
+ SCM result = SCM_BOOL_F;
+ struct value *res_val = NULL;
+ SCM except_scm;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ arg1 = vlscm_convert_value_from_scheme (func_name, SCM_ARG1, x,
+ &except_scm, gdbarch, language);
+ if (arg1 == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ switch (opcode)
+ {
+ case VALSCM_NOT:
+ /* Alas gdb and guile use the opposite meaning for "logical not". */
+ {
+ struct type *type = language_bool_type (language, gdbarch);
+ res_val
+ = value_from_longest (type, (LONGEST) value_logical_not (arg1));
+ }
+ break;
+ case VALSCM_NEG:
+ res_val = value_neg (arg1);
+ break;
+ case VALSCM_NOP:
+ /* Seemingly a no-op, but if X was a Scheme value it is now
+ a <gdb:value> object. */
+ res_val = arg1;
+ break;
+ case VALSCM_ABS:
+ if (value_less (arg1, value_zero (value_type (arg1), not_lval)))
+ res_val = value_neg (arg1);
+ else
+ res_val = arg1;
+ break;
+ case VALSCM_LOGNOT:
+ res_val = value_complement (arg1);
+ break;
+ default:
+ gdb_assert_not_reached ("unsupported operation");
+ }
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* Returns a value object which is the result of applying the operation
+ specified by OPCODE to the given arguments.
+ If there's an error a Scheme exception is thrown. */
+
+static SCM
+vlscm_binop (enum valscm_binary_opcode opcode, SCM x, SCM y,
+ const char *func_name)
+{
+ struct gdbarch *gdbarch = get_current_arch ();
+ const struct language_defn *language = current_language;
+ struct value *arg1, *arg2;
+ SCM result = SCM_BOOL_F;
+ struct value *res_val = NULL;
+ SCM except_scm;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ arg1 = vlscm_convert_value_from_scheme (func_name, SCM_ARG1, x,
+ &except_scm, gdbarch, language);
+ if (arg1 == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+ arg2 = vlscm_convert_value_from_scheme (func_name, SCM_ARG2, y,
+ &except_scm, gdbarch, language);
+ if (arg2 == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ switch (opcode)
+ {
+ case VALSCM_ADD:
+ {
+ struct type *ltype = value_type (arg1);
+ struct type *rtype = value_type (arg2);
+
+ CHECK_TYPEDEF (ltype);
+ ltype = STRIP_REFERENCE (ltype);
+ CHECK_TYPEDEF (rtype);
+ rtype = STRIP_REFERENCE (rtype);
+
+ if (TYPE_CODE (ltype) == TYPE_CODE_PTR
+ && is_integral_type (rtype))
+ res_val = value_ptradd (arg1, value_as_long (arg2));
+ else if (TYPE_CODE (rtype) == TYPE_CODE_PTR
+ && is_integral_type (ltype))
+ res_val = value_ptradd (arg2, value_as_long (arg1));
+ else
+ res_val = value_binop (arg1, arg2, BINOP_ADD);
+ }
+ break;
+ case VALSCM_SUB:
+ {
+ struct type *ltype = value_type (arg1);
+ struct type *rtype = value_type (arg2);
+
+ CHECK_TYPEDEF (ltype);
+ ltype = STRIP_REFERENCE (ltype);
+ CHECK_TYPEDEF (rtype);
+ rtype = STRIP_REFERENCE (rtype);
+
+ if (TYPE_CODE (ltype) == TYPE_CODE_PTR
+ && TYPE_CODE (rtype) == TYPE_CODE_PTR)
+ {
+ /* A ptrdiff_t for the target would be preferable here. */
+ res_val
+ = value_from_longest (builtin_type (gdbarch)->builtin_long,
+ value_ptrdiff (arg1, arg2));
+ }
+ else if (TYPE_CODE (ltype) == TYPE_CODE_PTR
+ && is_integral_type (rtype))
+ res_val = value_ptradd (arg1, - value_as_long (arg2));
+ else
+ res_val = value_binop (arg1, arg2, BINOP_SUB);
+ }
+ break;
+ case VALSCM_MUL:
+ res_val = value_binop (arg1, arg2, BINOP_MUL);
+ break;
+ case VALSCM_DIV:
+ res_val = value_binop (arg1, arg2, BINOP_DIV);
+ break;
+ case VALSCM_REM:
+ res_val = value_binop (arg1, arg2, BINOP_REM);
+ break;
+ case VALSCM_MOD:
+ res_val = value_binop (arg1, arg2, BINOP_MOD);
+ break;
+ case VALSCM_POW:
+ res_val = value_binop (arg1, arg2, BINOP_EXP);
+ break;
+ case VALSCM_LSH:
+ res_val = value_binop (arg1, arg2, BINOP_LSH);
+ break;
+ case VALSCM_RSH:
+ res_val = value_binop (arg1, arg2, BINOP_RSH);
+ break;
+ case VALSCM_MIN:
+ res_val = value_binop (arg1, arg2, BINOP_MIN);
+ break;
+ case VALSCM_MAX:
+ res_val = value_binop (arg1, arg2, BINOP_MAX);
+ break;
+ case VALSCM_BITAND:
+ res_val = value_binop (arg1, arg2, BINOP_BITWISE_AND);
+ break;
+ case VALSCM_BITOR:
+ res_val = value_binop (arg1, arg2, BINOP_BITWISE_IOR);
+ break;
+ case VALSCM_BITXOR:
+ res_val = value_binop (arg1, arg2, BINOP_BITWISE_XOR);
+ break;
+ default:
+ gdb_assert_not_reached ("unsupported operation");
+ }
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-add x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_add (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_ADD, x, y, FUNC_NAME);
+}
+
+/* (value-sub x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_sub (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_SUB, x, y, FUNC_NAME);
+}
+
+/* (value-mul x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_mul (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_MUL, x, y, FUNC_NAME);
+}
+
+/* (value-div x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_div (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_DIV, x, y, FUNC_NAME);
+}
+
+/* (value-rem x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_rem (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_REM, x, y, FUNC_NAME);
+}
+
+/* (value-mod x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_mod (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_MOD, x, y, FUNC_NAME);
+}
+
+/* (value-pow x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_pow (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_POW, x, y, FUNC_NAME);
+}
+
+/* (value-neg x) -> <gdb:value> */
+
+static SCM
+gdbscm_value_neg (SCM x)
+{
+ return vlscm_unop (VALSCM_NEG, x, FUNC_NAME);
+}
+
+/* (value-pos x) -> <gdb:value> */
+
+static SCM
+gdbscm_value_pos (SCM x)
+{
+ return vlscm_unop (VALSCM_NOP, x, FUNC_NAME);
+}
+
+/* (value-abs x) -> <gdb:value> */
+
+static SCM
+gdbscm_value_abs (SCM x)
+{
+ return vlscm_unop (VALSCM_ABS, x, FUNC_NAME);
+}
+
+/* (value-lsh x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_lsh (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_LSH, x, y, FUNC_NAME);
+}
+
+/* (value-rsh x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_rsh (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_RSH, x, y, FUNC_NAME);
+}
+
+/* (value-min x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_min (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_MIN, x, y, FUNC_NAME);
+}
+
+/* (value-max x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_max (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_MAX, x, y, FUNC_NAME);
+}
+
+/* (value-not x) -> <gdb:value> */
+
+static SCM
+gdbscm_value_not (SCM x)
+{
+ return vlscm_unop (VALSCM_NOT, x, FUNC_NAME);
+}
+
+/* (value-lognot x) -> <gdb:value> */
+
+static SCM
+gdbscm_value_lognot (SCM x)
+{
+ return vlscm_unop (VALSCM_LOGNOT, x, FUNC_NAME);
+}
+
+/* (value-logand x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_logand (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_BITAND, x, y, FUNC_NAME);
+}
+
+/* (value-logior x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_logior (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_BITOR, x, y, FUNC_NAME);
+}
+
+/* (value-logxor x y) -> <gdb:value> */
+
+static SCM
+gdbscm_value_logxor (SCM x, SCM y)
+{
+ return vlscm_binop (VALSCM_BITXOR, x, y, FUNC_NAME);
+}
+
+/* Utility to perform all value comparisons.
+ If there's an error a Scheme exception is thrown. */
+
+static SCM
+vlscm_rich_compare (int op, SCM x, SCM y, const char *func_name)
+{
+ struct gdbarch *gdbarch = get_current_arch ();
+ const struct language_defn *language = current_language;
+ struct value *v1, *v2;
+ int result = 0;
+ SCM except_scm;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ v1 = vlscm_convert_value_from_scheme (func_name, SCM_ARG1, x,
+ &except_scm, gdbarch, language);
+ if (v1 == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+ v2 = vlscm_convert_value_from_scheme (func_name, SCM_ARG2, y,
+ &except_scm, gdbarch, language);
+ if (v2 == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ switch (op)
+ {
+ case BINOP_LESS:
+ result = value_less (v1, v2);
+ break;
+ case BINOP_LEQ:
+ result = (value_less (v1, v2)
+ || value_equal (v1, v2));
+ break;
+ case BINOP_EQUAL:
+ result = value_equal (v1, v2);
+ break;
+ case BINOP_NOTEQUAL:
+ gdb_assert_not_reached ("not-equal not implemented");
+ case BINOP_GTR:
+ result = value_less (v2, v1);
+ break;
+ case BINOP_GEQ:
+ result = (value_less (v2, v1)
+ || value_equal (v1, v2));
+ break;
+ default:
+ gdb_assert_not_reached ("invalid <gdb:value> comparison");
+ }
+ }
+ do_cleanups (cleanups);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (result);
+}
+
+/* (value=? x y) -> boolean
+ There is no "not-equal?" function (value!= ?) on purpose.
+ We're following string=?, etc. as our Guide here. */
+
+static SCM
+gdbscm_value_eq_p (SCM x, SCM y)
+{
+ return vlscm_rich_compare (BINOP_EQUAL, x, y, FUNC_NAME);
+}
+
+/* (value<? x y) -> boolean */
+
+static SCM
+gdbscm_value_lt_p (SCM x, SCM y)
+{
+ return vlscm_rich_compare (BINOP_LESS, x, y, FUNC_NAME);
+}
+
+/* (value<=? x y) -> boolean */
+
+static SCM
+gdbscm_value_le_p (SCM x, SCM y)
+{
+ return vlscm_rich_compare (BINOP_LEQ, x, y, FUNC_NAME);
+}
+
+/* (value>? x y) -> boolean */
+
+static SCM
+gdbscm_value_gt_p (SCM x, SCM y)
+{
+ return vlscm_rich_compare (BINOP_GTR, x, y, FUNC_NAME);
+}
+
+/* (value>=? x y) -> boolean */
+
+static SCM
+gdbscm_value_ge_p (SCM x, SCM y)
+{
+ return vlscm_rich_compare (BINOP_GEQ, x, y, FUNC_NAME);
+}
+
+/* Subroutine of vlscm_convert_typed_value_from_scheme to simplify it.
+ Convert OBJ, a Scheme number, to a <gdb:value> object.
+ OBJ_ARG_POS is its position in the argument list, used in exception text.
+
+ TYPE is the result type. TYPE_ARG_POS is its position in
+ the argument list, used in exception text.
+ TYPE_SCM is Scheme object wrapping TYPE, used in exception text.
+
+ If the number isn't representable, e.g. it's too big, a <gdb:exception>
+ object is stored in *EXCEPT_SCMP and NULL is returned.
+ The conversion may throw a gdb error, e.g., if TYPE is invalid. */
+
+static struct value *
+vlscm_convert_typed_number (const char *func_name, int obj_arg_pos, SCM obj,
+ int type_arg_pos, SCM type_scm, struct type *type,
+ struct gdbarch *gdbarch, SCM *except_scmp)
+{
+ if (is_integral_type (type)
+ || TYPE_CODE (type) == TYPE_CODE_PTR)
+ {
+ if (TYPE_UNSIGNED (type))
+ {
+ ULONGEST max;
+
+ get_unsigned_type_max (type, &max);
+ if (!scm_is_unsigned_integer (obj, 0, max))
+ {
+ *except_scmp
+ = gdbscm_make_out_of_range_error (func_name,
+ obj_arg_pos, obj,
+ _("value out of range for type"));
+ return NULL;
+ }
+ return value_from_longest (type, gdbscm_scm_to_ulongest (obj));
+ }
+ else
+ {
+ LONGEST min, max;
+
+ get_signed_type_minmax (type, &min, &max);
+ if (!scm_is_signed_integer (obj, min, max))
+ {
+ *except_scmp
+ = gdbscm_make_out_of_range_error (func_name,
+ obj_arg_pos, obj,
+ _("value out of range for type"));
+ return NULL;
+ }
+ return value_from_longest (type, gdbscm_scm_to_longest (obj));
+ }
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ return value_from_double (type, scm_to_double (obj));
+ else
+ {
+ *except_scmp = gdbscm_make_type_error (func_name, obj_arg_pos, obj,
+ NULL);
+ return NULL;
+ }
+}
+
+/* Return non-zero if OBJ, an integer, fits in TYPE. */
+
+static int
+vlscm_integer_fits_p (SCM obj, struct type *type)
+{
+ if (TYPE_UNSIGNED (type))
+ {
+ ULONGEST max;
+
+ /* If scm_is_unsigned_integer can't work with this type, just punt. */
+ if (TYPE_LENGTH (type) > sizeof (scm_t_uintmax))
+ return 0;
+ get_unsigned_type_max (type, &max);
+ return scm_is_unsigned_integer (obj, 0, max);
+ }
+ else
+ {
+ LONGEST min, max;
+
+ /* If scm_is_signed_integer can't work with this type, just punt. */
+ if (TYPE_LENGTH (type) > sizeof (scm_t_intmax))
+ return 0;
+ get_signed_type_minmax (type, &min, &max);
+ return scm_is_signed_integer (obj, min, max);
+ }
+}
+
+/* Subroutine of vlscm_convert_typed_value_from_scheme to simplify it.
+ Convert OBJ, a Scheme number, to a <gdb:value> object.
+ OBJ_ARG_POS is its position in the argument list, used in exception text.
+
+ If OBJ is an integer, then the smallest int that will hold the value in
+ the following progression is chosen:
+ int, unsigned int, long, unsigned long, long long, unsigned long long.
+ Otherwise, if OBJ is a real number, then it is converted to a double.
+ Otherwise an exception is thrown.
+
+ If the number isn't representable, e.g. it's too big, a <gdb:exception>
+ object is stored in *EXCEPT_SCMP and NULL is returned. */
+
+static struct value *
+vlscm_convert_number (const char *func_name, int obj_arg_pos, SCM obj,
+ struct gdbarch *gdbarch, SCM *except_scmp)
+{
+ const struct builtin_type *bt = builtin_type (gdbarch);
+
+ /* One thing to keep in mind here is that we are interested in the
+ target's representation of OBJ, not the host's. */
+
+ if (scm_is_exact (obj) && scm_is_integer (obj))
+ {
+ if (vlscm_integer_fits_p (obj, bt->builtin_int))
+ return value_from_longest (bt->builtin_int,
+ gdbscm_scm_to_longest (obj));
+ if (vlscm_integer_fits_p (obj, bt->builtin_unsigned_int))
+ return value_from_longest (bt->builtin_unsigned_int,
+ gdbscm_scm_to_ulongest (obj));
+ if (vlscm_integer_fits_p (obj, bt->builtin_long))
+ return value_from_longest (bt->builtin_long,
+ gdbscm_scm_to_longest (obj));
+ if (vlscm_integer_fits_p (obj, bt->builtin_unsigned_long))
+ return value_from_longest (bt->builtin_unsigned_long,
+ gdbscm_scm_to_ulongest (obj));
+ if (vlscm_integer_fits_p (obj, bt->builtin_long_long))
+ return value_from_longest (bt->builtin_long_long,
+ gdbscm_scm_to_longest (obj));
+ if (vlscm_integer_fits_p (obj, bt->builtin_unsigned_long_long))
+ return value_from_longest (bt->builtin_unsigned_long_long,
+ gdbscm_scm_to_ulongest (obj));
+ }
+ else if (scm_is_real (obj))
+ return value_from_double (bt->builtin_double, scm_to_double (obj));
+
+ *except_scmp = gdbscm_make_out_of_range_error (func_name, obj_arg_pos, obj,
+ _("value not a number representable on the target"));
+ return NULL;
+}
+
+/* Subroutine of vlscm_convert_typed_value_from_scheme to simplify it.
+ Convert BV, a Scheme bytevector, to a <gdb:value> object.
+
+ TYPE, if non-NULL, is the result type. Otherwise, a vector of type
+ uint8_t is used.
+ TYPE_SCM is Scheme object wrapping TYPE, used in exception text,
+ or #f if TYPE is NULL.
+
+ If the bytevector isn't the same size as the type, then a <gdb:exception>
+ object is stored in *EXCEPT_SCMP, and NULL is returned. */
+
+static struct value *
+vlscm_convert_bytevector (SCM bv, struct type *type, SCM type_scm,
+ int arg_pos, const char *func_name,
+ SCM *except_scmp, struct gdbarch *gdbarch)
+{
+ LONGEST length = SCM_BYTEVECTOR_LENGTH (bv);
+ struct value *value;
+
+ if (type == NULL)
+ {
+ type = builtin_type (gdbarch)->builtin_uint8;
+ type = lookup_array_range_type (type, 0, length);
+ make_vector_type (type);
+ }
+ type = check_typedef (type);
+ if (TYPE_LENGTH (type) != length)
+ {
+ *except_scmp = gdbscm_make_out_of_range_error (func_name, arg_pos,
+ type_scm,
+ _("size of type does not match size of bytevector"));
+ return NULL;
+ }
+
+ value = value_from_contents (type,
+ (gdb_byte *) SCM_BYTEVECTOR_CONTENTS (bv));
+ return value;
+}
+
+/* Convert OBJ, a Scheme value, to a <gdb:value> object.
+ OBJ_ARG_POS is its position in the argument list, used in exception text.
+
+ TYPE, if non-NULL, is the result type which must be compatible with
+ the value being converted.
+ If TYPE is NULL then a suitable default type is chosen.
+ TYPE_SCM is Scheme object wrapping TYPE, used in exception text,
+ or SCM_UNDEFINED if TYPE is NULL.
+ TYPE_ARG_POS is its position in the argument list, used in exception text,
+ or -1 if TYPE is NULL.
+
+ OBJ may also be a <gdb:value> object, in which case a copy is returned
+ and TYPE must be NULL.
+
+ If the value cannot be converted, NULL is returned and a gdb:exception
+ object is stored in *EXCEPT_SCMP.
+ Otherwise the new value is returned, added to the all_values chain. */
+
+struct value *
+vlscm_convert_typed_value_from_scheme (const char *func_name,
+ int obj_arg_pos, SCM obj,
+ int type_arg_pos, SCM type_scm,
+ struct type *type,
+ SCM *except_scmp,
+ struct gdbarch *gdbarch,
+ const struct language_defn *language)
+{
+ struct value *value = NULL;
+ SCM except_scm = SCM_BOOL_F;
+ volatile struct gdb_exception except;
+
+ if (type == NULL)
+ {
+ gdb_assert (type_arg_pos == -1);
+ gdb_assert (SCM_UNBNDP (type_scm));
+ }
+
+ *except_scmp = SCM_BOOL_F;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (vlscm_is_value (obj))
+ {
+ if (type != NULL)
+ {
+ except_scm = gdbscm_make_misc_error (func_name, type_arg_pos,
+ type_scm,
+ _("No type allowed"));
+ value = NULL;
+ }
+ else
+ value = value_copy (vlscm_scm_to_value (obj));
+ }
+ else if (gdbscm_is_true (scm_bytevector_p (obj)))
+ {
+ value = vlscm_convert_bytevector (obj, type, type_scm,
+ obj_arg_pos, func_name,
+ &except_scm, gdbarch);
+ }
+ else if (gdbscm_is_bool (obj))
+ {
+ if (type != NULL
+ && !is_integral_type (type))
+ {
+ except_scm = gdbscm_make_type_error (func_name, type_arg_pos,
+ type_scm, NULL);
+ }
+ else
+ {
+ value = value_from_longest (type
+ ? type
+ : language_bool_type (language,
+ gdbarch),
+ gdbscm_is_true (obj));
+ }
+ }
+ else if (scm_is_number (obj))
+ {
+ if (type != NULL)
+ {
+ value = vlscm_convert_typed_number (func_name, obj_arg_pos, obj,
+ type_arg_pos, type_scm, type,
+ gdbarch, &except_scm);
+ }
+ else
+ {
+ value = vlscm_convert_number (func_name, obj_arg_pos, obj,
+ gdbarch, &except_scm);
+ }
+ }
+ else if (scm_is_string (obj))
+ {
+ char *s;
+ size_t len;
+ struct cleanup *cleanup;
+
+ if (type != NULL)
+ {
+ except_scm = gdbscm_make_misc_error (func_name, type_arg_pos,
+ type_scm,
+ _("No type allowed"));
+ value = NULL;
+ }
+ else
+ {
+ /* TODO: Provide option to specify conversion strategy. */
+ s = gdbscm_scm_to_string (obj, &len,
+ target_charset (gdbarch),
+ 0 /*non-strict*/,
+ &except_scm);
+ if (s != NULL)
+ {
+ cleanup = make_cleanup (xfree, s);
+ value
+ = value_cstring (s, len,
+ language_string_char_type (language,
+ gdbarch));
+ do_cleanups (cleanup);
+ }
+ else
+ value = NULL;
+ }
+ }
+ else if (lsscm_is_lazy_string (obj))
+ {
+ if (type != NULL)
+ {
+ except_scm = gdbscm_make_misc_error (func_name, type_arg_pos,
+ type_scm,
+ _("No type allowed"));
+ value = NULL;
+ }
+ else
+ {
+ value = lsscm_safe_lazy_string_to_value (obj, obj_arg_pos,
+ func_name,
+ &except_scm);
+ }
+ }
+ else /* OBJ isn't anything we support. */
+ {
+ except_scm = gdbscm_make_type_error (func_name, obj_arg_pos, obj,
+ NULL);
+ value = NULL;
+ }
+ }
+ if (except.reason < 0)
+ except_scm = gdbscm_scm_from_gdb_exception (except);
+
+ if (gdbscm_is_true (except_scm))
+ {
+ gdb_assert (value == NULL);
+ *except_scmp = except_scm;
+ }
+
+ return value;
+}
+
+/* Wrapper around vlscm_convert_typed_value_from_scheme for cases where there
+ is no supplied type. See vlscm_convert_typed_value_from_scheme for
+ details. */
+
+struct value *
+vlscm_convert_value_from_scheme (const char *func_name,
+ int obj_arg_pos, SCM obj,
+ SCM *except_scmp, struct gdbarch *gdbarch,
+ const struct language_defn *language)
+{
+ return vlscm_convert_typed_value_from_scheme (func_name, obj_arg_pos, obj,
+ -1, SCM_UNDEFINED, NULL,
+ except_scmp,
+ gdbarch, language);
+}
+
+/* Initialize value math support. */
+
+static const scheme_function math_functions[] =
+{
+ { "value-add", 2, 0, 0, gdbscm_value_add,
+ "\
+Return a + b." },
+
+ { "value-sub", 2, 0, 0, gdbscm_value_sub,
+ "\
+Return a - b." },
+
+ { "value-mul", 2, 0, 0, gdbscm_value_mul,
+ "\
+Return a * b." },
+
+ { "value-div", 2, 0, 0, gdbscm_value_div,
+ "\
+Return a / b." },
+
+ { "value-rem", 2, 0, 0, gdbscm_value_rem,
+ "\
+Return a % b." },
+
+ { "value-mod", 2, 0, 0, gdbscm_value_mod,
+ "\
+Return a mod b. See Knuth 1.2.4." },
+
+ { "value-pow", 2, 0, 0, gdbscm_value_pow,
+ "\
+Return pow (x, y)." },
+
+ { "value-not", 1, 0, 0, gdbscm_value_not,
+ "\
+Return !a." },
+
+ { "value-neg", 1, 0, 0, gdbscm_value_neg,
+ "\
+Return -a." },
+
+ { "value-pos", 1, 0, 0, gdbscm_value_pos,
+ "\
+Return a." },
+
+ { "value-abs", 1, 0, 0, gdbscm_value_abs,
+ "\
+Return abs (a)." },
+
+ { "value-lsh", 2, 0, 0, gdbscm_value_lsh,
+ "\
+Return a << b." },
+
+ { "value-rsh", 2, 0, 0, gdbscm_value_rsh,
+ "\
+Return a >> b." },
+
+ { "value-min", 2, 0, 0, gdbscm_value_min,
+ "\
+Return min (a, b)." },
+
+ { "value-max", 2, 0, 0, gdbscm_value_max,
+ "\
+Return max (a, b)." },
+
+ { "value-lognot", 1, 0, 0, gdbscm_value_lognot,
+ "\
+Return ~a." },
+
+ { "value-logand", 2, 0, 0, gdbscm_value_logand,
+ "\
+Return a & b." },
+
+ { "value-logior", 2, 0, 0, gdbscm_value_logior,
+ "\
+Return a | b." },
+
+ { "value-logxor", 2, 0, 0, gdbscm_value_logxor,
+ "\
+Return a ^ b." },
+
+ { "value=?", 2, 0, 0, gdbscm_value_eq_p,
+ "\
+Return a == b." },
+
+ { "value<?", 2, 0, 0, gdbscm_value_lt_p,
+ "\
+Return a < b." },
+
+ { "value<=?", 2, 0, 0, gdbscm_value_le_p,
+ "\
+Return a <= b." },
+
+ { "value>?", 2, 0, 0, gdbscm_value_gt_p,
+ "\
+Return a > b." },
+
+ { "value>=?", 2, 0, 0, gdbscm_value_ge_p,
+ "\
+Return a >= b." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_math (void)
+{
+ gdbscm_define_functions (math_functions, 1);
+}
diff --git a/gdb/guile/scm-objfile.c b/gdb/guile/scm-objfile.c
new file mode 100644
index 0000000..9a20dc7
--- /dev/null
+++ b/gdb/guile/scm-objfile.c
@@ -0,0 +1,413 @@
+/* Scheme interface to objfiles.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "objfiles.h"
+#include "language.h"
+#include "guile-internal.h"
+
+/* The <gdb:objfile> smob.
+ The typedef for this struct is in guile-internal.h. */
+
+struct _objfile_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The corresponding objfile. */
+ struct objfile *objfile;
+
+ /* The pretty-printer list of functions. */
+ SCM pretty_printers;
+
+ /* The <gdb:objfile> object we are contained in, needed to protect/unprotect
+ the object since a reference to it comes from non-gc-managed space
+ (the objfile). */
+ SCM containing_scm;
+};
+
+static const char objfile_smob_name[] = "gdb:objfile";
+
+/* The tag Guile knows the objfile smob by. */
+static scm_t_bits objfile_smob_tag;
+
+static const struct objfile_data *ofscm_objfile_data_key;
+
+/* Return the list of pretty-printers registered with O_SMOB. */
+
+SCM
+ofscm_objfile_smob_pretty_printers (objfile_smob *o_smob)
+{
+ return o_smob->pretty_printers;
+}
+
+/* Administrivia for objfile smobs. */
+
+/* The smob "mark" function for <gdb:objfile>. */
+
+static SCM
+ofscm_mark_objfile_smob (SCM self)
+{
+ objfile_smob *o_smob = (objfile_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (o_smob->pretty_printers);
+
+ /* We don't mark containing_scm here. It is just a backlink to our
+ container, and is gc'protected until the objfile is deleted. */
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&o_smob->base);
+}
+
+/* The smob "print" function for <gdb:objfile>. */
+
+static int
+ofscm_print_objfile_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ objfile_smob *o_smob = (objfile_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", objfile_smob_name);
+ gdbscm_printf (port, "%s",
+ o_smob->objfile != NULL
+ ? objfile_name (o_smob->objfile)
+ : "{invalid}");
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:objfile> object.
+ It's empty in the sense that an OBJFILE still needs to be associated
+ with it. */
+
+static SCM
+ofscm_make_objfile_smob (void)
+{
+ objfile_smob *o_smob = (objfile_smob *)
+ scm_gc_malloc (sizeof (objfile_smob), objfile_smob_name);
+ SCM o_scm;
+
+ o_smob->objfile = NULL;
+ o_smob->pretty_printers = SCM_EOL;
+ o_scm = scm_new_smob (objfile_smob_tag, (scm_t_bits) o_smob);
+ o_smob->containing_scm = o_scm;
+ gdbscm_init_gsmob (&o_smob->base);
+
+ return o_scm;
+}
+
+/* Clear the OBJFILE pointer in O_SMOB and unprotect the object from GC. */
+
+static void
+ofscm_release_objfile (objfile_smob *o_smob)
+{
+ o_smob->objfile = NULL;
+ scm_gc_unprotect_object (o_smob->containing_scm);
+}
+
+/* Objfile registry cleanup handler for when an objfile is deleted. */
+
+static void
+ofscm_handle_objfile_deleted (struct objfile *objfile, void *datum)
+{
+ objfile_smob *o_smob = datum;
+
+ gdb_assert (o_smob->objfile == objfile);
+
+ ofscm_release_objfile (o_smob);
+}
+
+/* Return non-zero if SCM is a <gdb:objfile> object. */
+
+static int
+ofscm_is_objfile (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (objfile_smob_tag, scm);
+}
+
+/* (objfile? object) -> boolean */
+
+static SCM
+gdbscm_objfile_p (SCM scm)
+{
+ return scm_from_bool (ofscm_is_objfile (scm));
+}
+
+/* Return a pointer to the objfile_smob that encapsulates OBJFILE,
+ creating one if necessary.
+ The result is cached so that we have only one copy per objfile. */
+
+objfile_smob *
+ofscm_objfile_smob_from_objfile (struct objfile *objfile)
+{
+ objfile_smob *o_smob;
+
+ o_smob = objfile_data (objfile, ofscm_objfile_data_key);
+ if (o_smob == NULL)
+ {
+ SCM o_scm = ofscm_make_objfile_smob ();
+
+ o_smob = (objfile_smob *) SCM_SMOB_DATA (o_scm);
+ o_smob->objfile = objfile;
+
+ set_objfile_data (objfile, ofscm_objfile_data_key, o_smob);
+ scm_gc_protect_object (o_smob->containing_scm);
+ }
+
+ return o_smob;
+}
+
+/* Return the <gdb:objfile> object that encapsulates OBJFILE. */
+
+SCM
+ofscm_scm_from_objfile (struct objfile *objfile)
+{
+ objfile_smob *o_smob = ofscm_objfile_smob_from_objfile (objfile);
+
+ return o_smob->containing_scm;
+}
+
+/* Returns the <gdb:objfile> object in SELF.
+ Throws an exception if SELF is not a <gdb:objfile> object. */
+
+static SCM
+ofscm_get_objfile_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (ofscm_is_objfile (self), self, arg_pos, func_name,
+ objfile_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the objfile smob of SELF.
+ Throws an exception if SELF is not a <gdb:objfile> object. */
+
+static objfile_smob *
+ofscm_get_objfile_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ SCM o_scm = ofscm_get_objfile_arg_unsafe (self, arg_pos, func_name);
+ objfile_smob *o_smob = (objfile_smob *) SCM_SMOB_DATA (o_scm);
+
+ return o_smob;
+}
+
+/* Return non-zero if objfile O_SMOB is valid. */
+
+static int
+ofscm_is_valid (objfile_smob *o_smob)
+{
+ return o_smob->objfile != NULL;
+}
+
+/* Return the objfile smob in SELF, verifying it's valid.
+ Throws an exception if SELF is not a <gdb:objfile> object or is invalid. */
+
+static objfile_smob *
+ofscm_get_valid_objfile_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ objfile_smob *o_smob
+ = ofscm_get_objfile_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!ofscm_is_valid (o_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:objfile>"));
+ }
+
+ return o_smob;
+}
+
+/* Objfile methods. */
+
+/* (objfile-valid? <gdb:objfile>) -> boolean
+ Returns #t if this object file still exists in GDB. */
+
+static SCM
+gdbscm_objfile_valid_p (SCM self)
+{
+ objfile_smob *o_smob
+ = ofscm_get_objfile_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (o_smob->objfile != NULL);
+}
+
+/* (objfile-filename <gdb:objfile>) -> string
+ Returns the objfile's file name.
+ Throw's an exception if the underlying objfile is invalid. */
+
+static SCM
+gdbscm_objfile_filename (SCM self)
+{
+ objfile_smob *o_smob
+ = ofscm_get_valid_objfile_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return gdbscm_scm_from_c_string (objfile_name (o_smob->objfile));
+}
+
+/* (objfile-pretty-printers <gdb:objfile>) -> list
+ Returns the list of pretty-printers for this objfile. */
+
+static SCM
+gdbscm_objfile_pretty_printers (SCM self)
+{
+ objfile_smob *o_smob
+ = ofscm_get_objfile_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return o_smob->pretty_printers;
+}
+
+/* (set-objfile-pretty-printers! <gdb:objfile> list) -> unspecified
+ Set the pretty-printers for this objfile. */
+
+static SCM
+gdbscm_set_objfile_pretty_printers_x (SCM self, SCM printers)
+{
+ objfile_smob *o_smob
+ = ofscm_get_objfile_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ SCM_ASSERT_TYPE (gdbscm_is_true (scm_list_p (printers)), printers,
+ SCM_ARG2, FUNC_NAME, _("list"));
+
+ o_smob->pretty_printers = printers;
+
+ return SCM_UNSPECIFIED;
+}
+
+/* The "current" objfile. This is set when gdb detects that a new
+ objfile has been loaded. It is only set for the duration of a call to
+ gdbscm_source_objfile_script; it is NULL at other times. */
+static struct objfile *ofscm_current_objfile;
+
+/* Set the current objfile to OBJFILE and then read FILE named FILENAME
+ as Guile code. This does not throw any errors. If an exception
+ occurs Guile will print the backtrace.
+ This is the extension_language_script_ops.objfile_script_sourcer
+ "method". */
+
+void
+gdbscm_source_objfile_script (const struct extension_language_defn *extlang,
+ struct objfile *objfile, FILE *file,
+ const char *filename)
+{
+ char *msg;
+
+ ofscm_current_objfile = objfile;
+
+ msg = gdbscm_safe_source_script (filename);
+ if (msg != NULL)
+ {
+ fprintf_filtered (gdb_stderr, "%s", msg);
+ xfree (msg);
+ }
+
+ ofscm_current_objfile = NULL;
+}
+
+/* (current-objfile) -> <gdb:obfjile>
+ Return the current objfile, or #f if there isn't one.
+ Ideally this would be named ofscm_current_objfile, but that name is
+ taken by the variable recording the current objfile. */
+
+static SCM
+gdbscm_get_current_objfile (void)
+{
+ if (ofscm_current_objfile == NULL)
+ return SCM_BOOL_F;
+
+ return ofscm_scm_from_objfile (ofscm_current_objfile);
+}
+
+/* (objfiles) -> list
+ Return a list of all objfiles in the current program space. */
+
+static SCM
+gdbscm_objfiles (void)
+{
+ struct objfile *objf;
+ SCM result;
+
+ result = SCM_EOL;
+
+ ALL_OBJFILES (objf)
+ {
+ SCM item = ofscm_scm_from_objfile (objf);
+
+ result = scm_cons (item, result);
+ }
+
+ return scm_reverse_x (result, SCM_EOL);
+}
+
+/* Initialize the Scheme objfile support. */
+
+static const scheme_function objfile_functions[] =
+{
+ { "objfile?", 1, 0, 0, gdbscm_objfile_p,
+ "\
+Return #t if the object is a <gdb:objfile> object." },
+
+ { "objfile-valid?", 1, 0, 0, gdbscm_objfile_valid_p,
+ "\
+Return #t if the objfile is valid (hasn't been deleted from gdb)." },
+
+ { "objfile-filename", 1, 0, 0, gdbscm_objfile_filename,
+ "\
+Return the file name of the objfile." },
+
+ { "objfile-pretty-printers", 1, 0, 0, gdbscm_objfile_pretty_printers,
+ "\
+Return a list of pretty-printers of the objfile." },
+
+ { "set-objfile-pretty-printers!", 2, 0, 0,
+ gdbscm_set_objfile_pretty_printers_x,
+ "\
+Set the list of pretty-printers of the objfile." },
+
+ { "current-objfile", 0, 0, 0, gdbscm_get_current_objfile,
+ "\
+Return the current objfile if there is one or #f if there isn't one." },
+
+ { "objfiles", 0, 0, 0, gdbscm_objfiles,
+ "\
+Return a list of all objfiles in the current program space." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_objfiles (void)
+{
+ objfile_smob_tag
+ = gdbscm_make_smob_type (objfile_smob_name, sizeof (objfile_smob));
+ scm_set_smob_mark (objfile_smob_tag, ofscm_mark_objfile_smob);
+ scm_set_smob_print (objfile_smob_tag, ofscm_print_objfile_smob);
+
+ gdbscm_define_functions (objfile_functions, 1);
+
+ ofscm_objfile_data_key
+ = register_objfile_data_with_cleanup (NULL, ofscm_handle_objfile_deleted);
+}
diff --git a/gdb/guile/scm-ports.c b/gdb/guile/scm-ports.c
new file mode 100644
index 0000000..30bbc97
--- /dev/null
+++ b/gdb/guile/scm-ports.c
@@ -0,0 +1,1372 @@
+/* Support for connecting Guile's stdio to GDB's.
+ as well as r/w memory via ports.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "gdb_select.h"
+#include "interps.h"
+#include "target.h"
+#include "guile-internal.h"
+
+#ifdef HAVE_POLL
+#if defined (HAVE_POLL_H)
+#include <poll.h>
+#elif defined (HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+#endif
+
+/* A ui-file for sending output to Guile. */
+
+typedef struct
+{
+ int *magic;
+ SCM port;
+} ioscm_file_port;
+
+/* Data for a memory port. */
+
+typedef struct
+{
+ /* Bounds of memory range this port is allowed to access, inclusive.
+ To simplify overflow handling, an END of 0xff..ff is not allowed.
+ This also means a start address of 0xff..ff is also not allowed.
+ I can live with that. */
+ CORE_ADDR start, end;
+
+ /* (end - start + 1), recorded for convenience. */
+ ULONGEST size;
+
+ /* Think of this as the lseek value maintained by the kernel.
+ This value is always in the range [0, size]. */
+ ULONGEST current;
+
+ /* The size of the internal r/w buffers.
+ Scheme ports aren't a straightforward mapping to memory r/w.
+ Generally the user specifies how much to r/w and all access is
+ unbuffered. We don't try to provide equivalent access, but we allow
+ the user to specify these values to help get something similar. */
+ unsigned read_buf_size, write_buf_size;
+} ioscm_memory_port;
+
+/* Copies of the original system input/output/error ports.
+ These are recorded for debugging purposes. */
+static SCM orig_input_port_scm;
+static SCM orig_output_port_scm;
+static SCM orig_error_port_scm;
+
+/* This is the stdio port descriptor, scm_ptob_descriptor. */
+static scm_t_bits stdio_port_desc;
+
+/* Note: scm_make_port_type takes a char * instead of a const char *. */
+static /*const*/ char stdio_port_desc_name[] = "gdb:stdio-port";
+
+/* Names of each gdb port. */
+static const char input_port_name[] = "gdb:stdin";
+static const char output_port_name[] = "gdb:stdout";
+static const char error_port_name[] = "gdb:stderr";
+
+/* This is the actual port used from Guile.
+ We don't expose these to the user though, to ensure they're not
+ overwritten. */
+static SCM input_port_scm;
+static SCM output_port_scm;
+static SCM error_port_scm;
+
+/* Magic number to identify port ui-files.
+ Actually, the address of this variable is the magic number. */
+static int file_port_magic;
+
+/* Internal enum for specifying output port. */
+enum oport { GDB_STDOUT, GDB_STDERR };
+
+/* This is the memory port descriptor, scm_ptob_descriptor. */
+static scm_t_bits memory_port_desc;
+
+/* Note: scm_make_port_type takes a char * instead of a const char *. */
+static /*const*/ char memory_port_desc_name[] = "gdb:memory-port";
+
+/* The default amount of memory to fetch for each read/write request.
+ Scheme ports don't provide a way to specify the size of a read,
+ which is important to us to minimize the number of inferior interactions,
+ which over a remote link can be important. To compensate we augment the
+ port API with a new function that let's the user specify how much the next
+ read request should fetch. This is the initial value for each new port. */
+static const unsigned default_read_buf_size = 16;
+static const unsigned default_write_buf_size = 16;
+
+/* Arbitrarily limit memory port buffers to 1 byte to 4K. */
+static const unsigned min_memory_port_buf_size = 1;
+static const unsigned max_memory_port_buf_size = 4096;
+
+/* "out of range" error message for buf sizes. */
+static char *out_of_range_buf_size;
+
+/* Keywords used by open-memory. */
+static SCM mode_keyword;
+static SCM start_keyword;
+static SCM size_keyword;
+
+/* Helper to do the low level work of opening a port.
+ Newer versions of Guile (2.1.x) have scm_c_make_port. */
+
+static SCM
+ioscm_open_port (scm_t_bits port_type, long mode_bits)
+{
+ SCM port;
+
+#if 0 /* TODO: Guile doesn't export this. What to do? */
+ scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex);
+#endif
+
+ port = scm_new_port_table_entry (port_type);
+
+ SCM_SET_CELL_TYPE (port, port_type | mode_bits);
+
+#if 0 /* TODO: Guile doesn't export this. What to do? */
+ scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
+#endif
+
+ return port;
+}
+
+/* Support for connecting Guile's stdio ports to GDB's stdio ports. */
+
+/* The scm_t_ptob_descriptor.input_waiting "method".
+ Return a lower bound on the number of bytes available for input. */
+
+static int
+ioscm_input_waiting (SCM port)
+{
+ int fdes = 0;
+
+ if (! scm_is_eq (port, input_port_scm))
+ return 0;
+
+#ifdef HAVE_POLL
+ {
+ /* This is copied from libguile/fports.c. */
+ struct pollfd pollfd = { fdes, POLLIN, 0 };
+ static int use_poll = -1;
+
+ if (use_poll < 0)
+ {
+ /* This is copied from event-loop.c: poll cannot be used for stdin on
+ m68k-motorola-sysv. */
+ struct pollfd test_pollfd = { fdes, POLLIN, 0 };
+
+ if (poll (&test_pollfd, 1, 0) == 1 && (test_pollfd.revents & POLLNVAL))
+ use_poll = 0;
+ else
+ use_poll = 1;
+ }
+
+ if (use_poll)
+ {
+ /* Guile doesn't export SIGINT hooks like Python does.
+ For now pass EINTR to scm_syserror, that's what fports.c does. */
+ if (poll (&pollfd, 1, 0) < 0)
+ scm_syserror (FUNC_NAME);
+
+ return pollfd.revents & POLLIN ? 1 : 0;
+ }
+ }
+ /* Fall through. */
+#endif
+
+ {
+ struct timeval timeout;
+ fd_set input_fds;
+ int num_fds = fdes + 1;
+ int num_found;
+
+ memset (&timeout, 0, sizeof (timeout));
+ FD_ZERO (&input_fds);
+ FD_SET (fdes, &input_fds);
+
+ num_found = gdb_select (num_fds, &input_fds, NULL, NULL, &timeout);
+ if (num_found < 0)
+ {
+ /* Guile doesn't export SIGINT hooks like Python does.
+ For now pass EINTR to scm_syserror, that's what fports.c does. */
+ scm_syserror (FUNC_NAME);
+ }
+ return num_found > 0 && FD_ISSET (fdes, &input_fds);
+ }
+}
+
+/* The scm_t_ptob_descriptor.fill_input "method". */
+
+static int
+ioscm_fill_input (SCM port)
+{
+ /* Borrowed from libguile/fports.c. */
+ long count;
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+
+ /* If we're called on stdout,stderr, punt. */
+ if (! scm_is_eq (port, input_port_scm))
+ return (scm_t_wchar) EOF; /* Set errno and return -1? */
+
+ gdb_flush (gdb_stdout);
+ gdb_flush (gdb_stderr);
+
+ count = ui_file_read (gdb_stdin, (char *) pt->read_buf, pt->read_buf_size);
+ if (count == -1)
+ scm_syserror (FUNC_NAME);
+ if (count == 0)
+ return (scm_t_wchar) EOF;
+
+ pt->read_pos = pt->read_buf;
+ pt->read_end = pt->read_buf + count;
+ return *pt->read_buf;
+}
+
+/* Like fputstrn_filtered, but don't escape characters, except nul.
+ Also like fputs_filtered, but a length is specified. */
+
+static void
+fputsn_filtered (const char *s, size_t size, struct ui_file *stream)
+{
+ size_t i;
+
+ for (i = 0; i < size; ++i)
+ {
+ if (s[i] == '\0')
+ fputs_filtered ("\\000", stream);
+ else
+ fputc_filtered (s[i], stream);
+ }
+}
+
+/* Write to gdb's stdout or stderr. */
+
+static void
+ioscm_write (SCM port, const void *data, size_t size)
+{
+ volatile struct gdb_exception except;
+
+ /* If we're called on stdin, punt. */
+ if (scm_is_eq (port, input_port_scm))
+ return;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (scm_is_eq (port, error_port_scm))
+ fputsn_filtered (data, size, gdb_stderr);
+ else
+ fputsn_filtered (data, size, gdb_stdout);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+}
+
+/* Flush gdb's stdout or stderr. */
+
+static void
+ioscm_flush (SCM port)
+{
+ /* If we're called on stdin, punt. */
+ if (scm_is_eq (port, input_port_scm))
+ return;
+
+ if (scm_is_eq (port, error_port_scm))
+ gdb_flush (gdb_stderr);
+ else
+ gdb_flush (gdb_stdout);
+}
+
+/* Initialize the gdb stdio port type.
+
+ N.B. isatty? will fail on these ports, it is only supported for file
+ ports. IWBN if we could "subclass" file ports. */
+
+static void
+ioscm_init_gdb_stdio_port (void)
+{
+ stdio_port_desc = scm_make_port_type (stdio_port_desc_name,
+ ioscm_fill_input, ioscm_write);
+
+ scm_set_port_input_waiting (stdio_port_desc, ioscm_input_waiting);
+ scm_set_port_flush (stdio_port_desc, ioscm_flush);
+}
+
+/* Subroutine of ioscm_make_gdb_stdio_port to simplify it.
+ Set up the buffers of port PORT.
+ MODE_BITS are the mode bits of PORT. */
+
+static void
+ioscm_init_stdio_buffers (SCM port, long mode_bits)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+#define GDB_STDIO_BUFFER_DEFAULT_SIZE 1024
+ int size = mode_bits & SCM_BUF0 ? 0 : GDB_STDIO_BUFFER_DEFAULT_SIZE;
+ int writing = (mode_bits & SCM_WRTNG) != 0;
+
+ /* This is heavily copied from scm_fport_buffer_add. */
+
+ if (!writing && size > 0)
+ {
+ pt->read_buf = scm_gc_malloc_pointerless (size, "port buffer");
+ pt->read_pos = pt->read_end = pt->read_buf;
+ pt->read_buf_size = size;
+ }
+ else
+ {
+ pt->read_pos = pt->read_buf = pt->read_end = &pt->shortbuf;
+ pt->read_buf_size = 1;
+ }
+
+ if (writing && size > 0)
+ {
+ pt->write_buf = scm_gc_malloc_pointerless (size, "port buffer");
+ pt->write_pos = pt->write_buf;
+ pt->write_buf_size = size;
+ }
+ else
+ {
+ pt->write_buf = pt->write_pos = &pt->shortbuf;
+ pt->write_buf_size = 1;
+ }
+ pt->write_end = pt->write_buf + pt->write_buf_size;
+}
+
+/* Create a gdb stdio port. */
+
+static SCM
+ioscm_make_gdb_stdio_port (int fd)
+{
+ int is_a_tty = isatty (fd);
+ const char *name;
+ long mode_bits;
+ SCM port;
+
+ switch (fd)
+ {
+ case 0:
+ name = input_port_name;
+ mode_bits = scm_mode_bits (is_a_tty ? "r0" : "r");
+ break;
+ case 1:
+ name = output_port_name;
+ mode_bits = scm_mode_bits (is_a_tty ? "w0" : "w");
+ break;
+ case 2:
+ name = error_port_name;
+ mode_bits = scm_mode_bits (is_a_tty ? "w0" : "w");
+ break;
+ default:
+ gdb_assert_not_reached ("bad stdio file descriptor");
+ }
+
+ port = ioscm_open_port (stdio_port_desc, mode_bits);
+
+ scm_set_port_filename_x (port, gdbscm_scm_from_c_string (name));
+
+ ioscm_init_stdio_buffers (port, mode_bits);
+
+ return port;
+}
+
+/* (stdio-port? object) -> boolean */
+
+static SCM
+gdbscm_stdio_port_p (SCM scm)
+{
+ /* This is copied from SCM_FPORTP. */
+ return scm_from_bool (!SCM_IMP (scm)
+ && (SCM_TYP16 (scm) == stdio_port_desc));
+}
+
+/* GDB's ports are accessed via functions to keep them read-only. */
+
+/* (input-port) -> port */
+
+static SCM
+gdbscm_input_port (void)
+{
+ return input_port_scm;
+}
+
+/* (output-port) -> port */
+
+static SCM
+gdbscm_output_port (void)
+{
+ return output_port_scm;
+}
+
+/* (error-port) -> port */
+
+static SCM
+gdbscm_error_port (void)
+{
+ return error_port_scm;
+}
+
+/* Support for sending GDB I/O to Guile ports. */
+
+static void
+ioscm_file_port_delete (struct ui_file *file)
+{
+ ioscm_file_port *stream = ui_file_data (file);
+
+ if (stream->magic != &file_port_magic)
+ internal_error (__FILE__, __LINE__,
+ _("ioscm_file_port_delete: bad magic number"));
+ xfree (stream);
+}
+
+static void
+ioscm_file_port_rewind (struct ui_file *file)
+{
+ ioscm_file_port *stream = ui_file_data (file);
+
+ if (stream->magic != &file_port_magic)
+ internal_error (__FILE__, __LINE__,
+ _("ioscm_file_port_rewind: bad magic number"));
+
+ scm_truncate_file (stream->port, 0);
+}
+
+static void
+ioscm_file_port_put (struct ui_file *file,
+ ui_file_put_method_ftype *write,
+ void *dest)
+{
+ ioscm_file_port *stream = ui_file_data (file);
+
+ if (stream->magic != &file_port_magic)
+ internal_error (__FILE__, __LINE__,
+ _("ioscm_file_port_put: bad magic number"));
+
+ /* This function doesn't meld with ports very well. */
+}
+
+static void
+ioscm_file_port_write (struct ui_file *file,
+ const char *buffer,
+ long length_buffer)
+{
+ ioscm_file_port *stream = ui_file_data (file);
+
+ if (stream->magic != &file_port_magic)
+ internal_error (__FILE__, __LINE__,
+ _("ioscm_pot_file_write: bad magic number"));
+
+ scm_c_write (stream->port, buffer, length_buffer);
+}
+
+/* Return a ui_file that writes to PORT. */
+
+static struct ui_file *
+ioscm_file_port_new (SCM port)
+{
+ ioscm_file_port *stream = XCNEW (ioscm_file_port);
+ struct ui_file *file = ui_file_new ();
+
+ set_ui_file_data (file, stream, ioscm_file_port_delete);
+ set_ui_file_rewind (file, ioscm_file_port_rewind);
+ set_ui_file_put (file, ioscm_file_port_put);
+ set_ui_file_write (file, ioscm_file_port_write);
+ stream->magic = &file_port_magic;
+ stream->port = port;
+
+ return file;
+}
+
+/* Helper routine for with-{output,error}-to-port. */
+
+static SCM
+ioscm_with_output_to_port_worker (SCM port, SCM thunk, enum oport oport,
+ const char *func_name)
+{
+ struct ui_file *port_file;
+ struct cleanup *cleanups;
+ SCM result;
+
+ SCM_ASSERT_TYPE (gdbscm_is_true (scm_output_port_p (port)), port,
+ SCM_ARG1, func_name, _("output port"));
+ SCM_ASSERT_TYPE (gdbscm_is_true (scm_thunk_p (thunk)), thunk,
+ SCM_ARG2, func_name, _("thunk"));
+
+ cleanups = set_batch_flag_and_make_cleanup_restore_page_info ();
+
+ make_cleanup_restore_integer (&interpreter_async);
+ interpreter_async = 0;
+
+ port_file = ioscm_file_port_new (port);
+
+ make_cleanup_ui_file_delete (port_file);
+
+ if (oport == GDB_STDERR)
+ {
+ make_cleanup_restore_ui_file (&gdb_stderr);
+ gdb_stderr = port_file;
+ }
+ else
+ {
+ make_cleanup_restore_ui_file (&gdb_stdout);
+
+ if (ui_out_redirect (current_uiout, port_file) < 0)
+ warning (_("Current output protocol does not support redirection"));
+ else
+ make_cleanup_ui_out_redirect_pop (current_uiout);
+
+ gdb_stdout = port_file;
+ }
+
+ result = gdbscm_safe_call_0 (thunk, NULL);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (%with-gdb-output-to-port port thunk) -> object
+ This function is experimental.
+ IWBN to not include "gdb" in the name, but it would collide with a standard
+ procedure, and it's common to import the gdb module without a prefix.
+ There are ways around this, but they're more cumbersome.
+
+ This has % in the name because it's experimental, and we want the
+ user-visible version to come from module (gdb experimental). */
+
+static SCM
+gdbscm_percent_with_gdb_output_to_port (SCM port, SCM thunk)
+{
+ return ioscm_with_output_to_port_worker (port, thunk, GDB_STDOUT, FUNC_NAME);
+}
+
+/* (%with-gdb-error-to-port port thunk) -> object
+ This function is experimental.
+ IWBN to not include "gdb" in the name, but it would collide with a standard
+ procedure, and it's common to import the gdb module without a prefix.
+ There are ways around this, but they're more cumbersome.
+
+ This has % in the name because it's experimental, and we want the
+ user-visible version to come from module (gdb experimental). */
+
+static SCM
+gdbscm_percent_with_gdb_error_to_port (SCM port, SCM thunk)
+{
+ return ioscm_with_output_to_port_worker (port, thunk, GDB_STDERR, FUNC_NAME);
+}
+
+/* Support for r/w memory via ports. */
+
+/* Perform an "lseek" to OFFSET,WHENCE on memory port IOMEM.
+ OFFSET must be in the range [0,size].
+ The result is non-zero for success, zero for failure. */
+
+static int
+ioscm_lseek_address (ioscm_memory_port *iomem, LONGEST offset, int whence)
+{
+ CORE_ADDR new_current;
+
+ gdb_assert (iomem->current <= iomem->size);
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ /* Catch over/underflow. */
+ if ((offset < 0 && iomem->current + offset > iomem->current)
+ || (offset >= 0 && iomem->current + offset < iomem->current))
+ return 0;
+ new_current = iomem->current + offset;
+ break;
+ case SEEK_SET:
+ new_current = offset;
+ break;
+ case SEEK_END:
+ if (offset == 0)
+ {
+ new_current = iomem->size;
+ break;
+ }
+ /* TODO: Not supported yet. */
+ return 0;
+ default:
+ return 0;
+ }
+
+ if (new_current > iomem->size)
+ return 0;
+ iomem->current = new_current;
+ return 1;
+}
+
+/* "fill_input" method for memory ports. */
+
+static int
+gdbscm_memory_port_fill_input (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ size_t to_read;
+
+ /* "current" is the offset of the first byte we want to read. */
+ if (iomem->current >= iomem->size)
+ return EOF;
+
+ /* Don't read outside the allowed memory range. */
+ to_read = pt->read_buf_size;
+ if (to_read > iomem->size - iomem->current)
+ to_read = iomem->size - iomem->current;
+
+ if (target_read_memory (iomem->start + iomem->current, pt->read_buf,
+ to_read) != 0)
+ gdbscm_memory_error (FUNC_NAME, _("error reading memory"), SCM_EOL);
+
+ pt->read_pos = pt->read_buf;
+ pt->read_end = pt->read_buf + to_read;
+ iomem->current += to_read;
+ return *pt->read_buf;
+}
+
+/* "end_input" method for memory ports.
+ Clear the read buffer and adjust the file position for unread bytes. */
+
+static void
+gdbscm_memory_port_end_input (SCM port, int offset)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ size_t remaining = pt->read_end - pt->read_pos;
+
+ /* Note: Use of "int offset" is specified by Guile ports API. */
+ if ((offset < 0 && remaining + offset > remaining)
+ || (offset > 0 && remaining + offset < remaining))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0, scm_from_int (offset),
+ _("overflow in offset calculation"));
+ }
+ offset += remaining;
+
+ if (offset > 0)
+ {
+ pt->read_pos = pt->read_end;
+ /* Throw error if unread-char used at beginning of file
+ then attempting to write. Seems correct. */
+ if (!ioscm_lseek_address (iomem, -offset, SEEK_CUR))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0, scm_from_int (offset),
+ _("bad offset"));
+ }
+ }
+
+ pt->rw_active = SCM_PORT_NEITHER;
+}
+
+/* "flush" method for memory ports. */
+
+static void
+gdbscm_memory_port_flush (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ size_t to_write = pt->write_pos - pt->write_buf;
+
+ if (to_write == 0)
+ return;
+
+ /* There's no way to indicate a short write, so if the request goes past
+ the end of the port's memory range, flag an error. */
+ if (to_write > iomem->size - iomem->current)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0,
+ gdbscm_scm_from_ulongest (to_write),
+ _("writing beyond end of memory range"));
+ }
+
+ if (target_write_memory (iomem->start + iomem->current, pt->write_buf,
+ to_write) != 0)
+ gdbscm_memory_error (FUNC_NAME, _("error writing memory"), SCM_EOL);
+
+ iomem->current += to_write;
+ pt->write_pos = pt->write_buf;
+ pt->rw_active = SCM_PORT_NEITHER;
+}
+
+/* "write" method for memory ports. */
+
+static void
+gdbscm_memory_port_write (SCM port, const void *data, size_t size)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ const char *input = (char *) data;
+
+ /* We could get fancy here, and try to buffer the request since we're
+ buffering anyway. But there's currently no need. */
+
+ /* First flush what's currently buffered. */
+ gdbscm_memory_port_flush (port);
+
+ /* There's no way to indicate a short write, so if the request goes past
+ the end of the port's memory range, flag an error. */
+ if (size > iomem->size - iomem->current)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0, gdbscm_scm_from_ulongest (size),
+ _("writing beyond end of memory range"));
+ }
+
+ if (target_write_memory (iomem->start + iomem->current, data, size) != 0)
+ gdbscm_memory_error (FUNC_NAME, _("error writing memory"), SCM_EOL);
+
+ iomem->current += size;
+}
+
+/* "seek" method for memory ports. */
+
+static scm_t_off
+gdbscm_memory_port_seek (SCM port, scm_t_off offset, int whence)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ CORE_ADDR result;
+ int rc;
+
+ if (pt->rw_active == SCM_PORT_WRITE)
+ {
+ if (offset != 0 || whence != SEEK_CUR)
+ {
+ gdbscm_memory_port_flush (port);
+ rc = ioscm_lseek_address (iomem, offset, whence);
+ result = iomem->current;
+ }
+ else
+ {
+ /* Read current position without disturbing the buffer,
+ but flag an error if what's in the buffer goes outside the
+ allowed range. */
+ CORE_ADDR current = iomem->current;
+ size_t delta = pt->write_pos - pt->write_buf;
+
+ if (current + delta < current
+ || current + delta > iomem->size + 1)
+ rc = 0;
+ else
+ {
+ result = current + delta;
+ rc = 1;
+ }
+ }
+ }
+ else if (pt->rw_active == SCM_PORT_READ)
+ {
+ if (offset != 0 || whence != SEEK_CUR)
+ {
+ scm_end_input (port);
+ rc = ioscm_lseek_address (iomem, offset, whence);
+ result = iomem->current;
+ }
+ else
+ {
+ /* Read current position without disturbing the buffer
+ (particularly the unread-char buffer). */
+ CORE_ADDR current = iomem->current;
+ size_t remaining = pt->read_end - pt->read_pos;
+
+ if (current - remaining > current
+ || current - remaining < iomem->start)
+ rc = 0;
+ else
+ {
+ result = current - remaining;
+ rc = 1;
+ }
+
+ if (rc != 0 && pt->read_buf == pt->putback_buf)
+ {
+ size_t saved_remaining = pt->saved_read_end - pt->saved_read_pos;
+
+ if (result - saved_remaining > result
+ || result - saved_remaining < iomem->start)
+ rc = 0;
+ else
+ result -= saved_remaining;
+ }
+ }
+ }
+ else /* SCM_PORT_NEITHER */
+ {
+ rc = ioscm_lseek_address (iomem, offset, whence);
+ result = iomem->current;
+ }
+
+ if (rc == 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0,
+ gdbscm_scm_from_longest (offset),
+ _("bad seek"));
+ }
+
+ /* TODO: The Guile API doesn't support 32x64. We can't fix that here,
+ and there's no need to throw an error if the new address can't be
+ represented in a scm_t_off. But we could return something less
+ clumsy. */
+ return result;
+}
+
+/* "close" method for memory ports. */
+
+static int
+gdbscm_memory_port_close (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+
+ gdbscm_memory_port_flush (port);
+
+ if (pt->read_buf == pt->putback_buf)
+ pt->read_buf = pt->saved_read_buf;
+ xfree (pt->read_buf);
+ xfree (pt->write_buf);
+ scm_gc_free (iomem, sizeof (*iomem), "memory port");
+
+ return 0;
+}
+
+/* "free" method for memory ports. */
+
+static size_t
+gdbscm_memory_port_free (SCM port)
+{
+ gdbscm_memory_port_close (port);
+
+ return 0;
+}
+
+/* "print" method for memory ports. */
+
+static int
+gdbscm_memory_port_print (SCM exp, SCM port, scm_print_state *pstate)
+{
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (exp);
+ char *type = SCM_PTOBNAME (SCM_PTOBNUM (exp));
+
+ scm_puts ("#<", port);
+ scm_print_port_mode (exp, port);
+ /* scm_print_port_mode includes a trailing space. */
+ gdbscm_printf (port, "%s %s-%s", type,
+ hex_string (iomem->start), hex_string (iomem->end));
+ scm_putc ('>', port);
+ return 1;
+}
+
+/* Create the port type used for memory. */
+
+static void
+ioscm_init_memory_port_type (void)
+{
+ memory_port_desc = scm_make_port_type (memory_port_desc_name,
+ gdbscm_memory_port_fill_input,
+ gdbscm_memory_port_write);
+
+ scm_set_port_end_input (memory_port_desc, gdbscm_memory_port_end_input);
+ scm_set_port_flush (memory_port_desc, gdbscm_memory_port_flush);
+ scm_set_port_seek (memory_port_desc, gdbscm_memory_port_seek);
+ scm_set_port_close (memory_port_desc, gdbscm_memory_port_close);
+ scm_set_port_free (memory_port_desc, gdbscm_memory_port_free);
+ scm_set_port_print (memory_port_desc, gdbscm_memory_port_print);
+}
+
+/* Helper for gdbscm_open_memory to parse the mode bits.
+ An exception is thrown if MODE is invalid. */
+
+static long
+ioscm_parse_mode_bits (const char *func_name, const char *mode)
+{
+ const char *p;
+ long mode_bits;
+
+ if (*mode != 'r' && *mode != 'w')
+ {
+ gdbscm_out_of_range_error (func_name, 0,
+ gdbscm_scm_from_c_string (mode),
+ _("bad mode string"));
+ }
+ for (p = mode + 1; *p != '\0'; ++p)
+ {
+ switch (*p)
+ {
+ case 'b':
+ case '+':
+ break;
+ default:
+ gdbscm_out_of_range_error (func_name, 0,
+ gdbscm_scm_from_c_string (mode),
+ _("bad mode string"));
+ }
+ }
+
+ /* Kinda awkward to convert the mode from SCM -> string only to have Guile
+ convert it back to SCM, but that's the API we have to work with. */
+ mode_bits = scm_mode_bits ((char *) mode);
+
+ return mode_bits;
+}
+
+/* Helper for gdbscm_open_memory to finish initializing the port.
+ The port has address range [start,end].
+ To simplify overflow handling, an END of 0xff..ff is not allowed.
+ This also means a start address of 0xff..f is also not allowed.
+ I can live with that. */
+
+static void
+ioscm_init_memory_port (SCM port, CORE_ADDR start, CORE_ADDR end)
+{
+ scm_t_port *pt;
+ ioscm_memory_port *iomem;
+
+ gdb_assert (start <= end);
+ gdb_assert (end < ~(CORE_ADDR) 0);
+
+ iomem = (ioscm_memory_port *) scm_gc_malloc_pointerless (sizeof (*iomem),
+ "memory port");
+
+ iomem->start = start;
+ iomem->end = end;
+ iomem->size = end - start + 1;
+ iomem->current = 0;
+ iomem->read_buf_size = default_read_buf_size;
+ iomem->write_buf_size = default_write_buf_size;
+
+ pt = SCM_PTAB_ENTRY (port);
+ /* Match the expectation of `binary-port?'. */
+ pt->encoding = NULL;
+ pt->rw_random = 1;
+ pt->read_buf_size = iomem->read_buf_size;
+ pt->read_buf = xmalloc (pt->read_buf_size);
+ pt->read_pos = pt->read_end = pt->read_buf;
+ pt->write_buf_size = iomem->write_buf_size;
+ pt->write_buf = xmalloc (pt->write_buf_size);
+ pt->write_pos = pt->write_buf;
+ pt->write_end = pt->write_buf + pt->write_buf_size;
+
+ SCM_SETSTREAM (port, iomem);
+}
+
+/* Re-initialize a memory port, updating its read/write buffer sizes.
+ An exception is thrown if data is still buffered, except in the case
+ where the buffer size isn't changing (since that's just a nop). */
+
+static void
+ioscm_reinit_memory_port (SCM port, size_t read_buf_size,
+ size_t write_buf_size, const char *func_name)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+ ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+
+ gdb_assert (read_buf_size >= min_memory_port_buf_size
+ && read_buf_size <= max_memory_port_buf_size);
+ gdb_assert (write_buf_size >= min_memory_port_buf_size
+ && write_buf_size <= max_memory_port_buf_size);
+
+ /* First check if anything is buffered. */
+
+ if (read_buf_size != pt->read_buf_size
+ && pt->read_end != pt->read_buf)
+ {
+ scm_misc_error (func_name, _("read buffer not empty: ~a"),
+ scm_list_1 (port));
+ }
+
+ if (write_buf_size != pt->write_buf_size
+ && pt->write_pos != pt->write_buf)
+ {
+ scm_misc_error (func_name, _("write buffer not empty: ~a"),
+ scm_list_1 (port));
+ }
+
+ /* Now we can update the buffer sizes, but only if the size has changed. */
+
+ if (read_buf_size != pt->read_buf_size)
+ {
+ iomem->read_buf_size = read_buf_size;
+ pt->read_buf_size = read_buf_size;
+ xfree (pt->read_buf);
+ pt->read_buf = xmalloc (pt->read_buf_size);
+ pt->read_pos = pt->read_end = pt->read_buf;
+ }
+
+ if (write_buf_size != pt->write_buf_size)
+ {
+ iomem->write_buf_size = write_buf_size;
+ pt->write_buf_size = write_buf_size;
+ xfree (pt->write_buf);
+ pt->write_buf = xmalloc (pt->write_buf_size);
+ pt->write_pos = pt->write_buf;
+ pt->write_end = pt->write_buf + pt->write_buf_size;
+ }
+}
+
+/* (open-memory [#:mode string] [#:start address] [#:size integer]) -> port
+ Return a port that can be used for reading and writing memory.
+ MODE is a string, and must be one of "r", "w", or "r+".
+ For compatibility "b" (binary) may also be present, but we ignore it:
+ memory ports are binary only.
+
+ TODO: Support "0" (unbuffered)? Only support "0" (always unbuffered)?
+
+ The chunk of memory that can be accessed can be bounded.
+ If both START,SIZE are unspecified, all of memory can be accessed.
+ If only START is specified, all of memory from that point on can be
+ accessed. If only SIZE if specified, all memory in [0,SIZE) can be
+ accessed. If both are specified, all memory in [START,START+SIZE) can be
+ accessed.
+
+ Note: If it becomes useful enough we can later add #:end as an alternative
+ to #:size. For now it is left out.
+
+ The result is a Scheme port, and its semantics are a bit odd for accessing
+ memory (e.g., unget), but we don't try to hide this. It's a port.
+
+ N.B. Seeks on the port must be in the range [0,size).
+ This is for similarity with bytevector ports, and so that one can seek
+ to the first byte. */
+
+static SCM
+gdbscm_open_memory (SCM rest)
+{
+ const SCM keywords[] = {
+ mode_keyword, start_keyword, size_keyword, SCM_BOOL_F
+ };
+ char *mode = NULL;
+ CORE_ADDR start = 0;
+ CORE_ADDR end;
+ int mode_arg_pos = -1, start_arg_pos = -1, size_arg_pos = -1;
+ ULONGEST size;
+ SCM port;
+ long mode_bits;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "#sUU", rest,
+ &mode_arg_pos, &mode,
+ &start_arg_pos, &start,
+ &size_arg_pos, &size);
+
+ scm_dynwind_begin (0);
+
+ if (mode == NULL)
+ mode = xstrdup ("r");
+ scm_dynwind_free (mode);
+
+ if (start == ~(CORE_ADDR) 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, scm_from_int (-1),
+ _("start address of 0xff..ff not allowed"));
+ }
+
+ if (size_arg_pos > 0)
+ {
+ if (size == 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0, scm_from_int (0),
+ "zero size");
+ }
+ /* For now be strict about start+size overflowing. If it becomes
+ a nuisance we can relax things later. */
+ if (start + size < start)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0,
+ scm_list_2 (gdbscm_scm_from_ulongest (start),
+ gdbscm_scm_from_ulongest (size)),
+ _("start+size overflows"));
+ }
+ end = start + size - 1;
+ if (end == ~(CORE_ADDR) 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, 0,
+ scm_list_2 (gdbscm_scm_from_ulongest (start),
+ gdbscm_scm_from_ulongest (size)),
+ _("end address of 0xff..ff not allowed"));
+ }
+ }
+ else
+ end = (~(CORE_ADDR) 0) - 1;
+
+ mode_bits = ioscm_parse_mode_bits (FUNC_NAME, mode);
+
+ port = ioscm_open_port (memory_port_desc, mode_bits);
+
+ ioscm_init_memory_port (port, start, end);
+
+ scm_dynwind_end ();
+
+ /* TODO: Set the file name as "memory-start-end"? */
+ return port;
+}
+
+/* Return non-zero if OBJ is a memory port. */
+
+static int
+gdbscm_is_memory_port (SCM obj)
+{
+ return !SCM_IMP (obj) && (SCM_TYP16 (obj) == memory_port_desc);
+}
+
+/* (memory-port? obj) -> boolean */
+
+static SCM
+gdbscm_memory_port_p (SCM obj)
+{
+ return scm_from_bool (gdbscm_is_memory_port (obj));
+}
+
+/* (memory-port-range port) -> (start end) */
+
+static SCM
+gdbscm_memory_port_range (SCM port)
+{
+ ioscm_memory_port *iomem;
+
+ SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
+ memory_port_desc_name);
+
+ iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ return scm_list_2 (gdbscm_scm_from_ulongest (iomem->start),
+ gdbscm_scm_from_ulongest (iomem->end));
+}
+
+/* (memory-port-read-buffer-size port) -> integer */
+
+static SCM
+gdbscm_memory_port_read_buffer_size (SCM port)
+{
+ ioscm_memory_port *iomem;
+
+ SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
+ memory_port_desc_name);
+
+ iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ return scm_from_uint (iomem->read_buf_size);
+}
+
+/* (set-memory-port-read-buffer-size! port size) -> unspecified
+ An exception is thrown if read data is still buffered. */
+
+static SCM
+gdbscm_set_memory_port_read_buffer_size_x (SCM port, SCM size)
+{
+ ioscm_memory_port *iomem;
+
+ SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
+ memory_port_desc_name);
+ SCM_ASSERT_TYPE (scm_is_integer (size), size, SCM_ARG2, FUNC_NAME,
+ _("integer"));
+
+ if (!scm_is_unsigned_integer (size, min_memory_port_buf_size,
+ max_memory_port_buf_size))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, size,
+ out_of_range_buf_size);
+ }
+
+ iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ ioscm_reinit_memory_port (port, scm_to_uint (size), iomem->write_buf_size,
+ FUNC_NAME);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (memory-port-write-buffer-size port) -> integer */
+
+static SCM
+gdbscm_memory_port_write_buffer_size (SCM port)
+{
+ ioscm_memory_port *iomem;
+
+ SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
+ memory_port_desc_name);
+
+ iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ return scm_from_uint (iomem->write_buf_size);
+}
+
+/* (set-memory-port-write-buffer-size! port size) -> unspecified
+ An exception is thrown if write data is still buffered. */
+
+static SCM
+gdbscm_set_memory_port_write_buffer_size_x (SCM port, SCM size)
+{
+ ioscm_memory_port *iomem;
+
+ SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
+ memory_port_desc_name);
+ SCM_ASSERT_TYPE (scm_is_integer (size), size, SCM_ARG2, FUNC_NAME,
+ _("integer"));
+
+ if (!scm_is_unsigned_integer (size, min_memory_port_buf_size,
+ max_memory_port_buf_size))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, size,
+ out_of_range_buf_size);
+ }
+
+ iomem = (ioscm_memory_port *) SCM_STREAM (port);
+ ioscm_reinit_memory_port (port, iomem->read_buf_size, scm_to_uint (size),
+ FUNC_NAME);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* Initialize gdb ports. */
+
+static const scheme_function port_functions[] =
+{
+ { "input-port", 0, 0, 0, gdbscm_input_port,
+ "\
+Return gdb's input port." },
+
+ { "output-port", 0, 0, 0, gdbscm_output_port,
+ "\
+Return gdb's output port." },
+
+ { "error-port", 0, 0, 0, gdbscm_error_port,
+ "\
+Return gdb's error port." },
+
+ { "stdio-port?", 1, 0, 0, gdbscm_stdio_port_p,
+ "\
+Return #t if the object is a gdb:stdio-port." },
+
+ { "open-memory", 0, 0, 1, gdbscm_open_memory,
+ "\
+Return a port that can be used for reading/writing inferior memory.\n\
+\n\
+ Arguments: [#:mode string] [#:start address] [#:size integer]\n\
+ Returns: A port object." },
+
+ { "memory-port?", 1, 0, 0, gdbscm_memory_port_p,
+ "\
+Return #t if the object is a memory port." },
+
+ { "memory-port-range", 1, 0, 0, gdbscm_memory_port_range,
+ "\
+Return the memory range of the port as (start end)." },
+
+ { "memory-port-read-buffer-size", 1, 0, 0,
+ gdbscm_memory_port_read_buffer_size,
+ "\
+Return the size of the read buffer for the memory port." },
+
+ { "set-memory-port-read-buffer-size!", 2, 0, 0,
+ gdbscm_set_memory_port_read_buffer_size_x,
+ "\
+Set the size of the read buffer for the memory port.\n\
+\n\
+ Arguments: port integer\n\
+ Returns: unspecified." },
+
+ { "memory-port-write-buffer-size", 1, 0, 0,
+ gdbscm_memory_port_write_buffer_size,
+ "\
+Return the size of the write buffer for the memory port." },
+
+ { "set-memory-port-write-buffer-size!", 2, 0, 0,
+ gdbscm_set_memory_port_write_buffer_size_x,
+ "\
+Set the size of the write buffer for the memory port.\n\
+\n\
+ Arguments: port integer\n\
+ Returns: unspecified." },
+
+ END_FUNCTIONS
+};
+
+static const scheme_function private_port_functions[] =
+{
+#if 0 /* TODO */
+ { "%with-gdb-input-from-port", 2, 0, 0,
+ gdbscm_percent_with_gdb_input_from_port,
+ "\
+Temporarily set GDB's input port to PORT and then invoke THUNK.\n\
+\n\
+ Arguments: port thunk\n\
+ Returns: The result of calling THUNK.\n\
+\n\
+This procedure is experimental." },
+#endif
+
+ { "%with-gdb-output-to-port", 2, 0, 0,
+ gdbscm_percent_with_gdb_output_to_port,
+ "\
+Temporarily set GDB's output port to PORT and then invoke THUNK.\n\
+\n\
+ Arguments: port thunk\n\
+ Returns: The result of calling THUNK.\n\
+\n\
+This procedure is experimental." },
+
+ { "%with-gdb-error-to-port", 2, 0, 0,
+ gdbscm_percent_with_gdb_error_to_port,
+ "\
+Temporarily set GDB's error port to PORT and then invoke THUNK.\n\
+\n\
+ Arguments: port thunk\n\
+ Returns: The result of calling THUNK.\n\
+\n\
+This procedure is experimental." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_ports (void)
+{
+ /* Save the original stdio ports for debugging purposes. */
+
+ orig_input_port_scm = scm_current_input_port ();
+ orig_output_port_scm = scm_current_output_port ();
+ orig_error_port_scm = scm_current_error_port ();
+
+ /* Set up the stdio ports. */
+
+ ioscm_init_gdb_stdio_port ();
+ input_port_scm = ioscm_make_gdb_stdio_port (0);
+ output_port_scm = ioscm_make_gdb_stdio_port (1);
+ error_port_scm = ioscm_make_gdb_stdio_port (2);
+
+ /* Set up memory ports. */
+
+ ioscm_init_memory_port_type ();
+
+ /* Install the accessor functions. */
+
+ gdbscm_define_functions (port_functions, 1);
+ gdbscm_define_functions (private_port_functions, 0);
+
+ /* Keyword args for open-memory. */
+
+ mode_keyword = scm_from_latin1_keyword ("mode");
+ start_keyword = scm_from_latin1_keyword ("start");
+ size_keyword = scm_from_latin1_keyword ("size");
+
+ /* Error message text for "out of range" memory port buffer sizes. */
+
+ out_of_range_buf_size = xstrprintf ("size not between %u - %u",
+ min_memory_port_buf_size,
+ max_memory_port_buf_size);
+}
diff --git a/gdb/guile/scm-pretty-print.c b/gdb/guile/scm-pretty-print.c
new file mode 100644
index 0000000..1b9902f4
--- /dev/null
+++ b/gdb/guile/scm-pretty-print.c
@@ -0,0 +1,1138 @@
+/* GDB/Scheme pretty-printing.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "charset.h"
+#include "gdb_assert.h"
+#include "symtab.h" /* Needed by language.h. */
+#include "language.h"
+#include "objfiles.h"
+#include "value.h"
+#include "valprint.h"
+#include "guile-internal.h"
+
+/* Return type of print_string_repr. */
+
+enum string_repr_result
+{
+ /* The string method returned None. */
+ STRING_REPR_NONE,
+ /* The string method had an error. */
+ STRING_REPR_ERROR,
+ /* Everything ok. */
+ STRING_REPR_OK
+};
+
+/* Display hints. */
+
+enum display_hint
+{
+ /* No display hint. */
+ HINT_NONE,
+ /* The display hint has a bad value. */
+ HINT_ERROR,
+ /* Print as an array. */
+ HINT_ARRAY,
+ /* Print as a map. */
+ HINT_MAP,
+ /* Print as a string. */
+ HINT_STRING
+};
+
+/* The <gdb:pretty-printer> smob. */
+
+typedef struct
+{
+ /* This must appear first. */
+ gdb_smob base;
+
+ /* A string representing the name of the printer. */
+ SCM name;
+
+ /* A boolean indicating whether the printer is enabled. */
+ SCM enabled;
+
+ /* A procedure called to look up the printer for the given value.
+ The procedure is called as (lookup gdb:pretty-printer value).
+ The result should either be a gdb:pretty-printer object that will print
+ the value, or #f if the value is not recognized. */
+ SCM lookup;
+
+ /* Note: Attaching subprinters to this smob is left to Scheme. */
+} pretty_printer_smob;
+
+/* The <gdb:pretty-printer-worker> smob. */
+
+typedef struct
+{
+ /* This must appear first. */
+ gdb_smob base;
+
+ /* Either #f or one of the supported display hints: map, array, string.
+ If neither of those then the display hint is ignored (treated as #f). */
+ SCM display_hint;
+
+ /* A procedure called to pretty-print the value.
+ (lambda (printer) ...) -> string | <gdb:lazy-string> | <gdb:value> */
+ SCM to_string;
+
+ /* A procedure called to print children of the value.
+ (lambda (printer) ...) -> <gdb:iterator>
+ The iterator returns a pair for each iteration: (name . value),
+ where "value" can have the same types as to_string. */
+ SCM children;
+} pretty_printer_worker_smob;
+
+static const char pretty_printer_smob_name[] =
+ "gdb:pretty-printer";
+static const char pretty_printer_worker_smob_name[] =
+ "gdb:pretty-printer-worker";
+
+/* The tag Guile knows the pretty-printer smobs by. */
+static scm_t_bits pretty_printer_smob_tag;
+static scm_t_bits pretty_printer_worker_smob_tag;
+
+/* Global list of pretty-printers. */
+static const char pretty_printer_list_name[] = "*pretty-printers*";
+
+/* The *pretty-printer* variable. */
+static SCM pretty_printer_list_var;
+
+/* gdb:pp-type-error. */
+static SCM pp_type_error_symbol;
+
+/* Pretty-printer display hints are specified by strings. */
+static SCM ppscm_map_string;
+static SCM ppscm_array_string;
+static SCM ppscm_string_string;
+
+/* Administrivia for pretty-printer matcher smobs. */
+
+/* The smob "mark" function for <gdb:pretty-printer>. */
+
+static SCM
+ppscm_mark_pretty_printer_smob (SCM self)
+{
+ pretty_printer_smob *pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (pp_smob->name);
+ scm_gc_mark (pp_smob->enabled);
+ scm_gc_mark (pp_smob->lookup);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&pp_smob->base);
+}
+
+/* The smob "print" function for <gdb:pretty-printer>. */
+
+static int
+ppscm_print_pretty_printer_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ pretty_printer_smob *pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", pretty_printer_smob_name);
+ scm_write (pp_smob->name, port);
+ scm_puts (gdbscm_is_true (pp_smob->enabled) ? " enabled" : " disabled",
+ port);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* (make-pretty-printer string procedure) -> <gdb:pretty-printer> */
+
+static SCM
+gdbscm_make_pretty_printer (SCM name, SCM lookup)
+{
+ pretty_printer_smob *pp_smob = (pretty_printer_smob *)
+ scm_gc_malloc (sizeof (pretty_printer_smob),
+ pretty_printer_smob_name);
+ SCM smob;
+
+ SCM_ASSERT_TYPE (scm_is_string (name), name, SCM_ARG1, FUNC_NAME,
+ _("string"));
+ SCM_ASSERT_TYPE (gdbscm_is_procedure (lookup), lookup, SCM_ARG2, FUNC_NAME,
+ _("procedure"));
+
+ pp_smob->name = name;
+ pp_smob->lookup = lookup;
+ pp_smob->enabled = SCM_BOOL_T;
+ smob = scm_new_smob (pretty_printer_smob_tag, (scm_t_bits) pp_smob);
+ gdbscm_init_gsmob (&pp_smob->base);
+
+ return smob;
+}
+
+/* Return non-zero if SCM is a <gdb:pretty-printer> object. */
+
+static int
+ppscm_is_pretty_printer (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (pretty_printer_smob_tag, scm);
+}
+
+/* (pretty-printer? object) -> boolean */
+
+static SCM
+gdbscm_pretty_printer_p (SCM scm)
+{
+ return scm_from_bool (ppscm_is_pretty_printer (scm));
+}
+
+/* Returns the <gdb:pretty-printer> object in SELF.
+ Throws an exception if SELF is not a <gdb:pretty-printer> object. */
+
+static SCM
+ppscm_get_pretty_printer_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ SCM_ASSERT_TYPE (ppscm_is_pretty_printer (self), self, arg_pos, func_name,
+ pretty_printer_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the pretty-printer smob of SELF.
+ Throws an exception if SELF is not a <gdb:pretty-printer> object. */
+
+static pretty_printer_smob *
+ppscm_get_pretty_printer_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ SCM pp_scm = ppscm_get_pretty_printer_arg_unsafe (self, arg_pos, func_name);
+ pretty_printer_smob *pp_smob
+ = (pretty_printer_smob *) SCM_SMOB_DATA (pp_scm);
+
+ return pp_smob;
+}
+
+/* Pretty-printer methods. */
+
+/* (pretty-printer-enabled? <gdb:pretty-printer>) -> boolean */
+
+static SCM
+gdbscm_pretty_printer_enabled_p (SCM self)
+{
+ pretty_printer_smob *pp_smob
+ = ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return pp_smob->enabled;
+}
+
+/* (set-pretty-printer-enabled! <gdb:pretty-printer> boolean)
+ -> unspecified */
+
+static SCM
+gdbscm_set_pretty_printer_enabled_x (SCM self, SCM enabled)
+{
+ pretty_printer_smob *pp_smob
+ = ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ pp_smob->enabled = scm_from_bool (gdbscm_is_true (enabled));
+
+ return SCM_UNSPECIFIED;
+}
+
+/* Administrivia for pretty-printer-worker smobs.
+ These are created when a matcher recognizes a value. */
+
+/* The smob "mark" function for <gdb:pretty-printer-worker>. */
+
+static SCM
+ppscm_mark_pretty_printer_worker_smob (SCM self)
+{
+ pretty_printer_worker_smob *w_smob
+ = (pretty_printer_worker_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (w_smob->display_hint);
+ scm_gc_mark (w_smob->to_string);
+ scm_gc_mark (w_smob->children);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&w_smob->base);
+}
+
+/* The smob "print" function for <gdb:pretty-printer-worker>. */
+
+static int
+ppscm_print_pretty_printer_worker_smob (SCM self, SCM port,
+ scm_print_state *pstate)
+{
+ pretty_printer_worker_smob *w_smob
+ = (pretty_printer_worker_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", pretty_printer_worker_smob_name);
+ scm_write (w_smob->display_hint, port);
+ scm_puts (" ", port);
+ scm_write (w_smob->to_string, port);
+ scm_puts (" ", port);
+ scm_write (w_smob->children, port);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* (make-pretty-printer-worker string procedure procedure)
+ -> <gdb:pretty-printer-worker> */
+
+static SCM
+gdbscm_make_pretty_printer_worker (SCM display_hint, SCM to_string,
+ SCM children)
+{
+ pretty_printer_worker_smob *w_smob = (pretty_printer_worker_smob *)
+ scm_gc_malloc (sizeof (pretty_printer_worker_smob),
+ pretty_printer_worker_smob_name);
+ SCM w_scm;
+
+ w_smob->display_hint = display_hint;
+ w_smob->to_string = to_string;
+ w_smob->children = children;
+ w_scm = scm_new_smob (pretty_printer_worker_smob_tag, (scm_t_bits) w_smob);
+ gdbscm_init_gsmob (&w_smob->base);
+ return w_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:pretty-printer-worker> object. */
+
+static int
+ppscm_is_pretty_printer_worker (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (pretty_printer_worker_smob_tag, scm);
+}
+
+/* (pretty-printer-worker? object) -> boolean */
+
+static SCM
+gdbscm_pretty_printer_worker_p (SCM scm)
+{
+ return scm_from_bool (ppscm_is_pretty_printer_worker (scm));
+}
+
+/* Helper function to create a <gdb:exception> object indicating that the
+ type of some value returned from a pretty-printer is invalid. */
+
+static SCM
+ppscm_make_pp_type_error_exception (const char *message, SCM object)
+{
+ char *msg = xstrprintf ("%s: ~S", message);
+ struct cleanup *cleanup = make_cleanup (xfree, msg);
+ SCM exception
+ = gdbscm_make_error (pp_type_error_symbol,
+ NULL /* func */, msg,
+ scm_list_1 (object), scm_list_1 (object));
+
+ do_cleanups (cleanup);
+
+ return exception;
+}
+
+/* Print MESSAGE as an exception (meaning it is controlled by
+ "guile print-stack").
+ Called from the printer code when the Scheme code returns an invalid type
+ for something. */
+
+static void
+ppscm_print_pp_type_error (const char *message, SCM object)
+{
+ SCM exception = ppscm_make_pp_type_error_exception (message, object);
+
+ gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
+}
+
+/* Helper function for find_pretty_printer which iterates over a list,
+ calls each function and inspects output. This will return a
+ <gdb:pretty-printer> object if one recognizes VALUE. If no printer is
+ found, it will return #f. On error, it will return a <gdb:exception>
+ object.
+
+ Note: This has to be efficient and careful.
+ We don't want to excessively slow down printing of values, but any kind of
+ random crud can appear in the pretty-printer list, and we can't crash
+ because of it. */
+
+static SCM
+ppscm_search_pp_list (SCM list, SCM value)
+{
+ SCM orig_list = list;
+
+ if (scm_is_null (list))
+ return SCM_BOOL_F;
+ if (gdbscm_is_false (scm_list_p (list))) /* scm_is_pair? */
+ {
+ return ppscm_make_pp_type_error_exception
+ (_("pretty-printer list is not a list"), list);
+ }
+
+ for ( ; scm_is_pair (list); list = scm_cdr (list))
+ {
+ SCM matcher = scm_car (list);
+ SCM worker;
+ pretty_printer_smob *pp_smob;
+ int rc;
+
+ if (!ppscm_is_pretty_printer (matcher))
+ {
+ return ppscm_make_pp_type_error_exception
+ (_("pretty-printer list contains non-pretty-printer object"),
+ matcher);
+ }
+
+ pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (matcher);
+
+ /* Skip if disabled. */
+ if (gdbscm_is_false (pp_smob->enabled))
+ continue;
+
+ if (!gdbscm_is_procedure (pp_smob->lookup))
+ {
+ return ppscm_make_pp_type_error_exception
+ (_("invalid lookup object in pretty-printer matcher"),
+ pp_smob->lookup);
+ }
+
+ worker = gdbscm_safe_call_2 (pp_smob->lookup, matcher,
+ value, gdbscm_memory_error_p);
+ if (!gdbscm_is_false (worker))
+ {
+ if (gdbscm_is_exception (worker))
+ return worker;
+ if (ppscm_is_pretty_printer_worker (worker))
+ return worker;
+ return ppscm_make_pp_type_error_exception
+ (_("invalid result from pretty-printer lookup"), worker);
+ }
+ }
+
+ if (!scm_is_null (list))
+ {
+ return ppscm_make_pp_type_error_exception
+ (_("pretty-printer list is not a list"), orig_list);
+ }
+
+ return SCM_BOOL_F;
+}
+
+/* Subroutine of find_pretty_printer to simplify it.
+ Look for a pretty-printer to print VALUE in all objfiles.
+ If there's an error an exception smob is returned.
+ The result is #f, if no pretty-printer was found.
+ Otherwise the result is the pretty-printer smob. */
+
+static SCM
+ppscm_find_pretty_printer_from_objfiles (SCM value)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ objfile_smob *o_smob = ofscm_objfile_smob_from_objfile (objfile);
+ SCM pp = ppscm_search_pp_list (ofscm_objfile_smob_pretty_printers (o_smob),
+ value);
+
+ /* Note: This will return if pp is a <gdb:exception> object,
+ which is what we want. */
+ if (gdbscm_is_true (pp))
+ return pp;
+ }
+
+ return SCM_BOOL_F;
+}
+
+/* Subroutine of find_pretty_printer to simplify it.
+ Look for a pretty-printer to print VALUE in the current program space.
+ If there's an error an exception smob is returned.
+ The result is #f, if no pretty-printer was found.
+ Otherwise the result is the pretty-printer smob. */
+
+static SCM
+ppscm_find_pretty_printer_from_progspace (SCM value)
+{
+ return SCM_BOOL_F; /*TODO*/
+}
+
+/* Subroutine of find_pretty_printer to simplify it.
+ Look for a pretty-printer to print VALUE in the gdb module.
+ If there's an error a Scheme exception is returned.
+ The result is #f, if no pretty-printer was found.
+ Otherwise the result is the pretty-printer smob. */
+
+static SCM
+ppscm_find_pretty_printer_from_gdb (SCM value)
+{
+ SCM pp_list, pp;
+
+ /* Fetch the global pretty printer list. */
+ pp_list = scm_variable_ref (pretty_printer_list_var);
+ pp = ppscm_search_pp_list (pp_list, value);
+ return pp;
+}
+
+/* Find the pretty-printing constructor function for VALUE. If no
+ pretty-printer exists, return #f. If one exists, return the
+ gdb:pretty-printer smob that implements it. On error, an exception smob
+ is returned.
+
+ Note: In the end it may be better to call out to Scheme once, and then
+ do all of the lookup from Scheme. TBD. */
+
+static SCM
+ppscm_find_pretty_printer (SCM value)
+{
+ SCM pp;
+
+ /* Look at the pretty-printer list for each objfile
+ in the current program-space. */
+ pp = ppscm_find_pretty_printer_from_objfiles (value);
+ /* Note: This will return if function is a <gdb:exception> object,
+ which is what we want. */
+ if (gdbscm_is_true (pp))
+ return pp;
+
+ /* Look at the pretty-printer list for the current program-space. */
+ pp = ppscm_find_pretty_printer_from_progspace (value);
+ /* Note: This will return if function is a <gdb:exception> object,
+ which is what we want. */
+ if (gdbscm_is_true (pp))
+ return pp;
+
+ /* Look at the pretty-printer list in the gdb module. */
+ pp = ppscm_find_pretty_printer_from_gdb (value);
+ return pp;
+}
+
+/* Pretty-print a single value, via the PRINTER, which must be a
+ <gdb:pretty-printer-worker> object.
+ The caller is responsible for ensuring PRINTER is valid.
+ If the function returns a string, an SCM containing the string
+ is returned. If the function returns #f that means the pretty
+ printer returned #f as a value. Otherwise, if the function returns a
+ <gdb:value> object, *OUT_VALUE is set to the value and #t is returned.
+ It is an error if the printer returns #t.
+ On error, an exception smob is returned. */
+
+static SCM
+ppscm_pretty_print_one_value (SCM printer, struct value **out_value,
+ struct gdbarch *gdbarch,
+ const struct language_defn *language)
+{
+ volatile struct gdb_exception except;
+ SCM result = SCM_BOOL_F;
+
+ *out_value = NULL;
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ int rc;
+ pretty_printer_worker_smob *w_smob
+ = (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
+
+ result = gdbscm_safe_call_1 (w_smob->to_string, printer,
+ gdbscm_memory_error_p);
+ if (gdbscm_is_false (result))
+ ; /* Done. */
+ else if (scm_is_string (result)
+ || lsscm_is_lazy_string (result))
+ ; /* Done. */
+ else if (vlscm_is_value (result))
+ {
+ SCM except_scm;
+
+ *out_value
+ = vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
+ result, &except_scm,
+ gdbarch, language);
+ if (*out_value != NULL)
+ result = SCM_BOOL_T;
+ else
+ result = except_scm;
+ }
+ else if (gdbscm_is_exception (result))
+ ; /* Done. */
+ else
+ {
+ /* Invalid result from to-string. */
+ result = ppscm_make_pp_type_error_exception
+ (_("invalid result from pretty-printer to-string"), result);
+ }
+ }
+
+ return result;
+}
+
+/* Return the display hint for PRINTER as a Scheme object.
+ The caller is responsible for ensuring PRINTER is a
+ <gdb:pretty-printer-worker> object. */
+
+static SCM
+ppscm_get_display_hint_scm (SCM printer)
+{
+ pretty_printer_worker_smob *w_smob
+ = (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
+
+ return w_smob->display_hint;
+}
+
+/* Return the display hint for the pretty-printer PRINTER.
+ The caller is responsible for ensuring PRINTER is a
+ <gdb:pretty-printer-worker> object.
+ Returns the display hint or #f if the hint is not a string. */
+
+static enum display_hint
+ppscm_get_display_hint_enum (SCM printer)
+{
+ SCM hint = ppscm_get_display_hint_scm (printer);
+
+ if (gdbscm_is_false (hint))
+ return HINT_NONE;
+ if (scm_is_string (hint))
+ {
+ if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_array_string)))
+ return HINT_STRING;
+ if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_map_string)))
+ return HINT_STRING;
+ if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_string_string)))
+ return HINT_STRING;
+ return HINT_ERROR;
+ }
+ return HINT_ERROR;
+}
+
+/* A wrapper for gdbscm_print_gdb_exception that ignores memory errors.
+ EXCEPTION is a <gdb:exception> object. */
+
+static void
+ppscm_print_exception_unless_memory_error (SCM exception,
+ struct ui_file *stream)
+{
+ if (gdbscm_memory_error_p (gdbscm_exception_key (exception)))
+ {
+ char *msg = gdbscm_exception_message_to_string (exception);
+ struct cleanup *cleanup = make_cleanup (xfree, msg);
+
+ /* This "shouldn't happen", but play it safe. */
+ if (msg == NULL || *msg == '\0')
+ fprintf_filtered (stream, _("<error reading variable>"));
+ else
+ {
+ /* Remove the trailing newline. We could instead call a special
+ routine for printing memory error messages, but this is easy
+ enough for now. */
+ size_t len = strlen (msg);
+
+ if (msg[len - 1] == '\n')
+ msg[len - 1] = '\0';
+ fprintf_filtered (stream, _("<error reading variable: %s>"), msg);
+ }
+
+ do_cleanups (cleanup);
+ }
+ else
+ gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
+}
+
+/* Helper for gdbscm_apply_val_pretty_printer which calls to_string and
+ formats the result. */
+
+static enum string_repr_result
+ppscm_print_string_repr (SCM printer, enum display_hint hint,
+ struct ui_file *stream, int recurse,
+ const struct value_print_options *options,
+ struct gdbarch *gdbarch,
+ const struct language_defn *language)
+{
+ struct value *replacement = NULL;
+ SCM str_scm;
+ enum string_repr_result result = STRING_REPR_ERROR;
+
+ str_scm = ppscm_pretty_print_one_value (printer, &replacement,
+ gdbarch, language);
+ if (gdbscm_is_false (str_scm))
+ {
+ result = STRING_REPR_NONE;
+ }
+ else if (scm_is_eq (str_scm, SCM_BOOL_T))
+ {
+ struct value_print_options opts = *options;
+
+ gdb_assert (replacement != NULL);
+ opts.addressprint = 0;
+ common_val_print (replacement, stream, recurse, &opts, language);
+ result = STRING_REPR_OK;
+ }
+ else if (scm_is_string (str_scm))
+ {
+ struct cleanup *cleanup;
+ size_t length;
+ char *string
+ = gdbscm_scm_to_string (str_scm, &length,
+ target_charset (gdbarch), 0 /*!strict*/, NULL);
+
+ cleanup = make_cleanup (xfree, string);
+ if (hint == HINT_STRING)
+ {
+ struct type *type = builtin_type (gdbarch)->builtin_char;
+
+ LA_PRINT_STRING (stream, type, (gdb_byte *) string,
+ length, NULL, 0, options);
+ }
+ else
+ {
+ /* Alas scm_to_stringn doesn't nul-terminate the string if we
+ ask for the length. */
+ size_t i;
+
+ for (i = 0; i < length; ++i)
+ {
+ if (string[i] == '\0')
+ fputs_filtered ("\\000", stream);
+ else
+ fputc_filtered (string[i], stream);
+ }
+ }
+ result = STRING_REPR_OK;
+ do_cleanups (cleanup);
+ }
+ else if (lsscm_is_lazy_string (str_scm))
+ {
+ struct value_print_options local_opts = *options;
+
+ local_opts.addressprint = 0;
+ lsscm_val_print_lazy_string (str_scm, stream, &local_opts);
+ result = STRING_REPR_OK;
+ }
+ else
+ {
+ gdb_assert (gdbscm_is_exception (str_scm));
+ ppscm_print_exception_unless_memory_error (str_scm, stream);
+ result = STRING_REPR_ERROR;
+ }
+
+ return result;
+}
+
+/* Helper for gdbscm_apply_val_pretty_printer that formats children of the
+ printer, if any exist.
+ The caller is responsible for ensuring PRINTER is a printer smob.
+ If PRINTED_NOTHING is true, then nothing has been printed by to_string,
+ and format output accordingly. */
+
+static void
+ppscm_print_children (SCM printer, enum display_hint hint,
+ struct ui_file *stream, int recurse,
+ const struct value_print_options *options,
+ struct gdbarch *gdbarch,
+ const struct language_defn *language,
+ int printed_nothing)
+{
+ pretty_printer_worker_smob *w_smob
+ = (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
+ int is_map, is_array, done_flag, pretty;
+ unsigned int i;
+ SCM children, status;
+ SCM iter = SCM_BOOL_F; /* -Wall */
+ struct cleanup *cleanups;
+
+ if (gdbscm_is_false (w_smob->children))
+ return;
+ if (!gdbscm_is_procedure (w_smob->children))
+ {
+ ppscm_print_pp_type_error
+ (_("pretty-printer \"children\" object is not a procedure or #f"),
+ w_smob->children);
+ return;
+ }
+
+ cleanups = make_cleanup (null_cleanup, NULL);
+
+ /* If we are printing a map or an array, we want special formatting. */
+ is_map = hint == HINT_MAP;
+ is_array = hint == HINT_ARRAY;
+
+ children = gdbscm_safe_call_1 (w_smob->children, printer,
+ gdbscm_memory_error_p);
+ if (gdbscm_is_exception (children))
+ {
+ ppscm_print_exception_unless_memory_error (children, stream);
+ goto done;
+ }
+ /* We combine two steps here: get children, make an iterator out of them.
+ This simplifies things because there's no language means of creating
+ iterators, and it's the printer object that knows how it will want its
+ children iterated over. */
+ if (!itscm_is_iterator (children))
+ {
+ ppscm_print_pp_type_error
+ (_("result of pretty-printer \"children\" procedure is not"
+ " a <gdb:iterator> object"), children);
+ goto done;
+ }
+ iter = children;
+
+ /* Use the prettyformat_arrays option if we are printing an array,
+ and the pretty option otherwise. */
+ if (is_array)
+ pretty = options->prettyformat_arrays;
+ else
+ {
+ if (options->prettyformat == Val_prettyformat)
+ pretty = 1;
+ else
+ pretty = options->prettyformat_structs;
+ }
+
+ done_flag = 0;
+ for (i = 0; i < options->print_max; ++i)
+ {
+ int rc;
+ SCM scm_name, v_scm;
+ char *name;
+ SCM item = itscm_safe_call_next_x (iter, gdbscm_memory_error_p);
+ struct cleanup *inner_cleanup = make_cleanup (null_cleanup, NULL);
+
+ if (gdbscm_is_exception (item))
+ {
+ ppscm_print_exception_unless_memory_error (item, stream);
+ break;
+ }
+ if (itscm_is_end_of_iteration (item))
+ {
+ /* Set a flag so we can know whether we printed all the
+ available elements. */
+ done_flag = 1;
+ break;
+ }
+
+ if (! scm_is_pair (item))
+ {
+ ppscm_print_pp_type_error
+ (_("result of pretty-printer children iterator is not a pair"
+ " or (end-of-iteration)"),
+ item);
+ continue;
+ }
+ scm_name = scm_car (item);
+ v_scm = scm_cdr (item);
+ if (!scm_is_string (scm_name))
+ {
+ ppscm_print_pp_type_error
+ (_("first element of pretty-printer children iterator is not"
+ " a string"), item);
+ continue;
+ }
+ name = gdbscm_scm_to_c_string (scm_name);
+ make_cleanup (xfree, name);
+
+ /* Print initial "{". For other elements, there are three cases:
+ 1. Maps. Print a "," after each value element.
+ 2. Arrays. Always print a ",".
+ 3. Other. Always print a ",". */
+ if (i == 0)
+ {
+ if (printed_nothing)
+ fputs_filtered ("{", stream);
+ else
+ fputs_filtered (" = {", stream);
+ }
+
+ else if (! is_map || i % 2 == 0)
+ fputs_filtered (pretty ? "," : ", ", stream);
+
+ /* In summary mode, we just want to print "= {...}" if there is
+ a value. */
+ if (options->summary)
+ {
+ /* This increment tricks the post-loop logic to print what
+ we want. */
+ ++i;
+ /* Likewise. */
+ pretty = 0;
+ break;
+ }
+
+ if (! is_map || i % 2 == 0)
+ {
+ if (pretty)
+ {
+ fputs_filtered ("\n", stream);
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ else
+ wrap_here (n_spaces (2 + 2 *recurse));
+ }
+
+ if (is_map && i % 2 == 0)
+ fputs_filtered ("[", stream);
+ else if (is_array)
+ {
+ /* We print the index, not whatever the child method
+ returned as the name. */
+ if (options->print_array_indexes)
+ fprintf_filtered (stream, "[%d] = ", i);
+ }
+ else if (! is_map)
+ {
+ fputs_filtered (name, stream);
+ fputs_filtered (" = ", stream);
+ }
+
+ if (lsscm_is_lazy_string (v_scm))
+ {
+ struct value_print_options local_opts = *options;
+
+ local_opts.addressprint = 0;
+ lsscm_val_print_lazy_string (v_scm, stream, &local_opts);
+ }
+ else if (scm_is_string (v_scm))
+ {
+ char *output = gdbscm_scm_to_c_string (v_scm);
+
+ fputs_filtered (output, stream);
+ xfree (output);
+ }
+ else
+ {
+ SCM except_scm;
+ struct value *value
+ = vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
+ v_scm, &except_scm,
+ gdbarch, language);
+
+ if (value == NULL)
+ {
+ ppscm_print_exception_unless_memory_error (except_scm, stream);
+ break;
+ }
+ common_val_print (value, stream, recurse + 1, options, language);
+ }
+
+ if (is_map && i % 2 == 0)
+ fputs_filtered ("] = ", stream);
+
+ do_cleanups (inner_cleanup);
+ }
+
+ if (i)
+ {
+ if (!done_flag)
+ {
+ if (pretty)
+ {
+ fputs_filtered ("\n", stream);
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ fputs_filtered ("...", stream);
+ }
+ if (pretty)
+ {
+ fputs_filtered ("\n", stream);
+ print_spaces_filtered (2 * recurse, stream);
+ }
+ fputs_filtered ("}", stream);
+ }
+
+ done:
+ do_cleanups (cleanups);
+
+ /* Play it safe, make sure ITER doesn't get GC'd. */
+ scm_remember_upto_here_1 (iter);
+}
+
+/* This is the extension_language_ops.apply_val_pretty_printer "method". */
+
+enum ext_lang_rc
+gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang,
+ 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,
+ const struct language_defn *language)
+{
+ struct gdbarch *gdbarch = get_type_arch (type);
+ SCM exception = SCM_BOOL_F;
+ SCM printer = SCM_BOOL_F;
+ SCM val_obj = SCM_BOOL_F;
+ struct value *value;
+ enum display_hint hint;
+ struct cleanup *cleanups;
+ int result = EXT_LANG_RC_NOP;
+ enum string_repr_result print_result;
+
+ /* No pretty-printer support for unavailable values. */
+ if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
+ return EXT_LANG_RC_NOP;
+
+ if (!gdb_scheme_initialized)
+ return EXT_LANG_RC_NOP;
+
+ cleanups = make_cleanup (null_cleanup, NULL);
+
+ /* Instantiate the printer. */
+ if (valaddr)
+ valaddr += embedded_offset;
+ value = value_from_contents_and_address (type, valaddr,
+ address + embedded_offset);
+
+ set_value_component_location (value, val);
+ /* set_value_component_location resets the address, so we may
+ need to set it again. */
+ if (VALUE_LVAL (value) != lval_internalvar
+ && VALUE_LVAL (value) != lval_internalvar_component
+ && VALUE_LVAL (value) != lval_computed)
+ set_value_address (value, address + embedded_offset);
+
+ val_obj = vlscm_scm_from_value (value);
+ if (gdbscm_is_exception (val_obj))
+ {
+ exception = val_obj;
+ result = EXT_LANG_RC_ERROR;
+ goto done;
+ }
+
+ printer = ppscm_find_pretty_printer (val_obj);
+
+ if (gdbscm_is_exception (printer))
+ {
+ exception = printer;
+ result = EXT_LANG_RC_ERROR;
+ goto done;
+ }
+ if (gdbscm_is_false (printer))
+ {
+ result = EXT_LANG_RC_NOP;
+ goto done;
+ }
+ gdb_assert (ppscm_is_pretty_printer_worker (printer));
+
+ /* If we are printing a map, we want some special formatting. */
+ hint = ppscm_get_display_hint_enum (printer);
+ if (hint == HINT_ERROR)
+ {
+ /* Print the error as an exception for consistency. */
+ SCM hint_scm = ppscm_get_display_hint_scm (printer);
+
+ ppscm_print_pp_type_error ("Invalid display hint", hint_scm);
+ /* Fall through. A bad hint doesn't stop pretty-printing. */
+ hint = HINT_NONE;
+ }
+
+ /* Print the section. */
+ print_result = ppscm_print_string_repr (printer, hint, stream, recurse,
+ options, gdbarch, language);
+ if (print_result != STRING_REPR_ERROR)
+ {
+ ppscm_print_children (printer, hint, stream, recurse, options,
+ gdbarch, language,
+ print_result == STRING_REPR_NONE);
+ }
+
+ result = EXT_LANG_RC_OK;
+
+ done:
+ if (gdbscm_is_exception (exception))
+ ppscm_print_exception_unless_memory_error (exception, stream);
+ do_cleanups (cleanups);
+ return result;
+}
+
+/* Initialize the Scheme pretty-printer code. */
+
+static const scheme_function pretty_printer_functions[] =
+{
+ { "make-pretty-printer", 2, 0, 0, gdbscm_make_pretty_printer,
+ "\
+Create a <gdb:pretty-printer> object.\n\
+\n\
+ Arguments: name lookup\n\
+ name: a string naming the matcher\n\
+ lookup: a procedure:\n\
+ (pretty-printer <gdb:value>) -> <gdb:pretty-printer-worker> | #f." },
+
+ { "pretty-printer?", 1, 0, 0, gdbscm_pretty_printer_p,
+ "\
+Return #t if the object is a <gdb:pretty-printer> object." },
+
+ { "pretty-printer-enabled?", 1, 0, 0, gdbscm_pretty_printer_enabled_p,
+ "\
+Return #t if the pretty-printer is enabled." },
+
+ { "set-pretty-printer-enabled!", 2, 0, 0,
+ gdbscm_set_pretty_printer_enabled_x,
+ "\
+Set the enabled flag of the pretty-printer.\n\
+Returns \"unspecified\"." },
+
+ { "make-pretty-printer-worker", 3, 0, 0, gdbscm_make_pretty_printer_worker,
+ "\
+Create a <gdb:pretty-printer-worker> object.\n\
+\n\
+ Arguments: display-hint to-string children\n\
+ display-hint: either #f or one of \"array\", \"map\", or \"string\"\n\
+ to-string: a procedure:\n\
+ (pretty-printer) -> string | #f | <gdb:value>\n\
+ children: either #f or a procedure:\n\
+ (pretty-printer) -> <gdb:iterator>" },
+
+ { "pretty-printer-worker?", 1, 0, 0, gdbscm_pretty_printer_worker_p,
+ "\
+Return #t if the object is a <gdb:pretty-printer-worker> object." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_pretty_printers (void)
+{
+ pretty_printer_smob_tag
+ = gdbscm_make_smob_type (pretty_printer_smob_name,
+ sizeof (pretty_printer_smob));
+ scm_set_smob_mark (pretty_printer_smob_tag,
+ ppscm_mark_pretty_printer_smob);
+ scm_set_smob_print (pretty_printer_smob_tag,
+ ppscm_print_pretty_printer_smob);
+
+ pretty_printer_worker_smob_tag
+ = gdbscm_make_smob_type (pretty_printer_worker_smob_name,
+ sizeof (pretty_printer_worker_smob));
+ scm_set_smob_mark (pretty_printer_worker_smob_tag,
+ ppscm_mark_pretty_printer_worker_smob);
+ scm_set_smob_print (pretty_printer_worker_smob_tag,
+ ppscm_print_pretty_printer_worker_smob);
+
+ gdbscm_define_functions (pretty_printer_functions, 1);
+
+ scm_c_define (pretty_printer_list_name, SCM_EOL);
+
+ pretty_printer_list_var
+ = scm_c_private_variable (gdbscm_module_name,
+ pretty_printer_list_name);
+ gdb_assert (!gdbscm_is_false (pretty_printer_list_var));
+
+ pp_type_error_symbol = scm_from_latin1_symbol ("gdb:pp-type-error");
+
+ ppscm_map_string = scm_from_latin1_string ("map");
+ ppscm_array_string = scm_from_latin1_string ("array");
+ ppscm_string_string = scm_from_latin1_string ("string");
+}
diff --git a/gdb/guile/scm-safe-call.c b/gdb/guile/scm-safe-call.c
new file mode 100644
index 0000000..147d7f5
--- /dev/null
+++ b/gdb/guile/scm-safe-call.c
@@ -0,0 +1,464 @@
+/* GDB/Scheme support for safe calls into the Guile interpreter.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "filenames.h"
+#include "gdb_assert.h"
+#include "guile-internal.h"
+
+/* Struct to marshall args to scscm_safe_call_body. */
+
+struct c_data
+{
+ void *(*func) (void *);
+ void *data;
+ /* An error message or NULL for success. */
+ void *result;
+};
+
+/* Struct to marshall args through gdbscm_with_catch. */
+
+struct with_catch_data
+{
+ scm_t_catch_body func;
+ void *data;
+ scm_t_catch_handler unwind_handler;
+ scm_t_catch_handler pre_unwind_handler;
+
+ /* If EXCP_MATCHER is non-NULL, it is an excp_matcher_func function.
+ If the exception is recognized by it, the exception is recorded as is,
+ without wrapping it in gdb:with-stack. */
+ excp_matcher_func *excp_matcher;
+
+ SCM stack;
+ SCM catch_result;
+};
+
+/* The "body" argument to scm_i_with_continuation_barrier.
+ Invoke the user-supplied function. */
+
+static SCM
+scscm_safe_call_body (void *d)
+{
+ struct c_data *data = (struct c_data *) d;
+
+ data->result = data->func (data->data);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* A "pre-unwind handler" to scm_c_catch that prints the exception
+ according to "set guile print-stack". */
+
+static SCM
+scscm_printing_pre_unwind_handler (void *data, SCM key, SCM args)
+{
+ SCM stack = scm_make_stack (SCM_BOOL_T, scm_list_1 (scm_from_int (2)));
+
+ gdbscm_print_exception_with_stack (SCM_BOOL_F, stack, key, args);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* A no-op unwind handler. */
+
+static SCM
+scscm_nop_unwind_handler (void *data, SCM key, SCM args)
+{
+ return SCM_UNSPECIFIED;
+}
+
+/* The "pre-unwind handler" to scm_c_catch that records the exception
+ for possible later printing. We do this in the pre-unwind handler because
+ we want the stack to include point where the exception occurred.
+
+ If DATA is non-NULL, it is an excp_matcher_func function.
+ If the exception is recognized by it, the exception is recorded as is,
+ without wrapping it in gdb:with-stack. */
+
+static SCM
+scscm_recording_pre_unwind_handler (void *datap, SCM key, SCM args)
+{
+ struct with_catch_data *data = datap;
+ excp_matcher_func *matcher = data->excp_matcher;
+
+ if (matcher != NULL && matcher (key))
+ return SCM_UNSPECIFIED;
+
+ /* There's no need to record the whole stack if we're not going to print it.
+ However, convention is to still print the stack frame in which the
+ exception occurred, even if we're not going to print a full backtrace.
+ For now, keep it simple. */
+
+ data->stack = scm_make_stack (SCM_BOOL_T, scm_list_1 (scm_from_int (2)));
+
+ /* IWBN if we could return the <gdb:exception> here and skip the unwind
+ handler, but it doesn't work that way. If we want to return a
+ <gdb:exception> object from the catch it needs to come from the unwind
+ handler. So what we do is save the stack for later use by the unwind
+ handler. */
+
+ return SCM_UNSPECIFIED;
+}
+
+/* Part two of the recording unwind handler.
+ Here we take the stack saved from the pre-unwind handler and create
+ the <gdb:exception> object. */
+
+static SCM
+scscm_recording_unwind_handler (void *datap, SCM key, SCM args)
+{
+ struct with_catch_data *data = datap;
+
+ /* We need to record the stack in the exception since we're about to
+ throw and lose the location that got the exception. We do this by
+ wrapping the exception + stack in a new exception. */
+
+ if (gdbscm_is_true (data->stack))
+ return gdbscm_make_exception_with_stack (key, args, data->stack);
+
+ return gdbscm_make_exception (key, args);
+}
+
+/* Ugh. :-(
+ Guile doesn't export scm_i_with_continuation_barrier which is exactly
+ what we need. To cope, have our own wrapper around scm_c_catch and
+ pass this as the "body" argument to scm_c_with_continuation_barrier.
+ Darn darn darn. */
+
+static void *
+gdbscm_with_catch (void *data)
+{
+ struct with_catch_data *d = data;
+
+ d->catch_result
+ = scm_c_catch (SCM_BOOL_T,
+ d->func, d->data,
+ d->unwind_handler, d,
+ d->pre_unwind_handler, d);
+
+ return NULL;
+}
+
+/* A wrapper around scm_with_guile that prints backtraces and exceptions
+ according to "set guile print-stack".
+ The result if NULL if no exception occurred, otherwise it is a statically
+ allocated error message (caller must *not* free). */
+
+void *
+gdbscm_with_guile (void *(*func) (void *), void *data)
+{
+ struct c_data c_data;
+ struct with_catch_data catch_data;
+
+ c_data.func = func;
+ c_data.data = data;
+ /* Set this now in case an exception is thrown. */
+ c_data.result = _("Error while executing Scheme code.");
+
+ catch_data.func = scscm_safe_call_body;
+ catch_data.data = &c_data;
+ catch_data.unwind_handler = scscm_nop_unwind_handler;
+ catch_data.pre_unwind_handler = scscm_printing_pre_unwind_handler;
+ catch_data.excp_matcher = NULL;
+ catch_data.stack = SCM_BOOL_F;
+ catch_data.catch_result = SCM_UNSPECIFIED;
+
+ scm_with_guile (gdbscm_with_catch, &catch_data);
+
+ return c_data.result;
+}
+
+/* Another wrapper of scm_with_guile for use by the safe call/apply routines
+ in this file, as well as for general purpose calling other functions safely.
+ For these we want to record the exception, but leave the possible printing
+ of it to later. */
+
+SCM
+gdbscm_call_guile (SCM (*func) (void *), void *data,
+ excp_matcher_func *ok_excps)
+{
+ struct with_catch_data catch_data;
+
+ catch_data.func = func;
+ catch_data.data = data;
+ catch_data.unwind_handler = scscm_recording_unwind_handler;
+ catch_data.pre_unwind_handler = scscm_recording_pre_unwind_handler;
+ catch_data.excp_matcher = ok_excps;
+ catch_data.stack = SCM_BOOL_F;
+ catch_data.catch_result = SCM_UNSPECIFIED;
+
+#if 0
+ scm_c_with_continuation_barrier (gdbscm_with_catch, &catch_data);
+#else
+ scm_with_guile (gdbscm_with_catch, &catch_data);
+#endif
+
+ return catch_data.catch_result;
+}
+
+/* Utilities to safely call Scheme code, catching all exceptions, and
+ preventing continuation capture.
+ The result is the result of calling the function, or if an exception occurs
+ then the result is a <gdb:exception> smob, which can be tested for with
+ gdbscm_is_exception. */
+
+/* Helper for gdbscm_safe_call_0. */
+
+static SCM
+scscm_call_0_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_call_0 (args[0]);
+}
+
+SCM
+gdbscm_safe_call_0 (SCM proc, excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc };
+
+ return gdbscm_call_guile (scscm_call_0_body, args, ok_excps);
+}
+
+/* Helper for gdbscm_safe_call_1. */
+
+static SCM
+scscm_call_1_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_call_1 (args[0], args[1]);
+}
+
+SCM
+gdbscm_safe_call_1 (SCM proc, SCM arg0, excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc, arg0 };
+
+ return gdbscm_call_guile (scscm_call_1_body, args, ok_excps);
+}
+
+/* Helper for gdbscm_safe_call_2. */
+
+static SCM
+scscm_call_2_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_call_2 (args[0], args[1], args[2]);
+}
+
+SCM
+gdbscm_safe_call_2 (SCM proc, SCM arg0, SCM arg1, excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc, arg0, arg1 };
+
+ return gdbscm_call_guile (scscm_call_2_body, args, ok_excps);
+}
+
+/* Helper for gdbscm_safe_call_3. */
+
+static SCM
+scscm_call_3_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_call_3 (args[0], args[1], args[2], args[3]);
+}
+
+SCM
+gdbscm_safe_call_3 (SCM proc, SCM arg1, SCM arg2, SCM arg3,
+ excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc, arg1, arg2, arg3 };
+
+ return gdbscm_call_guile (scscm_call_3_body, args, ok_excps);
+}
+
+/* Helper for gdbscm_safe_call_4. */
+
+static SCM
+scscm_call_4_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_call_4 (args[0], args[1], args[2], args[3], args[4]);
+}
+
+SCM
+gdbscm_safe_call_4 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4,
+ excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc, arg1, arg2, arg3, arg4 };
+
+ return gdbscm_call_guile (scscm_call_4_body, args, ok_excps);
+}
+
+/* Helper for gdbscm_safe_apply_1. */
+
+static SCM
+scscm_apply_1_body (void *argsp)
+{
+ SCM *args = argsp;
+
+ return scm_apply_1 (args[0], args[1], args[2]);
+}
+
+SCM
+gdbscm_safe_apply_1 (SCM proc, SCM arg0, SCM rest, excp_matcher_func *ok_excps)
+{
+ SCM args[] = { proc, arg0, rest };
+
+ return gdbscm_call_guile (scscm_apply_1_body, args, ok_excps);
+}
+
+/* Utilities to call Scheme code, not catching exceptions, and
+ not preventing continuation capture.
+ The result is the result of calling the function.
+ If an exception occurs then Guile is left to handle the exception,
+ unwinding the stack as appropriate.
+
+ USE THESE WITH CARE.
+ Typically these are called from functions that implement Scheme procedures,
+ and we don't want to catch the exception; otherwise it will get printed
+ twice: once when first caught and once if it ends up being rethrown and the
+ rethrow reaches the top repl, which will confuse the user.
+
+ While these calls just pass the call off to the corresponding Guile
+ procedure, all such calls are routed through these ones to:
+ a) provide a place to put hooks or whatnot in if we need to,
+ b) add "unsafe" to the name to alert the reader. */
+
+SCM
+gdbscm_unsafe_call_1 (SCM proc, SCM arg0)
+{
+ return scm_call_1 (proc, arg0);
+}
+
+/* Utilities for safely evaluating a Scheme expression string. */
+
+struct eval_scheme_string_data
+{
+ const char *string;
+ int display_result;
+};
+
+/* Wrapper to eval a C string in the Guile interpreter.
+ This is passed to scm_with_guile. */
+
+static void *
+scscm_eval_scheme_string (void *datap)
+{
+ struct eval_scheme_string_data *data = datap;
+ SCM result = scm_c_eval_string (data->string);
+
+ if (data->display_result && !scm_is_eq (result, SCM_UNSPECIFIED))
+ {
+ SCM port = scm_current_output_port ();
+
+ scm_write (result, port);
+ scm_newline (port);
+ }
+
+ /* If we get here the eval succeeded. */
+ return NULL;
+}
+
+/* Evaluate EXPR in the Guile interpreter, catching all exceptions
+ and preventing continuation capture.
+ The result is NULL if no exception occurred. Otherwise, the exception is
+ printed according to "set guile print-stack" and the result is an error
+ message allocated with malloc, caller must free. */
+
+char *
+gdbscm_safe_eval_string (const char *string, int display_result)
+{
+ struct eval_scheme_string_data data = { string, display_result };
+ void *result;
+
+ result = gdbscm_with_guile (scscm_eval_scheme_string, (void *) &data);
+
+ if (result != NULL)
+ return xstrdup (result);
+ return NULL;
+}
+
+/* Utilities for safely loading Scheme scripts. */
+
+/* Helper function for gdbscm_safe_source_scheme_script. */
+
+static void *
+scscm_source_scheme_script (void *data)
+{
+ const char *filename = data;
+
+ /* The Guile docs don't specify what the result is.
+ Maybe it's SCM_UNSPECIFIED, but the docs should specify that. :-) */
+ scm_c_primitive_load_path (filename);
+
+ /* If we get here the load succeeded. */
+ return NULL;
+}
+
+/* Try to load a script, catching all exceptions,
+ and preventing continuation capture.
+ The result is NULL if the load succeeded. Otherwise, the exception is
+ printed according to "set guile print-stack" and the result is an error
+ message allocated with malloc, caller must free. */
+
+char *
+gdbscm_safe_source_script (const char *filename)
+{
+ /* scm_c_primitive_load_path only looks in %load-path for files with
+ relative paths. An alternative could be to temporarily add "." to
+ %load-path, but we don't want %load-path to be searched. At least not
+ by default. This function is invoked by the "source" GDB command which
+ already has its own path search support. */
+ char *abs_filename = NULL;
+ void *result;
+
+ if (!IS_ABSOLUTE_PATH (filename))
+ {
+ abs_filename = gdb_realpath (filename);
+ filename = abs_filename;
+ }
+
+ result = gdbscm_with_guile (scscm_source_scheme_script,
+ (void *) filename);
+
+ xfree (abs_filename);
+ if (result != NULL)
+ return xstrdup (result);
+ return NULL;
+}
+
+/* Utility for entering an interactive Guile repl. */
+
+void
+gdbscm_enter_repl (void)
+{
+ /* It's unfortunate to have to resort to something like this, but
+ scm_shell doesn't return. :-( I found this code on guile-user@. */
+ gdbscm_safe_call_1 (scm_c_public_ref ("system repl repl", "start-repl"),
+ scm_from_latin1_symbol ("scheme"), NULL);
+}
diff --git a/gdb/guile/scm-string.c b/gdb/guile/scm-string.c
new file mode 100644
index 0000000..87ecabf
--- /dev/null
+++ b/gdb/guile/scm-string.c
@@ -0,0 +1,246 @@
+/* GDB/Scheme charset interface.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include <stdarg.h>
+#include "charset.h"
+#include "guile-internal.h"
+
+/* Convert a C (latin1) string to an SCM string.
+ "latin1" is chosen because Guile won't throw an exception. */
+
+SCM
+gdbscm_scm_from_c_string (const char *string)
+{
+ return scm_from_latin1_string (string);
+}
+
+/* Convert an SCM string to a C (latin1) string.
+ "latin1" is chosen because Guile won't throw an exception.
+ Space for the result is allocated with malloc, caller must free.
+ It is an error to call this if STRING is not a string. */
+
+char *
+gdbscm_scm_to_c_string (SCM string)
+{
+ return scm_to_latin1_string (string);
+}
+
+/* Use printf to construct a Scheme string. */
+
+SCM
+gdbscm_scm_from_printf (const char *format, ...)
+{
+ va_list args;
+ char *string;
+ SCM result;
+
+ va_start (args, format);
+ string = xstrvprintf (format, args);
+ va_end (args);
+ result = scm_from_latin1_string (string);
+ xfree (string);
+
+ return result;
+}
+
+/* Struct to pass data from gdbscm_scm_to_string to
+ gdbscm_call_scm_to_stringn. */
+
+struct scm_to_stringn_data
+{
+ SCM string;
+ size_t *lenp;
+ const char *charset;
+ int conversion_kind;
+ char *result;
+};
+
+/* Helper for gdbscm_scm_to_string to call scm_to_stringn
+ from within scm_c_catch. */
+
+static SCM
+gdbscm_call_scm_to_stringn (void *datap)
+{
+ struct scm_to_stringn_data *data = datap;
+
+ data->result = scm_to_stringn (data->string, data->lenp, data->charset,
+ data->conversion_kind);
+ return SCM_BOOL_F;
+}
+
+/* Convert an SCM string to a string in charset CHARSET.
+ This function is guaranteed to not throw an exception.
+ If STRICT is non-zero, and there's a conversion error, then a
+ <gdb:exception> object is stored in *EXCEPT_SCMP, and NULL is returned.
+ If STRICT is zero, then escape sequences are used for characters that
+ can't be converted, and EXCEPT_SCMP may be passed as NULL.
+ Space for the result is allocated with malloc, caller must free.
+ It is an error to call this if STRING is not a string. */
+
+char *
+gdbscm_scm_to_string (SCM string, size_t *lenp,
+ const char *charset, int strict, SCM *except_scmp)
+{
+ struct scm_to_stringn_data data;
+ SCM scm_result;
+
+ data.string = string;
+ data.lenp = lenp;
+ data.charset = charset;
+ data.conversion_kind = (strict
+ ? SCM_FAILED_CONVERSION_ERROR
+ : SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE);
+ data.result = NULL;
+
+ scm_result = gdbscm_call_guile (gdbscm_call_scm_to_stringn, &data, NULL);
+
+ if (gdbscm_is_false (scm_result))
+ {
+ gdb_assert (data.result != NULL);
+ return data.result;
+ }
+ gdb_assert (gdbscm_is_exception (scm_result));
+ *except_scmp = scm_result;
+ return NULL;
+}
+
+/* Struct to pass data from gdbscm_scm_from_string to
+ gdbscm_call_scm_from_stringn. */
+
+struct scm_from_stringn_data
+{
+ const char *string;
+ size_t len;
+ const char *charset;
+ int conversion_kind;
+ SCM result;
+};
+
+/* Helper for gdbscm_scm_from_string to call scm_from_stringn
+ from within scm_c_catch. */
+
+static SCM
+gdbscm_call_scm_from_stringn (void *datap)
+{
+ struct scm_from_stringn_data *data = datap;
+
+ data->result = scm_from_stringn (data->string, data->len, data->charset,
+ data->conversion_kind);
+ return SCM_BOOL_F;
+}
+
+/* Convert STRING to a Scheme string in charset CHARSET.
+ This function is guaranteed to not throw an exception.
+ If STRICT is non-zero, and there's a conversion error, then a
+ <gdb:exception> object is returned.
+ If STRICT is zero, then question marks are used for characters that
+ can't be converted (limitation of underlying Guile conversion support). */
+
+SCM
+gdbscm_scm_from_string (const char *string, size_t len,
+ const char *charset, int strict)
+{
+ struct scm_from_stringn_data data;
+ SCM scm_result;
+
+ data.string = string;
+ data.len = len;
+ data.charset = charset;
+ /* The use of SCM_FAILED_CONVERSION_QUESTION_MARK is specified by Guile. */
+ data.conversion_kind = (strict
+ ? SCM_FAILED_CONVERSION_ERROR
+ : SCM_FAILED_CONVERSION_QUESTION_MARK);
+ data.result = SCM_UNDEFINED;
+
+ scm_result = gdbscm_call_guile (gdbscm_call_scm_from_stringn, &data, NULL);
+
+ if (gdbscm_is_false (scm_result))
+ {
+ gdb_assert (!SCM_UNBNDP (data.result));
+ return data.result;
+ }
+ gdb_assert (gdbscm_is_exception (scm_result));
+ return scm_result;
+}
+
+/* Convert an SCM string to a target string.
+ This function will thrown a conversion error if there's a problem.
+ Space for the result is allocated with malloc, caller must free.
+ It is an error to call this if STRING is not a string. */
+
+char *
+gdbscm_scm_to_target_string_unsafe (SCM string, size_t *lenp,
+ struct gdbarch *gdbarch)
+{
+ return scm_to_stringn (string, lenp, target_charset (gdbarch),
+ SCM_FAILED_CONVERSION_ERROR);
+}
+
+/* (string->argv string) -> list
+ Return list of strings split up according to GDB's argv parsing rules.
+ This is useful when writing GDB commands in Scheme. */
+
+static SCM
+gdbscm_string_to_argv (SCM string_scm)
+{
+ char *string;
+ char **c_argv;
+ int i;
+ SCM result = SCM_EOL;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "s",
+ string_scm, &string);
+
+ if (string == NULL || *string == '\0')
+ {
+ xfree (string);
+ return SCM_EOL;
+ }
+
+ c_argv = gdb_buildargv (string);
+ for (i = 0; c_argv[i] != NULL; ++i)
+ result = scm_cons (gdbscm_scm_from_c_string (c_argv[i]), result);
+
+ freeargv (c_argv);
+ xfree (string);
+
+ return scm_reverse_x (result, SCM_EOL);
+}
+
+/* Initialize the Scheme charset interface to GDB. */
+
+static const scheme_function string_functions[] =
+{
+ { "string->argv", 1, 0, 0, gdbscm_string_to_argv,
+ "\
+Convert a string to a list of strings split up according to\n\
+gdb's argv parsing rules." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_strings (void)
+{
+ gdbscm_define_functions (string_functions, 1);
+}
diff --git a/gdb/guile/scm-symbol.c b/gdb/guile/scm-symbol.c
new file mode 100644
index 0000000..53cc272
--- /dev/null
+++ b/gdb/guile/scm-symbol.c
@@ -0,0 +1,777 @@
+/* Scheme interface to symbols.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "block.h"
+#include "exceptions.h"
+#include "frame.h"
+#include "symtab.h"
+#include "objfiles.h"
+#include "value.h"
+#include "guile-internal.h"
+
+/* The <gdb:symbol> smob. */
+
+typedef struct
+{
+ /* This always appears first. */
+ eqable_gdb_smob base;
+
+ /* The GDB symbol structure this smob is wrapping. */
+ struct symbol *symbol;
+} symbol_smob;
+
+static const char symbol_smob_name[] = "gdb:symbol";
+
+/* The tag Guile knows the symbol smob by. */
+static scm_t_bits symbol_smob_tag;
+
+/* Keywords used in argument passing. */
+static SCM block_keyword;
+static SCM domain_keyword;
+static SCM frame_keyword;
+
+static const struct objfile_data *syscm_objfile_data_key;
+
+/* Administrivia for symbol smobs. */
+
+/* Helper function to hash a symbol_smob. */
+
+static hashval_t
+syscm_hash_symbol_smob (const void *p)
+{
+ const symbol_smob *s_smob = p;
+
+ return htab_hash_pointer (s_smob->symbol);
+}
+
+/* Helper function to compute equality of symbol_smobs. */
+
+static int
+syscm_eq_symbol_smob (const void *ap, const void *bp)
+{
+ const symbol_smob *a = ap;
+ const symbol_smob *b = bp;
+
+ return (a->symbol == b->symbol
+ && a->symbol != NULL);
+}
+
+/* Return the struct symbol pointer -> SCM mapping table.
+ It is created if necessary. */
+
+static htab_t
+syscm_objfile_symbol_map (struct symbol *symbol)
+{
+ struct objfile *objfile = SYMBOL_SYMTAB (symbol)->objfile;
+ htab_t htab = objfile_data (objfile, syscm_objfile_data_key);
+
+ if (htab == NULL)
+ {
+ htab = gdbscm_create_eqable_gsmob_ptr_map (syscm_hash_symbol_smob,
+ syscm_eq_symbol_smob);
+ set_objfile_data (objfile, syscm_objfile_data_key, htab);
+ }
+
+ return htab;
+}
+
+/* The smob "mark" function for <gdb:symbol>. */
+
+static SCM
+syscm_mark_symbol_smob (SCM self)
+{
+ symbol_smob *s_smob = (symbol_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_eqable_gsmob (&s_smob->base);
+}
+
+/* The smob "free" function for <gdb:symbol>. */
+
+static size_t
+syscm_free_symbol_smob (SCM self)
+{
+ symbol_smob *s_smob = (symbol_smob *) SCM_SMOB_DATA (self);
+
+ if (s_smob->symbol != NULL)
+ {
+ htab_t htab = syscm_objfile_symbol_map (s_smob->symbol);
+
+ gdbscm_clear_eqable_gsmob_ptr_slot (htab, &s_smob->base);
+ }
+
+ /* Not necessary, done to catch bugs. */
+ s_smob->symbol = NULL;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:symbol>. */
+
+static int
+syscm_print_symbol_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ symbol_smob *s_smob = (symbol_smob *) SCM_SMOB_DATA (self);
+
+ if (pstate->writingp)
+ gdbscm_printf (port, "#<%s ", symbol_smob_name);
+ gdbscm_printf (port, "%s",
+ s_smob->symbol != NULL
+ ? SYMBOL_PRINT_NAME (s_smob->symbol)
+ : "<invalid>");
+ if (pstate->writingp)
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:symbol> object. */
+
+static SCM
+syscm_make_symbol_smob (void)
+{
+ symbol_smob *s_smob = (symbol_smob *)
+ scm_gc_malloc (sizeof (symbol_smob), symbol_smob_name);
+ SCM s_scm;
+
+ s_smob->symbol = NULL;
+ s_scm = scm_new_smob (symbol_smob_tag, (scm_t_bits) s_smob);
+ gdbscm_init_eqable_gsmob (&s_smob->base);
+
+ return s_scm;
+}
+
+/* Return non-zero if SCM is a symbol smob. */
+
+int
+syscm_is_symbol (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (symbol_smob_tag, scm);
+}
+
+/* (symbol? object) -> boolean */
+
+static SCM
+gdbscm_symbol_p (SCM scm)
+{
+ return scm_from_bool (syscm_is_symbol (scm));
+}
+
+/* Return the existing object that encapsulates SYMBOL, or create a new
+ <gdb:symbol> object. */
+
+SCM
+syscm_scm_from_symbol (struct symbol *symbol)
+{
+ htab_t htab;
+ eqable_gdb_smob **slot;
+ symbol_smob *s_smob, s_smob_for_lookup;
+ SCM s_scm;
+
+ /* If we've already created a gsmob for this symbol, return it.
+ This makes symbols eq?-able. */
+ htab = syscm_objfile_symbol_map (symbol);
+ s_smob_for_lookup.symbol = symbol;
+ slot = gdbscm_find_eqable_gsmob_ptr_slot (htab, &s_smob_for_lookup.base);
+ if (*slot != NULL)
+ return (*slot)->containing_scm;
+
+ s_scm = syscm_make_symbol_smob ();
+ s_smob = (symbol_smob *) SCM_SMOB_DATA (s_scm);
+ s_smob->symbol = symbol;
+ gdbscm_fill_eqable_gsmob_ptr_slot (slot, &s_smob->base, s_scm);
+
+ return s_scm;
+}
+
+/* Returns the <gdb:symbol> object in SELF.
+ Throws an exception if SELF is not a <gdb:symbol> object. */
+
+static SCM
+syscm_get_symbol_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (syscm_is_symbol (self), self, arg_pos, func_name,
+ symbol_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the symbol smob of SELF.
+ Throws an exception if SELF is not a <gdb:symbol> object. */
+
+static symbol_smob *
+syscm_get_symbol_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM s_scm = syscm_get_symbol_arg_unsafe (self, arg_pos, func_name);
+ symbol_smob *s_smob = (symbol_smob *) SCM_SMOB_DATA (s_scm);
+
+ return s_smob;
+}
+
+/* Return non-zero if symbol S_SMOB is valid. */
+
+static int
+syscm_is_valid (symbol_smob *s_smob)
+{
+ return s_smob->symbol != NULL;
+}
+
+/* Throw a Scheme error if SELF is not a valid symbol smob.
+ Otherwise return a pointer to the symbol smob. */
+
+static symbol_smob *
+syscm_get_valid_symbol_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ symbol_smob *s_smob
+ = syscm_get_symbol_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!syscm_is_valid (s_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:symbol>"));
+ }
+
+ return s_smob;
+}
+
+/* Throw a Scheme error if SELF is not a valid symbol smob.
+ Otherwise return a pointer to the symbol struct. */
+
+struct symbol *
+syscm_get_valid_symbol_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ symbol_smob *s_smob = syscm_get_valid_symbol_smob_arg_unsafe (self, arg_pos,
+ func_name);
+
+ return s_smob->symbol;
+}
+
+/* Helper function for syscm_del_objfile_symbols to mark the symbol
+ as invalid. */
+
+static int
+syscm_mark_symbol_invalid (void **slot, void *info)
+{
+ symbol_smob *s_smob = (symbol_smob *) *slot;
+
+ s_smob->symbol = NULL;
+ return 1;
+}
+
+/* This function is called when an objfile is about to be freed.
+ Invalidate the symbol as further actions on the symbol would result
+ in bad data. All access to s_smob->symbol should be gated by
+ syscm_get_valid_symbol_smob_arg_unsafe which will raise an exception on
+ invalid symbols. */
+
+static void
+syscm_del_objfile_symbols (struct objfile *objfile, void *datum)
+{
+ htab_t htab = datum;
+
+ if (htab != NULL)
+ {
+ htab_traverse_noresize (htab, syscm_mark_symbol_invalid, NULL);
+ htab_delete (htab);
+ }
+}
+
+/* Symbol methods. */
+
+/* (symbol-valid? <gdb:symbol>) -> boolean
+ Returns #t if SELF still exists in GDB. */
+
+static SCM
+gdbscm_symbol_valid_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (syscm_is_valid (s_smob));
+}
+
+/* (symbol-type <gdb:symbol>) -> <gdb:type>
+ Return the type of SELF, or #f if SELF has no type. */
+
+static SCM
+gdbscm_symbol_type (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ if (SYMBOL_TYPE (symbol) == NULL)
+ return SCM_BOOL_F;
+
+ return tyscm_scm_from_type (SYMBOL_TYPE (symbol));
+}
+
+/* (symbol-symtab <gdb:symbol>) -> <gdb:symtab>
+ Return the symbol table of SELF. */
+
+static SCM
+gdbscm_symbol_symtab (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return stscm_scm_from_symtab (SYMBOL_SYMTAB (symbol));
+}
+
+/* (symbol-name <gdb:symbol>) -> string */
+
+static SCM
+gdbscm_symbol_name (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return gdbscm_scm_from_c_string (SYMBOL_NATURAL_NAME (symbol));
+}
+
+/* (symbol-linkage-name <gdb:symbol>) -> string */
+
+static SCM
+gdbscm_symbol_linkage_name (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return gdbscm_scm_from_c_string (SYMBOL_LINKAGE_NAME (symbol));
+}
+
+/* (symbol-print-name <gdb:symbol>) -> string */
+
+static SCM
+gdbscm_symbol_print_name (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return gdbscm_scm_from_c_string (SYMBOL_PRINT_NAME (symbol));
+}
+
+/* (symbol-addr-class <gdb:symbol>) -> integer */
+
+static SCM
+gdbscm_symbol_addr_class (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return scm_from_int (SYMBOL_CLASS (symbol));
+}
+
+/* (symbol-argument? <gdb:symbol>) -> boolean */
+
+static SCM
+gdbscm_symbol_argument_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return scm_from_bool (SYMBOL_IS_ARGUMENT (symbol));
+}
+
+/* (symbol-constant? <gdb:symbol>) -> boolean */
+
+static SCM
+gdbscm_symbol_constant_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+ enum address_class class;
+
+ class = SYMBOL_CLASS (symbol);
+
+ return scm_from_bool (class == LOC_CONST || class == LOC_CONST_BYTES);
+}
+
+/* (symbol-function? <gdb:symbol>) -> boolean */
+
+static SCM
+gdbscm_symbol_function_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+ enum address_class class;
+
+ class = SYMBOL_CLASS (symbol);
+
+ return scm_from_bool (class == LOC_BLOCK);
+}
+
+/* (symbol-variable? <gdb:symbol>) -> boolean */
+
+static SCM
+gdbscm_symbol_variable_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+ enum address_class class;
+
+ class = SYMBOL_CLASS (symbol);
+
+ return scm_from_bool (!SYMBOL_IS_ARGUMENT (symbol)
+ && (class == LOC_LOCAL || class == LOC_REGISTER
+ || class == LOC_STATIC || class == LOC_COMPUTED
+ || class == LOC_OPTIMIZED_OUT));
+}
+
+/* (symbol-needs-frame? <gdb:symbol>) -> boolean
+ Return #t if the symbol needs a frame for evaluation. */
+
+static SCM
+gdbscm_symbol_needs_frame_p (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct symbol *symbol = s_smob->symbol;
+ volatile struct gdb_exception except;
+ int result = 0;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ result = symbol_read_needs_frame (symbol);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (result);
+}
+
+/* (symbol-line <gdb:symbol>) -> integer
+ Return the line number at which the symbol was defined. */
+
+static SCM
+gdbscm_symbol_line (SCM self)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symbol *symbol = s_smob->symbol;
+
+ return scm_from_int (SYMBOL_LINE (symbol));
+}
+
+/* (symbol-value <gdb:symbol> [#:frame <gdb:frame>]) -> <gdb:value>
+ Return the value of the symbol, or an error in various circumstances. */
+
+static SCM
+gdbscm_symbol_value (SCM self, SCM rest)
+{
+ symbol_smob *s_smob
+ = syscm_get_valid_symbol_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct symbol *symbol = s_smob->symbol;
+ SCM keywords[] = { frame_keyword, SCM_BOOL_F };
+ int frame_pos = -1;
+ SCM frame_scm = SCM_BOOL_F;
+ frame_smob *f_smob = NULL;
+ struct frame_info *frame_info = NULL;
+ struct value *value = NULL;
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "#O",
+ rest, &frame_pos, &frame_scm);
+ if (!gdbscm_is_false (frame_scm))
+ f_smob = frscm_get_frame_smob_arg_unsafe (frame_scm, frame_pos, FUNC_NAME);
+
+ if (SYMBOL_CLASS (symbol) == LOC_TYPEDEF)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _("cannot get the value of a typedef"));
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (f_smob != NULL)
+ {
+ frame_info = frscm_frame_smob_to_frame (f_smob);
+ if (frame_info == NULL)
+ error (_("Invalid frame"));
+ }
+
+ if (symbol_read_needs_frame (symbol) && frame_info == NULL)
+ error (_("Symbol requires a frame to compute its value"));
+
+ value = read_var_value (symbol, frame_info);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return vlscm_scm_from_value (value);
+}
+
+/* (lookup-symbol name [#:block <gdb:block>] [#:domain domain])
+ -> (<gdb:symbol> field-of-this?)
+ The result is #f if the symbol is not found.
+ See comment in lookup_symbol_in_language for field-of-this?. */
+
+static SCM
+gdbscm_lookup_symbol (SCM name_scm, SCM rest)
+{
+ char *name;
+ SCM keywords[] = { block_keyword, domain_keyword, SCM_BOOL_F };
+ const struct block *block = NULL;
+ SCM block_scm = SCM_BOOL_F;
+ int domain = VAR_DOMAIN;
+ int block_arg_pos = -1, domain_arg_pos = -1;
+ struct field_of_this_result is_a_field_of_this;
+ struct symbol *symbol = NULL;
+ volatile struct gdb_exception except;
+ struct cleanup *cleanups;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#Oi",
+ name_scm, &name, rest,
+ &block_arg_pos, &block_scm,
+ &domain_arg_pos, &domain);
+
+ cleanups = make_cleanup (xfree, name);
+
+ if (block_arg_pos >= 0)
+ {
+ SCM except_scm;
+
+ block = bkscm_scm_to_block (block_scm, block_arg_pos, FUNC_NAME,
+ &except_scm);
+ if (block == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+ }
+ else
+ {
+ struct frame_info *selected_frame;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ selected_frame = get_selected_frame (_("no frame selected"));
+ block = get_frame_block (selected_frame, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ symbol = lookup_symbol (name, block, domain, &is_a_field_of_this);
+ }
+ do_cleanups (cleanups);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (symbol == NULL)
+ return SCM_BOOL_F;
+
+ return scm_list_2 (syscm_scm_from_symbol (symbol),
+ scm_from_bool (is_a_field_of_this.type != NULL));
+}
+
+/* (lookup-global-symbol name [#:domain domain]) -> <gdb:symbol>
+ The result is #f if the symbol is not found. */
+
+static SCM
+gdbscm_lookup_global_symbol (SCM name_scm, SCM rest)
+{
+ char *name;
+ SCM keywords[] = { domain_keyword, SCM_BOOL_F };
+ int domain_arg_pos = -1;
+ int domain = VAR_DOMAIN;
+ struct symbol *symbol = NULL;
+ volatile struct gdb_exception except;
+ struct cleanup *cleanups;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#i",
+ name_scm, &name, rest,
+ &domain_arg_pos, &domain);
+
+ cleanups = make_cleanup (xfree, name);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ symbol = lookup_symbol_global (name, NULL, domain);
+ }
+ do_cleanups (cleanups);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (symbol == NULL)
+ return SCM_BOOL_F;
+
+ return syscm_scm_from_symbol (symbol);
+}
+
+/* Initialize the Scheme symbol support. */
+
+/* Note: The SYMBOL_ prefix on the integer constants here is present for
+ compatibility with the Python support. */
+
+static const scheme_integer_constant symbol_integer_constants[] =
+{
+#define X(SYM) { "SYMBOL_" #SYM, SYM }
+ X (LOC_UNDEF),
+ X (LOC_CONST),
+ X (LOC_STATIC),
+ X (LOC_REGISTER),
+ X (LOC_ARG),
+ X (LOC_REF_ARG),
+ X (LOC_LOCAL),
+ X (LOC_TYPEDEF),
+ X (LOC_LABEL),
+ X (LOC_BLOCK),
+ X (LOC_CONST_BYTES),
+ X (LOC_UNRESOLVED),
+ X (LOC_OPTIMIZED_OUT),
+ X (LOC_COMPUTED),
+ X (LOC_REGPARM_ADDR),
+
+ X (UNDEF_DOMAIN),
+ X (VAR_DOMAIN),
+ X (STRUCT_DOMAIN),
+ X (LABEL_DOMAIN),
+ X (VARIABLES_DOMAIN),
+ X (FUNCTIONS_DOMAIN),
+ X (TYPES_DOMAIN),
+#undef X
+
+ END_INTEGER_CONSTANTS
+};
+
+static const scheme_function symbol_functions[] =
+{
+ { "symbol?", 1, 0, 0, gdbscm_symbol_p,
+ "\
+Return #t if the object is a <gdb:symbol> object." },
+
+ { "symbol-valid?", 1, 0, 0, gdbscm_symbol_valid_p,
+ "\
+Return #t if object is a valid <gdb:symbol> object.\n\
+A valid symbol is a symbol that has not been freed.\n\
+Symbols are freed when the objfile they come from is freed." },
+
+ { "symbol-type", 1, 0, 0, gdbscm_symbol_type,
+ "\
+Return the type of symbol." },
+
+ { "symbol-symtab", 1, 0, 0, gdbscm_symbol_symtab,
+ "\
+Return the symbol table (<gdb:symtab>) containing symbol." },
+
+ { "symbol-line", 1, 0, 0, gdbscm_symbol_line,
+ "\
+Return the line number at which the symbol was defined." },
+
+ { "symbol-name", 1, 0, 0, gdbscm_symbol_name,
+ "\
+Return the name of the symbol as a string." },
+
+ { "symbol-linkage-name", 1, 0, 0, gdbscm_symbol_linkage_name,
+ "\
+Return the linkage name of the symbol as a string." },
+
+ { "symbol-print-name", 1, 0, 0, gdbscm_symbol_print_name,
+ "\
+Return the print name of the symbol as a string.\n\
+This is either name or linkage-name, depending on whether the user\n\
+asked GDB to display demangled or mangled names." },
+
+ { "symbol-addr-class", 1, 0, 0, gdbscm_symbol_addr_class,
+ "\
+Return the address class of the symbol." },
+
+ { "symbol-needs-frame?", 1, 0, 0, gdbscm_symbol_needs_frame_p,
+ "\
+Return #t if the symbol needs a frame to compute its value." },
+
+ { "symbol-argument?", 1, 0, 0, gdbscm_symbol_argument_p,
+ "\
+Return #t if the symbol is a function argument." },
+
+ { "symbol-constant?", 1, 0, 0, gdbscm_symbol_constant_p,
+ "\
+Return #t if the symbol is a constant." },
+
+ { "symbol-function?", 1, 0, 0, gdbscm_symbol_function_p,
+ "\
+Return #t if the symbol is a function." },
+
+ { "symbol-variable?", 1, 0, 0, gdbscm_symbol_variable_p,
+ "\
+Return #t if the symbol is a variable." },
+
+ { "symbol-value", 1, 0, 1, gdbscm_symbol_value,
+ "\
+Return the value of the symbol.\n\
+\n\
+ Arguments: <gdb:symbol> [#:frame frame]" },
+
+ { "lookup-symbol", 1, 0, 1, gdbscm_lookup_symbol,
+ "\
+Return (<gdb:symbol> field-of-this?) if found, otherwise #f.\n\
+\n\
+ Arguments: name [#:block block] [#:domain domain]\n\
+ name: a string containing the name of the symbol to lookup\n\
+ block: a <gdb:block> object\n\
+ domain: a SYMBOL_*_DOMAIN value" },
+
+ { "lookup-global-symbol", 1, 0, 1, gdbscm_lookup_global_symbol,
+ "\
+Return <gdb:symbol> if found, otherwise #f.\n\
+\n\
+ Arguments: name [#:domain domain]\n\
+ name: a string containing the name of the symbol to lookup\n\
+ domain: a SYMBOL_*_DOMAIN value" },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_symbols (void)
+{
+ symbol_smob_tag
+ = gdbscm_make_smob_type (symbol_smob_name, sizeof (symbol_smob));
+ scm_set_smob_mark (symbol_smob_tag, syscm_mark_symbol_smob);
+ scm_set_smob_free (symbol_smob_tag, syscm_free_symbol_smob);
+ scm_set_smob_print (symbol_smob_tag, syscm_print_symbol_smob);
+
+ gdbscm_define_integer_constants (symbol_integer_constants, 1);
+ gdbscm_define_functions (symbol_functions, 1);
+
+ block_keyword = scm_from_latin1_keyword ("block");
+ domain_keyword = scm_from_latin1_keyword ("domain");
+ frame_keyword = scm_from_latin1_keyword ("frame");
+
+ /* Register an objfile "free" callback so we can properly
+ invalidate symbols when an object file is about to be deleted. */
+ syscm_objfile_data_key
+ = register_objfile_data_with_cleanup (NULL, syscm_del_objfile_symbols);
+}
diff --git a/gdb/guile/scm-symtab.c b/gdb/guile/scm-symtab.c
new file mode 100644
index 0000000..910d8b7
--- /dev/null
+++ b/gdb/guile/scm-symtab.c
@@ -0,0 +1,735 @@
+/* Scheme interface to symbol tables.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "source.h"
+#include "objfiles.h"
+#include "block.h"
+#include "guile-internal.h"
+
+/* A <gdb:symtab> smob. */
+
+typedef struct
+{
+ /* This always appears first.
+ eqable_gdb_smob is used so that symtabs are eq?-able.
+ Also, a symtab object is associated with an objfile. eqable_gdb_smob
+ lets us track the lifetime of all symtabs associated with an objfile.
+ When an objfile is deleted we need to invalidate the symtab object. */
+ eqable_gdb_smob base;
+
+ /* The GDB symbol table structure.
+ If this is NULL the symtab is invalid. This can happen when the
+ underlying objfile is freed. */
+ struct symtab *symtab;
+} symtab_smob;
+
+/* A <gdb:sal> smob.
+ A smob describing a gdb symtab-and-line object.
+ A sal is associated with an objfile. All access must be gated by checking
+ the validity of symtab_scm.
+ TODO: Sals are not eq?-able at the moment, or even comparable. */
+
+typedef struct
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The <gdb:symtab> object of the symtab.
+ We store this instead of a pointer to the symtab_smob because it's not
+ clear GC will know the symtab_smob is referenced by us otherwise, and we
+ need quick access to symtab_smob->symtab to know if this sal is valid. */
+ SCM symtab_scm;
+
+ /* The GDB symbol table and line structure.
+ This object is ephemeral in GDB, so keep our own copy.
+ The symtab pointer in this struct is not usable: If the symtab is deleted
+ this pointer will not be updated. Use symtab_scm instead to determine
+ if this sal is valid. */
+ struct symtab_and_line sal;
+} sal_smob;
+
+static const char symtab_smob_name[] = "gdb:symtab";
+/* "symtab-and-line" is pretty long, and "sal" is short and unique. */
+static const char sal_smob_name[] = "gdb:sal";
+
+/* The tags Guile knows the symbol table smobs by. */
+static scm_t_bits symtab_smob_tag;
+static scm_t_bits sal_smob_tag;
+
+static const struct objfile_data *stscm_objfile_data_key;
+
+/* Administrivia for symtab smobs. */
+
+/* Helper function to hash a symbol_smob. */
+
+static hashval_t
+stscm_hash_symtab_smob (const void *p)
+{
+ const symtab_smob *st_smob = p;
+
+ return htab_hash_pointer (st_smob->symtab);
+}
+
+/* Helper function to compute equality of symtab_smobs. */
+
+static int
+stscm_eq_symtab_smob (const void *ap, const void *bp)
+{
+ const symtab_smob *a = ap;
+ const symtab_smob *b = bp;
+
+ return (a->symtab == b->symtab
+ && a->symtab != NULL);
+}
+
+/* Return the struct symtab pointer -> SCM mapping table.
+ It is created if necessary. */
+
+static htab_t
+stscm_objfile_symtab_map (struct symtab *symtab)
+{
+ struct objfile *objfile = symtab->objfile;
+ htab_t htab = objfile_data (objfile, stscm_objfile_data_key);
+
+ if (htab == NULL)
+ {
+ htab = gdbscm_create_eqable_gsmob_ptr_map (stscm_hash_symtab_smob,
+ stscm_eq_symtab_smob);
+ set_objfile_data (objfile, stscm_objfile_data_key, htab);
+ }
+
+ return htab;
+}
+
+/* The smob "mark" function for <gdb:symtab>. */
+
+static SCM
+stscm_mark_symtab_smob (SCM self)
+{
+ symtab_smob *st_smob = (symtab_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_eqable_gsmob (&st_smob->base);
+}
+
+/* The smob "free" function for <gdb:symtab>. */
+
+static size_t
+stscm_free_symtab_smob (SCM self)
+{
+ symtab_smob *st_smob = (symtab_smob *) SCM_SMOB_DATA (self);
+
+ if (st_smob->symtab != NULL)
+ {
+ htab_t htab = stscm_objfile_symtab_map (st_smob->symtab);
+
+ gdbscm_clear_eqable_gsmob_ptr_slot (htab, &st_smob->base);
+ }
+
+ /* Not necessary, done to catch bugs. */
+ st_smob->symtab = NULL;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:symtab>. */
+
+static int
+stscm_print_symtab_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ symtab_smob *st_smob = (symtab_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", symtab_smob_name);
+ gdbscm_printf (port, "%s",
+ st_smob->symtab != NULL
+ ? symtab_to_filename_for_display (st_smob->symtab)
+ : "<invalid>");
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:symtab> object. */
+
+static SCM
+stscm_make_symtab_smob (void)
+{
+ symtab_smob *st_smob = (symtab_smob *)
+ scm_gc_malloc (sizeof (symtab_smob), symtab_smob_name);
+ SCM st_scm;
+
+ st_smob->symtab = NULL;
+ st_scm = scm_new_smob (symtab_smob_tag, (scm_t_bits) st_smob);
+ gdbscm_init_eqable_gsmob (&st_smob->base);
+
+ return st_scm;
+}
+
+/* Return non-zero if SCM is a symbol table smob. */
+
+static int
+stscm_is_symtab (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (symtab_smob_tag, scm);
+}
+
+/* (symtab? object) -> boolean */
+
+static SCM
+gdbscm_symtab_p (SCM scm)
+{
+ return scm_from_bool (stscm_is_symtab (scm));
+}
+
+/* Create a new <gdb:symtab> object that encapsulates SYMTAB. */
+
+SCM
+stscm_scm_from_symtab (struct symtab *symtab)
+{
+ htab_t htab;
+ eqable_gdb_smob **slot;
+ symtab_smob *st_smob, st_smob_for_lookup;
+ SCM st_scm;
+
+ /* If we've already created a gsmob for this symtab, return it.
+ This makes symtabs eq?-able. */
+ htab = stscm_objfile_symtab_map (symtab);
+ st_smob_for_lookup.symtab = symtab;
+ slot = gdbscm_find_eqable_gsmob_ptr_slot (htab, &st_smob_for_lookup.base);
+ if (*slot != NULL)
+ return (*slot)->containing_scm;
+
+ st_scm = stscm_make_symtab_smob ();
+ st_smob = (symtab_smob *) SCM_SMOB_DATA (st_scm);
+ st_smob->symtab = symtab;
+ gdbscm_fill_eqable_gsmob_ptr_slot (slot, &st_smob->base, st_scm);
+
+ return st_scm;
+}
+
+/* Returns the <gdb:symtab> object in SELF.
+ Throws an exception if SELF is not a <gdb:symtab> object. */
+
+static SCM
+stscm_get_symtab_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (stscm_is_symtab (self), self, arg_pos, func_name,
+ symtab_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the symtab smob of SELF.
+ Throws an exception if SELF is not a <gdb:symtab> object. */
+
+static symtab_smob *
+stscm_get_symtab_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM st_scm = stscm_get_symtab_arg_unsafe (self, arg_pos, func_name);
+ symtab_smob *st_smob = (symtab_smob *) SCM_SMOB_DATA (st_scm);
+
+ return st_smob;
+}
+
+/* Return non-zero if symtab ST_SMOB is valid. */
+
+static int
+stscm_is_valid (symtab_smob *st_smob)
+{
+ return st_smob->symtab != NULL;
+}
+
+/* Throw a Scheme error if SELF is not a valid symtab smob.
+ Otherwise return a pointer to the symtab_smob object. */
+
+static symtab_smob *
+stscm_get_valid_symtab_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ symtab_smob *st_smob
+ = stscm_get_symtab_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!stscm_is_valid (st_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:symtab>"));
+ }
+
+ return st_smob;
+}
+
+/* Helper function for stscm_del_objfile_symtabs to mark the symtab
+ as invalid. */
+
+static int
+stscm_mark_symtab_invalid (void **slot, void *info)
+{
+ symtab_smob *st_smob = (symtab_smob *) *slot;
+
+ st_smob->symtab = NULL;
+ return 1;
+}
+
+/* This function is called when an objfile is about to be freed.
+ Invalidate the symbol table as further actions on the symbol table
+ would result in bad data. All access to st_smob->symtab should be
+ gated by stscm_get_valid_symtab_smob_arg_unsafe which will raise an
+ exception on invalid symbol tables. */
+
+static void
+stscm_del_objfile_symtabs (struct objfile *objfile, void *datum)
+{
+ htab_t htab = datum;
+
+ if (htab != NULL)
+ {
+ htab_traverse_noresize (htab, stscm_mark_symtab_invalid, NULL);
+ htab_delete (htab);
+ }
+}
+
+/* Symbol table methods. */
+
+/* (symtab-valid? <gdb:symtab>) -> boolean
+ Returns #t if SELF still exists in GDB. */
+
+static SCM
+gdbscm_symtab_valid_p (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (stscm_is_valid (st_smob));
+}
+
+/* (symtab-filename <gdb:symtab>) -> string */
+
+static SCM
+gdbscm_symtab_filename (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_valid_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct symtab *symtab = st_smob->symtab;
+
+ return gdbscm_scm_from_c_string (symtab_to_filename_for_display (symtab));
+}
+
+/* (symtab-fullname <gdb:symtab>) -> string */
+
+static SCM
+gdbscm_symtab_fullname (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_valid_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct symtab *symtab = st_smob->symtab;
+
+ return gdbscm_scm_from_c_string (symtab_to_fullname (symtab));
+}
+
+/* (symtab-objfile <gdb:symtab>) -> <gdb:objfile> */
+
+static SCM
+gdbscm_symtab_objfile (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_valid_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab *symtab = st_smob->symtab;
+
+ return ofscm_scm_from_objfile (symtab->objfile);
+}
+
+/* (symtab-global-block <gdb:symtab>) -> <gdb:block>
+ Return the GLOBAL_BLOCK of the underlying symtab. */
+
+static SCM
+gdbscm_symtab_global_block (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_valid_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab *symtab = st_smob->symtab;
+ const struct blockvector *blockvector;
+ const struct block *block;
+
+ blockvector = BLOCKVECTOR (symtab);
+ block = BLOCKVECTOR_BLOCK (blockvector, GLOBAL_BLOCK);
+
+ return bkscm_scm_from_block (block, symtab->objfile);
+}
+
+/* (symtab-static-block <gdb:symtab>) -> <gdb:block>
+ Return the STATIC_BLOCK of the underlying symtab. */
+
+static SCM
+gdbscm_symtab_static_block (SCM self)
+{
+ symtab_smob *st_smob
+ = stscm_get_valid_symtab_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab *symtab = st_smob->symtab;
+ const struct blockvector *blockvector;
+ const struct block *block;
+
+ blockvector = BLOCKVECTOR (symtab);
+ block = BLOCKVECTOR_BLOCK (blockvector, STATIC_BLOCK);
+
+ return bkscm_scm_from_block (block, symtab->objfile);
+}
+
+/* Administrivia for sal (symtab-and-line) smobs. */
+
+/* The smob "mark" function for <gdb:sal>. */
+
+static SCM
+stscm_mark_sal_smob (SCM self)
+{
+ sal_smob *s_smob = (sal_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (s_smob->symtab_scm);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&s_smob->base);
+}
+
+/* The smob "free" function for <gdb:sal>. */
+
+static size_t
+stscm_free_sal_smob (SCM self)
+{
+ sal_smob *s_smob = (sal_smob *) SCM_SMOB_DATA (self);
+
+ /* Not necessary, done to catch bugs. */
+ s_smob->symtab_scm = SCM_UNDEFINED;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:sal>. */
+
+static int
+stscm_print_sal_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ sal_smob *s_smob = (sal_smob *) SCM_SMOB_DATA (self);
+ symtab_smob *st_smob = (symtab_smob *) SCM_SMOB_DATA (s_smob->symtab_scm);
+
+ gdbscm_printf (port, "#<%s ", symtab_smob_name);
+ scm_write (s_smob->symtab_scm, port);
+ if (s_smob->sal.line != 0)
+ gdbscm_printf (port, " line %d", s_smob->sal.line);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:sal> object. */
+
+static SCM
+stscm_make_sal_smob (void)
+{
+ sal_smob *s_smob
+ = (sal_smob *) scm_gc_malloc (sizeof (sal_smob), sal_smob_name);
+ SCM s_scm;
+
+ s_smob->symtab_scm = SCM_BOOL_F;
+ memset (&s_smob->sal, 0, sizeof (s_smob->sal));
+ s_scm = scm_new_smob (sal_smob_tag, (scm_t_bits) s_smob);
+ gdbscm_init_gsmob (&s_smob->base);
+
+ return s_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:sal> object. */
+
+static int
+stscm_is_sal (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (sal_smob_tag, scm);
+}
+
+/* (sal? object) -> boolean */
+
+static SCM
+gdbscm_sal_p (SCM scm)
+{
+ return scm_from_bool (stscm_is_sal (scm));
+}
+
+/* Create a new <gdb:sal> object that encapsulates SAL. */
+
+SCM
+stscm_scm_from_sal (struct symtab_and_line sal)
+{
+ SCM st_scm, s_scm;
+ sal_smob *s_smob;
+
+ st_scm = SCM_BOOL_F;
+ if (sal.symtab != NULL)
+ st_scm = stscm_scm_from_symtab (sal.symtab);
+
+ s_scm = stscm_make_sal_smob ();
+ s_smob = (sal_smob *) SCM_SMOB_DATA (s_scm);
+ s_smob->symtab_scm = st_scm;
+ s_smob->sal = sal;
+
+ return s_scm;
+}
+
+/* Returns the <gdb:sal> object in SELF.
+ Throws an exception if SELF is not a <gdb:sal> object. */
+
+static SCM
+stscm_get_sal_arg (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (stscm_is_sal (self), self, arg_pos, func_name,
+ sal_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the sal smob of SELF.
+ Throws an exception if SELF is not a <gdb:sal> object. */
+
+static sal_smob *
+stscm_get_sal_smob_arg (SCM self, int arg_pos, const char *func_name)
+{
+ SCM s_scm = stscm_get_sal_arg (self, arg_pos, func_name);
+ sal_smob *s_smob = (sal_smob *) SCM_SMOB_DATA (s_scm);
+
+ return s_smob;
+}
+
+/* Return non-zero if the symtab in S_SMOB is valid. */
+
+static int
+stscm_sal_is_valid (sal_smob *s_smob)
+{
+ symtab_smob *st_smob;
+
+ /* If there's no symtab that's ok, the sal is still valid. */
+ if (gdbscm_is_false (s_smob->symtab_scm))
+ return 1;
+
+ st_smob = (symtab_smob *) SCM_SMOB_DATA (s_smob->symtab_scm);
+
+ return st_smob->symtab != NULL;
+}
+
+/* Throw a Scheme error if SELF is not a valid sal smob.
+ Otherwise return a pointer to the sal_smob object. */
+
+static sal_smob *
+stscm_get_valid_sal_smob_arg (SCM self, int arg_pos, const char *func_name)
+{
+ sal_smob *s_smob = stscm_get_sal_smob_arg (self, arg_pos, func_name);
+
+ if (!stscm_sal_is_valid (s_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:sal>"));
+ }
+
+ return s_smob;
+}
+
+/* sal methods */
+
+/* (sal-valid? <gdb:sal>) -> boolean
+ Returns #t if the symtab for SELF still exists in GDB. */
+
+static SCM
+gdbscm_sal_valid_p (SCM self)
+{
+ sal_smob *s_smob = stscm_get_sal_smob_arg (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (stscm_sal_is_valid (s_smob));
+}
+
+/* (sal-pc <gdb:sal>) -> address */
+
+static SCM
+gdbscm_sal_pc (SCM self)
+{
+ sal_smob *s_smob = stscm_get_valid_sal_smob_arg (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab_and_line *sal = &s_smob->sal;
+
+ return gdbscm_scm_from_ulongest (sal->pc);
+}
+
+/* (sal-last <gdb:sal>) -> address
+ Returns #f if no ending address is recorded. */
+
+static SCM
+gdbscm_sal_last (SCM self)
+{
+ sal_smob *s_smob = stscm_get_valid_sal_smob_arg (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab_and_line *sal = &s_smob->sal;
+
+ if (sal->end > 0)
+ return gdbscm_scm_from_ulongest (sal->end - 1);
+ return SCM_BOOL_F;
+}
+
+/* (sal-line <gdb:sal>) -> integer
+ Returns #f if no line number is recorded. */
+
+static SCM
+gdbscm_sal_line (SCM self)
+{
+ sal_smob *s_smob = stscm_get_valid_sal_smob_arg (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab_and_line *sal = &s_smob->sal;
+
+ if (sal->line > 0)
+ return scm_from_int (sal->line);
+ return SCM_BOOL_F;
+}
+
+/* (sal-symtab <gdb:sal>) -> <gdb:symtab>
+ Returns #f if no symtab is recorded. */
+
+static SCM
+gdbscm_sal_symtab (SCM self)
+{
+ sal_smob *s_smob = stscm_get_valid_sal_smob_arg (self, SCM_ARG1, FUNC_NAME);
+ const struct symtab_and_line *sal = &s_smob->sal;
+
+ return s_smob->symtab_scm;
+}
+
+/* (find-pc-line address) -> <gdb:sal> */
+
+static SCM
+gdbscm_find_pc_line (SCM pc_scm)
+{
+ ULONGEST pc_ull;
+ struct symtab_and_line sal;
+ volatile struct gdb_exception except;
+
+ init_sal (&sal); /* -Wall */
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "U", pc_scm, &pc_ull);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ CORE_ADDR pc = (CORE_ADDR) pc_ull;
+
+ sal = find_pc_line (pc, 0);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return stscm_scm_from_sal (sal);
+}
+
+/* Initialize the Scheme symbol support. */
+
+static const scheme_function symtab_functions[] =
+{
+ { "symtab?", 1, 0, 0, gdbscm_symtab_p,
+ "\
+Return #t if the object is a <gdb:symtab> object." },
+
+ { "symtab-valid?", 1, 0, 0, gdbscm_symtab_valid_p,
+ "\
+Return #t if the symtab still exists in GDB.\n\
+Symtabs are deleted when the corresponding objfile is freed." },
+
+ { "symtab-filename", 1, 0, 0, gdbscm_symtab_filename,
+ "\
+Return the symtab's source file name." },
+
+ { "symtab-fullname", 1, 0, 0, gdbscm_symtab_fullname,
+ "\
+Return the symtab's full source file name." },
+
+ { "symtab-objfile", 1, 0, 0, gdbscm_symtab_objfile,
+ "\
+Return the symtab's objfile." },
+
+ { "symtab-global-block", 1, 0, 0, gdbscm_symtab_global_block,
+ "\
+Return the symtab's global block." },
+
+ { "symtab-static-block", 1, 0, 0, gdbscm_symtab_static_block,
+ "\
+Return the symtab's static block." },
+
+ { "sal?", 1, 0, 0, gdbscm_sal_p,
+ "\
+Return #t if the object is a <gdb:sal> (symtab-and-line) object." },
+
+ { "sal-valid?", 1, 0, 0, gdbscm_sal_valid_p,
+ "\
+Return #t if the symtab for the sal still exists in GDB.\n\
+Symtabs are deleted when the corresponding objfile is freed." },
+
+ { "sal-symtab", 1, 0, 0, gdbscm_sal_symtab,
+ "\
+Return the sal's symtab." },
+
+ { "sal-line", 1, 0, 0, gdbscm_sal_line,
+ "\
+Return the sal's line number, or #f if there is none." },
+
+ { "sal-pc", 1, 0, 0, gdbscm_sal_pc,
+ "\
+Return the sal's address." },
+
+ { "sal-last", 1, 0, 0, gdbscm_sal_last,
+ "\
+Return the last address specified by the sal, or #f if there is none." },
+
+ { "find-pc-line", 1, 0, 0, gdbscm_find_pc_line,
+ "\
+Return the sal corresponding to the address, or #f if there isn't one.\n\
+\n\
+ Arguments: address" },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_symtabs (void)
+{
+ symtab_smob_tag
+ = gdbscm_make_smob_type (symtab_smob_name, sizeof (symtab_smob));
+ scm_set_smob_mark (symtab_smob_tag, stscm_mark_symtab_smob);
+ scm_set_smob_free (symtab_smob_tag, stscm_free_symtab_smob);
+ scm_set_smob_print (symtab_smob_tag, stscm_print_symtab_smob);
+
+ sal_smob_tag = gdbscm_make_smob_type (sal_smob_name, sizeof (sal_smob));
+ scm_set_smob_mark (sal_smob_tag, stscm_mark_sal_smob);
+ scm_set_smob_free (sal_smob_tag, stscm_free_sal_smob);
+ scm_set_smob_print (sal_smob_tag, stscm_print_sal_smob);
+
+ gdbscm_define_functions (symtab_functions, 1);
+
+ /* Register an objfile "free" callback so we can properly
+ invalidate symbol tables, and symbol table and line data
+ structures when an object file that is about to be deleted. */
+ stscm_objfile_data_key
+ = register_objfile_data_with_cleanup (NULL, stscm_del_objfile_symtabs);
+}
diff --git a/gdb/guile/scm-type.c b/gdb/guile/scm-type.c
new file mode 100644
index 0000000..36cba79
--- /dev/null
+++ b/gdb/guile/scm-type.c
@@ -0,0 +1,1495 @@
+/* Scheme interface to types.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "value.h"
+#include "exceptions.h"
+#include "gdbtypes.h"
+#include "objfiles.h"
+#include "language.h"
+#include "vec.h"
+#include "bcache.h"
+#include "dwarf2loc.h"
+#include "typeprint.h"
+#include "guile-internal.h"
+
+/* The <gdb:type> smob.
+ The type is chained with all types associated with its objfile, if any.
+ This lets us copy the underlying struct type when the objfile is
+ deleted. */
+
+typedef struct _type_smob
+{
+ /* This always appears first.
+ eqable_gdb_smob is used so that types are eq?-able.
+ Also, a type object can be associated with an objfile. eqable_gdb_smob
+ lets us track the lifetime of all types associated with an objfile.
+ When an objfile is deleted we need to invalidate the type object. */
+ eqable_gdb_smob base;
+
+ /* The GDB type structure this smob is wrapping. */
+ struct type *type;
+} type_smob;
+
+/* A field smob. */
+
+typedef struct
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Backlink to the containing <gdb:type> object. */
+ SCM type_scm;
+
+ /* The field number in TYPE_SCM. */
+ int field_num;
+} field_smob;
+
+static const char type_smob_name[] = "gdb:type";
+static const char field_smob_name[] = "gdb:field";
+
+static const char not_composite_error[] =
+ N_("type is not a structure, union, or enum type");
+
+/* The tag Guile knows the type smob by. */
+static scm_t_bits type_smob_tag;
+
+/* The tag Guile knows the field smob by. */
+static scm_t_bits field_smob_tag;
+
+/* The "next" procedure for field iterators. */
+static SCM tyscm_next_field_x_proc;
+
+/* Keywords used in argument passing. */
+static SCM block_keyword;
+
+static const struct objfile_data *tyscm_objfile_data_key;
+
+/* Hash table to uniquify global (non-objfile-owned) types. */
+static htab_t global_types_map;
+
+static struct type *tyscm_get_composite (struct type *type);
+
+/* Return the type field of T_SMOB.
+ This exists so that we don't have to export the struct's contents. */
+
+struct type *
+tyscm_type_smob_type (type_smob *t_smob)
+{
+ return t_smob->type;
+}
+
+/* Return the name of TYPE in expanded form.
+ Space for the result is malloc'd, caller must free.
+ If there's an error computing the name, the result is NULL and the
+ exception is stored in *EXCP. */
+
+static char *
+tyscm_type_name (struct type *type, SCM *excp)
+{
+ char *name = NULL;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *old_chain;
+ struct ui_file *stb;
+
+ stb = mem_fileopen ();
+ old_chain = make_cleanup_ui_file_delete (stb);
+
+ LA_PRINT_TYPE (type, "", stb, -1, 0, &type_print_raw_options);
+
+ name = ui_file_xstrdup (stb, NULL);
+ do_cleanups (old_chain);
+ }
+ if (except.reason < 0)
+ {
+ *excp = gdbscm_scm_from_gdb_exception (except);
+ return NULL;
+ }
+
+ return name;
+}
+
+/* Administrivia for type smobs. */
+
+/* Helper function to hash a type_smob. */
+
+static hashval_t
+tyscm_hash_type_smob (const void *p)
+{
+ const type_smob *t_smob = p;
+
+ return htab_hash_pointer (t_smob->type);
+}
+
+/* Helper function to compute equality of type_smobs. */
+
+static int
+tyscm_eq_type_smob (const void *ap, const void *bp)
+{
+ const type_smob *a = ap;
+ const type_smob *b = bp;
+
+ return (a->type == b->type
+ && a->type != NULL);
+}
+
+/* Return the struct type pointer -> SCM mapping table.
+ If type is owned by an objfile, the mapping table is created if necessary.
+ Otherwise, type is not owned by an objfile, and we use
+ global_types_map. */
+
+static htab_t
+tyscm_type_map (struct type *type)
+{
+ struct objfile *objfile = TYPE_OBJFILE (type);
+ htab_t htab;
+
+ if (objfile == NULL)
+ return global_types_map;
+
+ htab = objfile_data (objfile, tyscm_objfile_data_key);
+ if (htab == NULL)
+ {
+ htab = gdbscm_create_eqable_gsmob_ptr_map (tyscm_hash_type_smob,
+ tyscm_eq_type_smob);
+ set_objfile_data (objfile, tyscm_objfile_data_key, htab);
+ }
+
+ return htab;
+}
+
+/* The smob "mark" function for <gdb:type>. */
+
+static SCM
+tyscm_mark_type_smob (SCM self)
+{
+ type_smob *t_smob = (type_smob *) SCM_SMOB_DATA (self);
+
+ /* Do this last. */
+ return gdbscm_mark_eqable_gsmob (&t_smob->base);
+}
+
+/* The smob "free" function for <gdb:type>. */
+
+static size_t
+tyscm_free_type_smob (SCM self)
+{
+ type_smob *t_smob = (type_smob *) SCM_SMOB_DATA (self);
+
+ if (t_smob->type != NULL)
+ {
+ htab_t htab = tyscm_type_map (t_smob->type);
+
+ gdbscm_clear_eqable_gsmob_ptr_slot (htab, &t_smob->base);
+ }
+
+ /* Not necessary, done to catch bugs. */
+ t_smob->type = NULL;
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:type>. */
+
+static int
+tyscm_print_type_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ type_smob *t_smob = (type_smob *) SCM_SMOB_DATA (self);
+ SCM exception;
+ char *name = tyscm_type_name (t_smob->type, &exception);
+
+ if (name == NULL)
+ gdbscm_throw (exception);
+
+ /* pstate->writingp = zero if invoked by display/~A, and nonzero if
+ invoked by write/~S. What to do here may need to evolve.
+ IWBN if we could pass an argument to format that would we could use
+ instead of writingp. */
+ if (pstate->writingp)
+ gdbscm_printf (port, "#<%s ", type_smob_name);
+
+ scm_puts (name, port);
+
+ if (pstate->writingp)
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* The smob "equal?" function for <gdb:type>. */
+
+static SCM
+tyscm_equal_p_type_smob (SCM type1_scm, SCM type2_scm)
+{
+ type_smob *type1_smob, *type2_smob;
+ struct type *type1, *type2;
+ int result = 0;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (tyscm_is_type (type1_scm), type1_scm, SCM_ARG1, FUNC_NAME,
+ type_smob_name);
+ SCM_ASSERT_TYPE (tyscm_is_type (type2_scm), type2_scm, SCM_ARG2, FUNC_NAME,
+ type_smob_name);
+ type1_smob = (type_smob *) SCM_SMOB_DATA (type1_scm);
+ type2_smob = (type_smob *) SCM_SMOB_DATA (type2_scm);
+ type1 = type1_smob->type;
+ type2 = type2_smob->type;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ result = types_deeply_equal (type1, type2);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (result);
+}
+
+/* Low level routine to create a <gdb:type> object. */
+
+static SCM
+tyscm_make_type_smob (void)
+{
+ type_smob *t_smob = (type_smob *)
+ scm_gc_malloc (sizeof (type_smob), type_smob_name);
+ SCM t_scm;
+
+ /* This must be filled in by the caller. */
+ t_smob->type = NULL;
+
+ t_scm = scm_new_smob (type_smob_tag, (scm_t_bits) t_smob);
+ gdbscm_init_eqable_gsmob (&t_smob->base);
+
+ return t_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:type> object. */
+
+int
+tyscm_is_type (SCM self)
+{
+ return SCM_SMOB_PREDICATE (type_smob_tag, self);
+}
+
+/* (type? object) -> boolean */
+
+static SCM
+gdbscm_type_p (SCM self)
+{
+ return scm_from_bool (tyscm_is_type (self));
+}
+
+/* Return the existing object that encapsulates TYPE, or create a new
+ <gdb:type> object. */
+
+SCM
+tyscm_scm_from_type (struct type *type)
+{
+ htab_t htab;
+ eqable_gdb_smob **slot;
+ type_smob *t_smob, t_smob_for_lookup;
+ SCM t_scm;
+
+ /* If we've already created a gsmob for this type, return it.
+ This makes types eq?-able. */
+ htab = tyscm_type_map (type);
+ t_smob_for_lookup.type = type;
+ slot = gdbscm_find_eqable_gsmob_ptr_slot (htab, &t_smob_for_lookup.base);
+ if (*slot != NULL)
+ return (*slot)->containing_scm;
+
+ t_scm = tyscm_make_type_smob ();
+ t_smob = (type_smob *) SCM_SMOB_DATA (t_scm);
+ t_smob->type = type;
+ gdbscm_fill_eqable_gsmob_ptr_slot (slot, &t_smob->base, t_scm);
+
+ return t_scm;
+}
+
+/* Returns the <gdb:type> object in SELF.
+ Throws an exception if SELF is not a <gdb:type> object. */
+
+static SCM
+tyscm_get_type_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (tyscm_is_type (self), self, arg_pos, func_name,
+ type_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the type smob of SELF.
+ Throws an exception if SELF is not a <gdb:type> object. */
+
+type_smob *
+tyscm_get_type_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM t_scm = tyscm_get_type_arg_unsafe (self, arg_pos, func_name);
+ type_smob *t_smob = (type_smob *) SCM_SMOB_DATA (t_scm);
+
+ return t_smob;
+}
+
+/* Helper function for save_objfile_types to make a deep copy of the type. */
+
+static int
+tyscm_copy_type_recursive (void **slot, void *info)
+{
+ type_smob *t_smob = (type_smob *) *slot;
+ htab_t copied_types = info;
+ struct objfile *objfile = TYPE_OBJFILE (t_smob->type);
+
+ gdb_assert (objfile != NULL);
+
+ htab_empty (copied_types);
+ t_smob->type = copy_type_recursive (objfile, t_smob->type, copied_types);
+ return 1;
+}
+
+/* Called when OBJFILE is about to be deleted.
+ Make a copy of all types associated with OBJFILE. */
+
+static void
+save_objfile_types (struct objfile *objfile, void *datum)
+{
+ htab_t htab = datum;
+ htab_t copied_types;
+
+ if (!gdb_scheme_initialized)
+ return;
+
+ copied_types = create_copied_types_hash (objfile);
+
+ if (htab != NULL)
+ {
+ htab_traverse_noresize (htab, tyscm_copy_type_recursive, copied_types);
+ htab_delete (htab);
+ }
+
+ htab_delete (copied_types);
+}
+
+/* Administrivia for field smobs. */
+
+/* The smob "mark" function for <gdb:field>. */
+
+static SCM
+tyscm_mark_field_smob (SCM self)
+{
+ field_smob *f_smob = (field_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (f_smob->type_scm);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&f_smob->base);
+}
+
+/* The smob "print" function for <gdb:field>. */
+
+static int
+tyscm_print_field_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ field_smob *f_smob = (field_smob *) SCM_SMOB_DATA (self);
+
+ gdbscm_printf (port, "#<%s ", field_smob_name);
+ scm_write (f_smob->type_scm, port);
+ gdbscm_printf (port, " %d", f_smob->field_num);
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:field> object for field FIELD_NUM
+ of type TYPE_SCM. */
+
+static SCM
+tyscm_make_field_smob (SCM type_scm, int field_num)
+{
+ field_smob *f_smob = (field_smob *)
+ scm_gc_malloc (sizeof (field_smob), field_smob_name);
+ SCM result;
+
+ f_smob->type_scm = type_scm;
+ f_smob->field_num = field_num;
+ result = scm_new_smob (field_smob_tag, (scm_t_bits) f_smob);
+ gdbscm_init_gsmob (&f_smob->base);
+
+ return result;
+}
+
+/* Return non-zero if SCM is a <gdb:field> object. */
+
+static int
+tyscm_is_field (SCM self)
+{
+ return SCM_SMOB_PREDICATE (field_smob_tag, self);
+}
+
+/* (field? object) -> boolean */
+
+static SCM
+gdbscm_field_p (SCM self)
+{
+ return scm_from_bool (tyscm_is_field (self));
+}
+
+/* Create a new <gdb:field> object that encapsulates field FIELD_NUM
+ in type TYPE_SCM. */
+
+SCM
+tyscm_scm_from_field (SCM type_scm, int field_num)
+{
+ return tyscm_make_field_smob (type_scm, field_num);
+}
+
+/* Returns the <gdb:field> object in SELF.
+ Throws an exception if SELF is not a <gdb:field> object. */
+
+static SCM
+tyscm_get_field_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (tyscm_is_field (self), self, arg_pos, func_name,
+ field_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the field smob of SELF.
+ Throws an exception if SELF is not a <gdb:field> object. */
+
+static field_smob *
+tyscm_get_field_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM f_scm = tyscm_get_field_arg_unsafe (self, arg_pos, func_name);
+ field_smob *f_smob = (field_smob *) SCM_SMOB_DATA (f_scm);
+
+ return f_smob;
+}
+
+/* Returns a pointer to the type struct in F_SMOB
+ (the type the field is in). */
+
+static struct type *
+tyscm_field_smob_containing_type (field_smob *f_smob)
+{
+ type_smob *t_smob;
+
+ gdb_assert (tyscm_is_type (f_smob->type_scm));
+ t_smob = (type_smob *) SCM_SMOB_DATA (f_smob->type_scm);
+
+ return t_smob->type;
+}
+
+/* Returns a pointer to the field struct of F_SMOB. */
+
+static struct field *
+tyscm_field_smob_to_field (field_smob *f_smob)
+{
+ struct type *type = tyscm_field_smob_containing_type (f_smob);
+
+ /* This should be non-NULL by construction. */
+ gdb_assert (TYPE_FIELDS (type) != NULL);
+
+ return &TYPE_FIELD (type, f_smob->field_num);
+}
+
+/* Type smob accessors. */
+
+/* (type-code <gdb:type>) -> integer
+ Return the code for this type. */
+
+static SCM
+gdbscm_type_code (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+
+ return scm_from_int (TYPE_CODE (type));
+}
+
+/* (type-fields <gdb:type>) -> list
+ Return a list of all fields. Each element is a <gdb:field> object.
+ This also supports arrays, we return a field list of one element,
+ the range type. */
+
+static SCM
+gdbscm_type_fields (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ struct type *containing_type;
+ SCM containing_type_scm, result;
+ int i;
+
+ containing_type = tyscm_get_composite (type);
+ if (containing_type == NULL)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _(not_composite_error));
+
+ /* If SELF is a typedef or reference, we want the underlying type,
+ which is what tyscm_get_composite returns. */
+ if (containing_type == type)
+ containing_type_scm = self;
+ else
+ containing_type_scm = tyscm_scm_from_type (containing_type);
+
+ result = SCM_EOL;
+ for (i = 0; i < TYPE_NFIELDS (containing_type); ++i)
+ result = scm_cons (tyscm_make_field_smob (containing_type_scm, i), result);
+
+ return scm_reverse_x (result, SCM_EOL);
+}
+
+/* (type-tag <gdb:type>) -> string
+ Return the type's tag, or #f. */
+
+static SCM
+gdbscm_type_tag (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+
+ if (!TYPE_TAG_NAME (type))
+ return SCM_BOOL_F;
+ return gdbscm_scm_from_c_string (TYPE_TAG_NAME (type));
+}
+
+/* (type-name <gdb:type>) -> string
+ Return the type's name, or #f. */
+
+static SCM
+gdbscm_type_name (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+
+ if (!TYPE_NAME (type))
+ return SCM_BOOL_F;
+ return gdbscm_scm_from_c_string (TYPE_NAME (type));
+}
+
+/* (type-print-name <gdb:type>) -> string
+ Return the print name of type.
+ TODO: template support elided for now. */
+
+static SCM
+gdbscm_type_print_name (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ char *thetype;
+ SCM exception, result;
+
+ thetype = tyscm_type_name (type, &exception);
+
+ if (thetype == NULL)
+ gdbscm_throw (exception);
+
+ result = gdbscm_scm_from_c_string (thetype);
+ xfree (thetype);
+
+ return result;
+}
+
+/* (type-sizeof <gdb:type>) -> integer
+ Return the size of the type represented by SELF, in bytes. */
+
+static SCM
+gdbscm_type_sizeof (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ check_typedef (type);
+ }
+ /* Ignore exceptions. */
+
+ return scm_from_long (TYPE_LENGTH (type));
+}
+
+/* (type-strip-typedefs <gdb:type>) -> <gdb:type>
+ Return the type, stripped of typedefs. */
+
+static SCM
+gdbscm_type_strip_typedefs (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* Strip typedefs and pointers/reference from a type. Then check that
+ it is a struct, union, or enum type. If not, return NULL. */
+
+static struct type *
+tyscm_get_composite (struct type *type)
+{
+ volatile struct gdb_exception except;
+
+ for (;;)
+ {
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = check_typedef (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (TYPE_CODE (type) != TYPE_CODE_PTR
+ && TYPE_CODE (type) != TYPE_CODE_REF)
+ break;
+ type = TYPE_TARGET_TYPE (type);
+ }
+
+ /* If this is not a struct, union, or enum type, raise TypeError
+ exception. */
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION
+ && TYPE_CODE (type) != TYPE_CODE_ENUM)
+ return NULL;
+
+ return type;
+}
+
+/* Helper for tyscm_array and tyscm_vector. */
+
+static SCM
+tyscm_array_1 (SCM self, SCM n1_scm, SCM n2_scm, int is_vector,
+ const char *func_name)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, func_name);
+ struct type *type = t_smob->type;
+ long n1, n2 = 0;
+ struct type *array = NULL;
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (func_name, SCM_ARG2, NULL, "l|l",
+ n1_scm, &n1, n2_scm, &n2);
+
+ if (SCM_UNBNDP (n2_scm))
+ {
+ n2 = n1;
+ n1 = 0;
+ }
+
+ if (n2 < n1)
+ {
+ gdbscm_out_of_range_error (func_name, SCM_ARG3,
+ scm_cons (scm_from_long (n1),
+ scm_from_long (n2)),
+ _("Array length must not be negative"));
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ array = lookup_array_range_type (type, n1, n2);
+ if (is_vector)
+ make_vector_type (array);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (array);
+}
+
+/* (type-array <gdb:type> [low-bound] high-bound) -> <gdb:type>
+ The array has indices [low-bound,high-bound].
+ If low-bound is not provided zero is used.
+ Return an array type.
+
+ IWBN if the one argument version specified a size, not the high bound.
+ It's too easy to pass one argument thinking it is the size of the array.
+ The current semantics are for compatibility with the Python version.
+ Later we can add #:size. */
+
+static SCM
+gdbscm_type_array (SCM self, SCM n1, SCM n2)
+{
+ return tyscm_array_1 (self, n1, n2, 0, FUNC_NAME);
+}
+
+/* (type-vector <gdb:type> [low-bound] high-bound) -> <gdb:type>
+ The array has indices [low-bound,high-bound].
+ If low-bound is not provided zero is used.
+ Return a vector type.
+
+ IWBN if the one argument version specified a size, not the high bound.
+ It's too easy to pass one argument thinking it is the size of the array.
+ The current semantics are for compatibility with the Python version.
+ Later we can add #:size. */
+
+static SCM
+gdbscm_type_vector (SCM self, SCM n1, SCM n2)
+{
+ return tyscm_array_1 (self, n1, n2, 1, FUNC_NAME);
+}
+
+/* (type-pointer <gdb:type>) -> <gdb:type>
+ Return a <gdb:type> object which represents a pointer to SELF. */
+
+static SCM
+gdbscm_type_pointer (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = lookup_pointer_type (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (type-range <gdb:type>) -> (low high)
+ Return the range of a type represented by SELF. The return type is
+ a list. The first element is the low bound, and the second element
+ is the high bound. */
+
+static SCM
+gdbscm_type_range (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ SCM low_scm, high_scm;
+ /* Initialize these to appease GCC warnings. */
+ LONGEST low = 0, high = 0;
+
+ SCM_ASSERT_TYPE (TYPE_CODE (type) == TYPE_CODE_ARRAY
+ || TYPE_CODE (type) == TYPE_CODE_STRING
+ || TYPE_CODE (type) == TYPE_CODE_RANGE,
+ self, SCM_ARG1, FUNC_NAME, _("ranged type"));
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ case TYPE_CODE_STRING:
+ low = TYPE_LOW_BOUND (TYPE_INDEX_TYPE (type));
+ high = TYPE_HIGH_BOUND (TYPE_INDEX_TYPE (type));
+ break;
+ case TYPE_CODE_RANGE:
+ low = TYPE_LOW_BOUND (type);
+ high = TYPE_HIGH_BOUND (type);
+ break;
+ }
+
+ low_scm = gdbscm_scm_from_longest (low);
+ high_scm = gdbscm_scm_from_longest (high);
+
+ return scm_list_2 (low_scm, high_scm);
+}
+
+/* (type-reference <gdb:type>) -> <gdb:type>
+ Return a <gdb:type> object which represents a reference to SELF. */
+
+static SCM
+gdbscm_type_reference (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = lookup_reference_type (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (type-target <gdb:type>) -> <gdb:type>
+ Return a <gdb:type> object which represents the target type of SELF. */
+
+static SCM
+gdbscm_type_target (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+
+ SCM_ASSERT (TYPE_TARGET_TYPE (type), self, SCM_ARG1, FUNC_NAME);
+
+ return tyscm_scm_from_type (TYPE_TARGET_TYPE (type));
+}
+
+/* (type-const <gdb:type>) -> <gdb:type>
+ Return a const-qualified type variant. */
+
+static SCM
+gdbscm_type_const (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = make_cv_type (1, 0, type, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (type-volatile <gdb:type>) -> <gdb:type>
+ Return a volatile-qualified type variant. */
+
+static SCM
+gdbscm_type_volatile (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = make_cv_type (0, 1, type, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* (type-unqualified <gdb:type>) -> <gdb:type>
+ Return an unqualified type variant. */
+
+static SCM
+gdbscm_type_unqualified (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ type = make_cv_type (0, 0, type, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return tyscm_scm_from_type (type);
+}
+
+/* Field related accessors of types. */
+
+/* (type-num-fields <gdb:type>) -> integer
+ Return number of fields. */
+
+static SCM
+gdbscm_type_num_fields (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+
+ type = tyscm_get_composite (type);
+ if (type == NULL)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _(not_composite_error));
+
+ return scm_from_long (TYPE_NFIELDS (type));
+}
+
+/* (type-field <gdb:type> string) -> <gdb:field>
+ Return the <gdb:field> object for the field named by the argument. */
+
+static SCM
+gdbscm_type_field (SCM self, SCM field_scm)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ char *field;
+ int i;
+ struct cleanup *cleanups;
+
+ SCM_ASSERT_TYPE (scm_is_string (field_scm), field_scm, SCM_ARG2, FUNC_NAME,
+ _("string"));
+
+ /* We want just fields of this type, not of base types, so instead of
+ using lookup_struct_elt_type, portions of that function are
+ copied here. */
+
+ type = tyscm_get_composite (type);
+ if (type == NULL)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _(not_composite_error));
+
+ field = gdbscm_scm_to_c_string (field_scm);
+ cleanups = make_cleanup (xfree, field);
+
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
+ {
+ const char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+ if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+ {
+ do_cleanups (cleanups);
+ return tyscm_make_field_smob (self, i);
+ }
+ }
+
+ do_cleanups (cleanups);
+
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, field_scm,
+ _("Unknown field"));
+}
+
+/* (type-has-field? <gdb:type> string) -> boolean
+ Return boolean indicating if type SELF has FIELD_SCM (a string). */
+
+static SCM
+gdbscm_type_has_field_p (SCM self, SCM field_scm)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ char *field;
+ int i;
+ struct cleanup *cleanups;
+
+ SCM_ASSERT_TYPE (scm_is_string (field_scm), field_scm, SCM_ARG2, FUNC_NAME,
+ _("string"));
+
+ /* We want just fields of this type, not of base types, so instead of
+ using lookup_struct_elt_type, portions of that function are
+ copied here. */
+
+ type = tyscm_get_composite (type);
+ if (type == NULL)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _(not_composite_error));
+
+ field = gdbscm_scm_to_c_string (field_scm);
+ cleanups = make_cleanup (xfree, field);
+
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
+ {
+ const char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+ if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+ {
+ do_cleanups (cleanups);
+ return SCM_BOOL_T;
+ }
+ }
+
+ do_cleanups (cleanups);
+
+ return SCM_BOOL_F;
+}
+
+/* (make-field-iterator <gdb:type>) -> <gdb:iterator>
+ Make a field iterator object. */
+
+static SCM
+gdbscm_make_field_iterator (SCM self)
+{
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct type *type = t_smob->type;
+ struct type *containing_type;
+ SCM containing_type_scm;
+
+ containing_type = tyscm_get_composite (type);
+ if (containing_type == NULL)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _(not_composite_error));
+
+ /* If SELF is a typedef or reference, we want the underlying type,
+ which is what tyscm_get_composite returns. */
+ if (containing_type == type)
+ containing_type_scm = self;
+ else
+ containing_type_scm = tyscm_scm_from_type (containing_type);
+
+ return gdbscm_make_iterator (containing_type_scm, scm_from_int (0),
+ tyscm_next_field_x_proc);
+}
+
+/* (type-next-field! <gdb:iterator>) -> <gdb:field>
+ Return the next field in the iteration through the list of fields of the
+ type, or (end-of-iteration).
+ SELF is a <gdb:iterator> object created by gdbscm_make_field_iterator.
+ This is the next! <gdb:iterator> function, not exported to the user. */
+
+static SCM
+gdbscm_type_next_field_x (SCM self)
+{
+ iterator_smob *i_smob;
+ type_smob *t_smob;
+ struct type *type;
+ SCM it_scm, result, progress, object;
+ int field, rc;
+
+ it_scm = itscm_get_iterator_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ i_smob = (iterator_smob *) SCM_SMOB_DATA (it_scm);
+ object = itscm_iterator_smob_object (i_smob);
+ progress = itscm_iterator_smob_progress (i_smob);
+
+ SCM_ASSERT_TYPE (tyscm_is_type (object), object,
+ SCM_ARG1, FUNC_NAME, type_smob_name);
+ t_smob = (type_smob *) SCM_SMOB_DATA (object);
+ type = t_smob->type;
+
+ SCM_ASSERT_TYPE (scm_is_signed_integer (progress,
+ 0, TYPE_NFIELDS (type)),
+ progress, SCM_ARG1, FUNC_NAME, _("integer"));
+ field = scm_to_int (progress);
+
+ if (field < TYPE_NFIELDS (type))
+ {
+ result = tyscm_make_field_smob (object, field);
+ itscm_set_iterator_smob_progress_x (i_smob, scm_from_int (field + 1));
+ return result;
+ }
+
+ return gdbscm_end_of_iteration ();
+}
+
+/* Field smob accessors. */
+
+/* (field-name <gdb:field>) -> string
+ Return the name of this field or #f if there isn't one. */
+
+static SCM
+gdbscm_field_name (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+
+ if (FIELD_NAME (*field))
+ return gdbscm_scm_from_c_string (FIELD_NAME (*field));
+ return SCM_BOOL_F;
+}
+
+/* (field-type <gdb:field>) -> <gdb:type>
+ Return the <gdb:type> object of the field or #f if there isn't one. */
+
+static SCM
+gdbscm_field_type (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+
+ /* A field can have a NULL type in some situations. */
+ if (FIELD_TYPE (*field))
+ return tyscm_scm_from_type (FIELD_TYPE (*field));
+ return SCM_BOOL_F;
+}
+
+/* (field-enumval <gdb:field>) -> integer
+ For enum values, return its value as an integer. */
+
+static SCM
+gdbscm_field_enumval (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+ struct type *type = tyscm_field_smob_containing_type (f_smob);
+
+ SCM_ASSERT_TYPE (TYPE_CODE (type) == TYPE_CODE_ENUM,
+ self, SCM_ARG1, FUNC_NAME, _("enum type"));
+
+ return scm_from_long (FIELD_ENUMVAL (*field));
+}
+
+/* (field-bitpos <gdb:field>) -> integer
+ For bitfields, return its offset in bits. */
+
+static SCM
+gdbscm_field_bitpos (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+ struct type *type = tyscm_field_smob_containing_type (f_smob);
+
+ SCM_ASSERT_TYPE (TYPE_CODE (type) != TYPE_CODE_ENUM,
+ self, SCM_ARG1, FUNC_NAME, _("non-enum type"));
+
+ return scm_from_long (FIELD_BITPOS (*field));
+}
+
+/* (field-bitsize <gdb:field>) -> integer
+ Return the size of the field in bits. */
+
+static SCM
+gdbscm_field_bitsize (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+
+ return scm_from_long (FIELD_BITPOS (*field));
+}
+
+/* (field-artificial? <gdb:field>) -> boolean
+ Return #t if field is artificial. */
+
+static SCM
+gdbscm_field_artificial_p (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+
+ return scm_from_bool (FIELD_ARTIFICIAL (*field));
+}
+
+/* (field-baseclass? <gdb:field>) -> boolean
+ Return #t if field is a baseclass. */
+
+static SCM
+gdbscm_field_baseclass_p (SCM self)
+{
+ field_smob *f_smob
+ = tyscm_get_field_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct field *field = tyscm_field_smob_to_field (f_smob);
+ struct type *type = tyscm_field_smob_containing_type (f_smob);
+
+ if (TYPE_CODE (type) == TYPE_CODE_CLASS)
+ return scm_from_bool (f_smob->field_num < TYPE_N_BASECLASSES (type));
+ return SCM_BOOL_F;
+}
+
+/* Return the type named TYPE_NAME in BLOCK.
+ Returns NULL if not found.
+ This routine does not throw an error. */
+
+static struct type *
+tyscm_lookup_typename (const char *type_name, const struct block *block)
+{
+ struct type *type = NULL;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (!strncmp (type_name, "struct ", 7))
+ type = lookup_struct (type_name + 7, NULL);
+ else if (!strncmp (type_name, "union ", 6))
+ type = lookup_union (type_name + 6, NULL);
+ else if (!strncmp (type_name, "enum ", 5))
+ type = lookup_enum (type_name + 5, NULL);
+ else
+ type = lookup_typename (current_language, get_current_arch (),
+ type_name, block, 0);
+ }
+ if (except.reason < 0)
+ return NULL;
+
+ return type;
+}
+
+/* (lookup-type name [#:block <gdb:block>]) -> <gdb:type>
+ TODO: legacy template support left out until needed. */
+
+static SCM
+gdbscm_lookup_type (SCM name_scm, SCM rest)
+{
+ SCM keywords[] = { block_keyword, SCM_BOOL_F };
+ char *name;
+ SCM block_scm = SCM_BOOL_F;
+ int block_arg_pos = -1;
+ const struct block *block = NULL;
+ struct type *type;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#O",
+ name_scm, &name,
+ rest, &block_arg_pos, &block_scm);
+
+ if (block_arg_pos != -1)
+ {
+ SCM exception;
+
+ block = bkscm_scm_to_block (block_scm, block_arg_pos, FUNC_NAME,
+ &exception);
+ if (block == NULL)
+ {
+ xfree (name);
+ gdbscm_throw (exception);
+ }
+ }
+ type = tyscm_lookup_typename (name, block);
+ xfree (name);
+
+ if (type != NULL)
+ return tyscm_scm_from_type (type);
+ return SCM_BOOL_F;
+}
+
+/* Initialize the Scheme type code. */
+
+
+static const scheme_integer_constant type_integer_constants[] =
+{
+#define X(SYM) { #SYM, SYM }
+ X (TYPE_CODE_BITSTRING),
+ X (TYPE_CODE_PTR),
+ X (TYPE_CODE_ARRAY),
+ X (TYPE_CODE_STRUCT),
+ X (TYPE_CODE_UNION),
+ X (TYPE_CODE_ENUM),
+ X (TYPE_CODE_FLAGS),
+ X (TYPE_CODE_FUNC),
+ X (TYPE_CODE_INT),
+ X (TYPE_CODE_FLT),
+ X (TYPE_CODE_VOID),
+ X (TYPE_CODE_SET),
+ X (TYPE_CODE_RANGE),
+ X (TYPE_CODE_STRING),
+ X (TYPE_CODE_ERROR),
+ X (TYPE_CODE_METHOD),
+ X (TYPE_CODE_METHODPTR),
+ X (TYPE_CODE_MEMBERPTR),
+ X (TYPE_CODE_REF),
+ X (TYPE_CODE_CHAR),
+ X (TYPE_CODE_BOOL),
+ X (TYPE_CODE_COMPLEX),
+ X (TYPE_CODE_TYPEDEF),
+ X (TYPE_CODE_NAMESPACE),
+ X (TYPE_CODE_DECFLOAT),
+ X (TYPE_CODE_INTERNAL_FUNCTION),
+#undef X
+
+ END_INTEGER_CONSTANTS
+};
+
+static const scheme_function type_functions[] =
+{
+ { "type?", 1, 0, 0, gdbscm_type_p,
+ "\
+Return #t if the object is a <gdb:type> object." },
+
+ { "lookup-type", 1, 0, 1, gdbscm_lookup_type,
+ "\
+Return the <gdb:type> object representing string or #f if not found.\n\
+If block is given then the type is looked for in that block.\n\
+\n\
+ Arguments: string [#:block <gdb:block>]" },
+
+ { "type-code", 1, 0, 0, gdbscm_type_code,
+ "\
+Return the code of the type" },
+
+ { "type-tag", 1, 0, 0, gdbscm_type_tag,
+ "\
+Return the tag name of the type, or #f if there isn't one." },
+
+ { "type-name", 1, 0, 0, gdbscm_type_name,
+ "\
+Return the name of the type as a string, or #f if there isn't one." },
+
+ { "type-print-name", 1, 0, 0, gdbscm_type_print_name,
+ "\
+Return the print name of the type as a string." },
+
+ { "type-sizeof", 1, 0, 0, gdbscm_type_sizeof,
+ "\
+Return the size of the type, in bytes." },
+
+ { "type-strip-typedefs", 1, 0, 0, gdbscm_type_strip_typedefs,
+ "\
+Return a type formed by stripping the type of all typedefs." },
+
+ { "type-array", 2, 1, 0, gdbscm_type_array,
+ "\
+Return a type representing an array of objects of the type.\n\
+\n\
+ Arguments: <gdb:type> [low-bound] high-bound\n\
+ If low-bound is not provided zero is used.\n\
+ N.B. If only the high-bound parameter is specified, it is not\n\
+ the array size.\n\
+ Valid bounds for array indices are [low-bound,high-bound]." },
+
+ { "type-vector", 2, 1, 0, gdbscm_type_vector,
+ "\
+Return a type representing a vector of objects of the type.\n\
+Vectors differ from arrays in that if the current language has C-style\n\
+arrays, vectors don't decay to a pointer to the first element.\n\
+They are first class values.\n\
+\n\
+ Arguments: <gdb:type> [low-bound] high-bound\n\
+ If low-bound is not provided zero is used.\n\
+ N.B. If only the high-bound parameter is specified, it is not\n\
+ the array size.\n\
+ Valid bounds for array indices are [low-bound,high-bound]." },
+
+ { "type-pointer", 1, 0, 0, gdbscm_type_pointer,
+ "\
+Return a type of pointer to the type." },
+
+ { "type-range", 1, 0, 0, gdbscm_type_range,
+ "\
+Return (low high) representing the range for the type." },
+
+ { "type-reference", 1, 0, 0, gdbscm_type_reference,
+ "\
+Return a type of reference to the type." },
+
+ { "type-target", 1, 0, 0, gdbscm_type_target,
+ "\
+Return the target type of the type." },
+
+ { "type-const", 1, 0, 0, gdbscm_type_const,
+ "\
+Return a const variant of the type." },
+
+ { "type-volatile", 1, 0, 0, gdbscm_type_volatile,
+ "\
+Return a volatile variant of the type." },
+
+ { "type-unqualified", 1, 0, 0, gdbscm_type_unqualified,
+ "\
+Return a variant of the type without const or volatile attributes." },
+
+ { "type-num-fields", 1, 0, 0, gdbscm_type_num_fields,
+ "\
+Return the number of fields of the type." },
+
+ { "type-fields", 1, 0, 0, gdbscm_type_fields,
+ "\
+Return the list of <gdb:field> objects of fields of the type." },
+
+ { "make-field-iterator", 1, 0, 0, gdbscm_make_field_iterator,
+ "\
+Return a <gdb:iterator> object for iterating over the fields of the type." },
+
+ { "type-field", 2, 0, 0, gdbscm_type_field,
+ "\
+Return the field named by string of the type.\n\
+\n\
+ Arguments: <gdb:type> string" },
+
+ { "type-has-field?", 2, 0, 0, gdbscm_type_has_field_p,
+ "\
+Return #t if the type has field named string.\n\
+\n\
+ Arguments: <gdb:type> string" },
+
+ { "field?", 1, 0, 0, gdbscm_field_p,
+ "\
+Return #t if the object is a <gdb:field> object." },
+
+ { "field-name", 1, 0, 0, gdbscm_field_name,
+ "\
+Return the name of the field." },
+
+ { "field-type", 1, 0, 0, gdbscm_field_type,
+ "\
+Return the type of the field." },
+
+ { "field-enumval", 1, 0, 0, gdbscm_field_enumval,
+ "\
+Return the enum value represented by the field." },
+
+ { "field-bitpos", 1, 0, 0, gdbscm_field_bitpos,
+ "\
+Return the offset in bits of the field in its containing type." },
+
+ { "field-bitsize", 1, 0, 0, gdbscm_field_bitsize,
+ "\
+Return the size of the field in bits." },
+
+ { "field-artificial?", 1, 0, 0, gdbscm_field_artificial_p,
+ "\
+Return #t if the field is artificial." },
+
+ { "field-baseclass?", 1, 0, 0, gdbscm_field_baseclass_p,
+ "\
+Return #t if the field is a baseclass." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_types (void)
+{
+ type_smob_tag = gdbscm_make_smob_type (type_smob_name, sizeof (type_smob));
+ scm_set_smob_mark (type_smob_tag, tyscm_mark_type_smob);
+ scm_set_smob_free (type_smob_tag, tyscm_free_type_smob);
+ scm_set_smob_print (type_smob_tag, tyscm_print_type_smob);
+ scm_set_smob_equalp (type_smob_tag, tyscm_equal_p_type_smob);
+
+ field_smob_tag = gdbscm_make_smob_type (field_smob_name,
+ sizeof (field_smob));
+ scm_set_smob_mark (field_smob_tag, tyscm_mark_field_smob);
+ scm_set_smob_print (field_smob_tag, tyscm_print_field_smob);
+
+ gdbscm_define_integer_constants (type_integer_constants, 1);
+ gdbscm_define_functions (type_functions, 1);
+
+ /* This function is "private". */
+ tyscm_next_field_x_proc
+ = scm_c_define_gsubr ("%type-next-field!", 1, 0, 0,
+ gdbscm_type_next_field_x);
+ scm_set_procedure_property_x (tyscm_next_field_x_proc,
+ gdbscm_documentation_symbol,
+ gdbscm_scm_from_c_string ("\
+Internal function to assist the type fields iterator."));
+
+ block_keyword = scm_from_latin1_keyword ("block");
+
+ /* Register an objfile "free" callback so we can properly copy types
+ associated with the objfile when it's about to be deleted. */
+ tyscm_objfile_data_key
+ = register_objfile_data_with_cleanup (save_objfile_types, NULL);
+
+ global_types_map = gdbscm_create_eqable_gsmob_ptr_map (tyscm_hash_type_smob,
+ tyscm_eq_type_smob);
+}
diff --git a/gdb/guile/scm-utils.c b/gdb/guile/scm-utils.c
new file mode 100644
index 0000000..9e9901d
--- /dev/null
+++ b/gdb/guile/scm-utils.c
@@ -0,0 +1,585 @@
+/* General utility routines for GDB/Scheme code.
+
+ Copyright (C) 2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include <stdarg.h>
+#include <stdint.h>
+#include "gdb_assert.h"
+#include "guile-internal.h"
+
+/* Define VARIABLES in the gdb module. */
+
+void
+gdbscm_define_variables (const scheme_variable *variables, int public)
+{
+ const scheme_variable *sv;
+
+ for (sv = variables; sv->name != NULL; ++sv)
+ {
+ scm_c_define (sv->name, sv->value);
+ if (public)
+ scm_c_export (sv->name, NULL);
+ }
+}
+
+/* Define FUNCTIONS in the gdb module. */
+
+void
+gdbscm_define_functions (const scheme_function *functions, int public)
+{
+ const scheme_function *sf;
+
+ for (sf = functions; sf->name != NULL; ++sf)
+ {
+ SCM proc = scm_c_define_gsubr (sf->name, sf->required, sf->optional,
+ sf->rest, sf->func);
+
+ scm_set_procedure_property_x (proc, gdbscm_documentation_symbol,
+ gdbscm_scm_from_c_string (sf->doc_string));
+ if (public)
+ scm_c_export (sf->name, NULL);
+ }
+}
+
+/* Define CONSTANTS in the gdb module. */
+
+void
+gdbscm_define_integer_constants (const scheme_integer_constant *constants,
+ int public)
+{
+ const scheme_integer_constant *sc;
+
+ for (sc = constants; sc->name != NULL; ++sc)
+ {
+ scm_c_define (sc->name, scm_from_int (sc->value));
+ if (public)
+ scm_c_export (sc->name, NULL);
+ }
+}
+
+/* scm_printf, alas it doesn't exist. */
+
+void
+gdbscm_printf (SCM port, const char *format, ...)
+{
+ va_list args;
+ char *string;
+
+ va_start (args, format);
+ string = xstrvprintf (format, args);
+ va_end (args);
+ scm_puts (string, port);
+ xfree (string);
+}
+
+/* Utility for calling from gdb to "display" an SCM object. */
+
+void
+gdbscm_debug_display (SCM obj)
+{
+ SCM port = scm_current_output_port ();
+
+ scm_display (obj, port);
+ scm_newline (port);
+ scm_force_output (port);
+}
+
+/* Utility for calling from gdb to "write" an SCM object. */
+
+void
+gdbscm_debug_write (SCM obj)
+{
+ SCM port = scm_current_output_port ();
+
+ scm_write (obj, port);
+ scm_newline (port);
+ scm_force_output (port);
+}
+
+/* Subroutine of gdbscm_parse_function_args to simplify it.
+ Return the number of keyword arguments. */
+
+static int
+count_keywords (const SCM *keywords)
+{
+ int i;
+
+ if (keywords == NULL)
+ return 0;
+ for (i = 0; keywords[i] != SCM_BOOL_F; ++i)
+ continue;
+
+ return i;
+}
+
+/* Subroutine of gdbscm_parse_function_args to simplify it.
+ Validate an argument format string.
+ The result is a boolean indicating if "." was seen. */
+
+static int
+validate_arg_format (const char *format)
+{
+ const char *p;
+ int length = strlen (format);
+ int optional_position = -1;
+ int keyword_position = -1;
+ int dot_seen = 0;
+
+ gdb_assert (length > 0);
+
+ for (p = format; *p != '\0'; ++p)
+ {
+ switch (*p)
+ {
+ case 's':
+ case 't':
+ case 'i':
+ case 'u':
+ case 'l':
+ case 'n':
+ case 'L':
+ case 'U':
+ case 'O':
+ break;
+ case '|':
+ gdb_assert (keyword_position < 0);
+ gdb_assert (optional_position < 0);
+ optional_position = p - format;
+ break;
+ case '#':
+ gdb_assert (keyword_position < 0);
+ keyword_position = p - format;
+ break;
+ case '.':
+ gdb_assert (p[1] == '\0');
+ dot_seen = 1;
+ break;
+ default:
+ gdb_assert_not_reached ("invalid argument format character");
+ }
+ }
+
+ return dot_seen;
+}
+
+/* Our version of SCM_ASSERT_TYPE that calls gdbscm_make_type_error. */
+#define CHECK_TYPE(ok, arg, position, func_name, expected_type) \
+ do { \
+ if (!(ok)) \
+ { \
+ return gdbscm_make_type_error ((func_name), (position), (arg), \
+ (expected_type)); \
+ } \
+ } while (0)
+
+/* Subroutine of gdbscm_parse_function_args to simplify it.
+ Check the type of ARG against FORMAT_CHAR and extract the value.
+ POSITION is the position of ARG in the argument list.
+ The result is #f upon success or a <gdb:exception> object. */
+
+static SCM
+extract_arg (char format_char, SCM arg, void *argp,
+ const char *func_name, int position)
+{
+ switch (format_char)
+ {
+ case 's':
+ {
+ char **arg_ptr = argp;
+
+ CHECK_TYPE (gdbscm_is_true (scm_string_p (arg)), arg, position,
+ func_name, _("string"));
+ *arg_ptr = gdbscm_scm_to_c_string (arg);
+ break;
+ }
+ case 't':
+ {
+ int *arg_ptr = argp;
+
+ /* While in Scheme, anything non-#f is "true", we're strict. */
+ CHECK_TYPE (gdbscm_is_bool (arg), arg, position, func_name,
+ _("boolean"));
+ *arg_ptr = gdbscm_is_true (arg);
+ break;
+ }
+ case 'i':
+ {
+ int *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_signed_integer (arg, INT_MIN, INT_MAX),
+ arg, position, func_name, _("int"));
+ *arg_ptr = scm_to_int (arg);
+ break;
+ }
+ case 'u':
+ {
+ int *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_unsigned_integer (arg, 0, UINT_MAX),
+ arg, position, func_name, _("unsigned int"));
+ *arg_ptr = scm_to_uint (arg);
+ break;
+ }
+ case 'l':
+ {
+ long *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_signed_integer (arg, LONG_MIN, LONG_MAX),
+ arg, position, func_name, _("long"));
+ *arg_ptr = scm_to_long (arg);
+ break;
+ }
+ case 'n':
+ {
+ unsigned long *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_unsigned_integer (arg, 0, ULONG_MAX),
+ arg, position, func_name, _("unsigned long"));
+ *arg_ptr = scm_to_ulong (arg);
+ break;
+ }
+ case 'L':
+ {
+ LONGEST *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_signed_integer (arg, INT64_MIN, INT64_MAX),
+ arg, position, func_name, _("LONGEST"));
+ *arg_ptr = gdbscm_scm_to_longest (arg);
+ break;
+ }
+ case 'U':
+ {
+ ULONGEST *arg_ptr = argp;
+
+ CHECK_TYPE (scm_is_unsigned_integer (arg, 0, UINT64_MAX),
+ arg, position, func_name, _("ULONGEST"));
+ *arg_ptr = gdbscm_scm_to_ulongest (arg);
+ break;
+ }
+ case 'O':
+ {
+ SCM *arg_ptr = argp;
+
+ *arg_ptr = arg;
+ break;
+ }
+ default:
+ gdb_assert_not_reached ("invalid argument format character");
+ }
+
+ return SCM_BOOL_F;
+}
+
+#undef CHECK_TYPE
+
+/* Look up KEYWORD in KEYWORD_LIST.
+ The result is the index of the keyword in the list or -1 if not found. */
+
+static int
+lookup_keyword (const SCM *keyword_list, SCM keyword)
+{
+ int i = 0;
+
+ while (keyword_list[i] != SCM_BOOL_F)
+ {
+ if (scm_is_eq (keyword_list[i], keyword))
+ return i;
+ ++i;
+ }
+
+ return -1;
+}
+
+/* Utility to parse required, optional, and keyword arguments to Scheme
+ functions. Modelled on PyArg_ParseTupleAndKeywords, but no attempt is made
+ at similarity or functionality.
+ There is no result, if there's an error a Scheme exception is thrown.
+
+ Guile provides scm_c_bind_keyword_arguments, and feel free to use it.
+ This is for times when we want a bit more parsing.
+
+ BEGINNING_ARG_POS is the position of the first argument passed to this
+ routine. It should be one of the SCM_ARGn values. It could be > SCM_ARG1
+ if the caller chooses not to parse one or more required arguments.
+
+ KEYWORDS may be NULL if there are no keywords.
+
+ FORMAT:
+ s - string -> char *, malloc'd
+ t - boolean (gdb uses "t", for biT?) -> int
+ i - int
+ u - unsigned int
+ l - long
+ n - unsigned long
+ L - longest
+ U - unsigned longest
+ O - random scheme object
+ | - indicates the next set is for optional arguments
+ # - indicates the next set is for keyword arguments (must follow |)
+ . - indicates "rest" arguments are present, this character must appear last
+
+ FORMAT must match the definition from scm_c_{make,define}_gsubr.
+ Required and optional arguments appear in order in the format string.
+ Afterwards, keyword-based arguments are processed. There must be as many
+ remaining characters in the format string as their are keywords.
+ Except for "|#.", the number of characters in the format string must match
+ #required + #optional + #keywords.
+
+ The function is required to be defined in a compatible manner:
+ #required-args and #optional-arguments must match, and rest-arguments
+ must be specified if keyword args are desired, and/or regular "rest" args.
+
+ Example: For this function,
+ scm_c_define_gsubr ("execute", 2, 3, 1, foo);
+ the format string + keyword list could be any of:
+ 1) "ss|ttt#tt", { "key1", "key2", NULL }
+ 2) "ss|ttt.", { NULL }
+ 3) "ss|ttt#t.", { "key1", NULL }
+
+ For required and optional args pass the SCM of the argument, and a
+ pointer to the value to hold the parsed result (type depends on format
+ char). After that pass the SCM containing the "rest" arguments followed
+ by pointers to values to hold parsed keyword arguments, and if specified
+ a pointer to hold the remaining contents of "rest".
+
+ For keyword arguments pass two pointers: the first is a pointer to an int
+ that will contain the position of the argument in the arg list, and the
+ second will contain result of processing the argument. The int pointed
+ to by the first value should be initialized to -1. It can then be used
+ to tell whether the keyword was present.
+
+ If both keyword and rest arguments are present, the caller must pass a
+ pointer to contain the new value of rest (after keyword args have been
+ removed).
+
+ There's currently no way, that I know of, to specify default values for
+ optional arguments in C-provided functions. At the moment they're a
+ work-in-progress. The caller should test SCM_UNBNDP for each optional
+ argument. Unbound optional arguments are ignored. */
+
+void
+gdbscm_parse_function_args (const char *func_name,
+ int beginning_arg_pos,
+ const SCM *keywords,
+ const char *format, ...)
+{
+ va_list args;
+ const char *p;
+ int i, have_rest, num_keywords, length, position;
+ int have_optional = 0;
+ SCM status;
+ SCM rest = SCM_EOL;
+ /* Keep track of malloc'd strings. We need to free them upon error. */
+ VEC (char_ptr) *allocated_strings = NULL;
+ char *ptr;
+
+ have_rest = validate_arg_format (format);
+ num_keywords = count_keywords (keywords);
+
+ va_start (args, format);
+
+ p = format;
+ position = beginning_arg_pos;
+
+ /* Process required, optional arguments. */
+
+ while (*p && *p != '#' && *p != '.')
+ {
+ SCM arg;
+ void *arg_ptr;
+
+ if (*p == '|')
+ {
+ have_optional = 1;
+ ++p;
+ continue;
+ }
+
+ arg = va_arg (args, SCM);
+ if (!have_optional || !SCM_UNBNDP (arg))
+ {
+ arg_ptr = va_arg (args, void *);
+ status = extract_arg (*p, arg, arg_ptr, func_name, position);
+ if (!gdbscm_is_false (status))
+ goto fail;
+ if (*p == 's')
+ VEC_safe_push (char_ptr, allocated_strings, *(char **) arg_ptr);
+ }
+ ++p;
+ ++position;
+ }
+
+ /* Process keyword arguments. */
+
+ if (have_rest || num_keywords > 0)
+ rest = va_arg (args, SCM);
+
+ if (num_keywords > 0)
+ {
+ SCM *keyword_args = (SCM *) alloca (num_keywords * sizeof (SCM));
+ int *keyword_positions = (int *) alloca (num_keywords * sizeof (int));
+
+ gdb_assert (*p == '#');
+ ++p;
+
+ for (i = 0; i < num_keywords; ++i)
+ {
+ keyword_args[i] = SCM_UNSPECIFIED;
+ keyword_positions[i] = -1;
+ }
+
+ while (scm_is_pair (rest)
+ && scm_is_keyword (scm_car (rest)))
+ {
+ SCM keyword = scm_car (rest);
+
+ i = lookup_keyword (keywords, keyword);
+ if (i < 0)
+ {
+ status = gdbscm_make_error (scm_arg_type_key, func_name,
+ _("Unrecognized keyword: ~a"),
+ scm_list_1 (keyword), keyword);
+ goto fail;
+ }
+ if (!scm_is_pair (scm_cdr (rest)))
+ {
+ status = gdbscm_make_error
+ (scm_arg_type_key, func_name,
+ _("Missing value for keyword argument"),
+ scm_list_1 (keyword), keyword);
+ goto fail;
+ }
+ keyword_args[i] = scm_cadr (rest);
+ keyword_positions[i] = position + 1;
+ rest = scm_cddr (rest);
+ position += 2;
+ }
+
+ for (i = 0; i < num_keywords; ++i)
+ {
+ int *arg_pos_ptr = va_arg (args, int *);
+ void *arg_ptr = va_arg (args, void *);
+ SCM arg = keyword_args[i];
+
+ if (! scm_is_eq (arg, SCM_UNSPECIFIED))
+ {
+ *arg_pos_ptr = keyword_positions[i];
+ status = extract_arg (p[i], arg, arg_ptr, func_name,
+ keyword_positions[i]);
+ if (!gdbscm_is_false (status))
+ goto fail;
+ if (p[i] == 's')
+ {
+ VEC_safe_push (char_ptr, allocated_strings,
+ *(char **) arg_ptr);
+ }
+ }
+ }
+ }
+
+ /* Process "rest" arguments. */
+
+ if (have_rest)
+ {
+ if (num_keywords > 0)
+ {
+ SCM *rest_ptr = va_arg (args, SCM *);
+
+ *rest_ptr = rest;
+ }
+ }
+ else
+ {
+ if (! scm_is_null (rest))
+ {
+ status = gdbscm_make_error (scm_args_number_key, func_name,
+ _("Too many arguments"),
+ SCM_EOL, SCM_BOOL_F);
+ goto fail;
+ }
+ }
+
+ va_end (args);
+ VEC_free (char_ptr, allocated_strings);
+ return;
+
+ fail:
+ va_end (args);
+ for (i = 0; VEC_iterate (char_ptr, allocated_strings, i, ptr); ++i)
+ xfree (ptr);
+ VEC_free (char_ptr, allocated_strings);
+ gdbscm_throw (status);
+}
+
+/* Return longest L as a scheme object. */
+
+SCM
+gdbscm_scm_from_longest (LONGEST l)
+{
+ return scm_from_int64 (l);
+}
+
+/* Convert scheme object L to LONGEST.
+ It is an error to call this if L is not an integer in range of LONGEST.
+ (because the underlying Scheme function will thrown an exception,
+ which is not part of our contract with the caller). */
+
+LONGEST
+gdbscm_scm_to_longest (SCM l)
+{
+ return scm_to_int64 (l);
+}
+
+/* Return unsigned longest L as a scheme object. */
+
+SCM
+gdbscm_scm_from_ulongest (ULONGEST l)
+{
+ return scm_from_uint64 (l);
+}
+
+/* Convert scheme object U to ULONGEST.
+ It is an error to call this if U is not an integer in range of ULONGEST
+ (because the underlying Scheme function will thrown an exception,
+ which is not part of our contract with the caller). */
+
+ULONGEST
+gdbscm_scm_to_ulongest (SCM u)
+{
+ return scm_to_uint64 (u);
+}
+
+/* Same as scm_dynwind_free, but uses xfree. */
+
+void
+gdbscm_dynwind_xfree (void *ptr)
+{
+ scm_dynwind_unwind_handler (xfree, ptr, SCM_F_WIND_EXPLICITLY);
+}
+
+/* Return non-zero if PROC is a procedure. */
+
+int
+gdbscm_is_procedure (SCM proc)
+{
+ return gdbscm_is_true (scm_procedure_p (proc));
+}
diff --git a/gdb/guile/scm-value.c b/gdb/guile/scm-value.c
new file mode 100644
index 0000000..f7f27cee
--- /dev/null
+++ b/gdb/guile/scm-value.c
@@ -0,0 +1,1485 @@
+/* Scheme interface to values.
+
+ Copyright (C) 2008-2014 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "charset.h"
+#include "cp-abi.h"
+#include "gdb_assert.h"
+#include "infcall.h"
+#include "symtab.h" /* Needed by language.h. */
+#include "language.h"
+#include "valprint.h"
+#include "value.h"
+#include "guile-internal.h"
+
+/* The <gdb:value> smob. */
+
+typedef struct _value_smob
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* Doubly linked list of values in values_in_scheme.
+ IWBN to use a chained_gdb_smob instead, which is doable, it just requires
+ a bit more casting than normal. */
+ struct _value_smob *next;
+ struct _value_smob *prev;
+
+ struct value *value;
+
+ /* These are cached here to avoid making multiple copies of them.
+ Plus computing the dynamic_type can be a bit expensive.
+ We use #f to indicate that the value doesn't exist (e.g. value doesn't
+ have an address), so we need another value to indicate that we haven't
+ computed the value yet. For this we use SCM_UNDEFINED. */
+ SCM address;
+ SCM type;
+ SCM dynamic_type;
+} value_smob;
+
+static const char value_smob_name[] = "gdb:value";
+
+/* The tag Guile knows the value smob by. */
+static scm_t_bits value_smob_tag;
+
+/* List of all values which are currently exposed to Scheme. It is
+ maintained so that when an objfile is discarded, preserve_values
+ can copy the values' types if needed. */
+static value_smob *values_in_scheme;
+
+/* Keywords used by Scheme procedures in this file. */
+static SCM type_keyword;
+static SCM encoding_keyword;
+static SCM errors_keyword;
+static SCM length_keyword;
+
+/* Possible #:errors values. */
+static SCM error_symbol;
+static SCM escape_symbol;
+static SCM substitute_symbol;
+
+/* Administrivia for value smobs. */
+
+/* Iterate over all the <gdb:value> objects, calling preserve_one_value on
+ each.
+ This is the extension_language_ops.preserve_values "method". */
+
+void
+gdbscm_preserve_values (const struct extension_language_defn *extlang,
+ struct objfile *objfile, htab_t copied_types)
+{
+ value_smob *iter;
+
+ for (iter = values_in_scheme; iter; iter = iter->next)
+ preserve_one_value (iter->value, objfile, copied_types);
+}
+
+/* Helper to add a value_smob to the global list. */
+
+static void
+vlscm_remember_scheme_value (value_smob *v_smob)
+{
+ v_smob->next = values_in_scheme;
+ if (v_smob->next)
+ v_smob->next->prev = v_smob;
+ v_smob->prev = NULL;
+ values_in_scheme = v_smob;
+}
+
+/* Helper to remove a value_smob from the global list. */
+
+static void
+vlscm_forget_value_smob (value_smob *v_smob)
+{
+ /* Remove SELF from the global list. */
+ if (v_smob->prev)
+ v_smob->prev->next = v_smob->next;
+ else
+ {
+ gdb_assert (values_in_scheme == v_smob);
+ values_in_scheme = v_smob->next;
+ }
+ if (v_smob->next)
+ v_smob->next->prev = v_smob->prev;
+}
+
+/* The smob "mark" function for <gdb:value>. */
+
+static SCM
+vlscm_mark_value_smob (SCM self)
+{
+ value_smob *v_smob = (value_smob *) SCM_SMOB_DATA (self);
+
+ scm_gc_mark (v_smob->address);
+ scm_gc_mark (v_smob->type);
+ scm_gc_mark (v_smob->dynamic_type);
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&v_smob->base);
+}
+
+/* The smob "free" function for <gdb:value>. */
+
+static size_t
+vlscm_free_value_smob (SCM self)
+{
+ value_smob *v_smob = (value_smob *) SCM_SMOB_DATA (self);
+
+ vlscm_forget_value_smob (v_smob);
+ value_free (v_smob->value);
+
+ return 0;
+}
+
+/* The smob "print" function for <gdb:value>. */
+
+static int
+vlscm_print_value_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ value_smob *v_smob = (value_smob *) SCM_SMOB_DATA (self);
+ char *s = NULL;
+ struct value_print_options opts;
+ volatile struct gdb_exception except;
+
+ if (pstate->writingp)
+ gdbscm_printf (port, "#<%s ", value_smob_name);
+
+ get_user_print_options (&opts);
+ opts.deref_ref = 0;
+
+ /* pstate->writingp = zero if invoked by display/~A, and nonzero if
+ invoked by write/~S. What to do here may need to evolve.
+ IWBN if we could pass an argument to format that would we could use
+ instead of writingp. */
+ opts.raw = !!pstate->writingp;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct ui_file *stb = mem_fileopen ();
+ struct cleanup *old_chain = make_cleanup_ui_file_delete (stb);
+
+ common_val_print (v_smob->value, stb, 0, &opts, current_language);
+ s = ui_file_xstrdup (stb, NULL);
+
+ do_cleanups (old_chain);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (s != NULL)
+ {
+ scm_puts (s, port);
+ xfree (s);
+ }
+
+ if (pstate->writingp)
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* The smob "equalp" function for <gdb:value>. */
+
+static SCM
+vlscm_equal_p_value_smob (SCM v1, SCM v2)
+{
+ const value_smob *v1_smob = (value_smob *) SCM_SMOB_DATA (v1);
+ const value_smob *v2_smob = (value_smob *) SCM_SMOB_DATA (v2);
+ int result = 0;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ result = value_equal (v1_smob->value, v2_smob->value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (result);
+}
+
+/* Low level routine to create a <gdb:value> object. */
+
+static SCM
+vlscm_make_value_smob (void)
+{
+ value_smob *v_smob = (value_smob *)
+ scm_gc_malloc (sizeof (value_smob), value_smob_name);
+ SCM v_scm;
+
+ /* These must be filled in by the caller. */
+ v_smob->value = NULL;
+ v_smob->prev = NULL;
+ v_smob->next = NULL;
+
+ /* These are lazily computed. */
+ v_smob->address = SCM_UNDEFINED;
+ v_smob->type = SCM_UNDEFINED;
+ v_smob->dynamic_type = SCM_UNDEFINED;
+
+ v_scm = scm_new_smob (value_smob_tag, (scm_t_bits) v_smob);
+ gdbscm_init_gsmob (&v_smob->base);
+
+ return v_scm;
+}
+
+/* Return non-zero if SCM is a <gdb:value> object. */
+
+int
+vlscm_is_value (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (value_smob_tag, scm);
+}
+
+/* (value? object) -> boolean */
+
+static SCM
+gdbscm_value_p (SCM scm)
+{
+ return scm_from_bool (vlscm_is_value (scm));
+}
+
+/* Create a new <gdb:value> object that encapsulates VALUE.
+ The value is released from the all_values chain so its lifetime is not
+ bound to the execution of a command. */
+
+SCM
+vlscm_scm_from_value (struct value *value)
+{
+ /* N.B. It's important to not cause any side-effects until we know the
+ conversion worked. */
+ SCM v_scm = vlscm_make_value_smob ();
+ value_smob *v_smob = (value_smob *) SCM_SMOB_DATA (v_scm);
+
+ v_smob->value = value;
+ release_value_or_incref (value);
+ vlscm_remember_scheme_value (v_smob);
+
+ return v_scm;
+}
+
+/* Returns the <gdb:value> object in SELF.
+ Throws an exception if SELF is not a <gdb:value> object. */
+
+static SCM
+vlscm_get_value_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (vlscm_is_value (self), self, arg_pos, func_name,
+ value_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the value smob of SELF.
+ Throws an exception if SELF is not a <gdb:value> object. */
+
+static value_smob *
+vlscm_get_value_smob_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM v_scm = vlscm_get_value_arg_unsafe (self, arg_pos, func_name);
+ value_smob *v_smob = (value_smob *) SCM_SMOB_DATA (v_scm);
+
+ return v_smob;
+}
+
+/* Return the value field of V_SCM, an object of type <gdb:value>.
+ This exists so that we don't have to export the struct's contents. */
+
+struct value *
+vlscm_scm_to_value (SCM v_scm)
+{
+ value_smob *v_smob;
+
+ gdb_assert (vlscm_is_value (v_scm));
+ v_smob = (value_smob *) SCM_SMOB_DATA (v_scm);
+ return v_smob->value;
+}
+
+/* Value methods. */
+
+/* (make-value x [#:type type]) -> <gdb:value> */
+
+static SCM
+gdbscm_make_value (SCM x, SCM rest)
+{
+ struct gdbarch *gdbarch = get_current_arch ();
+ const struct language_defn *language = current_language;
+ const SCM keywords[] = { type_keyword, SCM_BOOL_F };
+ int type_arg_pos = -1;
+ SCM type_scm = SCM_UNDEFINED;
+ SCM except_scm, result;
+ type_smob *t_smob;
+ struct type *type = NULL;
+ struct value *value;
+ struct cleanup *cleanups;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "#O", rest,
+ &type_arg_pos, &type_scm);
+
+ if (type_arg_pos > 0)
+ {
+ t_smob = tyscm_get_type_smob_arg_unsafe (type_scm, type_arg_pos,
+ FUNC_NAME);
+ type = tyscm_type_smob_type (t_smob);
+ }
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ value = vlscm_convert_typed_value_from_scheme (FUNC_NAME, SCM_ARG1, x,
+ type_arg_pos, type_scm, type,
+ &except_scm,
+ gdbarch, language);
+ if (value == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+
+ result = vlscm_scm_from_value (value);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+ return result;
+}
+
+/* (make-lazy-value <gdb:type> address) -> <gdb:value> */
+
+static SCM
+gdbscm_make_lazy_value (SCM type_scm, SCM address_scm)
+{
+ type_smob *t_smob;
+ struct type *type;
+ ULONGEST address;
+ struct value *value = NULL;
+ SCM result;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ t_smob = tyscm_get_type_smob_arg_unsafe (type_scm, SCM_ARG1, FUNC_NAME);
+ type = tyscm_type_smob_type (t_smob);
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, NULL, "U",
+ address_scm, &address);
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ /* There's no (current) need to wrap this in a TRY_CATCH, but for consistency
+ and future-proofing we do. */
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ value = value_from_contents_and_address (type, NULL, address);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ result = vlscm_scm_from_value (value);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+ return result;
+}
+
+/* (value-optimized-out? <gdb:value>) -> boolean */
+
+static SCM
+gdbscm_value_optimized_out_p (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ int opt = 0;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ opt = value_optimized_out (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (opt);
+}
+
+/* (value-address <gdb:value>) -> integer
+ Returns #f if the value doesn't have one. */
+
+static SCM
+gdbscm_value_address (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+
+ if (SCM_UNBNDP (v_smob->address))
+ {
+ struct value *res_val = NULL;
+ struct cleanup *cleanup
+ = make_cleanup_value_free_to_mark (value_mark ());
+ SCM address;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ res_val = value_addr (value);
+ }
+ if (except.reason < 0)
+ address = SCM_BOOL_F;
+ else
+ address = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanup);
+
+ if (gdbscm_is_exception (address))
+ gdbscm_throw (address);
+
+ v_smob->address = address;
+ }
+
+ return v_smob->address;
+}
+
+/* (value-dereference <gdb:value>) -> <gdb:value>
+ Given a value of a pointer type, apply the C unary * operator to it. */
+
+static SCM
+gdbscm_value_dereference (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ SCM result;
+ struct value *res_val = NULL;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ res_val = value_ind (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-referenced-value <gdb:value>) -> <gdb:value>
+ Given a value of a reference type, return the value referenced.
+ The difference between this function and gdbscm_value_dereference is that
+ the latter applies * unary operator to a value, which need not always
+ result in the value referenced.
+ For example, for a value which is a reference to an 'int' pointer ('int *'),
+ gdbscm_value_dereference will result in a value of type 'int' while
+ gdbscm_value_referenced_value will result in a value of type 'int *'. */
+
+static SCM
+gdbscm_value_referenced_value (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ SCM result;
+ struct value *res_val = NULL;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ switch (TYPE_CODE (check_typedef (value_type (value))))
+ {
+ case TYPE_CODE_PTR:
+ res_val = value_ind (value);
+ break;
+ case TYPE_CODE_REF:
+ res_val = coerce_ref (value);
+ break;
+ default:
+ error (_("Trying to get the referenced value from a value which is"
+ " neither a pointer nor a reference"));
+ }
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-type <gdb:value>) -> <gdb:type> */
+
+static SCM
+gdbscm_value_type (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+
+ if (SCM_UNBNDP (v_smob->type))
+ v_smob->type = tyscm_scm_from_type (value_type (value));
+
+ return v_smob->type;
+}
+
+/* (value-dynamic-type <gdb:value>) -> <gdb:type> */
+
+static SCM
+gdbscm_value_dynamic_type (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct type *type = NULL;
+ volatile struct gdb_exception except;
+
+ if (! SCM_UNBNDP (v_smob->type))
+ return v_smob->dynamic_type;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup
+ = make_cleanup_value_free_to_mark (value_mark ());
+
+ type = value_type (value);
+ CHECK_TYPEDEF (type);
+
+ if (((TYPE_CODE (type) == TYPE_CODE_PTR)
+ || (TYPE_CODE (type) == TYPE_CODE_REF))
+ && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_CLASS))
+ {
+ struct value *target;
+ int was_pointer = TYPE_CODE (type) == TYPE_CODE_PTR;
+
+ target = value_ind (value);
+ type = value_rtti_type (target, NULL, NULL, NULL);
+
+ if (type)
+ {
+ if (was_pointer)
+ type = lookup_pointer_type (type);
+ else
+ type = lookup_reference_type (type);
+ }
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_CLASS)
+ type = value_rtti_type (value, NULL, NULL, NULL);
+ else
+ {
+ /* Re-use object's static type. */
+ type = NULL;
+ }
+
+ do_cleanups (cleanup);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (type == NULL)
+ v_smob->dynamic_type = gdbscm_value_type (self);
+ else
+ v_smob->dynamic_type = tyscm_scm_from_type (type);
+
+ return v_smob->dynamic_type;
+}
+
+/* A helper function that implements the various cast operators. */
+
+static SCM
+vlscm_do_cast (SCM self, SCM type_scm, enum exp_opcode op,
+ const char *func_name)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ type_smob *t_smob
+ = tyscm_get_type_smob_arg_unsafe (type_scm, SCM_ARG2, FUNC_NAME);
+ struct type *type = tyscm_type_smob_type (t_smob);
+ SCM result;
+ struct value *res_val = NULL;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (op == UNOP_DYNAMIC_CAST)
+ res_val = value_dynamic_cast (type, value);
+ else if (op == UNOP_REINTERPRET_CAST)
+ res_val = value_reinterpret_cast (type, value);
+ else
+ {
+ gdb_assert (op == UNOP_CAST);
+ res_val = value_cast (type, value);
+ }
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-cast <gdb:value> <gdb:type>) -> <gdb:value> */
+
+static SCM
+gdbscm_value_cast (SCM self, SCM new_type)
+{
+ return vlscm_do_cast (self, new_type, UNOP_CAST, FUNC_NAME);
+}
+
+/* (value-dynamic-cast <gdb:value> <gdb:type>) -> <gdb:value> */
+
+static SCM
+gdbscm_value_dynamic_cast (SCM self, SCM new_type)
+{
+ return vlscm_do_cast (self, new_type, UNOP_DYNAMIC_CAST, FUNC_NAME);
+}
+
+/* (value-reinterpret-cast <gdb:value> <gdb:type>) -> <gdb:value> */
+
+static SCM
+gdbscm_value_reinterpret_cast (SCM self, SCM new_type)
+{
+ return vlscm_do_cast (self, new_type, UNOP_REINTERPRET_CAST, FUNC_NAME);
+}
+
+/* (value-field <gdb:value> string) -> <gdb:value>
+ Given string name of an element inside structure, return its <gdb:value>
+ object. */
+
+static SCM
+gdbscm_value_field (SCM self, SCM field_scm)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ char *field = NULL;
+ struct value *res_val = NULL;
+ SCM result;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (scm_is_string (field_scm), field_scm, SCM_ARG2, FUNC_NAME,
+ _("string"));
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ field = gdbscm_scm_to_c_string (field_scm);
+ make_cleanup (xfree, field);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct value *tmp = value;
+
+ res_val = value_struct_elt (&tmp, NULL, field, NULL, NULL);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-subscript <gdb:value> integer|<gdb:value>) -> <gdb:value>
+ Return the specified value in an array. */
+
+static SCM
+gdbscm_value_subscript (SCM self, SCM index_scm)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct value *index = NULL;
+ struct value *res_val = NULL;
+ struct type *type = value_type (value);
+ struct gdbarch *gdbarch;
+ SCM result, except_scm;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ /* The sequencing here, as everywhere else, is important.
+ We can't have existing cleanups when a Scheme exception is thrown. */
+
+ SCM_ASSERT (type != NULL, self, SCM_ARG2, FUNC_NAME);
+ gdbarch = get_type_arch (type);
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+
+ index = vlscm_convert_value_from_scheme (FUNC_NAME, SCM_ARG2, index_scm,
+ &except_scm,
+ gdbarch, current_language);
+ if (index == NULL)
+ {
+ do_cleanups (cleanups);
+ gdbscm_throw (except_scm);
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct value *tmp = value;
+
+ /* Assume we are attempting an array access, and let the value code
+ throw an exception if the index has an invalid type.
+ Check the value's type is something that can be accessed via
+ a subscript. */
+ tmp = coerce_ref (tmp);
+ type = check_typedef (value_type (tmp));
+ if (TYPE_CODE (type) != TYPE_CODE_ARRAY
+ && TYPE_CODE (type) != TYPE_CODE_PTR)
+ error (_("Cannot subscript requested type"));
+
+ res_val = value_subscript (tmp, value_as_long (index));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-call <gdb:value> arg-list) -> <gdb:value>
+ Perform an inferior function call on the value. */
+
+static SCM
+gdbscm_value_call (SCM self, SCM args)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *function = v_smob->value;
+ struct value *mark = value_mark ();
+ struct type *ftype = NULL;
+ long args_count;
+ struct value **vargs = NULL;
+ SCM result = SCM_BOOL_F;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ ftype = check_typedef (value_type (function));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ SCM_ASSERT_TYPE (TYPE_CODE (ftype) == TYPE_CODE_FUNC, self,
+ SCM_ARG1, FUNC_NAME,
+ _("function (value of TYPE_CODE_FUNC)"));
+
+ SCM_ASSERT_TYPE (gdbscm_is_true (scm_list_p (args)), args,
+ SCM_ARG2, FUNC_NAME, _("list"));
+
+ args_count = scm_ilength (args);
+ if (args_count > 0)
+ {
+ struct gdbarch *gdbarch = get_current_arch ();
+ const struct language_defn *language = current_language;
+ SCM except_scm;
+ long i;
+
+ vargs = alloca (sizeof (struct value *) * args_count);
+ for (i = 0; i < args_count; i++)
+ {
+ SCM arg = scm_car (args);
+
+ vargs[i] = vlscm_convert_value_from_scheme (FUNC_NAME,
+ GDBSCM_ARG_NONE, arg,
+ &except_scm,
+ gdbarch, language);
+ if (vargs[i] == NULL)
+ gdbscm_throw (except_scm);
+
+ args = scm_cdr (args);
+ }
+ gdb_assert (gdbscm_is_true (scm_null_p (args)));
+ }
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup = make_cleanup_value_free_to_mark (mark);
+ struct value *return_value;
+
+ return_value = call_function_by_hand (function, args_count, vargs);
+ result = vlscm_scm_from_value (return_value);
+ do_cleanups (cleanup);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value->bytevector <gdb:value>) -> bytevector */
+
+static SCM
+gdbscm_value_to_bytevector (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct type *type;
+ size_t length = 0;
+ const gdb_byte *contents = NULL;
+ SCM bv;
+ volatile struct gdb_exception except;
+
+ type = value_type (value);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ CHECK_TYPEDEF (type);
+ length = TYPE_LENGTH (type);
+ contents = value_contents (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ bv = scm_c_make_bytevector (length);
+ memcpy (SCM_BYTEVECTOR_CONTENTS (bv), contents, length);
+
+ return bv;
+}
+
+/* Helper function to determine if a type is "int-like". */
+
+static int
+is_intlike (struct type *type, int ptr_ok)
+{
+ return (TYPE_CODE (type) == TYPE_CODE_INT
+ || TYPE_CODE (type) == TYPE_CODE_ENUM
+ || TYPE_CODE (type) == TYPE_CODE_BOOL
+ || TYPE_CODE (type) == TYPE_CODE_CHAR
+ || (ptr_ok && TYPE_CODE (type) == TYPE_CODE_PTR));
+}
+
+/* (value->bool <gdb:value>) -> boolean
+ Throws an error if the value is not integer-like. */
+
+static SCM
+gdbscm_value_to_bool (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct type *type;
+ LONGEST l = 0;
+ volatile struct gdb_exception except;
+
+ type = value_type (value);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ CHECK_TYPEDEF (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ SCM_ASSERT_TYPE (is_intlike (type, 1), self, SCM_ARG1, FUNC_NAME,
+ _("integer-like gdb value"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ l = value_as_address (value);
+ else
+ l = value_as_long (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return scm_from_bool (l != 0);
+}
+
+/* (value->integer <gdb:value>) -> integer
+ Throws an error if the value is not integer-like. */
+
+static SCM
+gdbscm_value_to_integer (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct type *type;
+ LONGEST l = 0;
+ volatile struct gdb_exception except;
+
+ type = value_type (value);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ CHECK_TYPEDEF (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ SCM_ASSERT_TYPE (is_intlike (type, 1), self, SCM_ARG1, FUNC_NAME,
+ _("integer-like gdb value"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ l = value_as_address (value);
+ else
+ l = value_as_long (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (TYPE_UNSIGNED (type))
+ return gdbscm_scm_from_ulongest (l);
+ else
+ return gdbscm_scm_from_longest (l);
+}
+
+/* (value->real <gdb:value>) -> real
+ Throws an error if the value is not a number. */
+
+static SCM
+gdbscm_value_to_real (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct type *type;
+ DOUBLEST d = 0;
+ volatile struct gdb_exception except;
+
+ type = value_type (value);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ CHECK_TYPEDEF (type);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ SCM_ASSERT_TYPE (is_intlike (type, 0) || TYPE_CODE (type) == TYPE_CODE_FLT,
+ self, SCM_ARG1, FUNC_NAME, _("number"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ d = value_as_double (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ /* TODO: Is there a better way to check if the value fits? */
+ if (d != (double) d)
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self,
+ _("number can't be converted to a double"));
+
+ return scm_from_double (d);
+}
+
+/* (value->string <gdb:value>
+ [#:encoding encoding]
+ [#:errors #f | 'error | 'substitute]
+ [#:length length])
+ -> string
+ Return Unicode string with value's contents, which must be a string.
+
+ If ENCODING is not given, the string is assumed to be encoded in
+ the target's charset.
+
+ ERRORS is one of #f, 'error or 'substitute.
+ An error setting of #f means use the default, which is
+ Guile's %default-port-conversion-strategy. If the default is not one
+ of 'error or 'substitute, 'substitute is used.
+ An error setting of "error" causes an exception to be thrown if there's
+ a decoding error. An error setting of "substitute" causes invalid
+ characters to be replaced with "?".
+
+ If LENGTH is provided, only fetch string to the length provided.
+ LENGTH must be a Scheme integer, it can't be a <gdb:value> integer. */
+
+static SCM
+gdbscm_value_to_string (SCM self, SCM rest)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ const SCM keywords[] = {
+ encoding_keyword, errors_keyword, length_keyword, SCM_BOOL_F
+ };
+ int encoding_arg_pos = -1, errors_arg_pos = -1, length_arg_pos = -1;
+ char *encoding = NULL;
+ SCM errors = SCM_BOOL_F;
+ int length = -1;
+ gdb_byte *buffer = NULL;
+ const char *la_encoding = NULL;
+ struct type *char_type = NULL;
+ SCM result;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ /* The sequencing here, as everywhere else, is important.
+ We can't have existing cleanups when a Scheme exception is thrown. */
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "#sOi", rest,
+ &encoding_arg_pos, &encoding,
+ &errors_arg_pos, &errors,
+ &length_arg_pos, &length);
+
+ cleanups = make_cleanup (xfree, encoding);
+
+ if (errors_arg_pos > 0
+ && errors != SCM_BOOL_F
+ && !scm_is_eq (errors, error_symbol)
+ && !scm_is_eq (errors, substitute_symbol))
+ {
+ SCM excp
+ = gdbscm_make_out_of_range_error (FUNC_NAME, errors_arg_pos, errors,
+ _("invalid error kind"));
+
+ do_cleanups (cleanups);
+ gdbscm_throw (excp);
+ }
+ if (errors == SCM_BOOL_F)
+ errors = scm_port_conversion_strategy (SCM_BOOL_F);
+ /* We don't assume anything about the result of scm_port_conversion_strategy.
+ From this point on, if errors is not 'errors, use 'substitute. */
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ LA_GET_STRING (value, &buffer, &length, &char_type, &la_encoding);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ /* If errors is "error" scm_from_stringn may throw a Scheme exception.
+ Make sure we don't leak. This is done via scm_dynwind_begin, et.al. */
+ discard_cleanups (cleanups);
+
+ scm_dynwind_begin (0);
+
+ gdbscm_dynwind_xfree (encoding);
+ gdbscm_dynwind_xfree (buffer);
+
+ result = scm_from_stringn ((const char *) buffer,
+ length * TYPE_LENGTH (char_type),
+ (encoding != NULL && *encoding != '\0'
+ ? encoding
+ : la_encoding),
+ scm_is_eq (errors, error_symbol)
+ ? SCM_FAILED_CONVERSION_ERROR
+ : SCM_FAILED_CONVERSION_QUESTION_MARK);
+
+ scm_dynwind_end ();
+
+ return result;
+}
+
+/* (value->lazy-string <gdb:value> [#:encoding encoding] [#:length length])
+ -> <gdb:lazy-string>
+ Return a Scheme object representing a lazy_string_object type.
+ A lazy string is a pointer to a string with an optional encoding and length.
+ If ENCODING is not given, the target's charset is used.
+ If LENGTH is provided then the length parameter is set to LENGTH, otherwise
+ length will be set to -1 (first null of appropriate with).
+ LENGTH must be a Scheme integer, it can't be a <gdb:value> integer. */
+
+static SCM
+gdbscm_value_to_lazy_string (SCM self, SCM rest)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ const SCM keywords[] = { encoding_keyword, length_keyword, SCM_BOOL_F };
+ int encoding_arg_pos = -1, length_arg_pos = -1;
+ char *encoding = NULL;
+ int length = -1;
+ SCM result = SCM_BOOL_F; /* -Wall */
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ /* The sequencing here, as everywhere else, is important.
+ We can't have existing cleanups when a Scheme exception is thrown. */
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "#si", rest,
+ &encoding_arg_pos, &encoding,
+ &length_arg_pos, &length);
+
+ cleanups = make_cleanup (xfree, encoding);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *inner_cleanup
+ = make_cleanup_value_free_to_mark (value_mark ());
+
+ if (TYPE_CODE (value_type (value)) == TYPE_CODE_PTR)
+ value = value_ind (value);
+
+ result = lsscm_make_lazy_string (value_address (value), length,
+ encoding, value_type (value));
+
+ do_cleanups (inner_cleanup);
+ }
+ do_cleanups (cleanups);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (value-lazy? <gdb:value>) -> boolean */
+
+static SCM
+gdbscm_value_lazy_p (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+
+ return scm_from_bool (value_lazy (value));
+}
+
+/* (value-fetch-lazy! <gdb:value>) -> unspecified */
+
+static SCM
+gdbscm_value_fetch_lazy_x (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (value_lazy (value))
+ value_fetch_lazy (value);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (value-print <gdb:value>) -> string */
+
+static SCM
+gdbscm_value_print (SCM self)
+{
+ value_smob *v_smob
+ = vlscm_get_value_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct value *value = v_smob->value;
+ struct value_print_options opts;
+ char *s = NULL;
+ SCM result;
+ volatile struct gdb_exception except;
+
+ get_user_print_options (&opts);
+ opts.deref_ref = 0;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct ui_file *stb = mem_fileopen ();
+ struct cleanup *old_chain = make_cleanup_ui_file_delete (stb);
+
+ common_val_print (value, stb, 0, &opts, current_language);
+ s = ui_file_xstrdup (stb, NULL);
+
+ do_cleanups (old_chain);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ /* Use SCM_FAILED_CONVERSION_QUESTION_MARK to ensure this doesn't
+ throw an error if the encoding fails.
+ IWBN to use scm_take_locale_string here, but we'd have to temporarily
+ override the default port conversion handler because contrary to
+ documentation it doesn't necessarily free the input string. */
+ result = scm_from_stringn (s, strlen (s), host_charset (),
+ SCM_FAILED_CONVERSION_QUESTION_MARK);
+ xfree (s);
+
+ return result;
+}
+
+/* (parse-and-eval string) -> <gdb:value>
+ Parse a string and evaluate the string as an expression. */
+
+static SCM
+gdbscm_parse_and_eval (SCM expr_scm)
+{
+ char *expr_str;
+ struct value *res_val = NULL;
+ SCM result;
+ struct cleanup *cleanups;
+ volatile struct gdb_exception except;
+
+ /* The sequencing here, as everywhere else, is important.
+ We can't have existing cleanups when a Scheme exception is thrown. */
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "s",
+ expr_scm, &expr_str);
+
+ cleanups = make_cleanup_value_free_to_mark (value_mark ());
+ make_cleanup (xfree, expr_str);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ res_val = parse_and_eval (expr_str);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
+
+ gdb_assert (res_val != NULL);
+ result = vlscm_scm_from_value (res_val);
+
+ do_cleanups (cleanups);
+
+ if (gdbscm_is_exception (result))
+ gdbscm_throw (result);
+
+ return result;
+}
+
+/* (history-ref integer) -> <gdb:value>
+ Return the specified value from GDB's value history. */
+
+static SCM
+gdbscm_history_ref (SCM index)
+{
+ int i;
+ struct value *res_val = NULL; /* Initialize to appease gcc warning. */
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, NULL, "i", index, &i);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ res_val = access_value_history (i);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return vlscm_scm_from_value (res_val);
+}
+
+/* Initialize the Scheme value code. */
+
+static const scheme_function value_functions[] =
+{
+ { "value?", 1, 0, 0, gdbscm_value_p,
+ "\
+Return #t if the object is a <gdb:value> object." },
+
+ { "make-value", 1, 0, 1, gdbscm_make_value,
+ "\
+Create a <gdb:value> representing object.\n\
+Typically this is used to convert numbers and strings to\n\
+<gdb:value> objects.\n\
+\n\
+ Arguments: object [#:type <gdb:type>]" },
+
+ { "value-optimized-out?", 1, 0, 0, gdbscm_value_optimized_out_p,
+ "\
+Return #t if the value has been optimizd out." },
+
+ { "value-address", 1, 0, 0, gdbscm_value_address,
+ "\
+Return the address of the value." },
+
+ { "value-type", 1, 0, 0, gdbscm_value_type,
+ "\
+Return the type of the value." },
+
+ { "value-dynamic-type", 1, 0, 0, gdbscm_value_dynamic_type,
+ "\
+Return the dynamic type of the value." },
+
+ { "value-cast", 2, 0, 0, gdbscm_value_cast,
+ "\
+Cast the value to the supplied type.\n\
+\n\
+ Arguments: <gdb:value> <gdb:type>" },
+
+ { "value-dynamic-cast", 2, 0, 0, gdbscm_value_dynamic_cast,
+ "\
+Cast the value to the supplied type, as if by the C++\n\
+dynamic_cast operator.\n\
+\n\
+ Arguments: <gdb:value> <gdb:type>" },
+
+ { "value-reinterpret-cast", 2, 0, 0, gdbscm_value_reinterpret_cast,
+ "\
+Cast the value to the supplied type, as if by the C++\n\
+reinterpret_cast operator.\n\
+\n\
+ Arguments: <gdb:value> <gdb:type>" },
+
+ { "value-dereference", 1, 0, 0, gdbscm_value_dereference,
+ "\
+Return the result of applying the C unary * operator to the value." },
+
+ { "value-referenced-value", 1, 0, 0, gdbscm_value_referenced_value,
+ "\
+Given a value of a reference type, return the value referenced.\n\
+The difference between this function and value-dereference is that\n\
+the latter applies * unary operator to a value, which need not always\n\
+result in the value referenced.\n\
+For example, for a value which is a reference to an 'int' pointer ('int *'),\n\
+value-dereference will result in a value of type 'int' while\n\
+value-referenced-value will result in a value of type 'int *'." },
+
+ { "value-field", 2, 0, 0, gdbscm_value_field,
+ "\
+Return the specified field of the value.\n\
+\n\
+ Arguments: <gdb:value> string" },
+
+ { "value-subscript", 2, 0, 0, gdbscm_value_subscript,
+ "\
+Return the value of the array at the specified index.\n\
+\n\
+ Arguments: <gdb:value> integer" },
+
+ { "value-call", 2, 0, 0, gdbscm_value_call,
+ "\
+Perform an inferior function call taking the value as a pointer to the\n\
+function to call.\n\
+Each element of the argument list must be a <gdb:value> object or an object\n\
+that can be converted to one.\n\
+The result is the value returned by the function.\n\
+\n\
+ Arguments: <gdb:value> arg-list" },
+
+ { "value->bool", 1, 0, 0, gdbscm_value_to_bool,
+ "\
+Return the Scheme boolean representing the GDB value.\n\
+The value must be \"integer like\". Pointers are ok." },
+
+ { "value->integer", 1, 0, 0, gdbscm_value_to_integer,
+ "\
+Return the Scheme integer representing the GDB value.\n\
+The value must be \"integer like\". Pointers are ok." },
+
+ { "value->real", 1, 0, 0, gdbscm_value_to_real,
+ "\
+Return the Scheme real number representing the GDB value.\n\
+The value must be a number." },
+
+ { "value->bytevector", 1, 0, 0, gdbscm_value_to_bytevector,
+ "\
+Return a Scheme bytevector with the raw contents of the GDB value.\n\
+No transformation, endian or otherwise, is performed." },
+
+ { "value->string", 1, 0, 1, gdbscm_value_to_string,
+ "\
+Return the Unicode string of the value's contents.\n\
+If ENCODING is not given, the string is assumed to be encoded in\n\
+the target's charset.\n\
+An error setting \"error\" causes an exception to be thrown if there's\n\
+a decoding error. An error setting of \"substitute\" causes invalid\n\
+characters to be replaced with \"?\". The default is \"error\".\n\
+If LENGTH is provided, only fetch string to the length provided.\n\
+\n\
+ Arguments: <gdb:value>\n\
+ [#:encoding encoding] [#:errors \"error\"|\"substitute\"]\n\
+ [#:length length]" },
+
+ { "value->lazy-string", 1, 0, 1, gdbscm_value_to_lazy_string,
+ "\
+Return a Scheme object representing a lazily fetched Unicode string\n\
+of the value's contents.\n\
+If ENCODING is not given, the string is assumed to be encoded in\n\
+the target's charset.\n\
+If LENGTH is provided, only fetch string to the length provided.\n\
+\n\
+ Arguments: <gdb:value> [#:encoding encoding] [#:length length]" },
+
+ { "value-lazy?", 1, 0, 0, gdbscm_value_lazy_p,
+ "\
+Return #t if the value is lazy (not fetched yet from the inferior).\n\
+A lazy value is fetched when needed, or when the value-fetch-lazy! function\n\
+is called." },
+
+ { "make-lazy-value", 2, 0, 0, gdbscm_make_lazy_value,
+ "\
+Create a <gdb:value> that will be lazily fetched from the target.\n\
+\n\
+ Arguments: <gdb:type> address" },
+
+ { "value-fetch-lazy!", 1, 0, 0, gdbscm_value_fetch_lazy_x,
+ "\
+Fetch the value from the inferior, if it was lazy.\n\
+The result is \"unspecified\"." },
+
+ { "value-print", 1, 0, 0, gdbscm_value_print,
+ "\
+Return the string representation (print form) of the value." },
+
+ { "parse-and-eval", 1, 0, 0, gdbscm_parse_and_eval,
+ "\
+Evaluates string in gdb and returns the result as a <gdb:value> object." },
+
+ { "history-ref", 1, 0, 0, gdbscm_history_ref,
+ "\
+Return the specified value from GDB's value history." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_values (void)
+{
+ value_smob_tag = gdbscm_make_smob_type (value_smob_name,
+ sizeof (value_smob));
+ scm_set_smob_mark (value_smob_tag, vlscm_mark_value_smob);
+ scm_set_smob_free (value_smob_tag, vlscm_free_value_smob);
+ scm_set_smob_print (value_smob_tag, vlscm_print_value_smob);
+ scm_set_smob_equalp (value_smob_tag, vlscm_equal_p_value_smob);
+
+ gdbscm_define_functions (value_functions, 1);
+
+ type_keyword = scm_from_latin1_keyword ("type");
+ encoding_keyword = scm_from_latin1_keyword ("encoding");
+ errors_keyword = scm_from_latin1_keyword ("errors");
+ length_keyword = scm_from_latin1_keyword ("length");
+
+ error_symbol = scm_from_latin1_symbol ("error");
+ escape_symbol = scm_from_latin1_symbol ("escape");
+ substitute_symbol = scm_from_latin1_symbol ("substitute");
+}