478 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			478 lines
		
	
	
		
			13 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 python; | ||
|  | 
 | ||
|  | import python.internal.MethodClosure; | ||
|  | import python.internal.ArrayImpl; | ||
|  | import python.internal.Internal; | ||
|  | import python.internal.StringImpl; | ||
|  | import python.internal.EnumImpl; | ||
|  | import python.internal.HxOverrides; | ||
|  | import python.internal.AnonObject; | ||
|  | import python.internal.UBuiltins; | ||
|  | import python.lib.Inspect; | ||
|  | import python.Syntax; | ||
|  | 
 | ||
|  | @:dox(hide) | ||
|  | class Boot { | ||
|  | 	static var keywords:Set<String> = new Set([ | ||
|  | 		"and", "del", "from", "not", "with", "as", "elif", "global", "or", "yield", "assert", "else", "if", "pass", "None", "break", "except", "import", | ||
|  | 		"raise", "True", "class", "exec", "in", "return", "False", "continue", "finally", "is", "try", "def", "for", "lambda", "while", | ||
|  | 	]); | ||
|  | 
 | ||
|  | 	inline static function arrayJoin<T>(x:Array<T>, sep:String):String { | ||
|  | 		return Syntax.field(sep, "join")(Syntax.code("[{0}(x1,'') for x1 in {1}]", python.Boot.toString1, x)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline static function safeJoin(x:Array<String>, sep:String):String { | ||
|  | 		return Syntax.field(sep, "join")(Syntax.code("[x1 for x1 in {0}]", x)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline static function isPyBool(o:Dynamic):Bool { | ||
|  | 		return UBuiltins.isinstance(o, UBuiltins.bool); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline static function isPyInt(o:Dynamic):Bool { | ||
|  | 		// for historic reasons bool extends int | ||
|  | 		return UBuiltins.isinstance(o, UBuiltins.int) && !isPyBool(o); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	inline static function isPyFloat(o:Dynamic):Bool { | ||
|  | 		return UBuiltins.isinstance(o, UBuiltins.float); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isClass(o:Dynamic):Bool { | ||
|  | 		return o != null && (o == String || Inspect.isclass(o)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isAnonObject(o:Dynamic) { | ||
|  | 		return UBuiltins.isinstance(o, AnonObject); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:ifFeature("add_dynamic") private static function _add_dynamic(a:Dynamic, b:Dynamic):Dynamic { | ||
|  | 		if (UBuiltins.isinstance(a, String) && UBuiltins.isinstance(b, String)) { | ||
|  | 			return Syntax.binop(a, "+", b); | ||
|  | 		} | ||
|  | 		if (UBuiltins.isinstance(a, String) || UBuiltins.isinstance(b, String)) { | ||
|  | 			return Syntax.binop(toString1(a, ""), "+", toString1(b, "")); | ||
|  | 		} | ||
|  | 		return Syntax.binop(a, "+", b); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function toString(o:Dynamic) { | ||
|  | 		return toString1(o, ""); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	private static function toString1(o:Dynamic, s:String):String { | ||
|  | 		if (o == null) | ||
|  | 			return "null"; | ||
|  | 
 | ||
|  | 		if (isString(o)) | ||
|  | 			return o; | ||
|  | 
 | ||
|  | 		if (s == null) | ||
|  | 			s = ""; | ||
|  | 		if (s.length >= 5) | ||
|  | 			return "<...>"; // too much deep recursion | ||
|  | 
 | ||
|  | 		if (isPyBool(o)) { | ||
|  | 			if ((o : Bool)) | ||
|  | 				return "true" | ||
|  | 			else | ||
|  | 				return "false"; | ||
|  | 		} | ||
|  | 		if (isPyInt(o)) { | ||
|  | 			return UBuiltins.str(o); | ||
|  | 		} | ||
|  | 		// 1.0 should be printed as 1 | ||
|  | 		if (isPyFloat(o)) { | ||
|  | 			try { | ||
|  | 				if ((o : Float) == UBuiltins.int(o)) { | ||
|  | 					return UBuiltins.str(Math.round(o)); | ||
|  | 				} else { | ||
|  | 					return UBuiltins.str(o); | ||
|  | 				} | ||
|  | 			} catch (e:Dynamic) { | ||
|  | 				return UBuiltins.str(o); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (isArray(o)) { | ||
|  | 			var o1:Array<Dynamic> = o; | ||
|  | 
 | ||
|  | 			var l = o1.length; | ||
|  | 
 | ||
|  | 			var st = "["; | ||
|  | 			s += "\t"; | ||
|  | 			for (i in 0...l) { | ||
|  | 				var prefix = ""; | ||
|  | 				if (i > 0) { | ||
|  | 					prefix = ","; | ||
|  | 				} | ||
|  | 				st += prefix + toString1(o1[i], s); | ||
|  | 			} | ||
|  | 			st += "]"; | ||
|  | 			return st; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		try { | ||
|  | 			if (UBuiltins.hasattr(o, "toString")) | ||
|  | 				return Syntax.callField(o, "toString"); | ||
|  | 		} catch (e:Dynamic) {} | ||
|  | 
 | ||
|  | 		if (UBuiltins.hasattr(o, "__class__")) { | ||
|  | 			if (isAnonObject(o)) { | ||
|  | 				var toStr = null; | ||
|  | 				try { | ||
|  | 					var fields = fields(o); | ||
|  | 					var fieldsStr = [for (f in fields) '$f : ${toString1(simpleField(o, f), s + "\t")}']; | ||
|  | 					toStr = "{ " + safeJoin(fieldsStr, ", ") + " }"; | ||
|  | 				} catch (e:Dynamic) { | ||
|  | 					return "{ ... }"; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if (toStr == null) { | ||
|  | 					return "{ ... }"; | ||
|  | 				} else { | ||
|  | 					return toStr; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			if (UBuiltins.isinstance(o, Enum)) { | ||
|  | 				var o:EnumImpl = (o : EnumImpl); | ||
|  | 
 | ||
|  | 				var l = UBuiltins.len(o.params); | ||
|  | 				var hasParams = l > 0; | ||
|  | 				if (hasParams) { | ||
|  | 					var paramsStr = ""; | ||
|  | 					for (i in 0...l) { | ||
|  | 						var prefix = ""; | ||
|  | 						if (i > 0) { | ||
|  | 							prefix = ","; | ||
|  | 						} | ||
|  | 						paramsStr += prefix + toString1(o.params[i], s); | ||
|  | 					} | ||
|  | 					return o.tag + "(" + paramsStr + ")"; | ||
|  | 				} else { | ||
|  | 					return o.tag; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (Internal.hasClassName(o)) { | ||
|  | 				if (Syntax.field(Syntax.field(o, "__class__"), "__name__") != "type") { | ||
|  | 					var fields = getInstanceFields(o); | ||
|  | 					var fieldsStr = [for (f in fields) '$f : ${toString1(simpleField(o, f), s + "\t")}']; | ||
|  | 
 | ||
|  | 					var toStr = (Internal.fieldClassName(o) : String) + "( " + safeJoin(fieldsStr, ", ") + " )"; | ||
|  | 					return toStr; | ||
|  | 				} else { | ||
|  | 					var fields = getClassFields(o); | ||
|  | 					var fieldsStr = [for (f in fields) '$f : ${toString1(simpleField(o, f), s + "\t")}']; | ||
|  | 					var toStr = "#" + (Internal.fieldClassName(o) : String) + "( " + safeJoin(fieldsStr, ", ") + " )"; | ||
|  | 					return toStr; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (isMetaType(o, String)) { | ||
|  | 				return "#String"; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (isMetaType(o, Array)) { | ||
|  | 				return "#Array"; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (UBuiltins.callable(o)) { | ||
|  | 				return "function"; | ||
|  | 			} | ||
|  | 			try { | ||
|  | 				if (UBuiltins.hasattr(o, "__repr__")) { | ||
|  | 					return Syntax.callField(o, "__repr__"); | ||
|  | 				} | ||
|  | 			} catch (e:Dynamic) {} | ||
|  | 
 | ||
|  | 			if (UBuiltins.hasattr(o, "__str__")) { | ||
|  | 				return Syntax.callField(o, "__str__", []); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (UBuiltins.hasattr(o, "__name__")) { | ||
|  | 				return Syntax.field(o, "__name__"); | ||
|  | 			} | ||
|  | 			return "???"; | ||
|  | 		} else { | ||
|  | 			return UBuiltins.str(o); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isMetaType(v:Dynamic, t:Dynamic):Bool { | ||
|  | 		return Syntax.binop(Syntax.binop(Syntax.call(UBuiltins.type, [v]), "==", UBuiltins.type), "and", Syntax.binop(v, "==", t)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:analyzer(no_local_dce) | ||
|  | 	static function fields(o:Dynamic) { | ||
|  | 		var a = []; | ||
|  | 		if (o != null) { | ||
|  | 			if (Internal.hasFields(o)) { | ||
|  | 				var fields = Internal.fieldFields(o); | ||
|  | 				if (fields != null) { | ||
|  | 					return (fields : Array<String>).copy(); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			if (isAnonObject(o)) { | ||
|  | 				var d = Syntax.field(o, "__dict__"); | ||
|  | 				var keys = Syntax.callField(d, "keys"); | ||
|  | 				var handler = unhandleKeywords; | ||
|  | 
 | ||
|  | 				Syntax.code("for k in keys:"); | ||
|  | 				Syntax.code("    if (k != '_hx_disable_getattr'):"); | ||
|  | 				Syntax.code("        a.append(handler(k))"); | ||
|  | 			} else if (UBuiltins.hasattr(o, "__dict__")) { | ||
|  | 				var a = []; | ||
|  | 				var d = Syntax.field(o, "__dict__"); | ||
|  | 				var keys1 = Syntax.callField(d, "keys"); | ||
|  | 				Syntax.code("for k in keys1:"); | ||
|  | 				Syntax.code("    a.append(k)"); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return a; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isString(o:Dynamic):Bool { | ||
|  | 		return UBuiltins.isinstance(o, UBuiltins.str); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isArray(o:Dynamic):Bool { | ||
|  | 		return UBuiltins.isinstance(o, UBuiltins.list); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function simpleField(o:Dynamic, field:String):Dynamic { | ||
|  | 		if (field == null) | ||
|  | 			return null; | ||
|  | 
 | ||
|  | 		var field = handleKeywords(field); | ||
|  | 		return if (UBuiltins.hasattr(o, field)) UBuiltins.getattr(o, field) else null; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:ifFeature("closure_Array", "closure_String") | ||
|  | 	static inline function createClosure(obj:Dynamic, func:Dynamic):Dynamic { | ||
|  | 		return new MethodClosure(obj, func); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function hasField(o:Dynamic, field:String):Bool { | ||
|  | 		if (isAnonObject(o)) { | ||
|  | 			return Syntax.code('{0}._hx_hasattr({1})', o, field); | ||
|  | 		} | ||
|  | 		return UBuiltins.hasattr(o, handleKeywords(field)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function field(o:Dynamic, field:String):Dynamic { | ||
|  | 		if (field == null) | ||
|  | 			return null; | ||
|  | 
 | ||
|  | 		inline function def() { | ||
|  | 			var field = handleKeywords(field); | ||
|  | 			return if (UBuiltins.hasattr(o, field)) UBuiltins.getattr(o, field) else null; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return if (isString(o)) { | ||
|  | 			switch (field) { | ||
|  | 				case "length": | ||
|  | 					StringImpl.get_length(o); | ||
|  | 				case "toLowerCase": | ||
|  | 					createClosure(o, StringImpl.toLowerCase); | ||
|  | 				case "toUpperCase": | ||
|  | 					createClosure(o, StringImpl.toUpperCase); | ||
|  | 				case "charAt": | ||
|  | 					createClosure(o, StringImpl.charAt); | ||
|  | 				case "charCodeAt": | ||
|  | 					createClosure(o, StringImpl.charCodeAt); | ||
|  | 				case "indexOf": | ||
|  | 					createClosure(o, StringImpl.indexOf); | ||
|  | 				case "lastIndexOf": | ||
|  | 					createClosure(o, StringImpl.lastIndexOf); | ||
|  | 				case "split": | ||
|  | 					createClosure(o, StringImpl.split); | ||
|  | 				case "substr": | ||
|  | 					createClosure(o, StringImpl.substr); | ||
|  | 				case "substring": | ||
|  | 					createClosure(o, StringImpl.substring); | ||
|  | 				case "toString": | ||
|  | 					createClosure(o, StringImpl.toString); | ||
|  | 				default: | ||
|  | 					def(); | ||
|  | 			} | ||
|  | 		} else if (isArray(o)) { | ||
|  | 			switch (field) { | ||
|  | 				case "length": | ||
|  | 					ArrayImpl.get_length(o); | ||
|  | 				case "map": | ||
|  | 					createClosure(o, ArrayImpl.map); | ||
|  | 				case "filter": | ||
|  | 					createClosure(o, ArrayImpl.filter); | ||
|  | 				case "concat": | ||
|  | 					createClosure(o, ArrayImpl.concat); | ||
|  | 				case "copy": | ||
|  | 					createClosure(o, ArrayImpl.copy); | ||
|  | 				case "iterator": | ||
|  | 					createClosure(o, ArrayImpl.iterator); | ||
|  | 				case "keyValueIterator": | ||
|  | 					createClosure(o, ArrayImpl.keyValueIterator); | ||
|  | 				case "insert": | ||
|  | 					createClosure(o, ArrayImpl.insert); | ||
|  | 				case "join": | ||
|  | 					createClosure(o, ArrayImpl.join); | ||
|  | 				case "toString": | ||
|  | 					createClosure(o, ArrayImpl.toString); | ||
|  | 				case "pop": | ||
|  | 					createClosure(o, ArrayImpl.pop); | ||
|  | 				case "push": | ||
|  | 					createClosure(o, ArrayImpl.push); | ||
|  | 				case "unshift": | ||
|  | 					createClosure(o, ArrayImpl.unshift); | ||
|  | 				case "indexOf": | ||
|  | 					createClosure(o, ArrayImpl.indexOf); | ||
|  | 				case "lastIndexOf": | ||
|  | 					createClosure(o, ArrayImpl.lastIndexOf); | ||
|  | 				case "contains": | ||
|  | 					createClosure(o, ArrayImpl.contains); | ||
|  | 				case "remove": | ||
|  | 					createClosure(o, ArrayImpl.remove); | ||
|  | 				case "reverse": | ||
|  | 					createClosure(o, ArrayImpl.reverse); | ||
|  | 				case "shift": | ||
|  | 					createClosure(o, ArrayImpl.shift); | ||
|  | 				case "slice": | ||
|  | 					createClosure(o, ArrayImpl.slice); | ||
|  | 				case "sort": | ||
|  | 					createClosure(o, ArrayImpl.sort); | ||
|  | 				case "splice": | ||
|  | 					createClosure(o, ArrayImpl.splice); | ||
|  | 				default: | ||
|  | 					def(); | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			def(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function getInstanceFields(c:Class<Dynamic>):Array<String> { | ||
|  | 		var f = if (Internal.hasFields(c)) (Internal.fieldFields(c) : Array<String>).copy() else []; | ||
|  | 		if (Internal.hasMethods(c)) | ||
|  | 			f = f.concat(Internal.fieldMethods(c)); | ||
|  | 
 | ||
|  | 		var sc = getSuperClass(c); | ||
|  | 
 | ||
|  | 		if (sc == null) { | ||
|  | 			return f; | ||
|  | 		} else { | ||
|  | 			var scArr = getInstanceFields(sc); | ||
|  | 			var scMap = new Set(scArr); | ||
|  | 			for (f1 in f) { | ||
|  | 				if (!scMap.has(f1)) { | ||
|  | 					scArr.push(f1); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return scArr; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function getSuperClass(c:Class<Dynamic>):Class<Dynamic> { | ||
|  | 		if (c == null) | ||
|  | 			return null; | ||
|  | 
 | ||
|  | 		try { | ||
|  | 			if (Internal.hasSuper(c)) { | ||
|  | 				return Internal.fieldSuper(c); | ||
|  | 			} | ||
|  | 			return null; | ||
|  | 		} catch (e:Dynamic) {} | ||
|  | 		return null; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function getClassFields(c:Class<Dynamic>):Array<String> { | ||
|  | 		if (Internal.hasStatics(c)) { | ||
|  | 			var x:Array<String> = Internal.fieldStatics(c); | ||
|  | 			return x.copy(); | ||
|  | 		} else { | ||
|  | 			return []; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function unsafeFastCodeAt(s:String, index:Int) { | ||
|  | 		return UBuiltins.ord(python.Syntax.arrayAccess(s, index)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function handleKeywords(name:String):String { | ||
|  | 		return if (keywords.has(name)) { | ||
|  | 			Internal.getPrefixed(name); | ||
|  | 		} else if (name.length > 2 | ||
|  | 			&& unsafeFastCodeAt(name, 0) == "_".code | ||
|  | 			&& unsafeFastCodeAt(name, 1) == "_".code | ||
|  | 			&& unsafeFastCodeAt(name, name.length - 1) != "_".code) { | ||
|  | 			Internal.getPrefixed(name); | ||
|  | 		} else | ||
|  | 			name; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static var prefixLength = Internal.prefix().length; | ||
|  | 
 | ||
|  | 	static function unhandleKeywords(name:String):String { | ||
|  | 		if (name.substr(0, prefixLength) == Internal.prefix()) { | ||
|  | 			var real = name.substr(prefixLength); | ||
|  | 			if (keywords.has(real)) | ||
|  | 				return real; | ||
|  | 		} | ||
|  | 		return name; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function implementsInterface(value:Dynamic, cls:Class<Dynamic>):Bool { | ||
|  | 		function loop(intf) { | ||
|  | 			var f:Array<Dynamic> = if (Internal.hasInterfaces(intf)) Internal.fieldInterfaces(intf) else []; | ||
|  | 			if (f != null) { | ||
|  | 				for (i in f) { | ||
|  | 					if (i == cls) { | ||
|  | 						return true; | ||
|  | 					} else { | ||
|  | 						var l = loop(i); | ||
|  | 						if (l) { | ||
|  | 							return true; | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 				return false; | ||
|  | 			} else { | ||
|  | 				return false; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var currentClass = Syntax.field(value, "__class__"); | ||
|  | 		var result = false; | ||
|  | 		while (currentClass != null) { | ||
|  | 			if (loop(currentClass)) { | ||
|  | 				result = true; | ||
|  | 				break; | ||
|  | 			} | ||
|  | 			currentClass = getSuperClass(currentClass); | ||
|  | 		} | ||
|  | 		return result; | ||
|  | 	} | ||
|  | } |