enumToClass.ml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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 Ast
  19. open Type
  20. open Codegen
  21. open Texpr.Builder
  22. open Gencommon
  23. (* ******************************************* *)
  24. (* EnumToClass *)
  25. (* ******************************************* *)
  26. (*
  27. For languages that don't support parameterized enums and/or metadata in enums, we need to transform
  28. enums into normal classes. This is done at the first module pass by creating new classes with the same
  29. path inside the modules, and removing the actual enum module by setting it as en extern.
  30. * The target must create its own strategy to deal with reflection. As it is right now, we will have a base class
  31. which the class will extend, create @:$IsEnum metadata for the class, and create @:alias() metadatas for the fields,
  32. with their tag order (as a string) as their alias. If you are using ReflectionCFs, then you don't have to worry
  33. about that, as it's already generating all information needed by the haxe runtime.
  34. so they can be
  35. *)
  36. let name = "enum_to_class"
  37. let priority = solve_deps name []
  38. type t = {
  39. ec_tbl : (path, tclass) Hashtbl.t;
  40. }
  41. let new_t () = {
  42. ec_tbl = Hashtbl.create 10
  43. }
  44. (* ******************************************* *)
  45. (* EnumToClassModf *)
  46. (* ******************************************* *)
  47. (*
  48. The actual Module Filter that will transform the enum into a class
  49. dependencies:
  50. Should run before ReflectionCFs, in order to enable proper reflection access.
  51. Should run before RealTypeParams.RealTypeParamsModf, since generic enums must be first converted to generic classes
  52. It needs that the target platform implements __array__() as a shortcut to declare haxe.ds.Vector
  53. *)
  54. module EnumToClassModf =
  55. struct
  56. let name = "enum_to_class_mod"
  57. let priority = solve_deps name [DBefore ReflectionCFs.priority; DBefore RealTypeParams.RealTypeParamsModf.priority]
  58. let pmap_exists fn pmap = try PMap.iter (fun a b -> if fn a b then raise Exit) pmap; false with | Exit -> true
  59. let has_any_meta en =
  60. let has_meta meta = List.exists (fun (m,_,_) -> match m with Meta.Custom _ -> true | _ -> false) meta in
  61. has_meta en.e_meta || pmap_exists (fun _ ef -> has_meta ef.ef_meta) en.e_constrs
  62. let convert gen t base_class base_param_class en =
  63. let handle_type_params = false in (* TODO: look into this *)
  64. let basic = gen.gcon.basic in
  65. let pos = en.e_pos in
  66. (* create the class *)
  67. let cl = mk_class en.e_module en.e_path pos in
  68. Hashtbl.add t.ec_tbl en.e_path cl;
  69. (match Texpr.build_metadata gen.gcon.basic (TEnumDecl en) with
  70. | Some expr ->
  71. let cf = mk_class_field ~static:true "__meta__" expr.etype false expr.epos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
  72. cf.cf_expr <- Some expr;
  73. cl.cl_statics <- PMap.add "__meta__" cf cl.cl_statics;
  74. cl.cl_ordered_statics <- cf :: cl.cl_ordered_statics
  75. | _ -> ()
  76. );
  77. let super, has_params = if Meta.has Meta.FlatEnum en.e_meta then base_class, false else base_param_class, true in
  78. cl.cl_super <- Some(super,[]);
  79. if en.e_extern then add_class_flag cl CExtern;
  80. en.e_meta <- (Meta.Class, [], pos) :: en.e_meta;
  81. cl.cl_module <- en.e_module;
  82. cl.cl_meta <- ( Meta.Enum, [], pos ) :: cl.cl_meta;
  83. (match gen.gcon.platform with
  84. | Cs when Common.defined gen.gcon Define.CoreApiSerialize ->
  85. cl.cl_meta <- ( Meta.Meta, [ (efield( (EConst (Ident "System"), null_pos ), "Serializable" ), null_pos) ], null_pos ) :: cl.cl_meta
  86. | _ -> ());
  87. let c_types =
  88. if handle_type_params then
  89. List.map (fun tp -> {tp with ttp_type=TInst (map_param (get_cl_t tp.ttp_type), [])}) en.e_params
  90. else
  91. []
  92. in
  93. cl.cl_params <- c_types;
  94. let i = ref 0 in
  95. let cfs = List.map (fun name ->
  96. let ef = PMap.find name en.e_constrs in
  97. let pos = ef.ef_pos in
  98. let old_i = !i in
  99. incr i;
  100. let cf = match follow ef.ef_type with
  101. | TFun(params,ret) ->
  102. let dup_types =
  103. if handle_type_params then
  104. List.map (fun tp -> {tp with ttp_type = TInst (map_param (get_cl_t tp.ttp_type), [])}) en.e_params
  105. else
  106. []
  107. in
  108. let ef_type =
  109. let fn, types = if handle_type_params then extract_param_type, dup_types else (fun _ -> t_dynamic), en.e_params in
  110. let t = apply_params en.e_params (List.map fn types) ef.ef_type in
  111. apply_params ef.ef_params (List.map fn ef.ef_params) t
  112. in
  113. let params, ret = get_fun ef_type in
  114. let cf_params = if handle_type_params then dup_types @ ef.ef_params else [] in
  115. let cf = mk_class_field name ef_type true pos (Method MethNormal) cf_params in
  116. cf.cf_meta <- [];
  117. let tf_args = List.map (fun (name,opt,t) -> (alloc_var name t, if opt then Some (Texpr.Builder.make_null t null_pos) else None) ) params in
  118. let arr_decl = mk_nativearray_decl gen t_dynamic (List.map (fun (v,_) -> mk_local v pos) tf_args) pos in
  119. let expr = {
  120. eexpr = TFunction({
  121. tf_args = tf_args;
  122. tf_type = ret;
  123. tf_expr = mk_block ( mk_return { eexpr = TNew(cl,extract_param_types dup_types, [make_int gen.gcon.basic old_i pos; arr_decl] ); etype = TInst(cl, extract_param_types dup_types); epos = pos } );
  124. });
  125. etype = ef_type;
  126. epos = pos
  127. } in
  128. cf.cf_expr <- Some expr;
  129. cf
  130. | _ ->
  131. let actual_t = match follow ef.ef_type with
  132. | TEnum(e, p) -> TEnum(e, List.map (fun _ -> t_dynamic) p)
  133. | _ -> die "" __LOC__
  134. in
  135. let cf = mk_class_field name actual_t true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
  136. let args = if has_params then
  137. [make_int gen.gcon.basic old_i pos; null (gen.gclasses.nativearray t_dynamic) pos]
  138. else
  139. [make_int gen.gcon.basic old_i pos]
  140. in
  141. cf.cf_meta <- [Meta.ReadOnly,[],pos];
  142. cf.cf_expr <- Some {
  143. eexpr = TNew(cl, List.map (fun _ -> t_empty) cl.cl_params, args);
  144. etype = TInst(cl, List.map (fun _ -> t_empty) cl.cl_params);
  145. epos = pos;
  146. };
  147. cf
  148. in
  149. cl.cl_statics <- PMap.add cf.cf_name cf cl.cl_statics;
  150. cf
  151. ) en.e_names in
  152. let constructs_cf = mk_class_field ~static:true "__hx_constructs" (gen.gclasses.nativearray basic.tstring) true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
  153. constructs_cf.cf_meta <- [Meta.ReadOnly,[],pos];
  154. constructs_cf.cf_expr <- Some (mk_nativearray_decl gen basic.tstring (List.map (fun s -> { eexpr = TConst(TString s); etype = basic.tstring; epos = pos }) en.e_names) pos);
  155. cl.cl_ordered_statics <- constructs_cf :: cfs @ cl.cl_ordered_statics ;
  156. cl.cl_statics <- PMap.add "__hx_constructs" constructs_cf cl.cl_statics;
  157. let getTag_cf_type = tfun [] basic.tstring in
  158. let getTag_cf = mk_class_field "getTag" getTag_cf_type true pos (Method MethNormal) [] in
  159. add_class_field_flag getTag_cf CfFinal;
  160. getTag_cf.cf_expr <- Some {
  161. eexpr = TFunction {
  162. tf_args = [];
  163. tf_type = basic.tstring;
  164. tf_expr = mk_return (
  165. let e_constructs = mk_static_field_access_infer cl "__hx_constructs" pos [] in
  166. let e_this = mk (TConst TThis) (TInst (cl,[])) pos in
  167. let e_index = mk_field_access gen e_this "index" pos in
  168. {
  169. eexpr = TArray(e_constructs,e_index);
  170. etype = basic.tstring;
  171. epos = pos;
  172. }
  173. )
  174. };
  175. etype = getTag_cf_type;
  176. epos = pos;
  177. };
  178. cl.cl_ordered_fields <- getTag_cf :: cl.cl_ordered_fields ;
  179. cl.cl_fields <- PMap.add "getTag" getTag_cf cl.cl_fields;
  180. add_class_field_flag getTag_cf CfOverride;
  181. cl.cl_meta <- (Meta.NativeGen,[],cl.cl_pos) :: cl.cl_meta;
  182. gen.gadd_to_module (TClassDecl cl) (max_dep);
  183. TEnumDecl en
  184. (*
  185. traverse
  186. gen - gen context
  187. convert_all : bool - should we convert all enums? If set, convert_if_has_meta will be ignored.
  188. convert_if_has_meta : bool - should we convert only if it has meta?
  189. enum_base_class : tclass - the enum base class.
  190. should_be_hxgen : bool - should the created enum be hxgen?
  191. *)
  192. let configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class =
  193. let convert e = convert gen t enum_base_class param_enum_class e in
  194. let run md =
  195. match md with
  196. | TEnumDecl e when is_hxgen md ->
  197. if convert_all then
  198. convert e
  199. else if convert_if_has_meta && has_any_meta e then
  200. convert e
  201. else if not (Meta.has Meta.FlatEnum e.e_meta) then
  202. convert e
  203. else begin
  204. (* take off the :hxgen meta from it, if there's any *)
  205. e.e_meta <- List.filter (fun (n,_,_) -> not (n = Meta.HxGen)) e.e_meta;
  206. md
  207. end
  208. | _ ->
  209. md
  210. in
  211. gen.gmodule_filters#add name (PCustom priority) run
  212. end;;
  213. (* ******************************************* *)
  214. (* EnumToClassExprf *)
  215. (* ******************************************* *)
  216. (*
  217. Enum to class Expression Filter
  218. dependencies:
  219. Should run before TArrayTransform, since it generates array access expressions
  220. *)
  221. module EnumToClassExprf =
  222. struct
  223. let name = "enum_to_class_exprf"
  224. let priority = solve_deps name [DBefore TArrayTransform.priority]
  225. let configure gen t mk_enum_index_call =
  226. let rec run e =
  227. let get_converted_enum_type et =
  228. let en, eparams = match follow (gen.gfollow#run_f et) with
  229. | TEnum(en,p) -> en, p
  230. | _ -> raise Not_found
  231. in
  232. let cl = Hashtbl.find t.ec_tbl en.e_path in
  233. TInst(cl, eparams)
  234. in
  235. match e.eexpr with
  236. | TEnumIndex f ->
  237. let f = run f in
  238. (try
  239. mk_field_access gen {f with etype = get_converted_enum_type f.etype} "index" e.epos
  240. with Not_found ->
  241. mk_enum_index_call f e.epos)
  242. | TCall (({eexpr = TField(_, FStatic({cl_path=[],"Type"},{cf_name="enumIndex"}))} as left), [f]) ->
  243. let f = run f in
  244. (try
  245. mk_field_access gen {f with etype = get_converted_enum_type f.etype} "index" e.epos
  246. with Not_found ->
  247. { e with eexpr = TCall(left, [f]) })
  248. | TEnumParameter(f, _,i) ->
  249. let f = run f in
  250. (* check if en was converted to class *)
  251. (* if it was, switch on tag field and change cond type *)
  252. let f = try
  253. { f with etype = get_converted_enum_type f.etype }
  254. with Not_found ->
  255. f
  256. in
  257. let cond_array = { (mk_field_access gen f "params" f.epos) with etype = gen.gclasses.nativearray t_dynamic } in
  258. index gen.gcon.basic cond_array i e.etype e.epos
  259. | _ ->
  260. Type.map_expr run e
  261. in
  262. gen.gexpr_filters#add name (PCustom priority) run
  263. end;;
  264. let configure gen convert_all convert_if_has_meta enum_base_class param_enum_class mk_enum_index_call =
  265. let t = new_t () in
  266. EnumToClassModf.configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class;
  267. EnumToClassExprf.configure gen t mk_enum_index_call