2
0

optimizer.ml 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741
  1. (*
  2. The Haxe Compiler
  3. Copyright (C) 2005-2016 Haxe Foundation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. *)
  16. open Ast
  17. open Type
  18. open Common
  19. open Typecore
  20. (* ---------------------------------------------------------------------- *)
  21. (* API OPTIMIZATIONS *)
  22. (* tells if an expression causes side effects. This does not account for potential null accesses (fields/arrays/ops) *)
  23. let has_side_effect e =
  24. let rec loop e =
  25. match e.eexpr with
  26. | TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
  27. | TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
  28. | TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
  29. | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
  30. | TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _ | TFor _
  31. | TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
  32. in
  33. try
  34. loop e; false
  35. with Exit ->
  36. true
  37. let rec is_exhaustive e1 = match e1.eexpr with
  38. | TMeta((Meta.Exhaustive,_,_),_) -> true
  39. | TMeta(_, e1) | TParenthesis e1 -> is_exhaustive e1
  40. | _ -> false
  41. let mk_untyped_call name p params =
  42. {
  43. eexpr = TCall({ eexpr = TLocal(alloc_unbound_var name t_dynamic); etype = t_dynamic; epos = p }, params);
  44. etype = t_dynamic;
  45. epos = p;
  46. }
  47. let api_inline2 com c field params p =
  48. match c.cl_path, field, params with
  49. | ([],"Type"),"enumIndex",[{ eexpr = TField (_,FEnum (en,f)) }] -> (match com.platform with
  50. | Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
  51. (* We don't want to optimize enums from external sources; as they might change unexpectedly *)
  52. (* and since native C# enums don't have the concept of index - they have rather a value, *)
  53. (* which can't be mapped to a native API - this kind of substitution is dangerous *)
  54. None
  55. | _ ->
  56. Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
  57. | ([],"Type"),"enumIndex",[{ eexpr = TCall({ eexpr = TField (_,FEnum (en,f)) },pl) }] when List.for_all (fun e -> not (has_side_effect e)) pl ->
  58. (match com.platform with
  59. | Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
  60. (* see comment above *)
  61. None
  62. | _ ->
  63. Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
  64. | ([],"Std"),"int",[{ eexpr = TConst (TInt _) } as e] ->
  65. Some { e with epos = p }
  66. | ([],"String"),"fromCharCode",[{ eexpr = TConst (TInt i) }] when i > 0l && i < 128l ->
  67. Some (mk (TConst (TString (String.make 1 (char_of_int (Int32.to_int i))))) com.basic.tstring p)
  68. | ([],"Std"),"string",[{ eexpr = TConst c } as e] ->
  69. (match c with
  70. | TString s ->
  71. Some { e with epos = p }
  72. | TInt i ->
  73. Some { eexpr = TConst (TString (Int32.to_string i)); epos = p; etype = com.basic.tstring }
  74. | TBool b ->
  75. Some { eexpr = TConst (TString (if b then "true" else "false")); epos = p; etype = com.basic.tstring }
  76. | _ ->
  77. None)
  78. | ([],"Std"),"string",[{ eexpr = TIf (_,{ eexpr = TConst (TString _)},Some { eexpr = TConst (TString _) }) } as e] ->
  79. Some e
  80. | ([],"Std"),"string",[{ eexpr = TLocal v | TField({ eexpr = TLocal v },_) } as ev] when (com.platform = Js || com.platform = Flash) && not (Meta.has Meta.CompilerGenerated v.v_meta) ->
  81. let pos = ev.epos in
  82. let stringv() =
  83. let to_str = mk (TBinop (Ast.OpAdd, mk (TConst (TString "")) com.basic.tstring pos, ev)) com.basic.tstring pos in
  84. if com.platform = Js || is_nullable ev.etype then
  85. let chk_null = mk (TBinop (Ast.OpEq, ev, mk (TConst TNull) t_dynamic pos)) com.basic.tbool pos in
  86. mk (TIf (chk_null, mk (TConst (TString "null")) com.basic.tstring pos, Some to_str)) com.basic.tstring pos
  87. else
  88. to_str
  89. in
  90. (match follow ev.etype with
  91. | TInst ({ cl_path = [],"String" }, []) ->
  92. Some (stringv())
  93. | TAbstract ({ a_path = [],"Float" }, []) ->
  94. Some (stringv())
  95. | TAbstract ({ a_path = [],"Int" }, []) ->
  96. Some (stringv())
  97. | TAbstract ({ a_path = [],"UInt" }, []) ->
  98. Some (stringv())
  99. | TAbstract ({ a_path = [],"Bool" }, []) ->
  100. Some (stringv())
  101. | _ ->
  102. None)
  103. | ([],"Std"),"int",[{ eexpr = TConst (TFloat f) }] ->
  104. let f = float_of_string f in
  105. (match classify_float f with
  106. | FP_infinite | FP_nan ->
  107. None
  108. | _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int +. 1. ->
  109. None (* out range, keep platform-specific behavior *)
  110. | _ ->
  111. Some { eexpr = TConst (TInt (Int32.of_float f)); etype = com.basic.tint; epos = p })
  112. | ([],"Math"),"ceil",[{ eexpr = TConst (TFloat f) }] ->
  113. let f = float_of_string f in
  114. (match classify_float f with
  115. | FP_infinite | FP_nan ->
  116. None
  117. | _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int ->
  118. None (* out range, keep platform-specific behavior *)
  119. | _ ->
  120. Some { eexpr = TConst (TInt (Int32.of_float (ceil f))); etype = com.basic.tint; epos = p })
  121. | ([],"Math"),"floor",[{ eexpr = TConst (TFloat f) }] ->
  122. let f = float_of_string f in
  123. (match classify_float f with
  124. | FP_infinite | FP_nan ->
  125. None
  126. | _ when f <= Int32.to_float Int32.min_int || f >= Int32.to_float Int32.max_int +. 1. ->
  127. None (* out range, keep platform-specific behavior *)
  128. | _ ->
  129. Some { eexpr = TConst (TInt (Int32.of_float (floor f))); etype = com.basic.tint; epos = p })
  130. | (["cs"],"Lib"),("fixed" | "checked" | "unsafe"),[e] ->
  131. Some (mk_untyped_call ("__" ^ field ^ "__") p [e])
  132. | (["cs"],"Lib"),("lock"),[obj;block] ->
  133. Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
  134. | (["java"],"Lib"),("lock"),[obj;block] ->
  135. Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
  136. | _ ->
  137. None
  138. let api_inline ctx c field params p = match c.cl_path, field, params with
  139. | ([],"Std"),"is",[o;t] | (["js"],"Boot"),"__instanceof",[o;t] when ctx.com.platform = Js ->
  140. let mk_local ctx n t pos =
  141. mk (TLocal (try
  142. PMap.find n ctx.locals
  143. with _ ->
  144. let v = add_local ctx n t in
  145. v.v_meta <- [Meta.Unbound,[],p];
  146. v
  147. )) t pos in
  148. let tstring = ctx.com.basic.tstring in
  149. let tbool = ctx.com.basic.tbool in
  150. let tint = ctx.com.basic.tint in
  151. let is_trivial e =
  152. match e.eexpr with
  153. | TConst _ | TLocal _ -> true
  154. | _ -> false
  155. in
  156. let typeof t =
  157. let tof = mk_local ctx "__typeof__" (tfun [o.etype] tstring) p in
  158. let tof = mk (TCall (tof, [o])) tstring p in
  159. mk (TBinop (Ast.OpEq, tof, (mk (TConst (TString t)) tstring p))) tbool p
  160. in
  161. (match t.eexpr with
  162. (* generate simple typeof checks for basic types *)
  163. | TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
  164. | TTypeExpr (TAbstractDecl ({ a_path = [],"Bool" })) -> Some (typeof "boolean")
  165. | TTypeExpr (TAbstractDecl ({ a_path = [],"Float" })) -> Some (typeof "number")
  166. | TTypeExpr (TAbstractDecl ({ a_path = [],"Int" })) when is_trivial o ->
  167. (* generate (o|0) === o check *)
  168. let teq = mk_local ctx "__strict_eq__" (tfun [tint; tint] tbool) p in
  169. let lhs = mk (TBinop (Ast.OpOr, o, mk (TConst (TInt Int32.zero)) tint p)) tint p in
  170. Some (mk (TCall (teq, [lhs; o])) tbool p)
  171. | TTypeExpr (TClassDecl ({ cl_path = [],"Array" })) ->
  172. (* generate (o instanceof Array) && o.__enum__ == null check *)
  173. let iof = mk_local ctx "__instanceof__" (tfun [o.etype;t.etype] tbool) p in
  174. let iof = mk (TCall (iof, [o; t])) tbool p in
  175. let enum = mk (TField (o, FDynamic "__enum__")) (mk_mono()) p in
  176. let null = mk (TConst TNull) (mk_mono()) p in
  177. let not_enum = mk (TBinop (Ast.OpEq, enum, null)) tbool p in
  178. Some (mk (TBinop (Ast.OpBoolAnd, iof, not_enum)) tbool p)
  179. | _ ->
  180. None)
  181. | (["cs" | "java"],"Lib"),("nativeArray"),[{ eexpr = TArrayDecl args } as edecl; _]
  182. | (["haxe";"ds";"_Vector"],"Vector_Impl_"),("fromArrayCopy"),[{ eexpr = TArrayDecl args } as edecl] -> (try
  183. let platf = match ctx.com.platform with
  184. | Cs -> "cs"
  185. | Java -> "java"
  186. | _ -> raise Exit
  187. in
  188. let mpath = if field = "fromArrayCopy" then
  189. (["haxe";"ds"],"Vector")
  190. else
  191. ([platf],"NativeArray")
  192. in
  193. let m = ctx.g.do_load_module ctx mpath null_pos in
  194. let main = List.find (function | TClassDecl _ | TAbstractDecl _ -> true | _ -> false) m.m_types in
  195. let t = match follow edecl.etype, main with
  196. | TInst({ cl_path = [],"Array" }, [t]), TClassDecl(cl) ->
  197. TInst(cl,[t])
  198. | TInst({ cl_path = [],"Array" }, [t]), TAbstractDecl(a) ->
  199. TAbstract(a,[t])
  200. | _ -> assert false
  201. in
  202. Some ({ (mk_untyped_call "__array__" p args) with etype = t })
  203. with | Exit ->
  204. None)
  205. | _ ->
  206. api_inline2 ctx.com c field params p
  207. let rec is_affected_type t = match follow t with
  208. | TAbstract({a_path = [],("Int" | "Float" | "Bool")},_) -> true
  209. | TAbstract({a_path = ["haxe"],("Int64" | "Int32")},_) -> true
  210. | TAbstract(a,tl) -> is_affected_type (Abstract.get_underlying_type a tl)
  211. | TDynamic _ -> true (* sadly *)
  212. | _ -> false
  213. let create_affection_checker () =
  214. let modified_locals = Hashtbl.create 0 in
  215. let rec might_be_affected e =
  216. let rec loop e = match e.eexpr with
  217. | TConst _ | TFunction _ | TTypeExpr _ -> ()
  218. | TLocal v when Hashtbl.mem modified_locals v.v_id -> raise Exit
  219. | TField _ when is_affected_type e.etype -> raise Exit
  220. | _ -> Type.iter loop e
  221. in
  222. try
  223. loop e;
  224. false
  225. with Exit ->
  226. true
  227. in
  228. let rec collect_modified_locals e = match e.eexpr with
  229. | TUnop((Increment | Decrement),_,{eexpr = TLocal v}) when is_affected_type v.v_type ->
  230. Hashtbl.add modified_locals v.v_id true
  231. | TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) when is_affected_type v.v_type ->
  232. collect_modified_locals e2;
  233. Hashtbl.add modified_locals v.v_id true
  234. | _ ->
  235. Type.iter collect_modified_locals e
  236. in
  237. might_be_affected,collect_modified_locals
  238. (* ---------------------------------------------------------------------- *)
  239. (* INLINING *)
  240. type in_local = {
  241. i_var : tvar;
  242. i_subst : tvar;
  243. mutable i_captured : bool;
  244. mutable i_write : bool;
  245. mutable i_read : int;
  246. mutable i_force_temp : bool;
  247. }
  248. let inline_default_config cf t =
  249. (* type substitution on both class and function type parameters *)
  250. let rec get_params c pl =
  251. match c.cl_super with
  252. | None -> c.cl_params, pl
  253. | Some (csup,spl) ->
  254. let spl = (match apply_params c.cl_params pl (TInst (csup,spl)) with
  255. | TInst (_,pl) -> pl
  256. | _ -> assert false
  257. ) in
  258. let ct, cpl = get_params csup spl in
  259. c.cl_params @ ct, pl @ cpl
  260. in
  261. let tparams = (match follow t with
  262. | TInst (c,pl) -> get_params c pl
  263. | _ -> ([],[]))
  264. in
  265. let pmonos = List.map (fun _ -> mk_mono()) cf.cf_params in
  266. let tmonos = snd tparams @ pmonos in
  267. let tparams = fst tparams @ cf.cf_params in
  268. tparams <> [], apply_params tparams tmonos
  269. let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=false) force =
  270. (* perform some specific optimization before we inline the call since it's not possible to detect at final optimization time *)
  271. try
  272. let cl = (match follow ethis.etype with
  273. | TInst (c,_) -> c
  274. | TAnon a -> (match !(a.a_status) with Statics c -> c | _ -> raise Exit)
  275. | _ -> raise Exit
  276. ) in
  277. (match api_inline ctx cl cf.cf_name params p with
  278. | None -> raise Exit
  279. | Some e -> Some e)
  280. with Exit ->
  281. let has_params,map_type = match config with Some config -> config | None -> inline_default_config cf ethis.etype in
  282. (* locals substitution *)
  283. let locals = Hashtbl.create 0 in
  284. let local v =
  285. try
  286. Hashtbl.find locals v.v_id
  287. with Not_found ->
  288. let v' = alloc_var v.v_name v.v_type in
  289. if Meta.has Meta.Unbound v.v_meta then v'.v_meta <- [Meta.Unbound,[],p];
  290. let i = {
  291. i_var = v;
  292. i_subst = v';
  293. i_captured = false;
  294. i_write = false;
  295. i_force_temp = false;
  296. i_read = 0;
  297. } in
  298. i.i_subst.v_meta <- v.v_meta;
  299. Hashtbl.add locals v.v_id i;
  300. Hashtbl.add locals i.i_subst.v_id i;
  301. i
  302. in
  303. let in_local_fun = ref false in
  304. let read_local v =
  305. let l = try
  306. Hashtbl.find locals v.v_id
  307. with Not_found ->
  308. (* make sure to duplicate unbound inline variable to prevent dependency leak when unifying monomorph *)
  309. if has_meta Meta.Unbound v.v_meta then local v else
  310. {
  311. i_var = v;
  312. i_subst = v;
  313. i_captured = false;
  314. i_write = false;
  315. i_force_temp = false;
  316. i_read = 0;
  317. }
  318. in
  319. if !in_local_fun then l.i_captured <- true;
  320. l
  321. in
  322. (* use default values for null/unset arguments *)
  323. let rec loop pl al first =
  324. match pl, al with
  325. | _, [] -> []
  326. | e :: pl, (v, opt) :: al ->
  327. (*
  328. if we pass a Null<T> var to an inlined method that needs a T.
  329. we need to force a local var to be created on some platforms.
  330. *)
  331. if ctx.com.config.pf_static && not (is_nullable v.v_type) && is_null e.etype then (local v).i_force_temp <- true;
  332. (*
  333. if we cast from Dynamic, create a local var as well to do the cast
  334. once and allow DCE to perform properly.
  335. *)
  336. let e = if v.v_type != t_dynamic && follow e.etype == t_dynamic then mk (TCast(e,None)) v.v_type e.epos else e in
  337. (match e.eexpr, opt with
  338. | TConst TNull , Some c -> mk (TConst c) v.v_type e.epos
  339. (*
  340. This is really weird and should be reviewed again. The problem is that we cannot insert a TCast here because
  341. the abstract `this` value could be written to, which is not possible if it is wrapped in a cast.
  342. The original problem here is that we do not generate a temporary variable and thus mute the type of the
  343. `this` variable, which leads to unification errors down the line. See issues #2236 and #3713.
  344. *)
  345. (* | _ when first && (Meta.has Meta.Impl cf.cf_meta) -> {e with etype = v.v_type} *)
  346. | _ -> e) :: loop pl al false
  347. | [], (v,opt) :: al ->
  348. (mk (TConst (match opt with None -> TNull | Some c -> c)) v.v_type p) :: loop [] al false
  349. in
  350. (*
  351. Build the expr/var subst list
  352. *)
  353. let ethis = (match ethis.eexpr with TConst TSuper -> { ethis with eexpr = TConst TThis } | _ -> ethis) in
  354. let vthis = alloc_var "_this" ethis.etype in
  355. let might_be_affected,collect_modified_locals = create_affection_checker() in
  356. let had_side_effect = ref false in
  357. let inlined_vars = List.map2 (fun e (v,_) ->
  358. let l = local v in
  359. if has_side_effect e then begin
  360. collect_modified_locals e;
  361. had_side_effect := true;
  362. l.i_force_temp <- true;
  363. end;
  364. l, e
  365. ) (ethis :: loop params f.tf_args true) ((vthis,None) :: f.tf_args) in
  366. List.iter (fun (l,e) ->
  367. if might_be_affected e then l.i_force_temp <- true;
  368. ) inlined_vars;
  369. let inlined_vars = List.rev inlined_vars in
  370. (*
  371. here, we try to eliminate final returns from the expression tree.
  372. However, this is not entirely correct since we don't yet correctly propagate
  373. the type of returned expressions upwards ("return" expr itself being Dynamic).
  374. We also substitute variables with fresh ones that might be renamed at later stage.
  375. *)
  376. let opt f = function
  377. | None -> None
  378. | Some e -> Some (f e)
  379. in
  380. let has_vars = ref false in
  381. let in_loop = ref false in
  382. let cancel_inlining = ref false in
  383. let has_return_value = ref false in
  384. let ret_val = (match follow f.tf_type with TAbstract ({ a_path = ([],"Void") },[]) -> false | _ -> true) in
  385. let map_pos = if self_calling_closure then (fun e -> e) else (fun e -> { e with epos = p }) in
  386. let rec map term e =
  387. let po = e.epos in
  388. let e = map_pos e in
  389. match e.eexpr with
  390. | TLocal v ->
  391. let l = read_local v in
  392. l.i_read <- l.i_read + (if !in_loop then 2 else 1);
  393. (* never inline a function which contain a delayed macro because its bound
  394. to its variables and not the calling method *)
  395. if v.v_name = "__dollar__delay_call" then cancel_inlining := true;
  396. let e = { e with eexpr = TLocal l.i_subst } in
  397. if Meta.has Meta.This v.v_meta then mk (TCast(e,None)) v.v_type e.epos else e
  398. | TConst TThis ->
  399. let l = read_local vthis in
  400. l.i_read <- l.i_read + (if !in_loop then 2 else 1);
  401. { e with eexpr = TLocal l.i_subst }
  402. | TVar (v,eo) ->
  403. has_vars := true;
  404. { e with eexpr = TVar ((local v).i_subst,opt (map false) eo)}
  405. | TReturn eo when not !in_local_fun ->
  406. if not term then error "Cannot inline a not final return" po;
  407. (match eo with
  408. | None -> mk (TConst TNull) f.tf_type p
  409. | Some e ->
  410. has_return_value := true;
  411. map term e)
  412. | TFor (v,e1,e2) ->
  413. let i = local v in
  414. let e1 = map false e1 in
  415. let old = !in_loop in
  416. in_loop := true;
  417. let e2 = map false e2 in
  418. in_loop := old;
  419. { e with eexpr = TFor (i.i_subst,e1,e2) }
  420. | TWhile (cond,eloop,flag) ->
  421. let cond = map false cond in
  422. let old = !in_loop in
  423. in_loop := true;
  424. let eloop = map false eloop in
  425. in_loop := old;
  426. { e with eexpr = TWhile (cond,eloop,flag) }
  427. | TSwitch (e1,cases,def) when term ->
  428. let term = term && (def <> None || is_exhaustive e1) in
  429. let cases = List.map (fun (el,e) ->
  430. let el = List.map (map false) el in
  431. el, map term e
  432. ) cases in
  433. let def = opt (map term) def in
  434. { e with eexpr = TSwitch (map false e1,cases,def); etype = if ret_val then unify_min ctx ((List.map snd cases) @ (match def with None -> [] | Some e -> [e])) else e.etype }
  435. | TTry (e1,catches) ->
  436. { e with eexpr = TTry (map term e1,List.map (fun (v,e) ->
  437. let lv = (local v).i_subst in
  438. let e = map term e in
  439. lv,e
  440. ) catches); etype = if term && ret_val then unify_min ctx (e1::List.map snd catches) else e.etype }
  441. | TBlock l ->
  442. let old = save_locals ctx in
  443. let t = ref e.etype in
  444. let rec has_term_return e =
  445. let rec loop e =
  446. let r = match e.eexpr with
  447. | TReturn _ -> true
  448. | TIf (_,_,None) | TSwitch (_,_,None) | TFor _ | TWhile (_,_,NormalWhile) -> false (* we might not enter this code at all *)
  449. | TTry (a, catches) -> List.for_all has_term_return (a :: List.map snd catches)
  450. | TIf (cond,a,Some b) -> has_term_return cond || (has_term_return a && has_term_return b)
  451. | TSwitch (cond,cases,Some def) -> has_term_return cond || List.for_all has_term_return (def :: List.map snd cases)
  452. | TBinop (OpBoolAnd,a,b) -> has_term_return a && has_term_return b
  453. | _ -> Type.iter loop e; false
  454. in
  455. if r then raise Exit
  456. in
  457. try loop e; false with Exit -> true
  458. in
  459. let rec loop = function
  460. | [] when term ->
  461. t := mk_mono();
  462. [mk (TConst TNull) (!t) p]
  463. | [] -> []
  464. | [e] ->
  465. let e = map term e in
  466. if term then t := e.etype;
  467. [e]
  468. | ({ eexpr = TIf (cond,e1,None) } as e) :: l when term && has_term_return e1 ->
  469. loop [{ e with eexpr = TIf (cond,e1,Some (mk (TBlock l) e.etype e.epos)); epos = punion e.epos (match List.rev l with e :: _ -> e.epos | [] -> assert false) }]
  470. | e :: l ->
  471. let e = map false e in
  472. e :: loop l
  473. in
  474. let l = loop l in
  475. old();
  476. { e with eexpr = TBlock l; etype = !t }
  477. | TIf (econd,eif,Some eelse) when term ->
  478. let econd = map false econd in
  479. let eif = map term eif in
  480. let eelse = map term eelse in
  481. { e with eexpr = TIf(econd,eif,Some eelse); etype = if ret_val then unify_min ctx [eif;eelse] else e.etype }
  482. | TParenthesis e1 ->
  483. let e1 = map term e1 in
  484. mk (TParenthesis e1) e1.etype e.epos
  485. | TUnop ((Increment|Decrement) as op,flag,({ eexpr = TLocal v } as e1)) ->
  486. let l = read_local v in
  487. l.i_write <- true;
  488. {e with eexpr = TUnop(op,flag,{e1 with eexpr = TLocal l.i_subst})}
  489. | TBinop ((OpAssign | OpAssignOp _) as op,({ eexpr = TLocal v } as e1),e2) ->
  490. let l = read_local v in
  491. l.i_write <- true;
  492. let e2 = map false e2 in
  493. {e with eexpr = TBinop(op,{e1 with eexpr = TLocal l.i_subst},e2)}
  494. | TObjectDecl fl ->
  495. let fl = List.map (fun (s,e) -> s,map false e) fl in
  496. begin match follow e.etype with
  497. | TAnon an when (match !(an.a_status) with Const -> true | _ -> false) ->
  498. {e with eexpr = TObjectDecl fl; etype = TAnon { an with a_status = ref Closed}}
  499. | _ ->
  500. {e with eexpr = TObjectDecl fl}
  501. end
  502. | TFunction f ->
  503. (match f.tf_args with [] -> () | _ -> has_vars := true);
  504. let old = save_locals ctx and old_fun = !in_local_fun in
  505. let args = List.map (function(v,c) -> (local v).i_subst, c) f.tf_args in
  506. in_local_fun := true;
  507. let expr = map false f.tf_expr in
  508. in_local_fun := old_fun;
  509. old();
  510. { e with eexpr = TFunction { tf_args = args; tf_expr = expr; tf_type = f.tf_type } }
  511. | TCall({eexpr = TConst TSuper; etype = t},el) ->
  512. begin match follow t with
  513. | TInst({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)},_) ->
  514. begin match type_inline ctx cf tf ethis el ctx.t.tvoid None po true with
  515. | Some e -> map term e
  516. | None -> error "Could not inline super constructor call" po
  517. end
  518. | _ -> error "Cannot inline function containing super" po
  519. end
  520. | TConst TSuper ->
  521. error "Cannot inline function containing super" po
  522. | TMeta(m,e1) ->
  523. let e1 = map term e1 in
  524. {e with eexpr = TMeta(m,e1)}
  525. | _ ->
  526. Type.map_expr (map false) e
  527. in
  528. let e = map true f.tf_expr in
  529. (*
  530. if variables are not written and used with a const value, let's substitute
  531. with the actual value, either create a temp var
  532. *)
  533. let subst = ref PMap.empty in
  534. let is_constant e =
  535. let rec loop e =
  536. match e.eexpr with
  537. | TLocal _
  538. | TConst TThis (* not really, but should not be move inside a function body *)
  539. -> raise Exit
  540. | TField (_,FEnum _)
  541. | TTypeExpr _
  542. | TConst _ -> ()
  543. | _ ->
  544. Type.iter loop e
  545. in
  546. try loop e; true with Exit -> false
  547. in
  548. let is_writable e =
  549. match e.eexpr with
  550. | TField _ | TEnumParameter _ | TLocal _ | TArray _ -> true
  551. | _ -> false
  552. in
  553. let force = ref force in
  554. let vars = List.fold_left (fun acc (i,e) ->
  555. let flag = not i.i_force_temp && (match e.eexpr with
  556. | TLocal v when Meta.has Meta.This v.v_meta -> true
  557. | TLocal _ | TConst _ -> not i.i_write
  558. | TFunction _ -> if i.i_write then error "Cannot modify a closure parameter inside inline method" p; true
  559. | _ -> not i.i_write && i.i_read <= 1
  560. ) in
  561. let flag = flag && (not i.i_captured || is_constant e) in
  562. (* force inlining if we modify 'this' *)
  563. if i.i_write && (Meta.has Meta.This i.i_var.v_meta) then force := true;
  564. (* force inlining of 'this' variable if it is written *)
  565. let flag = if not flag && (Meta.has Meta.This i.i_var.v_meta) && i.i_write then begin
  566. if not (is_writable e) then error "Cannot modify the abstract value, store it into a local first" p;
  567. true
  568. end else flag in
  569. if flag then begin
  570. subst := PMap.add i.i_subst.v_id e !subst;
  571. acc
  572. end else begin
  573. (* mark the replacement local for the analyzer *)
  574. if i.i_read <= 1 && not i.i_write then
  575. i.i_subst.v_meta <- (Meta.CompilerGenerated,[],p) :: i.i_subst.v_meta;
  576. (i.i_subst,Some e) :: acc
  577. end
  578. ) [] inlined_vars in
  579. let subst = !subst in
  580. let rec inline_params e =
  581. match e.eexpr with
  582. | TLocal v -> (try PMap.find v.v_id subst with Not_found -> e)
  583. | _ -> Type.map_expr inline_params e
  584. in
  585. let e = (if PMap.is_empty subst then e else inline_params e) in
  586. let init = match vars with [] -> None | l -> Some l in
  587. (*
  588. If we have local variables and returning a value, then this will result in
  589. unoptimized JS code, so let's instead skip inlining.
  590. This could be fixed with better post process code cleanup (planed)
  591. *)
  592. if !cancel_inlining then
  593. None
  594. else
  595. let wrap e =
  596. (* we can't mute the type of the expression because it is not correct to do so *)
  597. let etype = if has_params then map_type e.etype else e.etype in
  598. (* if the expression is "untyped" and we don't want to unify it accidentally ! *)
  599. try (match follow e.etype with
  600. | TMono _ | TInst ({cl_kind = KTypeParameter _ },_) ->
  601. (match follow tret with
  602. | TAbstract ({ a_path = [],"Void" },_) -> e
  603. | _ -> raise (Unify_error []))
  604. | _ ->
  605. type_eq (if ctx.com.config.pf_static then EqDoNotFollowNull else EqStrict) etype tret;
  606. e)
  607. with Unify_error _ ->
  608. mk (TCast (e,None)) tret e.epos
  609. in
  610. let e = (match e.eexpr, init with
  611. | _, None when not !has_return_value ->
  612. {e with etype = tret}
  613. | TBlock [e] , None -> wrap e
  614. | _ , None -> wrap e
  615. | TBlock l, Some vl ->
  616. let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
  617. mk (TBlock (el_v @ l)) tret e.epos
  618. | _, Some vl ->
  619. let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
  620. mk (TBlock (el_v @ [e])) tret e.epos
  621. ) in
  622. let inline_meta e meta = match meta with
  623. | Meta.Deprecated,_,_ -> mk (TMeta(meta,e)) e.etype e.epos
  624. | _ -> e
  625. in
  626. let e = List.fold_left inline_meta e cf.cf_meta in
  627. (* we need to replace type-parameters that were used in the expression *)
  628. if not has_params then
  629. Some e
  630. else
  631. let mt = map_type cf.cf_type in
  632. let unify_func () = unify_raise ctx mt (TFun (List.map (fun e -> "",false,e.etype) params,tret)) p in
  633. (match follow ethis.etype with
  634. | TAnon a -> (match !(a.a_status) with
  635. | Statics {cl_kind = KAbstractImpl a } when Meta.has Meta.Impl cf.cf_meta ->
  636. if cf.cf_name <> "_new" then begin
  637. (* the first argument must unify with a_this for abstract implementation functions *)
  638. let tb = (TFun(("",false,map_type a.a_this) :: List.map (fun e -> "",false,e.etype) (List.tl params),tret)) in
  639. unify_raise ctx mt tb p
  640. end
  641. | _ -> unify_func())
  642. | _ -> unify_func());
  643. (*
  644. this is very expensive since we are building the substitution list for
  645. every expression, but hopefully in such cases the expression size is small
  646. *)
  647. let vars = Hashtbl.create 0 in
  648. let map_var v =
  649. if not (Hashtbl.mem vars v.v_id) then begin
  650. Hashtbl.add vars v.v_id ();
  651. v.v_type <- map_type v.v_type;
  652. end;
  653. v
  654. in
  655. let rec map_expr_type e = Type.map_expr_type map_expr_type map_type map_var e in
  656. Some (map_expr_type e)
  657. (* ---------------------------------------------------------------------- *)
  658. (* LOOPS *)
  659. let rec optimize_for_loop ctx (i,pi) e1 e2 p =
  660. let t_void = ctx.t.tvoid in
  661. let t_int = ctx.t.tint in
  662. let lblock el = Some (mk (TBlock el) t_void p) in
  663. let mk_field e n =
  664. TField (e,try quick_field e.etype n with Not_found -> assert false)
  665. in
  666. let gen_int_iter pt f_get f_length =
  667. let i = add_local ctx i pt in
  668. let index = gen_local ctx t_int in
  669. let arr, avars = (match e1.eexpr with
  670. | TLocal _ -> e1, None
  671. | _ ->
  672. let atmp = gen_local ctx e1.etype in
  673. mk (TLocal atmp) e1.etype e1.epos, (Some (atmp,Some e1))
  674. ) in
  675. let iexpr = mk (TLocal index) t_int p in
  676. let e2 = type_expr ctx e2 NoValue in
  677. let aget = mk (TVar (i,Some (f_get arr iexpr pt p))) t_void pi in
  678. let incr = mk (TUnop (Increment,Prefix,iexpr)) t_int p in
  679. let block = match e2.eexpr with
  680. | TBlock el -> mk (TBlock (aget :: incr :: el)) t_void e2.epos
  681. | _ -> mk (TBlock [aget;incr;e2]) t_void p
  682. in
  683. let ivar = Some (mk (TConst (TInt 0l)) t_int p) in
  684. let elength = f_length arr p in
  685. let el = [mk (TWhile (
  686. mk (TBinop (OpLt, iexpr, elength)) ctx.t.tbool p,
  687. block,
  688. NormalWhile
  689. )) t_void p;
  690. ] in
  691. let el = match avars with None -> el | Some (v,eo) -> (mk (TVar (v,eo)) t_void p) :: el in
  692. let el = (mk (TVar (index,ivar)) t_void p) :: el in
  693. lblock el
  694. in
  695. let get_next_array_element arr iexpr pt p =
  696. (mk (TArray (arr,iexpr)) pt p)
  697. in
  698. let get_array_length arr p =
  699. mk (mk_field arr "length") ctx.com.basic.tint p
  700. in
  701. match e1.eexpr, follow e1.etype with
  702. | TNew ({ cl_path = ([],"IntIterator") },[],[i1;i2]) , _ ->
  703. let max = (match i1.eexpr , i2.eexpr with
  704. | TConst (TInt a), TConst (TInt b) when Int32.compare b a < 0 -> error "Range operator can't iterate backwards" p
  705. | _, TConst _ | _ , TLocal _ -> None
  706. | _ -> Some (gen_local ctx t_int)
  707. ) in
  708. let tmp = gen_local ctx t_int in
  709. let i = add_local ctx i t_int in
  710. let rec check e =
  711. match e.eexpr with
  712. | TBinop (OpAssign,{ eexpr = TLocal l },_)
  713. | TBinop (OpAssignOp _,{ eexpr = TLocal l },_)
  714. | TUnop (Increment,_,{ eexpr = TLocal l })
  715. | TUnop (Decrement,_,{ eexpr = TLocal l }) when l == i ->
  716. error "Loop variable cannot be modified" e.epos
  717. | _ ->
  718. Type.iter check e
  719. in
  720. let e2 = type_expr ctx e2 NoValue in
  721. check e2;
  722. let etmp = mk (TLocal tmp) t_int p in
  723. let incr = mk (TUnop (Increment,Postfix,etmp)) t_int p in
  724. let init = mk (TVar (i,Some incr)) t_void pi in
  725. let block = match e2.eexpr with
  726. | TBlock el -> mk (TBlock (init :: el)) t_void e2.epos
  727. | _ -> mk (TBlock [init;e2]) t_void p
  728. in
  729. (*
  730. force locals to be of Int type (to prevent Int/UInt issues)
  731. *)
  732. let i2 = match follow i2.etype with
  733. | TAbstract ({ a_path = ([],"Int") }, []) -> i2
  734. | _ -> { i2 with eexpr = TCast(i2, None); etype = t_int }
  735. in
  736. (match max with
  737. | None ->
  738. lblock [
  739. mk (TVar (tmp,Some i1)) t_void p;
  740. mk (TWhile (
  741. mk (TBinop (OpLt, etmp, i2)) ctx.t.tbool p,
  742. block,
  743. NormalWhile
  744. )) t_void p;
  745. ]
  746. | Some max ->
  747. lblock [
  748. mk (TVar (tmp,Some i1)) t_void p;
  749. mk (TVar (max,Some i2)) t_void p;
  750. mk (TWhile (
  751. mk (TBinop (OpLt, etmp, mk (TLocal max) t_int p)) ctx.t.tbool p,
  752. block,
  753. NormalWhile
  754. )) t_void p;
  755. ])
  756. | TArrayDecl el, TInst({ cl_path = [],"Array" },[pt]) when false ->
  757. begin try
  758. let num_expr = ref 0 in
  759. let rec loop e = match fst e with
  760. | EContinue | EBreak ->
  761. raise Exit
  762. | _ ->
  763. incr num_expr;
  764. Ast.map_expr loop e
  765. in
  766. ignore(loop e2);
  767. let v = add_local ctx i pt in
  768. let e2 = type_expr ctx e2 NoValue in
  769. let cost = (List.length el) * !num_expr in
  770. let max_cost = try
  771. int_of_string (Common.defined_value ctx.com Define.LoopUnrollMaxCost)
  772. with Not_found ->
  773. 250
  774. in
  775. if cost > max_cost then raise Exit;
  776. let eloc = mk (TLocal v) v.v_type p in
  777. let el = List.map (fun e ->
  778. let e_assign = mk (TBinop(OpAssign,eloc,e)) e.etype e.epos in
  779. concat e_assign e2
  780. ) el in
  781. let ev = mk (TVar(v, None)) ctx.t.tvoid p in
  782. Some (mk (TBlock (ev :: el)) ctx.t.tvoid p)
  783. with Exit ->
  784. gen_int_iter pt get_next_array_element get_array_length
  785. end
  786. | _ , TInst({ cl_path = [],"Array" },[pt])
  787. | _ , TInst({ cl_path = ["flash"],"Vector" },[pt]) ->
  788. gen_int_iter pt get_next_array_element get_array_length
  789. | _ , TInst({ cl_array_access = Some pt } as c,pl) when (try match follow (PMap.find "length" c.cl_fields).cf_type with TAbstract ({ a_path = [],"Int" },[]) -> true | _ -> false with Not_found -> false) && not (PMap.mem "iterator" c.cl_fields) ->
  790. gen_int_iter (apply_params c.cl_params pl pt) get_next_array_element get_array_length
  791. | _, TAbstract({a_impl = Some c} as a,tl) ->
  792. begin try
  793. let cf_length = PMap.find "get_length" c.cl_statics in
  794. let get_length e p =
  795. make_static_call ctx c cf_length (apply_params a.a_params tl) [e] ctx.com.basic.tint p
  796. in
  797. begin match follow cf_length.cf_type with
  798. | TFun(_,tr) ->
  799. begin match follow tr with
  800. | TAbstract({a_path = [],"Int"},_) -> ()
  801. | _ -> raise Not_found
  802. end
  803. | _ ->
  804. raise Not_found
  805. end;
  806. begin try
  807. (* first try: do we have an @:arrayAccess getter field? *)
  808. let todo = mk (TConst TNull) ctx.t.tint p in
  809. let cf,_,r,_,_ = (!find_array_access_raise_ref) ctx a tl todo None p in
  810. let get_next e_base e_index t p =
  811. make_static_call ctx c cf (apply_params a.a_params tl) [e_base;e_index] r p
  812. in
  813. gen_int_iter r get_next get_length
  814. with Not_found ->
  815. (* second try: do we have @:arrayAccess on the abstract itself? *)
  816. if not (Meta.has Meta.ArrayAccess a.a_meta) then raise Not_found;
  817. (* let's allow this only for core-type abstracts *)
  818. if not (Meta.has Meta.CoreType a.a_meta) then raise Not_found;
  819. (* in which case we assume that a singular type parameter is the element type *)
  820. let t = match tl with [t] -> t | _ -> raise Not_found in
  821. gen_int_iter t get_next_array_element get_length
  822. end with Not_found ->
  823. None
  824. end
  825. | _ , TInst ({ cl_kind = KGenericInstance ({ cl_path = ["haxe";"ds"],"GenericStack" },[t]) } as c,[]) ->
  826. let tcell = (try (PMap.find "head" c.cl_fields).cf_type with Not_found -> assert false) in
  827. let i = add_local ctx i t in
  828. let cell = gen_local ctx tcell in
  829. let cexpr = mk (TLocal cell) tcell p in
  830. let e2 = type_expr ctx e2 NoValue in
  831. let evar = mk (TVar (i,Some (mk (mk_field cexpr "elt") t p))) t_void pi in
  832. let enext = mk (TBinop (OpAssign,cexpr,mk (mk_field cexpr "next") tcell p)) tcell p in
  833. let block = match e2.eexpr with
  834. | TBlock el -> mk (TBlock (evar :: enext :: el)) t_void e2.epos
  835. | _ -> mk (TBlock [evar;enext;e2]) t_void p
  836. in
  837. lblock [
  838. mk (TVar (cell,Some (mk (mk_field e1 "head") tcell p))) t_void p;
  839. mk (TWhile (
  840. mk (TBinop (OpNotEq, cexpr, mk (TConst TNull) tcell p)) ctx.t.tbool p,
  841. block,
  842. NormalWhile
  843. )) t_void p
  844. ]
  845. | _ ->
  846. None
  847. let optimize_for_loop_iterator ctx v e1 e2 p =
  848. let c,tl = (match follow e1.etype with TInst (c,pl) -> c,pl | _ -> raise Exit) in
  849. let _, _, fhasnext = (try raw_class_field (fun cf -> apply_params c.cl_params tl cf.cf_type) c tl "hasNext" with Not_found -> raise Exit) in
  850. if fhasnext.cf_kind <> Method MethInline then raise Exit;
  851. let tmp = gen_local ctx e1.etype in
  852. let eit = mk (TLocal tmp) e1.etype p in
  853. let ehasnext = make_call ctx (mk (TField (eit,FInstance (c, tl, fhasnext))) (TFun([],ctx.t.tbool)) p) [] ctx.t.tbool p in
  854. let enext = mk (TVar (v,Some (make_call ctx (mk (TField (eit,quick_field_dynamic eit.etype "next")) (TFun ([],v.v_type)) p) [] v.v_type p))) ctx.t.tvoid p in
  855. let eblock = (match e2.eexpr with
  856. | TBlock el -> { e2 with eexpr = TBlock (enext :: el) }
  857. | _ -> mk (TBlock [enext;e2]) ctx.t.tvoid p
  858. ) in
  859. mk (TBlock [
  860. mk (TVar (tmp,Some e1)) ctx.t.tvoid p;
  861. mk (TWhile (ehasnext,eblock,NormalWhile)) ctx.t.tvoid p
  862. ]) ctx.t.tvoid p
  863. (* ---------------------------------------------------------------------- *)
  864. (* SANITIZE *)
  865. (*
  866. makes sure that when an AST get generated to source code, it will not
  867. generate expressions that evaluate differently. It is then necessary to
  868. add parenthesises around some binary expressions when the AST does not
  869. correspond to the natural operand priority order for the platform
  870. *)
  871. (*
  872. this is the standard C++ operator precedence, which is also used by both JS and PHP
  873. *)
  874. let standard_precedence op =
  875. let left = true and right = false in
  876. match op with
  877. | OpMult | OpDiv | OpMod -> 5, left
  878. | OpAdd | OpSub -> 6, left
  879. | OpShl | OpShr | OpUShr -> 7, left
  880. | OpLt | OpLte | OpGt | OpGte -> 8, left
  881. | OpEq | OpNotEq -> 9, left
  882. | OpAnd -> 10, left
  883. | OpXor -> 11, left
  884. | OpOr -> 12, left
  885. | OpInterval -> 13, right (* haxe specific *)
  886. | OpBoolAnd -> 14, left
  887. | OpBoolOr -> 15, left
  888. | OpArrow -> 16, left
  889. | OpAssignOp OpAssign -> 17, right (* mimics ?: *)
  890. | OpAssign | OpAssignOp _ -> 18, right
  891. let rec need_parent e =
  892. match e.eexpr with
  893. | TConst _ | TLocal _ | TArray _ | TField _ | TEnumParameter _ | TParenthesis _ | TMeta _ | TCall _ | TNew _ | TTypeExpr _ | TObjectDecl _ | TArrayDecl _ -> false
  894. | TCast (e,None) -> need_parent e
  895. | TCast _ | TThrow _ | TReturn _ | TTry _ | TSwitch _ | TFor _ | TIf _ | TWhile _ | TBinop _ | TContinue | TBreak
  896. | TBlock _ | TVar _ | TFunction _ | TUnop _ -> true
  897. let sanitize_expr com e =
  898. let parent e =
  899. match e.eexpr with
  900. | TParenthesis _ -> e
  901. | _ -> mk (TParenthesis e) e.etype e.epos
  902. in
  903. let block e =
  904. match e.eexpr with
  905. | TBlock _ -> e
  906. | _ -> mk (TBlock [e]) e.etype e.epos
  907. in
  908. let complex e =
  909. (* complex expressions are the one that once generated to source consists in several expressions *)
  910. match e.eexpr with
  911. | TVar _ (* needs to be put into blocks *)
  912. | TFor _ (* a temp var is needed for holding iterator *)
  913. | TCall ({ eexpr = TLocal { v_name = "__js__" } },_) (* we never know *)
  914. -> block e
  915. | _ -> e
  916. in
  917. (* tells if the printed expresssion ends with an if without else *)
  918. let rec has_if e =
  919. match e.eexpr with
  920. | TIf (_,_,None) -> true
  921. | TWhile (_,e,NormalWhile) -> has_if e
  922. | TFor (_,_,e) -> has_if e
  923. | _ -> false
  924. in
  925. match e.eexpr with
  926. | TConst TNull ->
  927. if com.config.pf_static && not (is_nullable e.etype) then begin
  928. let rec loop t = match follow t with
  929. | TMono _ -> () (* in these cases the null will cast to default value *)
  930. | TFun _ -> () (* this is a bit a particular case, maybe flash-specific actually *)
  931. (* TODO: this should use get_underlying_type, but we do not have access to Codegen here. *)
  932. | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> loop (apply_params a.a_params tl a.a_this)
  933. | _ -> com.error ("On static platforms, null can't be used as basic type " ^ s_type (print_context()) e.etype) e.epos
  934. in
  935. loop e.etype
  936. end;
  937. e
  938. | TBinop (op,e1,e2) ->
  939. let swap op1 op2 =
  940. let p1, left1 = standard_precedence op1 in
  941. let p2, _ = standard_precedence op2 in
  942. left1 && p1 <= p2
  943. in
  944. let rec loop ee left =
  945. match ee.eexpr with
  946. | TBinop (op2,_,_) -> if left then not (swap op2 op) else swap op op2
  947. | TIf _ -> if left then not (swap (OpAssignOp OpAssign) op) else swap op (OpAssignOp OpAssign)
  948. | TCast (e,None) -> loop e left
  949. | _ -> false
  950. in
  951. let e1 = if loop e1 true then parent e1 else e1 in
  952. let e2 = if loop e2 false then parent e2 else e2 in
  953. { e with eexpr = TBinop (op,e1,e2) }
  954. | TUnop (op,mode,e1) ->
  955. let rec loop ee =
  956. match ee.eexpr with
  957. | TBinop _ | TIf _ | TUnop _ -> parent e1
  958. | TCast (e,None) -> loop e
  959. | _ -> e1
  960. in
  961. { e with eexpr = TUnop (op,mode,loop e1)}
  962. | TIf (e1,e2,eelse) ->
  963. let e1 = parent e1 in
  964. let e2 = (if (eelse <> None && has_if e2) || (match e2.eexpr with TIf _ -> true | _ -> false) then block e2 else complex e2) in
  965. let eelse = (match eelse with None -> None | Some e -> Some (complex e)) in
  966. { e with eexpr = TIf (e1,e2,eelse) }
  967. | TWhile (e1,e2,flag) ->
  968. let e1 = parent e1 in
  969. let e2 = complex e2 in
  970. { e with eexpr = TWhile (e1,e2,flag) }
  971. | TFor (v,e1,e2) ->
  972. let e2 = complex e2 in
  973. { e with eexpr = TFor (v,e1,e2) }
  974. | TFunction f ->
  975. let f = (match f.tf_expr.eexpr with
  976. | TBlock _ -> f
  977. | _ -> { f with tf_expr = block f.tf_expr }
  978. ) in
  979. { e with eexpr = TFunction f }
  980. | TCall (e2,args) ->
  981. if need_parent e2 then { e with eexpr = TCall(parent e2,args) } else e
  982. | TEnumParameter (e2,ef,i) ->
  983. if need_parent e2 then { e with eexpr = TEnumParameter(parent e2,ef,i) } else e
  984. | TField (e2,f) ->
  985. if need_parent e2 then { e with eexpr = TField(parent e2,f) } else e
  986. | TArray (e1,e2) ->
  987. if need_parent e1 then { e with eexpr = TArray(parent e1,e2) } else e
  988. | TTry (e1,catches) ->
  989. let e1 = block e1 in
  990. let catches = List.map (fun (v,e) -> v, block e) catches in
  991. { e with eexpr = TTry (e1,catches) }
  992. | TSwitch (e1,cases,def) ->
  993. let e1 = parent e1 in
  994. let cases = List.map (fun (el,e) -> el, complex e) cases in
  995. let def = (match def with None -> None | Some e -> Some (complex e)) in
  996. { e with eexpr = TSwitch (e1,cases,def) }
  997. | _ ->
  998. e
  999. let reduce_expr com e =
  1000. match e.eexpr with
  1001. | TSwitch (_,cases,_) ->
  1002. List.iter (fun (cl,_) ->
  1003. List.iter (fun e ->
  1004. match e.eexpr with
  1005. | TCall ({ eexpr = TField (_,FEnum _) },_) -> error "Not-constant enum in switch cannot be matched" e.epos
  1006. | _ -> ()
  1007. ) cl
  1008. ) cases;
  1009. e
  1010. | TBlock l ->
  1011. (match List.rev l with
  1012. | [] -> e
  1013. | ec :: l ->
  1014. (* remove all no-ops : not-final constants in blocks *)
  1015. match List.filter (fun e -> match e.eexpr with
  1016. | TConst _
  1017. | TBlock []
  1018. | TObjectDecl [] ->
  1019. false
  1020. | _ ->
  1021. true
  1022. ) l with
  1023. | [] -> ec
  1024. | l -> { e with eexpr = TBlock (List.rev (ec :: l)) })
  1025. | TParenthesis ec ->
  1026. { ec with epos = e.epos }
  1027. | TTry (e,[]) ->
  1028. e
  1029. | _ ->
  1030. e
  1031. let rec sanitize com e =
  1032. sanitize_expr com (reduce_expr com (Type.map_expr (sanitize com) e))
  1033. (* ---------------------------------------------------------------------- *)
  1034. (* REDUCE *)
  1035. let optimize_binop e op e1 e2 =
  1036. let is_float t =
  1037. match follow t with
  1038. | TAbstract({ a_path = [],"Float" },_) -> true
  1039. | _ -> false
  1040. in
  1041. let is_numeric t =
  1042. match follow t with
  1043. | TAbstract({ a_path = [],("Float"|"Int") },_) -> true
  1044. | _ -> false
  1045. in
  1046. let check_float op f1 f2 =
  1047. let f = op f1 f2 in
  1048. let fstr = float_repres f in
  1049. if (match classify_float f with FP_nan | FP_infinite -> false | _ -> float_of_string fstr = f) then { e with eexpr = TConst (TFloat fstr) } else e
  1050. in
  1051. (match e1.eexpr, e2.eexpr with
  1052. | TConst (TInt 0l) , _ when op = OpAdd && is_numeric e2.etype -> e2
  1053. | TConst (TInt 1l) , _ when op = OpMult -> e2
  1054. | TConst (TFloat v) , _ when op = OpAdd && float_of_string v = 0. && is_float e2.etype -> e2
  1055. | TConst (TFloat v) , _ when op = OpMult && float_of_string v = 1. && is_float e2.etype -> e2
  1056. | _ , TConst (TInt 0l) when (match op with OpAdd -> is_numeric e1.etype | OpSub | OpShr | OpShl -> true | _ -> false) -> e1 (* bits operations might cause overflow *)
  1057. | _ , TConst (TInt 1l) when op = OpMult -> e1
  1058. | _ , TConst (TFloat v) when (match op with OpAdd | OpSub -> float_of_string v = 0. && is_float e1.etype | _ -> false) -> e1 (* bits operations might cause overflow *)
  1059. | _ , TConst (TFloat v) when op = OpMult && float_of_string v = 1. && is_float e1.etype -> e1
  1060. | TConst TNull, TConst TNull ->
  1061. (match op with
  1062. | OpEq -> { e with eexpr = TConst (TBool true) }
  1063. | OpNotEq -> { e with eexpr = TConst (TBool false) }
  1064. | _ -> e)
  1065. | TFunction _, TConst TNull ->
  1066. (match op with
  1067. | OpEq -> { e with eexpr = TConst (TBool false) }
  1068. | OpNotEq -> { e with eexpr = TConst (TBool true) }
  1069. | _ -> e)
  1070. | TConst TNull, TFunction _ ->
  1071. (match op with
  1072. | OpEq -> { e with eexpr = TConst (TBool false) }
  1073. | OpNotEq -> { e with eexpr = TConst (TBool true) }
  1074. | _ -> e)
  1075. | TConst (TInt a), TConst (TInt b) ->
  1076. let opt f = try { e with eexpr = TConst (TInt (f a b)) } with Exit -> e in
  1077. let check_overflow f =
  1078. opt (fun a b ->
  1079. let v = f (Int64.of_int32 a) (Int64.of_int32 b) in
  1080. let iv = Int64.to_int32 v in
  1081. if Int64.compare (Int64.of_int32 iv) v <> 0 then raise Exit;
  1082. iv
  1083. )
  1084. in
  1085. let ebool t =
  1086. { e with eexpr = TConst (TBool (t (Int32.compare a b) 0)) }
  1087. in
  1088. (match op with
  1089. | OpAdd -> check_overflow Int64.add
  1090. | OpSub -> check_overflow Int64.sub
  1091. | OpMult -> check_overflow Int64.mul
  1092. | OpDiv -> check_float ( /. ) (Int32.to_float a) (Int32.to_float b)
  1093. | OpAnd -> opt Int32.logand
  1094. | OpOr -> opt Int32.logor
  1095. | OpXor -> opt Int32.logxor
  1096. | OpShl -> opt (fun a b -> Int32.shift_left a (Int32.to_int b))
  1097. | OpShr -> opt (fun a b -> Int32.shift_right a (Int32.to_int b))
  1098. | OpUShr -> opt (fun a b -> Int32.shift_right_logical a (Int32.to_int b))
  1099. | OpEq -> ebool (=)
  1100. | OpNotEq -> ebool (<>)
  1101. | OpGt -> ebool (>)
  1102. | OpGte -> ebool (>=)
  1103. | OpLt -> ebool (<)
  1104. | OpLte -> ebool (<=)
  1105. | _ -> e)
  1106. | TConst ((TFloat _ | TInt _) as ca), TConst ((TFloat _ | TInt _) as cb) ->
  1107. let fa = (match ca with
  1108. | TFloat a -> float_of_string a
  1109. | TInt a -> Int32.to_float a
  1110. | _ -> assert false
  1111. ) in
  1112. let fb = (match cb with
  1113. | TFloat b -> float_of_string b
  1114. | TInt b -> Int32.to_float b
  1115. | _ -> assert false
  1116. ) in
  1117. let fop op = check_float op fa fb in
  1118. let ebool t =
  1119. { e with eexpr = TConst (TBool (t (compare fa fb) 0)) }
  1120. in
  1121. (match op with
  1122. | OpAdd -> fop (+.)
  1123. | OpDiv -> fop (/.)
  1124. | OpSub -> fop (-.)
  1125. | OpMult -> fop ( *. )
  1126. | OpEq -> ebool (=)
  1127. | OpNotEq -> ebool (<>)
  1128. | OpGt -> ebool (>)
  1129. | OpGte -> ebool (>=)
  1130. | OpLt -> ebool (<)
  1131. | OpLte -> ebool (<=)
  1132. | _ -> e)
  1133. | TConst (TString ""),TConst (TString s) | TConst (TString s),TConst (TString "") when op = OpAdd ->
  1134. {e with eexpr = TConst (TString s)}
  1135. | TConst (TBool a), TConst (TBool b) ->
  1136. let ebool f =
  1137. { e with eexpr = TConst (TBool (f a b)) }
  1138. in
  1139. (match op with
  1140. | OpEq -> ebool (=)
  1141. | OpNotEq -> ebool (<>)
  1142. | OpBoolAnd -> ebool (&&)
  1143. | OpBoolOr -> ebool (||)
  1144. | _ -> e)
  1145. | TConst a, TConst b when op = OpEq || op = OpNotEq ->
  1146. let ebool b =
  1147. { e with eexpr = TConst (TBool (if op = OpEq then b else not b)) }
  1148. in
  1149. (match a, b with
  1150. | TInt a, TFloat b | TFloat b, TInt a -> ebool (Int32.to_float a = float_of_string b)
  1151. | _ -> ebool (a = b))
  1152. | TConst (TBool a), _ ->
  1153. (match op with
  1154. | OpBoolAnd -> if a then e2 else { e with eexpr = TConst (TBool false) }
  1155. | OpBoolOr -> if a then { e with eexpr = TConst (TBool true) } else e2
  1156. | _ -> e)
  1157. | _ , TConst (TBool a) ->
  1158. (match op with
  1159. | OpBoolAnd when a -> e1
  1160. | OpBoolOr when not a -> e1
  1161. | _ -> e)
  1162. | TField (_,FEnum (e1,f1)), TField (_,FEnum (e2,f2)) when e1 == e2 ->
  1163. (match op with
  1164. | OpEq -> { e with eexpr = TConst (TBool (f1 == f2)) }
  1165. | OpNotEq -> { e with eexpr = TConst (TBool (f1 != f2)) }
  1166. | _ -> e)
  1167. | _, TCall ({ eexpr = TField (_,FEnum _) },_) | TCall ({ eexpr = TField (_,FEnum _) },_), _ ->
  1168. (match op with
  1169. | OpAssign -> e
  1170. | _ ->
  1171. error "You cannot directly compare enums with arguments. Use either 'switch' or 'Type.enumEq'" e.epos)
  1172. | _ ->
  1173. e)
  1174. let optimize_unop e op flag esub =
  1175. match op, esub.eexpr with
  1176. | Not, (TConst (TBool f) | TParenthesis({eexpr = TConst (TBool f)})) -> { e with eexpr = TConst (TBool (not f)) }
  1177. | Not, (TBinop(op,e1,e2) | TParenthesis({eexpr = TBinop(op,e1,e2)})) ->
  1178. begin try
  1179. let op = match op with
  1180. | OpGt -> OpLte
  1181. | OpGte -> OpLt
  1182. | OpLt -> OpGte
  1183. | OpLte -> OpGt
  1184. | OpEq -> OpNotEq
  1185. | OpNotEq -> OpEq
  1186. | _ -> raise Exit
  1187. in
  1188. {e with eexpr = TBinop(op,e1,e2)}
  1189. with Exit ->
  1190. e
  1191. end
  1192. | Neg, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.neg i)) }
  1193. | NegBits, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.lognot i)) }
  1194. | Neg, TConst (TFloat f) ->
  1195. let v = 0. -. float_of_string f in
  1196. let vstr = float_repres v in
  1197. if float_of_string vstr = v then
  1198. { e with eexpr = TConst (TFloat vstr) }
  1199. else
  1200. e
  1201. | _ -> e
  1202. let rec reduce_loop ctx e =
  1203. let e = Type.map_expr (reduce_loop ctx) e in
  1204. sanitize_expr ctx.com (match e.eexpr with
  1205. | TIf ({ eexpr = TConst (TBool t) },e1,e2) ->
  1206. (if t then e1 else match e2 with None -> { e with eexpr = TBlock [] } | Some e -> e)
  1207. | TWhile ({ eexpr = TConst (TBool false) },sub,flag) ->
  1208. (match flag with
  1209. | NormalWhile -> { e with eexpr = TBlock [] } (* erase sub *)
  1210. | DoWhile -> e) (* we cant remove while since sub can contain continue/break *)
  1211. | TBinop (op,e1,e2) ->
  1212. optimize_binop e op e1 e2
  1213. | TUnop (op,flag,esub) ->
  1214. optimize_unop e op flag esub
  1215. | TCall ({ eexpr = TField ({ eexpr = TTypeExpr (TClassDecl c) },field) },params) ->
  1216. (match api_inline ctx c (field_name field) params e.epos with
  1217. | None -> reduce_expr ctx e
  1218. | Some e -> reduce_loop ctx e)
  1219. | TCall ({ eexpr = TFunction func } as ef,el) ->
  1220. let cf = mk_field "" ef.etype e.epos in
  1221. let ethis = mk (TConst TThis) t_dynamic e.epos in
  1222. let rt = (match follow ef.etype with TFun (_,rt) -> rt | _ -> assert false) in
  1223. let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error (Custom _,_) -> None) in
  1224. (match inl with
  1225. | None -> reduce_expr ctx e
  1226. | Some e -> reduce_loop ctx e)
  1227. | TCall ({ eexpr = TField (o,FClosure (c,cf)) } as f,el) ->
  1228. let fmode = (match c with None -> FAnon cf | Some (c,tl) -> FInstance (c,tl,cf)) in
  1229. { e with eexpr = TCall ({ f with eexpr = TField (o,fmode) },el) }
  1230. | TSwitch (e1,[[{eexpr = TConst (TBool true)}],{eexpr = TConst (TBool true)}],Some ({eexpr = TConst (TBool false)})) ->
  1231. (* introduced by extractors in some cases *)
  1232. e1
  1233. | _ ->
  1234. reduce_expr ctx e)
  1235. let reduce_expression ctx e =
  1236. if ctx.com.foptimize then reduce_loop ctx e else e
  1237. let rec make_constant_expression ctx ?(concat_strings=false) e =
  1238. let e = reduce_loop ctx e in
  1239. match e.eexpr with
  1240. | TConst _ -> Some e
  1241. | TBinop ((OpAdd|OpSub|OpMult|OpDiv|OpMod) as op,e1,e2) -> (match make_constant_expression ctx e1,make_constant_expression ctx e2 with
  1242. | Some ({eexpr = TConst (TString s1)}), Some ({eexpr = TConst (TString s2)}) when concat_strings ->
  1243. Some (mk (TConst (TString (s1 ^ s2))) ctx.com.basic.tstring (punion e1.epos e2.epos))
  1244. | Some e1, Some e2 -> Some (mk (TBinop(op, e1, e2)) e.etype e.epos)
  1245. | _ -> None)
  1246. | TCast (e1, None) ->
  1247. (match make_constant_expression ctx e1 with
  1248. | None -> None
  1249. | Some e1 -> Some {e with eexpr = TCast(e1,None)})
  1250. | TParenthesis e1 ->
  1251. begin match make_constant_expression ctx ~concat_strings e1 with
  1252. | None -> None
  1253. | Some e1 -> Some {e with eexpr = TParenthesis e1}
  1254. end
  1255. | TMeta(m,e1) ->
  1256. begin match make_constant_expression ctx ~concat_strings e1 with
  1257. | None -> None
  1258. | Some e1 -> Some {e with eexpr = TMeta(m,e1)}
  1259. end
  1260. | TTypeExpr _ -> Some e
  1261. (* try to inline static function calls *)
  1262. | TCall ({ etype = TFun(_,ret); eexpr = TField (_,FStatic (c,cf)) },el) ->
  1263. (try
  1264. let func = match cf.cf_expr with Some ({eexpr = TFunction func}) -> func | _ -> raise Not_found in
  1265. let ethis = mk (TConst TThis) t_dynamic e.epos in
  1266. let inl = (try type_inline ctx cf func ethis el ret None e.epos false with Error (Custom _,_) -> None) in
  1267. (match inl with
  1268. | None -> None
  1269. | Some e -> make_constant_expression ctx e)
  1270. with Not_found -> None)
  1271. | _ -> None
  1272. (* ---------------------------------------------------------------------- *)
  1273. (* INLINE CONSTRUCTORS *)
  1274. (*
  1275. First pass :
  1276. We will look at local variables in the form var v = new ....
  1277. we only capture the ones which have constructors marked as inlined
  1278. then we make sure that these locals are no more referenced except for fields accesses
  1279. Second pass :
  1280. We replace the variables by their fields lists, and the corresponding fields accesses as well
  1281. *)
  1282. type inline_info_kind =
  1283. | IKCtor of tclass_field * bool
  1284. | IKStructure
  1285. | IKArray of int
  1286. type inline_info = {
  1287. ii_var : tvar;
  1288. ii_expr : texpr;
  1289. ii_kind : inline_info_kind;
  1290. mutable ii_fields : (string,tvar) PMap.t;
  1291. }
  1292. let inline_constructors ctx e =
  1293. let vars = ref IntMap.empty in
  1294. let is_valid_ident s =
  1295. try
  1296. if String.length s = 0 then raise Exit;
  1297. begin match String.unsafe_get s 0 with
  1298. | 'a'..'z' | 'A'..'Z' | '_' -> ()
  1299. | _ -> raise Exit
  1300. end;
  1301. for i = 1 to String.length s - 1 do
  1302. match String.unsafe_get s i with
  1303. | 'a'..'z' | 'A'..'Z' | '_' -> ()
  1304. | '0'..'9' when i > 0 -> ()
  1305. | _ -> raise Exit
  1306. done;
  1307. true
  1308. with Exit ->
  1309. false
  1310. in
  1311. let cancel v p =
  1312. try
  1313. let ii = IntMap.find v.v_id !vars in
  1314. vars := IntMap.remove v.v_id !vars;
  1315. v.v_id <- -v.v_id;
  1316. begin match ii.ii_kind with
  1317. | IKCtor(cf,true) ->
  1318. display_error ctx "Extern constructor could not be inlined" p;
  1319. error "Variable is used here" p;
  1320. | _ ->
  1321. ()
  1322. end;
  1323. with Not_found ->
  1324. ()
  1325. in
  1326. let add v e kind =
  1327. let ii = {
  1328. ii_var = v;
  1329. ii_fields = PMap.empty;
  1330. ii_expr = e;
  1331. ii_kind = kind
  1332. } in
  1333. v.v_id <- -v.v_id;
  1334. vars := IntMap.add v.v_id ii !vars;
  1335. in
  1336. let int_field_name i =
  1337. if i < 0 then "n" ^ (string_of_int (-i))
  1338. else (string_of_int i)
  1339. in
  1340. let rec find_locals e = match e.eexpr with
  1341. | TVar(v,Some e1) ->
  1342. find_locals e1;
  1343. let rec loop el_init e1 = match e1.eexpr with
  1344. | TBlock el ->
  1345. begin match List.rev el with
  1346. | e1 :: el ->
  1347. loop (el @ el_init) e1
  1348. | [] ->
  1349. ()
  1350. end
  1351. | TNew({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,tl,pl) when type_iseq v.v_type e1.etype ->
  1352. begin match type_inline ctx cf tf (mk (TLocal v) (TInst (c,tl)) e1.epos) pl ctx.t.tvoid None e1.epos true with
  1353. | Some e ->
  1354. (* add field inits here because the filter has not run yet (issue #2336) *)
  1355. let ev = mk (TLocal v) v.v_type e.epos in
  1356. let el_init = List.fold_left (fun acc cf -> match cf.cf_kind,cf.cf_expr with
  1357. | Var _,Some e ->
  1358. let ef = mk (TField(ev,FInstance(c,tl,cf))) e.etype e.epos in
  1359. let e = mk (TBinop(OpAssign,ef,e)) e.etype e.epos in
  1360. e :: acc
  1361. | _ -> acc
  1362. ) el_init c.cl_ordered_fields in
  1363. let e = match el_init with
  1364. | [] -> e
  1365. | _ -> mk (TBlock (List.rev (e :: el_init))) e.etype e.epos
  1366. in
  1367. add v e (IKCtor(cf,c.cl_extern || Meta.has Meta.Extern cf.cf_meta));
  1368. | None ->
  1369. ()
  1370. end
  1371. | TObjectDecl fl when fl <> [] ->
  1372. begin try
  1373. let ev = mk (TLocal v) v.v_type e.epos in
  1374. let el = List.fold_left (fun acc (s,e) ->
  1375. if not (is_valid_ident s) then raise Exit;
  1376. let ef = mk (TField(ev,FDynamic s)) e.etype e.epos in
  1377. let e = mk (TBinop(OpAssign,ef,e)) e.etype e.epos in
  1378. e :: acc
  1379. ) el_init fl in
  1380. let e = mk (TBlock (List.rev el)) ctx.t.tvoid e.epos in
  1381. add v e IKStructure
  1382. with Exit ->
  1383. ()
  1384. end
  1385. | TArrayDecl el ->
  1386. let ev = mk (TLocal v) v.v_type e.epos in
  1387. let el,_ = List.fold_left (fun (acc,i) e ->
  1388. let ef = mk (TField(ev,FDynamic (string_of_int i))) e.etype e.epos in
  1389. let e = mk (TBinop(OpAssign,ef,e)) e.etype e.epos in
  1390. e :: acc,i + 1
  1391. ) (el_init,0) el in
  1392. let e = mk (TBlock (List.rev el)) ctx.t.tvoid e.epos in
  1393. add v e (IKArray (List.length el))
  1394. | TCast(e1,None) | TParenthesis e1 ->
  1395. loop el_init e1
  1396. | _ ->
  1397. ()
  1398. in
  1399. loop [] e1
  1400. | TBinop(OpAssign,{eexpr = TField({eexpr = TLocal v},_)},e2) when v.v_id < 0 ->
  1401. find_locals e2
  1402. | TField({eexpr = TLocal v},fa) when v.v_id < 0 ->
  1403. begin match extract_field fa with
  1404. | Some {cf_kind = Var _} -> ()
  1405. | _ -> cancel v e.epos
  1406. end
  1407. | TArray({eexpr = TLocal v},{eexpr = TConst (TInt i)}) when v.v_id < 0 ->
  1408. let i = Int32.to_int i in
  1409. begin try
  1410. let ii = IntMap.find v.v_id !vars in
  1411. let l = match ii.ii_kind with
  1412. | IKArray l -> l
  1413. | _ -> raise Not_found
  1414. in
  1415. if i < 0 || i >= l then raise Not_found;
  1416. with Not_found ->
  1417. cancel v e.epos
  1418. end
  1419. | TLocal v when v.v_id < 0 ->
  1420. cancel v e.epos;
  1421. | _ ->
  1422. Type.iter find_locals e
  1423. in
  1424. find_locals e;
  1425. (* Pass 2 *)
  1426. let get_field_var v s =
  1427. let ii = IntMap.find v.v_id !vars in
  1428. PMap.find s ii.ii_fields
  1429. in
  1430. let add_field_var v s t =
  1431. let ii = IntMap.find v.v_id !vars in
  1432. let v' = alloc_var (Printf.sprintf "%s_%s" v.v_name s) t in
  1433. ii.ii_fields <- PMap.add s v' ii.ii_fields;
  1434. v'
  1435. in
  1436. let inline v p =
  1437. try
  1438. let ii = IntMap.find v.v_id !vars in
  1439. Some ii.ii_expr
  1440. with Not_found ->
  1441. None
  1442. in
  1443. let assign_or_declare v name e2 t p =
  1444. try
  1445. let v = get_field_var v name in
  1446. let e1 = mk (TLocal v) t p in
  1447. {e with eexpr = TBinop(OpAssign,e1,e2)}
  1448. with Not_found ->
  1449. let v = add_field_var v name t in
  1450. mk (TVar(v,Some e2)) ctx.t.tvoid e.epos
  1451. in
  1452. let use_local_or_null v name t p =
  1453. try
  1454. let v' = get_field_var v name in
  1455. mk (TLocal v') t p
  1456. with Not_found -> try
  1457. if name <> "length" then raise Not_found;
  1458. let ii = IntMap.find v.v_id !vars in
  1459. begin match ii.ii_kind with
  1460. | IKArray l -> mk (TConst (TInt (Int32.of_int l))) ctx.t.tint p
  1461. | _ -> raise Not_found
  1462. end
  1463. with Not_found ->
  1464. mk (TConst TNull) t p
  1465. in
  1466. let flatten e =
  1467. let el = ref [] in
  1468. let rec loop e = match e.eexpr with
  1469. | TBlock el ->
  1470. List.iter loop el
  1471. | _ ->
  1472. el := e :: !el
  1473. in
  1474. loop e;
  1475. let e = mk (TBlock (List.rev !el)) e.etype e.epos in
  1476. mk (TMeta((Meta.MergeBlock,[],e.epos),e)) e.etype e.epos
  1477. in
  1478. let rec loop e = match e.eexpr with
  1479. | TVar(v,_) when v.v_id < 0 ->
  1480. begin match inline v e.epos with
  1481. | Some e ->
  1482. let e = flatten e in
  1483. loop e
  1484. | None ->
  1485. cancel v e.epos;
  1486. e
  1487. end
  1488. | TBinop(OpAssign,({eexpr = TField({eexpr = TLocal v},fa)} as e1),e2) when v.v_id < 0 ->
  1489. let e2 = loop e2 in
  1490. assign_or_declare v (field_name fa) e2 e1.etype e.epos
  1491. | TField({eexpr = TLocal v},fa) when v.v_id < 0 ->
  1492. use_local_or_null v (field_name fa) e.etype e.epos
  1493. | TBinop(OpAssign,({eexpr = TArray({eexpr = TLocal v},{eexpr = TConst (TInt i)})} as e1),e2) when v.v_id < 0 ->
  1494. let e2 = loop e2 in
  1495. let name = int_field_name (Int32.to_int i) in
  1496. assign_or_declare v name e2 e1.etype e.epos
  1497. | TArray({eexpr = TLocal v},{eexpr = TConst (TInt i)}) when v.v_id < 0 ->
  1498. use_local_or_null v (int_field_name (Int32.to_int i)) e.etype e.epos
  1499. | TBlock el ->
  1500. let rec block acc el = match el with
  1501. | e1 :: el ->
  1502. begin match loop e1 with
  1503. | {eexpr = TMeta((Meta.MergeBlock,_,_),{eexpr = TBlock el2})} ->
  1504. let acc = block acc el2 in
  1505. block acc el
  1506. | e -> block (e :: acc) el
  1507. end
  1508. | [] ->
  1509. acc
  1510. in
  1511. let el = block [] el in
  1512. mk (TBlock (List.rev el)) e.etype e.epos
  1513. | _ ->
  1514. Type.map_expr loop e
  1515. in
  1516. loop e
  1517. (* ---------------------------------------------------------------------- *)
  1518. (* COMPLETION *)
  1519. exception Return of Ast.expr
  1520. type compl_locals = {
  1521. mutable r : (string, (complex_type option * (int * Ast.expr * compl_locals) option)) PMap.t;
  1522. }
  1523. let optimize_completion_expr e =
  1524. let iid = ref 0 in
  1525. let typing_side_effect = ref false in
  1526. let locals : compl_locals = { r = PMap.empty } in
  1527. let save() = let old = locals.r in (fun() -> locals.r <- old) in
  1528. let get_local n = PMap.find n locals.r in
  1529. let maybe_typed e =
  1530. match fst e with
  1531. | EConst (Ident "null") -> false
  1532. | _ -> true
  1533. in
  1534. let decl n t e =
  1535. typing_side_effect := true;
  1536. locals.r <- PMap.add n (t,(match e with Some e when maybe_typed e -> incr iid; Some (!iid,e,{ r = locals.r }) | _ -> None)) locals.r
  1537. in
  1538. let rec loop e =
  1539. let p = snd e in
  1540. match fst e with
  1541. | EConst (Ident n) ->
  1542. (try
  1543. (match get_local n with
  1544. | Some _ , _ -> ()
  1545. | _ -> typing_side_effect := true)
  1546. with Not_found ->
  1547. ());
  1548. e
  1549. | EBinop (OpAssign,(EConst (Ident n),_),esub) ->
  1550. (try
  1551. (match get_local n with
  1552. | None, None when maybe_typed esub -> decl n None (Some esub)
  1553. | _ -> ())
  1554. with Not_found ->
  1555. ());
  1556. map e
  1557. | EVars vl ->
  1558. let vl = List.map (fun (v,t,e) ->
  1559. let e = (match e with None -> None | Some e -> Some (loop e)) in
  1560. decl v t e;
  1561. (v,t,e)
  1562. ) vl in
  1563. (EVars vl,p)
  1564. | EBlock el ->
  1565. let old = save() in
  1566. let told = ref (!typing_side_effect) in
  1567. let el = List.fold_left (fun acc e ->
  1568. typing_side_effect := false;
  1569. let e = loop e in
  1570. if !typing_side_effect then begin told := true; e :: acc end else acc
  1571. ) [] el in
  1572. old();
  1573. typing_side_effect := !told;
  1574. (EBlock (List.rev el),p)
  1575. | EFunction (v,f) ->
  1576. (match v with
  1577. | None -> ()
  1578. | Some name ->
  1579. decl name None (Some e));
  1580. let old = save() in
  1581. List.iter (fun (n,_,t,e) -> decl n t e) f.f_args;
  1582. let e = map e in
  1583. old();
  1584. e
  1585. | EFor ((EIn ((EConst (Ident n),_) as id,it),p),efor) ->
  1586. let it = loop it in
  1587. let old = save() in
  1588. let etmp = (EConst (Ident "$tmp"),p) in
  1589. decl n None (Some (EBlock [
  1590. (EVars ["$tmp",None,None],p);
  1591. (EFor ((EIn (id,it),p),(EBinop (OpAssign,etmp,(EConst (Ident n),p)),p)),p);
  1592. etmp
  1593. ],p));
  1594. let efor = loop efor in
  1595. old();
  1596. (EFor ((EIn (id,it),p),efor),p)
  1597. | EReturn _ ->
  1598. typing_side_effect := true;
  1599. map e
  1600. | ESwitch (e,cases,def) ->
  1601. let e = loop e in
  1602. let cases = List.map (fun (el,eg,eo) -> match eo with
  1603. | None ->
  1604. el,eg,eo
  1605. | Some e ->
  1606. let el = List.map loop el in
  1607. let old = save() in
  1608. List.iter (fun e ->
  1609. match fst e with
  1610. | ECall (_,pl) ->
  1611. List.iter (fun p ->
  1612. match fst p with
  1613. | EConst (Ident i) -> decl i None None (* sadly *)
  1614. | _ -> ()
  1615. ) pl
  1616. | _ -> ()
  1617. ) el;
  1618. let e = loop e in
  1619. old();
  1620. el, eg, Some e
  1621. ) cases in
  1622. let def = match def with
  1623. | None -> None
  1624. | Some None -> Some None
  1625. | Some (Some e) -> Some (Some (loop e))
  1626. in
  1627. (ESwitch (e,cases,def),p)
  1628. | ETry (et,cl) ->
  1629. let et = loop et in
  1630. let cl = List.map (fun (n,t,e) ->
  1631. let old = save() in
  1632. decl n (Some t) None;
  1633. let e = loop e in
  1634. old();
  1635. n, t, e
  1636. ) cl in
  1637. (ETry (et,cl),p)
  1638. | EDisplay (s,call) ->
  1639. typing_side_effect := true;
  1640. let tmp_locals = ref [] in
  1641. let tmp_hlocals = ref PMap.empty in
  1642. let rec subst_locals locals e =
  1643. match fst e with
  1644. | EConst (Ident n) ->
  1645. let p = snd e in
  1646. (try
  1647. (match PMap.find n locals.r with
  1648. | Some t , _ -> (ECheckType ((EConst (Ident "null"),p),t),p)
  1649. | _, Some (id,e,lc) ->
  1650. let name = (try
  1651. PMap.find id (!tmp_hlocals)
  1652. with Not_found ->
  1653. let e = subst_locals lc e in
  1654. let name = "$tmp_" ^ string_of_int id in
  1655. tmp_locals := (name,None,Some e) :: !tmp_locals;
  1656. tmp_hlocals := PMap.add id name !tmp_hlocals;
  1657. name
  1658. ) in
  1659. (EConst (Ident name),p)
  1660. | None, None ->
  1661. (* we can't replace the var *)
  1662. raise Exit)
  1663. with Not_found ->
  1664. (* not found locals are most likely to be member/static vars *)
  1665. e)
  1666. | EFunction (_,f) ->
  1667. Ast.map_expr (subst_locals { r = PMap.foldi (fun n i acc -> if List.exists (fun (a,_,_,_) -> a = n) f.f_args then acc else PMap.add n i acc) locals.r PMap.empty }) e
  1668. | EObjectDecl [] ->
  1669. (* this probably comes from { | completion so we need some context} *)
  1670. raise Exit
  1671. | _ ->
  1672. Ast.map_expr (subst_locals locals) e
  1673. in
  1674. (try
  1675. let e = subst_locals locals s in
  1676. let e = (EBlock [(EVars (List.rev !tmp_locals),p);(EDisplay (e,call),p)],p) in
  1677. raise (Return e)
  1678. with Exit ->
  1679. map e)
  1680. | EDisplayNew _ ->
  1681. raise (Return e)
  1682. | _ ->
  1683. map e
  1684. and map e =
  1685. Ast.map_expr loop e
  1686. in
  1687. (try loop e with Return e -> e)
  1688. (* ---------------------------------------------------------------------- *)