580 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			580 lines
		
	
	
		
			17 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.Expr; | ||
|  | 
 | ||
|  | /** | ||
|  | 	All these methods can be called for compiler configuration macros. | ||
|  | **/ | ||
|  | #if hl | ||
|  | @:hlNative("macro") | ||
|  | #end | ||
|  | class Compiler { | ||
|  | 	/** | ||
|  | 		A conditional compilation flag can be set on the command line using | ||
|  | 		`-D key=value`. | ||
|  | 
 | ||
|  | 		Returns the value of a compiler flag. | ||
|  | 
 | ||
|  | 		If the compiler flag is defined but no value is set, | ||
|  | 		`Compiler.getDefine` returns `"1"` (e.g. `-D key`). | ||
|  | 
 | ||
|  | 		If the compiler flag is not defined, `Compiler.getDefine` returns | ||
|  | 		`null`. | ||
|  | 
 | ||
|  | 		Note: This is a macro and cannot be called from within other macros. Refer | ||
|  | 		to `haxe.macro.Context.definedValue` to obtain defined values in macro context. | ||
|  | 
 | ||
|  | 		@see https://haxe.org/manual/lf-condition-compilation.html | ||
|  | 	**/ | ||
|  | 	macro /* <-- ! */ static public function getDefine(key:String) { | ||
|  | 		return macro $v{haxe.macro.Context.definedValue(key)}; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	#if (neko || (macro && hl) || (macro && eval)) | ||
|  | 	static var ident = ~/^[A-Za-z_][A-Za-z0-9_]*$/; | ||
|  | 	static var path = ~/^[A-Za-z_][A-Za-z0-9_.]*$/; | ||
|  | 
 | ||
|  | 	public static function allowPackage(v:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("allow_package", 1)(v); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Set a conditional compiler flag. | ||
|  | 
 | ||
|  | 		Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. | ||
|  | 	**/ | ||
|  | 	public static function define(flag:String, ?value:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("define", 2)(flag, value); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	#if (!neko && !eval) | ||
|  | 	private static function typePatch(cl:String, f:String, stat:Bool, t:String) {} | ||
|  | 
 | ||
|  | 	private static function metaPatch(meta:String, cl:String, f:String, stat:Bool) {} | ||
|  | 
 | ||
|  | 	private static function addGlobalMetadataImpl(pathFilter:String, meta:String, recursive:Bool, toTypes:Bool, toFields:Bool) {} | ||
|  | 	#end | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Removes a (static) field from a given class by name. | ||
|  | 		An error is thrown when `className` or `field` is invalid. | ||
|  | 	**/ | ||
|  | 	public static function removeField(className:String, field:String, ?isStatic:Bool) { | ||
|  | 		if (!path.match(className)) | ||
|  | 			throw "Invalid " + className; | ||
|  | 		if (!ident.match(field)) | ||
|  | 			throw "Invalid " + field; | ||
|  | 		#if (neko || eval) | ||
|  | 		load("type_patch", 4)(className, field, isStatic == true, null); | ||
|  | 		#else | ||
|  | 		typePatch(className, field, isStatic == true, null); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Set the type of a (static) field at a given class by name. | ||
|  | 		An error is thrown when `className` or `field` is invalid. | ||
|  | 	**/ | ||
|  | 	public static function setFieldType(className:String, field:String, type:String, ?isStatic:Bool) { | ||
|  | 		if (!path.match(className)) | ||
|  | 			throw "Invalid " + className; | ||
|  | 		if (!ident.match((field.charAt(0) == "$") ? field.substr(1) : field)) | ||
|  | 			throw "Invalid " + field; | ||
|  | 		#if (neko || eval) | ||
|  | 		load("type_patch", 4)(className, field, isStatic == true, type); | ||
|  | 		#else | ||
|  | 		typePatch(className, field, isStatic == true, type); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Add metadata to a (static) field or class by name. | ||
|  | 		An error is thrown when `className` or `field` is invalid. | ||
|  | 	**/ | ||
|  | 	public static function addMetadata(meta:String, className:String, ?field:String, ?isStatic:Bool) { | ||
|  | 		if (!path.match(className)) | ||
|  | 			throw "Invalid " + className; | ||
|  | 		if (field != null && !ident.match(field)) | ||
|  | 			throw "Invalid " + field; | ||
|  | 		#if (neko || eval) | ||
|  | 		load("meta_patch", 4)(meta, className, field, isStatic == true); | ||
|  | 		#else | ||
|  | 		metaPatch(meta, className, field, isStatic == true); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Add a class path where ".hx" source files or packages (sub-directories) can be found. | ||
|  | 
 | ||
|  | 		Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. | ||
|  | 	**/ | ||
|  | 	public static function addClassPath(path:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("add_class_path", 1)(path); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function getOutput():String { | ||
|  | 		#if (neko || eval) | ||
|  | 		return load("get_output", 0)(); | ||
|  | 		#else | ||
|  | 		return null; | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function setOutput(fileOrDir:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("set_output", 1)(fileOrDir); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	public static function getDisplayPos():Null<{file:String, pos:Int}> { | ||
|  | 		#if (neko || eval) | ||
|  | 		return load("get_display_pos", 0)(); | ||
|  | 		#else | ||
|  | 		return null; | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Adds a native library depending on the platform (e.g. `-swf-lib` for Flash). | ||
|  | 
 | ||
|  | 		Usage of this function outside of initialization macros is deprecated and may cause compilation server issues. | ||
|  | 	**/ | ||
|  | 	public static function addNativeLib(name:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("add_native_lib", 1)(name); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Adds an argument to be passed to the native compiler (e.g. `-javac-arg` for Java). | ||
|  | 	**/ | ||
|  | 	public static function addNativeArg(argument:String) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("add_native_arg", 1)(argument); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Includes all modules in package `pack` in the compilation. | ||
|  | 
 | ||
|  | 		In order to include single modules, their paths can be listed directly | ||
|  | 		on command line: `haxe ... ModuleName pack.ModuleName`. | ||
|  | 
 | ||
|  | 		By default `Compiler.include` will search for modules in the directories defined with `-cp`. | ||
|  | 		If you want to specify a different set of paths to search for modules, you can use the optional | ||
|  | 		argument `classPath`. | ||
|  | 
 | ||
|  | 		@param pack The package dot-path as String. Use `''` to include the root package. | ||
|  | 		@param rec If true, recursively adds all sub-packages. | ||
|  | 		@param ignore Array of module names to ignore for inclusion. | ||
|  | 			   You can use `module*` with a * at the end for Wildcard matching | ||
|  | 		@param classPaths An alternative array of paths (directory names) to use to search for modules to include. | ||
|  | 			   Note that if you pass this argument, only the specified paths will be used for inclusion. | ||
|  | 		@param strict If true and given package wasn't found in any of class paths, fail with an error. | ||
|  | 	**/ | ||
|  | 	public static function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) { | ||
|  | 		var ignoreWildcard:Array<String> = []; | ||
|  | 		var ignoreString:Array<String> = []; | ||
|  | 		if (ignore != null) { | ||
|  | 			for (ignoreRule in ignore) { | ||
|  | 				if (StringTools.endsWith(ignoreRule, "*")) { | ||
|  | 					ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1)); | ||
|  | 				} else { | ||
|  | 					ignoreString.push(ignoreRule); | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var skip = if (ignore == null) { | ||
|  | 			function(c) return false; | ||
|  | 		} else { | ||
|  | 			function(c:String) { | ||
|  | 				if (Lambda.has(ignoreString, c)) | ||
|  | 					return true; | ||
|  | 				for (ignoreRule in ignoreWildcard) | ||
|  | 					if (StringTools.startsWith(c, ignoreRule)) | ||
|  | 						return true; | ||
|  | 				return false; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var displayValue = Context.definedValue("display"); | ||
|  | 		if (classPaths == null) { | ||
|  | 			classPaths = Context.getClassPath(); | ||
|  | 			// do not force inclusion when using completion | ||
|  | 			switch (displayValue) { | ||
|  | 				case null: | ||
|  | 				case "usage": | ||
|  | 				case _: | ||
|  | 					return; | ||
|  | 			} | ||
|  | 			// normalize class path | ||
|  | 			for (i in 0...classPaths.length) { | ||
|  | 				var cp = StringTools.replace(classPaths[i], "\\", "/"); | ||
|  | 				if (StringTools.endsWith(cp, "/")) | ||
|  | 					cp = cp.substr(0, -1); | ||
|  | 				if (cp == "") | ||
|  | 					cp = "."; | ||
|  | 				classPaths[i] = cp; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		var prefix = pack == '' ? '' : pack + '.'; | ||
|  | 		var found = false; | ||
|  | 		for (cp in classPaths) { | ||
|  | 			var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/"); | ||
|  | 			if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path)) | ||
|  | 				continue; | ||
|  | 			found = true; | ||
|  | 			for (file in sys.FileSystem.readDirectory(path)) { | ||
|  | 				if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) { | ||
|  | 					if( file == "import.hx" ) continue; | ||
|  | 					var cl = prefix + file.substr(0, file.length - 3); | ||
|  | 					if (skip(cl)) | ||
|  | 						continue; | ||
|  | 					Context.getModule(cl); | ||
|  | 				} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file)) | ||
|  | 					include(prefix + file, true, ignore, classPaths); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if (strict && !found) | ||
|  | 			Context.error('Package "$pack" was not found in any of class paths', Context.currentPos()); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Exclude a class or an enum without changing it to `@:nativeGen`. | ||
|  | 	**/ | ||
|  | 	static function excludeBaseType(baseType:Type.BaseType):Void { | ||
|  | 		if (!baseType.isExtern) { | ||
|  | 			var meta = baseType.meta; | ||
|  | 			if (!meta.has(":nativeGen")) { | ||
|  | 				meta.add(":hxGen", [], baseType.pos); | ||
|  | 			} | ||
|  | 			baseType.exclude(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Exclude a specific class, enum, or all classes and enums in a | ||
|  | 		package from being generated. Excluded types become `extern`. | ||
|  | 
 | ||
|  | 		@param pack The package dot-path as String. Use `''` to exclude the root package. | ||
|  | 		@param rec If true, recursively excludes all sub-packages. | ||
|  | 	**/ | ||
|  | 	public static function exclude(pack:String, ?rec = true) { | ||
|  | 		Context.onGenerate(function(types) { | ||
|  | 			for (t in types) { | ||
|  | 				var b:Type.BaseType, name; | ||
|  | 				switch (t) { | ||
|  | 					case TInst(c, _): | ||
|  | 						name = c.toString(); | ||
|  | 						b = c.get(); | ||
|  | 					case TEnum(e, _): | ||
|  | 						name = e.toString(); | ||
|  | 						b = e.get(); | ||
|  | 					default: | ||
|  | 						continue; | ||
|  | 				} | ||
|  | 				var p = b.pack.join("."); | ||
|  | 				if ((p == pack || name == pack) || (rec && StringTools.startsWith(p, pack + "."))) | ||
|  | 					excludeBaseType(b); | ||
|  | 			} | ||
|  | 		}, false); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Exclude classes and enums listed in an extern file (one per line) from being generated. | ||
|  | 	**/ | ||
|  | 	public static function excludeFile(fileName:String) { | ||
|  | 		fileName = Context.resolvePath(fileName); | ||
|  | 		var f = sys.io.File.read(fileName, true); | ||
|  | 		var classes = new haxe.ds.StringMap(); | ||
|  | 		try { | ||
|  | 			while (true) { | ||
|  | 				var l = StringTools.trim(f.readLine()); | ||
|  | 				if (l == "" || !~/[A-Za-z0-9._]/.match(l)) | ||
|  | 					continue; | ||
|  | 				classes.set(l, true); | ||
|  | 			} | ||
|  | 		} catch (e:haxe.io.Eof) {} | ||
|  | 		Context.onGenerate(function(types) { | ||
|  | 			for (t in types) { | ||
|  | 				switch (t) { | ||
|  | 					case TInst(c, _): | ||
|  | 						if (classes.exists(c.toString())) | ||
|  | 							excludeBaseType(c.get()); | ||
|  | 					case TEnum(e, _): | ||
|  | 						if (classes.exists(e.toString())) | ||
|  | 							excludeBaseType(e.get()); | ||
|  | 					default: | ||
|  | 				} | ||
|  | 			} | ||
|  | 		}); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Load a type patch file that can modify the field types within declared classes and enums. | ||
|  | 	**/ | ||
|  | 	public static function patchTypes(file:String):Void { | ||
|  | 		var file = Context.resolvePath(file); | ||
|  | 		var f = sys.io.File.read(file, true); | ||
|  | 		try { | ||
|  | 			while (true) { | ||
|  | 				var r = StringTools.trim(f.readLine()); | ||
|  | 				if (r == "" || r.substr(0, 2) == "//") | ||
|  | 					continue; | ||
|  | 				if (StringTools.endsWith(r, ";")) | ||
|  | 					r = r.substr(0, -1); | ||
|  | 				if (r.charAt(0) == "-") { | ||
|  | 					r = r.substr(1); | ||
|  | 					var isStatic = StringTools.startsWith(r, "static "); | ||
|  | 					if (isStatic) | ||
|  | 						r = r.substr(7); | ||
|  | 					var p = r.split("."); | ||
|  | 					var field = p.pop(); | ||
|  | 					removeField(p.join("."), field, isStatic); | ||
|  | 					continue; | ||
|  | 				} | ||
|  | 				if (r.charAt(0) == "@") { | ||
|  | 					var rp = r.split(" "); | ||
|  | 					var type = rp.pop(); | ||
|  | 					var isStatic = rp[rp.length - 1] == "static"; | ||
|  | 					if (isStatic) | ||
|  | 						rp.pop(); | ||
|  | 					var meta = rp.join(" "); | ||
|  | 					var p = type.split("."); | ||
|  | 					var field = if (p.length > 1 && p[p.length - 2].charAt(0) >= "a") null else p.pop(); | ||
|  | 					addMetadata(meta, p.join("."), field, isStatic); | ||
|  | 					continue; | ||
|  | 				} | ||
|  | 				if (StringTools.startsWith(r, "enum ")) { | ||
|  | 					define("enumAbstract:" + r.substr(5)); | ||
|  | 					continue; | ||
|  | 				} | ||
|  | 				var rp = r.split(" : "); | ||
|  | 				if (rp.length > 1) { | ||
|  | 					r = rp.shift(); | ||
|  | 					var isStatic = StringTools.startsWith(r, "static "); | ||
|  | 					if (isStatic) | ||
|  | 						r = r.substr(7); | ||
|  | 					var p = r.split("."); | ||
|  | 					var field = p.pop(); | ||
|  | 					setFieldType(p.join("."), field, rp.join(" : "), isStatic); | ||
|  | 					continue; | ||
|  | 				} | ||
|  | 				throw "Invalid type patch " + r; | ||
|  | 			} | ||
|  | 		} catch (e:haxe.io.Eof) {} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Marks types or packages to be kept by DCE. | ||
|  | 
 | ||
|  | 		This also extends to the sub-types of resolved modules. | ||
|  | 
 | ||
|  | 		In order to include module sub-types directly, their full dot path | ||
|  | 		including the containing module has to be used | ||
|  | 		(e.g. `msignal.Signal.Signal0`). | ||
|  | 
 | ||
|  | 		This operation has no effect if the type has already been loaded, e.g. | ||
|  | 		through `Context.getType`. | ||
|  | 
 | ||
|  | 		@param path A package, module or sub-type dot path to keep. | ||
|  | 		@param paths An Array of package, module or sub-type dot paths to keep. | ||
|  | 		@param recursive If true, recurses into sub-packages for package paths. | ||
|  | 	**/ | ||
|  | 	public static function keep(?path:String, ?paths:Array<String>, ?recursive:Bool = true) { | ||
|  | 		if (null == paths) | ||
|  | 			paths = []; | ||
|  | 		if (null != path) | ||
|  | 			paths.push(path); | ||
|  | 		for (path in paths) { | ||
|  | 			addGlobalMetadata(path, "@:keep", recursive, true, true); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Enables null safety for a type or a package. | ||
|  | 
 | ||
|  | 		@param path A package, module or sub-type dot path to enable null safety for. | ||
|  | 		@param recursive If true, recurses into sub-packages for package paths. | ||
|  | 	**/ | ||
|  | 	public static function nullSafety(path:String, mode:NullSafetyMode = Loose, recursive:Bool = true) { | ||
|  | 		addGlobalMetadata(path, '@:nullSafety($mode)', recursive); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Adds metadata `meta` to all types (if `toTypes = true`) or fields (if | ||
|  | 		`toFields = true`) whose dot-path matches `pathFilter`. | ||
|  | 
 | ||
|  | 		If `recursive` is true a dot-path is considered matched if it starts | ||
|  | 		with `pathFilter`. This automatically applies to path filters of | ||
|  | 		packages. Otherwise an exact match is required. | ||
|  | 
 | ||
|  | 		If `pathFilter` is the empty String `""` it matches everything (if | ||
|  | 		`recursive = true`) or only top-level types (if `recursive = false`). | ||
|  | 
 | ||
|  | 		This operation has no effect if the type has already been loaded, e.g. | ||
|  | 		through `Context.getType`. | ||
|  | 	**/ | ||
|  | 	public static function addGlobalMetadata(pathFilter:String, meta:String, ?recursive:Bool = true, ?toTypes:Bool = true, ?toFields:Bool = false) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("add_global_metadata_impl", 5)(pathFilter, meta, recursive, toTypes, toFields); | ||
|  | 		#else | ||
|  | 		addGlobalMetadataImpl(pathFilter, meta, recursive, toTypes, toFields); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Change the default JS output by using a custom generator callback | ||
|  | 	**/ | ||
|  | 	public static function setCustomJSGenerator(callb:JSGenApi->Void) { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("set_custom_js_generator", 1)(callb); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	#if (neko || eval) | ||
|  | 	static inline function load(f, nargs):Dynamic { | ||
|  | 		return @:privateAccess Context.load(f, nargs); | ||
|  | 	} | ||
|  | 	#end | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Clears cached results of file lookups | ||
|  | 	**/ | ||
|  | 	public static function flushDiskCache() { | ||
|  | 		#if (neko || eval) | ||
|  | 		load("flush_disk_cache", 0)(); | ||
|  | 		#end | ||
|  | 	} | ||
|  | 
 | ||
|  | 	#end | ||
|  | 
 | ||
|  | 	#if (js || lua || macro) | ||
|  | 	/** | ||
|  | 		Embed a JavaScript or Lua file at compile time (can be called by `--macro` or within an `__init__` method). | ||
|  | 	**/ | ||
|  | 	public static #if !macro macro #end function includeFile(file:String, position:IncludePosition = Top) { | ||
|  | 		return switch ((position : String).toLowerCase()) { | ||
|  | 			case Inline: | ||
|  | 				if (Context.getLocalModule() == "") | ||
|  | 					Context.error("Cannot use inline mode when includeFile is called by `--macro`", Context.currentPos()); | ||
|  | 
 | ||
|  | 				var f = try sys.io.File.getContent(Context.resolvePath(file)) catch (e:Dynamic) Context.error(Std.string(e), Context.currentPos()); | ||
|  | 				var p = Context.currentPos(); | ||
|  | 				if(Context.defined("js")) { | ||
|  | 					macro @:pos(p) js.Syntax.plainCode($v{f}); | ||
|  | 				} else { | ||
|  | 					macro @:pos(p) untyped __lua__($v{f}); | ||
|  | 				} | ||
|  | 			case Top | Closure: | ||
|  | 				@:privateAccess Context.includeFile(file, position); | ||
|  | 				macro {}; | ||
|  | 			case _: | ||
|  | 				Context.error("unknown includeFile position: " + position, Context.currentPos()); | ||
|  | 		} | ||
|  | 	} | ||
|  | 	#end | ||
|  | } | ||
|  | 
 | ||
|  | enum abstract IncludePosition(String) from String to String { | ||
|  | 	/** | ||
|  | 		Prepend the file content to the output file. | ||
|  | 	**/ | ||
|  | 	var Top = "top"; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Prepend the file content to the body of the top-level closure. | ||
|  | 
 | ||
|  | 		Since the closure is in strict-mode, there may be run-time error if the input is not strict-mode-compatible. | ||
|  | 	**/ | ||
|  | 	var Closure = "closure"; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Directly inject the file content at the call site. | ||
|  | 	**/ | ||
|  | 	var Inline = "inline"; | ||
|  | } | ||
|  | 
 | ||
|  | enum abstract NullSafetyMode(String) to String { | ||
|  | 	/** | ||
|  | 		Disable null safety. | ||
|  | 	**/ | ||
|  | 	var Off; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Loose safety. | ||
|  | 		If an expression is checked `!= null`, then it's considered safe even if it could be modified after the check.
 | ||
|  | 		E.g. | ||
|  | 		```haxe | ||
|  | 		function example(o:{field:Null<String>}) { | ||
|  | 			if(o.field != null) { | ||
|  | 				mutate(o); | ||
|  | 				var notNullable:String = o.field; //no error | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		function mutate(o:{field:Null<String>}) { | ||
|  | 			o.field = null; | ||
|  | 		} | ||
|  | 		``` | ||
|  | 	**/ | ||
|  | 	var Loose; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Full scale null safety. | ||
|  | 		If a field is checked `!= null` it stays safe until a call is made or any field of any object is reassigned, | ||
|  | 		because that could potentially alter an object of the checked field. | ||
|  | 		E.g. | ||
|  | 		```haxe | ||
|  | 		function example(o:{field:Null<String>}, b:{o:{field:Null<String>}}) { | ||
|  | 			if(o.field != null) { | ||
|  | 				var notNullable:String = o.field; //no error | ||
|  | 				someCall(); | ||
|  | 				var notNullable:String = o.field; // Error! | ||
|  | 			} | ||
|  | 			if(o.field != null) { | ||
|  | 				var notNullable:String = o.field; //no error | ||
|  | 				b.o = {field:null}; | ||
|  | 				var notNullable:String = o.field; // Error! | ||
|  | 			} | ||
|  | 		} | ||
|  | 		``` | ||
|  | 	**/ | ||
|  | 	var Strict; | ||
|  | 
 | ||
|  | 	/** | ||
|  | 		Full scale null safety for a multi-threaded environment. | ||
|  | 		With this mode checking a field `!= null` does not make it safe, because it could be changed from another thread | ||
|  | 		at the same time or immediately after the check. | ||
|  | 		The only nullable thing could be safe are local variables. | ||
|  | 	**/ | ||
|  | 	var StrictThreaded; | ||
|  | } |