home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Moscow ML 1.42 / src / compiler / Link.sml < prev    next >
Encoding:
Text File  |  1997-08-18  |  7.3 KB  |  258 lines  |  [TEXT/R*ch]

  1. (* Production of a bytecode executable file *)
  2.  
  3. open Misc BasicIO Nonstdio Miscsys Obj Fnlib Const Mixture Config;
  4. open Code_dec Symtable Patch Tr_const;
  5.  
  6. val autolink = ref true
  7. val verbose  = ref false
  8.  
  9. (* First pass: check the consistency of files *)
  10.  
  11. fun read_file name = 
  12.     let val truename = find_in_path name
  13.     val is = open_in_bin truename
  14.     val tables =
  15.         let val n = input_binary_int is in
  16.         seek_in is n;
  17.         input_value is : compiled_unit_tables
  18.         end
  19.         handle x =>
  20.         (close_in is;
  21.          msgIBlock 0;
  22.          errPrompt "Error on file ";
  23.          msgString truename; msgEOL();
  24.          msgEBlock();
  25.          raise x)
  26.     val _ = close_in is
  27.     in (truename, tables) end
  28.  
  29. exception WrongStamp and NotYet
  30.  
  31. fun check_file name stampOpt pending processed = 
  32.   let val simplename = Filename.chop_suffix name ".uo"
  33.       val uname = normalizedUnitName(Filename.basename simplename)
  34.       val () =
  35.       if member uname pending then
  36.           raise Fail ("Unit " ^ name ^ " depends on itself")
  37.       else ()
  38.       val () =
  39.       if member uname reservedUnitNames then
  40.           raise Fail ("Unit "^uname^" is built-in, and cannot be linked")
  41.       else ()
  42.  
  43.       val already = (SOME (Hasht.find (!watchDog) uname)) 
  44.                 handle Subscript => NONE 
  45.  
  46.       fun needs subuname substamp processed =
  47.       (check_file (subuname ^ ".uo") (SOME substamp) 
  48.                   (uname :: pending) processed)
  49.       handle WrongStamp => 
  50.                   raise Fail ("Compiled body of unit " ^ uname 
  51.                     ^ " is incompatible with unit "^ subuname)
  52.            | NotYet => 
  53.               raise Fail ("Unit " ^ subuname ^ " is mentioned by "
  54.                   ^ uname ^ " but not yet linked")
  55.   in 
  56.       case already of
  57.       SOME stamp' =>
  58.           (case stampOpt of
  59.            SOME stamp => 
  60.                if stamp <> stamp' then raise WrongStamp 
  61.                else processed
  62.          | NONE => 
  63.                        (msgIBlock 0;
  64.             errPrompt "Warning: unit ";
  65.             msgString uname; 
  66.             msgString " is needed by a unit preceding it";
  67.             msgEOL();
  68.             msgEBlock();
  69.             processed))
  70.     | NONE => let val implicit = case stampOpt of NONE => false | _ => true
  71.               val _ = if not(!autolink) andalso implicit 
  72.                   then raise NotYet else ()
  73.               val (truename, tables) = read_file name
  74.               val precedingUnits = 
  75.               Hasht.fold needs processed (#cu_mentions tables)
  76.            in 
  77.               Hasht.insert (!watchDog) uname (#cu_sig_stamp tables);
  78.               (uname, truename, tables) :: precedingUnits
  79.           end
  80.   end
  81.  
  82. val check_file = fn name => fn processed => check_file name NONE [] processed
  83.  
  84.  
  85. (* Second pass: determine which phrases are required *)
  86.  
  87. val missing_globals =
  88.     ref (Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t)
  89. ;
  90.  
  91. fun is_in_missing g =
  92.   (Hasht.find (!missing_globals) g; true)
  93.   handle Subscript => false
  94. ;
  95.  
  96. fun remove_from_missing g =
  97.   Hasht.remove (!missing_globals) g
  98. ;
  99.  
  100. fun add_to_missing g =
  101.   Hasht.insert (!missing_globals) g ()
  102. ;
  103.  
  104. fun is_required (Reloc_setglobal g, _) = is_in_missing g
  105.   | is_required _ = false
  106. ;
  107.  
  108. fun remove_required (Reloc_setglobal g, _) = remove_from_missing g
  109.   | remove_required _ = ()
  110. ;
  111.  
  112. fun add_required (Reloc_getglobal g, _) = add_to_missing g
  113.   | add_required _ = ()
  114. ;
  115.  
  116. fun scan_val uname (id, stamp) tolink =
  117.   let val q = {qual=uname, id=id} in
  118.     if is_in_missing (q, 0) then
  119.       (remove_from_missing (q, 0);
  120.        add_to_missing (q, stamp);
  121.        (id, stamp) :: tolink)
  122.     else
  123.       tolink
  124.   end;
  125.  
  126. fun scan_phrase (phr : compiled_phrase) tolink =
  127.   if not(#cph_pure phr) orelse
  128.      List.exists is_required (#cph_reloc phr)
  129.   then
  130.     (List.app remove_required (#cph_reloc phr);
  131.      List.app add_required (#cph_reloc phr);
  132.      phr :: tolink)
  133.   else
  134.     tolink
  135. ;
  136.  
  137. fun scan_file (uname, truename, (tables : compiled_unit_tables)) tolink =
  138.   let val exportedE = #cu_exc_ren_list tables
  139.       val valRenList = #cu_val_ren_list tables
  140.       val exportedV = foldL (scan_val uname) [] valRenList
  141.       val phraseIndex = #cu_phrase_index tables
  142.       val required = foldL scan_phrase [] phraseIndex
  143.   in
  144.     (uname, truename, required, exportedE, exportedV) :: tolink
  145.   end;
  146.  
  147. (* Third pass : link in the required phrases. *)
  148.  
  149. fun link_object os (uname, truename, required, exportedE, exportedV) =
  150.   let val is = open_in_bin truename in
  151.     (List.app
  152.        (fn (phr : compiled_phrase) =>
  153.          let val () = seek_in is (#cph_pos phr)
  154.              val buff = input(is, #cph_len phr)
  155.              val () = if size buff < #cph_len phr
  156.                       then raise Size else ()
  157.          in
  158.            patch_object buff 0 (#cph_reloc phr);
  159.            output(os, buff)
  160.          end)
  161.        required;
  162.      exportPublicNames uname exportedE exportedV;
  163.      close_in is)
  164.     handle x =>
  165.       (close_in is;
  166.        msgIBlock 0;
  167.        errPrompt "Error while linking file ";
  168.        msgString truename; msgEOL();
  169.        msgEBlock();
  170.        raise x)
  171.   end;
  172.  
  173. (* To build the initial table of globals *)
  174.  
  175. local
  176.   prim_val vector_ : int -> '_a -> '_a vector       = 2 "make_vect"
  177.   prim_val sub_    : 'a vector -> int -> 'a         = 2 "get_vect_item"
  178.   prim_val update_ : 'a vector -> int -> 'a -> unit = 3 "set_vect_item"
  179. in
  180.  
  181.   fun emit_data os =
  182.     let val len = number_of_globals()
  183.         val globals = vector_ len (repr 0)
  184.     in
  185.       List.app
  186.         (fn (n,sc) => update_ globals n (translStructuredConst sc))
  187.         (!literal_table);
  188.       output_value os globals
  189.     end;
  190.  
  191. end;
  192.  
  193. (* To build a bytecode executable file *)
  194.  
  195. val write_symbols = ref false;
  196. val no_header = ref false;
  197.  
  198. fun reportLinked toscan =
  199.     let fun reportUnit (uname, _, _) = (msgString uname; msgString ".uo ")
  200.     in 
  201.     msgIBlock 0; 
  202.     msgString "Linking: ";
  203.     List.app reportUnit (rev toscan); 
  204.     msgEOL(); msgEBlock()
  205.     end
  206.  
  207. fun link unit_list exec_name =
  208.   let val _ = missing_globals :=  (* 04Sep95 e *)
  209.                (Hasht.new 263 : (QualifiedIdent * int, unit) Hasht.t)
  210.       val toscan = foldL check_file [] unit_list
  211.       val _ = if !verbose then reportLinked toscan else ()
  212.       val tolink = foldL scan_file [] toscan
  213.       val os = if !no_header then open_out_bin exec_name
  214.                              else open_out_exe exec_name
  215.   in
  216.     ( (* The header *)
  217.       if !no_header then () else
  218.       let val is = open_in_bin (Filename.concat (!path_library) "header")
  219.           val buff = CharArray.array(4096, #"\000")
  220.           fun copy () =
  221.             case buff_input is buff 0 4096 of
  222.                 0 => ()
  223.               | n => (buff_output os buff 0 n; copy())
  224.       in
  225.         (copy(); close_in is)
  226.           handle x => (close_in is; raise x)
  227.       end;
  228.       missing_globals := (* for gc -- 04Sep95 e *)
  229.        (Hasht.new 1 : (QualifiedIdent * int, unit) Hasht.t);
  230.       (* The bytecode *)
  231.       let val pos1 = pos_out os
  232.           val () = List.app (link_object os) tolink
  233.           val () = output_byte os Opcodes.STOP;
  234.           (* The table of global data *)
  235.           val pos2 = pos_out os
  236.           val () = emit_data os
  237.           (* Linker tables *)
  238.           val pos3 = pos_out os
  239.           val () =
  240.             if !write_symbols then save_linker_tables os
  241.             else ();
  242.           (* Debugging info (none, presently) *)
  243.           val pos4 = pos_out os
  244.       in
  245.         (* The trailer *)
  246.         output_binary_int os (pos2 - pos1);
  247.         output_binary_int os (pos3 - pos2);
  248.         output_binary_int os (pos4 - pos3);
  249.         output_binary_int os 0;
  250.         output(os, "ML08");
  251.         close_out os
  252.       end
  253.     ) handle x =>
  254.        (close_out os;
  255.         remove_file exec_name;
  256.         raise x)
  257.   end;
  258.