dce.ml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. (*
  2. * Copyright (C)2005-2013 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. *)
  22. open Ast
  23. open Common
  24. open Type
  25. type dce = {
  26. com : context;
  27. full : bool;
  28. std_dirs : string list;
  29. debug : bool;
  30. follow_expr : dce -> texpr -> unit;
  31. mutable curclass : tclass;
  32. mutable added_fields : (tclass * tclass_field * bool) list;
  33. mutable marked_fields : tclass_field list;
  34. mutable marked_maybe_fields : tclass_field list;
  35. mutable t_stack : t list;
  36. mutable ts_stack : t list;
  37. mutable features : (string,(tclass * tclass_field * bool) list) Hashtbl.t;
  38. }
  39. (* checking *)
  40. (* check for @:keepSub metadata, which forces @:keep on child classes *)
  41. let rec super_forces_keep c =
  42. Meta.has Meta.KeepSub c.cl_meta || match c.cl_super with
  43. | Some (csup,_) -> super_forces_keep csup
  44. | _ -> false
  45. let is_std_file dce file =
  46. List.exists (ExtString.String.starts_with file) dce.std_dirs
  47. (* check if a class is kept entirely *)
  48. let keep_whole_class dce c =
  49. Meta.has Meta.Keep c.cl_meta
  50. || not (dce.full || is_std_file dce c.cl_module.m_extra.m_file || has_meta Meta.Dce c.cl_meta)
  51. || super_forces_keep c
  52. || (match c with
  53. | { cl_path = ([],("Math"|"Array"))} when dce.com.platform = Js -> false
  54. | { cl_extern = true }
  55. | { cl_path = ["flash";"_Boot"],"RealBoot" } -> true
  56. | { cl_path = [],"String" }
  57. | { cl_path = [],"Array" } -> not (dce.com.platform = Js)
  58. | _ -> false)
  59. let keep_whole_enum dce en =
  60. Meta.has Meta.Keep en.e_meta
  61. || not (dce.full || is_std_file dce en.e_module.m_extra.m_file || has_meta Meta.Dce en.e_meta)
  62. (* check if a field is kept *)
  63. let keep_field dce cf =
  64. Meta.has Meta.Keep cf.cf_meta
  65. || Meta.has Meta.Used cf.cf_meta
  66. || cf.cf_name = "__init__"
  67. || is_extern_field cf
  68. (* marking *)
  69. let rec check_feature dce s =
  70. try
  71. let l = Hashtbl.find dce.features s in
  72. List.iter (fun (c,cf,stat) ->
  73. mark_field dce c cf stat
  74. ) l;
  75. Hashtbl.remove dce.features s;
  76. with Not_found ->
  77. ()
  78. (* mark a field as kept *)
  79. and mark_field dce c cf stat =
  80. let add cf =
  81. if not (Meta.has Meta.Used cf.cf_meta) then begin
  82. cf.cf_meta <- (Meta.Used,[],cf.cf_pos) :: cf.cf_meta;
  83. dce.added_fields <- (c,cf,stat) :: dce.added_fields;
  84. dce.marked_fields <- cf :: dce.marked_fields;
  85. check_feature dce (Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name);
  86. end
  87. in
  88. if cf.cf_name = "new" then begin
  89. let rec loop c = match c.cl_super with
  90. | None -> ()
  91. | Some (csup,_) ->
  92. begin match csup.cl_constructor with
  93. | None -> ()
  94. | Some cf -> add cf
  95. end;
  96. loop csup
  97. in
  98. loop c
  99. end;
  100. if not (PMap.mem cf.cf_name (if stat then c.cl_statics else c.cl_fields)) then begin
  101. match c.cl_super with
  102. | None -> add cf
  103. | Some (c,_) -> mark_field dce c cf stat
  104. end else
  105. add cf
  106. let rec update_marked_class_fields dce c =
  107. (* mark all :?used fields as surely :used now *)
  108. List.iter (fun cf ->
  109. if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf true
  110. ) c.cl_ordered_statics;
  111. List.iter (fun cf ->
  112. if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf false
  113. ) c.cl_ordered_fields;
  114. (* we always have to keep super classes and implemented interfaces *)
  115. (match c.cl_init with None -> () | Some init -> dce.follow_expr dce init);
  116. List.iter (fun (c,_) -> mark_class dce c) c.cl_implements;
  117. (match c.cl_super with None -> () | Some (csup,pl) -> mark_class dce csup)
  118. (* mark a class as kept. If the class has fields marked as @:?keep, make sure to keep them *)
  119. and mark_class dce c = if not (Meta.has Meta.Used c.cl_meta) then begin
  120. c.cl_meta <- (Meta.Used,[],c.cl_pos) :: c.cl_meta;
  121. check_feature dce (Printf.sprintf "%s.*" (s_type_path c.cl_path));
  122. update_marked_class_fields dce c;
  123. end
  124. let rec mark_enum dce e = if not (Meta.has Meta.Used e.e_meta) then begin
  125. e.e_meta <- (Meta.Used,[],e.e_pos) :: e.e_meta;
  126. check_feature dce (Printf.sprintf "%s.*" (s_type_path e.e_path));
  127. PMap.iter (fun _ ef -> mark_t dce ef.ef_pos ef.ef_type) e.e_constrs;
  128. end
  129. and mark_abstract dce a = if not (Meta.has Meta.Used a.a_meta) then begin
  130. check_feature dce (Printf.sprintf "%s.*" (s_type_path a.a_path));
  131. a.a_meta <- (Meta.Used,[],a.a_pos) :: a.a_meta
  132. end
  133. (* mark a type as kept *)
  134. and mark_t dce p t =
  135. if not (List.exists (fun t2 -> Type.fast_eq t t2) dce.t_stack) then begin
  136. dce.t_stack <- t :: dce.t_stack;
  137. begin match follow t with
  138. | TInst({cl_kind = KTypeParameter tl} as c,pl) ->
  139. if not (Meta.has Meta.Used c.cl_meta) then begin
  140. c.cl_meta <- (Meta.Used,[],c.cl_pos) :: c.cl_meta;
  141. List.iter (mark_t dce p) tl;
  142. end;
  143. List.iter (mark_t dce p) pl
  144. | TInst(c,pl) ->
  145. mark_class dce c;
  146. List.iter (mark_t dce p) pl
  147. | TFun(args,ret) ->
  148. List.iter (fun (_,_,t) -> mark_t dce p t) args;
  149. mark_t dce p ret
  150. | TEnum(e,pl) ->
  151. mark_enum dce e;
  152. List.iter (mark_t dce p) pl
  153. | TAbstract(a,pl) when Meta.has Meta.MultiType a.a_meta ->
  154. begin try
  155. mark_t dce p (snd (Codegen.AbstractCast.find_multitype_specialization dce.com a pl p))
  156. with Typecore.Error _ ->
  157. ()
  158. end
  159. | TAbstract(a,pl) ->
  160. mark_abstract dce a;
  161. List.iter (mark_t dce p) pl;
  162. if not (Meta.has Meta.CoreType a.a_meta) then
  163. mark_t dce p (Abstract.get_underlying_type a pl)
  164. | TLazy _ | TDynamic _ | TAnon _ | TType _ | TMono _ -> ()
  165. end;
  166. dce.t_stack <- List.tl dce.t_stack
  167. end
  168. let mark_mt dce mt = match mt with
  169. | TClassDecl c ->
  170. mark_class dce c;
  171. | TEnumDecl e ->
  172. mark_enum dce e
  173. | TAbstractDecl a ->
  174. (* abstract 'feature' is defined as the abstract type beeing used as a value, not as a type *)
  175. if not (Meta.has Meta.ValueUsed a.a_meta) then a.a_meta <- (Meta.ValueUsed,[],a.a_pos) :: a.a_meta;
  176. mark_abstract dce a
  177. | TTypeDecl _ ->
  178. ()
  179. (* find all dependent fields by checking implementing/subclassing types *)
  180. let rec mark_dependent_fields dce csup n stat =
  181. List.iter (fun mt -> match mt with
  182. | TClassDecl c when is_parent csup c ->
  183. let rec loop c =
  184. (try
  185. let cf = PMap.find n (if stat then c.cl_statics else c.cl_fields) in
  186. (* if it's clear that the class is kept, the field has to be kept as well. This is also true for
  187. extern interfaces because we cannot remove fields from them *)
  188. if Meta.has Meta.Used c.cl_meta || (csup.cl_interface && csup.cl_extern) then mark_field dce c cf stat
  189. (* otherwise it might be kept if the class is kept later, so mark it as :?used *)
  190. else if not (Meta.has Meta.MaybeUsed cf.cf_meta) then begin
  191. cf.cf_meta <- (Meta.MaybeUsed,[],cf.cf_pos) :: cf.cf_meta;
  192. dce.marked_maybe_fields <- cf :: dce.marked_maybe_fields;
  193. end
  194. with Not_found ->
  195. (* if the field is not present on current class, it might come from a base class *)
  196. (match c.cl_super with None -> () | Some (csup,_) -> loop csup))
  197. in
  198. loop c
  199. | _ -> ()
  200. ) dce.com.types
  201. (* expr and field evaluation *)
  202. let opt f e = match e with None -> () | Some e -> f e
  203. let rec to_string dce t = match t with
  204. | TInst(c,tl) ->
  205. field dce c "toString" false;
  206. | TType(tt,tl) ->
  207. if not (List.exists (fun t2 -> Type.fast_eq t t2) dce.ts_stack) then begin
  208. dce.ts_stack <- t :: dce.ts_stack;
  209. to_string dce (apply_params tt.t_params tl tt.t_type)
  210. end
  211. | TAbstract({a_impl = Some c} as a,tl) ->
  212. if Meta.has Meta.CoreType a.a_meta then
  213. field dce c "toString" false
  214. else
  215. to_string dce (Abstract.get_underlying_type a tl)
  216. | TMono r ->
  217. (match !r with
  218. | Some t -> to_string dce t
  219. | _ -> ())
  220. | TLazy f ->
  221. to_string dce (!f())
  222. | TDynamic t ->
  223. if t == t_dynamic then
  224. ()
  225. else
  226. to_string dce t
  227. | TEnum _ | TFun _ | TAnon _ | TAbstract({a_impl = None},_) ->
  228. (* if we to_string these it does not imply that we need all its sub-types *)
  229. ()
  230. and field dce c n stat =
  231. let find_field n =
  232. if n = "new" then match c.cl_constructor with
  233. | None -> raise Not_found
  234. | Some cf -> cf
  235. else PMap.find n (if stat then c.cl_statics else c.cl_fields)
  236. in
  237. (try
  238. let cf = find_field n in
  239. mark_field dce c cf stat;
  240. with Not_found -> try
  241. if c.cl_interface then begin
  242. let rec loop cl = match cl with
  243. | [] -> raise Not_found
  244. | (c,_) :: cl ->
  245. try field dce c n stat with Not_found -> loop cl
  246. in
  247. loop c.cl_implements
  248. end else match c.cl_super with Some (csup,_) -> field dce csup n stat | None -> raise Not_found
  249. with Not_found -> try
  250. match c.cl_kind with
  251. | KTypeParameter tl ->
  252. let rec loop tl = match tl with
  253. | [] -> raise Not_found
  254. | TInst(c,_) :: cl ->
  255. (try field dce c n stat with Not_found -> loop cl)
  256. | t :: tl ->
  257. loop tl
  258. in
  259. loop tl
  260. | _ -> raise Not_found
  261. with Not_found ->
  262. if dce.debug then prerr_endline ("[DCE] Field " ^ n ^ " not found on " ^ (s_type_path c.cl_path)) else ())
  263. and mark_directly_used_class c =
  264. if not (Meta.has Meta.DirectlyUsed c.cl_meta) then
  265. c.cl_meta <- (Meta.DirectlyUsed,[],c.cl_pos) :: c.cl_meta
  266. and mark_directly_used_enum e =
  267. if not (Meta.has Meta.DirectlyUsed e.e_meta) then
  268. e.e_meta <- (Meta.DirectlyUsed,[],e.e_pos) :: e.e_meta
  269. and mark_directly_used_mt mt =
  270. match mt with
  271. | TClassDecl c ->
  272. mark_directly_used_class c
  273. | TEnumDecl e ->
  274. mark_directly_used_enum e
  275. | _ ->
  276. ()
  277. and expr dce e =
  278. mark_t dce e.epos e.etype;
  279. match e.eexpr with
  280. | TNew(c,pl,el) ->
  281. mark_class dce c;
  282. mark_directly_used_class c;
  283. field dce c "new" false;
  284. List.iter (expr dce) el;
  285. List.iter (mark_t dce e.epos) pl;
  286. | TVar (v,e1) ->
  287. opt (expr dce) e1;
  288. mark_t dce e.epos v.v_type;
  289. | TCast(e, Some mt) ->
  290. check_feature dce "typed_cast";
  291. mark_mt dce mt;
  292. mark_directly_used_mt mt;
  293. expr dce e;
  294. | TTypeExpr mt ->
  295. mark_mt dce mt;
  296. mark_directly_used_mt mt;
  297. | TTry(e, vl) ->
  298. expr dce e;
  299. List.iter (fun (v,e) ->
  300. if v.v_type != t_dynamic then check_feature dce "typed_catch";
  301. expr dce e;
  302. mark_t dce e.epos v.v_type;
  303. ) vl;
  304. | TCall ({eexpr = TLocal ({v_name = "__define_feature__"})},[{eexpr = TConst (TString ft)};e]) ->
  305. Hashtbl.replace dce.curclass.cl_module.m_extra.m_features ft true;
  306. check_feature dce ft;
  307. expr dce e
  308. (* keep toString method when the class is argument to Std.string or haxe.Log.trace *)
  309. | TCall ({eexpr = TField({eexpr = TTypeExpr (TClassDecl ({cl_path = (["haxe"],"Log")} as c))},FStatic (_,{cf_name="trace"}))} as ef, ((e2 :: el) as args))
  310. | TCall ({eexpr = TField({eexpr = TTypeExpr (TClassDecl ({cl_path = ([],"Std")} as c))},FStatic (_,{cf_name="string"}))} as ef, ((e2 :: el) as args)) ->
  311. mark_class dce c;
  312. to_string dce e2.etype;
  313. begin match el with
  314. | [{eexpr = TObjectDecl fl}] ->
  315. begin try
  316. begin match List.assoc "customParams" fl with
  317. | {eexpr = TArrayDecl el} ->
  318. List.iter (fun e -> to_string dce e.etype) el
  319. | _ ->
  320. ()
  321. end
  322. with Not_found ->
  323. ()
  324. end
  325. | _ ->
  326. ()
  327. end;
  328. expr dce ef;
  329. List.iter (expr dce) args;
  330. | TCall ({eexpr = TConst TSuper} as e,el) ->
  331. mark_t dce e.epos e.etype;
  332. List.iter (expr dce) el;
  333. | TField(e,fa) ->
  334. begin match fa with
  335. | FStatic(c,cf) ->
  336. mark_class dce c;
  337. mark_field dce c cf true;
  338. | FInstance(c,_,cf) ->
  339. mark_class dce c;
  340. mark_field dce c cf false;
  341. | _ ->
  342. let n = field_name fa in
  343. begin match follow e.etype with
  344. | TInst(c,_) ->
  345. mark_class dce c;
  346. field dce c n false;
  347. | TAnon a ->
  348. (match !(a.a_status) with
  349. | Statics c ->
  350. mark_class dce c;
  351. field dce c n true;
  352. | _ -> ())
  353. | _ -> ()
  354. end;
  355. end;
  356. expr dce e;
  357. | TThrow e ->
  358. to_string dce e.etype;
  359. expr dce e
  360. | _ ->
  361. Type.iter (expr dce) e
  362. let fix_accessors com =
  363. List.iter (fun mt -> match mt with
  364. | (TClassDecl c) ->
  365. let rec has_accessor c n stat =
  366. PMap.mem n (if stat then c.cl_statics else c.cl_fields)
  367. || match c.cl_super with Some (csup,_) -> has_accessor csup n stat | None -> false
  368. in
  369. let check_prop stat cf =
  370. (match cf.cf_kind with
  371. | Var {v_read = AccCall; v_write = a} ->
  372. let s = "get_" ^ cf.cf_name in
  373. cf.cf_kind <- Var {v_read = if has_accessor c s stat then AccCall else AccNever; v_write = a}
  374. | _ -> ());
  375. (match cf.cf_kind with
  376. | Var {v_write = AccCall; v_read = a} ->
  377. let s = "set_" ^ cf.cf_name in
  378. cf.cf_kind <- Var {v_write = if has_accessor c s stat then AccCall else AccNever; v_read = a}
  379. | _ -> ())
  380. in
  381. List.iter (check_prop true) c.cl_ordered_statics;
  382. List.iter (check_prop false) c.cl_ordered_fields;
  383. | _ -> ()
  384. ) com.types
  385. let run com main full =
  386. let dce = {
  387. com = com;
  388. full = full;
  389. std_dirs = if full then [] else List.map Common.unique_full_path com.std_path;
  390. debug = Common.defined com Define.DceDebug;
  391. added_fields = [];
  392. follow_expr = expr;
  393. marked_fields = [];
  394. marked_maybe_fields = [];
  395. t_stack = [];
  396. ts_stack = [];
  397. features = Hashtbl.create 0;
  398. curclass = null_class;
  399. } in
  400. begin match main with
  401. | Some {eexpr = TCall({eexpr = TField(e,(FStatic(c,cf)))},_)} ->
  402. cf.cf_meta <- (Meta.Keep,[],cf.cf_pos) :: cf.cf_meta
  403. | _ ->
  404. ()
  405. end;
  406. List.iter (fun m ->
  407. List.iter (fun (s,v) ->
  408. if Hashtbl.mem dce.features s then Hashtbl.replace dce.features s (v :: Hashtbl.find dce.features s)
  409. else Hashtbl.add dce.features s [v]
  410. ) m.m_extra.m_if_feature;
  411. ) com.modules;
  412. (* first step: get all entry points, which is the main method and all class methods which are marked with @:keep *)
  413. List.iter (fun t -> match t with
  414. | TClassDecl c ->
  415. let keep_class = keep_whole_class dce c && (not c.cl_extern || c.cl_interface) in
  416. let loop stat cf =
  417. if keep_class || keep_field dce cf then mark_field dce c cf stat
  418. in
  419. List.iter (loop true) c.cl_ordered_statics;
  420. List.iter (loop false) c.cl_ordered_fields;
  421. begin match c.cl_constructor with
  422. | Some cf -> loop false cf
  423. | None -> ()
  424. end;
  425. begin match c.cl_init with
  426. | Some e when keep_class || Meta.has Meta.KeepInit c.cl_meta ->
  427. (* create a fake field to deal with our internal logic (issue #3286) *)
  428. let cf = mk_field "__init__" e.etype e.epos in
  429. cf.cf_expr <- Some e;
  430. loop true cf
  431. | _ ->
  432. ()
  433. end;
  434. | TEnumDecl en when keep_whole_enum dce en ->
  435. mark_enum dce en
  436. | _ ->
  437. ()
  438. ) com.types;
  439. if dce.debug then begin
  440. List.iter (fun (c,cf,_) -> match cf.cf_expr with
  441. | None -> ()
  442. | Some _ -> print_endline ("[DCE] Entry point: " ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name)
  443. ) dce.added_fields;
  444. end;
  445. (* second step: initiate DCE passes and keep going until no new fields were added *)
  446. let rec loop () =
  447. match dce.added_fields with
  448. | [] -> ()
  449. | cfl ->
  450. dce.added_fields <- [];
  451. (* extend to dependent (= overriding/implementing) class fields *)
  452. List.iter (fun (c,cf,stat) -> mark_dependent_fields dce c cf.cf_name stat) cfl;
  453. (* mark fields as used *)
  454. List.iter (fun (c,cf,stat) ->
  455. if not (is_extern_field cf) then mark_class dce c;
  456. mark_field dce c cf stat;
  457. mark_t dce cf.cf_pos cf.cf_type
  458. ) cfl;
  459. (* follow expressions to new types/fields *)
  460. List.iter (fun (c,cf,_) ->
  461. dce.curclass <- c;
  462. opt (expr dce) cf.cf_expr;
  463. List.iter (fun cf -> if cf.cf_expr <> None then opt (expr dce) cf.cf_expr) cf.cf_overloads
  464. ) cfl;
  465. loop ()
  466. in
  467. loop ();
  468. (* third step: filter types *)
  469. let rec loop acc types =
  470. match types with
  471. | (TClassDecl c) as mt :: l when keep_whole_class dce c ->
  472. loop (mt :: acc) l
  473. | (TClassDecl c) as mt :: l ->
  474. let check_property cf stat =
  475. let add_accessor_metadata cf =
  476. if not (Meta.has Meta.Accessor cf.cf_meta) then cf.cf_meta <- (Meta.Accessor,[],c.cl_pos) :: cf.cf_meta
  477. in
  478. begin match cf.cf_kind with
  479. | Var {v_read = AccCall} ->
  480. begin try
  481. add_accessor_metadata (PMap.find ("get_" ^ cf.cf_name) (if stat then c.cl_statics else c.cl_fields))
  482. with Not_found ->
  483. ()
  484. end
  485. | _ ->
  486. ()
  487. end;
  488. begin match cf.cf_kind with
  489. | Var {v_write = AccCall} ->
  490. begin try
  491. add_accessor_metadata (PMap.find ("set_" ^ cf.cf_name) (if stat then c.cl_statics else c.cl_fields))
  492. with Not_found ->
  493. ()
  494. end
  495. | _ ->
  496. ()
  497. end;
  498. in
  499. (* add :keep so subsequent filter calls do not process class fields again *)
  500. c.cl_meta <- (Meta.Keep,[],c.cl_pos) :: c.cl_meta;
  501. c.cl_ordered_statics <- List.filter (fun cf ->
  502. let b = keep_field dce cf in
  503. if not b then begin
  504. if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
  505. check_property cf true;
  506. c.cl_statics <- PMap.remove cf.cf_name c.cl_statics;
  507. end;
  508. b
  509. ) c.cl_ordered_statics;
  510. c.cl_ordered_fields <- List.filter (fun cf ->
  511. let b = keep_field dce cf in
  512. if not b then begin
  513. if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
  514. check_property cf false;
  515. c.cl_fields <- PMap.remove cf.cf_name c.cl_fields;
  516. end;
  517. b
  518. ) c.cl_ordered_fields;
  519. (match c.cl_constructor with Some cf when not (keep_field dce cf) -> c.cl_constructor <- None | _ -> ());
  520. let inef cf = not (is_extern_field cf) in
  521. let has_non_extern_fields = List.exists inef c.cl_ordered_fields || List.exists inef c.cl_ordered_statics in
  522. (* we keep a class if it was used or has a used field *)
  523. if Meta.has Meta.Used c.cl_meta || has_non_extern_fields then loop (mt :: acc) l else begin
  524. (match c.cl_init with
  525. | Some f when Meta.has Meta.KeepInit c.cl_meta ->
  526. (* it means that we only need the __init__ block *)
  527. c.cl_extern <- true;
  528. loop (mt :: acc) l
  529. | _ ->
  530. if dce.debug then print_endline ("[DCE] Removed class " ^ (s_type_path c.cl_path));
  531. loop acc l)
  532. end
  533. | (TEnumDecl en) as mt :: l when Meta.has Meta.Used en.e_meta || en.e_extern || keep_whole_enum dce en ->
  534. loop (mt :: acc) l
  535. | TEnumDecl e :: l ->
  536. if dce.debug then print_endline ("[DCE] Removed enum " ^ (s_type_path e.e_path));
  537. loop acc l
  538. | mt :: l ->
  539. loop (mt :: acc) l
  540. | [] ->
  541. acc
  542. in
  543. com.types <- loop [] (List.rev com.types);
  544. (* extra step to adjust properties that had accessors removed (required for Php and Cpp) *)
  545. fix_accessors com;
  546. (* remove "override" from fields that do not override anything anymore *)
  547. List.iter (fun mt -> match mt with
  548. | TClassDecl c ->
  549. c.cl_overrides <- List.filter (fun s ->
  550. let rec loop c =
  551. match c.cl_super with
  552. | Some (csup,_) when PMap.mem s.cf_name csup.cl_fields -> true
  553. | Some (csup,_) -> loop csup
  554. | None -> false
  555. in
  556. loop c
  557. ) c.cl_overrides;
  558. | _ -> ()
  559. ) com.types;
  560. (* mark extern classes as really used if they are extended by non-extern ones *)
  561. List.iter (function
  562. | TClassDecl ({cl_extern = false; cl_super = Some ({cl_extern = true} as csup, _)}) ->
  563. mark_directly_used_class csup
  564. | TClassDecl ({cl_extern = false} as c) when c.cl_implements <> [] ->
  565. List.iter (fun (iface,_) -> if (iface.cl_extern) then mark_directly_used_class iface) c.cl_implements;
  566. | _ -> ()
  567. ) com.types;
  568. (* cleanup added fields metadata - compatibility with compilation server *)
  569. let rec remove_meta m = function
  570. | [] -> []
  571. | (m2,_,_) :: l when m = m2 -> l
  572. | x :: l -> x :: remove_meta m l
  573. in
  574. List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.Used cf.cf_meta) dce.marked_fields;
  575. List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.MaybeUsed cf.cf_meta) dce.marked_maybe_fields;