| 1 |
open Cduce_loc
|
| 2 |
open Ident
|
| 3 |
|
| 4 |
|
| 5 |
exception InconsistentCrc of U.t
|
| 6 |
exception InvalidObject of string
|
| 7 |
exception CannotOpen of string
|
| 8 |
exception NoImplementation of U.t
|
| 9 |
|
| 10 |
let run_loaded = ref false
|
| 11 |
|
| 12 |
type t = {
|
| 13 |
name: U.t;
|
| 14 |
descr: Compunit.t;
|
| 15 |
|
| 16 |
typing: Typer.t;
|
| 17 |
compile: Compile.env;
|
| 18 |
code: Lambda.code_item list;
|
| 19 |
ext_info: Externals.ext_info option;
|
| 20 |
|
| 21 |
mutable digest: Digest.t option;
|
| 22 |
vals: Value.t array; (* Exported values *)
|
| 23 |
mutable exts: Value.t array;
|
| 24 |
mutable depends: (U.t * string) list;
|
| 25 |
|
| 26 |
|
| 27 |
mutable status: [ `Evaluating | `Unevaluated | `Evaluated ];
|
| 28 |
}
|
| 29 |
|
| 30 |
let digest c = match c.digest with None -> assert false | Some x -> x
|
| 31 |
|
| 32 |
|
| 33 |
module Tbl = Hashtbl.Make(U)
|
| 34 |
let tbl = Tbl.create 64
|
| 35 |
|
| 36 |
module CTbl = Hashtbl.Make(Compunit)
|
| 37 |
let ctbl = CTbl.create 64
|
| 38 |
|
| 39 |
let mk name descr typing compile code ext_info depends =
|
| 40 |
{ name = name;
|
| 41 |
descr = descr;
|
| 42 |
typing = typing;
|
| 43 |
compile = compile;
|
| 44 |
code = code;
|
| 45 |
ext_info = ext_info;
|
| 46 |
digest = None;
|
| 47 |
vals = Array.make (Compile.global_size compile) Value.Absent;
|
| 48 |
exts = [| |];
|
| 49 |
depends = depends;
|
| 50 |
status = `Unevaluated;
|
| 51 |
}
|
| 52 |
|
| 53 |
let magic = "CDUCE:compunit:00007"
|
| 54 |
|
| 55 |
let has_obj n =
|
| 56 |
let base = U.to_string n ^ ".cdo" in
|
| 57 |
List.exists (fun p -> Sys.file_exists (Filename.concat p base)) !obj_path
|
| 58 |
|
| 59 |
let find_obj n =
|
| 60 |
let base = U.to_string n ^ ".cdo" in
|
| 61 |
let p =
|
| 62 |
List.find (fun p -> Sys.file_exists (Filename.concat p base)) !obj_path in
|
| 63 |
Filename.concat p base
|
| 64 |
|
| 65 |
let check_digest c dig =
|
| 66 |
if digest c <> dig then raise (InconsistentCrc c.name)
|
| 67 |
|
| 68 |
let show ppf id t v =
|
| 69 |
match id with
|
| 70 |
| Some id ->
|
| 71 |
Format.fprintf ppf "@[val %a : @[%a@]@."
|
| 72 |
Ident.print id
|
| 73 |
Types.Print.print t
|
| 74 |
| None -> ()
|
| 75 |
|
| 76 |
|
| 77 |
let compile verbose name src =
|
| 78 |
protect_op "Compile external file";
|
| 79 |
let ic =
|
| 80 |
if src = "" then (Cduce_loc.push_source `Stream; stdin)
|
| 81 |
else
|
| 82 |
try Cduce_loc.push_source (`File src); open_in src
|
| 83 |
with Sys_error _ -> raise (CannotOpen src) in
|
| 84 |
let input = Stream.of_channel ic in
|
| 85 |
let p =
|
| 86 |
try Parser.prog input
|
| 87 |
with
|
| 88 |
| Ulexer.Loc.Exc_located (_, (Location _ | Ulexer.Error _ as e)) -> raise e
|
| 89 |
| Ulexer.Loc.Exc_located ((i,j), e) ->
|
| 90 |
raise_loc i j e
|
| 91 |
in
|
| 92 |
if src <> "" then close_in ic;
|
| 93 |
|
| 94 |
let show =
|
| 95 |
if verbose
|
| 96 |
then Some (show Format.std_formatter)
|
| 97 |
else None in
|
| 98 |
Compunit.enter ();
|
| 99 |
let descr = Compunit.current () in
|
| 100 |
let (ty_env,c_env,code) =
|
| 101 |
Compile.comp_unit
|
| 102 |
?show
|
| 103 |
Builtin.env
|
| 104 |
(Compile.empty descr)
|
| 105 |
p in
|
| 106 |
Compunit.leave ();
|
| 107 |
let ext = Externals.get () in
|
| 108 |
let depends = Tbl.fold (fun name c accu -> (name,digest c) :: accu) tbl [] in
|
| 109 |
|
| 110 |
mk name descr ty_env c_env code ext depends
|
| 111 |
|
| 112 |
let set_hash c =
|
| 113 |
let h = Hashtbl.hash_param 1000 10000 (c.typing,c.name) in
|
| 114 |
let max_rank =
|
| 115 |
Tbl.fold
|
| 116 |
(fun _ c accu -> max accu (fst (Compunit.get_hash c.descr))) tbl 0 in
|
| 117 |
Compunit.set_hash c.descr (succ max_rank) h
|
| 118 |
(* This invalidates all hash tables on types ! *)
|
| 119 |
|
| 120 |
|
| 121 |
let compile_save verbose name src out =
|
| 122 |
protect_op "Save compilation unit";
|
| 123 |
|
| 124 |
let c = compile verbose name src in
|
| 125 |
set_hash c;
|
| 126 |
let pools = Value.extract_all () in
|
| 127 |
|
| 128 |
let oc = open_out_bin out in
|
| 129 |
output_string oc magic;
|
| 130 |
|
| 131 |
Marshal.to_channel oc (pools,c) [];
|
| 132 |
let digest = Digest.file out in
|
| 133 |
Marshal.to_channel oc digest [];
|
| 134 |
close_out oc
|
| 135 |
|
| 136 |
let from_descr descr : t =
|
| 137 |
try CTbl.find ctbl descr
|
| 138 |
with Not_found ->
|
| 139 |
let i1,i2 = Compunit.get_hash descr in
|
| 140 |
failwith (Printf.sprintf "Can't find cu(%i,%i)" i1 i2)
|
| 141 |
|
| 142 |
let register c =
|
| 143 |
(* Look for an already loaded unit with the same descriptor *)
|
| 144 |
if CTbl.mem ctbl c.descr then failwith "Collision on unit descriptors";
|
| 145 |
CTbl.add ctbl c.descr c
|
| 146 |
|
| 147 |
let reg_types = ref true
|
| 148 |
|
| 149 |
|
| 150 |
let rec real_load src =
|
| 151 |
let ic =
|
| 152 |
try open_in_bin src
|
| 153 |
with Sys_error _ -> raise (CannotOpen src) in
|
| 154 |
try
|
| 155 |
let s = String.copy magic in
|
| 156 |
really_input ic s 0 (String.length s);
|
| 157 |
if s <> magic then raise (InvalidObject src);
|
| 158 |
let pools,c = Marshal.from_channel ic in
|
| 159 |
let digest = Marshal.from_channel ic in
|
| 160 |
c.digest <- Some digest;
|
| 161 |
Value.intract_all pools;
|
| 162 |
close_in ic;
|
| 163 |
c
|
| 164 |
with Failure _ | End_of_file -> raise (InvalidObject src)
|
| 165 |
|
| 166 |
and load name =
|
| 167 |
protect_op "Load compiled compilation unit";
|
| 168 |
try Tbl.find tbl name
|
| 169 |
with Not_found ->
|
| 170 |
let src =
|
| 171 |
try find_obj name
|
| 172 |
with Not_found -> raise (NoImplementation name) in
|
| 173 |
let c = real_load src in
|
| 174 |
register c;
|
| 175 |
(* Register types *)
|
| 176 |
if !reg_types then
|
| 177 |
Typer.register_types (U.to_string c.name ^ ".") c.typing;
|
| 178 |
(* Load dependencies *)
|
| 179 |
List.iter (fun (name,dig) -> check_digest (load name) dig) c.depends;
|
| 180 |
Tbl.add tbl name c;
|
| 181 |
c
|
| 182 |
|
| 183 |
let rec run c =
|
| 184 |
match c.status with
|
| 185 |
| `Unevaluated ->
|
| 186 |
if (c.ext_info != None) && (Array.length c.exts = 0) then
|
| 187 |
failwith (Printf.sprintf
|
| 188 |
"The CDuce unit `%s' needs externals"
|
| 189 |
(U.to_string c.name));
|
| 190 |
|
| 191 |
(* Run dependencies *)
|
| 192 |
List.iter (fun (name,_) -> run (load name)) c.depends;
|
| 193 |
|
| 194 |
c.status <- `Evaluating;
|
| 195 |
Eval.eval_unit c.vals c.code;
|
| 196 |
c.status <- `Evaluated
|
| 197 |
| `Evaluating ->
|
| 198 |
failwith
|
| 199 |
("Librarian.run. Already running:" ^ (U.to_string c.name))
|
| 200 |
| `Evaluated -> ()
|
| 201 |
|
| 202 |
let compile_run verbose name src =
|
| 203 |
let c = compile verbose name src in
|
| 204 |
register c;
|
| 205 |
run c
|
| 206 |
|
| 207 |
let load_run name = reg_types := false; run (load name)
|
| 208 |
|
| 209 |
let static_externals = Hashtbl.create 17
|
| 210 |
let register_static_external n v =
|
| 211 |
Hashtbl.add static_externals n v
|
| 212 |
|
| 213 |
let get_builtins () =
|
| 214 |
List.sort Pervasives.compare
|
| 215 |
(Hashtbl.fold (fun n _ accu -> n::accu) static_externals [])
|
| 216 |
|
| 217 |
let () =
|
| 218 |
Typer.from_comp_unit := (fun d -> (from_descr d).typing);
|
| 219 |
Typer.load_comp_unit := (fun name ->
|
| 220 |
if has_obj name then
|
| 221 |
let cu = load name in
|
| 222 |
if !run_loaded then run cu;
|
| 223 |
cu.descr
|
| 224 |
else raise Not_found);
|
| 225 |
Typer.has_static_external := Hashtbl.mem static_externals;
|
| 226 |
Compile.from_comp_unit := (fun d -> (from_descr d).compile);
|
| 227 |
Eval.get_globals := (fun d -> (from_descr d).vals);
|
| 228 |
Eval.get_external := (fun d i -> (from_descr d).exts.(i));
|
| 229 |
Eval.get_builtin := Hashtbl.find static_externals
|
| 230 |
|
| 231 |
|
| 232 |
let stub_ml = ref (fun _ _ _ _ _ ->
|
| 233 |
Printf.eprintf
|
| 234 |
"Fatal error: no support for the OCaml interface.\n";
|
| 235 |
exit 2)
|
| 236 |
|
| 237 |
let prepare_stub src =
|
| 238 |
let c = real_load src in
|
| 239 |
|
| 240 |
(* Create stub types in a fresh compilation unit *)
|
| 241 |
Compunit.enter ();
|
| 242 |
let i1,i2 = Compunit.get_hash c.descr in
|
| 243 |
Compunit.set_hash (Compunit.current ()) (-i1) i2;
|
| 244 |
!stub_ml (U.get_str c.name) c.typing c.compile c.ext_info
|
| 245 |
(fun types ->
|
| 246 |
Compunit.leave ();
|
| 247 |
Marshal.to_string (Value.extract_all (), types, c) [])
|
| 248 |
(* TODO: could remove typing and compile env *)
|
| 249 |
|
| 250 |
let ocaml_stub stub =
|
| 251 |
let pools, types, (c : t) = Marshal.from_string stub 0 in
|
| 252 |
if Tbl.mem tbl c.name then
|
| 253 |
failwith ("CDuce unit " ^ (U.get_str c.name) ^ " already loaded");
|
| 254 |
Value.intract_all pools;
|
| 255 |
register c;
|
| 256 |
List.iter
|
| 257 |
(fun (name,dig) ->
|
| 258 |
let c =
|
| 259 |
try Tbl.find tbl name
|
| 260 |
with Not_found ->
|
| 261 |
failwith ("CDuce unit " ^ (U.get_str name) ^ " not loaded")
|
| 262 |
in
|
| 263 |
check_digest c dig) c.depends;
|
| 264 |
Tbl.add tbl c.name c;
|
| 265 |
types,
|
| 266 |
(fun a -> c.exts <- a),
|
| 267 |
c.vals,
|
| 268 |
(fun () -> run c)
|
| 269 |
|
| 270 |
let name d = (from_descr d).name
|
| 271 |
let run d = run (from_descr d)
|
| 272 |
|
| 273 |
let make_wrapper = ref (fun _ ->
|
| 274 |
failwith "OCaml/CDuce interface not available")
|