dce.ml 24 KB

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