Procházet zdrojové kódy

Merge branch 'development' into out_of_curiosity

# Conflicts:
#	src/codegen/gencommon/gencommon.ml
#	src/context/nativeLibraryHandler.ml
#	src/generators/gencs.ml
#	src/generators/genjava.ml
Simon Krajewski před 1 rokem
rodič
revize
b6fdffdea7
65 změnil soubory, kde provedl 790 přidání a 204 odebrání
  1. 1 1
      haxe.opam
  2. 5 0
      src-json/warning.json
  3. 2 21
      src/codegen/codegen.ml
  4. 1 9
      src/codegen/overloads.ml
  5. 8 11
      src/compiler/args.ml
  6. 16 1
      src/compiler/compilationContext.ml
  7. 1 1
      src/compiler/compiler.ml
  8. 6 2
      src/compiler/server.ml
  9. 4 5
      src/context/common.ml
  10. 2 2
      src/context/display/displayPath.ml
  11. 7 5
      src/context/nativeLibraryHandler.ml
  12. 8 1
      src/context/typecore.ml
  13. 2 0
      src/core/error.ml
  14. 10 1
      src/core/stringHelper.ml
  15. 1 1
      src/core/tFunctions.ml
  16. 15 0
      src/core/texpr.ml
  17. 1 1
      src/filters/ES6Ctors.ml
  18. 1 1
      src/filters/exceptions.ml
  19. 14 2
      src/filters/filters.ml
  20. 37 14
      src/filters/localStatic.ml
  21. 1 0
      src/generators/gencpp.ml
  22. 2 4
      src/generators/genhl.ml
  23. 1 0
      src/generators/genjs.ml
  24. 1 1
      src/generators/genjvm.ml
  25. 3 3
      src/generators/genneko.ml
  26. 1 1
      src/generators/genphp7.ml
  27. 2 1
      src/generators/genpy.ml
  28. 1 0
      src/generators/genswf.ml
  29. 1 0
      src/generators/genswf9.ml
  30. 1 1
      src/generators/hlinterp.ml
  31. 8 5
      src/generators/hlopt.ml
  32. 18 12
      src/generators/jvm/jvmFunctions.ml
  33. 8 10
      src/macro/macroApi.ml
  34. 2 3
      src/optimization/analyzerTexpr.ml
  35. 1 1
      src/optimization/analyzerTypes.ml
  36. 1 1
      src/typing/callUnification.ml
  37. 19 9
      src/typing/calls.ml
  38. 1 5
      src/typing/operators.ml
  39. 2 2
      src/typing/typeloadCheck.ml
  40. 1 1
      src/typing/typeloadFields.ml
  41. 1 10
      src/typing/typer.ml
  42. 0 3
      src/typing/typerBase.ml
  43. 2 3
      src/typing/typerEntry.ml
  44. 0 9
      std/haxe/macro/Compiler.hx
  45. 32 0
      tests/misc/java/projects/Issue11390/Main.hx
  46. 9 0
      tests/misc/java/projects/Issue11390/Setup.hx
  47. 12 0
      tests/misc/java/projects/Issue11390/compile.hxml
  48. 10 0
      tests/misc/java/projects/Issue11390/compile.hxml.stdout
  49. 42 0
      tests/misc/java/projects/Issue11390/project/test/Robot.java
  50. 56 0
      tests/misc/java/projects/Issue11390/project/test/RobotFactory.java
  51. 4 0
      tests/server/src/TestCase.hx
  52. 149 0
      tests/server/src/cases/CsSafeTypeBuilding.hx
  53. 1 1
      tests/server/src/cases/ServerTests.hx
  54. 53 0
      tests/server/src/cases/issues/Issue11460.hx
  55. 1 1
      tests/server/src/cases/issues/Issue9358.hx
  56. 0 10
      tests/server/src/utils/Vfs.hx
  57. 97 28
      tests/server/src/utils/macro/TestBuilder.macro.hx
  58. 6 0
      tests/server/test/templates/csSafeTypeBuilding/Bar.hx
  59. 7 0
      tests/server/test/templates/csSafeTypeBuilding/Baz.hx
  60. 2 0
      tests/server/test/templates/csSafeTypeBuilding/Foo.hx
  61. 63 0
      tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx
  62. 9 0
      tests/server/test/templates/csSafeTypeBuilding/Main.hx
  63. 5 0
      tests/server/test/templates/issues/Issue11460/C.hx
  64. 3 0
      tests/server/test/templates/issues/Issue11460/Main.hx
  65. 9 0
      tests/unit/src/unit/issues/Issue11469.hx

+ 1 - 1
haxe.opam

@@ -20,7 +20,7 @@ install: [make "install" "INSTALL_DIR=%{prefix}%"]
 remove: [make "uninstall" "INSTALL_DIR=%{prefix}%"]
 depends: [
   ("ocaml" {>= "5.0"} & ("camlp5" {build}))
-    | ("ocaml" {>= "4.08" & < "5.0"} & ("camlp5" {build & = "8.00"}))
+    | ("ocaml" {>= "4.08" & < "5.0"} & ("camlp5" {build & = "8.00.03"}))
   "ocamlfind" {build}
   "dune" {>= "1.11"}
   "sedlex" {>= "2.0"}

+ 5 - 0
src-json/warning.json

@@ -98,6 +98,11 @@
 		"doc": "A type path is being used that is supposed to be reserved on the current target",
 		"parent": "WTyper"
 	},
+	{
+		"name": "WInlineOptimizedField",
+		"doc": "A cached field which was optimized might lead to different output when inlined",
+		"parent": "WTyper"
+	},
 	{
 		"name": "WPatternMatcher",
 		"doc": "Warnings related to the pattern matcher",

+ 2 - 21
src/codegen/codegen.ml

@@ -65,15 +65,6 @@ let add_property_field com c =
 		c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics;
 		c.cl_ordered_statics <- cf :: c.cl_ordered_statics
 
-let escape_res_name name allowed =
-	ExtString.String.replace_chars (fun chr ->
-		if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr = '_' || chr = '.' then
-			Char.escaped chr
-		else if List.mem chr allowed then
-			Char.escaped chr
-		else
-			"-x" ^ (string_of_int (Char.code chr))) name
-
 (* -------------------------------------------------------------------------- *)
 (* FIX OVERRIDES *)
 
@@ -384,7 +375,7 @@ module Dump = struct
 			| "pretty" -> dump_types com true
 			| "record" -> dump_record com
 			| "position" -> dump_position com
-			| _ -> dump_types com false 
+			| _ -> dump_types com false
 
 	let dump_dependencies ?(target_override=None) com =
 		let target_name = match target_override with
@@ -428,16 +419,7 @@ let default_cast ?(vtmp="$t") com e texpr t p =
 	let var = mk (TVar (vtmp,Some e)) api.tvoid p in
 	let vexpr = mk (TLocal vtmp) e.etype p in
 	let texpr = Texpr.Builder.make_typeexpr texpr p in
-	let std = (try List.find (fun t -> t_path t = ([],"Std")) com.types with Not_found -> die "" __LOC__) in
-	let fis = (try
-			let c = (match std with TClassDecl c -> c | _ -> die "" __LOC__) in
-			FStatic (c, PMap.find "isOfType" c.cl_statics)
-		with Not_found ->
-			die "" __LOC__
-	) in
-	let std = Texpr.Builder.make_typeexpr std p in
-	let is = mk (TField (std,fis)) (tfun [t_dynamic;t_dynamic] api.tbool) p in
-	let is = mk (TCall (is,[vexpr;texpr])) api.tbool p in
+	let is = Texpr.Builder.resolve_and_make_static_call com.std "isOfType" [vexpr;texpr] p in
 	let enull = Texpr.Builder.make_null vexpr.etype p in
 	let eop = Texpr.Builder.binop OpEq vexpr enull api.tbool p in
 	let echeck = Texpr.Builder.binop OpBoolOr is eop api.tbool p in
@@ -519,4 +501,3 @@ module ExtClass = struct
 		let e_assign = mk (TBinop(OpAssign,ef1,e)) e.etype p in
 		add_cl_init c e_assign
 end
-	

+ 1 - 9
src/codegen/overloads.ml

@@ -13,7 +13,7 @@ let same_overload_args ?(get_vmtype) t1 t2 f1 f2 =
 			| [],[] ->
 				true
 			| tp1 :: params1,tp2 :: params2 ->
-				let constraints_equal ttp1 ttp2 = 
+				let constraints_equal ttp1 ttp2 =
 					Ast.safe_for_all2 f_eq (get_constraints ttp2) (get_constraints ttp2)
 				in
 				tp1.ttp_name = tp2.ttp_name && constraints_equal tp1 tp2 && loop params1 params2
@@ -79,14 +79,6 @@ let collect_overloads map c i =
 	loop map c;
 	List.rev !acc
 
-let get_overloads (com : Common.context) c i =
-	try
-		com.overload_cache#find (c.cl_path,i)
-	with Not_found ->
-		let l = collect_overloads (fun t -> t) c i in
-		com.overload_cache#add (c.cl_path,i) l;
-		l
-
 (** Overload resolution **)
 module Resolution =
 struct

+ 8 - 11
src/compiler/args.ml

@@ -66,7 +66,10 @@ let parse_args com =
 	let add_deprecation s =
 		actx.deprecations <- s :: actx.deprecations
 	in
-	let add_native_lib file extern = actx.native_libs <- (file,extern) :: actx.native_libs in
+	let add_native_lib file extern kind =
+		let lib = create_native_lib file extern kind in
+		actx.native_libs <- lib :: actx.native_libs
+	in
 	let basic_args_spec = [
 		("Target",["--js"],["-js"],Arg.String (set_platform com Js),"<file>","generate JavaScript code into target file");
 		("Target",["--lua"],["-lua"],Arg.String (set_platform com Lua),"<file>","generate Lua code into target file");
@@ -202,26 +205,20 @@ let parse_args com =
 			Common.define com Define.FlashStrict
 		), "","more type strict flash API");
 		("Target-specific",["--swf-lib"],["-swf-lib"],Arg.String (fun file ->
-			add_native_lib file false;
+			add_native_lib file false SwfLib;
 		),"<file>","add the SWF library to the compiled SWF");
 		("Target-specific",[],["--neko-lib-path"],Arg.String (fun dir ->
 			com.neko_lib_paths <- dir :: com.neko_lib_paths
 		),"<directory>","add the neko library path");
 		("Target-specific",["--swf-lib-extern"],["-swf-lib-extern"],Arg.String (fun file ->
-			add_native_lib file true;
+			add_native_lib file true SwfLib;
 		),"<file>","use the SWF library for type checking");
 		("Target-specific",["--java-lib"],["-java-lib"],Arg.String (fun file ->
-			add_native_lib file false;
+			add_native_lib file false JavaLib;
 		),"<file>","add an external JAR or directory of JAR files");
 		("Target-specific",["--java-lib-extern"],[],Arg.String (fun file ->
-			add_native_lib file true;
+			add_native_lib file true JavaLib;
 		),"<file>","use an external JAR or directory of JAR files for type checking");
