@@ -35,7 +35,9 @@ module Line_reader : sig
3535
3636 val peek : t -> string option
3737
38- val drop : t -> unit
38+ val drop : t -> drop_action :(unit -> unit ) -> unit
39+ (* * [drop_action] is the function to call if a line was effectively dropped
40+ (if EOF is reached, this function may return without dropping a line). *)
3941
4042 val close : t -> unit
4143
@@ -78,15 +80,17 @@ end = struct
7880 Some s
7981 with End_of_file -> None )
8082
81- let drop t =
83+ let drop t ~ drop_action =
8284 match t.next with
8385 | Some _ ->
8486 t.next < - None ;
85- t.lnum < - t.lnum + 1
87+ t.lnum < - t.lnum + 1 ;
88+ drop_action ()
8689 | None -> (
8790 try
8891 let (_ : string ) = input_line t.ic in
89- t.lnum < - t.lnum + 1
92+ t.lnum < - t.lnum + 1 ;
93+ drop_action ()
9094 with End_of_file -> () )
9195
9296 let lnum t = t.lnum
@@ -99,9 +103,15 @@ module Line_writer : sig
99103
100104 val of_channel : out_channel -> t
101105
102- val write : ?source : Line_reader .t -> t -> string -> unit
106+ val write : ?source : Line_reader .t -> t -> add :(int -> unit ) -> string -> unit
107+ (* * [write ~source t s ~add] writes [s], followed by a newline, and calls
108+ [edit], giving it in argument the number of "line number" pragma lines
109+ emitted before writing [s]. *)
103110
104- val write_lines : ?source : Line_reader .t -> t -> string -> unit
111+ val write_lines : ?source : Line_reader .t -> t -> total :(int -> unit ) -> string -> unit
112+ (* * [write_lines ~source t s ~total] writes all lines in [s], ensures that the last
113+ line ends with a newline, and calls [total], giving it in argument the total number
114+ of lines written, including "line number" pragma lines. *)
105115
106116 val lnum : t -> int
107117end = struct
@@ -113,7 +123,7 @@ end = struct
113123
114124 let of_channel oc = { oc; source = None ; lnum = 0 }
115125
116- let write ?source t s =
126+ let write ?source t ~ add s =
117127 let source =
118128 match source with
119129 | None -> None
@@ -130,21 +140,24 @@ end = struct
130140 | Some (fname1 , lnum1 ), Some (fname2 , lnum2 ) ->
131141 if String. equal fname1 fname2 && lnum1 + 1 = lnum2 then 0 else emit fname2 lnum2
132142 in
143+ add lnum_off;
133144 output_string t.oc s;
134145 output_string t.oc " \n " ;
135146 let lnum_off = lnum_off + 1 in
136147 t.source < - source;
137148 t.lnum < - t.lnum + lnum_off
138149
139- let write_lines ?source t lines =
150+ let write_lines ?source t ~ total lines =
140151 let l = String. split_on_char ~sep: '\n' lines in
152+ let lcount = ref 0 in
141153 let rec w = function
142154 | [ " " ] | [] -> ()
143155 | s :: xs ->
144- write ?source t s;
156+ let () = write ?source t s ~add: ( fun n -> lcount := ! lcount + n + 1 ) in
145157 w xs
146158 in
147- w l
159+ w l;
160+ total ! lcount
148161
149162 let lnum t = t.lnum
150163end
@@ -194,41 +207,41 @@ let action ~resolve_sourcemap_url ~drop_source_map file line =
194207 Source_map (rule_out_index_map (Source_map. of_string content))
195208
196209module Units : sig
197- val read : Line_reader .t -> Unit_info .t -> Unit_info .t
210+ val read : Line_reader .t -> drop_action :( unit -> unit ) -> Unit_info .t -> Unit_info .t
198211
199212 val scan_file : string -> Build_info .t option * Unit_info .t list
200213end = struct
201- let rec read ic uinfo =
214+ let rec read ic ~ drop_action uinfo =
202215 match Line_reader. peek ic with
203216 | None -> uinfo
204217 | Some line -> (
205218 match Unit_info. parse uinfo line with
206219 | None -> uinfo
207220 | Some uinfo ->
208- Line_reader. drop ic;
209- read ic uinfo)
221+ Line_reader. drop ~drop_action ic;
222+ read ic ~drop_action uinfo)
210223
211- let find_unit_info ic =
224+ let find_unit_info ~ drop_action ic =
212225 let rec find_next ic =
213226 match Line_reader. peek ic with
214227 | None -> None
215228 | Some line -> (
216229 match prefix_kind line with
217230 | `Json_base64 _ | `Url _ | `Other | `Build_info _ ->
218- Line_reader. drop ic;
231+ Line_reader. drop ~drop_action ic;
219232 find_next ic
220- | `Unit -> Some (read ic Unit_info. empty))
233+ | `Unit -> Some (read ic ~drop_action Unit_info. empty))
221234 in
222235 find_next ic
223236
224- let find_build_info ic =
237+ let find_build_info ~ drop_action ic =
225238 let rec find_next ic =
226239 match Line_reader. peek ic with
227240 | None -> None
228241 | Some line -> (
229242 match prefix_kind line with
230243 | `Json_base64 _ | `Url _ | `Other ->
231- Line_reader. drop ic;
244+ Line_reader. drop ~drop_action ic;
232245 find_next ic
233246 | `Build_info bi -> Some bi
234247 | `Unit -> None )
@@ -237,12 +250,13 @@ end = struct
237250
238251 let scan_file file =
239252 let ic = Line_reader. open_ file in
253+ let drop_action () = () in
240254 let rec scan_all ic acc =
241- match find_unit_info ic with
255+ match find_unit_info ~drop_action ic with
242256 | None -> List. rev acc
243257 | Some x -> scan_all ic (x :: acc)
244258 in
245- let build_info = find_build_info ic in
259+ let build_info = find_build_info ~drop_action ic in
246260 let units = scan_all ic [] in
247261 Line_reader. close ic;
248262 build_info, units
@@ -323,12 +337,28 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
323337 in
324338 let sm_for_file = ref None in
325339 let ic = Line_reader. open_ file in
326- let skip ic = Line_reader. drop ic in
327- let reloc = ref [] in
340+ let old_line_count = Line_writer. lnum oc in
341+ let edits = ref [] in
342+ let emit_drop_action edits () = edits := Source_map.Line_edits. Drop :: ! edits in
343+ let skip ic = Line_reader. drop ~drop_action: (emit_drop_action edits) ic in
328344 let copy ic oc =
329345 let line = Line_reader. next ic in
330- Line_writer. write ~source: ic oc line;
331- reloc := (Line_reader. lnum ic, Line_writer. lnum oc) :: ! reloc
346+ Line_writer. write
347+ ~source: ic
348+ ~add: (fun count -> edits := Add { count } :: ! edits)
349+ oc
350+ line;
351+ (* Note: line actions are in reverse order compared to the actual generated
352+ lines *)
353+ edits := Source_map.Line_edits. Keep :: ! edits
354+ in
355+ let write_line oc str =
356+ Line_writer. write oc str ~add: (fun count ->
357+ edits := Source_map.Line_edits. (Add { count = count + 1 }) :: ! edits)
358+ in
359+ let write_lines oc str =
360+ Line_writer. write_lines oc str ~total: (fun count ->
361+ edits := Source_map.Line_edits. (Add { count }) :: ! edits)
332362 in
333363 let rec read () =
334364 match Line_reader. peek ic with
@@ -347,11 +377,13 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
347377 if not ! build_info_emitted
348378 then (
349379 let bi = Build_info. with_kind bi (if mklib then `Cma else `Unknown ) in
350- Line_writer. write_lines oc (Build_info. to_string bi);
380+ write_lines oc (Build_info. to_string bi);
351381 build_info_emitted := true )
352382 | Drop -> skip ic
353383 | Unit ->
354- let u = Units. read ic Unit_info. empty in
384+ let u =
385+ Units. read ic ~drop_action: (emit_drop_action edits) Unit_info. empty
386+ in
355387 if StringSet. cardinal (StringSet. inter u.Unit_info. provides to_link) > 0
356388 then (
357389 if u.effects_without_cps && not ! warn_effects
@@ -363,7 +395,7 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
363395 (if mklib
364396 then
365397 let u = if linkall then { u with force_link = true } else u in
366- Line_writer. write_lines oc (Unit_info. to_string u));
398+ write_lines oc (Unit_info. to_string u));
367399 let size = ref 0 in
368400 while
369401 match Line_reader. peek ic with
@@ -407,7 +439,7 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
407439 read ()
408440 in
409441 read () ;
410- Line_writer. write oc " " ;
442+ write_line oc " " ;
411443 Line_reader. close ic;
412444 (match is_runtime with
413445 | None -> ()
@@ -429,10 +461,11 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
429461 (Parse_bytecode.Debug. create ~include_cmis: false false )
430462 code;
431463 let content = Buffer. contents b in
432- Line_writer. write_lines oc content);
464+ write_lines oc content);
433465 (match ! sm_for_file with
434466 | None -> ()
435- | Some x -> sm := (x, ! reloc) :: ! sm);
467+ | Some x ->
468+ sm := (file, x, List. rev ! edits, Line_writer. lnum oc - old_line_count) :: ! sm);
436469 match ! build_info, build_info_for_file with
437470 | None , None -> ()
438471 | Some _ , None -> ()
@@ -445,32 +478,51 @@ let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source
445478 match source_map with
446479 | None -> ()
447480 | Some (file , init_sm ) ->
448- let sm =
449- List. rev_map ! sm ~f: (fun (sm , reloc ) ->
450- let tbl = Hashtbl. create 17 in
451- List. iter reloc ~f: (fun (a , b ) -> Hashtbl. add tbl a b);
452- Source_map. filter_map sm ~f: (Hashtbl. find_opt tbl))
481+ let sourcemaps_and_line_counts =
482+ List. rev_map ! sm ~f: (fun (file , sm , edits , lcount ) ->
483+ if debug ()
484+ then (
485+ Format. eprintf " @[<v>line actions for '%s' (lcount %d)@," file lcount;
486+ Format. eprintf " %a@," Source_map.Line_edits. pp edits;
487+ Format. eprintf " @]" );
488+ let mappings = sm.Source_map. mappings in
489+ let mappings = Source_map.Mappings. edit ~strict: false mappings edits in
490+ { sm with mappings }, lcount)
453491 in
454- (match Source_map. merge (init_sm :: sm) with
455- | None -> ()
456- | Some sm -> (
457- (* preserve some info from [init_sm] *)
458- let sm =
459- { sm with
460- version = init_sm.version
461- ; file = init_sm.file
462- ; sourceroot = init_sm.sourceroot
463- }
464- in
465- match file with
466- | None ->
467- let data = Source_map. to_string sm in
468- let s = sourceMappingURL_base64 ^ Base64. encode_exn data in
469- Line_writer. write oc s
470- | Some file ->
471- Source_map. to_file sm file;
472- let s = sourceMappingURL ^ Filename. basename file in
473- Line_writer. write oc s));
492+ let merged_sourcemap =
493+ let open Source_map in
494+ assert (String. equal (Mappings. to_string init_sm.mappings) " " );
495+ { version = init_sm.version
496+ ; file = init_sm.file
497+ ; Index. sections =
498+ (let _, sections =
499+ List. fold_left
500+ sourcemaps_and_line_counts
501+ ~f: (fun (cur_ofs , sections ) (sm , generated_line_count ) ->
502+ let offset = Index. { gen_line = cur_ofs; gen_column = 0 } in
503+ cur_ofs + generated_line_count, (offset, `Map sm) :: sections)
504+ ~init: (0 , [] )
505+ in
506+ List. rev sections)
507+ }
508+ in
509+ (* preserve some info from [init_sm] *)
510+ let merged_sourcemap =
511+ { merged_sourcemap with
512+ sections =
513+ List. map merged_sourcemap.sections ~f: (fun (ofs , `Map sm ) ->
514+ ofs, `Map { sm with sourceroot = init_sm.sourceroot })
515+ }
516+ in
517+ (match file with
518+ | None ->
519+ let data = Source_map.Index. to_string merged_sourcemap in
520+ let s = sourceMappingURL_base64 ^ Base64. encode_exn data in
521+ Line_writer. write oc s ~add: (fun _ -> () ) |> ignore
522+ | Some file ->
523+ Source_map.Index. to_file merged_sourcemap file;
524+ let s = sourceMappingURL ^ Filename. basename file in
525+ Line_writer. write oc s ~add: (fun _ -> () ) |> ignore);
474526 if times () then Format. eprintf " sourcemap: %a@." Timer. print t
475527
476528let link ~output ~linkall ~mklib ~toplevel ~files ~resolve_sourcemap_url ~source_map =
0 commit comments