Skip to content

Commit 579adef

Browse files
committed
Add sourcemap support to wasm binary format
1 parent 7d0e3c5 commit 579adef

File tree

11 files changed

+139
-47
lines changed

11 files changed

+139
-47
lines changed

compiler/bin-wasm_of_ocaml/compile.ml

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ let opt_with action x f =
7474
| None -> f None
7575
| Some x -> action x (fun y -> f (Some y))
7676

77-
let output_gen output_file f = Filename.gen_file output_file f
78-
7977
let with_runtime_files ~runtime_wasm_files f =
8078
let inputs =
8179
List.map
@@ -163,8 +161,12 @@ let link_and_optimize
163161
@@ fun runtime_inputs ->
164162
Binaryen.link
165163
~inputs:
166-
(({ Binaryen.module_name = "env"; file = runtime_file } :: runtime_inputs)
167-
@ List.map ~f:(fun file -> { Binaryen.module_name = "OCaml"; file }) wat_files)
164+
({ Binaryen.module_name = "env"; file = runtime_file; source_map_file = None }
165+
:: runtime_inputs
166+
@ List.map
167+
~f:(fun (file, source_map_file) ->
168+
{ Binaryen.module_name = "OCaml"; file; source_map_file })
169+
wat_files)
168170
~opt_output_sourcemap:opt_temp_sourcemap
169171
~output_file:temp_file
170172
());
@@ -223,7 +225,7 @@ let link_runtime ~profile runtime_wasm_files output_file =
223225
~opt_output_sourcemap:None
224226
~inputs:
225227
(List.map
226-
~f:(fun file -> { Binaryen.module_name = "env"; file })
228+
~f:(fun file -> { Binaryen.module_name = "env"; file; source_map_file = None })
227229
[ runtime_file; extra_runtime ])
228230
~output_file
229231
()
@@ -246,7 +248,7 @@ let generate_prelude ~out_file =
246248
~deadcode_sentinal
247249
program
248250
in
249-
Generate.wasm_output ch ~context;
251+
Generate.wasm_output ch ~opt_source_map_file:None ~context;
250252
uinfo.provides
251253