-		("Target-specific",["--net-lib"],["-net-lib"],Arg.String (fun file ->
-			add_native_lib file false;
-		),"<file>[@std]","add an external .NET DLL file");
-		("Target-specific",["--c-arg"],["-c-arg"],Arg.String (fun arg ->
-			com.c_args <- arg :: com.c_args
-		),"<arg>","pass option <arg> to the native Java/C# compiler");
 		("Compilation",["-r";"--resource"],["-resource"],Arg.String (fun res ->
 			let file, name = (match ExtString.String.nsplit res "@" with
 				| [file; name] -> file, name

+ 16 - 1
src/compiler/compilationContext.ml

@@ -7,6 +7,16 @@ type server_mode =
 	| SMListen of string
 	| SMConnect of string
 
+type native_lib_kind =
+	| JavaLib
+	| SwfLib
+
+type native_lib_arg = {
+	lib_file : string;
+	lib_kind : native_lib_kind;
+	lib_extern : bool;
+}
+
 type arg_context = {
 	mutable classes : Globals.path list;
 	mutable xml_out : string option;
@@ -20,7 +30,7 @@ type arg_context = {
 	mutable interp : bool;
 	mutable jvm_flag : bool;
 	mutable swf_version : bool;
-	mutable native_libs : (string * bool) list;
+	mutable native_libs : native_lib_arg list;
 	mutable raise_usage : unit -> unit;
 	mutable display_arg : string option;
 	mutable deprecations : string list;
@@ -73,3 +83,8 @@ let error_ext ctx (err : Error.error) =
 		error ~depth ~from_macro:err.err_from_macro ctx (Error.error_msg err.err_message) err.err_pos
 	) err
 
+let create_native_lib file extern kind = {
+	lib_file = file;
+	lib_extern = extern;
+	lib_kind = kind;
+}

+ 1 - 1
src/compiler/compiler.ml

@@ -169,7 +169,7 @@ module Setup = struct
 		Common.log com (Buffer.contents buffer);
 		com.callbacks#run com.error_ext com.callbacks#get_before_typer_create;
 		(* Native lib pass 1: Register *)
-		let fl = List.map (fun (file,extern) -> NativeLibraryHandler.add_native_lib com file extern) (List.rev native_libs) in
+		let fl = List.map (fun lib -> NativeLibraryHandler.add_native_lib com lib) (List.rev native_libs) in
 		(* Native lib pass 2: Initialize *)
 		List.iter (fun f -> f()) fl;
 		TyperEntry.create com macros

+ 6 - 2
src/compiler/server.ml

@@ -124,7 +124,7 @@ module Communication = struct
 	let create_pipe sctx write =
 		let rec self = {
 			write_out = (fun s ->
-				write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit s "\n") ^ "\n")
+				write ("\x01" ^ String.concat "\n\x01" (ExtString.String.nsplit s "\n") ^ "\n")
 			);
 			write_err = (fun s ->
 				write s
@@ -312,7 +312,11 @@ let check_module sctx ctx m p =
 		in
 		let check_dependencies () =
 			PMap.iter (fun _ (sign,mpath) ->
-				let m2 = (com.cs#get_context sign)#find_module mpath in
+				let m2 = try
+					(com.cs#get_context sign)#find_module mpath
+				with Not_found ->
+					die (Printf.sprintf "Could not find dependency %s of %s in the cache" (s_type_path mpath) (s_type_path m.m_path)) __LOC__;
+				in
 				match check m2 with
 				| None -> ()
 				| Some reason -> raise (Dirty (DependencyDirty(m2.m_path,reason)))

+ 4 - 5
src/context/common.ml

@@ -375,6 +375,7 @@ type context = {
 	mutable user_metas : (string, Meta.user_meta) Hashtbl.t;
 	mutable get_macros : unit -> context option;
 	(* typing state *)
+	mutable std : tclass;
 	mutable global_metadata : (string list * metadata_entry * (bool * bool * bool)) list;
 	shared : shared_context;
 	display_information : display_information;
@@ -405,7 +406,6 @@ type context = {
 	mutable native_libs : native_libraries;
 	mutable net_std : string list;
 	net_path_map : (path,string list * string list * string) Hashtbl.t;
-	mutable c_args : string list;
 	mutable js_gen : (unit -> unit) option;
 	(* misc *)
 	mutable basic : basic_types;
@@ -789,7 +789,6 @@ let create compilation_step cs version args display_mode =
 		net_std = [];
 		native_libs = create_native_libs();
 		net_path_map = Hashtbl.create 0;
-		c_args = [];
 		neko_lib_paths = [];
 		include_files = [];
 		js_gen = None;
@@ -818,6 +817,7 @@ let create compilation_step cs version args display_mode =
 			tnull = (fun _ -> die "Could use locate abstract Null<T> (was it redefined?)" __LOC__);
 			tarray = (fun _ -> die "Could not locate class Array<T> (was it redefined?)" __LOC__);
 		};
+		std = null_class;
 		file_lookup_cache = new hashtbl_lookup;
 		file_keys = new file_keys;
 		file_contents = [];
@@ -877,6 +877,7 @@ let clone com is_macro_context =
 		module_to_file = new hashtbl_lookup;
 		overload_cache = new hashtbl_lookup;
 		module_lut = new module_lut;
+		std = null_class;
 	}
 
 let file_time file = Extc.filetime file
@@ -1024,8 +1025,6 @@ let allow_package ctx s =
 	with Not_found ->
 		()
 
-let abort ?(depth = 0) msg p = raise (Error.Fatal_error (Error.make_error ~depth (Custom msg) p))
-
 let platform ctx p = ctx.platform = p
 
 let platform_name_macro com =
@@ -1179,7 +1178,7 @@ let to_utf8 str p =
 	let ccount = ref 0 in
 	UTF8.iter (fun c ->
 		let c = UCharExt.code c in
-		if (c >= 0xD800 && c <= 0xDFFF) || c >= 0x110000 then abort "Invalid unicode char" p;
+		if (c >= 0xD800 && c <= 0xDFFF) || c >= 0x110000 then Error.abort "Invalid unicode char" p;
 		incr ccount;
 		if c > 0x10000 then incr ccount;
 	) u8;

+ 2 - 2
src/context/display/displayPath.ml

@@ -82,7 +82,7 @@ module TypePathHandler = struct
 	let complete_type_path com p =
 		let packs, modules = read_type_path com p in
 		if packs = [] && modules = [] then
-			(abort ("No modules found in " ^ String.concat "." p) null_pos)
+			(Error.abort ("No modules found in " ^ String.concat "." p) null_pos)
 		else
 			let packs = List.map (fun n -> make_ci_package (p,n) []) packs in
 			let modules = List.map (fun n -> make_ci_module (p,n)) modules in
@@ -157,7 +157,7 @@ module TypePathHandler = struct
 			in
 			Some fields
 		with _ ->
-			abort ("Could not load module " ^ (s_type_path (p,c))) null_pos
+			Error.abort ("Could not load module " ^ (s_type_path (p,c))) null_pos
 end
 
 let resolve_position_by_path ctx path p =

+ 7 - 5
src/context/nativeLibraryHandler.ml

@@ -19,11 +19,15 @@
 
 open Globals
 open Common
+open CompilationContext
 
-let add_native_lib com file is_extern = match com.platform with
-	| Globals.Flash ->
+let add_native_lib com lib =
+	let file = lib.lib_file in
+	let is_extern = lib.lib_extern in
+	match lib.lib_kind with
+	| SwfLib ->
 		SwfLoader.add_swf_lib com file is_extern
-	| Globals.Jvm ->
+	| JavaLib ->
 		let add file =
 			let std = file = "lib/hxjava-std.jar" in
 			JavaModern.add_java_lib com file std is_extern
@@ -35,5 +39,3 @@ let add_native_lib com file is_extern = match com.platform with
 			) (Sys.readdir file))
 		else
 			add file
-	| pf ->
-		failwith (Printf.sprintf "Target %s does not support native libraries (trying to load %s)" (platform_name pf) file);

+ 8 - 1
src/context/typecore.ml

@@ -100,7 +100,6 @@ type typer_globals = {
 	retain_meta : bool;
 	mutable core_api : typer option;
 	mutable macros : ((unit -> unit) * typer) option;
-	mutable std : tclass;
 	mutable std_types : module_def;
 	type_patches : (path, (string * bool, type_patch) Hashtbl.t * type_patch) Hashtbl.t;
 	mutable module_check_policies : (string list * module_check_policy list * bool) list;
@@ -758,6 +757,14 @@ let create_deprecation_context ctx = {
 	curmod = ctx.m.curmod;
 }
 
+let get_overloads (com : Common.context) c i =
+	try
+		com.overload_cache#find (c.cl_path,i)
+	with Not_found ->
+		let l = Overloads.collect_overloads (fun t -> t) c i in
+		com.overload_cache#add (c.cl_path,i) l;
+		l
+
 (* -------------- debug functions to activate when debugging typer passes ------------------------------- *)
 
 (*

+ 2 - 0
src/core/error.ml

@@ -51,6 +51,8 @@ let rec recurse_error ?(depth = 0) cb err =
 exception Fatal_error of error
 exception Error of error
 
+let abort ?(depth = 0) msg p = raise (Fatal_error (make_error ~depth (Custom msg) p))
+
 let string_source t = match follow t with
 	| TInst(c,tl) -> PMap.foldi (fun s _ acc -> s :: acc) (TClass.get_all_fields c tl) []
 	| TAnon a -> PMap.fold (fun cf acc -> cf.cf_name :: acc) a.a_fields []

+ 10 - 1
src/core/stringHelper.ml

@@ -48,4 +48,13 @@ let s_escape ?(hex=true) s =
 		| c when int_of_char c < 32 && hex -> Buffer.add_string b (Printf.sprintf "\\x%.2X" (int_of_char c))
 		| c -> Buffer.add_char b c
 	done;
-	Buffer.contents b
+	Buffer.contents b
+
+let escape_res_name name allowed =
+	ExtString.String.replace_chars (fun chr ->
+		if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr = '_' || chr = '.' then
+			Char.escaped chr
+		else if List.mem chr allowed then
+			Char.escaped chr
+		else
+			"-x" ^ (string_of_int (Char.code chr))) name

+ 1 - 1
src/core/tFunctions.ml

@@ -283,7 +283,7 @@ let null_abstract = {
 }
 
 let add_dependency ?(skip_postprocess=false) m mdep =
-	if m != null_module && (m.m_path != mdep.m_path || m.m_extra.m_sign != mdep.m_extra.m_sign) then begin
+	if m != null_module && mdep != null_module && (m.m_path != mdep.m_path || m.m_extra.m_sign != mdep.m_extra.m_sign) then begin
 		m.m_extra.m_deps <- PMap.add mdep.m_id (mdep.m_extra.m_sign, mdep.m_path) m.m_extra.m_deps;
 		(* In case the module is cached, we'll have to run post-processing on it again (issue #10635) *)
 		if not skip_postprocess then m.m_extra.m_processed <- 0

+ 15 - 0
src/core/texpr.ml

@@ -561,6 +561,21 @@ module Builder = struct
 
 	let index basic e index t p =
 		mk (TArray (e,mk (TConst (TInt (Int32.of_int index))) basic.tint p)) t p
+
+	let resolve_and_make_static_call c name args p =
+		ignore(c.cl_build());
+		let cf = try
+			PMap.find name c.cl_statics
+		with Not_found ->
+			die "" __LOC__
+		in
+		let ef = make_static_field c cf (mk_zero_range_pos p) in
+		let tret = match follow ef.etype with
+			| TFun(_,r) -> r
+			| _ -> assert false
+		in
+		mk (TCall (ef, args)) tret p
+
 end
 
 let set_default basic a c p =

+ 1 - 1
src/filters/ES6Ctors.ml

@@ -84,7 +84,7 @@ let rewrite_ctors com =
 		let rec mark_needs_ctor_skipping cl =
 			(* for non haxe-generated extern classes we can't generate any valid code, so just fail *)
 			if (has_class_flag cl CExtern) && not (Meta.has Meta.HxGen cl.cl_meta) then begin
-				abort "Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor" p_this_access;
+				Error.abort "Must call `super()` constructor before accessing `this` in classes derived from an extern class with constructor" p_this_access;
 			end;
 			try
 				Hashtbl.find needs_ctor_skipping cl.cl_path

+ 1 - 1
src/filters/exceptions.ml

@@ -63,7 +63,7 @@ let haxe_exception_instance_call ctx haxe_exception method_name args p =
 *)
 let std_is ctx e t p =
 	let t = follow t in
-	let std_cls = ctx.typer.g.std in
+	let std_cls = ctx.typer.com.std in
 	let isOfType_field =
 		try PMap.find "isOfType" std_cls.cl_statics
 		with Not_found -> raise_typing_error ("Std has no field isOfType") p

+ 14 - 2
src/filters/filters.ml

@@ -655,6 +655,15 @@ let save_class_state com t =
 			a.a_meta <- List.filter (fun (m,_,_) -> m <> Meta.ValueUsed) a.a_meta
 		)
 
+let might_need_cf_unoptimized c cf =
+	match cf.cf_kind,c.cl_kind with
+	| Method MethInline,_ ->
+		true
+	| _,KGeneric ->
+		true
+	| _ ->
+		has_class_field_flag cf CfGeneric
+
 let run tctx main before_destruction =
 	let com = tctx.com in
 	let detail_times = (try int_of_string (Common.defined_value_safe com ~default:"0" Define.FilterTimes) with _ -> 0) in
@@ -670,8 +679,11 @@ let run tctx main before_destruction =
 				(* Save cf_expr_unoptimized early: We want to inline with the original expression
 				   on the next compilation. *)
 				if not cached then begin
-					let field cf =
-						cf.cf_expr_unoptimized <- cf.cf_expr
+					let field cf = match cf.cf_expr,cf.cf_expr_unoptimized with
+						| Some e,None when might_need_cf_unoptimized cls cf ->
+							cf.cf_expr_unoptimized <- Some e
+						| _ ->
+							()
 					in
 					List.iter field cls.cl_ordered_fields;
 					List.iter field cls.cl_ordered_statics;

+ 37 - 14
src/filters/localStatic.ml

@@ -3,11 +3,18 @@ open Type
 open Typecore
 open Error
 
-let promote_local_static ctx lut v eo =
-	let name = Printf.sprintf "%s_%s" ctx.curfield.cf_name v.v_name in
+type lscontext = {
+	ctx : typer;
+	lut : (int,tclass_field) Hashtbl.t;
+	mutable added_fields : tclass_field list;
+}
+
+let promote_local_static lsctx run v eo =
+	let name = Printf.sprintf "%s_%s" lsctx.ctx.curfield.cf_name v.v_name in
+	let c = lsctx.ctx.curclass in
 	begin try
-		let cf = PMap.find name ctx.curclass.cl_statics in
-		display_error ctx.com (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos;
+		let cf = PMap.find name c.cl_statics in
+		display_error lsctx.ctx.com (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos;
 		raise_typing_error ~depth:1 "Conflicting field was found here" cf.cf_name_pos;
 	with Not_found ->
 		let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in
@@ -16,34 +23,45 @@ let promote_local_static ctx lut v eo =
 		| None ->
 			()
 		| Some e ->
+			let no_local_in_static p =
+				raise_typing_error "Accessing local variables in static initialization is not allowed" p
+			in
 			let rec loop e = match e.eexpr with
-				| TLocal _ | TFunction _ ->
-					raise_typing_error "Accessing local variables in static initialization is not allowed" e.epos
+				| TLocal v when has_var_flag v VStatic ->
+					run e
+				| TFunction _ | TLocal _ ->
+					no_local_in_static e.epos
 				| TConst (TThis | TSuper) ->
 					raise_typing_error "Accessing `this` in static initialization is not allowed" e.epos
 				| TReturn _ | TBreak | TContinue ->
 					raise_typing_error "This kind of control flow in static initialization is not allowed" e.epos
 				| _ ->
-					iter loop e
+					map_expr loop e
 			in
-			loop e;
+			let e = loop e in
 			cf.cf_expr <- Some e
 		end;
-		TClass.add_field ctx.curclass cf;
-		Hashtbl.add lut v.v_id cf
+		lsctx.added_fields <- cf :: lsctx.added_fields;
+		(* Add to lookup early so that the duplication check works. *)
+		c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics;
+		Hashtbl.add lsctx.lut v.v_id cf
 	end
 
 let find_local_static lut v =
 	Hashtbl.find lut v.v_id
 
 let run ctx e =
-	let local_static_lut = Hashtbl.create 0 in
+	let lsctx = {
+		ctx = ctx;
+		lut = Hashtbl.create 0;
+		added_fields = [];
+	} in
 	let c = ctx.curclass in
 	let rec run e = match e.eexpr with
 		| TBlock el ->
 			let el = ExtList.List.filter_map (fun e -> match e.eexpr with
 				| TVar(v,eo) when has_var_flag v VStatic ->
-					promote_local_static ctx local_static_lut v eo;
+					promote_local_static lsctx run v eo;
 					None
 				| _ ->
 					Some (run e)
@@ -51,7 +69,7 @@ let run ctx e =
 			{ e with eexpr = TBlock el }
 		| TLocal v when has_var_flag v VStatic ->
 			begin try
-				let cf = find_local_static local_static_lut v in
+				let cf = find_local_static lsctx.lut v in
 				Texpr.Builder.make_static_field c cf e.epos
 			with Not_found ->
 				raise_typing_error (Printf.sprintf "Could not find local static %s (id %i)" v.v_name v.v_id) e.epos
@@ -59,4 +77,9 @@ let run ctx e =
 		| _ ->
 			Type.map_expr run e
 	in
-	run e
+	let e = run e in
+	(* Add to ordered list in reverse order *)
+	List.iter (fun cf ->
+		c.cl_ordered_statics <- cf :: c.cl_ordered_statics
+	) lsctx.added_fields;
+	e

+ 1 - 0
src/generators/gencpp.ml

@@ -19,6 +19,7 @@
 open Extlib_leftovers
 open Ast
 open Type
+open Error
 open Common
 open Globals
 

+ 2 - 4
src/generators/genhl.ml

@@ -23,6 +23,7 @@ open Extlib_leftovers
 open Globals
 open Ast
 open Type
+open Error
 open Common
 open Hlcode
 
@@ -2698,13 +2699,10 @@ and eval_expr ctx e =
 		ctx.m.mbreaks <- [];
 		ctx.m.mcontinues <- [];
 		ctx.m.mloop_trys <- ctx.m.mtrys;
-		let start = jump ctx (fun p -> OJAlways p) in
 		let continue_pos = current_pos ctx in
 		let ret = jump_back ctx in
-		let j = jump_expr ctx cond false in
-		start();
 		ignore(eval_expr ctx eloop);
-		set_curpos ctx (max_pos e);
+		let j = jump_expr ctx cond false in
 		ret();
 		j();
 		List.iter (fun f -> f (current_pos ctx)) ctx.m.mbreaks;

+ 1 - 0
src/generators/genjs.ml

@@ -20,6 +20,7 @@ open Extlib_leftovers
 open Globals
 open Ast
 open Type
+open Error
 open Common
 open JsSourcemap
 

+ 1 - 1
src/generators/genjvm.ml

@@ -3102,7 +3102,7 @@ let generate jvm_flag com =
 		end
 	) com.native_libs.java_libs in
 	Hashtbl.iter (fun name v ->
-		let filename = Codegen.escape_res_name name ['/';'-'] in
+		let filename = StringHelper.escape_res_name name ['/';'-'] in
 		gctx.out#add_entry v filename;
 	) com.resources;
 	let generate_real_types () =

+ 3 - 3
src/generators/genneko.ml

@@ -170,7 +170,7 @@ let gen_constant ctx pe c =
 			if (h land 128 = 0) <> (h land 64 = 0) then raise Exit;
 			int p (Int32.to_int i)
 		with _ ->
-			if ctx.version < 2 then abort "This integer is too big to be compiled to a Neko 31-bit integer. Please use a Float instead" pe;
+			if ctx.version < 2 then Error.abort "This integer is too big to be compiled to a Neko 31-bit integer. Please use a Float instead" pe;
 			(EConst (Int32 i),p))
 	| TFloat f -> (EConst (Float (Texpr.replace_separators f "")),p)
 	| TString s -> call p (field p (ident p "String") "new") [gen_big_string ctx p s]
@@ -237,7 +237,7 @@ and gen_expr ctx e =
 		(match follow e.etype with
 		| TFun (args,_) ->
 			let n = List.length args in
-			if n > 5 then abort "Cannot create closure with more than 5 arguments" e.epos;
+			if n > 5 then Error.abort "Cannot create closure with more than 5 arguments" e.epos;
 			let tmp = ident p "@tmp" in
 			EBlock [
 				(EVars ["@tmp", Some (gen_expr ctx e2); "@fun", Some (field p tmp f.cf_name)] , p);
@@ -798,7 +798,7 @@ let generate com =
 				else
 					loop (p + 1)
 			in
-			abort msg (loop 0)
+			Error.abort msg (loop 0)
 	end;
 	let command cmd args = try com.run_command_args cmd args with _ -> -1 in
 	let neko_file = (try Filename.chop_extension com.file with _ -> com.file) ^ ".neko" in

+ 1 - 1
src/generators/genphp7.ml

@@ -35,7 +35,7 @@ let write_resource dir name data =
 	let rdir = dir ^ "/res" in
 	if not (Sys.file_exists dir) then Unix.mkdir dir 0o755;
 	if not (Sys.file_exists rdir) then Unix.mkdir rdir 0o755;
-	let name = Codegen.escape_res_name name [] in
+	let name = StringHelper.escape_res_name name [] in
 	let ch = open_out_bin (rdir ^ "/" ^ name) in
 	output_string ch data;
 	close_out ch

+ 2 - 1
src/generators/genpy.ml

@@ -19,6 +19,7 @@
 open Extlib_leftovers
 open Globals
 open Ast
+open Error
 open Type
 open Common
 open Texpr.Builder
@@ -2269,7 +2270,7 @@ module Generator = struct
 				end else
 					","
 				in
-				let k_enc = Codegen.escape_res_name k [] in
+				let k_enc = StringHelper.escape_res_name k [] in
 				print ctx "%s\"%s\": open('%%s.%%s'%%(_file,'%s'),'rb').read()" prefix (StringHelper.s_escape k) k_enc;
 
 				let f = open_out_bin (ctx.com.file ^ "." ^ k_enc) in

+ 1 - 0
src/generators/genswf.ml

@@ -20,6 +20,7 @@ open Swf
 open As3hl
 open ExtString
 open Type
+open Error
 open Common
 open Ast
 open Globals

+ 1 - 0
src/generators/genswf9.ml

@@ -20,6 +20,7 @@ open Extlib_leftovers
 open Globals
 open Ast
 open Type
+open Error
 open As3
 open As3hl
 open Common

+ 1 - 1
src/generators/hlinterp.ml

@@ -2209,7 +2209,7 @@ let check code macros =
 					Globals.pmin = low;
 					Globals.pmax = low + (dline lsr 20);
 				} in
-				Common.abort msg pos
+				Error.abort msg pos
 			end else
 				failwith (Printf.sprintf "\n%s:%d: %s" file dline msg)
 		in

+ 8 - 5
src/generators/hlopt.ml

@@ -947,8 +947,8 @@ let _optimize (f:fundecl) =
 				(* loop : first pass does not recurse, second pass uses cache *)
 				if b2.bloop && b2.bstart < b.bstart then (match b2.bneed_all with None -> acc | Some s -> ISet.union acc s) else
 				ISet.union acc (live b2)
-			) ISet.empty b.bnext in
-			let need_sub = ISet.filter (fun r ->
+			) ISet.empty in
+			let need_sub bl = ISet.filter (fun r ->
 				try
 					let w = PMap.find r b.bwrite in
 					set_live r (w + 1) b.bend;
@@ -956,8 +956,8 @@ let _optimize (f:fundecl) =
 				with Not_found ->
 					set_live r b.bstart b.bend;
 					true
-			) need_sub in
-			let need = ISet.union b.bneed need_sub in
+			) (need_sub bl) in
+			let need = ISet.union b.bneed (need_sub b.bnext) in
 			b.bneed_all <- Some need;
 			if b.bloop then begin
 				(*
@@ -974,8 +974,11 @@ let _optimize (f:fundecl) =
 				in
 				List.iter (fun b2 -> if b2.bstart > b.bstart then clear b2) b.bprev;
 				List.iter (fun b -> ignore(live b)) b.bnext;
+				(* do-while loop : recompute self after recompute all next *)
+				let need = ISet.union b.bneed (need_sub b.bnext) in
+				b.bneed_all <- Some need;
 			end;
-			need
+			Option.get b.bneed_all
 	in
 	ignore(live root);
 

+ 18 - 12
src/generators/jvm/jvmFunctions.ml

@@ -317,6 +317,8 @@ module JavaFunctionalInterfaces = struct
 	let unify jfi args ret =
 		let params = ref [] in
 		let rec unify jsig1 jsig2 = match jsig1,jsig2 with
+			| TObject _,TObject((["java";"lang"],"Object"),[]) ->
+				true
 			| TObject(path1,params1),TObject(path2,params2) ->
 				path1 = path2 &&
 				unify_params params1 params2
@@ -362,7 +364,7 @@ module JavaFunctionalInterfaces = struct
 		| None,None ->
 			loop jfi.jargs args
 		| Some jsig1,Some jsig2 ->
-			if unify jsig1 jsig2 then loop jfi.jargs args
+			if unify jsig2 jsig1 then loop jfi.jargs args
 			else None
 		| _ ->
 			None
@@ -441,24 +443,28 @@ class typed_function
 				Hashtbl.add implemented_interfaces path true;
 			end
 		in
+		let spawn_invoke_next name msig is_bridge =
+			let flags = [MPublic] in
+			let flags = if is_bridge then MBridge :: MSynthetic :: flags else flags in
+			jc_closure#spawn_method name msig flags
+		in
 		let spawn_forward_function meth_from meth_to is_bridge =
 			let msig = method_sig meth_from.dargs meth_from.dret in
 			if not (jc_closure#has_method meth_from.name msig) then begin
-				let flags = [MPublic] in
-				let flags = if is_bridge then MBridge :: MSynthetic :: flags else flags in
-				let jm_invoke_next = jc_closure#spawn_method meth_from.name msig flags in
+				let jm_invoke_next = spawn_invoke_next meth_from.name msig is_bridge in
 				functions#make_forward_method jc_closure jm_invoke_next meth_from meth_to;
 			end
 		in
 		let check_functional_interfaces meth =
-			try
-				let l = JavaFunctionalInterfaces.find_compatible meth.dargs meth.dret functional_interface_filter in
-				List.iter (fun (jfi,params) ->
-					add_interface jfi.jpath params;
-					spawn_forward_function {meth with name=jfi.jname} meth false;
-				) l
-			with Not_found ->
-				()
+			let l = JavaFunctionalInterfaces.find_compatible meth.dargs meth.dret functional_interface_filter in
+			List.iter (fun (jfi,params) ->
+				add_interface jfi.jpath params;
+				let msig = method_sig jfi.jargs jfi.jret in
+				if not (jc_closure#has_method jfi.jname msig) then begin
+					let jm_invoke_next = spawn_invoke_next jfi.jname msig false in
+					functions#make_forward_method_jsig jc_closure jm_invoke_next meth.name jfi.jargs jfi.jret meth.dargs meth.dret
+				end
+			) l
 		in
 		let rec loop meth =
 			check_functional_interfaces meth;

+ 8 - 10
src/macro/macroApi.ml

@@ -2183,16 +2183,14 @@ let macro_api ccom get_api =
 		"add_native_lib", vfun1 (fun file ->
 			let file = decode_string file in
 			let com = ccom() in
-			NativeLibraryHandler.add_native_lib com file false ();
-			vnull
-		);
-		"add_native_arg", vfun1 (fun arg ->
-			let arg = decode_string arg in
-			let com = ccom() in
-			(match com.platform with
-			| Globals.Jvm | Globals.Cpp ->
-				com.c_args <- arg :: com.c_args
-			| _ -> failwith "Unsupported platform");
+			let open CompilationContext in
+			let kind = match com.platform with
+				| Jvm -> JavaLib
+				| Flash -> SwfLib
+				| _ -> failwith "Unsupported platform"
+			in
+			let lib = create_native_lib file false kind in
+			NativeLibraryHandler.add_native_lib com lib ();
 			vnull
 		);
 		"register_module_dependency", vfun2 (fun m file ->

+ 2 - 3
src/optimization/analyzerTexpr.ml

@@ -1067,12 +1067,11 @@ module Cleanup = struct
 							| TLocal v when IntMap.mem v.v_id !locals -> true
 							| _ -> check_expr references_local e
 						in
-						let can_do = match com.platform with Hl -> false | _ -> true in
 						let rec loop2 el = match el with
-							| [{eexpr = TBreak}] when is_true_expr e1 && can_do && not has_continue ->
+							| [{eexpr = TBreak}] when is_true_expr e1 && not has_continue ->
 								do_while := Some (Texpr.Builder.make_bool com.basic true e1.epos);
 								[]
-							| [{eexpr = TIf(econd,{eexpr = TBlock[{eexpr = TBreak}]},None)}] when is_true_expr e1 && not (references_local econd) && can_do && not has_continue ->
+							| [{eexpr = TIf(econd,{eexpr = TBlock[{eexpr = TBreak}]},None)}] when is_true_expr e1 && not (references_local econd) && not has_continue ->
 								do_while := Some econd;
 								[]
 							| {eexpr = TBreak | TContinue | TReturn _ | TThrow _} as e :: el ->

+ 1 - 1
src/optimization/analyzerTypes.ml

@@ -218,7 +218,7 @@ module BasicBlock = struct
 		bb
 
 	let in_scope bb bb' = match bb'.bb_scopes with
-		| [] -> abort (Printf.sprintf "Scope-less block (kind: %s)" (s_block_kind bb'.bb_kind)) bb'.bb_pos
+		| [] -> Error.abort (Printf.sprintf "Scope-less block (kind: %s)" (s_block_kind bb'.bb_kind)) bb'.bb_pos
 		| scope :: _ -> List.mem scope bb.bb_scopes
 
 	let terminator_map f term = match term with

+ 1 - 1
src/typing/callUnification.ml

@@ -228,7 +228,7 @@ let unify_field_call ctx fa el_typed el p inline =
 			else
 				List.map (fun (t,cf) ->
 					cf
-				) (Overloads.get_overloads ctx.com c cf.cf_name)
+				) (get_overloads ctx.com c cf.cf_name)
 			in
 			cfl,Some c,false,TClass.get_map_function c tl,(fun t -> t)
 		| FHAbstract(a,tl,c) ->

+ 19 - 9
src/typing/calls.ml

@@ -67,17 +67,27 @@ let make_call ctx e params t ?(force_inline=false) p =
 		);
 		let params = List.map (Optimizer.reduce_expression ctx) params in
 		let force_inline = is_forced_inline cl f in
-		(match f.cf_expr_unoptimized,f.cf_expr with
-		| Some {eexpr = TFunction fd},_
-		| None,Some { eexpr = TFunction fd } ->
+		let inline fd =
 			Inline.type_inline ctx f fd ethis params t config p force_inline
+		in
+		begin match f.cf_expr_unoptimized with
+		| Some {eexpr = TFunction fd} ->
+			inline fd
 		| _ ->
-			(*
-				we can't inline because there is most likely a loop in the typing.
-				this can be caused by mutually recursive vars/functions, some of them
-				being inlined or not. In that case simply ignore inlining.
-			*)
-			raise Exit)
+			if has_class_field_flag f CfPostProcessed then
+				warning ctx  WInlineOptimizedField (Printf.sprintf "Inlining of cached field %s might lead to unexpected output" f.cf_name) p;
+			match f.cf_expr with
+			| Some ({ eexpr = TFunction fd } as e) ->
+				f.cf_expr_unoptimized <- Some (e);
+				inline fd
+			| _ ->
+				(*
+					we can't inline because there is most likely a loop in the typing.
+					this can be caused by mutually recursive vars/functions, some of them
+					being inlined or not. In that case simply ignore inlining.
+				*)
+				raise Exit
+		end
 	with Exit ->
 		mk (TCall (e,params)) t p
 

+ 1 - 5
src/typing/operators.ml

@@ -201,11 +201,7 @@ let make_binop ctx op e1 e2 is_assign_op p =
 				call_to_string ctx e
 			| KInt | KFloat | KString -> e
 			| KUnk | KDyn | KNumParam _ | KStrParam _ | KOther ->
-				let std = type_type ctx ([],"Std") e.epos in
-				let acc = acc_get ctx (type_field_default_cfg ctx std "string" e.epos (MCall []) WithType.value) in
-				ignore(follow acc.etype);
-				let acc = (match acc.eexpr with TField (e,FClosure (Some (c,tl),f)) -> { acc with eexpr = TField (e,FInstance (c,tl,f)) } | _ -> acc) in
-				make_call ctx acc [e] ctx.t.tstring e.epos
+				Texpr.Builder.resolve_and_make_static_call ctx.com.std "string" [e] e.epos
 			| KAbstract (a,tl) ->
 				try
 					AbstractCast.cast_or_unify_raise ctx tstring e p

+ 2 - 2
src/typing/typeloadCheck.ml

@@ -241,7 +241,7 @@ let check_overriding ctx c f =
 		let p = f.cf_name_pos in
 		let i = f.cf_name in
 		if has_class_field_flag f CfOverload then begin
-			let overloads = Overloads.get_overloads ctx.com csup i in
+			let overloads = get_overloads ctx.com csup i in
 			List.iter (fun (t,f2) ->
 				(* check if any super class fields are vars *)
 				match f2.cf_kind with
@@ -376,7 +376,7 @@ module Inheritance = struct
 				let map2, t2, f2 = class_field_no_interf c f.cf_name in
 				let t2, f2 =
 					if f2.cf_overloads <> [] || has_class_field_flag f2 CfOverload then
-						let overloads = Overloads.get_overloads ctx.com c f.cf_name in
+						let overloads = get_overloads ctx.com c f.cf_name in
 						is_overload := true;
 						List.find (fun (t1,f1) -> Overloads.same_overload_args t t1 f f1) overloads
 					else

+ 1 - 1
src/typing/typeloadFields.ml

@@ -1468,7 +1468,7 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p =
 			let cf = PMap.find m c.cl_statics in
 			(cf.cf_type,cf) :: (List.map (fun cf -> cf.cf_type,cf) cf.cf_overloads)
 		end else
-			Overloads.get_overloads ctx.com c m
+			get_overloads ctx.com c m
 	in
 	let cf = {
 		(mk_field name ~public:(is_public (ctx,cctx) f.cff_access None) ret f.cff_pos (pos f.cff_name)) with

+ 1 - 10
src/typing/typer.ml

@@ -2009,16 +2009,7 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
 			if ctx.in_display && DisplayPosition.display_position#enclosed_in p_t then
 				DisplayEmitter.display_module_type ctx mt p_t;
 			let e_t = type_module_type ctx mt p_t in
-			let e_Std_isOfType =
-				ignore(ctx.g.std.cl_build());
-				let cf = try
-					PMap.find "isOfType" ctx.g.std.cl_statics
-				with Not_found ->
-					die "" __LOC__
-				in
-				Texpr.Builder.make_static_field ctx.g.std cf (mk_zero_range_pos p)
-			in
-			mk (TCall (e_Std_isOfType, [e; e_t])) ctx.com.basic.tbool p
+			Texpr.Builder.resolve_and_make_static_call ctx.com.std "isOfType" [e;e_t] p
 		| _ ->
 			display_error ctx.com "Unsupported type for `is` operator" p_t;
 			Texpr.Builder.make_bool ctx.com.basic false p

+ 0 - 3
src/typing/typerBase.ml

@@ -232,9 +232,6 @@ let type_module_type ctx t p =
 	in
 	loop t None
 
-let type_type ctx tpath p =
-	type_module_type ctx (Typeload.load_type_def ctx p (mk_type_path tpath)) p
-
 let mk_module_type_access ctx t p =
 	AKExpr (type_module_type ctx t p)
 

+ 2 - 3
src/typing/typerEntry.ml

@@ -20,7 +20,6 @@ let create com macros =
 			doinline = com.display.dms_inline && not (Common.defined com Define.NoInline);
 			retain_meta = Common.defined com Define.RetainUntypedMeta;
 			std_types = null_module;
-			std = null_class;
 			global_using = [];
 			complete = false;
 			type_hints = [];
@@ -127,7 +126,7 @@ let create com macros =
 	) ctx.g.std_types.m_types;
 	let m = TypeloadModule.load_module ctx ([],"String") null_pos in
 	List.iter (fun mt -> match mt with
-		| TClassDecl c ->
+		| TClassDecl ({cl_path = ([],"String")} as c) ->
 			let t = (TInst (c,[])) in
 			Type.unify t ctx.t.tstring;
 			ctx.t.tstring <- t
@@ -135,7 +134,7 @@ let create com macros =
 	) m.m_types;
 	let m = TypeloadModule.load_module ctx ([],"Std") null_pos in
 	List.iter (fun mt -> match mt with
-		| TClassDecl c -> ctx.g.std <- c;
+		| TClassDecl ({cl_path = ([],"Std")} as c) -> ctx.com.std <- c;
 		| _ -> ()
 	) m.m_types;
 	let m = TypeloadModule.load_module ctx ([],"Array") null_pos in

+ 0 - 9
std/haxe/macro/Compiler.hx

@@ -204,15 +204,6 @@ class Compiler {
 		#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.
 

+ 32 - 0
tests/misc/java/projects/Issue11390/Main.hx

@@ -0,0 +1,32 @@
+package;
+
+import test.Robot;
+import test.RobotFactory;
+
+class Main {
+	public static function main() {
+		var robot1 = RobotFactory.buildMathRobot();
+		var robot2 = RobotFactory.buildGreetRobot(robot1);
+		var robot3 = RobotFactory.buildManufactureRobot();
+
+		robot1.performTask(add);
+		robot1.performTask(function(a:Int, b:Int):Int {
+			return a - b;
+		});
+
+		robot2.performTask(function (target:Robot) {
+            trace('Hello, ${target.toString()}!');
+        }, () -> {
+			trace('Cleanup...');
+		});
+
+		robot3.performTask(function (robotType:String) {
+			trace('Manufacturing ${robotType}...');
+			return robot2;
+		});
+	}
+
+	static function add(a:Int, b:Int):Int {
+		return a + b;
+	}
+}

+ 9 - 0
tests/misc/java/projects/Issue11390/Setup.hx

@@ -0,0 +1,9 @@
+import sys.FileSystem;
+
+function main() {
+	Sys.setCwd("./project");
+	FileSystem.createDirectory("./out");
+	Sys.command("javac", ["-d", "out", "test/Robot.java", "test/RobotFactory.java", "-g"]);
+	Sys.setCwd("./out");
+	Sys.command("jar", ["cf", "test.jar", "test/Robot.class", "test/Robot$CleanupTask.class", "test/Robot$MathOperation.class", "test/Robot$GreetRobot.class", "test/Robot$ManufactureRobot.class", "test/RobotFactory.class", "test/RobotFactory$1.class", "test/RobotFactory$2.class", "test/RobotFactory$3.class"]);
+}

+ 12 - 0
tests/misc/java/projects/Issue11390/compile.hxml

@@ -0,0 +1,12 @@
+--main Setup
+--interp
+
+--next
+
+--main Main
+--java-lib project/out/test.jar
+--jvm run.jar
+
+--next
+
+--cmd java -jar run.jar

+ 10 - 0
tests/misc/java/projects/Issue11390/compile.hxml.stdout

@@ -0,0 +1,10 @@
+Robot.performTask() called!
+Result: 7
+Robot.performTask() called!
+Result: -1
+Robot.performTask() called!
+Main.hx:18: Hello, Robot!
+Main.hx:20: Cleanup...
+Robot.performTask() called!
+Main.hx:24: Manufacturing Greet...
+Output: Robot

+ 42 - 0
tests/misc/java/projects/Issue11390/project/test/Robot.java

@@ -0,0 +1,42 @@
+package test;
+
+public abstract class Robot<T> {
+    public Robot() {}
+
+    public void performTask(T listener) {
+        System.out.println("Robot.performTask() called!");
+    }
+
+    public void performTask(T listener, CleanupTask cleanupTask) {
+        System.out.println("Robot.performTask() called!");
+        cleanupTask.cleanup();
+    }
+
+    /**
+     * MathOperation
+     */
+    @FunctionalInterface
+    public interface MathOperation {
+        public int operate(int a, int b);
+    }
+
+    @FunctionalInterface
+    public interface GreetRobot {
+        public void greet(Robot robot);
+    }
+
+    @FunctionalInterface
+    public interface ManufactureRobot<T extends Robot> {
+        public T manufacture(String robotType);
+    }
+
+    @FunctionalInterface
+    public interface CleanupTask {
+        public void cleanup();
+    }
+
+    @Override
+    public String toString() {
+        return "Robot";
+    }
+}

+ 56 - 0
tests/misc/java/projects/Issue11390/project/test/RobotFactory.java

@@ -0,0 +1,56 @@
+package test;
+
+import test.Robot.GreetRobot;
+import test.Robot.ManufactureRobot;
+import test.Robot.MathOperation;
+
+public class RobotFactory {
+    public static Robot<MathOperation> buildMathRobot() {
+        return new Robot<MathOperation>() {
+            public void performTask(MathOperation listener) {
+                System.out.println("Robot.performTask() called!");
+                int result = listener.operate(3, 4);
+                System.out.println("Result: " + result);
+            }
+
+            public void performTask(MathOperation listener, CleanupTask cleanupTask) {
+                System.out.println("Robot.performTask() called!");
+                int result = listener.operate(3, 4);
+                System.out.println("Result: " + result);
+                cleanupTask.cleanup();
+            }
+        };
+    }
+
+    public static Robot<GreetRobot> buildGreetRobot(Robot target) {
+        return new Robot<GreetRobot>() {
+            public void performTask(GreetRobot listener) {
+                System.out.println("Robot.performTask() called!");
+                listener.greet(target);
+            }
+
+            public void performTask(GreetRobot listener, CleanupTask cleanupTask) {
+                System.out.println("Robot.performTask() called!");
+                listener.greet(target);
+                cleanupTask.cleanup();
+            }
+        };
+    }
+
+    public static Robot<ManufactureRobot<Robot<GreetRobot>>> buildManufactureRobot() {
+        return new Robot<ManufactureRobot<Robot<GreetRobot>>>() {
+            public void performTask(ManufactureRobot<Robot<GreetRobot>> listener) {
+                System.out.println("Robot.performTask() called!");
+                Robot<GreetRobot> output = listener.manufacture("Greet");
+                System.out.println("Output: " + output.toString());
+            }
+
+            public void performTask(ManufactureRobot<Robot<GreetRobot>> listener, CleanupTask cleanupTask) {
+                System.out.println("Robot.performTask() called!");
+                Robot<GreetRobot> output = listener.manufacture("Greet");
+                System.out.println("Output: " + output.toString());
+                cleanupTask.cleanup();
+            }
+        };
+    }
+}

+ 4 - 0
tests/server/src/TestCase.hx

@@ -213,6 +213,10 @@ class TestCase implements ITest {
 		}
 	}
 
+	function assertSilence() {
+		return Assert.isTrue(lastResult.stderr == "");
+	}
+
 	function assertSuccess(?p:haxe.PosInfos) {
 		return Assert.isTrue(0 == errorMessages.length, p);
 	}

+ 149 - 0
tests/server/src/cases/CsSafeTypeBuilding.hx

@@ -0,0 +1,149 @@
+package cases;
+
+import haxe.display.Display;
+import haxe.display.FsPath;
+import haxe.display.Server;
+import utest.Assert;
+
+using StringTools;
+using Lambda;
+
+class CsSafeTypeBuilding extends TestCase {
+	var originalContent:String;
+
+	override public function setup(async:utest.Async) {
+		super.setup(async);
+
+		originalContent = "";
+		vfs.putContent("Bar.hx", getTemplate("csSafeTypeBuilding/Bar.hx"));
+		vfs.putContent("Baz.hx", getTemplate("csSafeTypeBuilding/Baz.hx"));
+		vfs.putContent("Foo.hx", getTemplate("csSafeTypeBuilding/Foo.hx"));
+		vfs.putContent("Macro.macro.hx", getTemplate("csSafeTypeBuilding/Macro.macro.hx"));
+		vfs.putContent("Main.hx", getTemplate("csSafeTypeBuilding/Main.hx"));
+	}
+
+	#if debug
+	var failed:Bool;
+	function _assertHasPrint(s:String, ?pos:haxe.PosInfos) {
+		if (!assertHasPrint(s)) {
+			failed = true;
+			haxe.Log.trace("Fail: doesn't contain \"" + s + "\"", pos);
+		}
+	}
+	#end
+
+	function assertResult(target:String) {
+		#if debug
+		failed = false;
+		var assertHasPrint = _assertHasPrint;
+		#end
+		assertSuccess();
+
+		// Make sure all types are generated
+		assertHasPrint("[runtime] Hello from Bar");
+		assertHasPrint("[runtime] Hello from Baz");
+		assertHasPrint("[runtime] Hello from Foo__Bar__Bar");
+		assertHasPrint("[runtime] Hello from Foo__Baz__Baz");
+		assertHasPrint("[runtime] Hello from Foo__Main__Main");
+		assertHasPrint("[runtime] Hello from Main");
+
+		#if debug
+		if (failed) messages.filter(m -> StringTools.startsWith(m, "Haxe print: ")).iter(m -> trace(m));
+		#end
+
+		// Disabled this check because types move around a bit so we get false negatives
+		// Kept for debugging purposes
+		if (false && target == "js") {
+			var content = sys.io.File.getContent(haxe.io.Path.join([testDir, "out.js"]));
+			Assert.isTrue(content == originalContent);
+
+			// Needs https://github.com/kLabz/hxdiff for displaying diff
+			// if (content != originalContent) {
+			// 	final a = new diff.FileData(haxe.io.Bytes.ofString(originalContent), "expected", Date.now());
+			// 	final b = new diff.FileData(haxe.io.Bytes.ofString(content), "actual", Date.now());
+			// 	var ctx:diff.Context = {
+			// 	  file1: a,
+			// 	  file2: b,
+			// 	  context: 10
+			// 	}
+
+			// 	final script = diff.Analyze.diff2Files(ctx);
+			// 	var diff = diff.Printer.printUnidiff(ctx, script);
+			// 	Sys.println(diff);
+			// }
+		}
+	}
+
+	function assertBuilt(modules:Array<String>, ?macroInvalidated:Bool = false) {
+		#if debug trace('Invalidated ${modules.join(",")} (macro invalidated: ${macroInvalidated ? "true" : "false"})'); #end
+		#if debug var assertHasPrint = _assertHasPrint; #end
+
+		for (m in modules) {
+			assertHasPrint('Building $m.');
+
+			var t = 'Foo__${m}__${m}';
+			if (!macroInvalidated) assertHasPrint('[$m] Previously generated type for $t has been discarded.');
+			assertHasPrint('[$m] Generating type for $t.');
+
+			if (m == "Baz") {
+				assertHasPrint('[$m] Reusing previously generated type for Foo__Bar__Bar.');
+			}
+		}
+	}
+
+	@:variant("JsDefineModule", true, "js")
+	@:variant("JsDefineType", false, "js")
+	@:variant("InterpDefineModule", true, "interp")
+	@:variant("InterpDefineType", false, "interp")
+	function test(defineModule:Bool, target:String) {
+		var targetArgs = switch target {
+			case "js": ["-js", "out.js", "-lib", "hxnodejs", "-cmd", "node out.js"];
+			case "interp": ["--interp"];
+			case _: [];
+		}
+
+		var args = ["-main", "Main", "Baz"];
+		if (defineModule) args = args.concat(["-D", "config.defineModule"]);
+		args = args.concat(targetArgs);
+
+		runHaxe(args);
+		if (target == "js") originalContent = sys.io.File.getContent(haxe.io.Path.join([testDir, "out.js"]));
+		assertBuilt(["Main", "Bar", "Baz"], true);
+		assertResult(target);
+
+		#if debug trace("Rerun without invalidate"); #end
+		runHaxe(args);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Baz.hx")});
+		runHaxe(args);
+		assertBuilt(["Baz"]);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		assertBuilt(["Main"]);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Bar.hx")});
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		assertBuilt(["Main", "Bar"]);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Bar.hx")});
+		runHaxe(args);
+		assertBuilt(["Main", "Bar", "Baz"]);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Foo.hx")});
+		runHaxe(args);
+		assertBuilt(["Main", "Bar", "Baz"]);
+		assertResult(target);
+
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Macro.macro.hx")});
+		runHaxe(args);
+		assertBuilt(["Main", "Bar", "Baz"], true);
+		assertResult(target);
+	}
+}

+ 1 - 1
tests/server/src/cases/ServerTests.hx

@@ -433,7 +433,7 @@ class ServerTests extends TestCase {
 		vfs.putContent("haxe/ds/Vector.hx", getTemplate("issues/Issue10986/Vector.hx"));
 		var args = ["-main", "Main", "--jvm", "Main.jar"];
 		runHaxe(args);
-		vfs.touchFile("haxe/ds/Vector.hx");
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("haxe/ds/Vector.hx")});
 		runHaxe(args);
 		assertSuccess();
 	}

+ 53 - 0
tests/server/src/cases/issues/Issue11460.hx

@@ -0,0 +1,53 @@
+package cases.issues;
+
+using StringTools;
+
+class Issue11460 extends TestCase {
+	function testClass(_) {
+		var mainContentWithInline = getTemplate("issues/Issue11460/Main.hx");
+		var mainContentWithoutInline = mainContentWithInline.replace("inline", "");
+		vfs.putContent("Main.hx", mainContentWithInline);
+		vfs.putContent("C.hx", getTemplate("issues/Issue11460/C.hx"));
+		var args = ["Main", "--interp"];
+
+		// initial cache
+		runHaxe(args);
+		assertSilence();
+
+		// touching Main doesn't do anything
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		assertSilence();
+
+		// touching both doesn't do anything
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("C.hx")});
+		runHaxe(args);
+		assertSilence();
+
+		// removing the inline is fine
+		vfs.putContent("Main.hx", mainContentWithoutInline);
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		assertSilence();
+
+		// adding it back is fine too because C is still cached
+		vfs.putContent("Main.hx", mainContentWithInline);
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		assertSilence();
+
+		// removing the inline and changing C is still fine
+		vfs.putContent("Main.hx", mainContentWithoutInline);
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("C.hx")});
+		runHaxe(args);
+		assertSilence();
+
+		// but adding it now gives us the warning
+		vfs.putContent("Main.hx", mainContentWithInline);
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")});
+		runHaxe(args);
+		utest.Assert.match(~/WInlineOptimizedField/, lastResult.stderr);
+	}
+}

+ 1 - 1
tests/server/src/cases/issues/Issue9358.hx

@@ -6,7 +6,7 @@ class Issue9358 extends TestCase {
 		vfs.putContent("StateHandler.hx", getTemplate("issues/Issue9358/StateHandler.hx"));
 		var args = ["-cp", "src", "-m", "Main", "-hl", "hl.hl"];
 		runHaxe(args);
-		vfs.touchFile("Main.hx");
+		runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")});
 		runHaxe(args);
 		assertSuccess();
 	}

+ 0 - 10
tests/server/src/utils/Vfs.hx

@@ -17,16 +17,6 @@ class Vfs {
 		FileSystem.createDirectory(physicalPath);
 	}
 
-	public function touchFile(path:String) {
-		var path = getPhysicalPath(path);
-		FileSystem.createDirectory(path.dir);
-		var file = Fs.openSync(path.dir + "/" + path.file + "." + path.ext, 'a');
-		var last = Fs.fstatSync(file).mtime;
-		var notNow = last.delta(1000);
-		Fs.futimesSync(file, notNow, notNow);
-		Fs.closeSync(file);
-	}
-
 	public function overwriteContent(path:String, content:String) {
 		var path = getPhysicalPath(path).toString();
 		if (!FileSystem.exists(path)) {

+ 97 - 28
tests/server/src/utils/macro/TestBuilder.macro.hx

@@ -2,11 +2,15 @@ package utils.macro;
 
 import haxe.macro.Expr;
 import haxe.macro.Context;
+import haxe.macro.Type;
 
 using StringTools;
 
 class TestBuilder {
 	static public function build(fields:Array<Field>):Array<Field> {
+		var removedFields = [];
+		var newFields = [];
+
 		for (field in fields) {
 			if (!field.name.startsWith("test")) {
 				continue;
@@ -16,40 +20,105 @@ class TestBuilder {
 					// Async is already manually handled, nothing to do
 
 				case FFun(f):
-					var asyncName = switch f.args {
-						case []:
-							var name = "async";
-							f.args.push({
-								name: name,
-								type: macro:utest.Async
-							});
-							name;
-						case [arg]:
-							if (arg.name == "_") {
-								arg.name = "async";
-								arg.type = macro:utest.Async;
+					var variants = field.meta.filter(m -> m.name == ":variant");
+					if (variants.length == 0) {
+						makeAsyncTest(f, field.pos);
+					} else {
+						// TODO: support functions that define their own async arg (not named `_` or `async`)
+						var args = f.args.copy();
+						f.args = [];
+						makeAsyncTest(f, field.pos);
+
+						// Ignore original field; generate variants instead
+						removedFields.push(field);
+
+						for (variant in variants) {
+							if (variant.params.length == 0) {
+								Context.error('Unexpected amount of variant parameters.', variant.pos);
 							}
-							arg.name;
-						case _:
-							Context.fatalError('Unexpected amount of test arguments', field.pos);
-							"";
-					}
-					switch (f.expr.expr) {
-						case EBlock(el):
-							var posInfos = Context.getPosInfos(f.expr.pos);
-							var pos = Context.makePosition({min: posInfos.max, max: posInfos.max, file: posInfos.file});
-							el.push(macro @:pos(pos) $i{asyncName}.done());
-							f.expr = macro {
-								$i{asyncName}.setTimeout(20000);
-								${transformHaxeCalls(asyncName, el)};
+
+							var nameParam = variant.params.shift();
+							var name:String = try haxe.macro.ExprTools.getValue(nameParam) catch(e) {
+								Context.error('Variant first parameter should be a String (variant name)', nameParam.pos);
+							};
+
+							var inits = [for (arg in args) {
+								var name = arg.name;
+								var ct = arg.type;
+
+								if (variant.params.length == 0) {
+									Context.error('Unexpected amount of variant parameters.', variant.pos);
+								}
+
+								var param = variant.params.shift();
+								macro @:pos(param.pos) var $name:$ct = (($name:$ct) -> $i{name})(${param});
+							}];
+
+							if (variant.params.length > 0) {
+								Context.error('Unexpected amount of variant parameters.', variant.params[0].pos);
 							}
-						case _:
-							Context.error("Block expression expected", f.expr.pos);
+
+							switch (f.expr.expr) {
+								case EBlock(b):
+									var ff = {
+										ret: f.ret,
+										params: f.params,
+										expr: {pos: variant.pos, expr: EBlock(inits.concat(b))},
+										args: [{name: "async", type: macro:utest.Async}]
+									};
+
+									newFields.push({
+										pos: variant.pos,
+										name: field.name + name,
+										meta: field.meta.filter(m -> m.name != ":variant"),
+										kind: FFun(ff),
+										doc: field.doc,
+										access : field.access
+									});
+
+								case _:
+							}
+						}
 					}
 				case _:
 			}
 		}
-		return fields;
+
+		for (f in removedFields) fields.remove(f);
+		return fields.concat(newFields);
+	}
+
+	static function makeAsyncTest(f:Function, fpos:Position) {
+		var asyncName = switch f.args {
+			case []:
+				var name = "async";
+				f.args.push({
+					name: name,
+					type: macro:utest.Async
+				});
+				name;
+			case [arg]:
+				if (arg.name == "_") {
+					arg.name = "async";
+					arg.type = macro:utest.Async;
+				}
+				arg.name;
+			case _:
+				Context.fatalError('Unexpected amount of test arguments', fpos);
+				"";
+		}
+		switch (f.expr.expr) {
+			case EBlock(el):
+				var posInfos = Context.getPosInfos(f.expr.pos);
+				var pos = Context.makePosition({min: posInfos.max, max: posInfos.max, file: posInfos.file});
+				el.push(macro @:pos(pos) $i{asyncName}.done());
+				f.expr = macro {
+					$i{asyncName}.setTimeout(20000);
+					${transformHaxeCalls(asyncName, el)};
+				}
+			case _:
+				Context.error("Block expression expected", f.expr.pos);
+		}
 	}
 
 	static function transformHaxeCalls(asyncName:String, el:Array<Expr>) {

+ 6 - 0
tests/server/test/templates/csSafeTypeBuilding/Bar.hx

@@ -0,0 +1,6 @@
+#if !macro @:build(Macro.logBuild()) #end
+class Bar {
+	static function __init__() Sys.println("[runtime] Hello from Bar");
+}
+
+typedef B = Foo<Bar>;

+ 7 - 0
tests/server/test/templates/csSafeTypeBuilding/Baz.hx

@@ -0,0 +1,7 @@
+#if !macro @:build(Macro.logBuild()) #end
+class Baz {
+	static function __init__() Sys.println("[runtime] Hello from Baz");
+}
+
+typedef AA = Foo<Bar>;
+typedef BB = Foo<Baz>;

+ 2 - 0
tests/server/test/templates/csSafeTypeBuilding/Foo.hx

@@ -0,0 +1,2 @@
+#if !macro @:genericBuild(Macro.buildFoo()) #end
+class Foo<T> {}

+ 63 - 0
tests/server/test/templates/csSafeTypeBuilding/Macro.macro.hx

@@ -0,0 +1,63 @@
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.Type;
+import haxe.macro.TypeTools;
+
+class Macro {
+	public static function logBuild() {
+		Sys.println('Building ${Context.getLocalClass().toString()}.');
+		return null;
+	}
+
+	@:persistent static var generated = new Map<String, Bool>();
+
+	static function isAlive(ct:ComplexType, pos:Position):Bool {
+		// Null check is just there to make it a one liner
+		// Basically returning true if no exception is caught
+		return try Context.resolveType(ct, pos) != null catch(e) false;
+	}
+
+	public static function buildFoo() {
+		var from = '[${Context.getLocalModule()}] ';
+		var print = s -> Sys.println(from + s);
+
+		switch (Context.getLocalType()) {
+			case TInst(_, [target]):
+				var pos = Context.currentPos();
+				var bt = TypeTools.toBaseType(target);
+				var key = ["Foo", bt.module, bt.name].join("__");
+				var ct = TPath({pack: [], name: key});
+
+				if (generated.exists(key)) {
+					if (isAlive(ct, pos)) {
+						print('Reusing previously generated type for $key.');
+						return ct;
+					}
+
+					print('Previously generated type for $key has been discarded.');
+				}
+
+				var genDef = macro class $key {
+					static function __init__() Sys.println("[runtime] Hello from " + $v{key});
+				};
+
+				// Not really needed but nicer
+				// genDef.pos = pos;
+
+				// Not needed unless dce full
+				// genDef.meta.push({name: ":keep", params: [], pos: pos});
+
+				print('Generating type for $key.');
+				#if config.defineModule
+				Context.defineModule(key, [genDef]);
+				#else
+				Context.defineType(genDef, bt.module);
+				#end
+
+				generated.set(key, true);
+				return ct;
+
+			case _: throw "";
+		}
+	}
+}

+ 9 - 0
tests/server/test/templates/csSafeTypeBuilding/Main.hx

@@ -0,0 +1,9 @@
+// Create a dependency to Bar
+import Bar;
+
+typedef A = Foo<Main>;
+
+#if !macro @:build(Macro.logBuild()) #end
+class Main {
+	static function main() Sys.println("[runtime] Hello from Main");
+}

+ 5 - 0
tests/server/test/templates/issues/Issue11460/C.hx

@@ -0,0 +1,5 @@
+class C {
+	static public function doSomething() {
+		trace("Ok I did something");
+	}
+}

+ 3 - 0
tests/server/test/templates/issues/Issue11460/Main.hx

@@ -0,0 +1,3 @@
+function main() {
+	inline C.doSomething();
+}

+ 9 - 0
tests/unit/src/unit/issues/Issue11469.hx

@@ -0,0 +1,9 @@
+package unit.issues;
+
+class Issue11469 extends Test {
+	function test() {
+		static var c = 10;
+		static var d = c + 1;
+		eq(11, d);
+	}
+}