forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			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 | ||
|  | } |