259 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			259 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (C)2005-2019 Haxe Foundation
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Permission is hereby granted, free of charge, to any person obtaining a
							 | 
						||
| 
								 | 
							
								 * copy of this software and associated documentation files (the "Software"),
							 | 
						||
| 
								 | 
							
								 * to deal in the Software without restriction, including without limitation
							 | 
						||
| 
								 | 
							
								 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
							 | 
						||
| 
								 | 
							
								 * and/or sell copies of the Software, and to permit persons to whom the
							 | 
						||
| 
								 | 
							
								 * Software is furnished to do so, subject to the following conditions:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The above copyright notice and this permission notice shall be included in
							 | 
						||
| 
								 | 
							
								 * all copies or substantial portions of the Software.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
							 | 
						||
| 
								 | 
							
								 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
							 | 
						||
| 
								 | 
							
								 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
							 | 
						||
| 
								 | 
							
								 * DEALINGS IN THE SOFTWARE.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package haxe.macro;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import haxe.macro.Type;
							 | 
						||
| 
								 | 
							
								import haxe.macro.Expr;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								using Lambda;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ExampleJSGenerator {
							 | 
						||
| 
								 | 
							
									var api:JSGenApi;
							 | 
						||
| 
								 | 
							
									var buf:StringBuf;
							 | 
						||
| 
								 | 
							
									var inits:List<TypedExpr>;
							 | 
						||
| 
								 | 
							
									var statics:List<{c:ClassType, f:ClassField}>;
							 | 
						||
| 
								 | 
							
									var packages:haxe.ds.StringMap<Bool>;
							 | 
						||
| 
								 | 
							
									var forbidden:haxe.ds.StringMap<Bool>;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function new(api) {
							 | 
						||
| 
								 | 
							
										this.api = api;
							 | 
						||
| 
								 | 
							
										buf = new StringBuf();
							 | 
						||
| 
								 | 
							
										inits = new List();
							 | 
						||
| 
								 | 
							
										statics = new List();
							 | 
						||
| 
								 | 
							
										packages = new haxe.ds.StringMap();
							 | 
						||
| 
								 | 
							
										forbidden = new haxe.ds.StringMap();
							 | 
						||
| 
								 | 
							
										for (x in ["prototype", "__proto__", "constructor"])
							 | 
						||
| 
								 | 
							
											forbidden.set(x, true);
							 | 
						||
| 
								 | 
							
										api.setTypeAccessor(getType);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getType(t:Type) {
							 | 
						||
| 
								 | 
							
										return switch (t) {
							 | 
						||
| 
								 | 
							
											case TInst(c, _): getPath(c.get());
							 | 
						||
| 
								 | 
							
											case TEnum(e, _): getPath(e.get());
							 | 
						||
| 
								 | 
							
											case TAbstract(a, _): getPath(a.get());
							 | 
						||
| 
								 | 
							
											default: throw "assert";
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function print(str:String) {
							 | 
						||
| 
								 | 
							
										buf.add(str);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function newline() {
							 | 
						||
| 
								 | 
							
										buf.add(";\n");
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function genExpr(e) {
							 | 
						||
| 
								 | 
							
										print(api.generateValue(e));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function field(p) {
							 | 
						||
| 
								 | 
							
										return api.isKeyword(p) ? '["' + p + '"]' : "." + p;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genPackage(p:Array<String>) {
							 | 
						||
| 
								 | 
							
										var full = null;
							 | 
						||
| 
								 | 
							
										for (x in p) {
							 | 
						||
| 
								 | 
							
											var prev = full;
							 | 
						||
| 
								 | 
							
											if (full == null)
							 | 
						||
| 
								 | 
							
												full = x
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
												full += "." + x;
							 | 
						||
| 
								 | 
							
											if (packages.exists(full))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											packages.set(full, true);
							 | 
						||
| 
								 | 
							
											if (prev == null)
							 | 
						||
| 
								 | 
							
												print('if(typeof $x==\'undefined\') $x = {}');
							 | 
						||
| 
								 | 
							
											else {
							 | 
						||
| 
								 | 
							
												var p = prev + field(x);
							 | 
						||
| 
								 | 
							
												print('if(!$p) $p = {}');
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function getPath(t:BaseType) {
							 | 
						||
| 
								 | 
							
										return (t.pack.length == 0) ? t.name : t.pack.join(".") + "." + t.name;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function checkFieldName(c:ClassType, f:ClassField) {
							 | 
						||
| 
								 | 
							
										if (forbidden.exists(f.name))
							 | 
						||
| 
								 | 
							
											Context.error("The field " + f.name + " is not allowed in JS", c.pos);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genClassField(c:ClassType, p:String, f:ClassField) {
							 | 
						||
| 
								 | 
							
										checkFieldName(c, f);
							 | 
						||
| 
								 | 
							
										var field = field(f.name);
							 | 
						||
| 
								 | 
							
										print('$p.prototype$field = ');
							 | 
						||
| 
								 | 
							
										var e = f.expr();
							 | 
						||
| 
								 | 
							
										if (e == null)
							 | 
						||
| 
								 | 
							
											print("null");
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											genExpr(e);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genStaticField(c:ClassType, p:String, f:ClassField) {
							 | 
						||
| 
								 | 
							
										checkFieldName(c, f);
							 | 
						||
| 
								 | 
							
										var field = field(f.name);
							 | 
						||
| 
								 | 
							
										var e = f.expr();
							 | 
						||
| 
								 | 
							
										if (e == null) {
							 | 
						||
| 
								 | 
							
											print('$p$field = null');
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										} else
							 | 
						||
| 
								 | 
							
											switch (f.kind) {
							 | 
						||
| 
								 | 
							
												case FMethod(_):
							 | 
						||
| 
								 | 
							
													print('$p$field = ');
							 | 
						||
| 
								 | 
							
													genExpr(e);
							 | 
						||
| 
								 | 
							
													newline();
							 | 
						||
| 
								 | 
							
												default:
							 | 
						||
| 
								 | 
							
													statics.add({c: c, f: f});
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genClass(c:ClassType) {
							 | 
						||
| 
								 | 
							
										genPackage(c.pack);
							 | 
						||
| 
								 | 
							
										api.setCurrentClass(c);
							 | 
						||
| 
								 | 
							
										var p = getPath(c);
							 | 
						||
| 
								 | 
							
										print('$p = $$hxClasses[\'$p\'] = ');
							 | 
						||
| 
								 | 
							
										if (c.constructor != null)
							 | 
						||
| 
								 | 
							
											genExpr(c.constructor.get().expr());
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
											print("function() { }");
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										print('$p.__name__ = "$p"');
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										if (c.superClass != null) {
							 | 
						||
| 
								 | 
							
											var psup = getPath(c.superClass.t.get());
							 | 
						||
| 
								 | 
							
											print('$p.__super__ = $psup');
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
											print('for(var k in $psup.prototype ) $p.prototype[k] = $psup.prototype[k]');
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for (f in c.statics.get())
							 | 
						||
| 
								 | 
							
											genStaticField(c, p, f);
							 | 
						||
| 
								 | 
							
										for (f in c.fields.get()) {
							 | 
						||
| 
								 | 
							
											switch (f.kind) {
							 | 
						||
| 
								 | 
							
												case FVar(r, _):
							 | 
						||
| 
								 | 
							
													if (r == AccResolve)
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
												default:
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											genClassField(c, p, f);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										print('$p.prototype.__class__ = $p');
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										if (c.interfaces.length > 0) {
							 | 
						||
| 
								 | 
							
											var me = this;
							 | 
						||
| 
								 | 
							
											var inter = c.interfaces.map(function(i) return me.getPath(i.t.get())).join(",");
							 | 
						||
| 
								 | 
							
											print('$p.__interfaces__ = [$inter]');
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genEnum(e:EnumType) {
							 | 
						||
| 
								 | 
							
										genPackage(e.pack);
							 | 
						||
| 
								 | 
							
										var p = getPath(e);
							 | 
						||
| 
								 | 
							
										var constructs = e.names.map(api.quoteString).join(",");
							 | 
						||
| 
								 | 
							
										print('$p = $$hxClasses[\'$p\'] = { __ename__ : \'$p\', __constructs__ : [$constructs] }');
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										for (c in e.constructs.keys()) {
							 | 
						||
| 
								 | 
							
											var c = e.constructs.get(c);
							 | 
						||
| 
								 | 
							
											var f = field(c.name);
							 | 
						||
| 
								 | 
							
											print('$p$f = ');
							 | 
						||
| 
								 | 
							
											switch (c.type) {
							 | 
						||
| 
								 | 
							
												case TFun(args, _):
							 | 
						||
| 
								 | 
							
													var sargs = args.map(function(a) return a.name).join(",");
							 | 
						||
| 
								 | 
							
													print('function($sargs) { var $$x = ["${c.name}",${c.index},$sargs]; $$x.__enum__ = $p; $$x.toString = $$estr; return $$x; }');
							 | 
						||
| 
								 | 
							
												default:
							 | 
						||
| 
								 | 
							
													print("[" + api.quoteString(c.name) + "," + c.index + "]");
							 | 
						||
| 
								 | 
							
													newline();
							 | 
						||
| 
								 | 
							
													print('$p$f.toString = $$estr');
							 | 
						||
| 
								 | 
							
													newline();
							 | 
						||
| 
								 | 
							
													print('$p$f.__enum__ = $p');
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var meta = api.buildMetaData(e);
							 | 
						||
| 
								 | 
							
										if (meta != null) {
							 | 
						||
| 
								 | 
							
											print('$p.__meta__ = ');
							 | 
						||
| 
								 | 
							
											genExpr(meta);
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genStaticValue(c:ClassType, cf:ClassField) {
							 | 
						||
| 
								 | 
							
										var p = getPath(c);
							 | 
						||
| 
								 | 
							
										var f = field(cf.name);
							 | 
						||
| 
								 | 
							
										print('$p$f = ');
							 | 
						||
| 
								 | 
							
										genExpr(cf.expr());
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function genType(t:Type) {
							 | 
						||
| 
								 | 
							
										switch (t) {
							 | 
						||
| 
								 | 
							
											case TInst(c, _):
							 | 
						||
| 
								 | 
							
												var c = c.get();
							 | 
						||
| 
								 | 
							
												if (c.init != null)
							 | 
						||
| 
								 | 
							
													inits.add(c.init);
							 | 
						||
| 
								 | 
							
												if (!c.isExtern)
							 | 
						||
| 
								 | 
							
													genClass(c);
							 | 
						||
| 
								 | 
							
											case TEnum(r, _):
							 | 
						||
| 
								 | 
							
												var e = r.get();
							 | 
						||
| 
								 | 
							
												if (!e.isExtern)
							 | 
						||
| 
								 | 
							
													genEnum(e);
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function generate() {
							 | 
						||
| 
								 | 
							
										print("var $_, $hxClasses = $hxClasses || {}, $estr = function() { return js.Boot.__string_rec(this,''); }");
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										print("function $bind(o,m) { var f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; return f; };");
							 | 
						||
| 
								 | 
							
										newline();
							 | 
						||
| 
								 | 
							
										for (t in api.types)
							 | 
						||
| 
								 | 
							
											genType(t);
							 | 
						||
| 
								 | 
							
										for (e in inits) {
							 | 
						||
| 
								 | 
							
											print(api.generateStatement(e));
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for (s in statics) {
							 | 
						||
| 
								 | 
							
											genStaticValue(s.c, s.f);
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (api.main != null) {
							 | 
						||
| 
								 | 
							
											genExpr(api.main);
							 | 
						||
| 
								 | 
							
											newline();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										sys.io.File.saveContent(api.outputFile, buf.toString());
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									#if (macro || display)
							 | 
						||
| 
								 | 
							
									public static function use() {
							 | 
						||
| 
								 | 
							
										Compiler.setCustomJSGenerator(function(api) new ExampleJSGenerator(api).generate());
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									#end
							 | 
						||
| 
								 | 
							
								}
							 |