genneko.ml 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  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 Nast
  25. open Common
  26. type context = {
  27. version : int;
  28. com : Common.context;
  29. packages : (string list, unit) Hashtbl.t;
  30. globals : (string list * string, string) Hashtbl.t;
  31. mutable curglobal : int;
  32. mutable macros : bool;
  33. mutable curclass : string;
  34. mutable curmethod : string;
  35. mutable inits : (tclass * texpr) list;
  36. }
  37. let files = Hashtbl.create 0
  38. let pos ctx p =
  39. if ctx.macros then
  40. {
  41. psource = p.pfile;
  42. pline = p.pmin lor ((p.pmax - p.pmin) lsl 20);
  43. }
  44. else let file = (match ctx.com.debug with
  45. | true -> ctx.curclass ^ "::" ^ ctx.curmethod
  46. | false ->
  47. try
  48. Hashtbl.find files p.pfile
  49. with Not_found -> try
  50. (* lookup relative path *)
  51. let len = String.length p.pfile in
  52. let base = List.find (fun path ->
  53. let l = String.length path in
  54. len > l && String.sub p.pfile 0 l = path
  55. ) ctx.com.Common.class_path in
  56. let l = String.length base in
  57. let path = String.sub p.pfile l (len - l) in
  58. Hashtbl.add files p.pfile path;
  59. path
  60. with Not_found ->
  61. Hashtbl.add files p.pfile p.pfile;
  62. p.pfile
  63. ) in
  64. {
  65. psource = file;
  66. pline = Lexer.get_error_line p;
  67. }
  68. let gen_global_name ctx path =
  69. match path with
  70. | [], name -> name
  71. | _ ->
  72. try
  73. Hashtbl.find ctx.globals path
  74. with Not_found ->
  75. let name = "@G" ^ string_of_int ctx.curglobal in
  76. ctx.curglobal <- ctx.curglobal + 1;
  77. Hashtbl.add ctx.globals path name;
  78. name
  79. let null p =
  80. (EConst Null,p)
  81. let this p =
  82. (EConst This,p)
  83. let int p n =
  84. (EConst (Int n),p)
  85. let str p s =
  86. (EConst (String s),p)
  87. let ident p s =
  88. let l = String.length s in
  89. if l > 10 && String.sub s 0 10 = "__dollar__" then
  90. (EConst (Builtin (String.sub s 10 (l - 10))),p)
  91. else
  92. (EConst (Ident s),p)
  93. let field p e f =
  94. (EField (e,f),p)
  95. let builtin p n =
  96. (EConst (Builtin n),p)
  97. let call p e el =
  98. (ECall (e,el),p)
  99. let array p el =
  100. call p (builtin p "array") el
  101. let pmap_list f p =
  102. PMap.fold (fun v acc -> f v :: acc) p []
  103. let rec needs_return e =
  104. match e with
  105. | (EBlock l,_) ->
  106. let rec loop = function
  107. | [] -> true
  108. | [x] -> needs_return x
  109. | _ :: l -> loop l
  110. in
  111. loop l
  112. | (EReturn _,_) ->
  113. false
  114. | _ ->
  115. true
  116. let with_return e =
  117. if needs_return e then
  118. let p = snd e in
  119. let ret = EReturn (Some (null p)),p in
  120. match e with
  121. | (EBlock l,_) ->
  122. (EBlock (l @ [ret]),p)
  123. | _ ->
  124. (EBlock [e;ret] , p)
  125. else
  126. e
  127. let gen_type_path p (path,t) =
  128. match path with
  129. | [] ->
  130. ident p t
  131. | path :: l ->
  132. let epath = List.fold_left (fun e path -> field p e path) (ident p path) l in
  133. field p epath t
  134. let rec gen_big_string ctx p s =
  135. let max = 1 lsl 16 - 1 in
  136. if String.length s > max then
  137. (EBinop ("+",str p (String.sub s 0 max),gen_big_string ctx p (String.sub s max (String.length s - max))),p)
  138. else
  139. str p s
  140. let gen_constant ctx pe c =
  141. let p = pos ctx pe in
  142. match c with
  143. | TInt i ->
  144. (try
  145. let h = Int32.to_int (Int32.shift_right_logical i 24) in
  146. if (h land 128 = 0) <> (h land 64 = 0) then raise Exit;
  147. int p (Int32.to_int i)
  148. with _ ->
  149. if ctx.version < 2 then error "This integer is too big to be compiled to a Neko 31-bit integer. Please use a Float instead" pe;
  150. (EConst (Int32 i),p))
  151. | TFloat f -> (EConst (Float f),p)
  152. | TString s -> call p (field p (ident p "String") "new") [gen_big_string ctx p s]
  153. | TBool b -> (EConst (if b then True else False),p)
  154. | TNull -> null p
  155. | TThis -> this p
  156. | TSuper -> assert false
  157. let rec gen_binop ctx p op e1 e2 =
  158. (EBinop (Ast.s_binop op,gen_expr ctx e1,gen_expr ctx e2),p)
  159. and gen_unop ctx p op flag e =
  160. match op with
  161. | Increment -> (EBinop ((if flag = Prefix then "+=" else "++="), gen_expr ctx e , int p 1),p)
  162. | Decrement -> (EBinop ((if flag = Prefix then "-=" else "--="), gen_expr ctx e , int p 1),p)
  163. | Not -> call p (builtin p "not") [gen_expr ctx e]
  164. | Neg -> (EBinop ("-",int p 0, gen_expr ctx e),p)
  165. | NegBits -> (EBinop ("-",int p (-1), gen_expr ctx e),p)
  166. and gen_call ctx p e el =
  167. match e.eexpr , el with
  168. | TConst TSuper , _ ->
  169. let c = (match follow e.etype with TInst (c,_) -> c | _ -> assert false) in
  170. call p (builtin p "call") [
  171. field p (gen_type_path p c.cl_path) "__construct__";
  172. this p;
  173. array p (List.map (gen_expr ctx) el)
  174. ]
  175. | TLocal { v_name = "__resources__" }, [] ->
  176. call p (builtin p "array") (Hashtbl.fold (fun name data acc ->
  177. (EObject [("name",gen_constant ctx e.epos (TString name));("data",gen_big_string ctx p data)],p) :: acc
  178. ) ctx.com.resources [])
  179. | TField ({ eexpr = TConst TSuper; etype = t },f) , _ ->
  180. let c = (match follow t with TInst (c,_) -> c | _ -> assert false) in
  181. call p (builtin p "call") [
  182. field p (gen_type_path p (fst c.cl_path,"@" ^ snd c.cl_path)) (field_name f);
  183. this p;
  184. array p (List.map (gen_expr ctx) el)
  185. ]
  186. | _ , _ ->
  187. let e = (match gen_expr ctx e with EFunction _, _ as e -> (EBlock [e],p) | e -> e) in
  188. call p e (List.map (gen_expr ctx) el)
  189. and gen_expr ctx e =
  190. let p = pos ctx e.epos in
  191. match e.eexpr with
  192. | TConst c ->
  193. gen_constant ctx e.epos c
  194. | TLocal v when v.v_name.[0] = '$' ->
  195. (EConst (Builtin (String.sub v.v_name 1 (String.length v.v_name - 1))),p)
  196. | TLocal v ->
  197. if v.v_capture then
  198. (EArray (ident p v.v_name,int p 0),p)
  199. else
  200. ident p v.v_name
  201. | TArray (e1,e2) ->
  202. (EArray (gen_expr ctx e1,gen_expr ctx e2),p)
  203. | TBinop (OpAssign,{ eexpr = TField (e1,f) },e2) ->
  204. (EBinop ("=",field p (gen_expr ctx e1) (field_name f),gen_expr ctx e2),p)
  205. | TBinop (op,e1,e2) ->
  206. gen_binop ctx p op e1 e2
  207. | TField (e2,FClosure (_,f)) ->
  208. (match follow e.etype with
  209. | TFun (args,_) ->
  210. let n = List.length args in
  211. if n > 5 then error "Cannot create closure with more than 5 arguments" e.epos;
  212. let tmp = ident p "@tmp" in
  213. EBlock [
  214. (EVars ["@tmp", Some (gen_expr ctx e2); "@fun", Some (field p tmp f.cf_name)] , p);
  215. if ctx.macros then
  216. call p (builtin p "closure") [ident p "@fun";tmp]
  217. else
  218. call p (ident p ("@closure" ^ string_of_int n)) [tmp;ident p "@fun"]
  219. ] , p
  220. | _ -> assert false)
  221. | TField (e,f) ->
  222. field p (gen_expr ctx e) (field_name f)
  223. | TTypeExpr t ->
  224. gen_type_path p (t_path t)
  225. | TParenthesis e ->
  226. (EParenthesis (gen_expr ctx e),p)
  227. | TObjectDecl fl ->
  228. let hasToString = ref false in
  229. let fl = List.map (fun (f,e) -> if f = "toString" then hasToString := (match follow e.etype with TFun ([],_) -> true | _ -> false); f , gen_expr ctx e) fl in
  230. (EObject (if !hasToString then ("__string",ident p "@default__string") :: fl else fl),p)
  231. | TArrayDecl el ->
  232. call p (field p (ident p "Array") "new1") [array p (List.map (gen_expr ctx) el); int p (List.length el)]
  233. | TCall (e,el) ->
  234. gen_call ctx p e el
  235. | TNew (c,_,params) ->
  236. call p (field p (gen_type_path p c.cl_path) "new") (List.map (gen_expr ctx) params)
  237. | TUnop (op,flag,e) ->
  238. gen_unop ctx p op flag e
  239. | TVars vl ->
  240. (EVars (List.map (fun (v,e) ->
  241. let e = (match e with
  242. | None ->
  243. if v.v_capture then
  244. Some (call p (builtin p "array") [null p])
  245. else
  246. None
  247. | Some e ->
  248. let e = gen_expr ctx e in
  249. if v.v_capture then
  250. Some (call p (builtin p "array") [e])
  251. else
  252. Some e
  253. ) in
  254. v.v_name , e
  255. ) vl),p)
  256. | TFunction f ->
  257. let inits = List.fold_left (fun acc (a,c) ->
  258. let acc = if a.v_capture then
  259. (EBinop ("=",ident p a.v_name,call p (builtin p "array") [ident p a.v_name]),p) :: acc
  260. else
  261. acc
  262. in
  263. match c with
  264. | None | Some TNull -> acc
  265. | Some c -> gen_expr ctx (Codegen.set_default ctx.com a c e.epos) :: acc
  266. ) [] f.tf_args in
  267. let e = gen_expr ctx f.tf_expr in
  268. let e = (match inits with [] -> e | _ -> EBlock (List.rev (e :: inits)),p) in
  269. (EFunction (List.map arg_name f.tf_args, with_return e),p)
  270. | TBlock el ->
  271. (EBlock (List.map (gen_expr ctx) el), p)
  272. | TFor (v, it, e) ->
  273. let it = gen_expr ctx it in
  274. let e = gen_expr ctx e in
  275. let next = call p (field p (ident p "@tmp") "next") [] in
  276. let next = (if v.v_capture then call p (builtin p "array") [next] else next) in
  277. (EBlock
  278. [(EVars ["@tmp", Some it],p);
  279. (EWhile (call p (field p (ident p "@tmp") "hasNext") [],
  280. (EBlock [
  281. (EVars [v.v_name, Some next],p);
  282. e
  283. ],p)
  284. ,NormalWhile),p)]
  285. ,p)
  286. | TIf (cond,e1,e2) ->
  287. (* if(e)-1 is parsed as if( e - 1 ) *)
  288. let parent e = mk (TParenthesis e) e.etype e.epos in
  289. let e1 = (match e1.eexpr with TConst (TInt n) when n < 0l -> parent e1 | TConst (TFloat f) when f.[0] = '-' -> parent e1 | _ -> e1) in
  290. (EIf (gen_expr ctx cond,gen_expr ctx e1,(match e2 with None -> None | Some e -> Some (gen_expr ctx e))),p)
  291. | TWhile (econd,e,flag) ->
  292. (EWhile (gen_expr ctx econd, gen_expr ctx e, match flag with Ast.NormalWhile -> NormalWhile | Ast.DoWhile -> DoWhile),p)
  293. | TTry (e,catchs) ->
  294. let rec loop = function
  295. | [] -> call p (builtin p "rethrow") [ident p "@tmp"]
  296. | (v,e) :: l ->
  297. let e2 = loop l in
  298. let path = (match follow v.v_type with
  299. | TInst (c,_) -> Some c.cl_path
  300. | TEnum (e,_) -> Some e.e_path
  301. | TDynamic _ -> None
  302. | _ -> assert false
  303. ) in
  304. let cond = (match path with
  305. | None -> (EConst True,p)
  306. | Some path -> call p (field p (gen_type_path p (["neko"],"Boot")) "__instanceof") [ident p "@tmp"; gen_type_path p path]
  307. ) in
  308. let id = ident p "@tmp" in
  309. let id = (if v.v_capture then call p (builtin p "array") [id] else id) in
  310. let e = gen_expr ctx e in
  311. (EIf (cond,(EBlock [
  312. EVars [v.v_name,Some id],p;
  313. e;
  314. ],p),Some e2),p)
  315. in
  316. let catchs = loop catchs in
  317. let catchs = (EBlock [
  318. (EIf (
  319. (EBinop ("==",call p (builtin p "typeof") [ident p "@tmp"],builtin p "tstring"),p),
  320. (EBinop ("=",ident p "@tmp",call p (field p (ident p "String") "new") [ident p "@tmp"]),p),
  321. None
  322. ),p);
  323. catchs;
  324. ],p) in
  325. (ETry (gen_expr ctx e,"@tmp",catchs),p)
  326. | TReturn eo ->
  327. (EReturn (match eo with None -> Some (null p) | Some e -> Some (gen_expr ctx e)),p)
  328. | TBreak ->
  329. (EBreak None,p)
  330. | TContinue ->
  331. (EContinue,p)
  332. | TThrow e ->
  333. call p (builtin p "throw") [gen_expr ctx e]
  334. | TCast (e,None) ->
  335. gen_expr ctx e
  336. | TCast (e1,Some t) ->
  337. gen_expr ctx (Codegen.default_cast ~vtmp:"@tmp" ctx.com e1 t e.etype e.epos)
  338. | TMatch (e,_,cases,eo) ->
  339. let p = pos ctx e.epos in
  340. let etmp = (EVars ["@tmp",Some (gen_expr ctx e)],p) in
  341. let eindex = field p (ident p "@tmp") "index" in
  342. let gen_params params e =
  343. match params with
  344. | None ->
  345. gen_expr ctx e
  346. | Some el ->
  347. let count = ref (-1) in
  348. let vars = List.fold_left (fun acc v ->
  349. incr count;
  350. match v with
  351. | None ->
  352. acc
  353. | Some v ->
  354. let e = (EArray (ident p "@tmp",int p (!count)),p) in
  355. let e = (if v.v_capture then call p (builtin p "array") [e] else e) in
  356. (v.v_name , Some e) :: acc
  357. ) [] el in
  358. let e = gen_expr ctx e in
  359. (EBlock [
  360. (EVars ["@tmp",Some (field p (ident p "@tmp") "args")],p);
  361. (match vars with [] -> null p | _ -> EVars vars,p);
  362. e
  363. ],p)
  364. in
  365. (try
  366. (EBlock [
  367. etmp;
  368. (ESwitch (
  369. eindex,
  370. List.map (fun (cl,params,e2) ->
  371. let cond = match cl with
  372. | [s] -> int p s
  373. | _ -> raise Exit
  374. in
  375. cond , gen_params params e2
  376. ) cases,
  377. (match eo with None -> None | Some e -> Some (gen_expr ctx e))
  378. ),p)
  379. ],p)
  380. with
  381. Exit ->
  382. (EBlock [
  383. etmp;
  384. (EVars ["@index",Some eindex],p);
  385. List.fold_left (fun acc (cl,params,e2) ->
  386. let cond = (match cl with
  387. | [] -> assert false
  388. | c :: l ->
  389. let eq c = (EBinop ("==",ident p "@index",int p c),p) in
  390. List.fold_left (fun acc c -> (EBinop ("||",acc,eq c),p)) (eq c) l
  391. ) in
  392. EIf (cond,gen_params params e2,Some acc),p
  393. ) (match eo with None -> null p | Some e -> (gen_expr ctx e)) (List.rev cases)
  394. ],p)
  395. )
  396. | TSwitch (e,cases,eo) ->
  397. let e = gen_expr ctx e in
  398. let eo = (match eo with None -> None | Some e -> Some (gen_expr ctx e)) in
  399. try
  400. (ESwitch (
  401. e,
  402. List.map (fun (el,e2) ->
  403. match List.map (gen_expr ctx) el with
  404. | [] -> assert false
  405. | [e] -> e, gen_expr ctx e2
  406. | _ -> raise Exit
  407. ) cases,
  408. eo
  409. ),p)
  410. with
  411. Exit ->
  412. (EBlock [
  413. (EVars ["@tmp",Some e],p);
  414. List.fold_left (fun acc (el,e) ->
  415. let cond = (match el with
  416. | [] -> assert false
  417. | e :: l ->
  418. let eq e = (EBinop ("==",ident p "@tmp",gen_expr ctx e),p) in
  419. List.fold_left (fun acc e -> (EBinop ("||",acc,eq e),p)) (eq e) l
  420. ) in
  421. EIf (cond,gen_expr ctx e,Some acc),p
  422. ) (match eo with None -> null p | Some e -> e) (List.rev cases)
  423. ],p)
  424. let gen_method ctx p c acc =
  425. ctx.curmethod <- c.cf_name;
  426. if is_extern_field c then acc else
  427. match c.cf_expr with
  428. | None ->
  429. ((c.cf_name, null p) :: acc)
  430. | Some e ->
  431. match e.eexpr with
  432. | TCall ({ eexpr = TField (_,FStatic ({cl_path=["neko"],"Lib"},{cf_name="load" | "loadLazy" as load})) },[{ eexpr = TConst (TString m) };{ eexpr = TConst (TString f) };{ eexpr = TConst (TInt n) }]) ->
  433. let p = pos ctx e.epos in
  434. let e = call p (EField (builtin p "loader","loadprim"),p) [(EBinop ("+",(EBinop ("+",str p m,str p "@"),p),str p f),p); (EConst (Int (Int32.to_int n)),p)] in
  435. let e = (if load = "load" then e else (ETry (e,"@e",call p (ident p "@lazy_error") [ident p "@e"]),p)) in
  436. (c.cf_name, e) :: acc
  437. | TFunction _ -> ((if c.cf_name = "new" then "__construct__" else c.cf_name), gen_expr ctx e) :: acc
  438. | _ -> (c.cf_name, null p) :: acc
  439. let gen_class ctx c =
  440. ctx.curclass <- s_type_path c.cl_path;
  441. ctx.curmethod <- "$init";
  442. let p = pos ctx c.cl_pos in
  443. let clpath = gen_type_path p (fst c.cl_path,"@" ^ snd c.cl_path) in
  444. let stpath = gen_type_path p c.cl_path in
  445. let fnew = (match c.cl_constructor with
  446. | Some f ->
  447. (match follow f.cf_type with
  448. | TFun (args,_) ->
  449. let params = List.map (fun (n,_,_) -> n) args in
  450. gen_method ctx p f ["new",(EFunction (params,(EBlock [
  451. (EVars ["@o",Some (call p (builtin p "new") [null p])],p);
  452. (call p (builtin p "objsetproto") [ident p "@o"; clpath]);
  453. (call p (builtin p "call") [field p (this p) "__construct__"; ident p "@o"; array p (List.map (ident p) params)]);
  454. (EReturn (Some (ident p "@o")),p)
  455. ],p)),p)]
  456. | _ -> [])
  457. | None ->
  458. []
  459. ) in
  460. let fstring = (try
  461. let f = PMap.find "toString" c.cl_fields in
  462. match follow f.cf_type with
  463. | TFun ([],_) -> ["__string",ident p "@default__string"]
  464. | _ -> []
  465. with Not_found ->
  466. []
  467. ) in
  468. let fserialize = "__serialize" , ident p "@serialize" in
  469. let others = (match c.cl_implements with
  470. | [] -> []
  471. | l -> ["__interfaces__",array p (List.map (fun (c,_) -> gen_type_path p c.cl_path) l)]
  472. ) @ (match c.cl_super with
  473. | None -> []
  474. | Some (c,_) -> ["__super__", gen_type_path p c.cl_path]
  475. ) in
  476. let build (f,e) = (EBinop ("=",field p (ident p "@tmp") f,e),p) in
  477. let tmp = (EVars ["@tmp",Some (call p (builtin p "new") [null p])],p) in
  478. let estat = (EBinop ("=", stpath, ident p "@tmp"),p) in
  479. let gen_props props = (EObject (List.map (fun (n,s) -> n,str p s) props),p) in
  480. let sprops = (match Codegen.get_properties c.cl_ordered_statics with
  481. | [] -> []
  482. | l -> ["__properties__",gen_props l]
  483. ) in
  484. let sfields = List.map build
  485. (
  486. ("prototype",clpath) :: sprops @
  487. PMap.fold (gen_method ctx p) c.cl_statics (fnew @ others)
  488. )
  489. in
  490. let eclass = (EBinop ("=", clpath, ident p "@tmp"),p) in
  491. let mfields = List.map build
  492. (PMap.fold (gen_method ctx p) c.cl_fields (fserialize :: fstring))
  493. in
  494. let props = Codegen.get_properties c.cl_ordered_fields in
  495. let emeta = (EBinop ("=",field p clpath "__class__",stpath),p) ::
  496. (match props with
  497. | [] -> []
  498. | _ ->
  499. let props = gen_props props in
  500. let props = (match c.cl_super with
  501. | Some (csup,_) when Codegen.has_properties csup ->
  502. (EBlock [
  503. (EVars ["@tmp",Some props],p);
  504. call p (builtin p "objsetproto") [ident p "@tmp";field p (field p (gen_type_path p csup.cl_path) "prototype") "__properties__"];
  505. ident p "@tmp"
  506. ],p)
  507. | _ -> props
  508. ) in
  509. [EBinop ("=",field p clpath "__properties__",props),p])
  510. @ match c.cl_path with
  511. | [] , name -> [(EBinop ("=",field p (ident p "@classes") name,ident p name),p)]
  512. | _ -> []
  513. in
  514. let emeta = if ctx.macros then
  515. (EBinop ("=",field p stpath "__ct__",call p (builtin p "typewrap") [Obj.magic (TClassDecl c)]),p) :: emeta
  516. else
  517. emeta
  518. in
  519. let eextends = (match c.cl_super with
  520. | None -> []
  521. | Some (c,_) ->
  522. let esuper = gen_type_path p (fst c.cl_path,"@" ^ snd c.cl_path) in
  523. [call p (builtin p "objsetproto") [clpath; esuper]]
  524. ) in
  525. (EBlock (tmp :: eclass :: mfields @ tmp :: estat :: sfields @ eextends @ emeta),p)
  526. let gen_enum_constr ctx path c =
  527. ctx.curmethod <- c.ef_name;
  528. let p = pos ctx c.ef_pos in
  529. (EBinop ("=",field p path c.ef_name, match follow c.ef_type with
  530. | TFun (params,_) ->
  531. let params = List.map (fun (n,_,_) -> n) params in
  532. (EFunction (params,
  533. (EBlock [
  534. (EVars ["@tmp",Some (EObject [
  535. "tag" , str p c.ef_name;
  536. "index" , int p c.ef_index;
  537. "args" , array p (List.map (ident p) params);
  538. ],p)],p);
  539. call p (builtin p "objsetproto") [ident p "@tmp"; field p path "prototype"];
  540. ident p "@tmp";
  541. ],p)
  542. ),p)
  543. | _ ->
  544. (EBlock [
  545. (EVars ["@tmp",Some (EObject ["tag" , str p c.ef_name; "index", int p c.ef_index; "__serialize" , ident p "@tag_serialize"],p)],p);
  546. call p (builtin p "objsetproto") [ident p "@tmp"; field p path "prototype"];
  547. ident p "@tmp";
  548. ],p)
  549. ),p)
  550. let gen_enum ctx e =
  551. ctx.curclass <- s_type_path e.e_path;
  552. ctx.curmethod <- "$init";
  553. let p = pos ctx e.e_pos in
  554. let path = gen_type_path p e.e_path in
  555. let uname = (EConst (Ident (gen_global_name ctx e.e_path)),p) in
  556. (EBlock (
  557. (EBinop ("=",uname, call p (builtin p "new") [null p]),p) ::
  558. (EBinop ("=",path, uname),p) ::
  559. (EBinop ("=",field p uname "prototype", (EObject [
  560. "__enum__" , uname;
  561. "__serialize" , ident p "@serialize";
  562. "__string" , ident p "@enum_to_string"
  563. ],p)),p) ::
  564. pmap_list (gen_enum_constr ctx uname) e.e_constrs @
  565. (match e.e_path with
  566. | [] , name -> [EBinop ("=",field p (ident p "@classes") name,ident p name),p]
  567. | _ -> [])
  568. ),p)
  569. let gen_type ctx t acc =
  570. match t with
  571. | TClassDecl c ->
  572. (match c.cl_init with
  573. | None -> ()
  574. | Some e -> ctx.inits <- (c,e) :: ctx.inits);
  575. if c.cl_extern then
  576. acc
  577. else
  578. gen_class ctx c :: acc
  579. | TEnumDecl e ->
  580. if e.e_extern then
  581. acc
  582. else
  583. gen_enum ctx e :: acc
  584. | TTypeDecl _ | TAbstractDecl _ ->
  585. acc
  586. let gen_static_vars ctx t =
  587. match t with
  588. | TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ -> []
  589. | TClassDecl c ->
  590. if c.cl_extern then
  591. []
  592. else
  593. List.fold_right (fun f acc ->
  594. match f.cf_expr with
  595. | None -> acc
  596. | Some e ->
  597. match e.eexpr with
  598. | TFunction _ -> acc
  599. | _ ->
  600. ctx.curclass <- s_type_path c.cl_path;
  601. ctx.curmethod <- "$statics";
  602. let p = pos ctx e.epos in
  603. (EBinop ("=",
  604. (field p (gen_type_path p c.cl_path) f.cf_name),
  605. gen_expr ctx e
  606. ),p) :: acc
  607. ) c.cl_ordered_statics []
  608. let gen_package ctx t =
  609. let rec loop acc p =
  610. match p with
  611. | [] -> []
  612. | x :: l ->
  613. let path = acc @ [x] in
  614. if not (Hashtbl.mem ctx.packages path) then begin
  615. let p = pos ctx (t_infos t).mt_pos in
  616. let e = (EBinop ("=",gen_type_path p (acc,x),call p (builtin p "new") [null p]),p) in
  617. Hashtbl.add ctx.packages path ();
  618. (match acc with
  619. | [] ->
  620. let reg = (EBinop ("=",field p (ident p "@classes") x,ident p x),p) in
  621. e :: reg :: loop path l
  622. | _ ->
  623. e :: loop path l)
  624. end else
  625. loop path l
  626. in
  627. loop [] (fst (t_path t))
  628. let gen_boot ctx =
  629. (EBlock [
  630. EBinop ("=",field null_pos (gen_type_path null_pos (["neko"],"Boot")) "__classes",ident null_pos "@classes"),null_pos;
  631. call null_pos (field null_pos (gen_type_path null_pos (["neko"],"Boot")) "__init") [];
  632. ],null_pos)
  633. let gen_name ctx acc t =
  634. match t with
  635. | TEnumDecl e when e.e_extern ->
  636. acc
  637. | TEnumDecl e ->
  638. let p = pos ctx e.e_pos in
  639. let name = fst e.e_path @ [snd e.e_path] in
  640. let arr = call p (field p (ident p "Array") "new1") [array p (List.map (fun n -> gen_constant ctx e.e_pos (TString n)) name); int p (List.length name)] in
  641. let path = gen_type_path p e.e_path in
  642. let setname = (EBinop ("=",field p path "__ename__",arr),p) in
  643. let arr = call p (field p (ident p "Array") "new1") [array p (List.map (fun n -> gen_constant ctx e.e_pos (TString n)) e.e_names); int p (List.length e.e_names)] in
  644. let setconstrs = (EBinop ("=", field p path "__constructs__", arr),p) in
  645. let meta = (match Codegen.build_metadata ctx.com (TEnumDecl e) with
  646. | None -> []
  647. | Some e -> [EBinop ("=",field p path "__meta__", gen_expr ctx e),p]
  648. ) in
  649. let meta = if ctx.macros then
  650. (EBinop ("=",field p path "__et__",call p (builtin p "typewrap") [Obj.magic t]),p) :: meta
  651. else
  652. meta
  653. in
  654. setname :: setconstrs :: meta @ acc
  655. | TClassDecl c ->
  656. if c.cl_extern then
  657. acc
  658. else
  659. let p = pos ctx c.cl_pos in
  660. let name = fst c.cl_path @ [snd c.cl_path] in
  661. let arr = call p (field p (ident p "Array") "new1") [array p (List.map (fun n -> gen_constant ctx c.cl_pos (TString n)) name); int p (List.length name)] in
  662. (EBinop ("=",field p (gen_type_path p c.cl_path) "__name__",arr),p) ::
  663. (match c.cl_implements with
  664. | [] -> acc
  665. | l ->
  666. let interf = field p (gen_type_path p c.cl_path) "__interfaces__" in
  667. (EBinop ("=",interf, call p (field p (ident p "Array") "new1") [interf; int p (List.length l)]),p) :: acc)
  668. | TTypeDecl _ | TAbstractDecl _ ->
  669. acc
  670. let generate_libs_init = function
  671. | [] -> []
  672. | libs ->
  673. (*
  674. var @s = $loader.loadprim("std@sys_string",0)();
  675. var @env = $loader.loadprim("std@get_env",1);
  676. var @b = if( @s == "Windows" )
  677. @env("HAXEPATH") + "\\lib\\"
  678. else try $loader.loadprim("std@file_contents",1)(@env("HOME")+"/.haxelib") + "/"
  679. catch e if( @s == "Linux" ) "/usr/lib/haxe/lib/" else "/usr/local/lib/haxe/lib/";
  680. if( $loader.loadprim("std@sys_is64",0)() ) @s = @s + 64;
  681. @s = @s + "/"
  682. *)
  683. let p = null_pos in
  684. let es = ident p "@s" in
  685. let loadp n nargs =
  686. call p (field p (builtin p "loader") "loadprim") [str p ("std@" ^ n); int p nargs]
  687. in
  688. let op o e1 e2 =
  689. (EBinop (o,e1,e2),p)
  690. in
  691. let boot = [
  692. (EVars [
  693. "@s",Some (call p (loadp "sys_string" 0) []);
  694. "@env",Some (loadp "get_env" 1);
  695. "@b", Some (EIf (op "==" es (str p "Windows"),
  696. op "+" (call p (ident p "@env") [str p "HAXEPATH"]) (str p "\\lib\\"),
  697. Some (ETry (
  698. op "+" (call p (loadp "file_contents" 1) [op "+" (call p (ident p "@env") [str p "HOME"]) (str p "./haxelib")]) (str p "/"),
  699. "e",
  700. (EIf (op "==" es (str p "Linux"),
  701. str p "/usr/lib/haxe/lib/",
  702. Some (str p "/usr/local/lib/haxe/lib/")
  703. ),p)
  704. ),p)
  705. ),p);
  706. ],p);
  707. (EIf (call p (loadp "sys_is64" 0) [],op "=" es (op "+" es (int p 64)),None),p);
  708. op "=" es (op "+" es (str p "/"));
  709. ] in
  710. let lpath = field p (builtin p "loader") "path" in
  711. boot @ List.map (fun dir ->
  712. let full_path = dir.[0] = '/' || dir.[1] = ':' in
  713. let dstr = str p dir in
  714. (*
  715. // for each lib dir
  716. $loader.path = $array($loader.path,dir+@s);
  717. *)
  718. op "=" lpath (call p (builtin p "array") [op "+" (if full_path then dstr else op "+" (ident p "@b") dstr) (ident p "@s"); lpath])
  719. ) libs
  720. let new_context com ver macros =
  721. {
  722. version = ver;
  723. com = com;
  724. globals = Hashtbl.create 0;
  725. curglobal = 0;
  726. packages = Hashtbl.create 0;
  727. macros = macros;
  728. curclass = "$boot";
  729. curmethod = "$init";
  730. inits = [];
  731. }
  732. let header() =
  733. let p = { psource = "<header>"; pline = 1 } in
  734. let fields l =
  735. let rec loop = function
  736. | [] -> assert false
  737. | [x] -> ident p x
  738. | x :: l -> field p (loop l) x
  739. in
  740. loop (List.rev l)
  741. in
  742. let func pl e =
  743. (EFunction (pl,(EReturn (Some e),p)),p)
  744. in
  745. let inits = [
  746. "@classes",call p (builtin p "new") [null p];
  747. "@enum_to_string",func [] (call p (fields ["neko";"Boot";"__enum_str"]) [this p]);
  748. "@serialize",func [] (call p (fields ["neko";"Boot";"__serialize"]) [this p]);
  749. "@tag_serialize",func [] (call p (fields ["neko";"Boot";"__tagserialize"]) [this p]);
  750. "@lazy_error",func ["e"] (call p (builtin p "varargs") [func ["_"] (call p (builtin p "throw") [ident p "e"])]);
  751. "@default__string",func [] (EBlock [
  752. EVars ["@s",Some (call p (field p (this p) "toString") [])] ,p;
  753. EIf ((EBinop ("!=",call p (builtin p "typeof") [ident p "@s"],builtin p "tobject"),p),(EReturn (Some (null p)),p),None),p;
  754. EReturn (Some (field p (ident p "@s") "__s")),p;
  755. ],p)
  756. ] in
  757. let inits = inits @ List.map (fun nargs ->
  758. let args = Array.to_list (Array.init nargs (fun i -> Printf.sprintf "%c" (char_of_int (int_of_char 'a' + i)))) in
  759. let efun = (EFunction (args,(EBlock [
  760. (EBinop ("=",(EConst This,p),ident p "@this"),p);
  761. call p (ident p "@fun") (List.map (ident p) args);
  762. ],p)),p) in
  763. let eif = EIf ((EBinop ("==",ident p "@fun",null p),p),null p,Some efun) in
  764. let e = func ["@this";"@fun"] (eif,p) in
  765. "@closure" ^ string_of_int nargs, e
  766. ) [0;1;2;3;4;5] in
  767. List.map (fun (v,e)-> EBinop ("=",ident p v,e),p) inits
  768. let build ctx types =
  769. let packs = List.concat (List.map (gen_package ctx) types) in
  770. let names = List.fold_left (gen_name ctx) [] types in
  771. let methods = List.rev (List.fold_left (fun acc t -> gen_type ctx t acc) [] types) in
  772. let boot = gen_boot ctx in
  773. let inits = List.map (fun (c,e) ->
  774. ctx.curclass <- s_type_path c.cl_path;
  775. ctx.curmethod <- "__init__";
  776. gen_expr ctx e
  777. ) (List.rev ctx.inits) in
  778. ctx.inits <- [];
  779. let vars = List.concat (List.map (gen_static_vars ctx) types) in
  780. packs @ methods @ boot :: names @ inits @ vars
  781. let generate com =
  782. let ctx = new_context com (if Common.defined com Define.NekoV1 then 1 else 2) false in
  783. let t = Common.timer "neko generation" in
  784. let libs = (EBlock (generate_libs_init com.neko_libs) , { psource = "<header>"; pline = 1; }) in
  785. let el = build ctx com.types in
  786. let emain = (match com.main with None -> [] | Some e -> [gen_expr ctx e]) in
  787. let e = (EBlock ((header()) @ libs :: el @ emain), null_pos) in
  788. let source = Common.defined com Define.NekoSource in
  789. let use_nekoc = Common.defined com Define.UseNekoc in
  790. if not use_nekoc then begin
  791. try
  792. let ch = IO.output_channel (open_out_bin com.file) in
  793. Nbytecode.write ch (Ncompile.compile ctx.version e);
  794. IO.close_out ch;
  795. with Ncompile.Error (msg,pos) ->
  796. let rec loop p =
  797. let pp = { pfile = pos.psource; pmin = p; pmax = p; } in
  798. if Lexer.get_error_line pp >= pos.pline then
  799. pp
  800. else
  801. loop (p + 1)
  802. in
  803. error msg (loop 0)
  804. end;
  805. let command cmd = try com.run_command cmd with _ -> -1 in
  806. let neko_file = (try Filename.chop_extension com.file with _ -> com.file) ^ ".neko" in
  807. if source || use_nekoc then begin
  808. let ch = IO.output_channel (open_out_bin neko_file) in
  809. Binast.write ch e;
  810. IO.close_out ch;
  811. end;
  812. if use_nekoc && command ("nekoc" ^ (if ctx.version > 1 then " -version " ^ string_of_int ctx.version else "") ^ " \"" ^ neko_file ^ "\"") <> 0 then failwith "Neko compilation failure";
  813. if source then begin
  814. if command ("nekoc -p \"" ^ neko_file ^ "\"") <> 0 then failwith "Failed to print neko code";
  815. Sys.remove neko_file;
  816. Sys.rename ((try Filename.chop_extension com.file with _ -> com.file) ^ "2.neko") neko_file;
  817. end;
  818. t()