optimizer.ml 53 KB

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