dce.ml 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. (*
  2. The Haxe Compiler
  3. Copyright (C) 2005-2015 Haxe Foundation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. *)
  16. open Ast
  17. open Common
  18. open Type
  19. type dce = {
  20. com : context;
  21. full : bool;
  22. std_dirs : string list;
  23. debug : bool;
  24. follow_expr : dce -> texpr -> unit;
  25. mutable curclass : tclass;
  26. mutable added_fields : (tclass * tclass_field * bool) list;
  27. mutable marked_fields : tclass_field list;
  28. mutable marked_maybe_fields : tclass_field list;
  29. mutable t_stack : t list;
  30. mutable ts_stack : t list;
  31. mutable features : (string,(tclass * tclass_field * bool) list) Hashtbl.t;
  32. }
  33. (* checking *)
  34. (* check for @:keepSub metadata, which forces @:keep on child classes *)
  35. let rec super_forces_keep c =
  36. Meta.has Meta.KeepSub c.cl_meta || match c.cl_super with
  37. | Some (csup,_) -> super_forces_keep csup
  38. | _ -> false
  39. let is_std_file dce file =
  40. List.exists (ExtString.String.starts_with file) dce.std_dirs
  41. (* check if a class is kept entirely *)
  42. let keep_whole_class dce c =
  43. Meta.has Meta.Keep c.cl_meta
  44. || not (dce.full || is_std_file dce c.cl_module.m_extra.m_file || has_meta Meta.Dce c.cl_meta)
  45. || super_forces_keep c
  46. || (match c with
  47. | { cl_path = ([],("Math"|"Array"))} when dce.com.platform = Js -> false
  48. | { cl_extern = true }
  49. | { cl_path = ["flash";"_Boot"],"RealBoot" } -> true
  50. | { cl_path = [],"String" }
  51. | { cl_path = [],"Array" } -> not (dce.com.platform = Js)
  52. | _ -> false)
  53. let keep_whole_enum dce en =
  54. Meta.has Meta.Keep en.e_meta
  55. || not (dce.full || is_std_file dce en.e_module.m_extra.m_file || has_meta Meta.Dce en.e_meta)
  56. (* check if a field is kept *)
  57. let keep_field dce cf =
  58. Meta.has Meta.Keep cf.cf_meta
  59. || Meta.has Meta.Used cf.cf_meta
  60. || cf.cf_name = "__init__"
  61. || is_extern_field cf
  62. (* marking *)
  63. let rec check_feature dce s =
  64. try
  65. let l = Hashtbl.find dce.features s in
  66. List.iter (fun (c,cf,stat) ->
  67. mark_field dce c cf stat
  68. ) l;
  69. Hashtbl.remove dce.features s;
  70. with Not_found ->
  71. ()
  72. and check_and_add_feature dce s =
  73. check_feature dce s;
  74. Common.add_feature dce.com s;
  75. (* mark a field as kept *)
  76. and mark_field dce c cf stat =
  77. let add cf =
  78. if not (Meta.has Meta.Used cf.cf_meta) then begin
  79. cf.cf_meta <- (Meta.Used,[],cf.cf_pos) :: cf.cf_meta;
  80. dce.added_fields <- (c,cf,stat) :: dce.added_fields;
  81. dce.marked_fields <- cf :: dce.marked_fields;
  82. check_feature dce (Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name);
  83. end
  84. in
  85. if cf.cf_name = "new" then begin
  86. let rec loop c = match c.cl_super with
  87. | None -> ()
  88. | Some (csup,_) ->
  89. begin match csup.cl_constructor with
  90. | None -> ()
  91. | Some cf -> add cf
  92. end;
  93. loop csup
  94. in
  95. loop c
  96. end;
  97. if not (PMap.mem cf.cf_name (if stat then c.cl_statics else c.cl_fields)) then begin
  98. match c.cl_super with
  99. | None -> add cf
  100. | Some (c,_) -> mark_field dce c cf stat
  101. end else
  102. add cf
  103. let rec update_marked_class_fields dce c =
  104. (* mark all :?used fields as surely :used now *)
  105. List.iter (fun cf ->
  106. if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf true
  107. ) c.cl_ordered_statics;
  108. List.iter (fun cf ->
  109. if Meta.has Meta.MaybeUsed cf.cf_meta then mark_field dce c cf false
  110. ) c.cl_ordered_fields;
  111. (* we always have to keep super classes and implemented interfaces *)
  112. (match c.cl_init with None -> () | Some init -> dce.follow_expr dce init);
  113. List.iter (fun (c,_) -> mark_class dce c) c.cl_implements;
  114. (match c.cl_super with None -> () | Some (csup,pl) -> mark_class dce csup)
  115. (* mark a class as kept. If the class has fields marked as @:?keep, make sure to keep them *)
  116. and mark_class dce c = if not (Meta.has Meta.Used c.cl_meta) then begin
  117. c.cl_meta <- (Meta.Used,[],c.cl_pos) :: c.cl_meta;
  118. check_feature dce (Printf.sprintf "%s.*" (s_type_path c.cl_path));
  119. update_marked_class_fields dce c;
  120. end
  121. let rec mark_enum dce e = if not (Meta.has Meta.Used e.e_meta) then begin
  122. e.e_meta <- (Meta.Used,[],e.e_pos) :: e.e_meta;
  123. check_and_add_feature dce "has_enum";
  124. check_feature dce (Printf.sprintf "%s.*" (s_type_path e.e_path));
  125. PMap.iter (fun _ ef -> mark_t dce ef.ef_pos ef.ef_type) e.e_constrs;
  126. end
  127. and mark_abstract dce a = if not (Meta.has Meta.Used a.a_meta) then begin
  128. check_feature dce (Printf.sprintf "%s.*" (s_type_path a.a_path));
  129. a.a_meta <- (Meta.Used,[],a.a_pos) :: a.a_meta
  130. end
  131. (* mark a type as kept *)
  132. and mark_t dce p t =
  133. if not (List.exists (fun t2 -> Type.fast_eq t t2) dce.t_stack) then begin
  134. dce.t_stack <- t :: dce.t_stack;
  135. begin match follow t with
  136. | TInst({cl_kind = KTypeParameter tl} as c,pl) ->
  137. if not (Meta.has Meta.Used c.cl_meta) then begin
  138. c.cl_meta <- (Meta.Used,[],c.cl_pos) :: c.cl_meta;
  139. List.iter (mark_t dce p) tl;
  140. end;
  141. List.iter (mark_t dce p) pl
  142. | TInst(c,pl) ->
  143. mark_class dce c;
  144. List.iter (mark_t dce p) pl
  145. | TFun(args,ret) ->
  146. List.iter (fun (_,_,t) -> mark_t dce p t) args;
  147. mark_t dce p ret
  148. | TEnum(e,pl) ->
  149. mark_enum dce e;
  150. List.iter (mark_t dce p) pl
  151. | TAbstract(a,pl) when Meta.has Meta.MultiType a.a_meta ->
  152. begin try
  153. mark_t dce p (snd (Codegen.AbstractCast.find_multitype_specialization dce.com a pl p))
  154. with Typecore.Error _ ->
  155. ()
  156. end
  157. | TAbstract(a,pl) ->
  158. mark_abstract dce a;
  159. List.iter (mark_t dce p) pl;
  160. if not (Meta.has Meta.CoreType a.a_meta) then
  161. mark_t dce p (Abstract.get_underlying_type a pl)
  162. | TLazy _ | TDynamic _ | TType _ | TAnon _ | TMono _ -> ()
  163. end;
  164. dce.t_stack <- List.tl dce.t_stack
  165. end
  166. let mark_mt dce mt = match mt with
  167. | TClassDecl c ->
  168. mark_class dce c;
  169. | TEnumDecl e ->
  170. mark_enum dce e
  171. | TAbstractDecl a ->
  172. (* abstract 'feature' is defined as the abstract type beeing used as a value, not as a type *)
  173. if not (Meta.has Meta.ValueUsed a.a_meta) then a.a_meta <- (Meta.ValueUsed,[],a.a_pos) :: a.a_meta;
  174. mark_abstract dce a
  175. | TTypeDecl _ ->
  176. ()
  177. (* find all dependent fields by checking implementing/subclassing types *)
  178. let rec mark_dependent_fields dce csup n stat =
  179. List.iter (fun mt -> match mt with
  180. | TClassDecl c when is_parent csup c ->
  181. let rec loop c =
  182. (try
  183. let cf = PMap.find n (if stat then c.cl_statics else c.cl_fields) in
  184. (* if it's clear that the class is kept, the field has to be kept as well. This is also true for
  185. extern interfaces because we cannot remove fields from them *)
  186. if Meta.has Meta.Used c.cl_meta || (csup.cl_interface && csup.cl_extern) then mark_field dce c cf stat
  187. (* otherwise it might be kept if the class is kept later, so mark it as :?used *)
  188. else if not (Meta.has Meta.MaybeUsed cf.cf_meta) then begin
  189. cf.cf_meta <- (Meta.MaybeUsed,[],cf.cf_pos) :: cf.cf_meta;
  190. dce.marked_maybe_fields <- cf :: dce.marked_maybe_fields;
  191. end
  192. with Not_found ->
  193. (* if the field is not present on current class, it might come from a base class *)
  194. (match c.cl_super with None -> () | Some (csup,_) -> loop csup))
  195. in
  196. loop c
  197. | _ -> ()
  198. ) dce.com.types
  199. (* expr and field evaluation *)
  200. let opt f e = match e with None -> () | Some e -> f e
  201. let rec to_string dce t = match t with
  202. | TInst(c,tl) ->
  203. field dce c "toString" false;
  204. | TType(tt,tl) ->
  205. if not (List.exists (fun t2 -> Type.fast_eq t t2) dce.ts_stack) then begin
  206. dce.ts_stack <- t :: dce.ts_stack;
  207. to_string dce (apply_params tt.t_params tl tt.t_type)
  208. end
  209. | TAbstract({a_impl = Some c} as a,tl) ->
  210. if Meta.has Meta.CoreType a.a_meta then
  211. field dce c "toString" false
  212. else
  213. to_string dce (Abstract.get_underlying_type a tl)
  214. | TMono r ->
  215. (match !r with
  216. | Some t -> to_string dce t
  217. | _ -> ())
  218. | TLazy f ->
  219. to_string dce (!f())
  220. | TDynamic t ->
  221. if t == t_dynamic then
  222. ()
  223. else
  224. to_string dce t
  225. | TEnum _ | TFun _ | TAnon _ | TAbstract({a_impl = None},_) ->
  226. (* if we to_string these it does not imply that we need all its sub-types *)
  227. ()
  228. and field dce c n stat =
  229. let find_field n =
  230. if n = "new" then match c.cl_constructor with
  231. | None -> raise Not_found
  232. | Some cf -> cf
  233. else PMap.find n (if stat then c.cl_statics else c.cl_fields)
  234. in
  235. (try
  236. let cf = find_field n in
  237. mark_field dce c cf stat;
  238. with Not_found -> try
  239. if c.cl_interface then begin
  240. let rec loop cl = match cl with
  241. | [] -> raise Not_found
  242. | (c,_) :: cl ->
  243. try field dce c n stat with Not_found -> loop cl
  244. in
  245. loop c.cl_implements
  246. end else match c.cl_super with Some (csup,_) -> field dce csup n stat | None -> raise Not_found
  247. with Not_found -> try
  248. match c.cl_kind with
  249. | KTypeParameter tl ->
  250. let rec loop tl = match tl with
  251. | [] -> raise Not_found
  252. | TInst(c,_) :: cl ->
  253. (try field dce c n stat with Not_found -> loop cl)
  254. | t :: tl ->
  255. loop tl
  256. in
  257. loop tl
  258. | _ -> raise Not_found
  259. with Not_found ->
  260. if dce.debug then prerr_endline ("[DCE] Field " ^ n ^ " not found on " ^ (s_type_path c.cl_path)) else ())
  261. and mark_directly_used_class c =
  262. if not (Meta.has Meta.DirectlyUsed c.cl_meta) then
  263. c.cl_meta <- (Meta.DirectlyUsed,[],c.cl_pos) :: c.cl_meta
  264. and mark_directly_used_enum e =
  265. if not (Meta.has Meta.DirectlyUsed e.e_meta) then
  266. e.e_meta <- (Meta.DirectlyUsed,[],e.e_pos) :: e.e_meta
  267. and mark_directly_used_mt mt =
  268. match mt with
  269. | TClassDecl c ->
  270. mark_directly_used_class c
  271. | TEnumDecl e ->
  272. mark_directly_used_enum e
  273. | _ ->
  274. ()
  275. and check_dynamic_write dce fa =
  276. let n = field_name fa in
  277. check_and_add_feature dce ("dynamic_write");
  278. check_and_add_feature dce ("dynamic_write." ^ n)
  279. and check_anon_optional_write dce fa =
  280. let n = field_name fa in
  281. check_and_add_feature dce ("anon_optional_write");
  282. check_and_add_feature dce ("anon_optional_write." ^ n)
  283. and check_anon_write dce fa =
  284. let n = field_name fa in
  285. check_and_add_feature dce ("anon_write");
  286. check_and_add_feature dce ("anon_write." ^ n)
  287. and is_array t = match follow t with
  288. | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> is_array (Abstract.get_underlying_type a tl)
  289. | TInst({ cl_path = ([], "Array")},_) -> true
  290. | _ -> false
  291. and is_dynamic t = match follow t with
  292. | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> is_dynamic (Abstract.get_underlying_type a tl)
  293. | TDynamic _ -> true
  294. | _ -> false
  295. and is_string t = match follow t with
  296. | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> is_string (Abstract.get_underlying_type a tl)
  297. | TInst( { cl_path = ([], "String")}, _) -> true
  298. | _ -> false
  299. and is_const_string e = match e.eexpr with
  300. | TConst(TString(_)) -> true
  301. | _ -> false
  302. and expr dce e =
  303. mark_t dce e.epos e.etype;
  304. match e.eexpr with
  305. | TNew(c,pl,el) ->
  306. mark_class dce c;
  307. mark_directly_used_class c;
  308. field dce c "new" false;
  309. List.iter (expr dce) el;
  310. List.iter (mark_t dce e.epos) pl;
  311. | TVar (v,e1) ->
  312. opt (expr dce) e1;
  313. mark_t dce e.epos v.v_type;
  314. | TCast(e, Some mt) ->
  315. check_feature dce "typed_cast";
  316. mark_mt dce mt;
  317. mark_directly_used_mt mt;
  318. expr dce e;
  319. | TObjectDecl(vl) ->
  320. check_and_add_feature dce "has_anon";
  321. List.iter (fun (_,e) -> expr dce e) vl;
  322. | TTypeExpr mt ->
  323. mark_mt dce mt;
  324. mark_directly_used_mt mt;
  325. | TTry(e, vl) ->
  326. expr dce e;
  327. List.iter (fun (v,e) ->
  328. if v.v_type != t_dynamic then check_feature dce "typed_catch";
  329. expr dce e;
  330. mark_t dce e.epos v.v_type;
  331. ) vl;
  332. | TCall ({eexpr = TLocal ({v_name = "`trace"})},[p;{ eexpr = TObjectDecl(v)}]) ->
  333. check_and_add_feature dce "has_anon_trace";
  334. List.iter (fun (_,e) -> expr dce e) v;
  335. expr dce p;
  336. | TCall ({eexpr = TLocal ({v_name = "__define_feature__"})},[{eexpr = TConst (TString ft)};e]) ->
  337. Hashtbl.replace dce.curclass.cl_module.m_extra.m_features ft true;
  338. check_feature dce ft;
  339. expr dce e;
  340. (* keep toString method when the class is argument to Std.string or haxe.Log.trace *)
  341. | TCall ({eexpr = TField({eexpr = TTypeExpr (TClassDecl ({cl_path = (["haxe"],"Log")} as c))},FStatic (_,{cf_name="trace"}))} as ef, ((e2 :: el) as args))
  342. | TCall ({eexpr = TField({eexpr = TTypeExpr (TClassDecl ({cl_path = ([],"Std")} as c))},FStatic (_,{cf_name="string"}))} as ef, ((e2 :: el) as args)) ->
  343. mark_class dce c;
  344. to_string dce e2.etype;
  345. begin match el with
  346. | [{eexpr = TObjectDecl fl}] ->
  347. begin try
  348. begin match List.assoc "customParams" fl with
  349. | {eexpr = TArrayDecl el} ->
  350. List.iter (fun e -> to_string dce e.etype) el
  351. | _ ->
  352. ()
  353. end
  354. with Not_found ->
  355. ()
  356. end
  357. | _ ->
  358. ()
  359. end;
  360. expr dce ef;
  361. List.iter (expr dce) args;
  362. | TCall ({eexpr = TConst TSuper} as e,el) ->
  363. mark_t dce e.epos e.etype;
  364. List.iter (expr dce) el;
  365. | TBinop(OpAdd,e1,e2) when is_dynamic e1.etype || is_dynamic e2.etype ->
  366. check_and_add_feature dce "add_dynamic";
  367. expr dce e1;
  368. expr dce e2;
  369. | TBinop( (OpAdd | (OpAssignOp OpAdd)),e1,e2) when ((is_string e1.etype || is_string e2.etype) && not ( is_const_string e1 && is_const_string e2)) ->
  370. check_and_add_feature dce "unsafe_string_concat";
  371. expr dce e1;
  372. expr dce e2;
  373. | TArray(({etype = TDynamic t} as e1),e2) when t == t_dynamic ->
  374. check_and_add_feature dce "dynamic_array_read";
  375. expr dce e1;
  376. expr dce e2;
  377. | TBinop( (OpAssign | OpAssignOp _), ({eexpr = TArray({etype = TDynamic t},_)} as e1), e2) when t == t_dynamic ->
  378. check_and_add_feature dce "dynamic_array_write";
  379. expr dce e1;
  380. expr dce e2;
  381. | TArray(({etype = t} as e1),e2) when is_array t ->
  382. check_and_add_feature dce "array_read";
  383. expr dce e1;
  384. expr dce e2;
  385. | TBinop( (OpAssign | OpAssignOp _), ({eexpr = TArray({etype = t},_)} as e1), e2) when is_array t ->
  386. check_and_add_feature dce "array_write";
  387. expr dce e1;
  388. expr dce e2;
  389. | TBinop(OpAssign,({eexpr = TField(_,(FDynamic _ as fa) )} as e1),e2) ->
  390. check_dynamic_write dce fa;
  391. expr dce e1;
  392. expr dce e2;
  393. | TBinop(OpAssign,({eexpr = TField(_,(FAnon cf as fa) )} as e1),e2) ->
  394. if Meta.has Meta.Optional cf.cf_meta then
  395. check_anon_optional_write dce fa
  396. else
  397. check_anon_write dce fa;
  398. expr dce e1;
  399. expr dce e2;
  400. | TBinop(OpAssignOp op,({eexpr = TField(_,(FDynamic _ as fa) )} as e1),e2) ->
  401. check_dynamic_write dce fa;
  402. expr dce e1;
  403. expr dce e2;
  404. | TBinop(OpAssignOp op,({eexpr = TField(_,(FAnon cf as fa) )} as e1),e2) ->
  405. if Meta.has Meta.Optional cf.cf_meta then
  406. check_anon_optional_write dce fa
  407. else
  408. check_anon_write dce fa;
  409. expr dce e1;
  410. expr dce e2;
  411. | TBinop(OpEq,({ etype = t1} as e1), ({ etype = t2} as e2) ) when is_dynamic t1 || is_dynamic t2 ->
  412. check_and_add_feature dce "dynamic_binop_==";
  413. expr dce e1;
  414. expr dce e2;
  415. | TBinop(OpEq,({ etype = t1} as e1), ({ etype = t2} as e2) ) when is_dynamic t1 || is_dynamic t2 ->
  416. check_and_add_feature dce "dynamic_binop_!=";
  417. expr dce e1;
  418. expr dce e2;
  419. | TBinop(OpMod,e1,e2) ->
  420. check_and_add_feature dce "binop_%";
  421. expr dce e1;
  422. expr dce e2;
  423. | TBinop((OpUShr | OpAssignOp OpUShr),e1,e2) ->
  424. check_and_add_feature dce "binop_>>>";
  425. expr dce e1;
  426. expr dce e2;
  427. | TField(e,fa) ->
  428. begin match fa with
  429. | FStatic(c,cf) ->
  430. mark_class dce c;
  431. mark_field dce c cf true;
  432. | FInstance(c,_,cf) ->
  433. mark_class dce c;
  434. mark_field dce c cf false;
  435. | _ ->
  436. let n = field_name fa in
  437. (match fa with
  438. | FAnon cf ->
  439. if Meta.has Meta.Optional cf.cf_meta then begin
  440. check_and_add_feature dce "anon_optional_read";
  441. check_and_add_feature dce ("anon_optional_read." ^ n);
  442. end else begin
  443. check_and_add_feature dce "anon_read";
  444. check_and_add_feature dce ("anon_read." ^ n);
  445. end
  446. | FDynamic _ ->
  447. check_and_add_feature dce "dynamic_read";
  448. check_and_add_feature dce ("dynamic_read." ^ n);
  449. | _ -> ());
  450. begin match follow e.etype with
  451. | TInst(c,_) ->
  452. mark_class dce c;
  453. field dce c n false;
  454. | TAnon a ->
  455. (match !(a.a_status) with
  456. | Statics c ->
  457. mark_class dce c;
  458. field dce c n true;
  459. | _ -> ())
  460. | _ -> ()
  461. end;
  462. end;
  463. expr dce e;
  464. | TThrow e ->
  465. check_and_add_feature dce "has_throw";
  466. to_string dce e.etype;
  467. expr dce e
  468. | _ ->
  469. Type.iter (expr dce) e
  470. let fix_accessors com =
  471. List.iter (fun mt -> match mt with
  472. | (TClassDecl c) ->
  473. let rec has_accessor c n stat =
  474. PMap.mem n (if stat then c.cl_statics else c.cl_fields)
  475. || match c.cl_super with Some (csup,_) -> has_accessor csup n stat | None -> false
  476. in
  477. let check_prop stat cf =
  478. (match cf.cf_kind with
  479. | Var {v_read = AccCall; v_write = a} ->
  480. let s = "get_" ^ cf.cf_name in
  481. cf.cf_kind <- Var {v_read = if has_accessor c s stat then AccCall else AccNever; v_write = a}
  482. | _ -> ());
  483. (match cf.cf_kind with
  484. | Var {v_write = AccCall; v_read = a} ->
  485. let s = "set_" ^ cf.cf_name in
  486. cf.cf_kind <- Var {v_write = if has_accessor c s stat then AccCall else AccNever; v_read = a}
  487. | _ -> ())
  488. in
  489. List.iter (check_prop true) c.cl_ordered_statics;
  490. List.iter (check_prop false) c.cl_ordered_fields;
  491. | _ -> ()
  492. ) com.types
  493. let run com main full =
  494. let dce = {
  495. com = com;
  496. full = full;
  497. std_dirs = if full then [] else List.map Common.unique_full_path com.std_path;
  498. debug = Common.defined com Define.DceDebug;
  499. added_fields = [];
  500. follow_expr = expr;
  501. marked_fields = [];
  502. marked_maybe_fields = [];
  503. t_stack = [];
  504. ts_stack = [];
  505. features = Hashtbl.create 0;
  506. curclass = null_class;
  507. } in
  508. begin match main with
  509. | Some {eexpr = TCall({eexpr = TField(e,(FStatic(c,cf)))},_)} ->
  510. cf.cf_meta <- (Meta.Keep,[],cf.cf_pos) :: cf.cf_meta
  511. | _ ->
  512. ()
  513. end;
  514. List.iter (fun m ->
  515. List.iter (fun (s,v) ->
  516. if Hashtbl.mem dce.features s then Hashtbl.replace dce.features s (v :: Hashtbl.find dce.features s)
  517. else Hashtbl.add dce.features s [v]
  518. ) m.m_extra.m_if_feature;
  519. ) com.modules;
  520. (* first step: get all entry points, which is the main method and all class methods which are marked with @:keep *)
  521. List.iter (fun t -> match t with
  522. | TClassDecl c ->
  523. let keep_class = keep_whole_class dce c && (not c.cl_extern || c.cl_interface) in
  524. let loop stat cf =
  525. if keep_class || keep_field dce cf then mark_field dce c cf stat
  526. in
  527. List.iter (loop true) c.cl_ordered_statics;
  528. List.iter (loop false) c.cl_ordered_fields;
  529. begin match c.cl_constructor with
  530. | Some cf -> loop false cf
  531. | None -> ()
  532. end;
  533. begin match c.cl_init with
  534. | Some e when keep_class || Meta.has Meta.KeepInit c.cl_meta ->
  535. (* create a fake field to deal with our internal logic (issue #3286) *)
  536. let cf = mk_field "__init__" e.etype e.epos in
  537. cf.cf_expr <- Some e;
  538. loop true cf
  539. | _ ->
  540. ()
  541. end;
  542. | TEnumDecl en when keep_whole_enum dce en ->
  543. mark_enum dce en
  544. | _ ->
  545. ()
  546. ) com.types;
  547. if dce.debug then begin
  548. List.iter (fun (c,cf,_) -> match cf.cf_expr with
  549. | None -> ()
  550. | Some _ -> print_endline ("[DCE] Entry point: " ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name)
  551. ) dce.added_fields;
  552. end;
  553. (* second step: initiate DCE passes and keep going until no new fields were added *)
  554. let rec loop () =
  555. match dce.added_fields with
  556. | [] -> ()
  557. | cfl ->
  558. dce.added_fields <- [];
  559. (* extend to dependent (= overriding/implementing) class fields *)
  560. List.iter (fun (c,cf,stat) -> mark_dependent_fields dce c cf.cf_name stat) cfl;
  561. (* mark fields as used *)
  562. List.iter (fun (c,cf,stat) ->
  563. if not (is_extern_field cf) then mark_class dce c;
  564. mark_field dce c cf stat;
  565. mark_t dce cf.cf_pos cf.cf_type
  566. ) cfl;
  567. (* follow expressions to new types/fields *)
  568. List.iter (fun (c,cf,_) ->
  569. dce.curclass <- c;
  570. opt (expr dce) cf.cf_expr;
  571. List.iter (fun cf -> if cf.cf_expr <> None then opt (expr dce) cf.cf_expr) cf.cf_overloads
  572. ) cfl;
  573. loop ()
  574. in
  575. loop ();
  576. (* third step: filter types *)
  577. let rec loop acc types =
  578. match types with
  579. | (TClassDecl c) as mt :: l when keep_whole_class dce c ->
  580. loop (mt :: acc) l
  581. | (TClassDecl c) as mt :: l ->
  582. let check_property cf stat =
  583. let add_accessor_metadata cf =
  584. if not (Meta.has Meta.Accessor cf.cf_meta) then cf.cf_meta <- (Meta.Accessor,[],c.cl_pos) :: cf.cf_meta
  585. in
  586. begin match cf.cf_kind with
  587. | Var {v_read = AccCall} ->
  588. begin try
  589. add_accessor_metadata (PMap.find ("get_" ^ cf.cf_name) (if stat then c.cl_statics else c.cl_fields))
  590. with Not_found ->
  591. ()
  592. end
  593. | _ ->
  594. ()
  595. end;
  596. begin match cf.cf_kind with
  597. | Var {v_write = AccCall} ->
  598. begin try
  599. add_accessor_metadata (PMap.find ("set_" ^ cf.cf_name) (if stat then c.cl_statics else c.cl_fields))
  600. with Not_found ->
  601. ()
  602. end
  603. | _ ->
  604. ()
  605. end;
  606. in
  607. (* add :keep so subsequent filter calls do not process class fields again *)
  608. c.cl_meta <- (Meta.Keep,[],c.cl_pos) :: c.cl_meta;
  609. c.cl_ordered_statics <- List.filter (fun cf ->
  610. let b = keep_field dce cf in
  611. if not b then begin
  612. if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
  613. check_property cf true;
  614. c.cl_statics <- PMap.remove cf.cf_name c.cl_statics;
  615. end;
  616. b
  617. ) c.cl_ordered_statics;
  618. c.cl_ordered_fields <- List.filter (fun cf ->
  619. let b = keep_field dce cf in
  620. if not b then begin
  621. if dce.debug then print_endline ("[DCE] Removed field " ^ (s_type_path c.cl_path) ^ "." ^ (cf.cf_name));
  622. check_property cf false;
  623. c.cl_fields <- PMap.remove cf.cf_name c.cl_fields;
  624. end;
  625. b
  626. ) c.cl_ordered_fields;
  627. (match c.cl_constructor with Some cf when not (keep_field dce cf) -> c.cl_constructor <- None | _ -> ());
  628. let inef cf = not (is_extern_field cf) in
  629. let has_non_extern_fields = List.exists inef c.cl_ordered_fields || List.exists inef c.cl_ordered_statics in
  630. (* we keep a class if it was used or has a used field *)
  631. if Meta.has Meta.Used c.cl_meta || has_non_extern_fields then loop (mt :: acc) l else begin
  632. (match c.cl_init with
  633. | Some f when Meta.has Meta.KeepInit c.cl_meta ->
  634. (* it means that we only need the __init__ block *)
  635. c.cl_extern <- true;
  636. loop (mt :: acc) l
  637. | _ ->
  638. if dce.debug then print_endline ("[DCE] Removed class " ^ (s_type_path c.cl_path));
  639. loop acc l)
  640. end
  641. | (TEnumDecl en) as mt :: l when Meta.has Meta.Used en.e_meta || en.e_extern || keep_whole_enum dce en ->
  642. loop (mt :: acc) l
  643. | TEnumDecl e :: l ->
  644. if dce.debug then print_endline ("[DCE] Removed enum " ^ (s_type_path e.e_path));
  645. loop acc l
  646. | mt :: l ->
  647. loop (mt :: acc) l
  648. | [] ->
  649. acc
  650. in
  651. com.types <- loop [] (List.rev com.types);
  652. (* extra step to adjust properties that had accessors removed (required for Php and Cpp) *)
  653. fix_accessors com;
  654. (* remove "override" from fields that do not override anything anymore *)
  655. List.iter (fun mt -> match mt with
  656. | TClassDecl c ->
  657. c.cl_overrides <- List.filter (fun s ->
  658. let rec loop c =
  659. match c.cl_super with
  660. | Some (csup,_) when PMap.mem s.cf_name csup.cl_fields -> true
  661. | Some (csup,_) -> loop csup
  662. | None -> false
  663. in
  664. loop c
  665. ) c.cl_overrides;
  666. | _ -> ()
  667. ) com.types;
  668. (* mark extern classes as really used if they are extended by non-extern ones *)
  669. List.iter (function
  670. | TClassDecl ({cl_extern = false; cl_super = Some ({cl_extern = true} as csup, _)}) ->
  671. mark_directly_used_class csup
  672. | TClassDecl ({cl_extern = false} as c) when c.cl_implements <> [] ->
  673. List.iter (fun (iface,_) -> if (iface.cl_extern) then mark_directly_used_class iface) c.cl_implements;
  674. | _ -> ()
  675. ) com.types;
  676. (* cleanup added fields metadata - compatibility with compilation server *)
  677. let rec remove_meta m = function
  678. | [] -> []
  679. | (m2,_,_) :: l when m = m2 -> l
  680. | x :: l -> x :: remove_meta m l
  681. in
  682. List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.Used cf.cf_meta) dce.marked_fields;
  683. List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.MaybeUsed cf.cf_meta) dce.marked_maybe_fields;