jon.recoil.org

Source file cmi_format.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
(**************************************************************************)
(*                                                                        *)
(*                                 OCaml                                  *)
(*                                                                        *)
(*                   Fabrice Le Fessant, INRIA Saclay                     *)
(*                                                                        *)
(*   Copyright 2012 Institut National de Recherche en Informatique et     *)
(*     en Automatique.                                                    *)
(*                                                                        *)
(*   All rights reserved.  This file is distributed under the terms of    *)
(*   the GNU Lesser General Public License version 2.1, with the          *)
(*   special exception on linking described in the file LICENSE.          *)
(*                                                                        *)
(**************************************************************************)

open Misc

type pers_flags =
  | Rectypes
  | Alerts of alerts
  | Opaque

type kind =
  | Normal of {
      cmi_impl : Compilation_unit.t;
      cmi_arg_for : Global_module.Parameter_name.t option;
    }
  | Parameter

(* A serialized cmi file has the following format.contents

  - magic number
  - size of data block
  - data block (serialized bits of signature)
  - compilation unit name
  - serialized signature with offsets into data block in wrapped positions
  - crcs
  - flags

  The serialized signature contains only the top level, with wrapped (cf. Types)
  values represented as offsets into the data block where their serialized
  (again, shallowly) representation can be found. When deserializing, we read
  the entire data block into memory as one blob and then deserialize from it as
  needed when values are forced.

  Note that we are deliberately using int for offsets here because int64 is more
  expensive. On 32 bits architectures, this imposes a constraint on the size of
  .cmi files. *)
module Serialized = Types.Make_wrapped(struct type 'a t = int end)

(* these type abbreviations are not exported;
   they are used to provide consistency across
   input_value and output_value usage. *)
type crcs = Import_info.t array  (* smaller on disk than using a list *)
type flags = pers_flags list
type header = {
    header_name : Compilation_unit.Name.t;
    header_kind : kind;
    header_globals : Global_module.With_precision.t array;
    header_sign : Serialized.persistent_signature;
    header_params : Global_module.Parameter_name.t list;
}

type 'sg cmi_infos_generic = {
    cmi_name : Compilation_unit.Name.t;
    cmi_kind : kind;
    cmi_globals : Global_module.With_precision.t array;
    cmi_sign : 'sg;
    cmi_params : Global_module.Parameter_name.t list;
    cmi_crcs : crcs;
    cmi_flags : flags;
}

type cmi_infos_lazy = Subst.Lazy.persistent_signature cmi_infos_generic
type cmi_infos = Types.persistent_signature cmi_infos_generic

let force_cmi_infos cmi =
  let sign, staticity = cmi.cmi_sign in
  { cmi with cmi_sign = Subst.Lazy.force_signature sign, staticity }

module Deserialize = Types.Map_wrapped(Serialized)(Subst.Lazy)

let deserialize data =
  (* Values are offsets into `data` *)
  let map_signature fn n =
    lazy(Marshal.from_bytes data n |> List.map (Deserialize.signature_item fn))
    |> Subst.Lazy.of_lazy
  in
  let map_type_expr _ n =
    lazy(Marshal.from_bytes data n : Types.type_expr) |> Subst.Lazy.of_lazy
  in
  let map_value_description _ (vd : Serialized.value_description) =
    (* See comments in [serialize] about [vars] and [ty]. *)
    let lpoly_type =
      lazy (Marshal.from_bytes data vd.val_type :
        Jkind_types.Sort.var list * Types.type_expr)
    in
    let val_lpoly =
      lazy (Lazy.force lpoly_type |> fst |> Types.Lpoly.determined)
      |> Subst.Lazy.of_lazy
    in
    let val_type =
      lazy (Lazy.force lpoly_type |> snd) |> Subst.Lazy.of_lazy
    in
    Subst.Lazy.{
      val_type;
      val_lpoly;
      val_modalities = vd.val_modalities;
      val_kind = vd.val_kind;
      val_zero_alloc = vd.val_zero_alloc;
      val_attributes = vd.val_attributes;
      val_loc = vd.val_loc;
      val_uid = vd.val_uid;
    }
  in
  Deserialize.signature {map_signature; map_type_expr; map_value_description}

