ExampleJSGenerator.hx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. * Copyright (C)2005-2019 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. package haxe.macro;
  23. import haxe.macro.Type;
  24. import haxe.macro.Expr;
  25. using Lambda;
  26. class ExampleJSGenerator {
  27. var api:JSGenApi;
  28. var buf:StringBuf;
  29. var inits:List<TypedExpr>;
  30. var statics:List<{c:ClassType, f:ClassField}>;
  31. var packages:haxe.ds.StringMap<Bool>;
  32. var forbidden:haxe.ds.StringMap<Bool>;
  33. public function new(api) {
  34. this.api = api;
  35. buf = new StringBuf();
  36. inits = new List();
  37. statics = new List();
  38. packages = new haxe.ds.StringMap();
  39. forbidden = new haxe.ds.StringMap();
  40. for (x in ["prototype", "__proto__", "constructor"])
  41. forbidden.set(x, true);
  42. api.setTypeAccessor(getType);
  43. }
  44. function getType(t:Type) {
  45. return switch (t) {
  46. case TInst(c, _): getPath(c.get());
  47. case TEnum(e, _): getPath(e.get());
  48. case TAbstract(a, _): getPath(a.get());
  49. default: throw "assert";
  50. };
  51. }
  52. inline function print(str:String) {
  53. buf.add(str);
  54. }
  55. inline function newline() {
  56. buf.add(";\n");
  57. }
  58. inline function genExpr(e) {
  59. print(api.generateValue(e));
  60. }
  61. function field(p) {
  62. return api.isKeyword(p) ? '["' + p + '"]' : "." + p;
  63. }
  64. function genPackage(p:Array<String>) {
  65. var full = null;
  66. for (x in p) {
  67. var prev = full;
  68. if (full == null)
  69. full = x
  70. else
  71. full += "." + x;
  72. if (packages.exists(full))
  73. continue;
  74. packages.set(full, true);
  75. if (prev == null)
  76. print('if(typeof $x==\'undefined\') $x = {}');
  77. else {
  78. var p = prev + field(x);
  79. print('if(!$p) $p = {}');
  80. }
  81. newline();
  82. }
  83. }
  84. function getPath(t:BaseType) {
  85. return (t.pack.length == 0) ? t.name : t.pack.join(".") + "." + t.name;
  86. }
  87. function checkFieldName(c:ClassType, f:ClassField) {
  88. if (forbidden.exists(f.name))
  89. Context.error("The field " + f.name + " is not allowed in JS", c.pos);
  90. }
  91. function genClassField(c:ClassType, p:String, f:ClassField) {
  92. checkFieldName(c, f);
  93. var field = field(f.name);
  94. print('$p.prototype$field = ');
  95. var e = f.expr();
  96. if (e == null)
  97. print("null");
  98. else {
  99. genExpr(e);
  100. }
  101. newline();
  102. }
  103. function genStaticField(c:ClassType, p:String, f:ClassField) {
  104. checkFieldName(c, f);
  105. var field = field(f.name);
  106. var e = f.expr();
  107. if (e == null) {
  108. print('$p$field = null');
  109. newline();
  110. } else
  111. switch (f.kind) {
  112. case FMethod(_):
  113. print('$p$field = ');
  114. genExpr(e);
  115. newline();
  116. default:
  117. statics.add({c: c, f: f});
  118. }
  119. }
  120. function genClass(c:ClassType) {
  121. genPackage(c.pack);
  122. api.setCurrentClass(c);
  123. var p = getPath(c);
  124. print('$p = $$hxClasses[\'$p\'] = ');
  125. if (c.constructor != null)
  126. genExpr(c.constructor.get().expr());
  127. else
  128. print("function() { }");
  129. newline();
  130. print('$p.__name__ = "$p"');
  131. newline();
  132. if (c.superClass != null) {
  133. var psup = getPath(c.superClass.t.get());
  134. print('$p.__super__ = $psup');
  135. newline();
  136. print('for(var k in $psup.prototype ) $p.prototype[k] = $psup.prototype[k]');
  137. newline();
  138. }
  139. for (f in c.statics.get())
  140. genStaticField(c, p, f);
  141. for (f in c.fields.get()) {
  142. switch (f.kind) {
  143. case FVar(r, _):
  144. if (r == AccResolve)
  145. continue;
  146. default:
  147. }
  148. genClassField(c, p, f);
  149. }
  150. print('$p.prototype.__class__ = $p');
  151. newline();
  152. if (c.interfaces.length > 0) {
  153. var me = this;
  154. var inter = c.interfaces.map(function(i) return me.getPath(i.t.get())).join(",");
  155. print('$p.__interfaces__ = [$inter]');
  156. newline();
  157. }
  158. }
  159. function genEnum(e:EnumType) {
  160. genPackage(e.pack);
  161. var p = getPath(e);
  162. var constructs = e.names.map(api.quoteString).join(",");
  163. print('$p = $$hxClasses[\'$p\'] = { __ename__ : \'$p\', __constructs__ : [$constructs] }');
  164. newline();
  165. for (c in e.constructs.keys()) {
  166. var c = e.constructs.get(c);
  167. var f = field(c.name);
  168. print('$p$f = ');
  169. switch (c.type) {
  170. case TFun(args, _):
  171. var sargs = args.map(function(a) return a.name).join(",");
  172. print('function($sargs) { var $$x = ["${c.name}",${c.index},$sargs]; $$x.__enum__ = $p; $$x.toString = $$estr; return $$x; }');
  173. default:
  174. print("[" + api.quoteString(c.name) + "," + c.index + "]");
  175. newline();
  176. print('$p$f.toString = $$estr');
  177. newline();
  178. print('$p$f.__enum__ = $p');
  179. }
  180. newline();
  181. }
  182. var meta = api.buildMetaData(e);
  183. if (meta != null) {
  184. print('$p.__meta__ = ');
  185. genExpr(meta);
  186. newline();
  187. }
  188. }
  189. function genStaticValue(c:ClassType, cf:ClassField) {
  190. var p = getPath(c);
  191. var f = field(cf.name);
  192. print('$p$f = ');
  193. genExpr(cf.expr());
  194. newline();
  195. }
  196. function genType(t:Type) {
  197. switch (t) {
  198. case TInst(c, _):
  199. var c = c.get();
  200. if (c.init != null)
  201. inits.add(c.init);
  202. if (!c.isExtern)
  203. genClass(c);
  204. case TEnum(r, _):
  205. var e = r.get();
  206. if (!e.isExtern)
  207. genEnum(e);
  208. default:
  209. }
  210. }
  211. public function generate() {
  212. print("var $_, $hxClasses = $hxClasses || {}, $estr = function() { return js.Boot.__string_rec(this,''); }");
  213. newline();
  214. print(
  215. #if (js_es < 5)
  216. "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; o.hx__closures__[m.__id__] = f; } return f; }"
  217. #else
  218. "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }"
  219. #end
  220. );
  221. newline();
  222. for (t in api.types)
  223. genType(t);
  224. for (e in inits) {
  225. print(api.generateStatement(e));
  226. newline();
  227. }
  228. for (s in statics) {
  229. genStaticValue(s.c, s.f);
  230. newline();
  231. }
  232. if (api.main != null) {
  233. genExpr(api.main);
  234. newline();
  235. }
  236. sys.io.File.saveContent(api.outputFile, buf.toString());
  237. }
  238. #if macro
  239. public static function use() {
  240. Compiler.setCustomJSGenerator(function(api) new ExampleJSGenerator(api).generate());
  241. }
  242. #end
  243. }