From 7cfc4e5146be5666419451bdd516f1f3f264d24a Mon Sep 17 00:00:00 2001 From: Enrico Tassi Date: Sun, 25 Jan 2015 14:42:51 +0100 Subject: Imported Upstream version 8.5~beta1+dfsg --- toplevel/mltop.ml | 439 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 toplevel/mltop.ml (limited to 'toplevel/mltop.ml') diff --git a/toplevel/mltop.ml b/toplevel/mltop.ml new file mode 100644 index 00000000..9dc1dd5b --- /dev/null +++ b/toplevel/mltop.ml @@ -0,0 +1,439 @@ +(************************************************************************) +(* v * The Coq Proof Assistant / The Coq Development Team *) +(* unit; + use_file : string -> unit; + add_dir : string -> unit; + ml_loop : unit -> unit } + +(* Determines the behaviour of Coq with respect to ML files (compiled + or not) *) +type kind_load = + | WithTop of toplevel + | WithoutTop + +(* Must be always initialized *) +let load = ref WithoutTop + +(* Are we in a native version of Coq? *) +let is_native = Dynlink.is_native + +(* Sets and initializes a toplevel (if any) *) +let set_top toplevel = load := + WithTop toplevel; + Nativelib.load_obj := toplevel.load_obj + +(* Removes the toplevel (if any) *) +let remove () = + load := WithoutTop; + Nativelib.load_obj := (fun x -> () : string -> unit) + +(* Tests if an Ocaml toplevel runs under Coq *) +let is_ocaml_top () = + match !load with + | WithTop _ -> true + |_ -> false + +(* Tests if we can load ML files *) +let has_dynlink = Coq_config.has_natdynlink || not is_native + +(* Runs the toplevel loop of Ocaml *) +let ocaml_toploop () = + match !load with + | WithTop t -> Printexc.catch t.ml_loop () + | _ -> () + +(* Try to interpret load_obj's (internal) errors *) +let report_on_load_obj_error exc = + let x = Obj.repr exc in + (* Try an horrible (fragile) hack to report on Symtable dynlink errors *) + (* (we follow ocaml's Printexc.to_string decoding of exceptions) *) + if Obj.is_block x && String.equal (Obj.magic (Obj.field (Obj.field x 0) 0)) "Symtable.Error" + then + let err_block = Obj.field x 1 in + if Int.equal (Obj.tag err_block) 0 then + (* Symtable.Undefined_global of string *) + str "reference to undefined global " ++ + str (Obj.magic (Obj.field err_block 0)) + else str (Printexc.to_string exc) + else str (Printexc.to_string exc) + +(* Dynamic loading of .cmo/.cma *) + +let ml_load s = + match !load with + | WithTop t -> + (try t.load_obj s; s + with + | e when Errors.noncritical e -> + let e = Errors.push e in + match fst e with + | (UserError _ | Failure _ | Not_found as u) -> Exninfo.iraise (u, snd e) + | exc -> + let msg = report_on_load_obj_error exc in + errorlabstrm "Mltop.load_object" (str"Cannot link ml-object " ++ + str s ++ str" to Coq code (" ++ msg ++ str ").")) + | WithoutTop -> + try + Dynlink.loadfile s; s + with Dynlink.Error a -> + errorlabstrm "Mltop.load_object" + (strbrk "while loading " ++ str s ++ + strbrk ": " ++ str (Dynlink.error_message a)) + +let dir_ml_load s = + match !load with + | WithTop _ -> ml_load s + | WithoutTop -> + let warn = Flags.is_verbose() in + let _,gname = find_file_in_path ~warn !coq_mlpath_copy s in + ml_load gname + +(* Dynamic interpretation of .ml *) +let dir_ml_use s = + match !load with + | WithTop t -> t.use_file s + | _ -> msg_warning (str "Cannot access the ML compiler") + +(* Adds a path to the ML paths *) +let add_ml_dir s = + match !load with + | WithTop t -> t.add_dir s; keep_copy_mlpath s + | WithoutTop when has_dynlink -> keep_copy_mlpath s + | _ -> () + +(* For Rec Add ML Path *) +let add_rec_ml_dir unix_path = + List.iter (fun (lp,_) -> add_ml_dir lp) (all_subdirs ~unix_path) + +(* Adding files to Coq and ML loadpath *) + +let add_path ~unix_path:dir ~coq_root:coq_dirpath ~implicit = + if exists_dir dir then + begin + add_ml_dir dir; + Loadpath.add_load_path dir + (if implicit then Loadpath.ImplicitRootPath else Loadpath.RootPath) + coq_dirpath + end + else + msg_warning (str ("Cannot open " ^ dir)) + +let convert_string d = + try Names.Id.of_string d + with UserError _ -> + msg_warning (str ("Directory "^d^" cannot be used as a Coq identifier (skipped)")); + raise Exit + +let add_rec_path ~unix_path ~coq_root ~implicit = + if exists_dir unix_path then + let dirs = all_subdirs ~unix_path in + let prefix = Names.DirPath.repr coq_root in + let convert_dirs (lp, cp) = + try + let path = List.rev_map convert_string cp @ prefix in + Some (lp, Names.DirPath.make path) + with Exit -> None + in + let dirs = List.map_filter convert_dirs dirs in + let () = add_ml_dir unix_path in + let add (path, dir) = + Loadpath.add_load_path path Loadpath.ImplicitPath dir in + let () = if implicit then List.iter add dirs in + Loadpath.add_load_path unix_path + (if implicit then Loadpath.ImplicitRootPath else Loadpath.RootPath) + coq_root + else + msg_warning (str ("Cannot open " ^ unix_path)) + +(* convertit un nom quelconque en nom de fichier ou de module *) +let mod_of_name name = + if Filename.check_suffix name ".cmo" then + Filename.chop_suffix name ".cmo" + else + name + +let get_ml_object_suffix name = + if Filename.check_suffix name ".cmo" then + Some ".cmo" + else if Filename.check_suffix name ".cma" then + Some ".cma" + else if Filename.check_suffix name ".cmxs" then + Some ".cmxs" + else + None + +let file_of_name name = + let suffix = get_ml_object_suffix name in + let fail s = + errorlabstrm "Mltop.load_object" + (str"File not found on loadpath : " ++ str s ++ str"\n" ++ + str"Loadpath: " ++ str(String.concat ":" !coq_mlpath_copy)) in + if not (Filename.is_relative name) then + if Sys.file_exists name then name else fail name + else if is_native then + let name = match suffix with + | Some ((".cmo"|".cma") as suffix) -> + (Filename.chop_suffix name suffix) ^ ".cmxs" + | Some ".cmxs" -> name + | _ -> name ^ ".cmxs" + in + if is_in_path !coq_mlpath_copy name then name else fail name + else + let (full, base) = match suffix with + | Some ".cmo" | Some ".cma" -> true, name + | Some ".cmxs" -> false, Filename.chop_suffix name ".cmxs" + | _ -> false, name + in + if full then + if is_in_path !coq_mlpath_copy base then base else fail base + else + let name = base ^ ".cma" in + if is_in_path !coq_mlpath_copy name then name else + let name = base ^ ".cmo" in + if is_in_path !coq_mlpath_copy name then name else + fail (base ^ ".cm[ao]") + +(** Is the ML code of the standard library placed into loadable plugins + or statically compiled into coqtop ? For the moment this choice is + made according to the presence of native dynlink : even if bytecode + coqtop could always load plugins, we prefer to have uniformity between + bytecode and native versions. *) + +(* [known_loaded_module] contains the names of the loaded ML modules + * (linked or loaded with load_object). It is used not to load a + * module twice. It is NOT the list of ML modules Coq knows. *) + +let known_loaded_modules = ref String.Map.empty + +let add_known_module mname path = + if not (String.Map.mem mname !known_loaded_modules) || + String.Map.find mname !known_loaded_modules = None then + known_loaded_modules := String.Map.add mname path !known_loaded_modules + +let module_is_known mname = + String.Map.mem mname !known_loaded_modules + +let known_module_path mname = + String.Map.find mname !known_loaded_modules + +(** A plugin is just an ML module with an initialization function. *) + +let known_loaded_plugins = ref String.Map.empty + +let add_known_plugin init name = + add_known_module name None; + known_loaded_plugins := String.Map.add name init !known_loaded_plugins + +let init_known_plugins () = + String.Map.iter (fun _ f -> f()) !known_loaded_plugins + +(** Registering functions to be used at caching time, that is when the Declare + ML module command is issued. *) + +let cache_objs = ref String.Map.empty + +let declare_cache_obj f name = + let objs = try String.Map.find name !cache_objs with Not_found -> [] in + let objs = f :: objs in + cache_objs := String.Map.add name objs !cache_objs + +let perform_cache_obj name = + let objs = try String.Map.find name !cache_objs with Not_found -> [] in + let objs = List.rev objs in + List.iter (fun f -> f ()) objs + +(** ml object = ml module or plugin *) + +let init_ml_object mname = + try String.Map.find mname !known_loaded_plugins () + with Not_found -> () + +let load_ml_object mname ?path fname= + let path = match path with + | None -> dir_ml_load fname + | Some p -> ml_load p in + add_known_module mname (Some path); + init_ml_object mname; + path + +let dir_ml_load m = ignore(dir_ml_load m) +let add_known_module m = add_known_module m None +let load_ml_object_raw fname = dir_ml_load (file_of_name fname) +let load_ml_objects_raw_rex rex = + List.iter (fun (_,fp) -> + let name = file_of_name (Filename.basename fp) in + try dir_ml_load name + with e -> prerr_endline (Printexc.to_string e)) + (System.where_in_path_rex !coq_mlpath_copy rex) + +(* Summary of declared ML Modules *) + +(* List and not String.Set because order is important: most recent first. *) + +let loaded_modules = ref [] +let get_loaded_modules () = List.rev !loaded_modules +let add_loaded_module md path = + if not (List.mem_assoc md !loaded_modules) then + loaded_modules := (md,path) :: !loaded_modules +let reset_loaded_modules () = loaded_modules := [] + +let if_verbose_load verb f name ?path fname = + if not verb then f name ?path fname + else + let info = "[Loading ML file "^fname^" ..." in + try + let path = f name ?path fname in + msg_info (str (info^" done]")); + path + with reraise -> + msg_info (str (info^" failed]")); + raise reraise + +(** Load a module for the first time (i.e. dynlink it) + or simulate its reload (i.e. doing nothing except maybe + an initialization function). *) + +let trigger_ml_object verb cache reinit ?path name = + if module_is_known name then begin + if reinit then init_ml_object name; + add_loaded_module name (known_module_path name); + if cache then perform_cache_obj name + end else if not has_dynlink then + error ("Dynamic link not supported (module "^name^")") + else begin + let file = file_of_name (Option.default name path) in + let path = + if_verbose_load (verb && is_verbose ()) load_ml_object name ?path file in + add_loaded_module name (Some path); + if cache then perform_cache_obj name + end + +let load_ml_object n m = ignore(load_ml_object n m) + +let unfreeze_ml_modules x = + reset_loaded_modules (); + List.iter + (fun (name,path) -> trigger_ml_object false false false ?path name) x + +let _ = + Summary.declare_summary Summary.ml_modules + { Summary.freeze_function = (fun _ -> get_loaded_modules ()); + Summary.unfreeze_function = unfreeze_ml_modules; + Summary.init_function = reset_loaded_modules } + +(* Liboject entries of declared ML Modules *) + +type ml_module_object = { + mlocal : Vernacexpr.locality_flag; + mnames : string list +} + +let cache_ml_objects (_,{mnames=mnames}) = + let iter obj = trigger_ml_object true true true obj in + List.iter iter mnames + +let load_ml_objects _ (_,{mnames=mnames}) = + let iter obj = trigger_ml_object true false true obj in + List.iter iter mnames + +let classify_ml_objects ({mlocal=mlocal} as o) = + if mlocal then Dispose else Substitute o + +let inMLModule : ml_module_object -> obj = + declare_object + {(default_object "ML-MODULE") with + cache_function = cache_ml_objects; + load_function = load_ml_objects; + subst_function = (fun (_,o) -> o); + classify_function = classify_ml_objects } + +let declare_ml_modules local l = + let l = List.map mod_of_name l in + Lib.add_anonymous_leaf (inMLModule {mlocal=local; mnames=l}) + +let print_ml_path () = + let l = !coq_mlpath_copy in + str"ML Load Path:" ++ fnl () ++ str" " ++ + hv 0 (prlist_with_sep fnl str l) + +(* Printing of loaded ML modules *) + +let print_ml_modules () = + let l = get_loaded_modules () in + str"Loaded ML Modules: " ++ pr_vertical_list str (List.map fst l) + +let print_gc () = + let stat = Gc.stat () in + let msg = + str "minor words: " ++ real stat.Gc.minor_words ++ fnl () ++ + str "promoted words: " ++ real stat.Gc.promoted_words ++ fnl () ++ + str "major words: " ++ real stat.Gc.major_words ++ fnl () ++ + str "minor_collections: " ++ int stat.Gc.minor_collections ++ fnl () ++ + str "major_collections: " ++ int stat.Gc.major_collections ++ fnl () ++ + str "heap_words: " ++ int stat.Gc.heap_words ++ fnl () ++ + str "heap_chunks: " ++ int stat.Gc.heap_chunks ++ fnl () ++ + str "live_words: " ++ int stat.Gc.live_words ++ fnl () ++ + str "live_blocks: " ++ int stat.Gc.live_blocks ++ fnl () ++ + str "free_words: " ++ int stat.Gc.free_words ++ fnl () ++ + str "free_blocks: " ++ int stat.Gc.free_blocks ++ fnl () ++ + str "largest_free: " ++ int stat.Gc.largest_free ++ fnl () ++ + str "fragments: " ++ int stat.Gc.fragments ++ fnl () ++ + str "compactions: " ++ int stat.Gc.compactions ++ fnl () ++ + str "top_heap_words: " ++ int stat.Gc.top_heap_words ++ fnl () ++ + str "stack_size: " ++ int stat.Gc.stack_size + in + hv 0 msg -- cgit v1.2.3