247 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			247 lines
		
	
	
		
			7.4 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 js; | ||
|  | 
 | ||
|  | import js.Syntax; // import it here so it's always available in the compiler | ||
|  | 
 | ||
|  | @:dox(hide) | ||
|  | class Boot { | ||
|  | 	static inline function isClass(o:Dynamic):Bool { | ||
|  | 		return untyped __define_feature__("js.Boot.isClass", o.__name__); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isInterface(o:Class<Dynamic>):Bool { | ||
|  | 		return untyped __define_feature__("js.Boot.isInterface", o.__isInterface__); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static inline function isEnum(e:Dynamic):Bool { | ||
|  | 		return untyped __define_feature__("js.Boot.isEnum", e.__ename__); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:pure static function getClass(o:Null<Dynamic>):Null<Dynamic> { | ||
|  | 		if (o == null) { | ||
|  | 			return null; | ||
|  | 		} else if (Std.isOfType(o, Array)) { | ||
|  | 			return Array; | ||
|  | 		} else { | ||
|  | 			var cl = untyped __define_feature__("js.Boot.getClass", o.__class__); | ||
|  | 			if (cl != null) | ||
|  | 				return cl; | ||
|  | 			var name = __nativeClassName(o); | ||
|  | 			if (name != null) | ||
|  | 				return __resolveNativeClass(name); | ||
|  | 			return null; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:ifFeature("has_enum") | ||
|  | 	private static function __string_rec(o, s:String) { | ||
|  | 		untyped { | ||
|  | 			if (o == null) | ||
|  | 				return "null"; | ||
|  | 			if (s.length >= 5) | ||
|  | 				return "<...>"; // too much deep recursion | ||
|  | 			var t = js.Syntax.typeof(o); | ||
|  | 			if (t == "function" && (isClass(o) || isEnum(o))) | ||
|  | 				t = "object"; | ||
|  | 			switch (t) { | ||
|  | 				case "object": | ||
|  | 					#if !js_enums_as_arrays | ||
|  | 					__feature__("has_enum", if (o.__enum__) { | ||
|  | 						var e = $hxEnums[o.__enum__]; | ||
|  | 						var con = e.__constructs__[o._hx_index]; | ||
|  | 						var n = con._hx_name; | ||
|  | 						if (con.__params__) { | ||
|  | 							s += "\t"; | ||
|  | 							return n + "(" + [for (p in (con.__params__ : Array<String>)) __string_rec(o[p], s)].join(",") + ")"; | ||
|  | 						} else { | ||
|  | 							return n; | ||
|  | 						} | ||
|  | 					}); | ||
|  | 					#end | ||
|  | 					if (js.Syntax.instanceof(o, Array)) { | ||
|  | 						#if js_enums_as_arrays | ||
|  | 						__feature__("has_enum", if (o.__enum__) { | ||
|  | 							if (o.length == 2) | ||
|  | 								return o[0]; | ||
|  | 							var str = o[0] + "("; | ||
|  | 							s += "\t"; | ||
|  | 							for (i in 2...o.length) { | ||
|  | 								if (i != 2) | ||
|  | 									str += "," + __string_rec(o[i], s); | ||
|  | 								else | ||
|  | 									str += __string_rec(o[i], s); | ||
|  | 							} | ||
|  | 							return str + ")"; | ||
|  | 						}); | ||
|  | 						#end | ||
|  | 						var str = "["; | ||
|  | 						s += "\t"; | ||
|  | 						for (i in 0...o.length) | ||
|  | 							str += (if (i > 0) "," else "") + __string_rec(o[i], s); | ||
|  | 						str += "]"; | ||
|  | 						return str; | ||
|  | 					} | ||
|  | 					var tostr; | ||
|  | 					try { | ||
|  | 						tostr = untyped o.toString; | ||
|  | 					} catch (e:Dynamic) { | ||
|  | 						// strange error on IE | ||
|  | 						return "???"; | ||
|  | 					} | ||
|  | 					if (tostr != null && tostr != js.Syntax.code("Object.toString") && js.Syntax.typeof(tostr) == "function") { | ||
|  | 						var s2 = o.toString(); | ||
|  | 						if (s2 != "[object Object]") | ||
|  | 							return s2; | ||
|  | 					} | ||
|  | 					var str = "{\n"; | ||
|  | 					s += "\t"; | ||
|  | 					var hasp = (o.hasOwnProperty != null); | ||
|  | 					var k:String = null; | ||
|  | 					js.Syntax.code("for( {0} in {1} ) {", k, o); | ||
|  | 					if (hasp && !o.hasOwnProperty(k)) | ||
|  | 						js.Syntax.code("continue"); | ||
|  | 					if (k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") | ||
|  | 						js.Syntax.code("continue"); | ||
|  | 					if (str.length != 2) | ||
|  | 						str += ", \n"; | ||
|  | 					str += s + k + " : " + __string_rec(o[k], s); | ||
|  | 					js.Syntax.code("}"); | ||
|  | 					s = s.substring(1); | ||
|  | 					str += "\n" + s + "}"; | ||
|  | 					return str; | ||
|  | 				case "function": | ||
|  | 					return "<function>"; | ||
|  | 				case "string": | ||
|  | 					return o; | ||
|  | 				default: | ||
|  | 					return String(o); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:pure private static function __interfLoop(cc:Dynamic, cl:Dynamic) { | ||
|  | 		if (cc == null) | ||
|  | 			return false; | ||
|  | 		if (cc == cl) | ||
|  | 			return true; | ||
|  | 		var intf:Dynamic = cc.__interfaces__; | ||
|  | 		if (intf != null | ||
|  | 			// ES6 classes inherit statics, so we want to avoid accessing inherited `__interfaces__` | ||
|  | 			#if (js_es >= 6) && (cc.__super__ == null || cc.__super__.__interfaces__ != intf) #end | ||
|  | 		) { | ||
|  | 			for (i in 0...intf.length) { | ||
|  | 				var i:Dynamic = intf[i]; | ||
|  | 				if (i == cl || __interfLoop(i, cl)) | ||
|  | 					return true; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return __interfLoop(cc.__super__, cl); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:pure private static function __instanceof(o:Dynamic, cl:Dynamic) { | ||
|  | 		if (cl == null) | ||
|  | 			return false; | ||
|  | 		switch (cl) { | ||
|  | 			case Int: | ||
|  | 				return js.Syntax.typeof(o) == "number" && js.Syntax.strictEq(o | 0, o); | ||
|  | 			case Float: | ||
|  | 				return js.Syntax.typeof(o) == "number"; | ||
|  | 			case Bool: | ||
|  | 				return js.Syntax.typeof(o) == "boolean"; | ||
|  | 			case String: | ||
|  | 				return js.Syntax.typeof(o) == "string"; | ||
|  | 			case Array: | ||
|  | 				return js.Syntax.instanceof(o, Array) #if js_enums_as_arrays && o.__enum__ == null #end; | ||
|  | 			case Dynamic: | ||
|  | 				return o != null; | ||
|  | 			default: | ||
|  | 				if (o != null) { | ||
|  | 					// Check if o is an instance of a Haxe class or a native JS object | ||
|  | 					if (js.Syntax.typeof(cl) == "function") { | ||
|  | 						if (__downcastCheck(o, cl)) | ||
|  | 							return true; | ||
|  | 					} else if (js.Syntax.typeof(cl) == "object" && __isNativeObj(cl)) { | ||
|  | 						if (js.Syntax.instanceof(o, cl)) | ||
|  | 							return true; | ||
|  | 					} | ||
|  | 				} else { | ||
|  | 					return false; | ||
|  | 				} | ||
|  | 				// do not use isClass/isEnum here | ||
|  | 				untyped __feature__("Class.*", if (cl == Class && o.__name__ != null) return true); | ||
|  | 				untyped __feature__("Enum.*", if (cl == Enum && o.__ename__ != null) return true); | ||
|  | 				#if js_enums_as_arrays | ||
|  | 				return o.__enum__ == cl; | ||
|  | 				#else | ||
|  | 				return untyped __feature__( | ||
|  | 					"has_enum", | ||
|  | 					if (o.__enum__ != null) ($hxEnums[o.__enum__]) == cl else false, | ||
|  | 					false | ||
|  | 				); | ||
|  | 				#end | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function __downcastCheck(o:Dynamic, cl:Class<Dynamic>):Bool { | ||
|  | 		return js.Syntax.instanceof(o, cl) || (isInterface(cl) && inline __implements(o, cl)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static function __implements(o:Dynamic, iface:Class<Dynamic>):Bool { | ||
|  | 		return __interfLoop(getClass(o), iface); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	@:ifFeature("typed_cast") private static function __cast(o:Dynamic, t:Dynamic) { | ||
|  | 		if (o == null || __instanceof(o, t)) | ||
|  | 			return o; | ||
|  | 		else | ||
|  | 			throw "Cannot cast " + Std.string(o) + " to " + Std.string(t); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static var __toStr:js.lib.Function; | ||
|  | 
 | ||
|  | 	static function __init__() { | ||
|  | 		Boot.__toStr = (cast {}).toString; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// get native JS [[Class]] | ||
|  | 	static function __nativeClassName(o:Dynamic):String { | ||
|  | 		var name:String = __toStr.call(o).slice(8, -1); | ||
|  | 		// exclude general Object and Function | ||
|  | 		// also exclude Math and JSON, because instanceof cannot be called on them | ||
|  | 		if (name == "Object" || name == "Function" || name == "Math" || name == "JSON") | ||
|  | 			return null; | ||
|  | 		return name; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// check for usable native JS object | ||
|  | 	static function __isNativeObj(o:Dynamic):Bool { | ||
|  | 		return __nativeClassName(o) != null; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// resolve native JS class in the global scope: | ||
|  | 	static function __resolveNativeClass(name:String) { | ||
|  | 		return js.Lib.global[cast name]; | ||
|  | 	} | ||
|  | } |