ES6Ctors.ml 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. (*
  2. The Haxe Compiler
  3. Copyright (C) 2005-2019 Haxe Foundation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. *)
  16. open Common
  17. open Globals
  18. open Type
  19. open Texpr.Builder
  20. (* name of the method to which the constructor is extracted *)
  21. let ctor_method_name = "_hx_constructor"
  22. (* name of the static bool flag to skip constructor body execution *)
  23. let skip_ctor_flag_name = "_hx_skip_constructor"
  24. (* replace super(a,b,c) with super._hx_constructor(a,b,c) *)
  25. let rec replace_super_call e =
  26. match e.eexpr with
  27. | TCall ({ eexpr = TConst TSuper } as e_super, args) ->
  28. let e_super_hxctor = { e_super with eexpr = TField (e_super, FDynamic ctor_method_name) } in
  29. { e with eexpr = TCall (e_super_hxctor, args) }
  30. | _ ->
  31. map_expr replace_super_call e
  32. let remove_default_arg_values args =
  33. List.map (fun (v,_) -> v,None) args
  34. exception Accessed_this of texpr
  35. (* return whether given expression has `this` access before calling `super` *)
  36. let has_this_before_super e =
  37. let rec loop e =
  38. match e.eexpr with
  39. | TCall ({ eexpr = TConst TSuper }, args) ->
  40. List.iter loop args;
  41. raise Exit
  42. | TConst TThis ->
  43. raise (Accessed_this e)
  44. | _ ->
  45. Type.iter loop e
  46. in
  47. try
  48. (loop e; None)
  49. with
  50. | Exit -> None
  51. | Accessed_this e -> Some e
  52. let get_num_args cf =
  53. match follow cf.cf_type with
  54. | TFun (args, _) -> List.length args
  55. | _ -> die "" __LOC__
  56. (*
  57. the filter works in two passes:
  58. - mark classes whether they need to support constructor skipping and/or they skip parent's constructor
  59. - change the constructors of marked classes (extract body into method and/or add skipping super calls)
  60. *)
  61. let rewrite_ctors com =
  62. (* we mark classes that need changing by storing them in these two maps *)
  63. let needs_ctor_skipping, does_ctor_skipping, inject_super =
  64. let l = List.length com.types in
  65. Hashtbl.create l, Hashtbl.create l, Hashtbl.create l
  66. in
  67. (*
  68. we're using a reference to the root of the inheritance chain so we can easily
  69. generate RootClass._hx_skip_constructor expressions
  70. *)
  71. let mark_does_ctor_skipping cl cl_super p_this_access =
  72. let rec mark_needs_ctor_skipping cl =
  73. (* for non haxe-generated extern classes we can't generate any valid code, so just fail *)
  74. if cl.cl_extern && not (Meta.has Meta.HxGen cl.cl_meta) then begin
  75. abort "Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor" p_this_access;
  76. end;
  77. try
  78. Hashtbl.find needs_ctor_skipping cl.cl_path
  79. with Not_found ->
  80. let root =
  81. match cl.cl_super with
  82. | Some ({ cl_constructor = Some ctor_super } as cl_super,_) ->
  83. let root = mark_needs_ctor_skipping cl_super in
  84. Option.may (fun ctor ->
  85. (* if parent's constructor receives less arguments than needed for this - we need to override the constructor *)
  86. if get_num_args ctor > get_num_args ctor_super then
  87. Hashtbl.add does_ctor_skipping cl.cl_path root;
  88. ) cl.cl_constructor;
  89. root
  90. | _ ->
  91. cl
  92. in
  93. Hashtbl.add needs_ctor_skipping cl.cl_path root;
  94. root
  95. in
  96. let root_cl = mark_needs_ctor_skipping cl_super in
  97. Hashtbl.add does_ctor_skipping cl.cl_path root_cl;
  98. in
  99. let e_empty_super_call = (* super() *)
  100. let e_super = mk (TConst TSuper) t_dynamic null_pos in
  101. mk (TCall (e_super,[])) com.basic.tvoid null_pos
  102. in
  103. let activated = ref false in
  104. let mark t =
  105. match t with
  106. | TClassDecl ({ cl_constructor = Some { cf_expr = Some { eexpr = TFunction tf } }; cl_super = Some (cl_super,_) } as cl) ->
  107. if Type.has_constructor cl_super then begin
  108. (* if parent class has a constructor, check for `this` accesses before calling `super()` *)
  109. let this_before_super = has_this_before_super tf.tf_expr in
  110. Option.may (fun e_this_access ->
  111. activated := true;
  112. mark_does_ctor_skipping cl cl_super e_this_access.epos
  113. ) this_before_super
  114. end else begin
  115. (* if there was no ctor in the parent class, we still gotta call `super` *)
  116. Hashtbl.add inject_super cl.cl_path cl;
  117. end
  118. | _ -> ()
  119. in
  120. List.iter mark com.types;
  121. if !activated then begin
  122. (* just some helper common exprs *)
  123. let e_false = (make_bool com.basic false null_pos) in
  124. let e_true = (make_bool com.basic true null_pos) in
  125. let e_hx_ctor = (* this._hx_constructor *)
  126. let ethis = mk (TConst TThis) t_dynamic null_pos in
  127. mk (TField (ethis, FDynamic ctor_method_name)) t_dynamic null_pos
  128. in
  129. let change t =
  130. match t with
  131. | TClassDecl ({ cl_constructor = Some ({ cf_expr = Some ({ eexpr = TFunction tf_ctor } as ctor_expr) } as cf_ctor) } as cl) ->
  132. let does_ctor_skipping = try Some (Hashtbl.find does_ctor_skipping cl.cl_path) with Not_found -> None in
  133. let add_hx_ctor_method () =
  134. let cf_fun_ctor = mk_field ctor_method_name cf_ctor.cf_type cf_ctor.cf_pos null_pos in
  135. cf_fun_ctor.cf_expr <- Some (replace_super_call ctor_expr);
  136. cf_fun_ctor.cf_kind <- Method MethNormal;
  137. cl.cl_ordered_fields <- cf_fun_ctor :: cl.cl_ordered_fields;
  138. cl.cl_fields <- PMap.add cf_fun_ctor.cf_name cf_fun_ctor cl.cl_fields;
  139. in
  140. let make_hx_ctor_call e_skip_flag = (* this._hx_constructor(a,b,c) *)
  141. let hxctor_call_args = List.map (fun (v,_) -> make_local v null_pos) tf_ctor.tf_args in
  142. let hx_ctor_call = mk (TCall (e_hx_ctor, hxctor_call_args)) com.basic.tvoid null_pos in
  143. if does_ctor_skipping <> None then
  144. mk (TBlock [
  145. mk (TBinop (OpAssign, e_skip_flag, e_true)) com.basic.tbool null_pos;
  146. e_empty_super_call;
  147. mk (TBinop (OpAssign, e_skip_flag, e_false)) com.basic.tbool null_pos;
  148. hx_ctor_call
  149. ]) com.basic.tvoid null_pos
  150. else
  151. hx_ctor_call
  152. in
  153. let make_skip_flag root_cl = (* TopClass._hx_skip_constructor *)
  154. let e_top = mk (TTypeExpr (TClassDecl root_cl)) t_dynamic null_pos in
  155. mk (TField (e_top, FDynamic skip_ctor_flag_name)) com.basic.tbool null_pos
  156. in
  157. (match (try Some (Hashtbl.find needs_ctor_skipping cl.cl_path) with Not_found -> None) with
  158. | Some root ->
  159. add_hx_ctor_method ();
  160. if does_ctor_skipping = None && cl != root then
  161. (* for intermediate classes that support skipping but don't do skipping themselves, we can just remove the constructor altogether,
  162. because the skipping logic is implemented in the parent constructor, and the actual constructor body is moved into _hx_constructor *)
  163. cf_ctor.cf_expr <- None
  164. else begin
  165. let e_skip =
  166. let e_return = (mk (TReturn None) t_dynamic null_pos) in
  167. if cl.cl_super = None || (Hashtbl.mem inject_super cl.cl_path) then
  168. (* just `return` *)
  169. e_return
  170. else
  171. (* `{ super(); return; }` *)
  172. mk (TBlock [
  173. e_empty_super_call;
  174. e_return;
  175. ]) com.basic.tvoid null_pos
  176. in
  177. let e_skip_flag = make_skip_flag root in
  178. let e_ctor_replaced = { tf_ctor.tf_expr with
  179. eexpr = TBlock [
  180. mk (TIf (mk_parent e_skip_flag, e_skip, None)) com.basic.tvoid null_pos;
  181. make_hx_ctor_call e_skip_flag
  182. ]
  183. } in
  184. cf_ctor.cf_expr <- Some { ctor_expr with eexpr = TFunction { tf_ctor with
  185. tf_args = remove_default_arg_values tf_ctor.tf_args;
  186. tf_expr = e_ctor_replaced
  187. } };
  188. end;
  189. if cl == root then begin
  190. let cf_skip_ctor = mk_field skip_ctor_flag_name com.basic.tbool null_pos null_pos in
  191. cf_skip_ctor.cf_expr <- Some e_false;
  192. cl.cl_ordered_statics <- cf_skip_ctor :: cl.cl_ordered_statics;
  193. cl.cl_statics <- PMap.add cf_skip_ctor.cf_name cf_skip_ctor cl.cl_statics;
  194. end
  195. | None ->
  196. (match does_ctor_skipping with
  197. | Some root ->
  198. add_hx_ctor_method ();
  199. let e_skip_flag = make_skip_flag root in
  200. let e_ctor_replaced = { tf_ctor.tf_expr with
  201. eexpr = TBlock [
  202. make_hx_ctor_call e_skip_flag
  203. ]
  204. } in
  205. cf_ctor.cf_expr <- Some { ctor_expr with eexpr = TFunction { tf_ctor with
  206. tf_args = remove_default_arg_values tf_ctor.tf_args;
  207. tf_expr = e_ctor_replaced
  208. } };
  209. | None -> ())
  210. )
  211. | _ ->
  212. ()
  213. in
  214. List.iter change com.types
  215. end;
  216. Hashtbl.iter (fun _ cl ->
  217. match cl with
  218. | { cl_constructor = Some ({ cf_expr = Some ({ eexpr = TFunction tf } as e_ctor) } as cf_ctor); cl_super = Some (cl_super,_) } ->
  219. cl.cl_constructor <- Some { cf_ctor with cf_expr = Some { e_ctor with eexpr = TFunction { tf with tf_expr = { tf.tf_expr with eexpr = TBlock [e_empty_super_call; tf.tf_expr] } } } };
  220. | _ ->
  221. die "" __LOC__
  222. ) inject_super;