ocaml/debugger/loadprinter.ml

176 lines
5.8 KiB
OCaml

(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(* Loading and installation of user-defined printer functions *)
open Misc
open Longident
open Types
(* Error report *)
type error =
| Load_failure of Dynlink.error
| Unbound_identifier of Longident.t
| Unavailable_module of string * Longident.t
| Wrong_type of Longident.t
| No_active_printer of Longident.t
exception Error of error
(* Load a .cmo or .cma file *)
open Format
let rec loadfiles ppf name =
try
let filename = Load_path.find name in
Dynlink.allow_unsafe_modules true;
Dynlink.loadfile filename;
let d = Filename.dirname name in
if d <> Filename.current_dir_name then begin
if not (List.mem d (Load_path.get_paths ())) then
Load_path.add_dir d;
end;
fprintf ppf "File %s loaded@."
(if d <> Filename.current_dir_name then
filename
else
Filename.basename filename);
true
with
| Dynlink.Error (Dynlink.Unavailable_unit unit) ->
loadfiles ppf (String.uncapitalize_ascii unit ^ ".cmo")
&&
loadfiles ppf name
| Not_found ->
fprintf ppf "Cannot find file %s@." name;
false
| Sys_error msg ->
fprintf ppf "%s: %s@." name msg;
false
| Dynlink.Error e ->
raise(Error(Load_failure e))
let loadfile ppf name =
ignore(loadfiles ppf name)
(* Return the value referred to by a path (as in toplevel/topdirs) *)
(* Note: evaluation proceeds in the debugger memory space, not in
the debuggee. *)
let rec eval_address = function
| Env.Aident id ->
assert (Ident.persistent id);
let bytecode_or_asm_symbol = Ident.name id in
begin match Dynlink.unsafe_get_global_value ~bytecode_or_asm_symbol with
| None ->
raise (Symtable.Error (Symtable.Undefined_global bytecode_or_asm_symbol))
| Some obj -> obj
end
| Env.Adot(addr, pos) -> Obj.field (eval_address addr) pos
let eval_value_path env path =
match Env.find_value_address path env with
| addr -> eval_address addr
| exception Not_found ->
fatal_error ("Cannot find address for: " ^ (Path.name path))
(* Install, remove a printer (as in toplevel/topdirs) *)
(* since 4.00, "topdirs.cmi" is not in the same directory as the standard
library, so we load it beforehand as it cannot be found in the search path. *)
let init () =
let topdirs =
Filename.concat !Parameters.topdirs_path "topdirs.cmi" in
ignore (Env.read_signature "Topdirs" topdirs)
let match_printer_type desc typename =
let printer_type =
match
Env.find_type_by_name
(Ldot(Lident "Topdirs", typename)) Env.empty
with
| path, _ -> path
| exception Not_found ->
raise (Error(Unbound_identifier(Ldot(Lident "Topdirs", typename))))
in
Ctype.begin_def();
let ty_arg = Ctype.newvar() in
Ctype.unify Env.empty
(Ctype.newconstr printer_type [ty_arg])
(Ctype.instance desc.val_type);
Ctype.end_def();
Ctype.generalize ty_arg;
ty_arg
let find_printer_type lid =
match Env.find_value_by_name lid Env.empty with
| (path, desc) -> begin
match match_printer_type desc "printer_type_new" with
| ty_arg -> (ty_arg, path, false)
| exception Ctype.Unify _ -> begin
match match_printer_type desc "printer_type_old" with
| ty_arg -> (ty_arg, path, true)
| exception Ctype.Unify _ -> raise(Error(Wrong_type lid))
end
end
| exception Not_found ->
raise(Error(Unbound_identifier lid))
let install_printer ppf lid =
let (ty_arg, path, is_old_style) = find_printer_type lid in
let v =
try
eval_value_path Env.empty path
with Symtable.Error(Symtable.Undefined_global s) ->
raise(Error(Unavailable_module(s, lid))) in
let print_function =
if is_old_style then
(fun _formatter repr -> Obj.obj v (Obj.obj repr))
else
(fun formatter repr -> Obj.obj v formatter (Obj.obj repr)) in
Printval.install_printer path ty_arg ppf print_function
let remove_printer lid =
let (_ty_arg, path, _is_old_style) = find_printer_type lid in
try
Printval.remove_printer path
with Not_found ->
raise(Error(No_active_printer lid))
(* Error report *)
open Format
let report_error ppf = function
| Load_failure e ->
fprintf ppf "@[Error during code loading: %s@]@."
(Dynlink.error_message e)
| Unbound_identifier lid ->
fprintf ppf "@[Unbound identifier %a@]@."
Printtyp.longident lid
| Unavailable_module(md, lid) ->
fprintf ppf
"@[The debugger does not contain the code for@ %a.@ \
Please load an implementation of %s first.@]@."
Printtyp.longident lid md
| Wrong_type lid ->
fprintf ppf "@[%a has the wrong type for a printing function.@]@."
Printtyp.longident lid
| No_active_printer lid ->
fprintf ppf "@[%a is not currently active as a printing function.@]@."
Printtyp.longident lid