252254
let build_prelude z =
@@ -381,7 +383,7 @@ let run
381383
| Some p -> p
382384
| None -> Profile.O1
383385
in
384-
let output (one : Parse_bytecode.one) ~unit_name ch =
386+
let output (one : Parse_bytecode.one) ~unit_name ~wat_file ~file ~opt_source_map_file =
385387
check_debug one;
386388
let code = one.code in
387389
let standalone = Option.is_none unit_name in
@@ -399,7 +401,14 @@ let run
399401
program
400402
in
401403
if standalone then Generate.add_start_function ~context toplevel_name;
402-
Generate.output ch ~enable_source_maps ~context;
404+
let ch = open_out_bin file in
405+
Generate.wasm_output ch ~opt_source_map_file ~context;
406+
close_out ch;
407+
if debug_wat ()
408+
then (
409+
let ch = open_out_bin wat_file in
410+
Generate.output ch ~context;
411+
close_out ch);
403412
if times () then Format.eprintf "compilation: %a@." Timer.print t;
404413
generated_js
405414
in
@@ -458,20 +467,28 @@ let run
458467
else None)
459468
@@ fun opt_tmp_map_file ->
460469
let unit_data =
461-
(if debug_wat ()
462-
then
463-
fun f ->
464-
f (Filename.concat (Filename.dirname output_file) (unit_name ^ ".wat"))
465-
else Fs.with_intermediate_file (Filename.temp_file unit_name ".wat"))
466-
@@ fun wat_file ->
470+
Fs.with_intermediate_file (Filename.temp_file unit_name ".wasm")
471+
@@ fun input_file ->
472+
opt_with
473+
Fs.with_intermediate_file
474+
(if enable_source_maps
475+
then Some (Filename.temp_file unit_name ".wasm.map")
476+
else None)
477+
@@ fun opt_input_sourcemap ->
467478
let strings, fragments =
468-
output_gen wat_file (output code ~unit_name:(Some unit_name))
479+
output
480+
code
481+
~wat_file:
482+
(Filename.concat (Filename.dirname output_file) (unit_name ^ ".wat"))
483+
~unit_name:(Some unit_name)
484+
~file:input_file
485+
~opt_source_map_file:opt_input_sourcemap
469486
in
470487
Binaryen.optimize
471488
~profile
472-
~opt_input_sourcemap:None
489+
~opt_input_sourcemap
473490
~opt_output_sourcemap:opt_tmp_map_file
474-
~input_file:wat_file
491+
~input_file
475492
~output_file:tmp_wasm_file
476493
();
477494
{ Link.unit_name; unit_info; strings; fragments }
@@ -491,10 +508,8 @@ let run
491508
ic
492509
in
493510
if times () then Format.eprintf " parsing: %a@." Timer.print t1;
494-
(if debug_wat ()
495-
then fun f -> f (Filename.chop_extension output_file ^ ".wat")
496-
else Fs.with_intermediate_file (Filename.temp_file "code" ".wat"))
497-
@@ fun wat_file ->
511+
Fs.with_intermediate_file (Filename.temp_file "code" ".wasm")
512+
@@ fun input_wasm_file ->
498513
let dir = Filename.chop_extension output_file ^ ".assets" in
499514
Link.gen_dir dir
500515
@@ fun tmp_dir ->
@@ -504,7 +519,19 @@ let run
504519
then Some (Filename.concat tmp_dir "code.wasm.map")
505520
else None
506521
in
507-
let generated_js = output_gen wat_file (output code ~unit_name:None) in
522+
let opt_source_map_file =
523+
if enable_source_maps
524+
then Some (Filename.temp_file "code" ".wasm.map")
525+
else None
526+
in
527+
let generated_js =
528+
output
529+
code
530+
~unit_name:None
531+
~wat_file:(Filename.chop_extension output_file ^ ".wat")
532+
~file:input_wasm_file
533+
~opt_source_map_file
534+
in
508535
let tmp_wasm_file = Filename.concat tmp_dir "code.wasm" in
509536
let primitives =
510537
link_and_optimize
@@ -513,7 +540,7 @@ let run
513540
~sourcemap_don't_inline_content
514541
~opt_sourcemap
515542
runtime_wasm_files
516-
[ wat_file ]
543+
[ input_wasm_file, opt_source_map_file ]
517544
tmp_wasm_file
518545
in
519546
let wasm_name =

compiler/lib-wasm/binaryen.ml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ let opt_flag flag v =
4848
type link_input =
4949
{ module_name : string
5050
; file : string
51+
; source_map_file : string option
5152
}
5253

5354
let link ?options ~inputs ~opt_output_sourcemap ~output_file () =
@@ -57,7 +58,13 @@ let link ?options ~inputs ~opt_output_sourcemap ~output_file () =
5758
@ Option.value ~default:[] options
5859
@ List.flatten
5960
(List.map
60-
~f:(fun { file; module_name } -> [ Filename.quote file; module_name ])
61+
~f:(fun { file; module_name; source_map_file } ->
62+
Filename.quote file
63+
:: module_name
64+
::
65+
(match source_map_file with
66+
| None -> []
67+
| Some file -> [ "--input-source-map"; Filename.quote file ]))
6168
inputs)
6269
@ [ "-o"; Filename.quote output_file ]
6370
@ opt_flag "--output-source-map" opt_output_sourcemap))

compiler/lib-wasm/binaryen.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
type link_input =
2020
{ module_name : string (** Name under which the module is imported in other modules *)
2121
; file : string (** File containing the Wasm module *)
22+
; source_map_file : string option
2223
}
2324

2425
val link :

compiler/lib-wasm/generate.ml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,16 +1315,14 @@ let add_start_function = G.add_start_function
13151315

13161316
let add_init_function = G.add_init_function
13171317

1318-
let output ch ~enable_source_maps ~context =
1318+
let output ch ~context =
13191319
let t = Timer.make () in
13201320
let fields = G.output ~context in
1321-
if enable_source_maps || Debug.find "wat" ()
1322-
then Wat_output.f ch fields
1323-
else Wasm_output.f ch fields;
1321+
Wat_output.f ch fields;
13241322
if times () then Format.eprintf " output: %a@." Timer.print t
13251323

