(* Production of a bytecode executable file *)open Misc BasicIO Nonstdio Miscsys Obj Fnlib Const Mixture Config;open Code_dec Symtable Patch Tr_const;val autolink = ref trueval verbose = ref false(* First pass: check the consistency of files *)fun read_file name = let val truename = find_in_path name val is = open_in_bin truename val tables = let val n = input_binary_int is in seek_in is n; input_value is : compiled_unit_tables end handle x => (close_in is; msgIBlock 0; errPrompt "Error on file "; msgString truename; msgEOL(); msgEBlock(); raise x) val _ = close_in is in (truename, tables) endexception WrongStamp and NotYetfun check_file name stampOpt pending processed = let val simplename = Filename.chop_suffix name ".uo" val uname = normalizedUnitName(Filename.basename simplename) val () = if member uname pending then raise Fail ("Unit " ^ name ^ " depends on itself") else () val () = if member uname reservedUnitNames then raise Fail ("Unit "^uname^" is built-in, and cannot be linked") else () val already = (SOME (Hasht.find (!watchDog) uname)) handle Subscript => NONE fun needs subuname substamp processed = (check_file (subuname ^ ".uo") (SOME substamp) (uname :: pending) processed) handle WrongStamp => raise Fail ("Compiled body of unit " ^ uname ^ " is incompatible with unit "^ subuname) | NotYet => raise Fail ("Unit " ^ subuname ^ " is mentioned by " ^ uname ^ " but not yet linked") in case already of SOME stamp' => (case stampOpt of SOME stamp => if stamp <> stamp' then raise WrongStamp else processed | NONE => (msgIBlock 0; errPrompt "Warning: unit "; msgString uname; msgString " is needed by a unit preceding it"; msgEOL(); msgEBlock(); processed)) | NONE => let val implicit = case stampOpt of NONE => false | _ => true val _ = if not(!autolink) andalso implicit then raise NotYet else () val (truename, tables) = read_file name val precedingUnits = Hasht.fold needs processed (#cu_mentions tables) in Hasht.insert (!watchDog) uname (#cu_sig_stamp tables); (uname, truename, tables) :: precedingUnits end endval check_file = fn name => fn processed => check_file name NONE [] processed(* Second pass: determine which phrases are required *)val missing_globals = ref (Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t);fun is_in_missing g = (Hasht.find (!missing_globals) g; true) handle Subscript => false;fun remove_from_missing g = Hasht.remove (!missing_globals) g;fun add_to_missing g = Hasht.insert (!missing_globals) g ();fun is_required (Reloc_setglobal g, _) = is_in_missing g | is_required _ = false;fun remove_required (Reloc_setglobal g, _) = remove_from_missing g | remove_required _ = ();fun add_required (Reloc_getglobal g, _) = add_to_missing g | add_required _ = ();fun scan_val uname (id, stamp) tolink = let val q = {qual=uname, id=[id]} in if is_in_missing (q, 0) then (remove_from_missing (q, 0); add_to_missing (q, stamp); (id, stamp) :: tolink) else tolink end;fun scan_phrase (phr : compiled_phrase) tolink = let val (_, otherlist) = #cph_reloc phr in if not (#cph_pure phr) orelse List.exists is_required otherlist then (List.app remove_required otherlist; List.app add_required otherlist; phr :: tolink) else tolink end;fun scan_file (uname, truename, (tables : compiled_unit_tables)) tolink = let val exportedE = #cu_exc_ren_list tables val valRenList = #cu_val_ren_list tables val exportedV = foldL (scan_val uname) [] valRenList val phraseIndex = #cu_phrase_index tables val required = foldL scan_phrase [] phraseIndex in (uname, truename, required, exportedE, exportedV) :: tolink end;(* Third pass : link in the required phrases. *)fun link_object os (uname, truename, required, exportedE, exportedV) = let val is = open_in_bin truename in (List.app (fn (phr : compiled_phrase) => let val () = seek_in is (#cph_pos phr) val buff = input(is, #cph_len phr) val () = if size buff < #cph_len phr then raise Size else () in patch_object buff 0 (#cph_reloc phr); output(os, buff) end) required; exportPublicNames uname exportedE exportedV; close_in is) handle x => (close_in is; msgIBlock 0; errPrompt "Error while linking file "; msgString truename; msgEOL(); msgEBlock(); raise x) end;(* To build the initial table of globals *)local prim_val vector_ : int -> '_a -> '_a vector = 2 "make_vect" prim_val sub_ : 'a vector -> int -> 'a = 2 "get_vect_item" prim_val update_ : 'a vector -> int -> 'a -> unit = 3 "set_vect_item"in fun emit_data os = let val len = number_of_globals() val globals = vector_ len (repr 0) in List.app (fn (n,sc) => update_ globals n (translStructuredConst sc)) (!literal_table); output_value os globals end;end;(* To build a bytecode executable file *)val write_symbols = ref false;val no_header = ref false;val stand_alone = ref false; (* cvr: 144 merge *)fun reportLinked toscan = let fun reportUnit (uname, _, _) = (msgString uname; msgString ".uo ") in msgIBlock 0; msgString "Linking: "; List.app reportUnit (rev toscan); msgEOL(); msgEBlock() endfun link unit_list exec_name = let val _ = missing_globals := (* 04Sep95 e *) (Hasht.new 263 : (QualifiedIdent * int, unit) Hasht.t) val toscan = foldL check_file [] unit_list val _ = if !verbose then reportLinked toscan else () val tolink = foldL scan_file [] toscan val os = if !no_header andalso not (!stand_alone) then open_out_bin exec_name else open_out_exe exec_name fun copy name = let val buff = CharArray.array(4096, #"\000") val is = open_in_bin (Filename.concat (!path_library) name) fun loop () = case buff_input is buff 0 4096 of 0 => () | n => (buff_output os buff 0 n; loop ()) in (loop (); close_in is) handle x => (close_in is; raise x) end in ((* Prepend the runtime system? *) if !stand_alone then copy "camlrunm" else (); (* Prepend the header? *) if !no_header orelse !stand_alone then () else copy "header"; missing_globals := (* for gc -- 04Sep95 e *) (Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t); (* The bytecode *) let val pos1 = pos_out os val () = List.app (link_object os) tolink val () = output_byte os Opcodes.STOP; (* The table of global data *) val pos2 = pos_out os val () = emit_data os (* Linker tables *) val pos3 = pos_out os val () = if !write_symbols then save_linker_tables os else (); (* Debugging info (none, presently) *) val pos4 = pos_out os in (* The trailer *) output_binary_int os (pos2 - pos1); output_binary_int os (pos3 - pos2); output_binary_int os (pos4 - pos3); output_binary_int os 0; output(os, "ML08"); close_out os end ) handle x => (close_out os; remove_file exec_name; raise x) end;
(* Production of a bytecode executable file *)
open Misc BasicIO Nonstdio Miscsys Obj Fnlib Const Mixture Config;
open Code_dec Symtable Patch Tr_const;
val autolink = ref true
val verbose = ref false
(* First pass: check the consistency of files *)
fun read_file name =
let val truename = find_in_path name
val is = open_in_bin truename
val tables =
let val n = input_binary_int is in
seek_in is n;
input_value is : compiled_unit_tables
end
handle x =>
(close_in is;
msgIBlock 0;
errPrompt "Error on file ";
msgString truename; msgEOL();
msgEBlock();
raise x)
val _ = close_in is
in (truename, tables) end
exception WrongStamp and NotYet
fun check_file name stampOpt pending processed =
let val simplename = Filename.chop_suffix name ".uo"
val uname = normalizedUnitName(Filename.basename simplename)
val () =
if member uname pending then
raise Fail ("Unit " ^ name ^ " depends on itself")
else ()
val () =
if member uname reservedUnitNames then
raise Fail ("Unit "^uname^" is built-in, and cannot be linked")
else ()
val already = (SOME (Hasht.find (!watchDog) uname))
handle Subscript => NONE
fun needs subuname substamp processed =
(check_file (subuname ^ ".uo") (SOME substamp)
(uname :: pending) processed)
handle WrongStamp =>
raise Fail ("Compiled body of unit " ^ uname
^ " is incompatible with unit "^ subuname)
| NotYet =>
raise Fail ("Unit " ^ subuname ^ " is mentioned by "
^ uname ^ " but not yet linked")
in
case already of
SOME stamp' =>
(case stampOpt of
SOME stamp =>
if stamp <> stamp' then raise WrongStamp
else processed
| NONE =>
(msgIBlock 0;
errPrompt "Warning: unit ";
msgString uname;
msgString " is needed by a unit preceding it";
msgEOL();
msgEBlock();
processed))
| NONE => let val implicit = case stampOpt of NONE => false | _ => true
val _ = if not(!autolink) andalso implicit
then raise NotYet else ()
val (truename, tables) = read_file name
val precedingUnits =
Hasht.fold needs processed (#cu_mentions tables)
in
Hasht.insert (!watchDog) uname (#cu_sig_stamp tables);
(uname, truename, tables) :: precedingUnits
end
end
val check_file = fn name => fn processed => check_file name NONE [] processed
(* Second pass: determine which phrases are required *)
val missing_globals =
ref (Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t)
;
fun is_in_missing g =
(Hasht.find (!missing_globals) g; true)
handle Subscript => false
;
fun remove_from_missing g =
Hasht.remove (!missing_globals) g
;
fun add_to_missing g =
Hasht.insert (!missing_globals) g ()
;
fun is_required (Reloc_setglobal g, _) = is_in_missing g
| is_required _ = false
;
fun remove_required (Reloc_setglobal g, _) = remove_from_missing g
| remove_required _ = ()
;
fun add_required (Reloc_getglobal g, _) = add_to_missing g
| add_required _ = ()
;
fun scan_val uname (id, stamp) tolink =
let val q = {qual=uname, id=[id]} in
if is_in_missing (q, 0) then
(remove_from_missing (q, 0);
add_to_missing (q, stamp);
(id, stamp) :: tolink)
else
tolink
end;
fun scan_phrase (phr : compiled_phrase) tolink =
let val (_, otherlist) = #cph_reloc phr
in
if not (#cph_pure phr) orelse List.exists is_required otherlist then
(List.app remove_required otherlist;
List.app add_required otherlist;
phr :: tolink)
else
tolink
end;
fun scan_file (uname, truename, (tables : compiled_unit_tables)) tolink =
let val exportedE = #cu_exc_ren_list tables
val valRenList = #cu_val_ren_list tables
val exportedV = foldL (scan_val uname) [] valRenList
val phraseIndex = #cu_phrase_index tables
val required = foldL scan_phrase [] phraseIndex
in
(uname, truename, required, exportedE, exportedV) :: tolink
end;
(* Third pass : link in the required phrases. *)
fun link_object os (uname, truename, required, exportedE, exportedV) =
let val is = open_in_bin truename in
(List.app
(fn (phr : compiled_phrase) =>
let val () = seek_in is (#cph_pos phr)
val buff = input(is, #cph_len phr)
val () = if size buff < #cph_len phr
then raise Size else ()
in
patch_object buff 0 (#cph_reloc phr);
output(os, buff)
end)
required;
exportPublicNames uname exportedE exportedV;
close_in is)
handle x =>
(close_in is;
msgIBlock 0;
errPrompt "Error while linking file ";
msgString truename; msgEOL();
msgEBlock();
raise x)
end;
(* To build the initial table of globals *)
local
prim_val vector_ : int -> '_a -> '_a vector = 2 "make_vect"
prim_val sub_ : 'a vector -> int -> 'a = 2 "get_vect_item"
prim_val update_ : 'a vector -> int -> 'a -> unit = 3 "set_vect_item"
in
fun emit_data os =
let val len = number_of_globals()
val globals = vector_ len (repr 0)
in
List.app
(fn (n,sc) => update_ globals n (translStructuredConst sc))
(!literal_table);
output_value os globals
end;
end;
(* To build a bytecode executable file *)
val write_symbols = ref false;
val no_header = ref false;
val stand_alone = ref false; (* cvr: 144 merge *)
fun reportLinked toscan =
let fun reportUnit (uname, _, _) = (msgString uname; msgString ".uo ")
in
msgIBlock 0;
msgString "Linking: ";
List.app reportUnit (rev toscan);
msgEOL(); msgEBlock()
end
fun link unit_list exec_name =
let val _ = missing_globals := (* 04Sep95 e *)
(Hasht.new 263 : (QualifiedIdent * int, unit) Hasht.t)
val toscan = foldL check_file [] unit_list
val _ = if !verbose then reportLinked toscan else ()
val tolink = foldL scan_file [] toscan
val os = if !no_header andalso not (!stand_alone) then
open_out_bin exec_name
else
open_out_exe exec_name
fun copy name =
let val buff = CharArray.array(4096, #"\000")
val is = open_in_bin (Filename.concat (!path_library) name)
fun loop () =
case buff_input is buff 0 4096 of
0 => ()
| n => (buff_output os buff 0 n; loop ())
in
(loop (); close_in is) handle x => (close_in is; raise x)
end
in
((* Prepend the runtime system? *)
if !stand_alone then copy "camlrunm" else ();
(* Prepend the header? *)
if !no_header orelse !stand_alone then () else copy "header";
missing_globals := (* for gc -- 04Sep95 e *)
(Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t);
(* The bytecode *)
let val pos1 = pos_out os
val () = List.app (link_object os) tolink
val () = output_byte os Opcodes.STOP;
(* The table of global data *)
val pos2 = pos_out os
val () = emit_data os
(* Linker tables *)
val pos3 = pos_out os
val () =
if !write_symbols then save_linker_tables os
else ();
(* Debugging info (none, presently) *)
val pos4 = pos_out os
in
(* The trailer *)
output_binary_int os (pos2 - pos1);
output_binary_int os (pos3 - pos2);
output_binary_int os (pos4 - pos3);
output_binary_int os 0;
output(os, "ML08");
close_out os
end
) handle x =>
(close_out os;
remove_file exec_name;
raise x)
end;