2 * Copyright (c) 2014, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
13 (*****************************************************************************)
14 (* On the fly type-declaration are called when the user modified a file
15 * we are not at initilalization time anymore. Therefore, we have a bit more
16 * work to do. We need calculate what must be re-checked.
18 (*****************************************************************************)
21 (*****************************************************************************)
22 (* The neutral element of declaration (cf procs/multiWorker.mli) *)
23 (*****************************************************************************)
24 let otf_neutral = [], SSet.empty
25 let compute_deps_neutral = ISet.empty
, ISet.empty
27 (*****************************************************************************)
28 (* This is the place where we are going to put everything necessary for
29 * the redeclaration. We could "pass" the values directly to the workers,
30 * but it gives too much work to the master and slows things downn.
31 * So what we do instead is:
32 * Set the value of "data_storage", which means that one of the data nodes
34 * Let each worker pool the data from "data_storage".
35 * I tried replicating the data to speed things up but it had no effect.
37 (*****************************************************************************)
39 type fast
= FileInfo.names
SMap.t
40 type classes
= SSet.t
SMap.t
42 module OnTheFlyStore
= GlobalStorage.Make
(struct
43 type t
= Naming.env
* classes
* fast
46 (*****************************************************************************)
47 (* Re-declaring the types in a file *)
48 (*****************************************************************************)
50 let on_the_fly_decl_file nenv all_classes fast
(errors
, failed
) fn
=
52 (* We start "recording" dependencies.
53 * Whenever we are type-checking or declaring types, the checker
54 * records all the dependencies in a global (cf Typing_deps).
55 * At any given time, all the workers must be aware of all the
56 * dependencies (cf Typing_deps.udpate_dependencies).
57 * The problem is, sending the entire graph every time is too
58 * expensive. So what we do instead, is that we "record" the changes.
59 * If a new dependency shows up, it will end-up in Typing_deps.record_acc.
60 * Sending only the difference is a drastic improvement in perf.
62 Typing_decl.make_env nenv all_classes fn
;
65 (* It is important to add the file that is the cause of the failure.
66 * What can happen is that during a declaration phase, we realize
67 * that a parent class is outdated. When this happens, we redeclare
68 * the class, even if it is in a different file. Therefore, the file
69 * where the error occurs might be different from the file we
70 * are declaring right now.
72 let file_with_error = Pos.filename
(fst
(List.hd l
)) in
73 assert (file_with_error <> "");
74 let failed = SSet.add
file_with_error failed in
75 let failed = SSet.add fn
failed in
77 | e
-> failwith
("Things went horribly wrong: "^
Printexc.to_string e
)
79 (*****************************************************************************)
80 (* Given a set of classes, compare the old and the new type and deduce
81 * what must be rechecked accordingly.
83 (*****************************************************************************)
85 let compute_classes_deps old_classes new_classes acc classes
=
86 let to_redecl, to_recheck
= acc
in
87 let trace = ref ISet.empty
in
89 Typing_compare.get_classes_deps old_classes new_classes
trace classes
91 let to_redecl = ISet.union
rdd to_redecl in
92 let to_recheck = ISet.union rdc
to_recheck in
95 (*****************************************************************************)
96 (* Given a set of functions, compare the old and the new type and deduce
97 * what must be rechecked accordingly.
99 (*****************************************************************************)
101 let compute_funs_deps old_funs
(to_redecl, to_recheck) funs
=
102 let rdd, rdc
= Typing_compare.get_funs_deps old_funs funs
in
103 let to_redecl = ISet.union
rdd to_redecl in
104 let to_recheck = ISet.union rdc
to_recheck in
105 to_redecl, to_recheck
107 (*****************************************************************************)
108 (* Given a set of typedefs, compare the old and the new type and deduce
109 * what must be rechecked accordingly.
111 (*****************************************************************************)
113 let compute_types_deps old_types
(to_redecl, to_recheck) types
=
114 let rdc = Typing_compare.get_types_deps old_types types
in
115 let to_redecl = ISet.union
rdc to_redecl in
116 let to_recheck = ISet.union
rdc to_recheck in
117 to_redecl, to_recheck
119 (*****************************************************************************)
120 (* Given a set of global constants, compare the old and the new type and
121 * deduce what must be rechecked accordingly.
123 (*****************************************************************************)
125 let compute_gconsts_deps old_gconsts
(to_redecl, to_recheck) gconsts
=
126 let rdd, rdc = Typing_compare.get_gconsts_deps old_gconsts gconsts
in
127 let to_redecl = ISet.union
rdd to_redecl in
128 let to_recheck = ISet.union
rdc to_recheck in
129 to_redecl, to_recheck
131 (*****************************************************************************)
132 (* Sometimes, we know that the API of class hasn't changed. The only thing
133 * that changed are the positions (the user added a new line somewhere).
134 * When that happens, it is too expensive to go off and redeclare everything
135 * that inherits from this class.
136 * Some classes have thousands of sub-classes, working on such of file,
137 * without this function, would be nightmarish. Every new line, would trigger
138 * a huge re-declaration phase.
139 * The solution is simple. If we know the API hasn't changed (only the
140 * positions). We create a substitution (from Pos to Pos), replacing the
141 * old_positions with the new one. We then apply this substitution to all the
142 * classes that needed to be redeclared directly on the name nodes.
144 (*****************************************************************************)
147 let update_positions classes to_update =
148 (* First compute the substitution *)
149 let position_subst, is_empty
=
150 let old_classes = Typing_env.Classes.get_old_batch classes
in
151 let new_classes = Typing_env.Classes.find_batch classes
in
152 Typing_compare.get_classes_psubst
old_classes new_classes classes
157 (* Now apply the sustitution in parallel on all the data nodes *)
158 Typing_env.Classes.apply_batch to_update
begin fun class_
->
159 Typing_compare.SubstPos.class_type
position_subst class_
165 (*****************************************************************************)
166 (* Redeclares a list of files
167 * And then computes the files that must be redeclared/rechecked by looking
168 * at what changed in the signatures of the classes/functions.
170 (*****************************************************************************)
172 let redeclare_files nenv all_classes fast filel
=
174 (on_the_fly_decl_file nenv all_classes fast
)
178 let otf_decl_files nenv all_classes fast filel
=
179 SharedMem.invalidate_caches
();
180 (* Redeclaring the files *)
181 let errors, failed = redeclare_files nenv all_classes fast filel
in
184 let compute_deps ~update_pos nenv fast filel
=
185 let infol = List.map
(fun fn
-> SMap.find_unsafe fn fast
) filel
in
186 let names = List.fold_left
FileInfo.merge_names
FileInfo.empty_names
infol in
187 let { FileInfo.n_classes
; n_funs
; n_types
; n_consts
} = names in
188 let acc = ISet.empty
, ISet.empty
in
189 (* Fetching everything at once is faster *)
190 let old_funs = Typing_env.Funs.get_old_batch n_funs
in
191 let acc = compute_funs_deps old_funs acc n_funs
in
193 let old_types = Typing_env.Typedefs.get_old_batch n_types
in
194 let acc = compute_types_deps old_types acc n_types
in
196 let old_consts = Typing_env.GConsts.get_old_batch n_consts
in
197 let acc = compute_gconsts_deps old_consts acc n_consts
in
199 let old_classes = Typing_env.Classes.get_old_batch n_classes
in
200 let new_classes = Typing_env.Classes.get_batch n_classes
in
201 let compare_classes = compute_classes_deps old_classes new_classes in
202 let (to_redecl, to_recheck) = compare_classes acc n_classes
in
203 (* TODO: DEACTIVATING THE CODE FOR NOW BECAUSE OF A BUG
205 then update_positions classes (SSet.diff to_redecl to_recheck);
206 let to_redecl = SSet.inter to_redecl to_recheck in
208 to_redecl, to_recheck
210 (*****************************************************************************)
211 (* Load the environment and then redeclare *)
212 (*****************************************************************************)
214 let load_and_otf_decl_files acc filel
=
216 let nenv, all_classes
, fast
= OnTheFlyStore.load
() in
217 otf_decl_files nenv all_classes fast filel
219 Printf.printf
"Error: %s\n" (Printexc.to_string e
);
223 let load_and_compute_deps ~update_pos
acc filel
=
225 let nenv, _
, fast
= OnTheFlyStore.load
() in
226 compute_deps ~update_pos
nenv fast filel
228 Printf.printf
"Error: %s\n" (Printexc.to_string e
);
232 (*****************************************************************************)
233 (* Merges the results comming back from the different workers *)
234 (*****************************************************************************)
236 let merge_on_the_fly (errorl1
, failed1
) (errorl2
, failed2
) =
237 errorl1
@ errorl2
, SSet.union failed1 failed2
239 let merge_compute_deps (to_redecl1
, to_recheck1
) (to_redecl2
, to_recheck2
) =
240 ISet.union to_redecl1 to_redecl2
, ISet.union to_recheck1 to_recheck2
242 (*****************************************************************************)
243 (* The parallel worker *)
244 (*****************************************************************************)
246 let parallel_otf_decl ~update_pos workers
nenv all_classes fast fnl
=
247 OnTheFlyStore.store
(nenv, all_classes
, fast
);
251 ~job
:load_and_otf_decl_files
253 ~merge
:merge_on_the_fly
254 ~next
:(Bucket.make fnl
)
256 let to_redecl, to_recheck =
259 ~job
:(load_and_compute_deps ~update_pos
)
260 ~neutral
:compute_deps_neutral
261 ~merge
:merge_compute_deps
262 ~next
:(Bucket.make fnl
)
264 OnTheFlyStore.clear
();
265 errors, failed, to_redecl, to_recheck
267 (*****************************************************************************)
268 (* Code invalidating the heap *)
269 (*****************************************************************************)
271 let invalidate_heap { FileInfo.n_funs
; n_classes
; n_types
; n_consts
} =
272 Typing_env.Funs.oldify_batch n_funs
;
273 Typing_env.Classes.oldify_batch n_classes
;
274 Typing_env.Typedefs.oldify_batch n_types
;
275 Typing_env.GConsts.oldify_batch n_consts
;
276 Naming_heap.FunHeap.remove_batch n_funs
;
277 Naming_heap.ClassStatus.remove_batch n_classes
;
278 Naming_heap.ClassHeap.remove_batch n_classes
;
279 Naming_heap.TypedefStatus.remove_batch n_types
;
280 Naming_heap.TypedefHeap.remove_batch n_types
;
281 Naming_heap.ConstHeap.remove_batch n_consts
;
285 let remove_old_defs { FileInfo.n_funs
; n_classes
; n_types
; n_consts
} =
286 Typing_env.Funs.remove_old_batch n_funs
;
287 Typing_env.Classes.remove_old_batch n_classes
;
288 Typing_env.Typedefs.remove_old_batch n_types
;
289 Typing_env.GConsts.remove_old_batch n_consts
;
294 SMap.fold
begin fun _ names1 names2
->
295 FileInfo.merge_names names1 names2
296 end fast
FileInfo.empty_names
298 (*****************************************************************************)
299 (* The main entry points *)
300 (*****************************************************************************)
302 let init workers fast
=
303 let fnl = SMap.fold
(fun x _ y
-> x
:: y
) fast
[] in
304 let all_classes = Typing_decl_service.get_classes fast
in
307 let redo_type_decl ~update_pos workers
nenv fast
=
308 let all_classes, fnl = init workers fast
in
309 let defs = get_defs fast
in
310 invalidate_heap defs;
311 (* If there aren't enough files, let's do this ourselves ... it's faster! *)
313 if List.length
fnl < 10
315 let errors, failed = otf_decl_files nenv all_classes fast
fnl in
316 let to_redecl, to_recheck = compute_deps ~update_pos
nenv fast
fnl in
317 errors, failed, to_redecl, to_recheck
318 else parallel_otf_decl ~update_pos workers
nenv all_classes fast
fnl
320 remove_old_defs defs;