analyzer.ml 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696
  1. open Ast
  2. open Type
  3. open Common
  4. open Typecore
  5. let s_expr = s_expr (s_type (print_context()))
  6. let s_expr_pretty = s_expr_pretty "" (s_type (print_context()))
  7. let debug e = print_endline (s_expr e)
  8. let debug_pretty s e = Printf.printf "%s %s\n" s (s_expr_pretty e)
  9. let flag_no_check = "no_check"
  10. let flag_check = "check"
  11. let flag_no_const_propagation = "no_const_propagation"
  12. let flag_const_propagation = "const_propagation"
  13. let flag_no_local_dce = "no_local_dce"
  14. let flag_local_dce = "local_dce"
  15. let flag_ignore = "ignore"
  16. let flag_no_simplification = "no_simplification"
  17. let flag_check_has_effect = "check_has_effect"
  18. let flag_no_check_has_effect = "no_check_has_effect"
  19. let has_analyzer_option meta s =
  20. try
  21. let rec loop ml = match ml with
  22. | (Meta.Analyzer,el,_) :: ml ->
  23. if List.exists (fun (e,p) ->
  24. match e with
  25. | EConst(Ident s2) when s = s2 -> true
  26. | _ -> false
  27. ) el then
  28. true
  29. else
  30. loop ml
  31. | _ :: ml ->
  32. loop ml
  33. | [] ->
  34. false
  35. in
  36. loop meta
  37. with Not_found ->
  38. false
  39. let is_ignored meta =
  40. try
  41. let rec loop ml = match ml with
  42. | (Meta.Analyzer,el,_) :: ml ->
  43. if List.exists (fun (e,p) ->
  44. match e with
  45. | EConst(Ident s2) when flag_ignore = s2 -> true
  46. | _ -> false
  47. ) el then
  48. true
  49. else
  50. loop ml
  51. | (Meta.HasUntyped,_,_) :: _ ->
  52. true
  53. | _ :: ml ->
  54. loop ml
  55. | [] ->
  56. false
  57. in
  58. loop meta
  59. with Not_found ->
  60. false
  61. let rec get_type_meta t = match t with
  62. | TMono r ->
  63. begin match !r with
  64. | None -> raise Not_found
  65. | Some t -> get_type_meta t
  66. end
  67. | TLazy f ->
  68. get_type_meta (!f())
  69. | TInst(c,_) ->
  70. c.cl_meta
  71. | TEnum(en,_) ->
  72. en.e_meta
  73. | TAbstract(a,_) ->
  74. a.a_meta
  75. | TType(t,_) ->
  76. t.t_meta
  77. | TAnon _ | TFun _ | TDynamic _ ->
  78. raise Not_found
  79. let type_has_analyzer_option t s =
  80. try
  81. has_analyzer_option (get_type_meta t) s
  82. with Not_found ->
  83. false
  84. let is_enum_type t = match follow t with
  85. | TEnum(_) -> true
  86. | _ -> false
  87. let rec awkward_get_enum_index com e = match e.eexpr with
  88. | TArray(e1,{eexpr = TConst(TInt i)}) when com.platform = Js && Int32.to_int i = 1 && is_enum_type e1.etype ->
  89. e1
  90. | TCall({eexpr = TField(e1, FDynamic "__Index")},[]) when com.platform = Cpp && is_enum_type e1.etype ->
  91. e1
  92. | TField(e1,FDynamic "index") when com.platform = Neko && is_enum_type e1.etype ->
  93. e1
  94. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
  95. awkward_get_enum_index com e1
  96. | _ ->
  97. raise Not_found
  98. (*
  99. This module simplifies the AST by introducing temporary variables for complex expressions in many places.
  100. In particular, it ensures that no branching can occur in value-places so that we can later insert SSA PHI
  101. nodes without worrying about their placement.
  102. *)
  103. module Simplifier = struct
  104. let mk_block_context com =
  105. let block_el = ref [] in
  106. let push e = block_el := e :: !block_el in
  107. let assign ev e =
  108. let mk_assign e2 = match e2.eexpr with
  109. | TBreak | TContinue | TThrow _ | TReturn _ -> e2
  110. | _ -> mk (TBinop(OpAssign,ev,e2)) e2.etype e2.epos
  111. in
  112. let rec loop e = match e.eexpr with
  113. | TBlock el ->
  114. begin match List.rev el with
  115. | e1 :: el ->
  116. let el = List.rev ((loop e1) :: el) in
  117. {e with eexpr = TBlock el}
  118. | _ ->
  119. mk_assign e
  120. end
  121. | TIf(e1,e2,eo) ->
  122. let e2 = loop e2 in
  123. let eo = match eo with None -> None | Some e3 -> Some (loop e3) in
  124. {e with eexpr = TIf(e1,e2,eo)}
  125. | TSwitch(e1,cases,edef) ->
  126. let cases = List.map (fun (el,e) ->
  127. let e = loop e in
  128. el,e
  129. ) cases in
  130. let edef = match edef with None -> None | Some edef -> Some (loop edef) in
  131. {e with eexpr = TSwitch(e1,cases,edef)}
  132. | TTry(e1,catches) ->
  133. let e1 = loop e1 in
  134. let catches = List.map (fun (v,e) ->
  135. let e = loop e in
  136. v,e
  137. ) catches in
  138. {e with eexpr = TTry(e1,catches)}
  139. | TParenthesis e1 | TMeta(_,e1) ->
  140. loop e1 (* this is still weird, have to review *)
  141. (* | TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
  142. push e;
  143. mk_assign e1 *)
  144. (* | TBinop(OpAssignOp op,({eexpr = TLocal _} as e1),e2) ->
  145. push e;
  146. mk_assign e1 *)
  147. | _ ->
  148. mk_assign e
  149. in
  150. loop e
  151. in
  152. let declare_temp t eo p =
  153. let v = alloc_var "tmp" t in
  154. v.v_meta <- [Meta.CompilerGenerated,[],p];
  155. let e_v = mk (TLocal v) t p in
  156. let declare e_init =
  157. let e = mk (TVar (v,e_init)) com.basic.tvoid p in
  158. push e;
  159. in
  160. let e_v = match eo with
  161. | None ->
  162. declare None;
  163. e_v
  164. | Some e1 ->
  165. begin match e1.eexpr with
  166. | TThrow _ | TReturn _ | TBreak | TContinue ->
  167. e1
  168. | _ ->
  169. let rec loop e_v e = match e.eexpr with
  170. | TParenthesis e1 ->
  171. loop {e_v with eexpr = TParenthesis e_v} e1
  172. | TMeta(m,e1) ->
  173. loop {e_v with eexpr = TMeta(m,e_v)} e1
  174. | _ ->
  175. e_v,e
  176. in
  177. let e_v',e1 = loop e_v e1 in
  178. let e1 = assign e_v e1 in
  179. begin match e1.eexpr with
  180. | TBinop(OpAssign,{eexpr = TLocal v1},e2) when v == v1 ->
  181. declare (Some e2)
  182. | _ ->
  183. declare None;
  184. push e1
  185. end;
  186. e_v'
  187. end
  188. in
  189. e_v
  190. in
  191. let rec push_block () =
  192. let cur = !block_el in
  193. block_el := [];
  194. fun () ->
  195. let added = !block_el in
  196. block_el := cur;
  197. List.rev added
  198. and block f el =
  199. let close = push_block() in
  200. List.iter (fun e ->
  201. push (f e)
  202. ) el;
  203. close()
  204. in
  205. block,declare_temp,fun () -> !block_el
  206. let apply com e =
  207. let block,declare_temp,close_block = mk_block_context com in
  208. let skip_binding ?(allow_tlocal=false) e =
  209. let rec loop e =
  210. match e.eexpr with
  211. | TConst _ | TTypeExpr _ | TFunction _ -> ()
  212. | TLocal _ when allow_tlocal -> ()
  213. | TParenthesis e1 | TCast(e1,None) -> Type.iter loop e
  214. | TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
  215. | TField({eexpr = TLocal _},_) when allow_tlocal -> ()
  216. | TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
  217. | TCall({eexpr = TLocal { v_name = "__cpp__" } },_) -> ()
  218. | TField(_,FEnum _) -> ()
  219. | TField(_,FDynamic _) -> ()
  220. | _ when (try ignore(awkward_get_enum_index com e); true with Not_found -> false) -> ()
  221. | _ -> raise Exit
  222. in
  223. try
  224. loop e;
  225. true
  226. with Exit ->
  227. begin match follow e.etype with
  228. | TAbstract({a_path = [],"Void"},_) -> true
  229. (* | TInst ({ cl_path = [],"Array" }, _) when com.platform = Cpp -> true *)
  230. | _ -> false
  231. end
  232. in
  233. let has_unbound = ref false in
  234. let rec loop e = match e.eexpr with
  235. | TCall({eexpr = TLocal v | TField({eexpr = TLocal v},_)},_) | TField({eexpr = TLocal v},_) | TLocal v when Meta.has Meta.Unbound v.v_meta && v.v_name <> "`trace" ->
  236. has_unbound := true;
  237. e
  238. | TBlock el ->
  239. {e with eexpr = TBlock (block loop el)}
  240. | TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
  241. e
  242. | TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
  243. e
  244. | TCall(e1,el) ->
  245. let rec is_valid_call_target e = match e.eexpr with
  246. | TFunction _ | TField _ | TLocal _ | TConst (TSuper) ->
  247. true
  248. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
  249. is_valid_call_target e1
  250. | _ ->
  251. false
  252. in
  253. let e1 = if is_valid_call_target e1 then
  254. loop e1
  255. else
  256. bind e1
  257. in
  258. let check e t =
  259. if type_has_analyzer_option t flag_no_simplification then e
  260. else bind e
  261. in
  262. let el = match e1.eexpr,follow e1.etype with
  263. | TConst TSuper,_ when com.platform = Java || com.platform = Cs ->
  264. (* they hate you if you mess up the super call *)
  265. el
  266. | _,TFun _ | TConst TSuper,_ ->
  267. Codegen.UnificationCallback.check_call check el e1.etype
  268. | _ ->
  269. (* too dangerous *)
  270. List.map loop el
  271. in
  272. {e with eexpr = TCall(e1,el)}
  273. | TNew(c,tl,el) ->
  274. {e with eexpr = TNew(c,tl,ordered_list el)}
  275. | TArrayDecl el ->
  276. {e with eexpr = TArrayDecl (ordered_list el)}
  277. | TObjectDecl fl ->
  278. let el = ordered_list (List.map snd fl) in
  279. {e with eexpr = TObjectDecl (List.map2 (fun (n,_) e -> n,e) fl el)}
  280. | TBinop(OpBoolAnd | OpBoolOr as op,e1,e2) ->
  281. let e1 = loop e1 in
  282. let e_then = mk (TBlock (block loop [e2])) e2.etype e2.epos in
  283. let e_if,e_else = if op = OpBoolOr then
  284. mk (TUnop(Not,Prefix,e1)) com.basic.tbool e.epos,mk (TConst (TBool(true))) com.basic.tbool e.epos
  285. else
  286. e1,mk (TConst (TBool(false))) com.basic.tbool e.epos
  287. in
  288. loop (mk (TIf(e_if,e_then,Some e_else)) com.basic.tbool e.epos)
  289. | TBinop((OpAssign | OpAssignOp _) as op,{eexpr = TArray(e11,e12)},e2) ->
  290. let e1 = match ordered_list [e11;e12] with
  291. | [e1;e2] ->
  292. {e with eexpr = TArray(e1,e2)}
  293. | _ ->
  294. assert false
  295. in
  296. let e2 = bind e2 in
  297. {e with eexpr = TBinop(op,e1,e2)}
  298. | TBinop((OpAssign | OpAssignOp _) as op,e1,e2) ->
  299. let e2 = bind ~allow_tlocal:true e2 in
  300. let e1 = loop e1 in
  301. {e with eexpr = TBinop(op,e1,e2)}
  302. | TBinop(op,e1,e2) ->
  303. begin match ordered_list [e1;e2] with
  304. | [e1;e2] ->
  305. {e with eexpr = TBinop(op,e1,e2)}
  306. | _ ->
  307. assert false
  308. end
  309. | TArray(e1,e2) ->
  310. begin match ordered_list [e1;e2] with
  311. | [e1;e2] ->
  312. {e with eexpr = TArray(e1,e2)}
  313. | _ ->
  314. assert false
  315. end
  316. | TWhile(e1,e2,flag) when (match e1.eexpr with TConst(TBool true) | TParenthesis {eexpr = TConst(TBool true)} -> false | _ -> true) ->
  317. let p = e.epos in
  318. let e_break = mk TBreak t_dynamic p in
  319. let e_not = mk (TUnop(Not,Prefix,Codegen.mk_parent e1)) e1.etype e1.epos in
  320. let e_if eo = mk (TIf(e_not,e_break,eo)) com.basic.tvoid p in
  321. let rec map_continue e = match e.eexpr with
  322. | TContinue ->
  323. (e_if (Some e))
  324. | TWhile _ | TFor _ ->
  325. e
  326. | _ ->
  327. Type.map_expr map_continue e
  328. in
  329. let e2 = if flag = NormalWhile then e2 else map_continue e2 in
  330. let e_if = e_if None in
  331. let e_if = mk (TMeta((Meta.Custom ":whileCond",[],e_if.epos), e_if)) e_if.etype e_if.epos in
  332. let e_block = if flag = NormalWhile then Type.concat e_if e2 else Type.concat e2 e_if in
  333. let e_true = mk (TConst (TBool true)) com.basic.tbool p in
  334. let e = mk (TWhile(Codegen.mk_parent e_true,e_block,NormalWhile)) e.etype p in
  335. loop e
  336. | TFor(v,e1,e2) ->
  337. let e1 = bind e1 in
  338. let e2 = loop e2 in
  339. {e with eexpr = TFor(v,e1,e2)}
  340. | TIf(e1,e2,eo) ->
  341. let e1 = bind e1 in
  342. let e2 = loop e2 in
  343. let eo = match eo with None -> None | Some e -> Some (loop e) in
  344. {e with eexpr = TIf(e1,e2,eo)}
  345. | TSwitch (e1,cases,eo) ->
  346. let e1 = bind e1 in
  347. let cases = List.map (fun (el,e) ->
  348. let el = List.map loop el in
  349. let e = loop e in
  350. el,e
  351. ) cases in
  352. let eo = match eo with None -> None | Some e -> Some (loop e) in
  353. {e with eexpr = TSwitch(e1,cases,eo)}
  354. | TVar(v,Some e1) ->
  355. let e1 = match e1.eexpr with
  356. | TFunction _ -> loop e1
  357. | TArrayDecl [{eexpr = TFunction _}] -> loop e1
  358. | TNew(_,_,el) when not (List.exists Optimizer.has_side_effect el) -> loop e1 (* issue #4322 *)
  359. | _ -> bind ~allow_tlocal:true e1
  360. in
  361. {e with eexpr = TVar(v,Some e1)}
  362. | TUnop((Neg | NegBits | Not) as op,flag,e1) ->
  363. let e1 = bind e1 in
  364. {e with eexpr = TUnop(op,flag,e1)}
  365. | TField(e1,fa) ->
  366. let e1 = bind ~allow_tlocal:true e1 in
  367. {e with eexpr = TField(e1,fa)}
  368. | TReturn (Some ({eexpr = TThrow _ | TReturn _} as e1)) ->
  369. loop e1 (* this is a bit hackish *)
  370. | TReturn (Some e1) ->
  371. let e1 = bind e1 in
  372. {e with eexpr = TReturn (Some e1)}
  373. | TThrow e1 ->
  374. let e1 = bind e1 in
  375. {e with eexpr = TThrow e1}
  376. | TCast(e1,mto) ->
  377. let e1 = bind ~allow_tlocal:true e1 in
  378. {e with eexpr = TCast(e1,mto)}
  379. | _ ->
  380. Type.map_expr loop e
  381. and bind ?(allow_tlocal=false) e =
  382. let e = loop e in
  383. if skip_binding ~allow_tlocal e then
  384. e
  385. else
  386. declare_temp e.etype (Some e) e.epos
  387. and ordered_list el =
  388. if List.for_all (skip_binding ~allow_tlocal:true) el then
  389. List.map loop el
  390. else
  391. List.map bind el
  392. in
  393. let e = loop e in
  394. !has_unbound,match close_block() with
  395. | [] ->
  396. e
  397. | el ->
  398. mk (TBlock (List.rev (e :: el))) e.etype e.epos
  399. let unapply com e =
  400. let var_map = ref IntMap.empty in
  401. let rec get_assignment_to v e = match e.eexpr with
  402. | TBinop(OpAssign,{eexpr = TLocal v2},e2) when v == v2 -> Some e2
  403. | TBlock [e] -> get_assignment_to v e
  404. | TIf(e1,e2,Some e3) ->
  405. begin match get_assignment_to v e2,get_assignment_to v e3 with
  406. | Some e2,Some e3 -> Some ({e with eexpr = TIf(e1,e2,Some e3)})
  407. | _ -> None
  408. end
  409. | _ -> None
  410. in
  411. let if_or_op e e1 e2 e3 = match e1.eexpr,e3.eexpr with
  412. | TUnop(Not,Prefix,e1),TConst (TBool true) -> {e with eexpr = TBinop(OpBoolOr,e1,e2)}
  413. | _,TConst (TBool false) -> {e with eexpr = TBinop(OpBoolAnd,e1,e2)}
  414. | _ -> {e with eexpr = TIf(e1,e2,Some e3)}
  415. in
  416. let rec loop e = match e.eexpr with
  417. | TBlock el ->
  418. let rec loop2 el = match el with
  419. | e :: el ->
  420. begin match e.eexpr with
  421. | TVar(v,Some e1) when Meta.has Meta.CompilerGenerated v.v_meta ->
  422. if el = [] then
  423. [loop e1]
  424. else begin
  425. var_map := IntMap.add v.v_id (loop e1) !var_map;
  426. loop2 el
  427. end
  428. | TVar(v,None) when not (com.platform = Php) ->
  429. begin match el with
  430. | {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v == v2 ->
  431. let e = {e with eexpr = TVar(v,Some e2)} in
  432. loop2 (e :: el)
  433. | ({eexpr = TIf(e1,e2,Some e3)} as e_if) :: el ->
  434. let e1 = loop e1 in
  435. let e2 = loop e2 in
  436. let e3 = loop e3 in
  437. begin match get_assignment_to v e2,get_assignment_to v e3 with
  438. | Some e2,Some e3 ->
  439. let e_if = if_or_op e_if (loop e1) (loop e2) (loop e3) in
  440. let e = {e with eexpr = TVar(v,Some e_if)} in
  441. loop2 (e :: el)
  442. | _ ->
  443. let e_if = {e_if with eexpr = TIf(e1,e2,Some e3)} in
  444. e :: e_if :: loop2 el
  445. end
  446. | _ ->
  447. let e = loop e in
  448. e :: loop2 el
  449. end
  450. | TReturn (Some e1) when (match follow e1.etype with TAbstract({a_path=[],"Void"},_) -> true | _ -> false) ->
  451. [(loop e1);{e with eexpr = TReturn None}]
  452. | _ ->
  453. let e = loop e in
  454. e :: loop2 el
  455. end
  456. | [] ->
  457. []
  458. in
  459. let el = loop2 el in
  460. {e with eexpr = TBlock el}
  461. | TLocal v when Meta.has Meta.CompilerGenerated v.v_meta ->
  462. begin try IntMap.find v.v_id !var_map
  463. with Not_found -> e end
  464. | TWhile(e1,e2,flag) ->
  465. let e1 = loop e1 in
  466. let e2 = loop e2 in
  467. let extract_cond e = match e.eexpr with
  468. | TIf({eexpr = TUnop(Not,_,e1)},_,_) -> e1
  469. | TBreak -> raise Exit (* can happen due to optimization, not so easy to deal with because there might be other breaks/continues *)
  470. | _ -> assert false
  471. in
  472. let e1,e2,flag = try
  473. begin match e2.eexpr with
  474. | TBlock el ->
  475. begin match el with
  476. | {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
  477. let e1 = extract_cond e1 in
  478. e1,{e2 with eexpr = TBlock el},NormalWhile
  479. | _ ->
  480. e1,e2,flag
  481. (* issue 3844 *)
  482. (* begin match List.rev el with
  483. | {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
  484. let e1 = extract_cond e1 in
  485. e1,{e2 with eexpr = TBlock (List.rev el)},DoWhile
  486. | _ ->
  487. e1,e2,flag
  488. end *)
  489. end
  490. | _ ->
  491. e1,e2,flag
  492. end with Exit ->
  493. e1,e2,flag
  494. in
  495. {e with eexpr = TWhile(e1,e2,flag)}
  496. | TIf(e1,e2,Some e3) ->
  497. let e1 = loop e1 in
  498. let e2 = loop e2 in
  499. let e3 = loop e3 in
  500. if_or_op e e1 e2 e3
  501. | _ ->
  502. Type.map_expr loop e
  503. in
  504. loop e
  505. end
  506. module Ssa = struct
  507. type var_map = tvar IntMap.t
  508. type condition =
  509. | Equal of tvar * texpr
  510. | NotEqual of tvar * texpr
  511. type node_data = {
  512. nd_pos: pos;
  513. mutable nd_var_map : var_map;
  514. mutable nd_terminates : bool;
  515. }
  516. type join_node = {
  517. mutable branches : node_data list;
  518. }
  519. type ssa_context = {
  520. com : Common.context;
  521. mutable cleanup : (unit -> unit) list;
  522. mutable cur_data : node_data;
  523. mutable var_conds : (condition list) IntMap.t;
  524. mutable loop_stack : (join_node * join_node) list;
  525. mutable exception_stack : join_node list;
  526. mutable block_depth : int;
  527. }
  528. let s_cond = function
  529. | Equal(v,e) -> Printf.sprintf "%s == %s" v.v_name (s_expr_pretty e)
  530. | NotEqual(v,e) -> Printf.sprintf "%s != %s" v.v_name (s_expr_pretty e)
  531. let s_conds conds =
  532. String.concat " && " (List.map s_cond conds)
  533. let mk_loc v p = mk (TLocal v) v.v_type p
  534. let mk_phi =
  535. let v_phi = alloc_var "__ssa_phi__" t_dynamic in
  536. (fun vl p ->
  537. let e = mk (TCall(mk_loc v_phi p,(List.map (fun (v,p) -> mk_loc v p) vl))) t_dynamic p in
  538. e
  539. )
  540. (* TODO: make sure this is conservative *)
  541. let can_throw e =
  542. let rec loop e = match e.eexpr with
  543. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TBlock _ -> ()
  544. | TCall _ | TNew _ | TThrow _ | TCast(_,Some _) -> raise Exit
  545. | _ -> Type.iter loop e
  546. in
  547. try
  548. loop e; false
  549. with Exit ->
  550. true
  551. let mk_join_node() = {
  552. branches = []
  553. }
  554. let mk_node_data p = {
  555. nd_pos = p;
  556. nd_var_map = IntMap.empty;
  557. nd_terminates = false;
  558. }
  559. let add_branch join branch p =
  560. join.branches <- {branch with nd_pos = p} :: join.branches
  561. let branch ctx p =
  562. let old_map = ctx.cur_data.nd_var_map in
  563. let old_term = ctx.cur_data.nd_terminates in
  564. ctx.cur_data.nd_terminates <- false;
  565. (fun join ->
  566. add_branch join ctx.cur_data p;
  567. ctx.cur_data.nd_var_map <- old_map;
  568. ctx.cur_data.nd_terminates <- old_term;
  569. )
  570. let terminate ctx =
  571. ctx.cur_data.nd_terminates <- true
  572. let set_loop_join ctx join_top join_bottom =
  573. ctx.loop_stack <- (join_top,join_bottom) :: ctx.loop_stack;
  574. (fun () ->
  575. ctx.loop_stack <- List.tl ctx.loop_stack
  576. )
  577. let set_exception_join ctx join =
  578. ctx.exception_stack <- join :: ctx.exception_stack;
  579. (fun () ->
  580. ctx.exception_stack <- List.tl ctx.exception_stack;
  581. )
  582. let create_v_extra v =
  583. match v.v_extra with
  584. | Some (_,Some _) ->
  585. ()
  586. | Some (tl,None) ->
  587. let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
  588. v.v_extra <- Some (tl,Some e_extra)
  589. | None ->
  590. let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
  591. v.v_extra <- Some ([],Some e_extra)
  592. let set_v_extra_value v s e = match v.v_extra with
  593. | Some (tl, Some {eexpr = TObjectDecl fl}) ->
  594. let rec loop fl = match fl with
  595. | (s',_) :: fl when s' = s ->
  596. (s,e) :: fl
  597. | f1 :: fl ->
  598. f1 :: loop fl
  599. | [] ->
  600. [s,e]
  601. in
  602. let e_extra = mk (TObjectDecl (loop fl)) t_dynamic null_pos in
  603. v.v_extra <- Some (tl, Some e_extra)
  604. | _ ->
  605. assert false
  606. let get_origin_var v = match v.v_extra with
  607. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  608. begin match List.assoc "origin_var" fl with
  609. | {eexpr = TLocal v'} -> v'
  610. | _ -> raise Not_found
  611. end
  612. | _ ->
  613. raise Not_found
  614. let set_origin_var v v_origin p =
  615. let ev = mk_loc v_origin p in
  616. set_v_extra_value v "origin_var" ev
  617. let get_var_value v = match v.v_extra with
  618. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  619. List.assoc "var_value" fl
  620. | _ ->
  621. raise Not_found
  622. let set_var_value v e =
  623. set_v_extra_value v "var_value" e
  624. let get_var_usage_count v = match v.v_extra with
  625. | Some (_,Some {eexpr = TObjectDecl fl}) ->
  626. begin try
  627. begin match List.assoc "usage_count" fl with
  628. | {eexpr = TConst (TInt i32)} -> Int32.to_int i32
  629. | _ -> 0
  630. end
  631. with Not_found ->
  632. 0
  633. end
  634. | _ ->
  635. raise Not_found
  636. let set_var_usage_count v i =
  637. let e = mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos in
  638. set_v_extra_value v "usage_count" e
  639. let declare_var ctx v p =
  640. let old = v.v_extra in
  641. ctx.cleanup <- (fun () ->
  642. v.v_extra <- old
  643. ) :: ctx.cleanup;
  644. ctx.cur_data.nd_var_map <- IntMap.add v.v_id v ctx.cur_data.nd_var_map;
  645. v.v_meta <- ((Meta.Custom ":blockDepth",[EConst (Int (string_of_int ctx.block_depth)),p],p)) :: v.v_meta;
  646. v.v_extra <- None;
  647. create_v_extra v;
  648. set_origin_var v v p
  649. let assign_var ctx v e p =
  650. if v.v_capture then
  651. v
  652. else begin
  653. let i = match v.v_extra with
  654. | Some (l,eo) ->
  655. v.v_extra <- Some (("",t_dynamic) :: l,eo);
  656. List.length l + 1
  657. | _ ->
  658. error "Something went wrong" p
  659. in
  660. let v' = alloc_var (Printf.sprintf "%s<%i>" v.v_name i) v.v_type in
  661. create_v_extra v';
  662. v'.v_meta <- [(Meta.Custom ":ssa"),[],p];
  663. set_origin_var v' v p;
  664. ctx.cur_data.nd_var_map <- IntMap.add v.v_id v' ctx.cur_data.nd_var_map;
  665. set_var_value v' e;
  666. v'
  667. end
  668. let get_var ctx v p =
  669. try
  670. IntMap.find v.v_id ctx.cur_data.nd_var_map
  671. with Not_found ->
  672. if not (has_meta Meta.Unbound v.v_meta) then
  673. error (Printf.sprintf "Unbound variable %s" v.v_name) p;
  674. v
  675. let close_join_node ctx node p =
  676. let terminates = ref true in
  677. let branches = List.filter (fun branch ->
  678. if branch.nd_terminates then false
  679. else begin
  680. terminates := false;
  681. true
  682. end
  683. ) node.branches in
  684. match branches with
  685. | [] ->
  686. ()
  687. | branch :: branches ->
  688. let vars = ref (IntMap.map (fun v -> [v,branch.nd_pos]) branch.nd_var_map) in
  689. let rec handle_branch branch =
  690. IntMap.iter (fun i v ->
  691. try
  692. let vl = IntMap.find i !vars in
  693. if not (List.exists (fun (v',_) -> v == v') vl) then
  694. vars := IntMap.add i ((v,p) :: vl) !vars
  695. with Not_found ->
  696. ()
  697. ) branch.nd_var_map;
  698. in
  699. List.iter handle_branch branches;
  700. ctx.cur_data.nd_terminates <- !terminates;
  701. IntMap.iter (fun i vl -> match vl with
  702. | [v,p] ->
  703. ctx.cur_data.nd_var_map <- IntMap.add i v ctx.cur_data.nd_var_map;
  704. | (v',_) :: _ ->
  705. let v = get_origin_var v' in
  706. ignore(assign_var ctx v (mk_phi vl p) p)
  707. | _ ->
  708. assert false
  709. ) !vars
  710. let invert_cond = function
  711. | Equal(v,e) -> NotEqual(v,e)
  712. | NotEqual(v,e) -> Equal(v,e)
  713. let invert_conds =
  714. List.map invert_cond
  715. let rec eval_cond ctx e = match e.eexpr with
  716. | TBinop(OpNotEq,{eexpr = TLocal v},e1) ->
  717. [NotEqual(v,e1)]
  718. | TBinop(OpEq,{eexpr = TLocal v},e1) ->
  719. [Equal(v,e1)]
  720. | TUnop(Not,_,e1) ->
  721. invert_conds (eval_cond ctx e1)
  722. | TLocal v ->
  723. begin try eval_cond ctx (get_var_value v)
  724. with Not_found -> [] end
  725. | TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) ->
  726. eval_cond ctx e1
  727. | _ ->
  728. []
  729. let append_cond ctx v cond p =
  730. begin try
  731. let conds = IntMap.find v.v_id ctx.var_conds in
  732. ctx.var_conds <- IntMap.add v.v_id (cond :: conds) ctx.var_conds
  733. with Not_found ->
  734. ctx.var_conds <- IntMap.add v.v_id [cond] ctx.var_conds
  735. end
  736. let apply_cond ctx = function
  737. | Equal(v,e1) ->
  738. (* let v' = assign_var ctx (get_origin_var v) (mk_loc v e1.epos) e1.epos in
  739. append_cond ctx v' (Equal(v',e1)) e1.epos *)
  740. ()
  741. | NotEqual(v,e1) ->
  742. (* let v' = assign_var ctx (get_origin_var v) (mk_loc v e1.epos) e1.epos in
  743. append_cond ctx v' (NotEqual(v',e1)) e1.epos *)
  744. ()
  745. let apply_not_null_cond ctx v p =
  746. apply_cond ctx (NotEqual(v,(mk (TConst TNull) t_dynamic p)))
  747. let apply com e =
  748. let rec handle_if ctx f econd eif eelse =
  749. let econd = loop ctx econd in
  750. let cond = eval_cond ctx econd in
  751. let join = mk_join_node() in
  752. let close = branch ctx eif.epos in
  753. List.iter (apply_cond ctx) cond;
  754. let eif = loop ctx eif in
  755. close join;
  756. let eelse = match eelse with
  757. | None ->
  758. let cond = invert_conds cond in
  759. List.iter (apply_cond ctx) cond;
  760. add_branch join ctx.cur_data e.epos;
  761. None
  762. | Some e ->
  763. let close = branch ctx e.epos in
  764. let cond = invert_conds cond in
  765. List.iter (apply_cond ctx) cond;
  766. let eelse = loop ctx e in
  767. close join;
  768. Some eelse
  769. in
  770. close_join_node ctx join e.epos;
  771. f econd eif eelse
  772. and handle_loop_body ctx e =
  773. let join_top = mk_join_node() in
  774. let join_bottom = mk_join_node() in
  775. let unset = set_loop_join ctx join_top join_bottom in
  776. let close = branch ctx e.epos in
  777. ignore(loop ctx e); (* TODO: I don't know if this is sane. *)
  778. close join_top;
  779. add_branch join_top ctx.cur_data e.epos;
  780. close_join_node ctx join_top e.epos;
  781. let ebody = loop ctx e in
  782. ctx.cur_data.nd_terminates <- false;
  783. unset();
  784. close_join_node ctx join_bottom e.epos;
  785. ebody
  786. and loop ctx e = match e.eexpr with
  787. (* var declarations *)
  788. | TVar(v,eo) ->
  789. declare_var ctx v e.epos;
  790. let eo = match eo with
  791. | None -> None
  792. | Some e ->
  793. let e = loop ctx e in
  794. set_var_value v e;
  795. Some e
  796. in
  797. {e with eexpr = TVar(v,eo)}
  798. | TFunction tf ->
  799. let close = branch ctx e.epos in
  800. List.iter (fun (v,co) ->
  801. declare_var ctx v e.epos;
  802. match co with
  803. | Some TNull when (match v.v_type with TType({t_path=["haxe"],"PosInfos"},_) -> false | _ -> true) -> ()
  804. | _ -> apply_not_null_cond ctx v e.epos
  805. ) tf.tf_args;
  806. let e' = loop ctx tf.tf_expr in
  807. close (mk_join_node());
  808. {e with eexpr = TFunction {tf with tf_expr = e'}}
  809. (* var modifications *)
  810. | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
  811. let e2 = loop ctx e2 in
  812. let _ = assign_var ctx v e2 e1.epos in
  813. {e with eexpr = TBinop(OpAssign,e1,e2)}
  814. | TBinop(OpAssignOp op,({eexpr = TLocal v} as e1),e2) ->
  815. let e1 = loop ctx e1 in
  816. let e2 = loop ctx e2 in
  817. let e_op = mk (TBinop(op,e1,e2)) e.etype e.epos in
  818. let _ = assign_var ctx v e_op e1.epos in
  819. {e with eexpr = TBinop(OpAssignOp op,e1,e2)}
  820. | TUnop((Increment | Decrement as op),flag,({eexpr = TLocal v} as e1)) ->
  821. let op = match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false in
  822. let e_one = mk (TConst (TInt (Int32.of_int 1))) com.basic.tint e.epos in
  823. let e1 = loop ctx e1 in
  824. let e_op = mk (TBinop(op,e1,e_one)) e.etype e.epos in
  825. let _ = assign_var ctx v e_op e1.epos in
  826. e
  827. (* var user *)
  828. | TLocal v ->
  829. let v = get_var ctx v e.epos in
  830. {e with eexpr = TLocal v}
  831. (* control flow *)
  832. | TIf(econd,eif,eelse) ->
  833. let f econd eif eelse = {e with eexpr = TIf(econd,eif,eelse)} in
  834. handle_if ctx f econd eif eelse
  835. | TSwitch(e1,cases,edef) ->
  836. let e1 = loop ctx e1 in
  837. let join = mk_join_node() in
  838. let cases = List.map (fun (el,e) ->
  839. let close = branch ctx e.epos in
  840. let el = List.map (loop ctx) el in
  841. let e = loop ctx e in
  842. close join;
  843. el,e
  844. ) cases in
  845. let edef = match edef with
  846. | Some e ->
  847. let close = branch ctx e.epos in
  848. let e = loop ctx e in
  849. close join;
  850. Some e
  851. | None ->
  852. if not (Optimizer.is_exhaustive e1) then
  853. add_branch join ctx.cur_data e.epos;
  854. None
  855. in
  856. close_join_node ctx join e.epos;
  857. let e = {e with eexpr = TSwitch(e1,cases,edef)} in
  858. e
  859. | TWhile(econd,ebody,mode) ->
  860. let econd = loop ctx econd in
  861. let ebody = handle_loop_body ctx ebody in
  862. let e = {e with eexpr = TWhile(econd,ebody,mode)} in
  863. e
  864. | TFor(v,e1,ebody) ->
  865. declare_var ctx v e.epos;
  866. apply_not_null_cond ctx v e1.epos;
  867. let v' = IntMap.find v.v_id ctx.cur_data.nd_var_map in
  868. let e1 = loop ctx e1 in
  869. let ebody = handle_loop_body ctx ebody in
  870. let e = {e with eexpr = TFor(v',e1,ebody)} in
  871. e
  872. | TTry(e1,catches) ->
  873. let join_ex = mk_join_node() in
  874. let join_bottom = mk_join_node() in
  875. let unset = set_exception_join ctx join_ex in
  876. let e1 = loop ctx e1 in
  877. unset();
  878. add_branch join_bottom ctx.cur_data e.epos;
  879. close_join_node ctx join_ex e.epos;
  880. let catches = List.map (fun (v,e) ->
  881. declare_var ctx v e.epos;
  882. apply_not_null_cond ctx v e.epos;
  883. let close = branch ctx e.epos in
  884. let e = loop ctx e in
  885. close join_bottom;
  886. v,e
  887. ) catches in
  888. close_join_node ctx join_bottom e.epos;
  889. let e = {e with eexpr = TTry(e1,catches)} in
  890. e
  891. | TBreak ->
  892. begin match ctx.loop_stack with
  893. | [] -> error "Break outside loop" e.epos
  894. | (_,join) :: _ -> add_branch join ctx.cur_data e.epos
  895. end;
  896. terminate ctx;
  897. e
  898. | TContinue ->
  899. begin match ctx.loop_stack with
  900. | [] -> error "Continue outside loop" e.epos
  901. | (join,_) :: _ -> add_branch join ctx.cur_data e.epos
  902. end;
  903. terminate ctx;
  904. e
  905. | TThrow e1 ->
  906. let e1 = loop ctx e1 in
  907. begin match ctx.exception_stack with
  908. | join :: _ -> add_branch join ctx.cur_data e.epos
  909. | _ -> ()
  910. end;
  911. terminate ctx;
  912. {e with eexpr = TThrow e1}
  913. | TReturn eo ->
  914. let eo = match eo with None -> None | Some e -> Some (loop ctx e) in
  915. terminate ctx;
  916. {e with eexpr = TReturn eo}
  917. | TBlock el ->
  918. let rec loop2 el = match el with
  919. | [] ->
  920. []
  921. | e :: el ->
  922. if ctx.cur_data.nd_terminates then begin
  923. (* ctx.com.warning (Printf.sprintf "Unreachable code: %s" (s_expr_pretty e)) e.epos; *)
  924. []
  925. end else
  926. let e = loop ctx e in
  927. e :: (loop2 el)
  928. in
  929. ctx.block_depth <- ctx.block_depth + 1;
  930. let el = loop2 el in
  931. ctx.block_depth <- ctx.block_depth - 1;
  932. {e with eexpr = TBlock(el)}
  933. | _ ->
  934. begin match ctx.exception_stack with
  935. | join :: _ when can_throw e -> add_branch join ctx.cur_data e.epos
  936. | _ -> ()
  937. end;
  938. Type.map_expr (loop ctx) e
  939. in
  940. let ctx = {
  941. com = com;
  942. cur_data = mk_node_data e.epos;
  943. var_conds = IntMap.empty;
  944. loop_stack = [];
  945. exception_stack = [];
  946. cleanup = [];
  947. block_depth = 0;
  948. } in
  949. let e = loop ctx e in
  950. e,ctx
  951. let unapply com e =
  952. let rec loop e = match e.eexpr with
  953. | TFor(v,e1,e2) when Meta.has (Meta.Custom ":ssa") v.v_meta ->
  954. let v' = get_origin_var v in
  955. let e1 = loop e1 in
  956. let e2 = loop e2 in
  957. {e with eexpr = TFor(v',e1,e2)}
  958. | TLocal v when Meta.has (Meta.Custom ":ssa") v.v_meta ->
  959. let v' = get_origin_var v in
  960. {e with eexpr = TLocal v'}
  961. | TBlock el ->
  962. let rec filter e = match e.eexpr with
  963. | TMeta((Meta.Custom ":ssa",_,_),_) ->
  964. false
  965. | _ ->
  966. true
  967. in
  968. let el = List.filter filter el in
  969. let el = List.map loop el in
  970. {e with eexpr = TBlock el}
  971. | _ ->
  972. Type.map_expr loop e
  973. in
  974. loop e
  975. end
  976. module ConstPropagation = struct
  977. open Ssa
  978. let expr_eq e1 e2 = match e1.eexpr,e2.eexpr with
  979. | TConst ct1, TConst ct2 ->
  980. ct1 = ct2
  981. | _ ->
  982. false
  983. let get_block_depth v = try
  984. let i = match Meta.get (Meta.Custom ":blockDepth") v.v_meta with
  985. | _,[EConst(Int s),_],_ -> int_of_string s
  986. | _ -> raise Not_found
  987. in
  988. i
  989. with Not_found ->
  990. -1
  991. let rec can_be_inlined com v0 e = type_iseq_strict v0.v_type e.etype && match e.eexpr with
  992. | TConst ct ->
  993. begin match ct with
  994. | TThis | TSuper -> false
  995. (* Some targets don't like seeing null in certain places and won't even compile. We have to detect `if (x != null)
  996. in order for this to work. *)
  997. | TNull when (match com.platform with Php | Cpp -> true | _ -> false) -> false
  998. | _ -> true
  999. end
  1000. | TLocal v ->
  1001. not (Meta.has Meta.CompilerGenerated v.v_meta) &&
  1002. begin try
  1003. let v' = Ssa.get_origin_var v in
  1004. begin match v'.v_extra with
  1005. | Some ([],_) -> get_block_depth v <= get_block_depth v0
  1006. | _ -> false
  1007. end
  1008. with Not_found ->
  1009. false
  1010. end
  1011. | TEnumParameter _ when not (com.platform = Php) ->
  1012. Ssa.get_var_usage_count v0 <= 1
  1013. | TField(_,FEnum _) ->
  1014. Ssa.get_var_usage_count v0 <= 1
  1015. | TCast(e1,None) ->
  1016. (* We can inline an unsafe cast if the variable is only used once. *)
  1017. can_be_inlined com v0 {e1 with etype = e.etype} && Ssa.get_var_usage_count v0 <= 1
  1018. | _ ->
  1019. false
  1020. let semi_awkward_enum_value ssa e i = match e.eexpr with
  1021. | TCall({eexpr = TField(_,FEnum _)},el) -> (try List.nth el i with Failure _ -> raise Not_found)
  1022. | _ -> raise Not_found
  1023. let rec local ssa force v e =
  1024. begin try
  1025. if v.v_capture then raise Not_found;
  1026. if type_has_analyzer_option v.v_type flag_no_const_propagation then raise Not_found;
  1027. begin match follow v.v_type with
  1028. | TDynamic _ -> raise Not_found
  1029. | _ -> ()
  1030. end;
  1031. let e = Ssa.get_var_value v in
  1032. let old = v.v_extra in
  1033. v.v_extra <- None;
  1034. let e = value ssa force e in
  1035. v.v_extra <- old;
  1036. Ssa.set_var_value v e;
  1037. e
  1038. with Not_found ->
  1039. e
  1040. end
  1041. (* force must only be true if the value is not used in the output *)
  1042. and value ssa force e = match e.eexpr with
  1043. | TUnop((Increment | Decrement),_,_)
  1044. | TBinop(OpAssignOp _,_,_)
  1045. | TBinop(OpAssign,_,_) ->
  1046. e
  1047. | TBinop(op,e1,e2) ->
  1048. let e1 = value ssa force e1 in
  1049. let e2 = value ssa force e2 in
  1050. let e = {e with eexpr = TBinop(op,e1,e2)} in
  1051. let e' = Optimizer.optimize_binop e op e1 e2 in
  1052. if e == e' then
  1053. e
  1054. else
  1055. value ssa force e'
  1056. | TUnop(op,flag,e1) ->
  1057. let e1 = value ssa force e1 in
  1058. let e = {e with eexpr = TUnop(op,flag,e1)} in
  1059. let e' = Optimizer.optimize_unop e op flag e1 in
  1060. if e == e' then
  1061. e
  1062. else
  1063. value ssa force e'
  1064. | TCall ({ eexpr = TField ({eexpr = TTypeExpr (TClassDecl c) },fa)},el) ->
  1065. let el = List.map (value ssa force) el in
  1066. (match Optimizer.api_inline2 ssa.com c (field_name fa) el e.epos with
  1067. | None -> e
  1068. | Some e -> value ssa force e)
  1069. | TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}} as ephi),el) ->
  1070. let el = List.map (value ssa force) el in
  1071. begin match el with
  1072. | [] -> assert false
  1073. | e1 :: el ->
  1074. if List.for_all (fun e2 -> expr_eq e1 e2) el then
  1075. value ssa force e1
  1076. else
  1077. {e with eexpr = TCall(ephi, e1 :: el)}
  1078. end
  1079. | TParenthesis e1 | TMeta(_,e1) ->
  1080. value ssa force e1
  1081. | TLocal v ->
  1082. let e' = local ssa force v e in
  1083. if force || can_be_inlined ssa.com v e' then
  1084. e'
  1085. else
  1086. e
  1087. | TEnumParameter(e1,ef,i) ->
  1088. let ev = value ssa true e1 in
  1089. begin try
  1090. value ssa force (semi_awkward_enum_value ssa ev i)
  1091. with Not_found ->
  1092. e
  1093. end
  1094. | _ ->
  1095. try
  1096. let ct = awkward_get_enum_index2 ssa e in
  1097. {e with eexpr = TConst ct}
  1098. with Not_found ->
  1099. e
  1100. (* TODO: the name is quite accurate *)
  1101. and awkward_get_enum_index2 ssa e =
  1102. let e = awkward_get_enum_index ssa.com e in
  1103. let ev = (value ssa true e) in
  1104. match ev.eexpr with
  1105. | TField(_,FEnum(_,ef)) -> TInt (Int32.of_int ef.ef_index)
  1106. | TCall({eexpr = TField(_,FEnum(_,ef))},_) -> TInt (Int32.of_int ef.ef_index)
  1107. | _ -> raise Not_found
  1108. let apply ssa e =
  1109. let rec loop e = match e.eexpr with
  1110. | TLocal v when not (Meta.has Meta.Unbound v.v_meta) ->
  1111. set_var_usage_count v (get_var_usage_count v + 1);
  1112. | _ ->
  1113. Type.iter loop e
  1114. in
  1115. loop e;
  1116. let had_function = ref false in
  1117. let rec loop e = match e.eexpr with
  1118. | TFunction _ when !had_function ->
  1119. e
  1120. | TFunction tf ->
  1121. had_function := true;
  1122. {e with eexpr = TFunction {tf with tf_expr = loop tf.tf_expr}}
  1123. | TLocal v ->
  1124. let e' = local ssa false v e in
  1125. if can_be_inlined ssa.com v e' then
  1126. e'
  1127. else
  1128. e
  1129. | TCall({eexpr = TField(_,(FStatic(_,cf) | FInstance(_,_,cf) | FAnon cf))},el) when has_analyzer_option cf.cf_meta flag_no_const_propagation ->
  1130. e
  1131. | TCall(e1,el) ->
  1132. let e1 = loop e1 in
  1133. let check e t =
  1134. if type_has_analyzer_option t flag_no_const_propagation then e
  1135. else loop e
  1136. in
  1137. let el = Codegen.UnificationCallback.check_call check el e1.etype in
  1138. let e = {e with eexpr = TCall(e1,el)} in
  1139. begin match e1.eexpr with
  1140. | TField({eexpr = TTypeExpr (TClassDecl c)},fa) ->
  1141. begin match Optimizer.api_inline2 ssa.com c (field_name fa) el e.epos with
  1142. | None -> e
  1143. | Some e -> loop e
  1144. end
  1145. | _ ->
  1146. e
  1147. end
  1148. (* | TField(e1,fa) ->
  1149. let e1' = loop e1 in
  1150. let fa = if e1' != e1 then
  1151. begin try quick_field e1'.etype (field_name fa)
  1152. with Not_found -> fa end
  1153. else
  1154. fa
  1155. in
  1156. {e with eexpr = TField(e1',fa)} *)
  1157. | TUnop((Increment | Decrement),_,_) ->
  1158. e
  1159. | TBinop(OpAssignOp op,e1,e2) ->
  1160. let e2 = loop e2 in
  1161. {e with eexpr = TBinop(OpAssignOp op,e1,e2)}
  1162. | TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
  1163. let e2 = loop e2 in
  1164. {e with eexpr = TBinop(OpAssign,e1,e2)}
  1165. | TBinop(op,e1,e2) ->
  1166. let e1 = loop e1 in
  1167. let e2 = loop e2 in
  1168. let e = {e with eexpr = TBinop(op,e1,e2)} in
  1169. let e' = Optimizer.optimize_binop e op e1 e2 in
  1170. e'
  1171. | TUnop(op,flag,e1) ->
  1172. let e1 = loop e1 in
  1173. let e = {e with eexpr = TUnop(op,flag,e1)} in
  1174. let e' = Optimizer.optimize_unop e op flag e1 in
  1175. e'
  1176. | TIf(e1,e2,eo) ->
  1177. let e1 = loop e1 in
  1178. let e2 = loop e2 in
  1179. let rec check_const e1 = match e1.eexpr with
  1180. | TConst (TBool true) ->
  1181. e2
  1182. | TConst (TBool false) ->
  1183. begin match eo with
  1184. | None ->
  1185. mk (TConst TNull) t_dynamic e.epos
  1186. | Some e ->
  1187. loop e
  1188. end
  1189. | TParenthesis e1 ->
  1190. check_const e1
  1191. | _ ->
  1192. let eo = match eo with None -> None | Some e -> Some (loop e) in
  1193. {e with eexpr = TIf(e1,e2,eo)}
  1194. in
  1195. check_const e1
  1196. | TSwitch(e1,cases,edef) ->
  1197. let e1 = loop e1 in
  1198. let rec check_constant e = match e.eexpr with
  1199. | TConst ct -> ct
  1200. | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) -> check_constant e1
  1201. | _ -> raise Not_found
  1202. in
  1203. begin try
  1204. let ct = check_constant e1 in
  1205. begin try
  1206. let _,e = List.find (fun (el,_) ->
  1207. List.exists (fun e -> match e.eexpr with
  1208. | TConst ct2 -> ct = ct2
  1209. | _ -> false
  1210. ) el
  1211. ) cases in
  1212. loop e
  1213. with Not_found ->
  1214. begin match edef with None -> raise Not_found | Some e -> loop e end
  1215. end
  1216. with Not_found ->
  1217. let cases = List.map (fun (el,e) -> el,loop e) cases in
  1218. let edef = match edef with None -> None | Some e -> Some (loop e) in
  1219. {e with eexpr = TSwitch(e1,cases,edef)}
  1220. end
  1221. | _ ->
  1222. try
  1223. let ct = awkward_get_enum_index2 ssa e in
  1224. {e with eexpr = TConst ct}
  1225. with Not_found ->
  1226. Type.map_expr loop e
  1227. in
  1228. loop e
  1229. end
  1230. module EffectChecker = struct
  1231. let run com is_var_expression e =
  1232. let has_effect e = match e.eexpr with
  1233. | TVar _ -> true
  1234. | _ -> Optimizer.has_side_effect e
  1235. in
  1236. let e = if is_var_expression then
  1237. (* var initialization expressions are like assignments, so let's cheat a bit here *)
  1238. snd (Simplifier.apply com (Codegen.binop OpAssign (mk (TConst TNull) t_dynamic e.epos) e e.etype e.epos))
  1239. else e
  1240. in
  1241. let rec loop e = match e.eexpr with
  1242. | TBlock el ->
  1243. List.iter (fun e ->
  1244. if not (has_effect e) then com.warning "This expression has no effect" e.epos
  1245. ) el
  1246. | _ ->
  1247. Type.iter loop e
  1248. in
  1249. loop e
  1250. end
  1251. module Checker = struct
  1252. open Ssa
  1253. let apply ssa e =
  1254. let given_warnings = ref PMap.empty in
  1255. let add_pos p =
  1256. given_warnings := PMap.add p true !given_warnings
  1257. in
  1258. let resolve_value v =
  1259. let e' = Ssa.get_var_value v in
  1260. begin match e'.eexpr with
  1261. | TLocal v' when v == v' -> e'
  1262. | _ -> e'
  1263. end
  1264. in
  1265. let rec is_null_expr e = match e.eexpr with
  1266. | TConst TNull ->
  1267. true
  1268. | TLocal v ->
  1269. (try is_null_expr (resolve_value v) with Not_found -> false)
  1270. | _ ->
  1271. false
  1272. in
  1273. let can_be_null v =
  1274. not (has_meta Meta.NotNull v.v_meta)
  1275. && try not (List.exists (fun cond -> match cond with
  1276. | NotEqual(v',e) when v == v' && is_null_expr e -> true
  1277. | _ -> false
  1278. ) (IntMap.find v.v_id ssa.var_conds)) with Not_found -> true
  1279. in
  1280. let return b p =
  1281. if b then add_pos p;
  1282. b
  1283. in
  1284. let rec can_be_null_expr vstack e =
  1285. if PMap.mem e.epos !given_warnings then
  1286. false
  1287. else match e.eexpr with
  1288. | TConst TNull ->
  1289. add_pos e.epos;
  1290. true
  1291. | TBinop((OpAssign | OpAssignOp _),_,e1) ->
  1292. can_be_null_expr vstack e1
  1293. | TBinop _ | TUnop _ ->
  1294. false
  1295. | TConst _ | TTypeExpr _ | TNew _ | TObjectDecl _ | TArrayDecl _ | TEnumParameter _ | TFunction _ | TVar _ ->
  1296. false
  1297. | TFor _ | TWhile _ | TIf _ | TSwitch _ | TTry _ | TReturn _ | TBreak | TContinue | TThrow _ ->
  1298. assert false
  1299. | TField _ | TBlock _ | TArray _ ->
  1300. false (* TODO *)
  1301. | TCall ({eexpr = TLocal {v_name = "__ssa_phi__"}},el) ->
  1302. List.exists (can_be_null_expr vstack) el
  1303. | TLocal v ->
  1304. if List.mem v.v_id vstack then
  1305. false (* not really, but let's not be a nuisance *)
  1306. else
  1307. return (can_be_null v && (try can_be_null_expr (v.v_id :: vstack) (resolve_value v) with Not_found -> true)) e.epos;
  1308. | TMeta(_,e1) | TParenthesis e1 | TCast(e1,_) ->
  1309. can_be_null_expr vstack e1
  1310. | TCall(e1,_) ->
  1311. begin match follow e1.etype with
  1312. | TFun(_,r) -> return (is_explicit_null r) e1.epos
  1313. | _ -> false
  1314. end
  1315. in
  1316. let check_null e p =
  1317. if can_be_null_expr [] e then begin
  1318. ssa.com.warning "Possible null exception" p;
  1319. end
  1320. in
  1321. let rec loop e = match e.eexpr with
  1322. | TField(e1,fa) ->
  1323. let e1 = loop e1 in
  1324. check_null e1 e.epos;
  1325. {e with eexpr = TField(e1,fa)}
  1326. | TMeta((Meta.Analyzer,[EConst(Ident "testIsNull"),_],_),e1) ->
  1327. if not (can_be_null_expr [] e) then error "Analyzer did not find a possible null exception" e.epos;
  1328. e
  1329. | TMeta((Meta.Analyzer,[EConst(Ident "testIsNotNull"),_],_),e1) ->
  1330. if (can_be_null_expr [] e) then error "Analyzer found a possible null exception" e.epos;
  1331. e
  1332. | _ ->
  1333. Type.map_expr loop e
  1334. in
  1335. loop e;
  1336. end
  1337. let rec lrev_iter f el = match el with
  1338. | e :: el ->
  1339. lrev_iter f el;
  1340. f e
  1341. | [] ->
  1342. ()
  1343. let rev_iter f e = match e.eexpr with
  1344. | TConst _
  1345. | TLocal _
  1346. | TBreak
  1347. | TContinue
  1348. | TTypeExpr _ ->
  1349. ()
  1350. | TArray (e1,e2)
  1351. | TBinop (_,e1,e2)
  1352. | TFor (_,e1,e2)
  1353. | TWhile (e1,e2,_) ->
  1354. f e2;
  1355. f e1;
  1356. | TThrow e
  1357. | TField (e,_)
  1358. | TEnumParameter (e,_,_)
  1359. | TParenthesis e
  1360. | TCast (e,_)
  1361. | TUnop (_,_,e)
  1362. | TMeta(_,e) ->
  1363. f e
  1364. | TArrayDecl el
  1365. | TNew (_,_,el)
  1366. | TBlock el ->
  1367. lrev_iter f el
  1368. | TObjectDecl fl ->
  1369. lrev_iter (fun (_,e) -> f e) fl
  1370. | TCall (e,el) ->
  1371. f e;
  1372. lrev_iter f el
  1373. | TVar (v,eo) ->
  1374. (match eo with None -> () | Some e -> f e)
  1375. | TFunction fu ->
  1376. f fu.tf_expr
  1377. | TIf (e,e1,e2) ->
  1378. (match e2 with None -> () | Some e -> f e);
  1379. f e1;
  1380. f e;
  1381. | TSwitch (e,cases,def) ->
  1382. (match def with None -> () | Some e -> f e);
  1383. lrev_iter (fun (el,e2) -> lrev_iter f el; f e2) cases;
  1384. f e;
  1385. | TTry (e,catches) ->
  1386. lrev_iter (fun (_,e) -> f e) catches;
  1387. f e;
  1388. | TReturn eo ->
  1389. (match eo with None -> () | Some e -> f e)
  1390. module LocalDce = struct
  1391. let apply e =
  1392. let is_used v = Meta.has Meta.Used v.v_meta || type_has_analyzer_option v.v_type flag_no_local_dce || v.v_capture in
  1393. let is_ref_type t = match t with
  1394. | TType({t_path = ["cs"],("Ref" | "Out")},_) -> true
  1395. | _ -> false
  1396. in
  1397. let rec use v =
  1398. if not (Meta.has Meta.Used v.v_meta) then begin
  1399. v.v_meta <- (Meta.Used,[],Ast.null_pos) :: v.v_meta;
  1400. try use (Ssa.get_origin_var v) with Not_found -> ()
  1401. end
  1402. in
  1403. let rec has_side_effect e =
  1404. let rec loop e =
  1405. match e.eexpr with
  1406. | TLocal v when Meta.has Meta.CompilerGenerated v.v_meta -> (try loop (Ssa.get_var_value v) with Not_found -> ())
  1407. | TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) when is_used v || has_side_effect e2 || is_ref_type v.v_type -> raise Exit
  1408. | TVar(v,None) when is_used v -> raise Exit
  1409. | TVar(v,Some e1) when is_used v || has_side_effect e1 -> raise Exit
  1410. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
  1411. | TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
  1412. | TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}}),el) -> ()
  1413. | TCall ({eexpr = TField(_,FEnum _)},_) -> Type.iter loop e
  1414. | TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
  1415. | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
  1416. | TFor _ -> raise Exit
  1417. | TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _
  1418. | TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
  1419. in
  1420. try
  1421. loop e;
  1422. false
  1423. with Exit ->
  1424. true
  1425. in
  1426. let rec collect e = match e.eexpr with
  1427. | TLocal v ->
  1428. use v
  1429. | TVar(v,_) when not (is_used v) ->
  1430. (* TODO: this is probably dangerous *)
  1431. ()
  1432. | _ ->
  1433. rev_iter collect e
  1434. in
  1435. let rec loop need_val e =
  1436. match e.eexpr with
  1437. | TLocal v ->
  1438. use v;
  1439. e
  1440. | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
  1441. let e2 = loop false e2 in
  1442. if not (is_used v) && not (is_ref_type v.v_type) then
  1443. e2
  1444. else
  1445. {e with eexpr = TBinop(OpAssign,{e1 with eexpr = TLocal v},e2)}
  1446. | TVar(v,Some e1) when not (is_used v) ->
  1447. let e1 = if has_side_effect e1 then loop true e1 else e1 in
  1448. e1
  1449. | TVar(v,Some e1) ->
  1450. let e1 = loop true e1 in
  1451. {e with eexpr = TVar(v,Some e1)}
  1452. | TWhile(e1,e2,flag) ->
  1453. collect e2;
  1454. let e2 = loop false e2 in
  1455. let e1 = loop false e1 in
  1456. {e with eexpr = TWhile(e1,e2,flag)}
  1457. | TFor(v,e1,e2) ->
  1458. collect e2;
  1459. let e2 = loop false e2 in
  1460. let e1 = loop false e1 in
  1461. {e with eexpr = TFor(v,e1,e2)}
  1462. | TBlock el ->
  1463. let rec block el = match el with
  1464. | e :: el ->
  1465. let el = block el in
  1466. if not need_val && not (has_side_effect e) then
  1467. el
  1468. else begin
  1469. let e = loop false e in
  1470. e :: el
  1471. end
  1472. | [] ->
  1473. []
  1474. in
  1475. {e with eexpr = TBlock (block el)}
  1476. | TCall(e1, el) ->
  1477. let el = List.rev_map (loop true) (List.rev el) in
  1478. let e1 = loop false e1 in
  1479. {e with eexpr = TCall(e1,el)}
  1480. | TIf(e1,e2,e3) ->
  1481. let e3 = match e3 with None -> None | Some e -> Some (loop need_val e) in
  1482. let e2 = loop need_val e2 in
  1483. let e1 = loop false e1 in
  1484. {e with eexpr = TIf(e1,e2,e3)}
  1485. | TArrayDecl el ->
  1486. let el = List.rev_map (loop true) (List.rev el) in
  1487. {e with eexpr = TArrayDecl el}
  1488. | TObjectDecl fl ->
  1489. let fl = List.rev_map (fun (s,e) -> s,loop true e) (List.rev fl) in
  1490. {e with eexpr = TObjectDecl fl}
  1491. | _ ->
  1492. Type.map_expr (loop false) e
  1493. in
  1494. loop false e
  1495. end
  1496. module Config = struct
  1497. type analyzer_config = {
  1498. analyzer_use : bool;
  1499. simplifier_apply : bool;
  1500. ssa_apply : bool;
  1501. const_propagation : bool;
  1502. check : bool;
  1503. check_has_effect : bool;
  1504. local_dce : bool;
  1505. ssa_unapply : bool;
  1506. simplifier_unapply : bool;
  1507. }
  1508. let get_base_config com =
  1509. {
  1510. analyzer_use = true;
  1511. simplifier_apply = true;
  1512. ssa_apply = true;
  1513. const_propagation = not (Common.raw_defined com "analyzer-no-const-propagation");
  1514. check_has_effect = (Common.raw_defined com "analyzer-check-has-effect");
  1515. check = (Common.raw_defined com "analyzer-check-null");
  1516. local_dce = not (Common.raw_defined com "analyzer-no-local-dce") && not (Common.defined com Define.As3);
  1517. ssa_unapply = not (Common.raw_defined com "analyzer-no-ssa-unapply");
  1518. simplifier_unapply = not (Common.raw_defined com "analyzer-no-simplify-unapply");
  1519. }
  1520. let update_config_from_meta config meta =
  1521. List.fold_left (fun config meta -> match meta with
  1522. | (Meta.Analyzer,el,_) ->
  1523. List.fold_left (fun config e -> match fst e with
  1524. | EConst (Ident s) when s = flag_no_check -> { config with check = false}
  1525. | EConst (Ident s) when s = flag_check -> { config with check = true}
  1526. | EConst (Ident s) when s = flag_no_const_propagation -> { config with const_propagation = false}
  1527. | EConst (Ident s) when s = flag_const_propagation -> { config with const_propagation = true}
  1528. | EConst (Ident s) when s = flag_no_local_dce -> { config with local_dce = false}
  1529. | EConst (Ident s) when s = flag_local_dce -> { config with local_dce = true}
  1530. | EConst (Ident s) when s = flag_no_check_has_effect -> { config with check_has_effect = false}
  1531. | EConst (Ident s) when s = flag_check_has_effect -> { config with check_has_effect = true}
  1532. | _ -> config
  1533. ) config el
  1534. | _ ->
  1535. config
  1536. ) config meta
  1537. let get_class_config com c =
  1538. let config = get_base_config com in
  1539. update_config_from_meta config c.cl_meta
  1540. let get_field_config com c cf =
  1541. let config = get_class_config com c in
  1542. update_config_from_meta config cf.cf_meta
  1543. end
  1544. module Run = struct
  1545. open Config
  1546. let run_on_expr com config is_var_expression e =
  1547. let do_simplify = (not (Common.defined com Define.NoSimplify) ) && match com.platform with
  1548. | Cpp when Common.defined com Define.Cppia -> false
  1549. | Cpp | Python -> true
  1550. | _ -> false
  1551. in
  1552. let with_timer s f =
  1553. let timer = timer s in
  1554. let r = f() in
  1555. timer();
  1556. r
  1557. in
  1558. try
  1559. let has_unbound,e = if do_simplify || config.analyzer_use then
  1560. with_timer "analyzer-simplify-apply" (fun () -> Simplifier.apply com e)
  1561. else
  1562. false,e
  1563. in
  1564. let e = if config.analyzer_use && not has_unbound then begin
  1565. if config.check_has_effect then EffectChecker.run com is_var_expression e;
  1566. let e,ssa = with_timer "analyzer-ssa-apply" (fun () -> Ssa.apply com e) in
  1567. let e = if config.const_propagation then with_timer "analyzer-const-propagation" (fun () -> ConstPropagation.apply ssa e) else e in
  1568. (* let e = if config.check then with_timer "analyzer-checker" (fun () -> Checker.apply ssa e) else e in *)
  1569. let e = if config.local_dce && config.analyzer_use && not has_unbound && not is_var_expression then with_timer "analyzer-local-dce" (fun () -> LocalDce.apply e) else e in
  1570. let e = if config.ssa_unapply then with_timer "analyzer-ssa-unapply" (fun () -> Ssa.unapply com e) else e in
  1571. List.iter (fun f -> f()) ssa.Ssa.cleanup;
  1572. e
  1573. end else
  1574. e
  1575. in
  1576. let e = if not do_simplify && not (Common.raw_defined com "analyzer-no-simplify-unapply") then
  1577. with_timer "analyzer-simplify-unapply" (fun () -> Simplifier.unapply com e)
  1578. else
  1579. e
  1580. in
  1581. e
  1582. with Exit ->
  1583. e
  1584. let run_on_field ctx config cf =
  1585. match cf.cf_expr with
  1586. | Some e when not (is_ignored cf.cf_meta) && not (Codegen.is_removable_field ctx cf) ->
  1587. let config = update_config_from_meta config cf.cf_meta in
  1588. let is_var_expression = match cf.cf_kind with
  1589. | Var _ -> true
  1590. | _ -> false
  1591. in
  1592. cf.cf_expr <- Some (run_on_expr ctx.com config is_var_expression e);
  1593. | _ -> ()
  1594. let run_on_class ctx config c =
  1595. let config = update_config_from_meta config c.cl_meta in
  1596. let process_field cf = run_on_field ctx config cf in
  1597. List.iter process_field c.cl_ordered_fields;
  1598. List.iter process_field c.cl_ordered_statics;
  1599. (match c.cl_constructor with
  1600. | None -> ()
  1601. | Some f -> process_field f);
  1602. (match c.cl_init with
  1603. | None -> ()
  1604. | Some e ->
  1605. (* never optimize init expressions (too messy) *)
  1606. c.cl_init <- Some (run_on_expr ctx.com {config with analyzer_use = false} false e))
  1607. let run_on_type ctx config t =
  1608. match t with
  1609. | TClassDecl c when (is_ignored c.cl_meta) -> ()
  1610. | TClassDecl c -> run_on_class ctx config c
  1611. | TEnumDecl _ -> ()
  1612. | TTypeDecl _ -> ()
  1613. | TAbstractDecl _ -> ()
  1614. let run_on_types ctx types =
  1615. let com = ctx.com in
  1616. let config = get_base_config com in
  1617. List.iter (run_on_type ctx config) types
  1618. end