optimizer.ml 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. (*
  2. * Haxe Compiler
  3. * Copyright (c)2005-2008 Nicolas Cannasse
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *)
  19. open Ast
  20. open Type
  21. open Common
  22. open Typecore
  23. (* ---------------------------------------------------------------------- *)
  24. (* API OPTIMIZATIONS *)
  25. let has_side_effect e =
  26. let rec loop e =
  27. match e.eexpr with
  28. | TConst _ | TLocal _ | TEnumField _ | TTypeExpr _ | TFunction _ -> ()
  29. | TMatch _ | TNew _ | TCall _ | TClosure _ | TField _ | TArray _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
  30. | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
  31. | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TWhile _ | TFor _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TVars _ | TBlock _ | TObjectDecl _ -> Type.iter loop e
  32. in
  33. try
  34. loop e; false
  35. with Exit ->
  36. true
  37. let api_inline ctx c field params p =
  38. match c.cl_path, field, params with
  39. | ([],"Type"),"enumIndex",[{ eexpr = TEnumField (en,f) }] ->
  40. let c = (try PMap.find f en.e_constrs with Not_found -> assert false) in
  41. Some (mk (TConst (TInt (Int32.of_int c.ef_index))) ctx.t.tint p)
  42. | ([],"Type"),"enumIndex",[{ eexpr = TCall({ eexpr = TEnumField (en,f) },pl) }] when List.for_all (fun e -> not (has_side_effect e)) pl ->
  43. let c = (try PMap.find f en.e_constrs with Not_found -> assert false) in
  44. Some (mk (TConst (TInt (Int32.of_int c.ef_index))) ctx.t.tint p)
  45. | ([],"Std"),"int",[{ eexpr = TConst (TInt _) } as e] ->
  46. Some { e with epos = p }
  47. | ([],"Std"),"int",[{ eexpr = TConst (TFloat f) }] ->
  48. let f = float_of_string f in
  49. (match classify_float f with
  50. | FP_infinite | FP_nan ->
  51. None
  52. | _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int +. 1. ->
  53. None (* out range, keep platform-specific behavior *)
  54. | _ ->
  55. Some { eexpr = TConst (TInt (Int32.of_float f)); etype = ctx.t.tint; epos = p })
  56. | _ ->
  57. None
  58. (* ---------------------------------------------------------------------- *)
  59. (* INLINING *)
  60. type in_local = {
  61. i_var : tvar;
  62. i_subst : tvar;
  63. mutable i_captured : bool;
  64. mutable i_write : bool;
  65. mutable i_read : int;
  66. }
  67. let rec type_inline ctx cf f ethis params tret p force =
  68. (* perform some specific optimization before we inline the call since it's not possible to detect at final optimization time *)
  69. try
  70. let cl = (match follow ethis.etype with
  71. | TInst (c,_) -> c
  72. | TAnon a -> (match !(a.a_status) with Statics c -> c | _ -> raise Exit)
  73. | _ -> raise Exit
  74. ) in
  75. (match api_inline ctx cl cf.cf_name params p with
  76. | None -> raise Exit
  77. | Some e -> Some e)
  78. with Exit ->
  79. (* type substitution on both class and function type parameters *)
  80. let has_params, map_type =
  81. let rec get_params c pl =
  82. match c.cl_super with
  83. | None -> c.cl_types, pl
  84. | Some (csup,spl) ->
  85. let spl = (match apply_params c.cl_types pl (TInst (csup,spl)) with
  86. | TInst (_,pl) -> pl
  87. | _ -> assert false
  88. ) in
  89. let ct, cpl = get_params csup spl in
  90. c.cl_types @ ct, pl @ cpl
  91. in
  92. let tparams = (match follow ethis.etype with TInst (c,pl) -> get_params c pl | _ -> ([],[])) in
  93. let pmonos = List.map (fun _ -> mk_mono()) cf.cf_params in
  94. let tmonos = snd tparams @ pmonos in
  95. let tparams = fst tparams @ cf.cf_params in
  96. tparams <> [], apply_params tparams tmonos
  97. in
  98. (* locals substitution *)
  99. let locals = Hashtbl.create 0 in
  100. let local v =
  101. try
  102. Hashtbl.find locals v.v_id
  103. with Not_found ->
  104. let i = {
  105. i_var = v;
  106. i_subst = alloc_var v.v_name v.v_type;
  107. i_captured = false;
  108. i_write = false;
  109. i_read = 0;
  110. } in
  111. Hashtbl.add locals v.v_id i;
  112. Hashtbl.add locals i.i_subst.v_id i;
  113. i
  114. in
  115. let read_local v =
  116. try
  117. Hashtbl.find locals v.v_id
  118. with Not_found ->
  119. {
  120. i_var = v;
  121. i_subst = v;
  122. i_captured = false;
  123. i_write = false;
  124. i_read = 0;
  125. }
  126. in
  127. (* use default values for null/unset arguments *)
  128. let rec loop pl al =
  129. match pl, al with
  130. | _, [] -> []
  131. | e :: pl, (v, opt) :: al ->
  132. (*
  133. if we pass a Null<T> var to an inlined method that needs a T.
  134. we need to force a local var to be created on some platforms.
  135. *)
  136. if is_static_platform ctx.com && not (is_nullable v.v_type) && is_null e.etype then (local v).i_write <- true;
  137. (match e.eexpr, opt with
  138. | TConst TNull , Some c -> mk (TConst c) v.v_type e.epos
  139. | _ -> e) :: loop pl al
  140. | [], (v,opt) :: al ->
  141. mk (TConst (match opt with None -> TNull | Some c -> c)) v.v_type p :: loop [] al
  142. in
  143. (*
  144. Build the expr/var subst list
  145. *)
  146. let ethis = (match ethis.eexpr with TConst TSuper -> { ethis with eexpr = TConst TThis } | _ -> ethis) in
  147. let vthis = alloc_var "_this" ethis.etype in
  148. let inlined_vars = List.map2 (fun e (v,_) -> local v, e) (ethis :: loop params f.tf_args) ((vthis,None) :: f.tf_args) in
  149. (*
  150. here, we try to eliminate final returns from the expression tree.
  151. However, this is not entirely correct since we don't yet correctly propagate
  152. the type of returned expressions upwards ("return" expr itself being Dynamic).
  153. We also substitute variables with fresh ones that might be renamed at later stage.
  154. *)
  155. let opt f = function
  156. | None -> None
  157. | Some e -> Some (f e)
  158. in
  159. let has_vars = ref false in
  160. let in_loop = ref false in
  161. let in_local_fun = ref false in
  162. let cancel_inlining = ref false in
  163. let ret_val = (match follow f.tf_type with TEnum ({ e_path = ([],"Void") },[]) -> false | _ -> true) in
  164. let rec map term e =
  165. let po = e.epos in
  166. let e = { e with epos = p } in
  167. match e.eexpr with
  168. | TLocal v ->
  169. let l = read_local v in
  170. if !in_local_fun then l.i_captured <- true;
  171. l.i_read <- l.i_read + (if !in_loop then 2 else 1);
  172. (* never inline a function which contain a delayed macro because its bound
  173. to its variables and not the calling method *)
  174. if v.v_name = "__dollar__delay_call" then cancel_inlining := true;
  175. { e with eexpr = TLocal l.i_subst }
  176. | TConst TThis ->
  177. let l = read_local vthis in
  178. l.i_read <- l.i_read + (if !in_loop then 2 else 1);
  179. { e with eexpr = TLocal l.i_subst }
  180. | TVars vl ->
  181. has_vars := true;
  182. let vl = List.map (fun (v,e) ->
  183. (local v).i_subst,opt (map false) e
  184. ) vl in
  185. { e with eexpr = TVars vl }
  186. | TReturn eo when not !in_local_fun ->
  187. if not term then error "Cannot inline a not final return" po;
  188. (match eo with
  189. | None -> mk (TConst TNull) f.tf_type p
  190. | Some e -> map term e)
  191. | TFor (v,e1,e2) ->
  192. let i = local v in
  193. let e1 = map false e1 in
  194. let old = !in_loop in
  195. in_loop := true;
  196. let e2 = map false e2 in
  197. in_loop := old;
  198. { e with eexpr = TFor (i.i_subst,e1,e2) }
  199. | TWhile (cond,eloop,flag) ->
  200. let cond = map false cond in
  201. let old = !in_loop in
  202. in_loop := true;
  203. let eloop = map false eloop in
  204. in_loop := old;
  205. { e with eexpr = TWhile (cond,eloop,flag) }
  206. | TMatch (v,en,cases,def) ->
  207. let term = term && def <> None in
  208. let cases = List.map (fun (i,vl,e) ->
  209. let vl = opt (List.map (fun v -> opt (fun v -> (local v).i_subst) v)) vl in
  210. i, vl, map term e
  211. ) cases in
  212. let def = opt (map term) def in
  213. { 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 }
  214. | TSwitch (e1,cases,def) when term ->
  215. let term = term && def <> None in
  216. let cases = List.map (fun (el,e) ->
  217. let el = List.map (map false) el in
  218. el, map term e
  219. ) cases in
  220. let def = opt (map term) def in
  221. { 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 }
  222. | TTry (e1,catches) ->
  223. { e with eexpr = TTry (map term e1,List.map (fun (v,e) ->
  224. let lv = (local v).i_subst in
  225. let e = map term e in
  226. lv,e
  227. ) catches); etype = if term && ret_val then unify_min ctx (e1::List.map snd catches) else e.etype }
  228. | TBlock l ->
  229. let old = save_locals ctx in
  230. let t = ref e.etype in
  231. let rec loop = function
  232. | [] when term ->
  233. t := mk_mono();
  234. [mk (TConst TNull) (!t) p]
  235. | [] -> []
  236. | [e] ->
  237. let e = map term e in
  238. if term then t := e.etype;
  239. [e]
  240. | e :: l ->
  241. let e = map false e in
  242. e :: loop l
  243. in
  244. let l = loop l in
  245. old();
  246. { e with eexpr = TBlock l; etype = !t }
  247. | TIf (econd,eif,Some eelse) when term ->
  248. let econd = map false econd in
  249. let eif = map term eif in
  250. let eelse = map term eelse in
  251. { e with eexpr = TIf(econd,eif,Some eelse); etype = if ret_val then unify_min ctx [eif;eelse] else e.etype }
  252. | TParenthesis e1 ->
  253. let e1 = map term e1 in
  254. mk (TParenthesis e1) e1.etype e.epos
  255. | TUnop ((Increment|Decrement),_,{ eexpr = TLocal v }) ->
  256. (read_local v).i_write <- true;
  257. Type.map_expr (map false) e
  258. | TBinop ((OpAssign | OpAssignOp _),{ eexpr = TLocal v },_) ->
  259. (read_local v).i_write <- true;
  260. Type.map_expr (map false) e;
  261. | TFunction f ->
  262. (match f.tf_args with [] -> () | _ -> has_vars := true);
  263. let old = save_locals ctx and old_fun = !in_local_fun in
  264. let args = List.map (function(v,c) -> (local v).i_subst, c) f.tf_args in
  265. in_local_fun := true;
  266. let expr = map false f.tf_expr in
  267. in_local_fun := old_fun;
  268. old();
  269. { e with eexpr = TFunction { tf_args = args; tf_expr = expr; tf_type = f.tf_type } }
  270. | TConst TSuper ->
  271. error "Cannot inline function containing super" po
  272. | _ ->
  273. Type.map_expr (map false) e
  274. in
  275. let e = map true f.tf_expr in
  276. (*
  277. if variables are not written and used with a const value, let's substitute
  278. with the actual value, either create a temp var
  279. *)
  280. let subst = ref PMap.empty in
  281. let is_constant e =
  282. let rec loop e =
  283. match e.eexpr with
  284. | TLocal _
  285. | TConst TThis (* not really, but should not be move inside a function body *)
  286. -> raise Exit
  287. | TEnumField _
  288. | TTypeExpr _
  289. | TConst _ -> ()
  290. | _ ->
  291. Type.iter loop e
  292. in
  293. try loop e; true with Exit -> false
  294. in
  295. let vars = List.fold_left (fun acc (i,e) ->
  296. let flag = (match e.eexpr with
  297. | TLocal _ | TConst _ -> not i.i_write
  298. | TFunction _ -> if i.i_write then error "Cannot modify a closure parameter inside inline method" p; true
  299. | _ -> not i.i_write && i.i_read <= 1
  300. ) in
  301. let flag = flag && (not i.i_captured || is_constant e) in
  302. if flag then begin
  303. subst := PMap.add i.i_subst.v_id e !subst;
  304. acc
  305. end else
  306. (i.i_subst,Some e) :: acc
  307. ) [] inlined_vars in
  308. let subst = !subst in
  309. let rec inline_params e =
  310. match e.eexpr with
  311. | TLocal v -> (try PMap.find v.v_id subst with Not_found -> e)
  312. | _ -> Type.map_expr inline_params e
  313. in
  314. let e = (if PMap.is_empty subst then e else inline_params e) in
  315. let init = (match vars with [] -> None | l -> Some (mk (TVars (List.rev l)) ctx.t.tvoid p)) in
  316. (*
  317. If we have local variables and returning a value, then this will result in
  318. unoptimized JS code, so let's instead skip inlining.
  319. This could be fixed with better post process code cleanup (planed)
  320. *)
  321. if !cancel_inlining || (Common.platform ctx.com Js && not force && (init <> None || !has_vars)) then
  322. None
  323. else
  324. let wrap e =
  325. (* we can't mute the type of the expression because it is not correct to do so *)
  326. (try
  327. (* if the expression is "untyped" and we don't want to unify it accidentally ! *)
  328. (match follow e.etype with
  329. | TMono _ ->
  330. (match follow tret with
  331. | TEnum ({ e_path = [],"Void" },_) -> e
  332. | _ -> raise (Unify_error []))
  333. | _ ->
  334. type_eq EqStrict (if has_params then map_type e.etype else e.etype) tret;
  335. e)
  336. with Unify_error _ ->
  337. mk (TCast (e,None)) tret e.epos)
  338. in
  339. let e = (match e.eexpr, init, tret with
  340. | _, None, TEnum ({ e_path = [],"Void" },_) ->
  341. {e with etype = tret}
  342. | TBlock [e] , None, _ -> wrap e
  343. | _ , None, _ -> wrap e
  344. | TBlock l, Some init, _ -> mk (TBlock (init :: l)) tret e.epos
  345. | _, Some init, _ -> mk (TBlock [init;e]) tret e.epos
  346. ) in
  347. (* we need to replace type-parameters that were used in the expression *)
  348. if not has_params then
  349. Some e
  350. else
  351. let mt = map_type cf.cf_type in
  352. unify_raise ctx mt (TFun (List.map (fun e -> "",false,e.etype) params,tret)) p;
  353. (*
  354. this is very expensive since we are building the substitution list for
  355. every expression, but hopefully in such cases the expression size is small
  356. *)
  357. let vars = Hashtbl.create 0 in
  358. let map_var v =
  359. if not (Hashtbl.mem vars v.v_id) then begin
  360. Hashtbl.add vars v.v_id ();
  361. v.v_type <- map_type v.v_type;
  362. end;
  363. v
  364. in
  365. let rec map_expr_type e = Type.map_expr_type map_expr_type map_type map_var e in
  366. Some (map_expr_type e)
  367. (* ---------------------------------------------------------------------- *)
  368. (* LOOPS *)
  369. let optimize_for_loop ctx i e1 e2 p =
  370. let t_void = ctx.t.tvoid in
  371. let t_int = ctx.t.tint in
  372. let lblock el = Some (mk (TBlock el) t_void p) in
  373. match e1.eexpr, follow e1.etype with
  374. | TNew ({ cl_path = ([],"IntIter") },[],[i1;i2]) , _ ->
  375. let max = (match i1.eexpr , i2.eexpr with
  376. | TConst (TInt a), TConst (TInt b) when Int32.compare b a < 0 -> error "Range operate can't iterate backwards" p
  377. | _, TConst _ | _ , TLocal _ -> None
  378. | _ -> Some (gen_local ctx t_int)
  379. ) in
  380. let tmp = gen_local ctx t_int in
  381. let i = add_local ctx i t_int in
  382. let rec check e =
  383. match e.eexpr with
  384. | TBinop (OpAssign,{ eexpr = TLocal l },_)
  385. | TBinop (OpAssignOp _,{ eexpr = TLocal l },_)
  386. | TUnop (Increment,_,{ eexpr = TLocal l })
  387. | TUnop (Decrement,_,{ eexpr = TLocal l }) when l == i ->
  388. error "Loop variable cannot be modified" e.epos
  389. | _ ->
  390. Type.iter check e
  391. in
  392. let e2 = type_expr ctx e2 false in
  393. check e2;
  394. let etmp = mk (TLocal tmp) t_int p in
  395. let incr = mk (TUnop (Increment,Postfix,etmp)) t_int p in
  396. let init = mk (TVars [i,Some incr]) t_void p in
  397. let block = match e2.eexpr with
  398. | TBlock el -> mk (TBlock (init :: el)) t_void e2.epos
  399. | _ -> mk (TBlock [init;e2]) t_void p
  400. in
  401. (*
  402. force locals to be of Int type (to prevent Int/UInt issues)
  403. *)
  404. (match max with
  405. | None ->
  406. lblock [
  407. mk (TVars [tmp,Some i1]) t_void p;
  408. mk (TWhile (
  409. mk (TBinop (OpLt, etmp, { i2 with etype = t_int })) ctx.t.tbool p,
  410. block,
  411. NormalWhile
  412. )) t_void p;
  413. ]
  414. | Some max ->
  415. lblock [
  416. mk (TVars [tmp,Some i1;max,Some i2]) t_void p;
  417. mk (TWhile (
  418. mk (TBinop (OpLt, etmp, mk (TLocal max) t_int p)) ctx.t.tbool p,
  419. block,
  420. NormalWhile
  421. )) t_void p;
  422. ])
  423. | _ , TInst({ cl_path = [],"Array" },[pt])
  424. | _ , TInst({ cl_path = ["flash"],"Vector" },[pt]) ->
  425. let i = add_local ctx i pt in
  426. let index = gen_local ctx t_int in
  427. let arr, avars = (match e1.eexpr with
  428. | TLocal _ -> e1, []
  429. | _ ->
  430. let atmp = gen_local ctx e1.etype in
  431. mk (TLocal atmp) e1.etype e1.epos, [atmp,Some e1]
  432. ) in
  433. let iexpr = mk (TLocal index) t_int p in
  434. let e2 = type_expr ctx e2 false in
  435. let aget = mk (TVars [i,Some (mk (TArray (arr,iexpr)) pt p)]) t_void p in
  436. let incr = mk (TUnop (Increment,Prefix,iexpr)) t_int p in
  437. let block = match e2.eexpr with
  438. | TBlock el -> mk (TBlock (aget :: incr :: el)) t_void e2.epos
  439. | _ -> mk (TBlock [aget;incr;e2]) t_void p
  440. in
  441. let ivar = index, Some (mk (TConst (TInt 0l)) t_int p) in
  442. lblock [
  443. mk (TVars (ivar :: avars)) t_void p;
  444. mk (TWhile (
  445. mk (TBinop (OpLt, iexpr, mk (TField (arr,"length")) t_int p)) ctx.t.tbool p,
  446. block,
  447. NormalWhile
  448. )) t_void p;
  449. ]
  450. | _ , TInst ({ cl_kind = KGenericInstance ({ cl_path = ["haxe"],"FastList" },[t]) } as c,[]) ->
  451. let tcell = (try (PMap.find "head" c.cl_fields).cf_type with Not_found -> assert false) in
  452. let i = add_local ctx i t in
  453. let cell = gen_local ctx tcell in
  454. let cexpr = mk (TLocal cell) tcell p in
  455. let e2 = type_expr ctx e2 false in
  456. let evar = mk (TVars [i,Some (mk (TField (cexpr,"elt")) t p)]) t_void p in
  457. let enext = mk (TBinop (OpAssign,cexpr,mk (TField (cexpr,"next")) tcell p)) tcell p in
  458. let block = match e2.eexpr with
  459. | TBlock el -> mk (TBlock (evar :: enext :: el)) t_void e2.epos
  460. | _ -> mk (TBlock [evar;enext;e2]) t_void p
  461. in
  462. lblock [
  463. mk (TVars [cell,Some (mk (TField (e1,"head")) tcell p)]) t_void p;
  464. mk (TWhile (
  465. mk (TBinop (OpNotEq, cexpr, mk (TConst TNull) tcell p)) ctx.t.tbool p,
  466. block,
  467. NormalWhile
  468. )) t_void p
  469. ]
  470. | _ ->
  471. None
  472. (* ---------------------------------------------------------------------- *)
  473. (* SANITIZE *)
  474. (*
  475. makes sure that when an AST get generated to source code, it will not
  476. generate expressions that evaluate differently. It is then necessary to
  477. add parenthesises around some binary expressions when the AST does not
  478. correspond to the natural operand priority order for the platform
  479. *)
  480. (*
  481. this is the standard C++ operator precedence, which is also used by both JS and PHP
  482. *)
  483. let standard_precedence op =
  484. let left = true and right = false in
  485. match op with
  486. | OpMult | OpDiv | OpMod -> 5, left
  487. | OpAdd | OpSub -> 6, left
  488. | OpShl | OpShr | OpUShr -> 7, left
  489. | OpLt | OpLte | OpGt | OpGte -> 8, left
  490. | OpEq | OpNotEq -> 9, left
  491. | OpAnd -> 10, left
  492. | OpXor -> 11, left
  493. | OpOr -> 12, left
  494. | OpInterval -> 13, right (* haxe specific *)
  495. | OpBoolAnd -> 14, left
  496. | OpBoolOr -> 15, left
  497. | OpAssignOp OpAssign -> 16, right (* mimics ?: *)
  498. | OpAssign | OpAssignOp _ -> 17, right
  499. let rec need_parent e =
  500. match e.eexpr with
  501. | TConst _ | TLocal _ | TEnumField _ | TArray _ | TField _ | TParenthesis _ | TCall _ | TClosure _ | TNew _ | TTypeExpr _ | TObjectDecl _ | TArrayDecl _ -> false
  502. | TCast (e,None) -> need_parent e
  503. | TCast _ | TThrow _ | TReturn _ | TTry _ | TMatch _ | TSwitch _ | TFor _ | TIf _ | TWhile _ | TBinop _ | TContinue | TBreak
  504. | TBlock _ | TVars _ | TFunction _ | TUnop _ -> true
  505. let rec add_final_return e t =
  506. let def_return p =
  507. let c = (match follow t with
  508. | TInst ({ cl_path = [],"Int" },_) -> TInt 0l
  509. | TInst ({ cl_path = [],"Float" },_) -> TFloat "0."
  510. | TEnum ({ e_path = [],"Bool" },_) -> TBool false
  511. | _ -> TNull
  512. ) in
  513. { eexpr = TReturn (Some { eexpr = TConst c; epos = p; etype = t }); etype = t; epos = p }
  514. in
  515. match e.eexpr with
  516. | TBlock el ->
  517. (match List.rev el with
  518. | [] -> e
  519. | elast :: el ->
  520. match add_final_return elast t with
  521. | { eexpr = TBlock el2 } -> { e with eexpr = TBlock ((List.rev el) @ el2) }
  522. | elast -> { e with eexpr = TBlock (List.rev (elast :: el)) })
  523. | TReturn _ ->
  524. e
  525. | _ ->
  526. { e with eexpr = TBlock [e;def_return e.epos] }
  527. let sanitize_expr com e =
  528. let parent e =
  529. match e.eexpr with
  530. | TParenthesis _ -> e
  531. | _ -> mk (TParenthesis e) e.etype e.epos
  532. in
  533. let block e =
  534. match e.eexpr with
  535. | TBlock _ -> e
  536. | _ -> mk (TBlock [e]) e.etype e.epos
  537. in
  538. let complex e =
  539. (* complex expressions are the one that once generated to source consists in several expressions *)
  540. match e.eexpr with
  541. | TVars _ (* needs to be put into blocks *)
  542. | TFor _ (* a temp var is needed for holding iterator *)
  543. | TMatch _ (* a temp var is needed for holding enum *)
  544. | TCall ({ eexpr = TLocal { v_name = "__js__" } },_) (* we never know *)
  545. -> block e
  546. | _ -> e
  547. in
  548. (* tells if the printed expresssion ends with an if without else *)
  549. let rec has_if e =
  550. match e.eexpr with
  551. | TIf (_,_,None) -> true
  552. | TWhile (_,e,NormalWhile) -> has_if e
  553. | TFor (_,_,e) -> has_if e
  554. | _ -> false
  555. in
  556. match e.eexpr with
  557. | TConst TNull ->
  558. if is_static_platform com && not (is_nullable e.etype) then
  559. (match follow e.etype with
  560. | TMono _ -> () (* in these cases the null will cast to default value *)
  561. | TFun _ -> () (* this is a bit a particular case, maybe flash-specific actually *)
  562. | _ -> com.error ("On static platforms, null can't be used as basic type " ^ s_type (print_context()) e.etype) e.epos);
  563. e
  564. | TBinop (op,e1,e2) ->
  565. let swap op1 op2 =
  566. let p1, left1 = standard_precedence op1 in
  567. let p2, _ = standard_precedence op2 in
  568. left1 && p1 <= p2
  569. in
  570. let rec loop ee left =
  571. match ee.eexpr with
  572. | TBinop (op2,_,_) -> if left then not (swap op2 op) else swap op op2
  573. | TIf _ -> if left then not (swap (OpAssignOp OpAssign) op) else swap op (OpAssignOp OpAssign)
  574. | TCast (e,None) -> loop e left
  575. | _ -> false
  576. in
  577. let e1 = if loop e1 true then parent e1 else e1 in
  578. let e2 = if loop e2 false then parent e2 else e2 in
  579. { e with eexpr = TBinop (op,e1,e2) }
  580. | TUnop (op,mode,e2) ->
  581. let rec loop ee =
  582. match ee.eexpr with
  583. | TBinop _ | TIf _ -> parent e2
  584. | TCast (e,None) -> loop e
  585. | _ -> e2
  586. in
  587. { e with eexpr = TUnop (op,mode,loop e2) }
  588. | TIf (e1,e2,eelse) ->
  589. let e1 = parent e1 in
  590. let e2 = (if (eelse <> None && has_if e2) || (match e2.eexpr with TIf _ -> true | _ -> false) then block e2 else complex e2) in
  591. let eelse = (match eelse with None -> None | Some e -> Some (complex e)) in
  592. { e with eexpr = TIf (e1,e2,eelse) }
  593. | TWhile (e1,e2,flag) ->
  594. let e1 = parent e1 in
  595. let e2 = complex e2 in
  596. { e with eexpr = TWhile (e1,e2,flag) }
  597. | TFor (v,e1,e2) ->
  598. let e2 = complex e2 in
  599. { e with eexpr = TFor (v,e1,e2) }
  600. | TFunction f ->
  601. let f = (match com.platform, follow f.tf_type with
  602. | _, TEnum ({ e_path = [],"Void" },[]) -> f
  603. | Flash , t when Common.defined com "as3" -> { f with tf_expr = add_final_return f.tf_expr t }
  604. | Cpp, t -> { f with tf_expr = add_final_return f.tf_expr t }
  605. | _ -> f
  606. ) in
  607. let f = (match f.tf_expr.eexpr with
  608. | TBlock _ -> f
  609. | _ -> { f with tf_expr = block f.tf_expr }
  610. ) in
  611. { e with eexpr = TFunction f }
  612. | TCall (e2,args) ->
  613. if need_parent e2 then { e with eexpr = TCall(parent e2,args) } else e
  614. | TField (e2,f) ->
  615. if need_parent e2 then { e with eexpr = TField(parent e2,f) } else e
  616. | TArray (e1,e2) ->
  617. if need_parent e1 then { e with eexpr = TArray(parent e1,e2) } else e
  618. | TTry (e1,catches) ->
  619. let e1 = block e1 in
  620. let catches = List.map (fun (v,e) -> v, block e) catches in
  621. { e with eexpr = TTry (e1,catches) }
  622. | TSwitch (e1,cases,def) ->
  623. let e1 = parent e1 in
  624. let cases = List.map (fun (el,e) -> el, complex e) cases in
  625. let def = (match def with None -> None | Some e -> Some (complex e)) in
  626. { e with eexpr = TSwitch (e1,cases,def) }
  627. | TMatch (e1, en, cases, def) ->
  628. let e1 = parent e1 in
  629. let cases = List.map (fun (el,vars,e) -> el, vars, complex e) cases in
  630. let def = (match def with None -> None | Some e -> Some (complex e)) in
  631. { e with eexpr = TMatch (e1,en,cases,def) }
  632. | _ ->
  633. e
  634. let reduce_expr ctx e =
  635. match e.eexpr with
  636. | TSwitch (_,cases,_) ->
  637. List.iter (fun (cl,_) ->
  638. List.iter (fun e ->
  639. match e.eexpr with
  640. | TCall ({ eexpr = TEnumField _ },_) -> error "Not-constant enum in switch cannot be matched" e.epos
  641. | _ -> ()
  642. ) cl
  643. ) cases;
  644. e
  645. | TBlock l ->
  646. (match List.rev l with
  647. | [] -> e
  648. | ec :: l ->
  649. (* remove all no-ops : not-final constants in blocks *)
  650. match List.filter (fun e -> match e.eexpr with TConst _ -> false | _ -> true) l with
  651. | [] -> { ec with epos = e.epos }
  652. | l -> { e with eexpr = TBlock (List.rev (ec :: l)) })
  653. | TParenthesis ec ->
  654. { ec with epos = e.epos }
  655. | TTry (e,[]) ->
  656. e
  657. | _ ->
  658. e
  659. let rec sanitize ctx e =
  660. sanitize_expr ctx.com (reduce_expr ctx (Type.map_expr (sanitize ctx) e))
  661. (* ---------------------------------------------------------------------- *)
  662. (* REDUCE *)
  663. let rec reduce_loop ctx e =
  664. let is_float t =
  665. match follow t with
  666. | TInst ({ cl_path = ([],"Float") },_) -> true
  667. | _ -> false
  668. in
  669. let is_numeric t =
  670. match follow t with
  671. | TInst ({ cl_path = ([],("Float" | "Int")) },_) -> true
  672. | _ -> false
  673. in
  674. let e = Type.map_expr (reduce_loop ctx) e in
  675. let check_float op f1 f2 =
  676. let f = op f1 f2 in
  677. let fstr = string_of_float f in
  678. 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
  679. in
  680. sanitize_expr ctx.com (match e.eexpr with
  681. | TIf ({ eexpr = TConst (TBool t) },e1,e2) ->
  682. (if t then e1 else match e2 with None -> { e with eexpr = TBlock [] } | Some e -> e)
  683. | TWhile ({ eexpr = TConst (TBool false) },sub,flag) ->
  684. (match flag with
  685. | NormalWhile -> { e with eexpr = TBlock [] } (* erase sub *)
  686. | DoWhile -> e) (* we cant remove while since sub can contain continue/break *)
  687. | TBinop (op,e1,e2) ->
  688. (match e1.eexpr, e2.eexpr with
  689. | TConst (TInt 0l) , _ when op = OpAdd && is_numeric e2.etype -> e2
  690. | TConst (TInt 1l) , _ when op = OpMult -> e2
  691. | TConst (TFloat v) , _ when op = OpAdd && float_of_string v = 0. && is_float e2.etype -> e2
  692. | TConst (TFloat v) , _ when op = OpMult && float_of_string v = 1. && is_float e2.etype -> e2
  693. | _ , TConst (TInt 0l) when (match op with OpAdd -> is_numeric e1.etype | OpSub | OpShr | OpShl -> true | _ -> false) -> e1 (* bits operations might cause overflow *)
  694. | _ , TConst (TInt 1l) when op = OpMult -> e1
  695. | _ , 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 *)
  696. | _ , TConst (TFloat v) when op = OpMult && float_of_string v = 1. && is_float e1.etype -> e1
  697. | TConst TNull, TConst TNull ->
  698. (match op with
  699. | OpEq -> { e with eexpr = TConst (TBool true) }
  700. | OpNotEq -> { e with eexpr = TConst (TBool false) }
  701. | _ -> e)
  702. | TConst (TInt a), TConst (TInt b) ->
  703. let opt f = try { e with eexpr = TConst (TInt (f a b)) } with Exit -> e in
  704. let check_overflow f =
  705. opt (fun a b ->
  706. let v = f (Int64.of_int32 a) (Int64.of_int32 b) in
  707. let iv = Int64.to_int32 v in
  708. if Int64.compare (Int64.of_int32 iv) v <> 0 then raise Exit;
  709. iv
  710. )
  711. in
  712. let ebool t =
  713. { e with eexpr = TConst (TBool (t (Int32.compare a b) 0)) }
  714. in
  715. (match op with
  716. | OpAdd -> check_overflow Int64.add
  717. | OpSub -> check_overflow Int64.sub
  718. | OpMult -> check_overflow Int64.mul
  719. | OpDiv -> check_float ( /. ) (Int32.to_float a) (Int32.to_float b)
  720. | OpAnd -> opt Int32.logand
  721. | OpOr -> opt Int32.logor
  722. | OpXor -> opt Int32.logxor
  723. | OpShl -> opt (fun a b -> Int32.shift_left a (Int32.to_int b))
  724. | OpShr -> opt (fun a b -> Int32.shift_right a (Int32.to_int b))
  725. | OpUShr -> opt (fun a b -> Int32.shift_right_logical a (Int32.to_int b))
  726. | OpEq -> ebool (=)
  727. | OpNotEq -> ebool (<>)
  728. | OpGt -> ebool (>)
  729. | OpGte -> ebool (>=)
  730. | OpLt -> ebool (<)
  731. | OpLte -> ebool (<=)
  732. | _ -> e)
  733. | TConst ((TFloat _ | TInt _) as ca), TConst ((TFloat _ | TInt _) as cb) ->
  734. let fa = (match ca with
  735. | TFloat a -> float_of_string a
  736. | TInt a -> Int32.to_float a
  737. | _ -> assert false
  738. ) in
  739. let fb = (match cb with
  740. | TFloat b -> float_of_string b
  741. | TInt b -> Int32.to_float b
  742. | _ -> assert false
  743. ) in
  744. let fop op = check_float op fa fb in
  745. let ebool t =
  746. { e with eexpr = TConst (TBool (t (compare fa fb) 0)) }
  747. in
  748. (match op with
  749. | OpAdd -> fop (+.)
  750. | OpDiv -> fop (/.)
  751. | OpSub -> fop (-.)
  752. | OpMult -> fop ( *. )
  753. | OpEq -> ebool (=)
  754. | OpNotEq -> ebool (<>)
  755. | OpGt -> ebool (>)
  756. | OpGte -> ebool (>=)
  757. | OpLt -> ebool (<)
  758. | OpLte -> ebool (<=)
  759. | _ -> e)
  760. | TConst (TBool a), TConst (TBool b) ->
  761. let ebool f =
  762. { e with eexpr = TConst (TBool (f a b)) }
  763. in
  764. (match op with
  765. | OpEq -> ebool (=)
  766. | OpNotEq -> ebool (<>)
  767. | OpBoolAnd -> ebool (&&)
  768. | OpBoolOr -> ebool (||)
  769. | _ -> e)
  770. | TConst a, TConst b when op = OpEq || op = OpNotEq ->
  771. let ebool b =
  772. { e with eexpr = TConst (TBool (if op = OpEq then b else not b)) }
  773. in
  774. (match a, b with
  775. | TInt a, TFloat b | TFloat b, TInt a -> ebool (Int32.to_float a = float_of_string b)
  776. | _ -> ebool (a = b))
  777. | TConst (TBool a), _ ->
  778. (match op with
  779. | OpBoolAnd -> if a then e2 else { e with eexpr = TConst (TBool false) }
  780. | OpBoolOr -> if a then { e with eexpr = TConst (TBool true) } else e2
  781. | _ -> e)
  782. | _ , TConst (TBool a) ->
  783. (match op with
  784. | OpBoolAnd when a -> e1
  785. | OpBoolOr when not a -> e1
  786. | _ -> e)
  787. | TEnumField (e1,f1), TEnumField (e2,f2) when e1 == e2 ->
  788. (match op with
  789. | OpEq -> { e with eexpr = TConst (TBool (f1 = f2)) }
  790. | OpNotEq -> { e with eexpr = TConst (TBool (f1 <> f2)) }
  791. | _ -> e)
  792. | _, TCall ({ eexpr = TEnumField _ },_) | TCall ({ eexpr = TEnumField _ },_), _ ->
  793. (match op with
  794. | OpAssign -> e
  795. | _ ->
  796. error "You cannot directly compare enums with arguments. Use either 'switch' or 'Type.enumEq'" e.epos)
  797. | _ ->
  798. e)
  799. | TUnop (op,flag,esub) ->
  800. (match op, esub.eexpr with
  801. | Not, TConst (TBool f) -> { e with eexpr = TConst (TBool (not f)) }
  802. | Neg, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.neg i)) }
  803. | NegBits, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.lognot i)) }
  804. | Neg, TConst (TFloat f) ->
  805. let v = 0. -. float_of_string f in
  806. let vstr = string_of_float v in
  807. if float_of_string vstr = v then
  808. { e with eexpr = TConst (TFloat vstr) }
  809. else
  810. e
  811. | _ -> e
  812. )
  813. | TCall ({ eexpr = TField ({ eexpr = TTypeExpr (TClassDecl c) },field) },params) ->
  814. (match api_inline ctx c field params e.epos with
  815. | None -> reduce_expr ctx e
  816. | Some e -> reduce_loop ctx e)
  817. | TCall ({ eexpr = TFunction func } as ef,el) ->
  818. let cf = mk_field "" ef.etype e.epos in
  819. let ethis = mk (TConst TThis) t_dynamic e.epos in
  820. let rt = (match follow ef.etype with TFun (_,rt) -> rt | _ -> assert false) in
  821. let inl = (try type_inline ctx cf func ethis el rt e.epos false with Error (Custom _,_) -> None) in
  822. (match inl with
  823. | None -> reduce_expr ctx e
  824. | Some e -> reduce_loop ctx e)
  825. | TCall ({ eexpr = TClosure (o,name) } as f,el) ->
  826. { e with eexpr = TCall ({ f with eexpr = TField (o,name) },el) }
  827. | _ ->
  828. reduce_expr ctx e)
  829. let reduce_expression ctx e =
  830. if ctx.com.foptimize then reduce_loop ctx e else e
  831. (* ---------------------------------------------------------------------- *)
  832. (* COMPLETION *)
  833. exception Return of Ast.expr
  834. type compl_locals = {
  835. mutable r : (string, (complex_type option * (int * Ast.expr * compl_locals) option)) PMap.t;
  836. }
  837. let optimize_completion_expr e =
  838. let iid = ref 0 in
  839. let typing_side_effect = ref false in
  840. let locals : compl_locals = { r = PMap.empty } in
  841. let save() = let old = locals.r in (fun() -> locals.r <- old) in
  842. let get_local n = PMap.find n locals.r in
  843. let maybe_typed e =
  844. match fst e with
  845. | EConst (Ident "null") -> false
  846. | _ -> true
  847. in
  848. let decl n t e =
  849. typing_side_effect := true;
  850. 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
  851. in
  852. let rec loop e =
  853. let p = snd e in
  854. match fst e with
  855. | EConst (Ident n) ->
  856. (try
  857. (match get_local n with
  858. | Some _ , _ -> ()
  859. | _ -> typing_side_effect := true)
  860. with Not_found ->
  861. ());
  862. e
  863. | EBinop (OpAssign,(EConst (Ident n),_),esub) ->
  864. (try
  865. (match get_local n with
  866. | None, None when maybe_typed esub -> decl n None (Some esub)
  867. | _ -> ())
  868. with Not_found ->
  869. ());
  870. map e
  871. | EVars vl ->
  872. let vl = List.map (fun (v,t,e) ->
  873. let e = (match e with None -> None | Some e -> Some (loop e)) in
  874. decl v t e;
  875. (v,t,e)
  876. ) vl in
  877. (EVars vl,p)
  878. | EBlock el ->
  879. let old = save() in
  880. let told = ref (!typing_side_effect) in
  881. let el = List.fold_left (fun acc e ->
  882. typing_side_effect := false;
  883. let e = loop e in
  884. if !typing_side_effect then begin told := true; e :: acc end else acc
  885. ) [] el in
  886. old();
  887. typing_side_effect := !told;
  888. (EBlock (List.rev el),p)
  889. | EFunction (v,f) ->
  890. (match v with
  891. | None -> ()
  892. | Some name ->
  893. decl name None (Some e));
  894. let old = save() in
  895. List.iter (fun (n,_,t,e) -> decl n t e) f.f_args;
  896. let e = map e in
  897. old();
  898. e
  899. | EFor ((EIn ((EConst (Ident n),_) as id,it),p),efor) ->
  900. let it = loop it in
  901. let old = save() in
  902. decl n None (Some (ECall ((EField ((ECall ((EField (it,"iterator"),p),[]),p),"next"),p),[]),p));
  903. let efor = loop efor in
  904. old();
  905. (EFor ((EIn (id,it),p),efor),p)
  906. | EReturn _ ->
  907. typing_side_effect := true;
  908. map e
  909. | ESwitch (e,cases,def) ->
  910. let e = loop e in
  911. let cases = List.map (fun (el,e) ->
  912. let el = List.map loop el in
  913. let old = save() in
  914. List.iter (fun e ->
  915. match fst e with
  916. | ECall (_,pl) ->
  917. List.iter (fun p ->
  918. match fst p with
  919. | EConst (Ident i) -> decl i None None (* sadly *)
  920. | _ -> ()
  921. ) pl
  922. | _ -> ()
  923. ) el;
  924. let e = loop e in
  925. old();
  926. el, e
  927. ) cases in
  928. let def = (match def with None -> None | Some e -> Some (loop e)) in
  929. (ESwitch (e,cases,def),p)
  930. | ETry (et,cl) ->
  931. let et = loop et in
  932. let cl = List.map (fun (n,t,e) ->
  933. let old = save() in
  934. decl n (Some t) None;
  935. let e = loop e in
  936. old();
  937. n, t, e
  938. ) cl in
  939. (ETry (et,cl),p)
  940. | EDisplay (s,call) ->
  941. typing_side_effect := true;
  942. let tmp_locals = ref [] in
  943. let tmp_hlocals = ref PMap.empty in
  944. let rec subst_locals locals e =
  945. match fst e with
  946. | EConst (Ident n) ->
  947. let p = snd e in
  948. (try
  949. (match PMap.find n locals.r with
  950. | Some t , _ -> (ECheckType ((EConst (Ident "null"),p),t),p)
  951. | _, Some (id,e,lc) ->
  952. let name = (try
  953. PMap.find id (!tmp_hlocals)
  954. with Not_found ->
  955. let e = subst_locals lc e in
  956. let name = "$tmp_" ^ string_of_int id in
  957. tmp_locals := (name,None,Some e) :: !tmp_locals;
  958. tmp_hlocals := PMap.add id name !tmp_hlocals;
  959. name
  960. ) in
  961. (EConst (Ident name),p)
  962. | None, None ->
  963. (* we can't replace the var *)
  964. raise Exit)
  965. with Not_found ->
  966. (* not found locals are most likely to be member/static vars *)
  967. e)
  968. | _ ->
  969. Ast.map_expr (subst_locals locals) e
  970. in
  971. (try
  972. let e = subst_locals locals s in
  973. let e = (EBlock [(EVars (List.rev !tmp_locals),p);(EDisplay (e,call),p)],p) in
  974. raise (Return e)
  975. with Exit ->
  976. map e)
  977. | EDisplayNew _ ->
  978. raise (Return e)
  979. | _ ->
  980. map e
  981. and map e =
  982. Ast.map_expr loop e
  983. in
  984. (try loop e with Return e -> e)
  985. let optimize_completion c fields =
  986. let cp = !Parser.resume_display in
  987. List.map (fun f ->
  988. if cp.pmin = 0 || (f.cff_pos.pmin <= cp.pmin && f.cff_pos.pmax >= cp.pmax) then
  989. let k = try (match f.cff_kind with
  990. | FVar (t,Some e) -> FVar (t,Some (optimize_completion_expr e))
  991. | FFun fn -> (match optimize_completion_expr (EFunction (None,fn),f.cff_pos) with (EFunction (None,fn),_) -> FFun fn | e -> FFun({ fn with f_expr = Some e; f_args = []; }))
  992. | k -> k
  993. ) with Exit -> f.cff_kind in
  994. { f with cff_kind = k }
  995. else
  996. f
  997. ) fields
  998. (* ---------------------------------------------------------------------- *)