(**************************************************************************) (* *) (* OCaml *) (* *) (* Xavier Leroy, projet Gallium, INRIA Rocquencourt *) (* Pierre Chambart, OCamlPro *) (* Mark Shinwell and Leo White, Jane Street Europe *) (* *) (* Copyright 2010 Institut National de Recherche en Informatique et *) (* en Automatique *) (* Copyright 2013--2016 OCamlPro SAS *) (* Copyright 2014--2016 Jane Street Group LLC *) (* *) (* 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. *) (* *) (**************************************************************************) (* Compilation environments for compilation units *) [@@@ocaml.warning "+a-4-9-40-41-42"] open Config open Misc open Cmx_format type error = Not_a_unit_info of string | Corrupted_unit_info of string | Illegal_renaming of string * string * string exception Error of error let global_infos_table = (Hashtbl.create 17 : (string, unit_infos option) Hashtbl.t) let export_infos_table = (Hashtbl.create 10 : (string, Export_info.t) Hashtbl.t) let imported_sets_of_closures_table = (Set_of_closures_id.Tbl.create 10 : Flambda.function_declarations option Set_of_closures_id.Tbl.t) module CstMap = Map.Make(struct type t = Clambda.ustructured_constant let compare = Clambda.compare_structured_constants (* PR#6442: it is incorrect to use Pervasives.compare on values of type t because it compares "0.0" and "-0.0" equal. *) end) type structured_constants = { strcst_shared: string CstMap.t; strcst_all: (string * Clambda.ustructured_constant) list; } let structured_constants_empty = { strcst_shared = CstMap.empty; strcst_all = []; } let structured_constants = ref structured_constants_empty let exported_constants = Hashtbl.create 17 let merged_environment = ref Export_info.empty let default_ui_export_info = if Config.flambda then Cmx_format.Flambda Export_info.empty else Cmx_format.Clambda Value_unknown let current_unit = { ui_name = ""; ui_symbol = ""; ui_defines = []; ui_imports_cmi = []; ui_imports_cmx = []; ui_curry_fun = []; ui_apply_fun = []; ui_send_fun = []; ui_force_link = false; ui_export_info = default_ui_export_info } let symbolname_for_pack pack name = match pack with | None -> name | Some p -> let b = Buffer.create 64 in for i = 0 to String.length p - 1 do match p.[i] with | '.' -> Buffer.add_string b "__" | c -> Buffer.add_char b c done; Buffer.add_string b "__"; Buffer.add_string b name; Buffer.contents b let unit_id_from_name name = Ident.create_persistent name let concat_symbol unitname id = unitname ^ "__" ^ id let make_symbol ?(unitname = current_unit.ui_symbol) idopt = let prefix = "caml" ^ unitname in match idopt with | None -> prefix | Some id -> concat_symbol prefix id let current_unit_linkage_name () = Linkage_name.create (make_symbol ~unitname:current_unit.ui_symbol None) let reset ?packname name = Hashtbl.clear global_infos_table; Set_of_closures_id.Tbl.clear imported_sets_of_closures_table; let symbol = symbolname_for_pack packname name in current_unit.ui_name <- name; current_unit.ui_symbol <- symbol; current_unit.ui_defines <- [symbol]; current_unit.ui_imports_cmi <- []; current_unit.ui_imports_cmx <- []; current_unit.ui_curry_fun <- []; current_unit.ui_apply_fun <- []; current_unit.ui_send_fun <- []; current_unit.ui_force_link <- !Clflags.link_everything; Hashtbl.clear exported_constants; structured_constants := structured_constants_empty; current_unit.ui_export_info <- default_ui_export_info; merged_environment := Export_info.empty; Hashtbl.clear export_infos_table; let compilation_unit = Compilation_unit.create (Ident.create_persistent name) (current_unit_linkage_name ()) in Compilation_unit.set_current compilation_unit let current_unit_infos () = current_unit let current_unit_name () = current_unit.ui_name let make_symbol ?(unitname = current_unit.ui_symbol) idopt = let prefix = "caml" ^ unitname in match idopt with | None -> prefix | Some id -> prefix ^ "__" ^ id let symbol_in_current_unit name = let prefix = "caml" ^ current_unit.ui_symbol in name = prefix || (let lp = String.length prefix in String.length name >= 2 + lp && String.sub name 0 lp = prefix && name.[lp] = '_' && name.[lp + 1] = '_') let read_unit_info filename = let ic = open_in_bin filename in try let buffer = really_input_string ic (String.length cmx_magic_number) in if buffer <> cmx_magic_number then begin close_in ic; raise(Error(Not_a_unit_info filename)) end; let ui = (input_value ic : unit_infos) in let crc = Digest.input ic in close_in ic; (ui, crc) with End_of_file | Failure _ -> close_in ic; raise(Error(Corrupted_unit_info(filename))) let read_library_info filename = let ic = open_in_bin filename in let buffer = really_input_string ic (String.length cmxa_magic_number) in if buffer <> cmxa_magic_number then raise(Error(Not_a_unit_info filename)); let infos = (input_value ic : library_infos) in close_in ic; infos (* Read and cache info on global identifiers *) let get_global_info global_ident = ( let modname = Ident.name global_ident in if modname = current_unit.ui_name then Some current_unit else begin try Hashtbl.find global_infos_table modname with Not_found -> let (infos, crc) = if Env.is_imported_opaque modname then (None, None) else begin try let filename = find_in_path_uncap !load_path (modname ^ ".cmx") in let (ui, crc) = read_unit_info filename in if ui.ui_name <> modname then raise(Error(Illegal_renaming(modname, ui.ui_name, filename))); (Some ui, Some crc) with Not_found -> let warn = Warnings.No_cmx_file modname in Location.prerr_warning Location.none warn; (None, None) end in current_unit.ui_imports_cmx <- (modname, crc) :: current_unit.ui_imports_cmx; Hashtbl.add global_infos_table modname infos; infos end ) let cache_unit_info ui = Hashtbl.add global_infos_table ui.ui_name (Some ui) (* Return the approximation of a global identifier *) let get_clambda_approx ui = assert(not Config.flambda); match ui.ui_export_info with | Flambda _ -> assert false | Clambda approx -> approx let toplevel_approx : (string, Clambda.value_approximation) Hashtbl.t = Hashtbl.create 16 let record_global_approx_toplevel () = Hashtbl.add toplevel_approx current_unit.ui_name (get_clambda_approx current_unit) let global_approx id = if Ident.is_predef_exn id then Clambda.Value_unknown else try Hashtbl.find toplevel_approx (Ident.name id) with Not_found -> match get_global_info id with | None -> Clambda.Value_unknown | Some ui -> get_clambda_approx ui (* Return the symbol used to refer to a global identifier *) let symbol_for_global id = if Ident.is_predef_exn id then "caml_exn_" ^ Ident.name id else begin let unitname = Ident.name id in match try ignore (Hashtbl.find toplevel_approx unitname); None with Not_found -> get_global_info id with | None -> make_symbol ~unitname:(Ident.name id) None | Some ui -> make_symbol ~unitname:ui.ui_symbol None end (* Register the approximation of the module being compiled *) let unit_for_global id = let sym_label = Linkage_name.create (symbol_for_global id) in Compilation_unit.create id sym_label let predefined_exception_compilation_unit = Compilation_unit.create (Ident.create_persistent "__dummy__") (Linkage_name.create "__dummy__") let is_predefined_exception sym = Compilation_unit.equal predefined_exception_compilation_unit (Symbol.compilation_unit sym) let symbol_for_global' id = let sym_label = Linkage_name.create (symbol_for_global id) in if Ident.is_predef_exn id then Symbol.unsafe_create predefined_exception_compilation_unit sym_label else Symbol.unsafe_create (unit_for_global id) sym_label let set_global_approx approx = assert(not Config.flambda); current_unit.ui_export_info <- Clambda approx (* Exporting and importing cross module information *) let get_flambda_export_info ui = assert(Config.flambda); match ui.ui_export_info with | Clambda _ -> assert false | Flambda ei -> ei let set_export_info export_info = assert(Config.flambda); current_unit.ui_export_info <- Flambda export_info let approx_for_global comp_unit = let id = Compilation_unit.get_persistent_ident comp_unit in if (Compilation_unit.equal predefined_exception_compilation_unit comp_unit) || Ident.is_predef_exn id || not (Ident.global id) then invalid_arg (Format.asprintf "approx_for_global %a" Ident.print id); let modname = Ident.name id in try Hashtbl.find export_infos_table modname with | Not_found -> let exported = match get_global_info id with | None -> Export_info.empty | Some ui -> get_flambda_export_info ui in Hashtbl.add export_infos_table modname exported; merged_environment := Export_info.merge !merged_environment exported; exported let approx_env () = !merged_environment (* Record that a currying function or application function is needed *) let need_curry_fun n = if not (List.mem n current_unit.ui_curry_fun) then current_unit.ui_curry_fun <- n :: current_unit.ui_curry_fun let need_apply_fun n = assert(n > 0); if not (List.mem n current_unit.ui_apply_fun) then current_unit.ui_apply_fun <- n :: current_unit.ui_apply_fun let need_send_fun n = if not (List.mem n current_unit.ui_send_fun) then current_unit.ui_send_fun <- n :: current_unit.ui_send_fun (* Write the description of the current unit *) let write_unit_info info filename = let oc = open_out_bin filename in output_string oc cmx_magic_number; output_value oc info; flush oc; let crc = Digest.file filename in Digest.output oc crc; close_out oc let save_unit_info filename = current_unit.ui_imports_cmi <- Env.imports(); write_unit_info current_unit filename let current_unit_linkage_name () = Linkage_name.create (make_symbol ~unitname:current_unit.ui_symbol None) let current_unit () = match Compilation_unit.get_current () with | Some current_unit -> current_unit | None -> Misc.fatal_error "Compilenv.current_unit" let current_unit_symbol () = Symbol.unsafe_create (current_unit ()) (current_unit_linkage_name ()) let const_label = ref 0 let new_const_symbol () = incr const_label; make_symbol (Some (string_of_int !const_label)) let snapshot () = !structured_constants let backtrack s = structured_constants := s let new_structured_constant cst ~shared = let {strcst_shared; strcst_all} = !structured_constants in if shared then try CstMap.find cst strcst_shared with Not_found -> let lbl = new_const_symbol() in structured_constants := { strcst_shared = CstMap.add cst lbl strcst_shared; strcst_all = (lbl, cst) :: strcst_all; }; lbl else let lbl = new_const_symbol() in structured_constants := { strcst_shared; strcst_all = (lbl, cst) :: strcst_all; }; lbl let add_exported_constant s = Hashtbl.replace exported_constants s () let clear_structured_constants () = structured_constants := structured_constants_empty let structured_constants () = List.map (fun (symbol, definition) -> { Clambda.symbol; exported = Hashtbl.mem exported_constants symbol; definition; }) (!structured_constants).strcst_all let closure_symbol fv = let compilation_unit = Closure_id.get_compilation_unit fv in let unitname = Linkage_name.to_string (Compilation_unit.get_linkage_name compilation_unit) in let linkage_name = concat_symbol unitname ((Closure_id.unique_name fv) ^ "_closure") in Symbol.unsafe_create compilation_unit (Linkage_name.create linkage_name) let function_label fv = let compilation_unit = Closure_id.get_compilation_unit fv in let unitname = Linkage_name.to_string (Compilation_unit.get_linkage_name compilation_unit) in (concat_symbol unitname (Closure_id.unique_name fv)) let require_global global_ident = if not (Ident.is_predef_exn global_ident) then ignore (get_global_info global_ident : Cmx_format.unit_infos option) (* Error report *) open Format let report_error ppf = function | Not_a_unit_info filename -> fprintf ppf "%a@ is not a compilation unit description." Location.print_filename filename | Corrupted_unit_info filename -> fprintf ppf "Corrupted compilation unit description@ %a" Location.print_filename filename | Illegal_renaming(name, modname, filename) -> fprintf ppf "%a@ contains the description for unit\ @ %s when %s was expected" Location.print_filename filename name modname let () = Location.register_error_of_exn (function | Error err -> Some (Location.error_of_printer_file report_error err) | _ -> None )