1326-
let wasm_output ch ~context =
1324+
let wasm_output ch ~opt_source_map_file ~context =
13271325
let t = Timer.make () in
13281326
let fields = G.output ~context in
1329-
Wasm_output.f ch fields;
1327+
Wasm_output.f ch ~opt_source_map_file fields;
13301328
if times () then Format.eprintf " output: %a@." Timer.print t

compiler/lib-wasm/generate.mli

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ val add_start_function : context:Code_generation.context -> Wasm_ast.var -> unit
3333

3434
val add_init_function : context:Code_generation.context -> to_link:string list -> unit
3535

36-
val output :
37-
out_channel -> enable_source_maps:bool -> context:Code_generation.context -> unit
36+
val output : out_channel -> context:Code_generation.context -> unit
3837

39-
val wasm_output : out_channel -> context:Code_generation.context -> unit
38+
val wasm_output :
39+
out_channel
40+
-> opt_source_map_file:string option
41+
-> context:Code_generation.context
42+
-> unit

compiler/lib-wasm/link.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ let generate_start_function ~to_link ~out_file =
402402
@@ fun ch ->
403403
let context = Generate.start () in
404404
Generate.add_init_function ~context ~to_link:("prelude" :: to_link);
405-
Generate.wasm_output ch ~context;
405+
Generate.wasm_output ch ~opt_source_map_file:None ~context;
406406
if times () then Format.eprintf " generate start: %a@." Timer.print t1
407407

408408
let output_js js =

compiler/lib-wasm/wasm_output.ml

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ module Make (Output : sig
6262
val byte : t -> int -> unit
6363

6464
val string : t -> string -> unit
65+
66+
val push_mapping : Source_map.map -> unit
67+
68+
val get_file_index : string -> int
6569
end) : sig
6670
val output_module : Output.t -> module_field list -> unit
6771
end = struct
@@ -460,6 +464,29 @@ end = struct
460464
; current_local_names : int Code.Var.Hashtbl.t
461465
}
462466

467+
let last_event = ref None
468+
469+
let push_no_event ch =
470+
if Option.is_some !last_event
471+
then (
472+
Output.push_mapping (Source_map.Gen { gen_line = 1; gen_col = position ch });
473+
last_event := None)
474+
475+
let push_event ch ~src ~line ~col =
476+
match !last_event with
477+
| Some (src', line', col') when col = col' && line = line' && String.equal src src' ->
478+
()
479+
| _ ->
480+
Output.push_mapping
481+
(Source_map.Gen_Ori
482+
{ gen_line = 1
483+
; gen_col = position ch
484+
; ori_source = Output.get_file_index src
485+
; ori_line = line
486+
; ori_col = col
487+
});
488+
last_event := Some (src, line, col)
489+
463490
let rec output_expression st ch e =
464491
match e with
465492
| Const c -> (
@@ -772,7 +799,8 @@ end = struct
772799
output_byte ch 0x15;
773800
output_uint ch (Code.Var.Hashtbl.find st.type_names typ)
774801
| Unreachable -> output_byte ch 0x00
775-
| Event _ -> ()
802+
| Event Parse_info.{ src = None | Some ""; _ } -> push_no_event ch
803+
| Event Parse_info.{ src = Some src; line; col; _ } -> push_event ch ~src ~line ~col
776804

777805
let output_globals ch (st, global_idx, fields) =
778806
let count =
@@ -1034,7 +1062,8 @@ end = struct
10341062
prerr_endline (Printexc.to_string e);
10351063
prerr_endline backtrace;
10361064
assert false);
1037-
output_byte ch 0x0B))
1065+
output_byte ch 0x0B;
1066+
push_no_event ch))
10381067
ch
10391068
(List.rev l)
10401069

@@ -1170,7 +1199,9 @@ end = struct
11701199
output_section 0 output_features ch ()
11711200
end
11721201

