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;
 | |
| }
 |