displayOutput.ml 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. open Globals
  2. open Common
  3. open Timer
  4. open Common.DisplayMode
  5. open Type
  6. open Display
  7. open Typecore
  8. (* Old XML stuff *)
  9. let htmlescape s =
  10. let s = String.concat "&" (ExtString.String.nsplit s "&") in
  11. let s = String.concat "&lt;" (ExtString.String.nsplit s "<") in
  12. let s = String.concat "&gt;" (ExtString.String.nsplit s ">") in
  13. let s = String.concat "&quot;" (ExtString.String.nsplit s "\"") in
  14. s
  15. let get_timer_fields start_time =
  16. let tot = ref 0. in
  17. Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Timer.htimers;
  18. let fields = [("@TOTAL", Printf.sprintf "%.3fs" (get_time() -. start_time))] in
  19. if !tot > 0. then
  20. Hashtbl.fold (fun _ t acc ->
  21. ((String.concat "." t.id),(Printf.sprintf "%.3fs (%.0f%%)" t.total (t.total *. 100. /. !tot))) :: acc
  22. ) Timer.htimers fields
  23. else
  24. fields
  25. let print_keywords () =
  26. let b = Buffer.create 0 in
  27. Buffer.add_string b "<list>\n";
  28. Hashtbl.iter (fun k _ ->
  29. Buffer.add_string b (Printf.sprintf "<i n=\"%s\"></i>\n" k)
  30. ) Lexer.keywords;
  31. Buffer.add_string b "</list>\n";
  32. Buffer.contents b
  33. let print_fields fields =
  34. let b = Buffer.create 0 in
  35. Buffer.add_string b "<list>\n";
  36. List.iter (fun (n,k,d) ->
  37. let s_kind, t = match k with
  38. | FKVar t -> "var", s_type (print_context()) t
  39. | FKMethod t -> "method", s_type (print_context()) t
  40. | FKType t -> "type", s_type (print_context()) t
  41. | FKPackage -> "package", ""
  42. | FKModule -> "type", ""
  43. | FKMetadata -> "metadata", ""
  44. | FKTimer s -> "timer", s
  45. in
  46. Buffer.add_string b (Printf.sprintf "<i n=\"%s\" k=\"%s\"><t>%s</t><d>%s</d></i>\n" n s_kind (htmlescape t) (htmlescape d))
  47. ) (List.sort (fun (a,ak,_) (b,bk,_) -> compare (display_field_kind_index ak,a) (display_field_kind_index bk,b)) fields);
  48. Buffer.add_string b "</list>\n";
  49. Buffer.contents b
  50. let maybe_print_doc d =
  51. Option.map_default (fun s -> Printf.sprintf " d=\"%s\"" (htmlescape s)) "" d
  52. let print_toplevel il =
  53. let b = Buffer.create 0 in
  54. Buffer.add_string b "<il>\n";
  55. let s_type t = htmlescape (s_type (print_context()) t) in
  56. let s_doc d = maybe_print_doc d in
  57. let identifiers = Hashtbl.create 0 in
  58. let check_ident s =
  59. if Hashtbl.mem identifiers s then false
  60. else begin
  61. Hashtbl.add identifiers s true;
  62. true
  63. end
  64. in
  65. List.iter (fun id -> match id with
  66. | IdentifierType.ITLocal v ->
  67. if check_ident v.v_name then Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
  68. | IdentifierType.ITMember cf ->
  69. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  70. | IdentifierType.ITStatic cf ->
  71. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  72. | IdentifierType.ITEnum(en,ef) ->
  73. if check_ident ef.ef_name then Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\"%s>%s</i>\n" (s_type ef.ef_type) (s_doc ef.ef_doc) ef.ef_name);
  74. | IdentifierType.ITEnumAbstract(a,cf) ->
  75. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"enumabstract\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  76. | IdentifierType.ITGlobal(mt,s,t) ->
  77. if check_ident s then Buffer.add_string b (Printf.sprintf "<i k=\"global\" p=\"%s\" t=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (s_type t) s);
  78. | IdentifierType.ITType(mt) ->
  79. let infos = t_infos mt in
  80. if check_ident (snd infos.mt_path) then Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path infos.mt_path) (s_doc infos.mt_doc) (snd infos.mt_path));
  81. | IdentifierType.ITPackage s ->
  82. Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s</i>\n" s)
  83. | IdentifierType.ITLiteral s ->
  84. Buffer.add_string b (Printf.sprintf "<i k=\"literal\">%s</i>\n" s)
  85. | IdentifierType.ITTimer s ->
  86. Buffer.add_string b (Printf.sprintf "<i k=\"timer\">%s</i>\n" s)
  87. ) il;
  88. Buffer.add_string b "</il>";
  89. Buffer.contents b
  90. let print_type t p doc =
  91. let b = Buffer.create 0 in
  92. if p = null_pos then
  93. Buffer.add_string b "<type"
  94. else begin
  95. let error_printer file line = Printf.sprintf "%s:%d:" (Path.unique_full_path file) line in
  96. let epos = Lexer.get_error_pos error_printer p in
  97. Buffer.add_string b ("<type p=\"" ^ (htmlescape epos) ^ "\"")
  98. end;
  99. Buffer.add_string b (maybe_print_doc doc);
  100. Buffer.add_string b ">\n";
  101. Buffer.add_string b (htmlescape (s_type (print_context()) t));
  102. Buffer.add_string b "\n</type>\n";
  103. Buffer.contents b
  104. let print_signatures tl =
  105. let b = Buffer.create 0 in
  106. List.iter (fun ((args,ret),doc) ->
  107. Buffer.add_string b "<type";
  108. Option.may (fun s -> Buffer.add_string b (Printf.sprintf " d=\"%s\"" (htmlescape s))) doc;
  109. Buffer.add_string b ">\n";
  110. Buffer.add_string b (htmlescape (s_type (print_context()) (TFun(args,ret))));
  111. Buffer.add_string b "\n</type>\n";
  112. ) tl;
  113. Buffer.contents b
  114. let print_positions pl =
  115. let b = Buffer.create 0 in
  116. let error_printer file line = Printf.sprintf "%s:%d:" (Path.get_real_path file) line in
  117. Buffer.add_string b "<list>\n";
  118. List.iter (fun p ->
  119. let epos = Lexer.get_error_pos error_printer p in
  120. Buffer.add_string b "<pos>";
  121. Buffer.add_string b epos;
  122. Buffer.add_string b "</pos>\n";
  123. ) pl;
  124. Buffer.add_string b "</list>";
  125. Buffer.contents b
  126. let display_memory com =
  127. let verbose = com.verbose in
  128. let print = print_endline in
  129. let fmt_size sz =
  130. if sz < 1024 then
  131. string_of_int sz ^ " B"
  132. else if sz < 1024*1024 then
  133. string_of_int (sz asr 10) ^ " KB"
  134. else
  135. Printf.sprintf "%.1f MB" ((float_of_int sz) /. (1024.*.1024.))
  136. in
  137. let size v =
  138. fmt_size (mem_size v)
  139. in
  140. Gc.full_major();
  141. Gc.compact();
  142. let mem = Gc.stat() in
  143. print ("Total Allocated Memory " ^ fmt_size (mem.Gc.heap_words * (Sys.word_size asr 8)));
  144. print ("Free Memory " ^ fmt_size (mem.Gc.free_words * (Sys.word_size asr 8)));
  145. (match CompilationServer.get() with
  146. | None ->
  147. print "No cache found";
  148. | Some {CompilationServer.cache = c} ->
  149. print ("Total cache size " ^ size c);
  150. print (" haxelib " ^ size c.CompilationServer.c_haxelib);
  151. print (" parsed ast " ^ size c.CompilationServer.c_files ^ " (" ^ string_of_int (Hashtbl.length c.CompilationServer.c_files) ^ " files stored)");
  152. print (" typed modules " ^ size c.CompilationServer.c_modules ^ " (" ^ string_of_int (Hashtbl.length c.CompilationServer.c_modules) ^ " modules stored)");
  153. let rec scan_module_deps m h =
  154. if Hashtbl.mem h m.m_id then
  155. ()
  156. else begin
  157. Hashtbl.add h m.m_id m;
  158. PMap.iter (fun _ m -> scan_module_deps m h) m.m_extra.m_deps
  159. end
  160. in
  161. let all_modules = Hashtbl.fold (fun _ m acc -> PMap.add m.m_id m acc) c.CompilationServer.c_modules PMap.empty in
  162. let modules = Hashtbl.fold (fun (path,key) m acc ->
  163. let mdeps = Hashtbl.create 0 in
  164. scan_module_deps m mdeps;
  165. let deps = ref [Obj.repr null_module] in
  166. let out = ref all_modules in
  167. Hashtbl.iter (fun _ md ->
  168. out := PMap.remove md.m_id !out;
  169. if m == md then () else begin
  170. deps := Obj.repr md :: !deps;
  171. List.iter (fun t ->
  172. match t with
  173. | TClassDecl c ->
  174. deps := Obj.repr c :: !deps;
  175. c.cl_descendants <- []; (* prevent false positive *)
  176. List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_statics;
  177. List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_fields;
  178. | TEnumDecl e ->
  179. deps := Obj.repr e :: !deps;
  180. List.iter (fun n -> deps := Obj.repr (PMap.find n e.e_constrs) :: !deps) e.e_names;
  181. | TTypeDecl t -> deps := Obj.repr t :: !deps;
  182. | TAbstractDecl a -> deps := Obj.repr a :: !deps;
  183. ) md.m_types;
  184. end
  185. ) mdeps;
  186. let chk = Obj.repr Common.memory_marker :: PMap.fold (fun m acc -> Obj.repr m :: acc) !out [] in
  187. let inf = Objsize.objsize m !deps chk in
  188. (m,Objsize.size_with_headers inf, (inf.Objsize.reached,!deps,!out)) :: acc
  189. ) c.CompilationServer.c_modules [] in
  190. let cur_key = ref "" and tcount = ref 0 and mcount = ref 0 in
  191. List.iter (fun (m,size,(reached,deps,out)) ->
  192. let key = m.m_extra.m_sign in
  193. if key <> !cur_key then begin
  194. print (Printf.sprintf (" --- CONFIG %s ----------------------------") (Digest.to_hex key));
  195. cur_key := key;
  196. end;
  197. let sign md =
  198. if md.m_extra.m_sign = key then "" else "(" ^ (try Digest.to_hex md.m_extra.m_sign with _ -> "???" ^ md.m_extra.m_sign) ^ ")"
  199. in
  200. print (Printf.sprintf " %s : %s" (s_type_path m.m_path) (fmt_size size));
  201. (if reached then try
  202. incr mcount;
  203. let lcount = ref 0 in
  204. let leak l =
  205. incr lcount;
  206. incr tcount;
  207. print (Printf.sprintf " LEAK %s" l);
  208. if !lcount >= 3 && !tcount >= 100 && not verbose then begin
  209. print (Printf.sprintf " ...");
  210. raise Exit;
  211. end;
  212. in
  213. if (Objsize.objsize m deps [Obj.repr Common.memory_marker]).Objsize.reached then leak "common";
  214. PMap.iter (fun _ md ->
  215. if (Objsize.objsize m deps [Obj.repr md]).Objsize.reached then leak (s_type_path md.m_path ^ sign md);
  216. ) out;
  217. with Exit ->
  218. ());
  219. if verbose then begin
  220. print (Printf.sprintf " %d total deps" (List.length deps));
  221. PMap.iter (fun _ md ->
  222. print (Printf.sprintf " dep %s%s" (s_type_path md.m_path) (sign md));
  223. ) m.m_extra.m_deps;
  224. end;
  225. flush stdout
  226. ) (List.sort (fun (m1,s1,_) (m2,s2,_) ->
  227. let k1 = m1.m_extra.m_sign and k2 = m2.m_extra.m_sign in
  228. if k1 = k2 then s1 - s2 else if k1 > k2 then 1 else -1
  229. ) modules);
  230. if !mcount > 0 then print ("*** " ^ string_of_int !mcount ^ " modules have leaks !");
  231. print "Cache dump complete")
  232. module TypePathHandler = struct
  233. let unique l =
  234. let rec _unique = function
  235. | [] -> []
  236. | x1 :: x2 :: l when x1 = x2 -> _unique (x2 :: l)
  237. | x :: l -> x :: _unique l
  238. in
  239. _unique (List.sort compare l)
  240. let rec read_type_path com p =
  241. let classes = ref [] in
  242. let packages = ref [] in
  243. let p = (match p with
  244. | x :: l ->
  245. (try
  246. match PMap.find x com.package_rules with
  247. | Directory d -> d :: l
  248. | Remap s -> s :: l
  249. | _ -> p
  250. with
  251. Not_found -> p)
  252. | _ -> p
  253. ) in
  254. List.iter (fun path ->
  255. let dir = path ^ String.concat "/" p in
  256. let r = (try Sys.readdir dir with _ -> [||]) in
  257. Array.iter (fun f ->
  258. if (try (Unix.stat (dir ^ "/" ^ f)).Unix.st_kind = Unix.S_DIR with _ -> false) then begin
  259. if f.[0] >= 'a' && f.[0] <= 'z' then begin
  260. if p = ["."] then
  261. match read_type_path com [f] with
  262. | [] , [] -> ()
  263. | _ ->
  264. try
  265. match PMap.find f com.package_rules with
  266. | Forbidden -> ()
  267. | Remap f -> packages := f :: !packages
  268. | Directory _ -> raise Not_found
  269. with Not_found ->
  270. packages := f :: !packages
  271. else
  272. packages := f :: !packages
  273. end;
  274. end else if file_extension f = "hx" then begin
  275. let c = Filename.chop_extension f in
  276. try
  277. ignore(String.index c '.')
  278. with Not_found ->
  279. if String.length c < 2 || String.sub c (String.length c - 2) 2 <> "__" then classes := c :: !classes;
  280. end;
  281. ) r;
  282. ) com.class_path;
  283. List.iter (fun (_,_,extract) ->
  284. Hashtbl.iter (fun (path,name) _ ->
  285. if path = p then classes := name :: !classes else
  286. let rec loop p1 p2 =
  287. match p1, p2 with
  288. | [], _ -> ()
  289. | x :: _, [] -> packages := x :: !packages
  290. | a :: p1, b :: p2 -> if a = b then loop p1 p2
  291. in
  292. loop path p
  293. ) (extract());
  294. ) com.swf_libs;
  295. List.iter (fun (path,std,close,all_files,lookup) ->
  296. List.iter (fun (path, name) ->
  297. if path = p then classes := name :: !classes else
  298. let rec loop p1 p2 =
  299. match p1, p2 with
  300. | [], _ -> ()
  301. | x :: _, [] -> packages := x :: !packages
  302. | a :: p1, b :: p2 -> if a = b then loop p1 p2
  303. in
  304. loop path p
  305. ) (all_files())
  306. ) com.java_libs;
  307. List.iter (fun (path,std,all_files,lookup) ->
  308. List.iter (fun (path, name) ->
  309. if path = p then classes := name :: !classes else
  310. let rec loop p1 p2 =
  311. match p1, p2 with
  312. | [], _ -> ()
  313. | x :: _, [] -> packages := x :: !packages
  314. | a :: p1, b :: p2 -> if a = b then loop p1 p2
  315. in
  316. loop path p
  317. ) (all_files())
  318. ) com.net_libs;
  319. unique !packages, unique !classes
  320. (** raise field completion listing packages and modules in a given package *)
  321. let complete_type_path com p =
  322. let packs, modules = read_type_path com p in
  323. if packs = [] && modules = [] then
  324. (abort ("No classes found in " ^ String.concat "." p) null_pos)
  325. else
  326. let packs = List.map (fun n -> n,Display.FKPackage,"") packs in
  327. let modules = List.map (fun n -> n,Display.FKModule,"") modules in
  328. Some (packs @ modules)
  329. (** raise field completion listing module sub-types and static fields *)
  330. let complete_type_path_inner com p c cur_package is_import =
  331. try
  332. let sl_pack,s_module = match List.rev p with
  333. | s :: sl when s.[0] >= 'A' && s.[0] <= 'Z' -> List.rev sl,s
  334. | _ -> p,c
  335. in
  336. let ctx = Typer.create com in
  337. let rec lookup p =
  338. try
  339. Typeload.load_module ctx (p,s_module) null_pos
  340. with e ->
  341. if cur_package then
  342. match List.rev p with
  343. | [] -> raise e
  344. | _ :: p -> lookup (List.rev p)
  345. else
  346. raise e
  347. in
  348. let m = lookup sl_pack in
  349. let statics = ref None in
  350. let public_types = List.filter (fun t ->
  351. let tinfos = t_infos t in
  352. let is_module_type = snd tinfos.mt_path = c in
  353. if is_import && is_module_type then begin match t with
  354. | TClassDecl c ->
  355. ignore(c.cl_build());
  356. statics := Some c.cl_ordered_statics
  357. | _ -> ()
  358. end;
  359. not tinfos.mt_private
  360. ) m.m_types in
  361. let types =
  362. if c <> s_module then
  363. []
  364. else
  365. List.map (fun t ->
  366. let infos = t_infos t in
  367. (snd infos.mt_path), Display.FKModule, (Option.default "" infos.mt_doc)
  368. ) public_types
  369. in
  370. let make_field_doc cf =
  371. cf.cf_name,
  372. (match cf.cf_kind with Method _ -> Display.FKMethod cf.cf_type | Var _ -> Display.FKVar cf.cf_type),
  373. (match cf.cf_doc with Some s -> s | None -> "")
  374. in
  375. let fields = match !statics with
  376. | None -> types
  377. | Some cfl -> types @ (List.map make_field_doc (List.filter (fun cf -> cf.cf_public) cfl))
  378. in
  379. Some fields
  380. with _ ->
  381. abort ("Could not load module " ^ (s_type_path (p,c))) null_pos
  382. end
  383. (* New JSON stuff *)
  384. open Json
  385. (** return a range JSON structure for given position
  386. positions are 0-based and the result object looks like this:
  387. {
  388. start: {line: 0, character: 0},
  389. end: {line: 3, character: 42},
  390. }
  391. *)
  392. let pos_to_json_range p =
  393. if p.pmin = -1 then
  394. JNull
  395. else
  396. let l1, p1, l2, p2 = Lexer.get_pos_coords p in
  397. let to_json l c = JObject [("line", JInt (l - 1)); ("character", JInt (c - 1))] in
  398. JObject [
  399. ("start", to_json l1 p1);
  400. ("end", to_json l2 p2);
  401. ]
  402. let print_signature tl display_arg =
  403. let st = s_type (print_context()) in
  404. let s_arg (n,o,t) = Printf.sprintf "%s%s:%s" (if o then "?" else "") n (st t) in
  405. let s_fun args ret = Printf.sprintf "(%s):%s" (String.concat ", " (List.map s_arg args)) (st ret) in
  406. let siginf = List.map (fun ((args,ret),doc) ->
  407. let label = s_fun args ret in
  408. let parameters =
  409. List.map (fun arg ->
  410. let label = s_arg arg in
  411. JObject [
  412. "label",JString label
  413. ]
  414. ) args
  415. in
  416. let js = [
  417. "label",JString label;
  418. "parameters",JArray parameters;
  419. ] in
  420. JObject (match doc with None -> js | Some s -> ("documentation",JString s) :: js)
  421. ) tl in
  422. let jo = JObject [
  423. "signatures",JArray siginf;
  424. "activeParameter",JInt display_arg;
  425. "activeSignature",JInt 0;
  426. ] in
  427. let b = Buffer.create 0 in
  428. write_json (Buffer.add_string b) jo;
  429. Buffer.contents b
  430. module StatisticsPrinter = struct
  431. open Statistics
  432. let relation_to_string = function
  433. | Implemented -> "implementers"
  434. | Extended -> "subclasses"
  435. | Overridden -> "overrides"
  436. | Referenced -> "references"
  437. let symbol_to_string = function
  438. | SKClass _ -> "class type"
  439. | SKInterface _ -> "interface type"
  440. | SKEnum _ -> "enum type"
  441. | SKField _ -> "class field"
  442. | SKEnumField _ -> "enum field"
  443. | SKVariable _ -> "variable"
  444. let print_statistics (kinds,relations) =
  445. let files = Hashtbl.create 0 in
  446. Hashtbl.iter (fun p rl ->
  447. let file = Path.get_real_path p.pfile in
  448. try
  449. Hashtbl.replace files file ((p,rl) :: Hashtbl.find files file)
  450. with Not_found ->
  451. Hashtbl.add files file [p,rl]
  452. ) relations;
  453. let ja = Hashtbl.fold (fun file relations acc ->
  454. let l = List.map (fun (p,rl) ->
  455. let h = Hashtbl.create 0 in
  456. List.iter (fun (r,p) ->
  457. let s = relation_to_string r in
  458. let jo = JObject [
  459. "range",pos_to_json_range p;
  460. "file",JString (Path.get_real_path p.pfile);
  461. ] in
  462. try Hashtbl.replace h s (jo :: Hashtbl.find h s)
  463. with Not_found -> Hashtbl.add h s [jo]
  464. ) rl;
  465. let l = Hashtbl.fold (fun s js acc -> (s,JArray js) :: acc) h [] in
  466. let l = ("range",pos_to_json_range p) :: l in
  467. let l = try ("kind",JString (symbol_to_string (Hashtbl.find kinds p))) :: l with Not_found -> l in
  468. JObject l
  469. ) relations in
  470. (JObject [
  471. "file",JString file;
  472. "statistics",JArray l
  473. ]) :: acc
  474. ) files [] in
  475. let b = Buffer.create 0 in
  476. write_json (Buffer.add_string b) (JArray ja);
  477. Buffer.contents b
  478. end
  479. module DiagnosticsPrinter = struct
  480. open Diagnostics
  481. open Diagnostics.DiagnosticsKind
  482. open DisplayTypes
  483. type t = DiagnosticsKind.t * pos
  484. module UnresolvedIdentifierSuggestion = struct
  485. type t =
  486. | UISImport
  487. | UISTypo
  488. let to_int = function
  489. | UISImport -> 0
  490. | UISTypo -> 1
  491. end
  492. let print_diagnostics ctx global =
  493. let com = ctx.com in
  494. let diag = Hashtbl.create 0 in
  495. let add dk p sev args =
  496. let file = Path.get_real_path p.pfile in
  497. let diag = try
  498. Hashtbl.find diag file
  499. with Not_found ->
  500. let d = DynArray.create() in
  501. Hashtbl.add diag file d;
  502. d
  503. in
  504. DynArray.add diag (dk,p,sev,args)
  505. in
  506. let add dk p sev args =
  507. if global || is_display_file p.pfile then add dk p sev args
  508. in
  509. let find_type i =
  510. let types = ref [] in
  511. Hashtbl.iter (fun _ m ->
  512. List.iter (fun mt ->
  513. let s_full_type_path (p,s) n = s_type_path (p,s) ^ if (s <> n) then "." ^ n else "" in
  514. let tinfos = t_infos mt in
  515. if snd tinfos.mt_path = i then
  516. types := JObject [
  517. "kind",JInt (UnresolvedIdentifierSuggestion.to_int UnresolvedIdentifierSuggestion.UISImport);
  518. "name",JString (s_full_type_path m.m_path i)
  519. ] :: !types
  520. ) m.m_types;
  521. ) ctx.g.modules;
  522. !types
  523. in
  524. List.iter (fun (s,p,suggestions) ->
  525. let suggestions = List.map (fun (s,_) ->
  526. JObject [
  527. "kind",JInt (UnresolvedIdentifierSuggestion.to_int UnresolvedIdentifierSuggestion.UISTypo);
  528. "name",JString s
  529. ]
  530. ) suggestions in
  531. add DKUnresolvedIdentifier p DiagnosticsSeverity.Error (JArray (suggestions @ (find_type s)));
  532. ) com.display_information.unresolved_identifiers;
  533. PMap.iter (fun p (r,_) ->
  534. if not !r then add DKUnusedImport p DiagnosticsSeverity.Warning (JArray [])
  535. ) com.shared.shared_display_information.import_positions;
  536. List.iter (fun (s,p,sev) ->
  537. add DKCompilerError p sev (JString s)
  538. ) com.shared.shared_display_information.diagnostics_messages;
  539. List.iter (fun (s,p,prange) ->
  540. add DKRemovableCode p DiagnosticsSeverity.Warning (JObject ["description",JString s;"range",if prange = null_pos then JNull else pos_to_json_range prange])
  541. ) com.shared.shared_display_information.removable_code;
  542. let jl = Hashtbl.fold (fun file diag acc ->
  543. let jl = DynArray.fold_left (fun acc (dk,p,sev,jargs) ->
  544. (JObject [
  545. "kind",JInt (to_int dk);
  546. "severity",JInt (DiagnosticsSeverity.to_int sev);
  547. "range",pos_to_json_range p;
  548. "args",jargs
  549. ]) :: acc
  550. ) [] diag in
  551. (JObject [
  552. "file",JString file;
  553. "diagnostics",JArray jl
  554. ]) :: acc
  555. ) diag [] in
  556. let js = JArray jl in
  557. let b = Buffer.create 0 in
  558. write_json (Buffer.add_string b) js;
  559. Buffer.contents b
  560. end
  561. module ModuleSymbolsPrinter = struct
  562. open DisplayTypes.SymbolKind
  563. open DisplayTypes.SymbolInformation
  564. let print_module_symbols com symbols filter =
  565. let regex = Option.map Str.regexp_case_fold filter in
  566. let reported = Hashtbl.create 0 in
  567. let add si =
  568. if Hashtbl.mem reported si.pos then false
  569. else begin
  570. let b = match regex with
  571. | None -> true
  572. | Some regex -> (try ignore(Str.search_forward regex si.name 0); true with Not_found -> false)
  573. in
  574. Hashtbl.replace reported si.pos true;
  575. b
  576. end
  577. in
  578. let ja = List.fold_left (fun acc (file,l) ->
  579. let jl = ExtList.List.filter_map (fun si ->
  580. if not (add si) then
  581. None
  582. else begin
  583. let l =
  584. ("name",JString si.name) ::
  585. ("kind",JInt (to_int si.kind)) ::
  586. ("range", pos_to_json_range si.pos) ::
  587. (match si.container_name with None -> [] | Some s -> ["containerName",JString s])
  588. in
  589. Some (JObject l)
  590. end
  591. ) (DynArray.to_list l) in
  592. if jl = [] then
  593. acc
  594. else
  595. (JObject [
  596. "file",JString file;
  597. "symbols",JArray jl
  598. ]) :: acc
  599. ) [] symbols in
  600. let js = JArray ja in
  601. let b = Buffer.create 0 in
  602. write_json (Buffer.add_string b) js;
  603. Buffer.contents b
  604. end
  605. (* Mode processing *)
  606. exception Completion of string
  607. let unquote v =
  608. let len = String.length v in
  609. if len > 0 && v.[0] = '"' && v.[len - 1] = '"' then String.sub v 1 (len - 2) else v
  610. let handle_display_argument com file_pos pre_compilation did_something =
  611. match file_pos with
  612. | "classes" ->
  613. pre_compilation := (fun() -> raise (Parser.TypePath (["."],None,true))) :: !pre_compilation;
  614. | "keywords" ->
  615. raise (Completion (print_keywords ()))
  616. | "memory" ->
  617. did_something := true;
  618. (try display_memory com with e -> prerr_endline (Printexc.get_backtrace ()));
  619. | "diagnostics" ->
  620. Common.define com Define.NoCOpt;
  621. com.display <- DisplayMode.create (DMDiagnostics true);
  622. Common.display_default := DMDiagnostics true;
  623. | _ ->
  624. let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format: " ^ file_pos) in
  625. let file = unquote file in
  626. let pos, smode = try ExtString.String.split pos "@" with _ -> pos,"" in
  627. let mode = match smode with
  628. | "position" ->
  629. Common.define com Define.NoCOpt;
  630. DMPosition
  631. | "usage" ->
  632. Common.define com Define.NoCOpt;
  633. DMUsage false
  634. (*| "rename" ->
  635. Common.define com Define.NoCOpt;
  636. DMUsage true*)
  637. | "package" ->
  638. DMPackage
  639. | "type" ->
  640. Common.define com Define.NoCOpt;
  641. DMType
  642. | "toplevel" ->
  643. Common.define com Define.NoCOpt;
  644. DMToplevel
  645. | "module-symbols" ->
  646. Common.define com Define.NoCOpt;
  647. DMModuleSymbols None;
  648. | "diagnostics" ->
  649. Common.define com Define.NoCOpt;
  650. DMDiagnostics false;
  651. | "statistics" ->
  652. Common.define com Define.NoCOpt;
  653. DMStatistics
  654. | "signature" ->
  655. DMSignature
  656. | "" ->
  657. DMField
  658. | _ ->
  659. let smode,arg = try ExtString.String.split smode "@" with _ -> pos,"" in
  660. match smode with
  661. | "resolve" ->
  662. DMResolve arg
  663. | "workspace-symbols" ->
  664. Common.define com Define.NoCOpt;
  665. DMModuleSymbols (Some arg)
  666. | _ ->
  667. DMField
  668. in
  669. let pos = try int_of_string pos with _ -> failwith ("Invalid format: " ^ pos) in
  670. com.display <- DisplayMode.create mode;
  671. Common.display_default := mode;
  672. Common.define_value com Define.Display (if smode <> "" then smode else "1");
  673. Parser.use_doc := true;
  674. Parser.resume_display := {
  675. pfile = Path.unique_full_path file;
  676. pmin = pos;
  677. pmax = pos;
  678. }
  679. let process_display_file com classes =
  680. let get_module_path_from_file_path com spath =
  681. let rec loop = function
  682. | [] -> None
  683. | cp :: l ->
  684. let cp = (if cp = "" then "./" else cp) in
  685. let c = Path.add_trailing_slash (Path.get_real_path cp) in
  686. let clen = String.length c in
  687. if clen < String.length spath && String.sub spath 0 clen = c then begin
  688. let path = String.sub spath clen (String.length spath - clen) in
  689. (try
  690. let path = Path.parse_path path in
  691. (match loop l with
  692. | Some x as r when String.length (s_type_path x) < String.length (s_type_path path) -> r
  693. | _ -> Some path)
  694. with _ -> loop l)
  695. end else
  696. loop l
  697. in
  698. loop com.class_path
  699. in
  700. match com.display.dms_display_file_policy with
  701. | DFPNo ->
  702. ()
  703. | dfp ->
  704. if dfp = DFPOnly then begin
  705. classes := [];
  706. com.main_class <- None;
  707. end;
  708. let real = Path.get_real_path (!Parser.resume_display).pfile in
  709. (match get_module_path_from_file_path com real with
  710. | Some path ->
  711. if com.display.dms_kind = DMPackage then raise (DisplayPackage (fst path));
  712. classes := path :: !classes
  713. | None ->
  714. if not (Sys.file_exists real) then failwith "Display file does not exist";
  715. (match List.rev (ExtString.String.nsplit real Path.path_sep) with
  716. | file :: _ when file.[0] >= 'a' && file.[0] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter")
  717. | _ -> ());
  718. failwith "Display file was not found in class path"
  719. );
  720. Common.log com ("Display file : " ^ real);
  721. Common.log com ("Classes found : [" ^ (String.concat "," (List.map s_type_path !classes)) ^ "]")
  722. let process_global_display_mode com tctx = match com.display.dms_kind with
  723. | DMUsage with_definition ->
  724. let symbols,relations = Statistics.collect_statistics tctx in
  725. let rec loop acc relations = match relations with
  726. | (Statistics.Referenced,p) :: relations -> loop (p :: acc) relations
  727. | _ :: relations -> loop acc relations
  728. | [] -> acc
  729. in
  730. let usages = Hashtbl.fold (fun p sym acc ->
  731. if Statistics.is_usage_symbol sym then begin
  732. let acc = if with_definition then p :: acc else acc in
  733. (try loop acc (Hashtbl.find relations p)
  734. with Not_found -> acc)
  735. end else
  736. acc
  737. ) symbols [] in
  738. let usages = List.sort (fun p1 p2 ->
  739. let c = compare p1.pfile p2.pfile in
  740. if c <> 0 then c else compare p1.pmin p2.pmin
  741. ) usages in
  742. raise (DisplayPosition usages)
  743. | DMDiagnostics global ->
  744. Diagnostics.prepare com global;
  745. raise (Diagnostics (DiagnosticsPrinter.print_diagnostics tctx global))
  746. | DMStatistics ->
  747. let stats = Statistics.collect_statistics tctx in
  748. raise (Statistics (StatisticsPrinter.print_statistics stats))
  749. | DMModuleSymbols filter ->
  750. let symbols = com.shared.shared_display_information.document_symbols in
  751. let symbols = match CompilationServer.get() with
  752. | None -> symbols
  753. | Some cs ->
  754. let l = CompilationServer.get_context_files cs ((Define.get_signature com.defines) :: (match com.get_macros() with None -> [] | Some com -> [Define.get_signature com.defines])) in
  755. List.fold_left (fun acc (file,data) ->
  756. print_endline (Printf.sprintf "%s %b" file (is_display_file file));
  757. if (filter <> None || is_display_file file) then
  758. (file,DocumentSymbols.collect_module_symbols data) :: acc
  759. else
  760. acc
  761. ) symbols l
  762. in
  763. raise (ModuleSymbols(ModuleSymbolsPrinter.print_module_symbols com symbols filter))
  764. | _ -> ()
  765. let find_doc t =
  766. let doc = match follow t with
  767. | TAnon an ->
  768. begin match !(an.a_status) with
  769. | Statics c -> c.cl_doc
  770. | EnumStatics en -> en.e_doc
  771. | AbstractStatics a -> a.a_doc
  772. | _ -> None
  773. end
  774. | _ ->
  775. None
  776. in
  777. doc