1173-
let f ch fields =
1202+
let f ~opt_source_map_file ch fields =
1203+
let mappings = ref [] in
1204+
let files = String.Hashtbl.create 16 in
11741205
let module O = Make (struct
11751206
type t = out_channel
11761207

@@ -1181,5 +1212,27 @@ let f ch fields =
11811212
let byte = output_byte
11821213

11831214
let string = output_string
1215+
1216+
let push_mapping m = mappings := m :: !mappings
1217+
1218+
let get_file_index file =
1219+
try String.Hashtbl.find files file
1220+
with Not_found ->
1221+
let pos = String.Hashtbl.length files in
1222+
String.Hashtbl.add files file pos;
1223+
pos
11841224
end) in
1185-
O.output_module ch fields
1225+
O.output_module ch fields;
1226+
Option.iter opt_source_map_file ~f:(fun source_map_file ->
1227+
let hashtbl_to_list htb =
1228+
String.Hashtbl.fold (fun k v l -> (k, v) :: l) htb []
1229+
|> List.sort ~cmp:(fun (_, a) (_, b) -> compare a b)
1230+
|> List.map ~f:fst
1231+
in
1232+
let sm =
1233+
{ (Source_map.Standard.empty ~inline_source_content:false) with
1234+
sources = hashtbl_to_list files
1235+
; mappings = Source_map.Mappings.encode (List.rev !mappings)
1236+
}
1237+
in
1238+
Source_map.to_file ~rewrite_paths:false (Standard sm) source_map_file)

compiler/lib-wasm/wasm_output.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1717
*)
1818

19-
val f : out_channel -> Wasm_ast.module_field list -> unit
19+
val f :
20+
opt_source_map_file:string option -> out_channel -> Wasm_ast.module_field list -> unit

compiler/lib-wasm/wat_preprocess.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ let with_preprocessed_files ~variables ~inputs action =
626626
if Link.Wasm_binary.check_file ~file then None else Some (Fs.read_file file)
627627
| Contents contents -> Some contents
628628
with
629-
| None -> cont ({ Binaryen.module_name; file } :: inputs)
629+
| None -> cont ({ Binaryen.module_name; file; source_map_file = None } :: inputs)
630630
| Some contents ->
631631
let source_file = file in
632632
Fs.with_intermediate_file (Filename.temp_file module_name ".wat")
@@ -637,7 +637,7 @@ let with_preprocessed_files ~variables ~inputs action =
637637
(if Link.Wasm_binary.check ~contents
638638
then contents
639639
else f ~variables ~filename:source_file ~contents);
640-
cont ({ Binaryen.module_name; file } :: inputs))
640+
cont ({ Binaryen.module_name; file; source_map_file = None } :: inputs))
641641
~init:action
642642
inputs
643643
[]

compiler/lib/source_map.ml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,8 @@ module Standard = struct
574574

575575
let to_string m = Yojson.Raw.to_string (json (rewrite_paths m))
576576

577-
let to_file m file = Yojson.Raw.to_file file (json (rewrite_paths m))
577+
let to_file ?rewrite_paths:(rewrite = true) m file =
578+
Yojson.Raw.to_file file (json (if rewrite then rewrite_paths m else m))
578579

579580
let invariant
580581
{ version
@@ -715,7 +716,8 @@ module Index = struct
715716

716717
let to_string m = Yojson.Raw.to_string (json (rewrite_paths m))
717718

718-
let to_file m file = Yojson.Raw.to_file file (json (rewrite_paths m))
719+
let to_file ?rewrite_paths:(rewrite = true) m file =
720+
Yojson.Raw.to_file file (json (if rewrite then rewrite_paths m else m))
719721

720722
let invariant { version; file = _; sections } =
721723
if not (version_is_valid version)
@@ -756,10 +758,10 @@ let to_string = function
756758
| Standard m -> Standard.to_string m
757759
| Index i -> Index.to_string i
758760

759-
let to_file x f =
761+
let to_file ?rewrite_paths x f =
760762
match x with
761-
| Standard m -> Standard.to_file m f
762-
| Index i -> Index.to_file i f
763+
| Standard m -> Standard.to_file ?rewrite_paths m f
764+
| Index i -> Index.to_file ?rewrite_paths i f
763765

764766
let invariant = function
765767
| Standard m -> Standard.invariant m

compiler/lib/source_map.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ type t =
141141

142142
val to_string : t -> string
143143

144-
val to_file : t -> string -> unit
144+
val to_file : ?rewrite_paths:bool -> t -> string -> unit
145145

146146
val of_string : ?tmp_buf:Buffer.t -> string -> t
147147

0 commit comments

Comments
 (0)