server.ml 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. open Globals
  2. open Common
  3. open CompilationCache
  4. open Timer
  5. open Type
  6. open DisplayProcessingGlobals
  7. open Ipaddr
  8. open Json
  9. open CompilationContext
  10. open MessageReporting
  11. open HxbData
  12. open TypeloadCacheHook
  13. exception Dirty of module_skip_reason
  14. exception ServerError of string
  15. let has_error ctx =
  16. ctx.has_error || ctx.com.Common.has_error
  17. let check_display_flush ctx f_otherwise = match ctx.com.json_out with
  18. | Some api when not (is_diagnostics ctx.com) ->
  19. if has_error ctx then begin
  20. let errors = List.map (fun cm ->
  21. JObject [
  22. "severity",JInt (MessageSeverity.to_int cm.cm_severity);
  23. "location",Genjson.generate_pos_as_location cm.cm_pos;
  24. "message",JString cm.cm_message;
  25. ]
  26. ) (List.rev ctx.messages) in
  27. api.send_error errors
  28. end
  29. | _ ->
  30. if is_diagnostics ctx.com then begin
  31. List.iter (fun cm ->
  32. add_diagnostics_message ~depth:cm.cm_depth ctx.com cm.cm_message cm.cm_pos cm.cm_kind cm.cm_severity
  33. ) (List.rev ctx.messages);
  34. (match ctx.com.report_mode with
  35. | RMDiagnostics _ -> ()
  36. | RMLegacyDiagnostics _ -> raise (Completion (Diagnostics.print ctx.com))
  37. | _ -> die "" __LOC__)
  38. end else
  39. f_otherwise ()
  40. let current_stdin = ref None
  41. let parse_file cs com (rfile : ClassPaths.resolved_file) p =
  42. let cc = CommonCache.get_cache com in
  43. let file = rfile.file in
  44. let ffile = Path.get_full_path rfile.file
  45. and fkey = com.file_keys#get file in
  46. let is_display_file = DisplayPosition.display_position#is_in_file (com.file_keys#get ffile) in
  47. match is_display_file, !current_stdin with
  48. | true, Some stdin when (com.file_contents <> [] || Common.defined com Define.DisplayStdin) ->
  49. TypeloadParse.parse_file_from_string com file p stdin
  50. | _ ->
  51. let ftime = file_time ffile in
  52. let data = Std.finally (Timer.timer ["server";"parser cache"]) (fun () ->
  53. try
  54. let cfile = cc#find_file fkey in
  55. if cfile.c_time <> ftime then raise Not_found;
  56. Parser.ParseSuccess((cfile.c_package,cfile.c_decls),false,cfile.c_pdi)
  57. with Not_found ->
  58. let parse_result = TypeloadParse.parse_file com rfile p in
  59. let info,is_unusual = match parse_result with
  60. | ParseError(_,_,_) -> "not cached, has parse error",true
  61. | ParseSuccess(data,is_display_file,pdi) ->
  62. if is_display_file then begin
  63. if pdi.pd_errors <> [] then
  64. "not cached, is display file with parse errors",true
  65. else if com.display.dms_per_file then begin
  66. cc#cache_file fkey rfile ftime data pdi;
  67. "cached, is intact display file",true
  68. end else
  69. "not cached, is display file",true
  70. end else begin try
  71. (* We assume that when not in display mode it's okay to cache stuff that has #if display
  72. checks. The reasoning is that non-display mode has more information than display mode. *)
  73. if com.display.dms_full_typing then raise Not_found;
  74. let ident = Hashtbl.find Parser.special_identifier_files fkey in
  75. Printf.sprintf "not cached, using \"%s\" define" ident,true
  76. with Not_found ->
  77. cc#cache_file fkey (ClassPaths.create_resolved_file ffile rfile.class_path) ftime data pdi;
  78. "cached",false
  79. end
  80. in
  81. if is_unusual then ServerMessage.parsed com "" (ffile,info);
  82. parse_result
  83. ) () in
  84. data
  85. open ServerCompilationContext
  86. module Communication = struct
  87. let create_stdio () =
  88. let rec self = {
  89. write_out = (fun s ->
  90. print_string s;
  91. flush stdout;
  92. );
  93. write_err = (fun s ->
  94. prerr_string s;
  95. );
  96. flush = (fun ctx ->
  97. display_messages ctx (fun sev output ->
  98. match sev with
  99. | MessageSeverity.Information -> print_endline output
  100. | Warning | Error | Hint -> prerr_endline output
  101. );
  102. if has_error ctx && !Helper.prompt then begin
  103. print_endline "Press enter to exit...";
  104. ignore(read_line());
  105. end;
  106. flush stdout;
  107. );
  108. exit = (fun code ->
  109. if code = 0 then begin
  110. Timer.close_times();
  111. if !Timer.measure_times then Timer.report_times (fun s -> self.write_err (s ^ "\n"));
  112. end;
  113. exit code;
  114. );
  115. is_server = false;
  116. } in
  117. self
  118. let create_pipe sctx write =
  119. let rec self = {
  120. write_out = (fun s ->
  121. write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit s "\n") ^ "\n")
  122. );
  123. write_err = (fun s ->
  124. write s
  125. );
  126. flush = (fun ctx ->
  127. check_display_flush ctx (fun () ->
  128. display_messages ctx (fun _ output ->
  129. write (output ^ "\n");
  130. ServerMessage.message output;
  131. );
  132. sctx.was_compilation <- ctx.com.display.dms_full_typing;
  133. if has_error ctx then begin
  134. measure_times := false;
  135. write "\x02\n"
  136. end else begin
  137. Timer.close_times();
  138. if !Timer.measure_times then Timer.report_times (fun s -> self.write_err (s ^ "\n"));
  139. end
  140. )
  141. );
  142. exit = (fun i ->
  143. ()
  144. );
  145. is_server = true;
  146. }
  147. in
  148. self
  149. end
  150. let stat dir =
  151. (Unix.stat (Path.remove_trailing_slash dir)).Unix.st_mtime
  152. (* Gets a list of changed directories for the current compilation. *)
  153. let get_changed_directories sctx com =
  154. let t = Timer.timer ["server";"module cache";"changed dirs"] in
  155. let cs = sctx.cs in
  156. let sign = Define.get_signature com.defines in
  157. let dirs = try
  158. (* First, check if we already have determined changed directories for current compilation. *)
  159. Hashtbl.find sctx.changed_directories sign
  160. with Not_found ->
  161. let dirs = try
  162. (* Next, get all directories from the cache and filter the ones that haven't changed. *)
  163. let all_dirs = cs#find_directories sign in
  164. let dirs = List.fold_left (fun acc dir ->
  165. try
  166. let time' = stat dir.c_path in
  167. if dir.c_mtime < time' then begin
  168. dir.c_mtime <- time';
  169. let sub_dirs = Path.find_directories (platform_name com.platform) false [dir.c_path] in
  170. List.iter (fun dir ->
  171. if not (cs#has_directory sign dir) then begin
  172. let time = stat dir in
  173. ServerMessage.added_directory com "" dir;
  174. cs#add_directory sign (CompilationCache.create_directory dir time)
  175. end;
  176. ) sub_dirs;
  177. (CompilationCache.create_directory dir.c_path time') :: acc
  178. end else
  179. acc
  180. with Unix.Unix_error _ ->
  181. cs#remove_directory sign dir.c_path;
  182. ServerMessage.removed_directory com "" dir.c_path;
  183. acc
  184. ) [] all_dirs in
  185. ServerMessage.changed_directories com "" dirs;
  186. dirs
  187. with Not_found ->
  188. (* There were no directories in the cache, so this must be a new context. Let's add
  189. an empty list to make sure no crazy recursion happens. *)
  190. cs#add_directories sign [];
  191. (* Register the delay that is going to populate the cache dirs. *)
  192. sctx.delays <- (fun () ->
  193. let dirs = ref [] in
  194. let add_dir path =
  195. try
  196. let time = stat path in
  197. dirs := CompilationCache.create_directory path time :: !dirs
  198. with Unix.Unix_error _ ->
  199. ()
  200. in
  201. let class_path_strings = com.class_paths#as_string_list in
  202. List.iter add_dir class_path_strings;
  203. List.iter add_dir (Path.find_directories (platform_name com.platform) true class_path_strings);
  204. ServerMessage.found_directories com "" !dirs;
  205. cs#add_directories sign !dirs
  206. ) :: sctx.delays;
  207. (* Returning [] should be fine here because it's a new context, so we won't do any
  208. shadowing checks anyway. *)
  209. []
  210. in
  211. Hashtbl.add sctx.changed_directories sign dirs;
  212. dirs
  213. in
  214. t();
  215. dirs
  216. (* Checks if module [m] can be reused from the cache and returns None in that case. Otherwise, returns
  217. [Some m'] where [m'] is the module responsible for [m] not being reusable. *)
  218. let check_module sctx com m_path m_extra p =
  219. let cc = CommonCache.get_cache com in
  220. let content_changed m_path file =
  221. let fkey = com.file_keys#get file in
  222. try
  223. let cfile = cc#find_file fkey in
  224. (* We must use the module path here because the file path is absolute and would cause
  225. positions in the parsed declarations to differ. *)
  226. let new_data = TypeloadParse.parse_module com m_path p in
  227. cfile.c_decls <> snd new_data
  228. with Not_found ->
  229. true
  230. in
  231. let check_module_shadowing paths m_path m_extra =
  232. List.iter (fun dir ->
  233. let file = (dir.c_path ^ (snd m_path)) ^ ".hx" in
  234. if Sys.file_exists file then begin
  235. let time = file_time file in
  236. if time > m_extra.m_time then begin
  237. ServerMessage.module_path_changed com "" (m_path,m_extra,time,file);
  238. raise (Dirty (Shadowed file))
  239. end
  240. end
  241. ) paths
  242. in
  243. let start_mark = sctx.compilation_step in
  244. let unknown_state_modules = ref [] in
  245. let rec check m_path m_extra =
  246. let check_module_path () =
  247. let directories = get_changed_directories sctx com in
  248. match m_extra.m_kind with
  249. | MFake | MImport -> () (* don't get classpath *)
  250. | MExtern ->
  251. (* if we have a file then this will override our extern type *)
  252. check_module_shadowing directories m_path m_extra;
  253. let rec loop = function
  254. | [] ->
  255. if sctx.verbose then print_endline ("No library file was found for " ^ s_type_path m_path); (* TODO *)
  256. raise (Dirty LibraryChanged)
  257. | (file,load) :: l ->
  258. match load m_path p with
  259. | None ->
  260. loop l
  261. | Some _ ->
  262. if com.file_keys#get file <> (Path.UniqueKey.lazy_key m_extra.m_file) then begin
  263. if sctx.verbose then print_endline ("Library file was changed for " ^ s_type_path m_path); (* TODO *)
  264. raise (Dirty LibraryChanged)
  265. end
  266. in
  267. loop com.load_extern_type
  268. | MCode ->
  269. check_module_shadowing directories m_path m_extra
  270. | MMacro when com.is_macro_context ->
  271. check_module_shadowing directories m_path m_extra
  272. | MMacro ->
  273. begin match com.get_macros() with
  274. | None ->
  275. ()
  276. | Some mcom ->
  277. check_module_shadowing (get_changed_directories sctx mcom) m_path m_extra
  278. end
  279. in
  280. let has_policy policy = List.mem policy m_extra.m_check_policy || match policy with
  281. | NoCheckShadowing | NoCheckFileTimeModification when !ServerConfig.do_not_check_modules && !Parser.display_mode <> DMNone -> true
  282. | _ -> false
  283. in
  284. let check_file () =
  285. let file = Path.UniqueKey.lazy_path m_extra.m_file in
  286. if file_time file <> m_extra.m_time then begin
  287. if has_policy CheckFileContentModification && not (content_changed m_path file) then begin
  288. ServerMessage.unchanged_content com "" file;
  289. end else begin
  290. ServerMessage.not_cached com "" m_path;
  291. if m_extra.m_kind = MFake then Hashtbl.remove com.fake_modules (Path.UniqueKey.lazy_key m_extra.m_file);
  292. raise (Dirty (FileChanged file))
  293. end
  294. end
  295. in
  296. let find_module_extra sign mpath =
  297. (com.cs#get_context sign)#find_module_extra mpath
  298. in
  299. let check_dependencies () =
  300. PMap.iter (fun _ mdep ->
  301. let sign = mdep.md_sign in
  302. let mpath = mdep.md_path in
  303. let m2_extra = try
  304. find_module_extra sign mpath
  305. with Not_found ->
  306. die (Printf.sprintf "Could not find dependency %s of %s in the cache" (s_type_path mpath) (s_type_path m_path)) __LOC__;
  307. in
  308. match check mpath m2_extra with
  309. | None -> ()
  310. | Some reason -> raise (Dirty (DependencyDirty(mpath,reason)))
  311. ) m_extra.m_deps;
  312. in
  313. let check () =
  314. try
  315. if not (has_policy NoCheckShadowing) then check_module_path();
  316. if not (has_policy NoCheckFileTimeModification) || Path.file_extension (Path.UniqueKey.lazy_path m_extra.m_file) <> "hx" then check_file();
  317. if not (has_policy NoCheckDependencies) then check_dependencies();
  318. None
  319. with
  320. | Dirty reason ->
  321. Some reason
  322. in
  323. (* If the module mark matches our compilation mark, we are done *)
  324. if m_extra.m_checked = start_mark then begin match m_extra.m_cache_state with
  325. | MSGood | MSUnknown ->
  326. None
  327. | MSBad reason ->
  328. Some reason
  329. end else begin
  330. (* Otherwise, set to current compilation mark for recursion *)
  331. m_extra.m_checked <- start_mark;
  332. let dirty = match m_extra.m_cache_state with
  333. | MSBad reason ->
  334. (* If we are already dirty, stick to it. *)
  335. Some reason
  336. | MSUnknown ->
  337. (* This should not happen because any MSUnknown module is supposed to have the current m_checked. *)
  338. die "" __LOC__
  339. | MSGood ->
  340. (* Otherwise, run the checks *)
  341. m_extra.m_cache_state <- MSUnknown;
  342. check ()
  343. in
  344. (* Update the module now. It will use this dirty status for the remainder of this compilation. *)
  345. begin match dirty with
  346. | Some reason ->
  347. (* Update the state if we're dirty. *)
  348. m_extra.m_cache_state <- MSBad reason;
  349. | None ->
  350. (* We cannot update if we're clean because at this point it might just be an assumption.
  351. Instead We add the module to a list which is updated at the end of handling this subgraph. *)
  352. unknown_state_modules := m_extra :: !unknown_state_modules;
  353. end;
  354. dirty
  355. end
  356. in
  357. let state = check m_path m_extra in
  358. begin match state with
  359. | None ->
  360. (* If the entire subgraph is clean, we can set all modules to good state *)
  361. List.iter (fun m_extra -> m_extra.m_cache_state <- MSGood) !unknown_state_modules;
  362. | Some _ ->
  363. (* Otherwise, unknown state module may or may not be dirty. We didn't check everything eagerly, so we have
  364. to make sure that the module is checked again if it appears in a different check. This is achieved by
  365. setting m_checked to a lower value and assuming Good state again. *)
  366. List.iter (fun m_extra -> match m_extra.m_cache_state with
  367. | MSUnknown ->
  368. m_extra.m_checked <- start_mark - 1;
  369. m_extra.m_cache_state <- MSGood;
  370. | MSGood | MSBad _ ->
  371. ()
  372. ) !unknown_state_modules
  373. end;
  374. state
  375. class hxb_reader_api_server
  376. (com : Common.context)
  377. (cc : context_cache)
  378. = object(self)
  379. method make_module (path : path) (file : string) =
  380. let mc = cc#get_hxb_module path in
  381. {
  382. m_id = mc.mc_id;
  383. m_path = path;
  384. m_types = [];
  385. m_statics = None;
  386. m_extra = mc.mc_extra
  387. }
  388. method add_module (m : module_def) =
  389. com.module_lut#add m.m_path m
  390. method resolve_type (pack : string list) (mname : string) (tname : string) =
  391. let path = (pack,mname) in
  392. let m = self#resolve_module path in
  393. List.find (fun t -> snd (t_path t) = tname) m.m_types
  394. method resolve_module (path : path) =
  395. match self#find_module path with
  396. | GoodModule m ->
  397. m
  398. | BinaryModule mc ->
  399. let reader = new HxbReader.hxb_reader path com.hxb_reader_stats in
  400. let f_next chunks until =
  401. let t_hxb = Timer.timer ["server";"module cache";"hxb read"] in
  402. let r = reader#read_chunks_until (self :> HxbReaderApi.hxb_reader_api) chunks until in
  403. t_hxb();
  404. r
  405. in
  406. let m,chunks = f_next mc.mc_chunks EOF in
  407. (* We try to avoid reading expressions as much as possible, so we only do this for
  408. our current display file if we're in display mode. *)
  409. let is_display_file = DisplayPosition.display_position#is_in_file (Path.UniqueKey.lazy_key m.m_extra.m_file) in
  410. if is_display_file || com.display.dms_full_typing then ignore(f_next chunks EOM);
  411. m
  412. | BadModule reason ->
  413. die (Printf.sprintf "Unexpected BadModule %s" (s_type_path path)) __LOC__
  414. | NoModule ->
  415. die (Printf.sprintf "Unexpected NoModule %s" (s_type_path path)) __LOC__
  416. method find_module (m_path : path) =
  417. try
  418. GoodModule (com.module_lut#find m_path)
  419. with Not_found -> try
  420. let mc = cc#get_hxb_module m_path in
  421. begin match mc.mc_extra.m_cache_state with
  422. | MSBad reason -> BadModule reason
  423. | _ -> BinaryModule mc
  424. end
  425. with Not_found ->
  426. NoModule
  427. method basic_types =
  428. com.basic
  429. method get_var_id (i : int) =
  430. i
  431. method read_expression_eagerly (cf : tclass_field) =
  432. com.display.dms_full_typing
  433. end
  434. let handle_cache_bound_objects com cbol =
  435. DynArray.iter (function
  436. | Resource(name,data) ->
  437. Hashtbl.replace com.resources name data
  438. | IncludeFile(file,position) ->
  439. com.include_files <- (file,position) :: com.include_files
  440. | Warning(w,msg,p) ->
  441. com.warning w [] msg p
  442. ) cbol
  443. (* Adds module [m] and all its dependencies (recursively) from the cache to the current compilation
  444. context. *)
  445. let rec add_modules sctx com (m : module_def) (from_binary : bool) (p : pos) =
  446. let own_sign = CommonCache.get_cache_sign com in
  447. let rec add_modules tabs m0 m =
  448. if m.m_extra.m_added < com.compilation_step then begin
  449. m.m_extra.m_added <- com.compilation_step;
  450. (match m0.m_extra.m_kind, m.m_extra.m_kind with
  451. | MCode, MMacro | MMacro, MCode ->
  452. (* this was just a dependency to check : do not add to the context *)
  453. handle_cache_bound_objects com m.m_extra.m_cache_bound_objects;
  454. | _ ->
  455. ServerMessage.reusing com tabs m;
  456. List.iter (fun t ->
  457. (t_infos t).mt_restore()
  458. ) m.m_types;
  459. (* The main module gets added when reading hxb already, so let's not add it again. Note that we
  460. can't set its m_added ahead of time because we want the rest of the logic here to run. *)
  461. if not from_binary || m != m then
  462. com.module_lut#add m.m_path m;
  463. handle_cache_bound_objects com m.m_extra.m_cache_bound_objects;
  464. PMap.iter (fun _ mdep ->
  465. let mpath = mdep.md_path in
  466. if mdep.md_sign = own_sign then begin
  467. let m2 = try
  468. com.module_lut#find mpath
  469. with Not_found ->
  470. match type_module sctx com mpath p with
  471. | GoodModule m ->
  472. m
  473. | BinaryModule mc ->
  474. failwith (Printf.sprintf "Unexpectedly found unresolved binary module %s as a dependency of %s" (s_type_path mpath) (s_type_path m0.m_path))
  475. | NoModule ->
  476. failwith (Printf.sprintf "Unexpectedly could not find module %s as a dependency of %s" (s_type_path mpath) (s_type_path m0.m_path))
  477. | BadModule reason ->
  478. failwith (Printf.sprintf "Unexpected bad module %s (%s) as a dependency of %s" (s_type_path mpath) (Printer.s_module_skip_reason reason) (s_type_path m0.m_path))
  479. in
  480. add_modules (tabs ^ " ") m0 m2
  481. end
  482. ) m.m_extra.m_deps
  483. )
  484. end
  485. in
  486. add_modules "" m m
  487. (* Looks up the module referred to by [mpath] in the cache. If it exists, a check is made to
  488. determine if it's still valid. If this function returns None, the module is re-typed. *)
  489. and type_module sctx com mpath p =
  490. let t = Timer.timer ["server";"module cache"] in
  491. let cc = CommonCache.get_cache com in
  492. let skip m_path reason =
  493. ServerMessage.skipping_dep com "" (m_path,(Printer.s_module_skip_reason reason));
  494. BadModule reason
  495. in
  496. let add_modules from_binary m =
  497. let tadd = Timer.timer ["server";"module cache";"add modules"] in
  498. add_modules sctx com m from_binary p;
  499. tadd();
  500. GoodModule m
  501. in
  502. let check_module sctx m_path m_extra p =
  503. let tcheck = Timer.timer ["server";"module cache";"check"] in
  504. let r = check_module sctx com mpath m_extra p in
  505. tcheck();
  506. r
  507. in
  508. let find_module_in_cache cc m_path p =
  509. try
  510. let m = cc#find_module m_path in
  511. begin match m.m_extra.m_cache_state with
  512. | MSBad reason -> BadModule reason
  513. | _ -> GoodModule m
  514. end;
  515. with Not_found -> try
  516. let mc = cc#get_hxb_module m_path in
  517. begin match mc.mc_extra.m_cache_state with
  518. | MSBad reason -> BadModule reason
  519. | _ -> BinaryModule mc
  520. end
  521. with Not_found ->
  522. NoModule
  523. in
  524. (* Should not raise anything! *)
  525. let m = match find_module_in_cache cc mpath p with
  526. | GoodModule m ->
  527. (* "Good" here is an assumption, it only means that the module wasn't explicitly invalidated
  528. in the cache. The true cache state will be known after check_module. *)
  529. begin match check_module sctx mpath m.m_extra p with
  530. | None ->
  531. add_modules false m;
  532. | Some reason ->
  533. skip m.m_path reason
  534. end
  535. | BinaryModule mc ->
  536. (* Similarly, we only know that a binary module wasn't explicitly tainted. Decode it only after
  537. checking dependencies. This means that the actual decoding never has any reason to fail. *)
  538. begin match check_module sctx mpath mc.mc_extra p with
  539. | None ->
  540. let reader = new HxbReader.hxb_reader mpath com.hxb_reader_stats in
  541. let api = (new hxb_reader_api_server com cc :> HxbReaderApi.hxb_reader_api) in
  542. let f_next chunks until =
  543. let t_hxb = Timer.timer ["server";"module cache";"hxb read"] in
  544. let r = reader#read_chunks_until api chunks until in
  545. t_hxb();
  546. r
  547. in
  548. let m,chunks = f_next mc.mc_chunks EOF in
  549. (* We try to avoid reading expressions as much as possible, so we only do this for
  550. our current display file if we're in display mode. *)
  551. let is_display_file = DisplayPosition.display_position#is_in_file (Path.UniqueKey.lazy_key m.m_extra.m_file) in
  552. if is_display_file || com.display.dms_full_typing then ignore(f_next chunks EOM);
  553. add_modules true m;
  554. | Some reason ->
  555. skip mpath reason
  556. end
  557. | BadModule reason ->
  558. (* A BadModule state here means that the module is already invalidated in the cache, e.g. from server/invalidate. *)
  559. skip mpath reason
  560. | NoModule as mr ->
  561. mr
  562. in
  563. t();
  564. m
  565. let before_anything sctx ctx =
  566. ensure_macro_setup sctx
  567. let after_target_init sctx ctx =
  568. let com = ctx.com in
  569. let cs = sctx.cs in
  570. let sign = Define.get_signature com.defines in
  571. ServerMessage.defines com "";
  572. ServerMessage.signature com "" sign;
  573. ServerMessage.display_position com "" (DisplayPosition.display_position#get);
  574. let class_path_strings = com.class_paths#as_string_list in
  575. try
  576. if (Hashtbl.find sctx.class_paths sign) <> class_path_strings then begin
  577. ServerMessage.class_paths_changed com "";
  578. Hashtbl.replace sctx.class_paths sign class_path_strings;
  579. cs#clear_directories sign;
  580. (cs#get_context sign)#set_initialized false;
  581. end;
  582. with Not_found ->
  583. Hashtbl.add sctx.class_paths sign class_path_strings;
  584. ()
  585. let after_save sctx ctx =
  586. if ctx.comm.is_server && not (has_error ctx) then
  587. maybe_cache_context sctx ctx.com
  588. let after_compilation sctx ctx =
  589. ()
  590. let mk_length_prefixed_communication allow_nonblock chin chout =
  591. let sin = Unix.descr_of_in_channel chin in
  592. let chin = IO.input_channel chin in
  593. let chout = IO.output_channel chout in
  594. let bout = Buffer.create 0 in
  595. let block () = Unix.clear_nonblock sin in
  596. let unblock () = Unix.set_nonblock sin in
  597. let read_nonblock _ =
  598. let len = IO.read_i32 chin in
  599. Some (IO.really_nread_string chin len)
  600. in
  601. let read = if allow_nonblock then fun do_block ->
  602. if do_block then begin
  603. block();
  604. read_nonblock true;
  605. end else begin
  606. let c0 =
  607. unblock();
  608. try
  609. Some (IO.read_byte chin)
  610. with
  611. | Sys_blocked_io
  612. (* TODO: We're supposed to catch Sys_blocked_io only, but that doesn't work on my PC... *)
  613. | Sys_error _ ->
  614. None
  615. in
  616. begin match c0 with
  617. | Some c0 ->
  618. block(); (* We got something, make sure we block until we're done. *)
  619. let c1 = IO.read_byte chin in
  620. let c2 = IO.read_byte chin in
  621. let c3 = IO.read_byte chin in
  622. let len = c3 lsl 24 + c2 lsl 16 + c1 lsl 8 + c0 in
  623. Some (IO.really_nread_string chin len)
  624. | None ->
  625. None
  626. end
  627. end
  628. else read_nonblock in
  629. let write = Buffer.add_string bout in
  630. let close = fun() ->
  631. flush stdout;
  632. IO.write_i32 chout (Buffer.length bout);
  633. IO.nwrite_string chout (Buffer.contents bout);
  634. IO.flush chout
  635. in
  636. fun () ->
  637. Buffer.clear bout;
  638. allow_nonblock, read, write, close
  639. let ssend sock str =
  640. let rec loop pos len =
  641. if len = 0 then
  642. ()
  643. else
  644. let s = Unix.send sock str pos len [] in
  645. loop (pos + s) (len - s)
  646. in
  647. loop 0 (Bytes.length str)
  648. (* The accept-function to wait for a stdio connection. *)
  649. let init_wait_stdio() =
  650. set_binary_mode_in stdin true;
  651. set_binary_mode_out stderr true;
  652. mk_length_prefixed_communication false stdin stderr
  653. (* The connect function to connect to [host] at [port] and send arguments [args]. *)
  654. let do_connect ip port args =
  655. let (domain, host) = match ip with
  656. | V4 ip -> (Unix.PF_INET, V4.to_string ip)
  657. | V6 ip -> (Unix.PF_INET6, V6.to_string ip)
  658. in
  659. let sock = Unix.socket domain Unix.SOCK_STREAM 0 in
  660. (try Unix.connect sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with
  661. | Unix.Unix_error(code,_,_) -> failwith("Couldn't connect on " ^ host ^ ":" ^ string_of_int port ^ " (" ^ (Unix.error_message code) ^ ")");
  662. | _ -> failwith ("Couldn't connect on " ^ host ^ ":" ^ string_of_int port)
  663. );
  664. let rec display_stdin args =
  665. match args with
  666. | [] -> ""
  667. | "-D" :: ("display_stdin" | "display-stdin") :: _ ->
  668. let accept = init_wait_stdio() in
  669. let _, read, _, _ = accept() in
  670. Option.default "" (read true)
  671. | _ :: args ->
  672. display_stdin args
  673. in
  674. let args = ("--cwd " ^ Unix.getcwd()) :: args in
  675. let s = (String.concat "" (List.map (fun a -> a ^ "\n") args)) ^ (display_stdin args) in
  676. ssend sock (Bytes.of_string (s ^ "\000"));
  677. let has_error = ref false in
  678. let print line =
  679. match (if line = "" then '\x00' else line.[0]) with
  680. | '\x01' ->
  681. print_string (String.concat "\n" (List.tl (ExtString.String.nsplit line "\x01")));
  682. flush stdout
  683. | '\x02' ->
  684. has_error := true;
  685. | _ ->
  686. prerr_endline line;
  687. in
  688. let buf = Buffer.create 0 in
  689. let process() =
  690. let lines = ExtString.String.nsplit (Buffer.contents buf) "\n" in
  691. (* the last line ends with \n *)
  692. let lines = (match List.rev lines with "" :: l -> List.rev l | _ -> lines) in
  693. List.iter print lines;
  694. in
  695. let tmp = Bytes.create 1024 in
  696. let rec loop() =
  697. let b = Unix.recv sock tmp 0 1024 [] in
  698. Buffer.add_subbytes buf tmp 0 b;
  699. if b > 0 then begin
  700. if Bytes.get tmp (b - 1) = '\n' then begin
  701. process();
  702. Buffer.reset buf;
  703. end;
  704. loop();
  705. end
  706. in
  707. loop();
  708. process();
  709. if !has_error then exit 1
  710. let enable_cache_mode sctx =
  711. type_module_hook := type_module sctx;
  712. ServerCompilationContext.ensure_macro_setup sctx;
  713. TypeloadParse.parse_hook := parse_file sctx.cs
  714. let rec process sctx comm args =
  715. let t0 = get_time() in
  716. ServerMessage.arguments args;
  717. reset sctx;
  718. let api = {
  719. on_context_create = (fun () ->
  720. sctx.compilation_step <- sctx.compilation_step + 1;
  721. sctx.compilation_step;
  722. );
  723. cache = sctx.cs;
  724. callbacks = {
  725. before_anything = before_anything sctx;
  726. after_target_init = after_target_init sctx;
  727. after_save = after_save sctx;
  728. after_compilation = after_compilation sctx;
  729. };
  730. init_wait_socket = init_wait_socket;
  731. init_wait_connect = init_wait_connect;
  732. init_wait_stdio = init_wait_stdio;
  733. wait_loop = wait_loop;
  734. do_connect = do_connect;
  735. } in
  736. Compiler.HighLevel.entry api comm args;
  737. run_delays sctx;
  738. ServerMessage.stats stats (get_time() -. t0)
  739. (* The server main loop. Waits for the [accept] call to then process the sent compilation
  740. parameters through [process_params]. *)
  741. and wait_loop verbose accept =
  742. if verbose then ServerMessage.enable_all ();
  743. Sys.catch_break false; (* Sys can never catch a break *)
  744. (* Create server context and set up hooks for parsing and typing *)
  745. let sctx = ServerCompilationContext.create verbose in
  746. let cs = sctx.cs in
  747. enable_cache_mode sctx;
  748. let ring = Ring.create 10 0. in
  749. let gc_heap_stats () =
  750. let stats = Gc.quick_stat() in
  751. stats.major_words,stats.heap_words
  752. in
  753. let heap_stats_start = ref (gc_heap_stats()) in
  754. let update_heap () =
  755. (* On every compilation: Track how many words were allocated for this compilation (working memory). *)
  756. let heap_stats_now = gc_heap_stats() in
  757. let words_allocated = (fst heap_stats_now) -. (fst !heap_stats_start) in
  758. let heap_size = float_of_int (snd heap_stats_now) in
  759. Ring.push ring words_allocated;
  760. if Ring.is_filled ring then begin
  761. Ring.reset_filled ring;
  762. (* Maximum working memory for the last X compilations. *)
  763. let max = Ring.fold ring 0. (fun m i -> if i > m then i else m) in
  764. cs#add_task (new Tasks.gc_task max heap_size)
  765. end;
  766. heap_stats_start := heap_stats_now;
  767. in
  768. (* Main loop: accept connections and process arguments *)
  769. while true do
  770. let support_nonblock, read, write, close = accept() in
  771. begin try
  772. (* Read arguments *)
  773. let rec loop block =
  774. match read block with
  775. | Some s ->
  776. let hxml =
  777. try
  778. let idx = String.index s '\001' in
  779. current_stdin := Some (String.sub s (idx + 1) ((String.length s) - idx - 1));
  780. (String.sub s 0 idx)
  781. with Not_found ->
  782. s
  783. in
  784. let data = Helper.parse_hxml_data hxml in
  785. process sctx (Communication.create_pipe sctx write) data
  786. | None ->
  787. if not cs#has_task then
  788. (* If there is no pending task, turn into blocking mode. *)
  789. loop true
  790. else begin
  791. (* Otherwise run the task and loop to check if there are more or if there's a request now. *)
  792. cs#get_task#run;
  793. loop false
  794. end;
  795. in
  796. loop (not support_nonblock)
  797. with Unix.Unix_error _ ->
  798. ServerMessage.socket_message "Connection Aborted"
  799. | e ->
  800. let estr = Printexc.to_string e in
  801. ServerMessage.uncaught_error estr;
  802. (try write ("\x02\n" ^ estr); with _ -> ());
  803. if Helper.is_debug_run then print_endline (estr ^ "\n" ^ Printexc.get_backtrace());
  804. if e = Out_of_memory then begin
  805. close();
  806. exit (-1);
  807. end;
  808. end;
  809. (* Close connection and perform some cleanup *)
  810. close();
  811. current_stdin := None;
  812. cleanup();
  813. update_heap();
  814. (* If our connection always blocks, we have to execute all pending tasks now. *)
  815. if not support_nonblock then
  816. while cs#has_task do cs#get_task#run done
  817. else if sctx.was_compilation then
  818. cs#add_task (new Tasks.server_exploration_task cs)
  819. done;
  820. 0
  821. (* Connect to given host/port and return accept function for communication *)
  822. and init_wait_connect ip port =
  823. let host = match ip with
  824. | V4 ip -> V4.to_string ip
  825. | V6 ip -> V6.to_string ip
  826. in
  827. let host = Unix.inet_addr_of_string host in
  828. let chin, chout = Unix.open_connection (Unix.ADDR_INET (host,port)) in
  829. mk_length_prefixed_communication true chin chout
  830. (* The accept-function to wait for a socket connection. *)
  831. and init_wait_socket ip port =
  832. let (domain, host) = match ip with
  833. | V4 ip -> (Unix.PF_INET, V4.to_string ip)
  834. | V6 ip -> (Unix.PF_INET6, V6.to_string ip)
  835. in
  836. let sock = Unix.socket domain Unix.SOCK_STREAM 0 in
  837. (try Unix.setsockopt sock Unix.SO_REUSEADDR true with _ -> ());
  838. (try Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't wait on " ^ host ^ ":" ^ string_of_int port));
  839. ServerMessage.socket_message ("Waiting on " ^ host ^ ":" ^ string_of_int port);
  840. Unix.listen sock 10;
  841. let bufsize = 1024 in
  842. let tmp = Bytes.create bufsize in
  843. let accept() = (
  844. let sin, _ = Unix.accept sock in
  845. Unix.set_nonblock sin;
  846. ServerMessage.socket_message "Client connected";
  847. let b = Buffer.create 0 in
  848. let rec read_loop count =
  849. try
  850. let r = Unix.recv sin tmp 0 bufsize [] in
  851. if r = 0 then
  852. failwith "Incomplete request"
  853. else begin
  854. ServerMessage.socket_message (Printf.sprintf "Reading %d bytes\n" r);
  855. Buffer.add_subbytes b tmp 0 r;
  856. if Bytes.get tmp (r-1) = '\000' then
  857. Buffer.sub b 0 (Buffer.length b - 1)
  858. else
  859. read_loop 0
  860. end
  861. with Unix.Unix_error((Unix.EWOULDBLOCK|Unix.EAGAIN),_,_) ->
  862. if count = 100 then
  863. failwith "Aborting inactive connection"
  864. else begin
  865. ServerMessage.socket_message "Waiting for data...";
  866. ignore(Unix.select [] [] [] 0.05); (* wait a bit *)
  867. read_loop (count + 1);
  868. end
  869. in
  870. let read = fun _ -> (let s = read_loop 0 in Unix.clear_nonblock sin; Some s) in
  871. let closed = ref false in
  872. let close() =
  873. if not !closed then begin
  874. try Unix.close sin with Unix.Unix_error _ -> trace "Error while closing socket.";
  875. closed := true;
  876. end
  877. in
  878. let write s =
  879. if not !closed then
  880. match Unix.getsockopt_error sin with
  881. | Some _ -> close()
  882. | None -> ssend sin (Bytes.unsafe_of_string s);
  883. in
  884. false, read, write, close
  885. ) in
  886. accept