(* Serialization is unused by merlin.

module Serialize = Types.Map_wrapped(Subst.Lazy)(Serialized)

let serialize oc base =
  (* Serialize values into the stream and produce their offsets within the data
    block (which starts at `base`). *)
  let marshal x =
    let pos = Out_channel.pos oc in
    Marshal.to_channel oc x [];
    Int64.to_int (Int64.sub pos base)
  in
  let map_signature fn sg =
    Subst.Lazy.force_signature_once sg
    |> List.map (Serialize.signature_item fn)
    |> marshal
  in
  let map_type_expr _ ty = Subst.Lazy.force_type_expr ty |> marshal in
  let map_value_description _ (vd : Subst.Lazy.value_description) =
    (* [val_type] and [val_lpoly] are marshalled in one-go to preserve physical
       identity of sort variables. The result is stored in [val_type], and
       [val_lpoly] is set to [-1] (to be ignored upon unmarshalling) *)
    let vars = Types.Lpoly.get_exn (Subst.Lazy.force_lpoly vd.val_lpoly) in
    let ty = Subst.Lazy.force_type_expr vd.val_type in
    Serialized.{
      val_type = marshal (vars, ty);
      val_lpoly = -1; (* invalid offset *)
      val_modalities = vd.val_modalities;
      val_kind = vd.val_kind;
      val_zero_alloc = vd.val_zero_alloc;
      val_attributes = vd.val_attributes;
      val_loc = vd.val_loc;
      val_uid = vd.val_uid;
    }
  in
  Serialize.signature {map_signature; map_type_expr; map_value_description}
*)

let input_cmi_lazy ic =
  let read_bytes n =
    let buf = Bytes.create n in
    match In_channel.really_input ic buf 0 n with
    | Some () -> buf
    | None -> assert false
  in
  let data_len = Bytes.get_int64_ne (read_bytes 8) 0 |> Int64.to_int in
  let data = read_bytes data_len in
  let {
      header_name = name;
      header_kind = kind;
      header_globals = globals;
      header_sign = (sign, staticity);
      header_params = params;
    } = (input_value ic : header) in
  let crcs = (input_value ic : crcs) in
  let flags = (input_value ic : flags) in
  (* CR ocaml 5 compressed-marshal mshinwell: upstream uses [Compression] *)
  {
      cmi_name = name;
      cmi_kind = kind;
      cmi_globals = globals;
      cmi_sign = (deserialize data sign, staticity);
      cmi_params = params;
      cmi_crcs = crcs;
      cmi_flags = flags;
    }

let read_cmi_lazy filename =
  let open Magic_numbers.Cmi in
  let ic = open_in_bin filename in
  try
    let buffer =
      really_input_string ic (String.length Config.cmi_magic_number)
    in
    if buffer <> Config.cmi_magic_number then begin
      close_in ic;
      let pre_len = String.length Config.cmi_magic_number - 3 in
      if String.sub buffer 0 pre_len
          = String.sub Config.cmi_magic_number 0 pre_len then
      begin
        raise (Error (Wrong_version_interface (filename, buffer)))
      end else begin
        raise(Error(Not_an_interface filename))
      end
    end;
    let cmi = input_cmi_lazy ic in
    close_in ic;
    cmi
  with End_of_file | Failure _ ->
      close_in ic;
      raise(Error(Corrupted_interface(filename)))
    | Error e ->
      close_in ic;
      raise (Error e)

let output_cmi filename oc cmi =
  ignore (filename, oc, cmi); ""
(*
(* beware: the provided signature must have been substituted for saving *)
  output_string oc Config.cmi_magic_number;
  let output_int64 oc n =
    let buf = Bytes.create 8 in
    Bytes.set_int64_ne buf 0 n;
    output_bytes oc buf
  in
  (* Reserve space for length of data block, produce the block and then write
     the length. *)
  let len_pos = Out_channel.pos oc in
  output_int64 oc Int64.zero;
  let data_pos = Int64.add len_pos (Int64.of_int 8) in
  let sign, staticity = cmi.cmi_sign in
  let sign = serialize oc data_pos sign in
  let val_pos = Out_channel.pos oc in
  Out_channel.seek oc len_pos;
  let len = Int64.sub val_pos data_pos in
  output_int64 oc len;
  Out_channel.seek oc val_pos;
  output_value oc
    {
      header_name = cmi.cmi_name;
      header_kind = cmi.cmi_kind;
      header_globals = cmi.cmi_globals;
      header_sign = (sign, staticity);
      header_params = cmi.cmi_params;
    };
  flush oc;
  let crc = Digest.file filename in
  let my_info =
    match cmi.cmi_kind with
    | Normal { cmi_impl } ->
      Import_info.Intf.create_normal cmi.cmi_name cmi_impl ~crc
    | Parameter ->
      Import_info.Intf.create_parameter cmi.cmi_name ~crc
  in
  let crcs = Array.append [| my_info |] cmi.cmi_crcs in
  output_value oc (crcs : crcs);
  output_value oc (cmi.cmi_flags : flags);
  crc
*)

let input_cmi ic = input_cmi_lazy ic |> force_cmi_infos
let read_cmi filename = read_cmi_lazy filename |> force_cmi_infos

(* Error report moved to src/ocaml/typing/magic_numbers.ml *)