Преглед изворни кода

Inline cleanup (#7155)

* [inliner] factor out vars substitutions

* [inliner] move local handling to a class

* [inliner] factor out locals loop

* [inliner] more cleanup

* [inliner] even more cleanup

* [inliner] ban inline to its own file

* [inliner] rewrite inline flag handling

closes #5436
closes #6711
closes #6715

* [inliner] check dynamic casting in both directions

closes #6706

* [inliner] always map result expression

closes #3881

I know there's a comment there which says that this is very expensive, but I refuse to believe that. Either way, it's dumb that adding useless type parameters to an inline function changes code generation.

* [tests] add test

closes #6047

* [lua] skip casts when checking the `__lua_table__` argument

* [tests] dodge unification issue

* [inliner] handle type parameters of constraints

closes #7134

* [inliner] still only re-unify if there were type parameters

There's nothing to infer otherwise, but we still want to re-map!

* [inliner] merge argument loops

* [lua] fix staticToInstance issue and clean up Reflect a bit
Simon Krajewski пре 7 година
родитељ
комит
784dcbfed0

+ 2 - 2
src/filters/capturedVars.ml

@@ -63,14 +63,14 @@ let captured_vars com e =
 					let t = match v.v_type with TInst (_, [t]) -> t | _ -> assert false in
 					mk (TNew (cnativearray,[t],[eone])) v.v_type p
 				| Some e ->
-					{ (Optimizer.mk_untyped_call "__array__" p [e]) with etype = v.v_type }
+					{ (Inline.mk_untyped_call "__array__" p [e]) with etype = v.v_type }
 
 			method mk_ref_access e v =
 				mk (TArray ({ e with etype = v.v_type }, mk (TConst (TInt 0l)) t.tint e.epos)) e.etype e.epos
 
 			method mk_init av v pos =
 				let elocal = mk (TLocal v) v.v_type pos in
-				let earray = { (Optimizer.mk_untyped_call "__array__" pos [elocal]) with etype = av.v_type } in
+				let earray = { (Inline.mk_untyped_call "__array__" pos [elocal]) with etype = av.v_type } in
 				mk (TVar (av,Some earray)) t.tvoid pos
 		end
 	(* default implementation - use haxe array *)

+ 1 - 1
src/filters/jsExceptions.ml

@@ -147,7 +147,7 @@ let init ctx =
 					let etype = make_typeexpr (module_type_of_type t) e.epos in
 					let args = [eunwrapped;etype] in
 					let echeck =
-						match Optimizer.api_inline ctx cStd "is" args e.epos with
+						match Inline.api_inline ctx cStd "is" args e.epos with
 						| Some e -> e
 						| None ->
 							let eBoot = make_static_this cBoot e.epos in

+ 15 - 9
src/generators/genlua.ml

@@ -407,7 +407,7 @@ and gen_call ctx e el =
          let count = ref 0 in
          spr ctx "({";
          List.iter (fun e ->
-             (match e with
+             (match Texpr.skip e with
               | { eexpr = TArrayDecl arr } ->
                   if (!count > 0 && List.length(arr) > 0) then spr ctx ",";
                   concat ctx "," (gen_value ctx) arr;
@@ -782,7 +782,13 @@ and gen_expr ?(local=true) ctx e = begin
                     (* we have to box it in an object conforming to a multi-return extern class *)
                     let is_boxed_multireturn = Meta.has (Meta.Custom ":lua_mr_box") v.v_meta in
                     let e = if is_boxed_multireturn then mk_mr_box ctx e else e in
-                    gen_value ctx e;
+                    (match e.eexpr with
+                    | TCast ({ eexpr = TTypeExpr mt } as e1, None) when (match mt with TClassDecl {cl_path = ([],"Array")} -> false | _ -> true) ->
+                        add_feature ctx "use._hx_staticToInstance";
+                        spr ctx "_hx_staticToInstance(";
+                        gen_expr ctx e1;
+                        spr ctx ")";
+                    | _ -> gen_value ctx e);
         end
     | TNew (c,_,el) ->
         (match c.cl_constructor with
@@ -1178,13 +1184,6 @@ and gen_value ctx e =
     | TBreak
     | TContinue ->
         unsupported e.epos
-    (* TODO: this is just a hack because this specific case is a TestReflect unit test. I don't know how to address this properly
-       	   at the moment. - Simon *)
-    | TCast ({ eexpr = TTypeExpr mt } as e1, None) when (match mt with TClassDecl {cl_path = ([],"Array")} -> false | _ -> true) ->
-        add_feature ctx "use._hx_staticToInstance";
-        spr ctx "_hx_staticToInstance(";
-        gen_expr ctx e1;
-        spr ctx ")";
     | TCast (e1, Some t) ->
         print ctx "%s.__cast(" (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" }));
         gen_value ctx e1;
@@ -1308,6 +1307,13 @@ and gen_tbinop ctx op e1 e2 =
               spr ctx "_hx_funcToField(";
               gen_value ctx e2;
               spr ctx ")";
+          | _, TCast ({ eexpr = TTypeExpr mt } as e1, None) when (match mt with TClassDecl {cl_path = ([],"Array")} -> false | _ -> true) ->
+              add_feature ctx "use._hx_staticToInstance";
+              gen_value ctx e1;
+              print ctx " %s " (Ast.s_binop op);
+              spr ctx "_hx_staticToInstance(";
+              gen_expr ctx e2;
+              spr ctx ")";
           | _ ->
               gen_value ctx e1;
               print ctx " %s " (Ast.s_binop op);

+ 2 - 2
src/optimization/analyzer.ml

@@ -438,7 +438,7 @@ module ConstPropagation = DataFlow(struct
 				begin match follow e1.etype,eval bb e1 with
 					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i))
 					| _,e1 ->
-						begin match Optimizer.api_inline2 ctx.com c cf.cf_name [wrap e1] e.epos with
+						begin match Inline.api_inline2 ctx.com c cf.cf_name [wrap e1] e.epos with
 							| None -> raise Exit
 							| Some e -> eval bb e
 						end
@@ -446,7 +446,7 @@ module ConstPropagation = DataFlow(struct
 			| TCall ({ eexpr = TField (_,FStatic(c,cf))},el) ->
 				let el = List.map (eval bb) el in
 				let el = List.map wrap el in
-				begin match Optimizer.api_inline2 ctx.com c cf.cf_name el e.epos with
+				begin match Inline.api_inline2 ctx.com c cf.cf_name el e.epos with
 					| None -> raise Exit
 					| Some e -> eval bb e
 				end

+ 714 - 0
src/optimization/inline.ml

@@ -0,0 +1,714 @@
+open Globals
+open Ast
+open Type
+open OptimizerTexpr
+open Common
+open Typecore
+open Error
+
+let mk_untyped_call name p params =
+	{
+		eexpr = TCall({ eexpr = TIdent name; etype = t_dynamic; epos = p }, params);
+		etype = t_dynamic;
+		epos = p;
+	}
+
+let api_inline2 com c field params p =
+	match c.cl_path, field, params with
+	| ([],"Type"),"enumIndex",[{ eexpr = TField (_,FEnum (en,f)) }] -> (match com.platform with
+		| Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
+			(* We don't want to optimize enums from external sources; as they might change unexpectedly *)
+			(* and since native C# enums don't have the concept of index - they have rather a value, *)
+			(* which can't be mapped to a native API - this kind of substitution is dangerous *)
+			None
+		| _ ->
+			Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
+	| ([],"Type"),"enumIndex",[{ eexpr = TCall({ eexpr = TField (_,FEnum (en,f)) },pl) }] when List.for_all (fun e -> not (has_side_effect e)) pl ->
+		(match com.platform with
+			| Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
+				(* see comment above *)
+				None
+			| _ ->
+				Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
+	| ([],"Std"),"int",[{ eexpr = TConst (TInt _) } as e] ->
+		Some { e with epos = p }
+	| ([],"String"),"fromCharCode",[{ eexpr = TConst (TInt i) }] when i > 0l && i < 128l ->
+		Some (mk (TConst (TString (String.make 1 (char_of_int (Int32.to_int i))))) com.basic.tstring p)
+	| ([],"Std"),"string",[{ eexpr = TCast ({ eexpr = TConst c } as e, None)}]
+	| ([],"Std"),"string",[{ eexpr = TConst c } as e] ->
+		(match c with
+		| TString s ->
+			Some { e with epos = p }
+		| TInt i ->
+			Some { eexpr = TConst (TString (Int32.to_string i)); epos = p; etype = com.basic.tstring }
+		| TBool b ->
+			Some { eexpr = TConst (TString (if b then "true" else "false")); epos = p; etype = com.basic.tstring }
+		| _ ->
+			None)
+	| ([],"Std"),"string",[{ eexpr = TIf (_,{ eexpr = TConst (TString _)},Some { eexpr = TConst (TString _) }) } as e] ->
+		Some e
+	| ([],"Std"),"string",[{ eexpr = TLocal v | TField({ eexpr = TLocal v },_) } as ev] when (com.platform = Js || com.platform = Flash) && not (Meta.has Meta.CompilerGenerated v.v_meta) ->
+		let pos = ev.epos in
+		let stringv() =
+			let to_str = mk (TBinop (Ast.OpAdd, mk (TConst (TString "")) com.basic.tstring pos, ev)) com.basic.tstring pos in
+			if com.platform = Js || is_nullable ev.etype then
+				let chk_null = mk (TBinop (Ast.OpEq, ev, mk (TConst TNull) t_dynamic pos)) com.basic.tbool pos in
+				mk (TIf (chk_null, mk (TConst (TString "null")) com.basic.tstring pos, Some to_str)) com.basic.tstring pos
+			else
+				to_str
+		in
+		(match follow ev.etype with
+		| TInst ({ cl_path = [],"String" }, []) ->
+			Some (stringv())
+		| TAbstract ({ a_path = [],"Float" }, []) ->
+			Some (stringv())
+		| TAbstract ({ a_path = [],"Int" }, []) ->
+			Some (stringv())
+		| TAbstract ({ a_path = [],"UInt" }, []) ->
+			Some (stringv())
+		| TAbstract ({ a_path = [],"Bool" }, []) ->
+			Some (stringv())
+		| _ ->
+			None)
+	| ([],"Std"),"int",[{ eexpr = TConst (TFloat f) }] ->
+		let f = float_of_string f in
+		(match classify_float f with
+		| FP_infinite | FP_nan ->
+			None
+		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int +. 1. ->
+			None (* out range, keep platform-specific behavior *)
+		| _ ->
+			Some { eexpr = TConst (TInt (Int32.of_float f)); etype = com.basic.tint; epos = p })
+	| ([],"Math"),"ceil",[{ eexpr = TConst (TFloat f) }] ->
+		let f = float_of_string f in
+		(match classify_float f with
+		| FP_infinite | FP_nan ->
+			None
+		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int ->
+			None (* out range, keep platform-specific behavior *)
+		| _ ->
+			Some { eexpr = TConst (TInt (Int32.of_float (ceil f))); etype = com.basic.tint; epos = p })
+	| ([],"Math"),"floor",[{ eexpr = TConst (TFloat f) }] ->
+		let f = float_of_string f in
+		(match classify_float f with
+		| FP_infinite | FP_nan ->
+			None
+		| _ when f <= Int32.to_float Int32.min_int || f >= Int32.to_float Int32.max_int +. 1. ->
+			None (* out range, keep platform-specific behavior *)
+		| _ ->
+			Some { eexpr = TConst (TInt (Int32.of_float (floor f))); etype = com.basic.tint; epos = p })
+	| (["cs"],"Lib"),("fixed" | "checked" | "unsafe"),[e] ->
+			Some (mk_untyped_call ("__" ^ field ^ "__") p [e])
+	| (["cs"],"Lib"),("lock"),[obj;block] ->
+			Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
+	| (["java"],"Lib"),("lock"),[obj;block] ->
+			Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
+	| _ ->
+		None
+
+let api_inline ctx c field params p = match c.cl_path, field, params with
+	| ([],"Std"),"is",[o;t] | (["js"],"Boot"),"__instanceof",[o;t] when ctx.com.platform = Js ->
+		let tstring = ctx.com.basic.tstring in
+		let tbool = ctx.com.basic.tbool in
+		let tint = ctx.com.basic.tint in
+
+		let esyntax =
+			let m = Hashtbl.find ctx.g.modules (["js"],"Syntax") in
+			ExtList.List.find_map (function
+				| TClassDecl ({ cl_path = ["js"],"Syntax" } as cl) -> Some (make_static_this cl p)
+				| _ -> None
+			) m.m_types
+		in
+
+		let is_trivial e =
+			match e.eexpr with
+			| TConst _ | TLocal _ -> true
+			| _ -> false
+		in
+
+		let typeof t =
+			let tof = Texpr.Builder.fcall esyntax "typeof" [o] tstring p in
+			mk (TBinop (Ast.OpEq, tof, (mk (TConst (TString t)) tstring p))) tbool p
+		in
+
+		(match t.eexpr with
+		(* generate simple typeof checks for basic types *)
+		| TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
+		| TTypeExpr (TAbstractDecl ({ a_path = [],"Bool" })) -> Some (typeof "boolean")
+		| TTypeExpr (TAbstractDecl ({ a_path = [],"Float" })) -> Some (typeof "number")
+		| TTypeExpr (TAbstractDecl ({ a_path = [],"Int" })) when is_trivial o ->
+			(* generate typeof(o) == "number" && (o|0) === o check *)
+			let lhs = mk (TBinop (Ast.OpOr, o, mk (TConst (TInt Int32.zero)) tint p)) tint p in
+			let jscheck = Texpr.Builder.fcall esyntax "strictEq" [lhs;o] tbool p in
+			Some(mk (TBinop (Ast.OpBoolAnd, typeof "number", jscheck)) tbool p)
+		| TTypeExpr (TClassDecl ({ cl_path = [],"Array" })) ->
+			(* generate (o instanceof Array) && o.__enum__ == null check *)
+			let iof = Texpr.Builder.fcall esyntax "instanceof" [o;t] tbool p in
+			let enum = mk (TField (o, FDynamic "__enum__")) (mk_mono()) p in
+			let null = mk (TConst TNull) (mk_mono()) p in
+			let not_enum = mk (TBinop (Ast.OpEq, enum, null)) tbool p in
+			Some (mk (TBinop (Ast.OpBoolAnd, iof, not_enum)) tbool p)
+		| TTypeExpr (TClassDecl cls) when not cls.cl_interface ->
+			Some (Texpr.Builder.fcall esyntax "instanceof" [o;t] tbool p)
+		| _ ->
+			None)
+	| (["cs" | "java"],"Lib"),("nativeArray"),[{ eexpr = TArrayDecl args } as edecl; _]
+	| (["haxe";"ds";"_Vector"],"Vector_Impl_"),("fromArrayCopy"),[{ eexpr = TArrayDecl args } as edecl] -> (try
+			let platf = match ctx.com.platform with
+				| Cs -> "cs"
+				| Java -> "java"
+				| _ -> raise Exit
+			in
+			let mpath = if field = "fromArrayCopy" then
+				(["haxe";"ds"],"Vector")
+			else
+				([platf],"NativeArray")
+			in
+
+			let m = ctx.g.do_load_module ctx mpath null_pos in
+			let main = List.find (function | TClassDecl _ | TAbstractDecl _ -> true | _ -> false) m.m_types in
+			let t = match follow edecl.etype, main with
+				| TInst({ cl_path = [],"Array" }, [t]), TClassDecl(cl) ->
+					TInst(cl,[t])
+				| TInst({ cl_path = [],"Array" }, [t]), TAbstractDecl(a) ->
+					TAbstract(a,[t])
+				| _ -> assert false
+			in
+			Some ({ (mk_untyped_call "__array__" p args) with etype = t })
+		with | Exit ->
+			None)
+	| _ ->
+		api_inline2 ctx.com c field params p
+
+type in_local = {
+	i_var : tvar;
+	i_subst : tvar;
+	i_outside : bool;
+	i_abstract_this : bool;
+	mutable i_captured : bool;
+	mutable i_write : bool;
+	mutable i_read : int;
+	mutable i_force_temp : bool;
+}
+
+type var_inline_kind =
+	| VIInline
+	| VIInlineIfCalled
+	| VIDoNotInline
+
+let inline_default_config cf t =
+	(* type substitution on both class and function type parameters *)
+	let rec get_params c pl =
+		match c.cl_super with
+		| None -> c.cl_params, pl
+		| Some (csup,spl) ->
+			let spl = (match apply_params c.cl_params pl (TInst (csup,spl)) with
+			| TInst (_,pl) -> pl
+			| _ -> assert false
+			) in
+			let ct, cpl = get_params csup spl in
+			c.cl_params @ ct, pl @ cpl
+	in
+	let rec loop t = match follow t with
+		| TInst({cl_kind = KTypeParameter tl},_) -> List.fold_left (fun (params',tl') (params,tl) -> (params @ params',tl @ tl')) ([],[]) (List.map loop tl)
+		| TInst (c,pl) -> get_params c pl
+		| _ -> ([],[])
+	in
+	let tparams = loop t in
+	let pmonos = List.map (fun _ -> mk_mono()) cf.cf_params in
+	let tmonos = snd tparams @ pmonos in
+	let tparams = fst tparams @ cf.cf_params in
+	tparams <> [], apply_params tparams tmonos
+
+let inline_metadata e meta =
+	let inline_meta e meta = match meta with
+		| (Meta.Deprecated | Meta.Pure),_,_ -> mk (TMeta(meta,e)) e.etype e.epos
+		| _ -> e
+	in
+	List.fold_left inline_meta e meta
+
+class inline_state ctx ethis params cf f p = object(self)
+	val locals = Hashtbl.create 0
+	val checker = create_affection_checker()
+	val mutable _inlined_vars = []
+	val mutable _in_local_fun = false
+	val mutable _had_side_effect = false
+	val mutable _has_return_value = false
+
+	method enter_local_fun =
+		let old = _in_local_fun in
+		_in_local_fun <- true;
+		(fun () -> _in_local_fun <- old)
+
+	method in_local_fun = _in_local_fun
+
+	method inlined_vars = _inlined_vars
+
+	method had_side_effect = _had_side_effect
+	method set_side_effect = _had_side_effect <- true
+
+	method has_return_value = _has_return_value
+	method set_return_value = _has_return_value <- true
+
+	method private collect_modified_locals e = (snd checker) e
+	method might_be_affected e = (fst checker) e
+
+	method declare v =
+		try
+			Hashtbl.find locals v.v_id
+		with Not_found ->
+			let v' = alloc_var v.v_name v.v_type v.v_pos in
+			v'.v_extra <- v.v_extra;
+			let i = {
+				i_var = v;
+				i_subst = v';
+				i_outside = false;
+				i_abstract_this = Meta.has Meta.This v.v_meta;
+				i_captured = false;
+				i_write = false;
+				i_force_temp = false;
+				i_read = 0;
+			} in
+			i.i_subst.v_meta <- List.filter (fun (m,_,_) -> m <> Meta.This) v.v_meta;
+			Hashtbl.add locals v.v_id i;
+			Hashtbl.add locals i.i_subst.v_id i;
+			i
+
+	method read v =
+		let l = try
+			Hashtbl.find locals v.v_id
+		with Not_found ->
+			{
+				i_var = v;
+				i_subst = v;
+				i_outside = true;
+				i_abstract_this = Meta.has Meta.This v.v_meta;
+				i_captured = false;
+				i_write = false;
+				i_force_temp = false;
+				i_read = 0;
+			}
+		in
+		if _in_local_fun then l.i_captured <- true;
+		l
+
+	method private get_substitutions p =
+		(*
+			if variables are not written and used with a const value, let's substitute
+			with the actual value, either create a temp var
+		*)
+		let subst = ref PMap.empty in
+		let is_constant e =
+			let rec loop e =
+				match e.eexpr with
+				| TLocal _
+				| TConst TThis (* not really, but should not be move inside a function body *)
+					-> raise Exit
+				| TObjectDecl _ | TArrayDecl _ -> raise Exit
+				| TField (_,FEnum _)
+				| TTypeExpr _
+				| TConst _ -> ()
+				| _ ->
+					Type.iter loop e
+			in
+			try loop e; true with Exit -> false
+		in
+		let is_writable e =
+			match e.eexpr with
+			| TField _ | TEnumParameter _ | TLocal _ | TArray _ -> true
+			| _  -> false
+		in
+		let vars = List.fold_left (fun acc (i,e) ->
+			let accept vik =
+				subst := PMap.add i.i_subst.v_id (vik,e) !subst;
+				acc
+			in
+			let reject () =
+				(* mark the replacement local for the analyzer *)
+				if i.i_read <= 1 && not i.i_write then
+					i.i_subst.v_meta <- (Meta.CompilerGenerated,[],p) :: i.i_subst.v_meta;
+				(i.i_subst,Some e) :: acc
+			in
+			if i.i_abstract_this && i.i_write then begin
+				if not (is_writable e) then error "Cannot modify the abstract value, store it into a local first" p;
+				accept VIInline
+			end else if i.i_force_temp || (i.i_captured && not (is_constant e)) then
+				reject()
+			else begin
+				let vik = match e.eexpr with
+					| TLocal _ when i.i_abstract_this -> VIInline
+					| TLocal _ | TConst _ ->
+						if not i.i_write then VIInline else VIDoNotInline
+					| TFunction _ ->
+						if i.i_write then error "Cannot modify a closure parameter inside inline method" p;
+						if i.i_read <= 1 then VIInline else VIInlineIfCalled
+					| _ ->
+						if not i.i_write && i.i_read <= 1 then VIInline else VIDoNotInline
+				in
+				match vik with
+				| VIInline -> accept vik
+				| VIDoNotInline -> reject()
+				| VIInlineIfCalled ->
+					(* "Accept" it so it is added to the substitutions. *)
+					ignore(accept vik);
+					(* But actually reject it so we get a proper variable. The analyzer will clean it up if it's unused. *)
+					reject();
+			end
+		) [] _inlined_vars in
+		vars,!subst
+
+	method initialize =
+		(* use default values for null/unset arguments *)
+		let rec loop acc pl al first =
+			match pl, al with
+			| _, [] ->
+				acc
+			| e :: pl, (v, opt) :: al ->
+				let l = self#declare v in
+				(*
+					if we pass a Null<T> var to an inlined method that needs a T.
+					we need to force a local var to be created on some platforms.
+				*)
+				if ctx.com.config.pf_static && not (is_nullable v.v_type) && is_null e.etype then l.i_force_temp <- true;
+				(*
+					if we cast from Dynamic, create a local var as well to do the cast
+					once and allow DCE to perform properly.
+				*)
+				let dynamic_v = follow v.v_type == t_dynamic in
+				let dynamic_e = follow e.etype == t_dynamic in
+				let e = if dynamic_v <> dynamic_e then mk (TCast(e,None)) v.v_type e.epos else e in
+				let e = match e.eexpr, opt with
+					| TConst TNull , Some c -> mk (TConst c) v.v_type e.epos
+					| _ -> e
+				in
+				if has_side_effect e then begin
+					self#collect_modified_locals e;
+					_had_side_effect <- true;
+					l.i_force_temp <- true;
+				end;
+				if l.i_abstract_this then l.i_subst.v_extra <- Some ([],Some e);
+				loop ((l,e) :: acc) pl al false
+			| [], (v,opt) :: al ->
+				let l = self#declare v in
+				let e = mk (TConst (match opt with None -> TNull | Some c -> c)) v.v_type p in
+				loop ((l,e) :: acc) [] al false
+		in
+		(*
+			Build the expr/var subst list
+		*)
+		let ethis = (match ethis.eexpr with TConst TSuper -> { ethis with eexpr = TConst TThis } | _ -> ethis) in
+		let vthis = alloc_var "_this" ethis.etype ethis.epos in
+		let args1 = (ethis :: params) in
+		let args2 = ((vthis,None) :: f.tf_args) in
+		let vars = loop [] args1 args2 true in
+		_inlined_vars <- vars; (* Order is reversed due to tail-recursion *)
+		vthis
+
+	method finalize config e tl tret p =
+		let has_params,map_type = match config with Some config -> config | None -> inline_default_config cf ethis.etype in
+		if self#had_side_effect then List.iter (fun (l,e) ->
+			if self#might_be_affected e then l.i_force_temp <- true;
+		) _inlined_vars;
+		let vars,subst = self#get_substitutions p in
+		let rec inline_params in_call e =
+			match e.eexpr with
+			| TLocal v ->
+				begin try
+					let vik,e' = PMap.find v.v_id subst in
+					begin match vik with
+						| VIInline -> e'
+						| VIInlineIfCalled when in_call ->
+							(* We allow inlining function expressions into call-places. However, we have to substitute
+							   their locals to avoid duplicate declarations. *)
+							Texpr.duplicate_tvars e'
+						| _ -> e
+					end
+				with Not_found ->
+					e
+				end
+			| TCall(e1,el) ->
+				let e1 = inline_params true e1 in
+				let el = List.map (inline_params false) el in
+				{e with eexpr = TCall(e1,el)}
+			| _ -> Type.map_expr (inline_params false) e
+		in
+		let e = (if PMap.is_empty subst then e else inline_params false e) in
+		let init = match vars with [] -> None | l -> Some l in
+		let md = ctx.curclass.cl_module.m_extra.m_display in
+		md.m_inline_calls <- (cf.cf_name_pos,{p with pmax = p.pmin + String.length cf.cf_name}) :: md.m_inline_calls;
+		let wrap e =
+			(* we can't mute the type of the expression because it is not correct to do so *)
+			let etype = if has_params then map_type e.etype else e.etype in
+			(* if the expression is "untyped" and we don't want to unify it accidentally ! *)
+			try (match follow e.etype with
+			| TMono _ | TInst ({cl_kind = KTypeParameter _ },_) ->
+				(match follow tret with
+				| TAbstract ({ a_path = [],"Void" },_) -> e
+				| _ -> raise (Unify_error []))
+			| _ ->
+				type_eq (if ctx.com.config.pf_static then EqDoNotFollowNull else EqStrict) etype tret;
+				e)
+			with Unify_error _ ->
+				mk (TCast (e,None)) tret e.epos
+		in
+		let e = (match e.eexpr, init with
+			| _, None when not self#has_return_value ->
+				begin match e.eexpr with
+					| TBlock _ -> {e with etype = tret}
+					| _ -> mk (TBlock [e]) tret e.epos
+				end
+			| TBlock [e] , None -> wrap e
+			| _ , None -> wrap e
+			| TBlock l, Some vl ->
+				let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
+				mk (TBlock (el_v @ l)) tret e.epos
+			| _, Some vl ->
+				let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
+				mk (TBlock (el_v @ [e])) tret e.epos
+		) in
+		let e = inline_metadata e cf.cf_meta in
+		let e = Diagnostics.secure_generated_code ctx e in
+		if has_params then begin
+			let mt = map_type cf.cf_type in
+			let unify_func () = unify_raise ctx mt (TFun (tl,tret)) p in
+			(match follow ethis.etype with
+			| TAnon a -> (match !(a.a_status) with
+				| Statics {cl_kind = KAbstractImpl a } when Meta.has Meta.Impl cf.cf_meta ->
+					if cf.cf_name <> "_new" then begin
+						(* the first argument must unify with a_this for abstract implementation functions *)
+						let tb = (TFun(("",false,map_type a.a_this) :: (List.tl tl),tret)) in
+						unify_raise ctx mt tb p
+					end
+				| _ -> unify_func())
+			| _ -> unify_func());
+		end;
+		let vars = Hashtbl.create 0 in
+		let map_var v =
+			if not (Hashtbl.mem vars v.v_id) then begin
+				Hashtbl.add vars v.v_id ();
+				if not (self#read v).i_outside then v.v_type <- map_type v.v_type;
+			end;
+			v
+		in
+		let rec map_expr_type e = Type.map_expr_type map_expr_type map_type map_var e in
+		map_expr_type e
+end
+
+let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=false) force =
+	(* perform some specific optimization before we inline the call since it's not possible to detect at final optimization time *)
+	try
+		let cl = (match follow ethis.etype with
+			| TInst (c,_) -> c
+			| TAnon a -> (match !(a.a_status) with Statics c -> c | _ -> raise Exit)
+			| _ -> raise Exit
+		) in
+		(match api_inline ctx cl cf.cf_name params p with
+		| None -> raise Exit
+		| Some e -> Some e)
+	with Exit ->
+	let state = new inline_state ctx ethis params cf f p in
+	let vthis = state#initialize in
+	let opt f = function
+		| None -> None
+		| Some e -> Some (f e)
+	in
+	let in_loop = ref false in
+	let cancel_inlining = ref false in
+	let return_type t el =
+		(* If the function return is Dynamic or Void, stick to it. *)
+		if follow f.tf_type == t_dynamic || ExtType.is_void (follow f.tf_type) then f.tf_type
+		(* If the expression is Void, find common type of its branches. *)
+		else if ExtType.is_void t then unify_min ctx el
+		else t
+	in
+	let map_pos = if self_calling_closure then (fun e -> e) else (fun e -> { e with epos = p }) in
+	let rec map term e =
+		let po = e.epos in
+		let e = map_pos e in
+		match e.eexpr with
+		| TLocal v ->
+			let l = state#read v in
+			l.i_read <- l.i_read + (if !in_loop then 2 else 1);
+			(* never inline a function which contain a delayed macro because its bound
+				to its variables and not the calling method *)
+			if v.v_name = "$__delayed_call__" then cancel_inlining := true;
+			let e = { e with eexpr = TLocal l.i_subst } in
+			if l.i_abstract_this then mk (TCast(e,None)) v.v_type e.epos else e
+		| TConst TThis ->
+			let l = state#read vthis in
+			l.i_read <- l.i_read + (if !in_loop then 2 else 1);
+			{ e with eexpr = TLocal l.i_subst }
+		| TVar (v,eo) ->
+			{ e with eexpr = TVar ((state#declare v).i_subst,opt (map false) eo)}
+		| TReturn eo when not state#in_local_fun ->
+			if not term then error "Cannot inline a not final return" po;
+			(match eo with
+			| None -> mk (TConst TNull) f.tf_type p
+			| Some e ->
+				state#set_return_value;
+				map term e)
+		| TFor (v,e1,e2) ->
+			let i = state#declare v in
+			let e1 = map false e1 in
+			let old = !in_loop in
+			in_loop := true;
+			let e2 = map false e2 in
+			in_loop := old;
+			{ e with eexpr = TFor (i.i_subst,e1,e2) }
+		| TWhile (cond,eloop,flag) ->
+			let cond = map false cond in
+			let old = !in_loop in
+			in_loop := true;
+			let eloop = map false eloop in
+			in_loop := old;
+			{ e with eexpr = TWhile (cond,eloop,flag) }
+		| TSwitch (e1,cases,def) when term ->
+			let term = term && (def <> None || is_exhaustive e1) in
+			let cases = List.map (fun (el,e) ->
+				let el = List.map (map false) el in
+				el, map term e
+			) cases in
+			let def = opt (map term) def in
+			let t = return_type e.etype ((List.map snd cases) @ (match def with None -> [] | Some e -> [e])) in
+			{ e with eexpr = TSwitch (map false e1,cases,def); etype = t }
+		| TTry (e1,catches) ->
+			let t = if not term then e.etype else return_type e.etype (e1::List.map snd catches) in
+			{ e with eexpr = TTry (map term e1,List.map (fun (v,e) ->
+				let lv = (state#declare v).i_subst in
+				let e = map term e in
+				lv,e
+			) catches); etype = t }
+		| TBlock l ->
+			let old = save_locals ctx in
+			let t = ref e.etype in
+			let rec has_term_return e =
+				let rec loop e =
+					let r = match e.eexpr with
+					| TReturn _ -> true
+					| TFunction _ -> false
+					| TIf (_,_,None) | TSwitch (_,_,None) | TFor _ | TWhile (_,_,NormalWhile) -> false (* we might not enter this code at all *)
+					| TTry (a, catches) -> List.for_all has_term_return (a :: List.map snd catches)
+					| TIf (cond,a,Some b) -> has_term_return cond || (has_term_return a && has_term_return b)
+					| TSwitch (cond,cases,Some def) -> has_term_return cond || List.for_all has_term_return (def :: List.map snd cases)
+					| TBinop (OpBoolAnd,a,b) -> has_term_return a && has_term_return b
+					| _ -> Type.iter loop e; false
+					in
+					if r then raise Exit
+				in
+				try loop e; false with Exit -> true
+			in
+			let rec loop = function
+				| [] when term ->
+					t := mk_mono();
+					[mk (TConst TNull) (!t) p]
+				| [] -> []
+				| [e] ->
+					let e = map term e in
+					if term then t := e.etype;
+					[e]
+				| ({ eexpr = TIf (cond,e1,None) } as e) :: l when term && has_term_return e1 ->
+					loop [{ e with eexpr = TIf (cond,e1,Some (mk (TBlock l) e.etype e.epos)); epos = punion e.epos (match List.rev l with e :: _ -> e.epos | [] -> assert false) }]
+				| e :: l ->
+					let e = map false e in
+					e :: loop l
+			in
+			let l = loop l in
+			old();
+			{ e with eexpr = TBlock l; etype = !t }
+		| TIf (econd,eif,Some eelse) when term ->
+			let econd = map false econd in
+			let eif = map term eif in
+			let eelse = map term eelse in
+			let t = return_type e.etype [eif;eelse] in
+			{ e with eexpr = TIf(econd,eif,Some eelse); etype = t }
+		| TParenthesis e1 ->
+			let e1 = map term e1 in
+			mk (TParenthesis e1) e1.etype e.epos
+		| TUnop ((Increment|Decrement) as op,flag,({ eexpr = TLocal v } as e1)) ->
+			state#set_side_effect;
+			let l = state#read v in
+			l.i_write <- true;
+			{e with eexpr = TUnop(op,flag,{e1 with eexpr = TLocal l.i_subst})}
+		| TBinop ((OpAssign | OpAssignOp _) as op,({ eexpr = TLocal v } as e1),e2) ->
+			state#set_side_effect;
+			let l = state#read v in
+			l.i_write <- true;
+			let e2 = map false e2 in
+			{e with eexpr = TBinop(op,{e1 with eexpr = TLocal l.i_subst},e2)}
+		| TObjectDecl fl ->
+			let fl = List.map (fun (s,e) -> s,map false e) fl in
+			begin match follow e.etype with
+				| TAnon an when (match !(an.a_status) with Const -> true | _ -> false) ->
+					{e with eexpr = TObjectDecl fl; etype = TAnon { an with a_status = ref Closed}}
+				| _ ->
+					{e with eexpr = TObjectDecl fl}
+			end
+		| TFunction f ->
+			let old = save_locals ctx in
+			let args = List.map (function(v,c) -> (state#declare v).i_subst, c) f.tf_args in
+			let restore = state#enter_local_fun in
+			let expr = map false f.tf_expr in
+			restore();
+			old();
+			{ e with eexpr = TFunction { tf_args = args; tf_expr = expr; tf_type = f.tf_type } }
+		| TCall({eexpr = TConst TSuper; etype = t},el) ->
+			state#set_side_effect;
+			begin match follow t with
+			| TInst({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,_) ->
+				begin match type_inline_ctor ctx c cf tf ethis el po with
+				| Some e -> map term e
+				| None -> error "Could not inline super constructor call" po
+				end
+			| _ -> error "Cannot inline function containing super" po
+			end
+		| TConst TSuper ->
+			error "Cannot inline function containing super" po
+		| TMeta(m,e1) ->
+			let e1 = map term e1 in
+			{e with eexpr = TMeta(m,e1)}
+		| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) ->
+			state#set_side_effect;
+			Type.map_expr (map false) e
+		| _ ->
+			Type.map_expr (map false) e
+	in
+	let e = map true f.tf_expr in
+	if !cancel_inlining then
+		None
+	else begin
+	let tl = List.map (fun e -> "",false,e.etype) params in
+		let e = state#finalize config e tl tret p in
+		if Meta.has (Meta.Custom ":inlineDebug") ctx.meta then begin
+			let se t = s_expr_pretty true t true (s_type (print_context())) in
+			print_endline (Printf.sprintf "Inline %s:\n\tArgs: %s\n\tExpr: %s\n\tResult: %s"
+				cf.cf_name
+				(String.concat "" (List.map (fun (i,e) -> Printf.sprintf "\n\t\t%s<%i> = %s" (i.i_subst.v_name) (i.i_subst.v_id) (se "\t\t" e)) state#inlined_vars))
+				(se "\t" f.tf_expr)
+				(se "\t" e)
+			);
+		end;
+		Some e
+	end
+
+(* Same as type_inline, but modifies the function body to add field inits *)
+and type_inline_ctor ctx c cf tf ethis el po =
+	let field_inits =
+		let cparams = List.map snd c.cl_params in
+		let ethis = mk (TConst TThis) (TInst (c,cparams)) c.cl_pos in
+		let el = List.fold_left (fun acc cf ->
+			match cf.cf_kind,cf.cf_expr with
+			| Var _,Some e ->
+				let lhs = mk (TField(ethis,FInstance (c,cparams,cf))) cf.cf_type e.epos in
+				let eassign = mk (TBinop(OpAssign,lhs,e)) cf.cf_type e.epos in
+				eassign :: acc
+			| _ -> acc
+		) [] c.cl_ordered_fields in
+		List.rev el
+	in
+	let tf =
+		if field_inits = [] then tf
+		else
+			let bl = match tf.tf_expr with {eexpr = TBlock b } -> b | x -> [x] in
+			{tf with tf_expr = mk (TBlock (field_inits @ bl)) ctx.t.tvoid c.cl_pos}
+	in
+	type_inline ctx cf tf ethis el ctx.t.tvoid None po true

+ 1 - 1
src/optimization/inlineConstructors.ml

@@ -242,7 +242,7 @@ let inline_constructors ctx e =
 				let argvs, argvdecls, pl = loop ([],[],[]) pl in
 				let _, cname = c.cl_path in
 				let v = alloc_var ("inl"^cname) e.etype e.epos in
-				match Optimizer.type_inline_ctor ctx c cf tf (mk (TLocal v) (TInst (c,tl)) e.epos) pl e.epos with
+				match Inline.type_inline_ctor ctx c cf tf (mk (TLocal v) (TInst (c,tl)) e.epos) pl e.epos with
 				| Some inlined_expr ->
 					let has_untyped = (Meta.has Meta.HasUntyped cf.cf_meta) in
 					let io = mk_io (IOKCtor(cf,is_extern_ctor c cf,argvs)) io_id inlined_expr ~has_untyped:has_untyped in

+ 1 - 666
src/optimization/optimizer.ml

@@ -24,672 +24,7 @@ open Typecore
 open OptimizerTexpr
 open Error
 open Globals
-
-(* ---------------------------------------------------------------------- *)
-(* API OPTIMIZATIONS *)
-
-let mk_untyped_call name p params =
-	{
-		eexpr = TCall({ eexpr = TIdent name; etype = t_dynamic; epos = p }, params);
-		etype = t_dynamic;
-		epos = p;
-	}
-
-let api_inline2 com c field params p =
-	match c.cl_path, field, params with
-	| ([],"Type"),"enumIndex",[{ eexpr = TField (_,FEnum (en,f)) }] -> (match com.platform with
-		| Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
-			(* We don't want to optimize enums from external sources; as they might change unexpectedly *)
-			(* and since native C# enums don't have the concept of index - they have rather a value, *)
-			(* which can't be mapped to a native API - this kind of substitution is dangerous *)
-			None
-		| _ ->
-			Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
-	| ([],"Type"),"enumIndex",[{ eexpr = TCall({ eexpr = TField (_,FEnum (en,f)) },pl) }] when List.for_all (fun e -> not (has_side_effect e)) pl ->
-		(match com.platform with
-			| Cs when en.e_extern && not (Meta.has Meta.HxGen en.e_meta) ->
-				(* see comment above *)
-				None
-			| _ ->
-				Some (mk (TConst (TInt (Int32.of_int f.ef_index))) com.basic.tint p))
-	| ([],"Std"),"int",[{ eexpr = TConst (TInt _) } as e] ->
-		Some { e with epos = p }
-	| ([],"String"),"fromCharCode",[{ eexpr = TConst (TInt i) }] when i > 0l && i < 128l ->
-		Some (mk (TConst (TString (String.make 1 (char_of_int (Int32.to_int i))))) com.basic.tstring p)
-	| ([],"Std"),"string",[{ eexpr = TCast ({ eexpr = TConst c } as e, None)}]
-	| ([],"Std"),"string",[{ eexpr = TConst c } as e] ->
-		(match c with
-		| TString s ->
-			Some { e with epos = p }
-		| TInt i ->
-			Some { eexpr = TConst (TString (Int32.to_string i)); epos = p; etype = com.basic.tstring }
-		| TBool b ->
-			Some { eexpr = TConst (TString (if b then "true" else "false")); epos = p; etype = com.basic.tstring }
-		| _ ->
-			None)
-	| ([],"Std"),"string",[{ eexpr = TIf (_,{ eexpr = TConst (TString _)},Some { eexpr = TConst (TString _) }) } as e] ->
-		Some e
-	| ([],"Std"),"string",[{ eexpr = TLocal v | TField({ eexpr = TLocal v },_) } as ev] when (com.platform = Js || com.platform = Flash) && not (Meta.has Meta.CompilerGenerated v.v_meta) ->
-		let pos = ev.epos in
-		let stringv() =
-			let to_str = mk (TBinop (Ast.OpAdd, mk (TConst (TString "")) com.basic.tstring pos, ev)) com.basic.tstring pos in
-			if com.platform = Js || is_nullable ev.etype then
-				let chk_null = mk (TBinop (Ast.OpEq, ev, mk (TConst TNull) t_dynamic pos)) com.basic.tbool pos in
-				mk (TIf (chk_null, mk (TConst (TString "null")) com.basic.tstring pos, Some to_str)) com.basic.tstring pos
-			else
-				to_str
-		in
-		(match follow ev.etype with
-		| TInst ({ cl_path = [],"String" }, []) ->
-			Some (stringv())
-		| TAbstract ({ a_path = [],"Float" }, []) ->
-			Some (stringv())
-		| TAbstract ({ a_path = [],"Int" }, []) ->
-			Some (stringv())
-		| TAbstract ({ a_path = [],"UInt" }, []) ->
-			Some (stringv())
-		| TAbstract ({ a_path = [],"Bool" }, []) ->
-			Some (stringv())
-		| _ ->
-			None)
-	| ([],"Std"),"int",[{ eexpr = TConst (TFloat f) }] ->
-		let f = float_of_string f in
-		(match classify_float f with
-		| FP_infinite | FP_nan ->
-			None
-		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int +. 1. ->
-			None (* out range, keep platform-specific behavior *)
-		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float f)); etype = com.basic.tint; epos = p })
-	| ([],"Math"),"ceil",[{ eexpr = TConst (TFloat f) }] ->
-		let f = float_of_string f in
-		(match classify_float f with
-		| FP_infinite | FP_nan ->
-			None
-		| _ when f <= Int32.to_float Int32.min_int -. 1. || f >= Int32.to_float Int32.max_int ->
-			None (* out range, keep platform-specific behavior *)
-		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float (ceil f))); etype = com.basic.tint; epos = p })
-	| ([],"Math"),"floor",[{ eexpr = TConst (TFloat f) }] ->
-		let f = float_of_string f in
-		(match classify_float f with
-		| FP_infinite | FP_nan ->
-			None
-		| _ when f <= Int32.to_float Int32.min_int || f >= Int32.to_float Int32.max_int +. 1. ->
-			None (* out range, keep platform-specific behavior *)
-		| _ ->
-			Some { eexpr = TConst (TInt (Int32.of_float (floor f))); etype = com.basic.tint; epos = p })
-	| (["cs"],"Lib"),("fixed" | "checked" | "unsafe"),[e] ->
-			Some (mk_untyped_call ("__" ^ field ^ "__") p [e])
-	| (["cs"],"Lib"),("lock"),[obj;block] ->
-			Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
-	| (["java"],"Lib"),("lock"),[obj;block] ->
-			Some (mk_untyped_call ("__lock__") p [obj;mk_block block])
-	| _ ->
-		None
-
-let api_inline ctx c field params p = match c.cl_path, field, params with
-	| ([],"Std"),"is",[o;t] | (["js"],"Boot"),"__instanceof",[o;t] when ctx.com.platform = Js ->
-		let tstring = ctx.com.basic.tstring in
-		let tbool = ctx.com.basic.tbool in
-		let tint = ctx.com.basic.tint in
-
-		let esyntax =
-			let m = Hashtbl.find ctx.g.modules (["js"],"Syntax") in
-			ExtList.List.find_map (function
-				| TClassDecl ({ cl_path = ["js"],"Syntax" } as cl) -> Some (make_static_this cl p)
-				| _ -> None
-			) m.m_types
-		in
-
-		let is_trivial e =
-			match e.eexpr with
-			| TConst _ | TLocal _ -> true
-			| _ -> false
-		in
-
-		let typeof t =
-			let tof = Texpr.Builder.fcall esyntax "typeof" [o] tstring p in
-			mk (TBinop (Ast.OpEq, tof, (mk (TConst (TString t)) tstring p))) tbool p
-		in
-
-		(match t.eexpr with
-		(* generate simple typeof checks for basic types *)
-		| TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
-		| TTypeExpr (TAbstractDecl ({ a_path = [],"Bool" })) -> Some (typeof "boolean")
-		| TTypeExpr (TAbstractDecl ({ a_path = [],"Float" })) -> Some (typeof "number")
-		| TTypeExpr (TAbstractDecl ({ a_path = [],"Int" })) when is_trivial o ->
-			(* generate typeof(o) == "number" && (o|0) === o check *)
-			let lhs = mk (TBinop (Ast.OpOr, o, mk (TConst (TInt Int32.zero)) tint p)) tint p in
-			let jscheck = Texpr.Builder.fcall esyntax "strictEq" [lhs;o] tbool p in
-			Some(mk (TBinop (Ast.OpBoolAnd, typeof "number", jscheck)) tbool p)
-		| TTypeExpr (TClassDecl ({ cl_path = [],"Array" })) ->
-			(* generate (o instanceof Array) && o.__enum__ == null check *)
-			let iof = Texpr.Builder.fcall esyntax "instanceof" [o;t] tbool p in
-			let enum = mk (TField (o, FDynamic "__enum__")) (mk_mono()) p in
-			let null = mk (TConst TNull) (mk_mono()) p in
-			let not_enum = mk (TBinop (Ast.OpEq, enum, null)) tbool p in
-			Some (mk (TBinop (Ast.OpBoolAnd, iof, not_enum)) tbool p)
-		| TTypeExpr (TClassDecl cls) when not cls.cl_interface ->
-			Some (Texpr.Builder.fcall esyntax "instanceof" [o;t] tbool p)
-		| _ ->
-			None)
-	| (["cs" | "java"],"Lib"),("nativeArray"),[{ eexpr = TArrayDecl args } as edecl; _]
-	| (["haxe";"ds";"_Vector"],"Vector_Impl_"),("fromArrayCopy"),[{ eexpr = TArrayDecl args } as edecl] -> (try
-			let platf = match ctx.com.platform with
-				| Cs -> "cs"
-				| Java -> "java"
-				| _ -> raise Exit
-			in
-			let mpath = if field = "fromArrayCopy" then
-				(["haxe";"ds"],"Vector")
-			else
-				([platf],"NativeArray")
-			in
-
-			let m = ctx.g.do_load_module ctx mpath null_pos in
-			let main = List.find (function | TClassDecl _ | TAbstractDecl _ -> true | _ -> false) m.m_types in
-			let t = match follow edecl.etype, main with
-				| TInst({ cl_path = [],"Array" }, [t]), TClassDecl(cl) ->
-					TInst(cl,[t])
-				| TInst({ cl_path = [],"Array" }, [t]), TAbstractDecl(a) ->
-					TAbstract(a,[t])
-				| _ -> assert false
-			in
-			Some ({ (mk_untyped_call "__array__" p args) with etype = t })
-		with | Exit ->
-			None)
-	| _ ->
-		api_inline2 ctx.com c field params p
-
-(* ---------------------------------------------------------------------- *)
-(* INLINING *)
-
-type in_local = {
-	i_var : tvar;
-	i_subst : tvar;
-	i_outside : bool;
-	i_abstract_this : bool;
-	mutable i_captured : bool;
-	mutable i_write : bool;
-	mutable i_read : int;
-	mutable i_force_temp : bool;
-}
-
-let inline_default_config cf t =
-	(* type substitution on both class and function type parameters *)
-	let rec get_params c pl =
-		match c.cl_super with
-		| None -> c.cl_params, pl
-		| Some (csup,spl) ->
-			let spl = (match apply_params c.cl_params pl (TInst (csup,spl)) with
-			| TInst (_,pl) -> pl
-			| _ -> assert false
-			) in
-			let ct, cpl = get_params csup spl in
-			c.cl_params @ ct, pl @ cpl
-	in
-	let tparams = (match follow t with
-		| TInst (c,pl) -> get_params c pl
-		| _ -> ([],[]))
-	in
-	let pmonos = List.map (fun _ -> mk_mono()) cf.cf_params in
-	let tmonos = snd tparams @ pmonos in
-	let tparams = fst tparams @ cf.cf_params in
-	tparams <> [], apply_params tparams tmonos
-
-let inline_metadata e meta =
-	let inline_meta e meta = match meta with
-		| (Meta.Deprecated | Meta.Pure),_,_ -> mk (TMeta(meta,e)) e.etype e.epos
-		| _ -> e
-	in
-	List.fold_left inline_meta e meta
-
-let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=false) force =
-	(* perform some specific optimization before we inline the call since it's not possible to detect at final optimization time *)
-	try
-		let cl = (match follow ethis.etype with
-			| TInst (c,_) -> c
-			| TAnon a -> (match !(a.a_status) with Statics c -> c | _ -> raise Exit)
-			| _ -> raise Exit
-		) in
-		(match api_inline ctx cl cf.cf_name params p with
-		| None -> raise Exit
-		| Some e -> Some e)
-	with Exit ->
-	let has_params,map_type = match config with Some config -> config | None -> inline_default_config cf ethis.etype in
-	(* locals substitution *)
-	let locals = Hashtbl.create 0 in
-	let local v =
-		try
-			Hashtbl.find locals v.v_id
-		with Not_found ->
-			let v' = alloc_var v.v_name v.v_type v.v_pos in
-			v'.v_extra <- v.v_extra;
-			let i = {
-				i_var = v;
-				i_subst = v';
-				i_outside = false;
-				i_abstract_this = Meta.has Meta.This v.v_meta;
-				i_captured = false;
-				i_write = false;
-				i_force_temp = false;
-				i_read = 0;
-			} in
-			i.i_subst.v_meta <- List.filter (fun (m,_,_) -> m <> Meta.This) v.v_meta;
-			Hashtbl.add locals v.v_id i;
-			Hashtbl.add locals i.i_subst.v_id i;
-			i
-	in
-	let in_local_fun = ref false in
-	let read_local v =
-		let l = try
-			Hashtbl.find locals v.v_id
-		with Not_found ->
-			{
-				i_var = v;
-				i_subst = v;
-				i_outside = true;
-				i_abstract_this = Meta.has Meta.This v.v_meta;
-				i_captured = false;
-				i_write = false;
-				i_force_temp = false;
-				i_read = 0;
-			}
-		in
-		if !in_local_fun then l.i_captured <- true;
-		l
-	in
-	(* use default values for null/unset arguments *)
-	let rec loop pl al first =
-		match pl, al with
-		| _, [] -> []
-		| e :: pl, (v, opt) :: al ->
-			(*
-				if we pass a Null<T> var to an inlined method that needs a T.
-				we need to force a local var to be created on some platforms.
-			*)
-			if ctx.com.config.pf_static && not (is_nullable v.v_type) && is_null e.etype then (local v).i_force_temp <- true;
-			(*
-				if we cast from Dynamic, create a local var as well to do the cast
-				once and allow DCE to perform properly.
-			*)
-			let e = if follow v.v_type != t_dynamic && follow e.etype == t_dynamic then mk (TCast(e,None)) v.v_type e.epos else e in
-			(match e.eexpr, opt with
-			| TConst TNull , Some c -> mk (TConst c) v.v_type e.epos
-			(*
-				This is really weird and should be reviewed again. The problem is that we cannot insert a TCast here because
-				the abstract `this` value could be written to, which is not possible if it is wrapped in a cast.
-
-				The original problem here is that we do not generate a temporary variable and thus mute the type of the
-				`this` variable, which leads to unification errors down the line. See issues #2236 and #3713.
-			*)
-			(* | _ when first && (Meta.has Meta.Impl cf.cf_meta) -> {e with etype = v.v_type} *)
-			| _ -> e) :: loop pl al false
-		| [], (v,opt) :: al ->
-			(mk (TConst (match opt with None -> TNull | Some c -> c)) v.v_type p) :: loop [] al false
-	in
-	(*
-		Build the expr/var subst list
-	*)
-	let ethis = (match ethis.eexpr with TConst TSuper -> { ethis with eexpr = TConst TThis } | _ -> ethis) in
-	let vthis = alloc_var "_this" ethis.etype ethis.epos in
-	let might_be_affected,collect_modified_locals = create_affection_checker() in
-	let had_side_effect = ref false in
-	let inlined_vars = List.map2 (fun e (v,_) ->
-		let l = local v in
-		if has_side_effect e then begin
-			collect_modified_locals e;
-			had_side_effect := true;
-			l.i_force_temp <- true;
-		end;
-		if l.i_abstract_this then l.i_subst.v_extra <- Some ([],Some e);
-		l, e
-	) (ethis :: loop params f.tf_args true) ((vthis,None) :: f.tf_args) in
-	let inlined_vars = List.rev inlined_vars in
-	(*
-		here, we try to eliminate final returns from the expression tree.
-		However, this is not entirely correct since we don't yet correctly propagate
-		the type of returned expressions upwards ("return" expr itself being Dynamic).
-
-		We also substitute variables with fresh ones that might be renamed at later stage.
-	*)
-	let opt f = function
-		| None -> None
-		| Some e -> Some (f e)
-	in
-	let has_vars = ref false in
-	let in_loop = ref false in
-	let cancel_inlining = ref false in
-	let has_return_value = ref false in
-	let return_type t el =
-		(* If the function return is Dynamic or Void, stick to it. *)
-		if follow f.tf_type == t_dynamic || ExtType.is_void (follow f.tf_type) then f.tf_type
-		(* If the expression is Void, find common type of its branches. *)
-		else if ExtType.is_void t then unify_min ctx el
-		else t
-	in
-	let map_pos = if self_calling_closure then (fun e -> e) else (fun e -> { e with epos = p }) in
-	let rec map term e =
-		let po = e.epos in
-		let e = map_pos e in
-		match e.eexpr with
-		| TLocal v ->
-			let l = read_local v in
-			l.i_read <- l.i_read + (if !in_loop then 2 else 1);
-			(* never inline a function which contain a delayed macro because its bound
-				to its variables and not the calling method *)
-			if v.v_name = "$__delayed_call__" then cancel_inlining := true;
-			let e = { e with eexpr = TLocal l.i_subst } in
-			if l.i_abstract_this then mk (TCast(e,None)) v.v_type e.epos else e
-		| TConst TThis ->
-			let l = read_local vthis in
-			l.i_read <- l.i_read + (if !in_loop then 2 else 1);
-			{ e with eexpr = TLocal l.i_subst }
-		| TVar (v,eo) ->
-			has_vars := true;
-			{ e with eexpr = TVar ((local v).i_subst,opt (map false) eo)}
-		| TReturn eo when not !in_local_fun ->
-			if not term then error "Cannot inline a not final return" po;
-			(match eo with
-			| None -> mk (TConst TNull) f.tf_type p
-			| Some e ->
-				has_return_value := true;
-				map term e)
-		| TFor (v,e1,e2) ->
-			let i = local v in
-			let e1 = map false e1 in
-			let old = !in_loop in
-			in_loop := true;
-			let e2 = map false e2 in
-			in_loop := old;
-			{ e with eexpr = TFor (i.i_subst,e1,e2) }
-		| TWhile (cond,eloop,flag) ->
-			let cond = map false cond in
-			let old = !in_loop in
-			in_loop := true;
-			let eloop = map false eloop in
-			in_loop := old;
-			{ e with eexpr = TWhile (cond,eloop,flag) }
-		| TSwitch (e1,cases,def) when term ->
-			let term = term && (def <> None || is_exhaustive e1) in
-			let cases = List.map (fun (el,e) ->
-				let el = List.map (map false) el in
-				el, map term e
-			) cases in
-			let def = opt (map term) def in
-			let t = return_type e.etype ((List.map snd cases) @ (match def with None -> [] | Some e -> [e])) in
-			{ e with eexpr = TSwitch (map false e1,cases,def); etype = t }
-		| TTry (e1,catches) ->
-			let t = if not term then e.etype else return_type e.etype (e1::List.map snd catches) in
-			{ e with eexpr = TTry (map term e1,List.map (fun (v,e) ->
-				let lv = (local v).i_subst in
-				let e = map term e in
-				lv,e
-			) catches); etype = t }
-		| TBlock l ->
-			let old = save_locals ctx in
-			let t = ref e.etype in
-			let rec has_term_return e =
-				let rec loop e =
-					let r = match e.eexpr with
-					| TReturn _ -> true
-					| TFunction _ -> false
-					| TIf (_,_,None) | TSwitch (_,_,None) | TFor _ | TWhile (_,_,NormalWhile) -> false (* we might not enter this code at all *)
-					| TTry (a, catches) -> List.for_all has_term_return (a :: List.map snd catches)
-					| TIf (cond,a,Some b) -> has_term_return cond || (has_term_return a && has_term_return b)
-					| TSwitch (cond,cases,Some def) -> has_term_return cond || List.for_all has_term_return (def :: List.map snd cases)
-					| TBinop (OpBoolAnd,a,b) -> has_term_return a && has_term_return b
-					| _ -> Type.iter loop e; false
-					in
-					if r then raise Exit
-				in
-				try loop e; false with Exit -> true
-			in
-			let rec loop = function
-				| [] when term ->
-					t := mk_mono();
-					[mk (TConst TNull) (!t) p]
-				| [] -> []
-				| [e] ->
-					let e = map term e in
-					if term then t := e.etype;
-					[e]
-				| ({ eexpr = TIf (cond,e1,None) } as e) :: l when term && has_term_return e1 ->
-					loop [{ e with eexpr = TIf (cond,e1,Some (mk (TBlock l) e.etype e.epos)); epos = punion e.epos (match List.rev l with e :: _ -> e.epos | [] -> assert false) }]
-				| e :: l ->
-					let e = map false e in
-					e :: loop l
-			in
-			let l = loop l in
-			old();
-			{ e with eexpr = TBlock l; etype = !t }
-		| TIf (econd,eif,Some eelse) when term ->
-			let econd = map false econd in
-			let eif = map term eif in
-			let eelse = map term eelse in
-			let t = return_type e.etype [eif;eelse] in
-			{ e with eexpr = TIf(econd,eif,Some eelse); etype = t }
-		| TParenthesis e1 ->
-			let e1 = map term e1 in
-			mk (TParenthesis e1) e1.etype e.epos
-		| TUnop ((Increment|Decrement) as op,flag,({ eexpr = TLocal v } as e1)) ->
-			had_side_effect := true;
-			let l = read_local v in
-			l.i_write <- true;
-			{e with eexpr = TUnop(op,flag,{e1 with eexpr = TLocal l.i_subst})}
-		| TBinop ((OpAssign | OpAssignOp _) as op,({ eexpr = TLocal v } as e1),e2) ->
-			had_side_effect := true;
-			let l = read_local v in
-			l.i_write <- true;
-			let e2 = map false e2 in
-			{e with eexpr = TBinop(op,{e1 with eexpr = TLocal l.i_subst},e2)}
-		| TObjectDecl fl ->
-			let fl = List.map (fun (s,e) -> s,map false e) fl in
-			begin match follow e.etype with
-				| TAnon an when (match !(an.a_status) with Const -> true | _ -> false) ->
-					{e with eexpr = TObjectDecl fl; etype = TAnon { an with a_status = ref Closed}}
-				| _ ->
-					{e with eexpr = TObjectDecl fl}
-			end
-		| TFunction f ->
-			(match f.tf_args with [] -> () | _ -> has_vars := true);
-			let old = save_locals ctx and old_fun = !in_local_fun in
-			let args = List.map (function(v,c) -> (local v).i_subst, c) f.tf_args in
-			in_local_fun := true;
-			let expr = map false f.tf_expr in
-			in_local_fun := old_fun;
-			old();
-			{ e with eexpr = TFunction { tf_args = args; tf_expr = expr; tf_type = f.tf_type } }
-		| TCall({eexpr = TConst TSuper; etype = t},el) ->
-			had_side_effect := true;
-			begin match follow t with
-			| TInst({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,_) ->
-				begin match type_inline_ctor ctx c cf tf ethis el po with
-				| Some e -> map term e
-				| None -> error "Could not inline super constructor call" po
-				end
-			| _ -> error "Cannot inline function containing super" po
-			end
-		| TConst TSuper ->
-			error "Cannot inline function containing super" po
-		| TMeta(m,e1) ->
-			let e1 = map term e1 in
-			{e with eexpr = TMeta(m,e1)}
-		| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) ->
-			had_side_effect := true;
-			Type.map_expr (map false) e
-		| _ ->
-			Type.map_expr (map false) e
-	in
-	let e = map true f.tf_expr in
-	if !had_side_effect then List.iter (fun (l,e) ->
-		if might_be_affected e then l.i_force_temp <- true;
-	) inlined_vars;
-	(*
-		if variables are not written and used with a const value, let's substitute
-		with the actual value, either create a temp var
-	*)
-	let subst = ref PMap.empty in
-	let is_constant e =
-		let rec loop e =
-			match e.eexpr with
-			| TLocal _
-			| TConst TThis (* not really, but should not be move inside a function body *)
-				-> raise Exit
-			| TObjectDecl _ | TArrayDecl _ -> raise Exit
-			| TField (_,FEnum _)
-			| TTypeExpr _
-			| TConst _ -> ()
-			| _ ->
-				Type.iter loop e
-		in
-		try loop e; true with Exit -> false
-	in
-	let is_writable e =
-		match e.eexpr with
-		| TField _ | TEnumParameter _ | TLocal _ | TArray _ -> true
-		| _  -> false
-	in
-	let force = ref force in
-	let vars = List.fold_left (fun acc (i,e) ->
-		let flag = not i.i_force_temp && (match e.eexpr with
-			| TLocal _ when i.i_abstract_this -> true
-			| TLocal _ | TConst _ -> not i.i_write
-			| TFunction _ -> if i.i_write then error "Cannot modify a closure parameter inside inline method" p; true
-			| _ -> not i.i_write && i.i_read <= 1
-		) in
-		let flag = flag && (not i.i_captured || is_constant e) in
-		(* force inlining if we modify 'this' *)
-		if i.i_write && i.i_abstract_this then force := true;
-		(* force inlining of 'this' variable if it is written *)
-		let flag = if not flag && i.i_abstract_this && i.i_write then begin
-			if not (is_writable e) then error "Cannot modify the abstract value, store it into a local first" p;
-			true
-		end else flag in
-		if flag then begin
-			subst := PMap.add i.i_subst.v_id e !subst;
-			acc
-		end else begin
-			(* mark the replacement local for the analyzer *)
-			if i.i_read <= 1 && not i.i_write then
-				i.i_subst.v_meta <- (Meta.CompilerGenerated,[],p) :: i.i_subst.v_meta;
-			(i.i_subst,Some e) :: acc
-		end
-	) [] inlined_vars in
-	let subst = !subst in
-	let rec inline_params e =
-		match e.eexpr with
-		| TLocal v -> (try PMap.find v.v_id subst with Not_found -> e)
-		| _ -> Type.map_expr inline_params e
-	in
-	let e = (if PMap.is_empty subst then e else inline_params e) in
-	let init = match vars with [] -> None | l -> Some l in
-	(*
-		If we have local variables and returning a value, then this will result in
-		unoptimized JS code, so let's instead skip inlining.
-
-		This could be fixed with better post process code cleanup (planed)
-	*)
-	if !cancel_inlining then
-		None
-	else
-		let md = ctx.curclass.cl_module.m_extra.m_display in
-		md.m_inline_calls <- (cf.cf_name_pos,{p with pmax = p.pmin + String.length cf.cf_name}) :: md.m_inline_calls;
-		let wrap e =
-			(* we can't mute the type of the expression because it is not correct to do so *)
-			let etype = if has_params then map_type e.etype else e.etype in
-			(* if the expression is "untyped" and we don't want to unify it accidentally ! *)
-			try (match follow e.etype with
-			| TMono _ | TInst ({cl_kind = KTypeParameter _ },_) ->
-				(match follow tret with
-				| TAbstract ({ a_path = [],"Void" },_) -> e
-				| _ -> raise (Unify_error []))
-			| _ ->
-				type_eq (if ctx.com.config.pf_static then EqDoNotFollowNull else EqStrict) etype tret;
-				e)
-			with Unify_error _ ->
-				mk (TCast (e,None)) tret e.epos
-		in
-		let e = (match e.eexpr, init with
-			| _, None when not !has_return_value ->
-				begin match e.eexpr with
-					| TBlock _ -> {e with etype = tret}
-					| _ -> mk (TBlock [e]) tret e.epos
-				end
-			| TBlock [e] , None -> wrap e
-			| _ , None -> wrap e
-			| TBlock l, Some vl ->
-				let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
-				mk (TBlock (el_v @ l)) tret e.epos
-			| _, Some vl ->
-				let el_v = List.map (fun (v,eo) -> mk (TVar (v,eo)) ctx.t.tvoid e.epos) vl in
-				mk (TBlock (el_v @ [e])) tret e.epos
-		) in
-		let e = inline_metadata e cf.cf_meta in
-		let e = Diagnostics.secure_generated_code ctx e in
-		if Meta.has (Meta.Custom ":inlineDebug") ctx.meta then begin
-			let se t = s_expr_pretty true t true (s_type (print_context())) in
-			print_endline (Printf.sprintf "Inline %s:\n\tArgs: %s\n\tExpr: %s\n\tResult: %s"
-				cf.cf_name
-				(String.concat "" (List.map (fun (i,e) -> Printf.sprintf "\n\t\t%s<%i> = %s" (i.i_subst.v_name) (i.i_subst.v_id) (se "\t\t" e)) inlined_vars))
-				(se "\t" f.tf_expr)
-				(se "\t" e)
-			);
-		end;
-		(* we need to replace type-parameters that were used in the expression *)
-		if not has_params then
-			Some e
-		else
-			let mt = map_type cf.cf_type in
-			let unify_func () = unify_raise ctx mt (TFun (List.map (fun e -> "",false,e.etype) params,tret)) p in
-			(match follow ethis.etype with
-			| TAnon a -> (match !(a.a_status) with
-				| Statics {cl_kind = KAbstractImpl a } when Meta.has Meta.Impl cf.cf_meta ->
-					if cf.cf_name <> "_new" then begin
-						(* the first argument must unify with a_this for abstract implementation functions *)
-						let tb = (TFun(("",false,map_type a.a_this) :: List.map (fun e -> "",false,e.etype) (List.tl params),tret)) in
-						unify_raise ctx mt tb p
-					end
-				| _ -> unify_func())
-			| _ -> unify_func());
-			(*
-				this is very expensive since we are building the substitution list for
-				every expression, but hopefully in such cases the expression size is small
-			*)
-			let vars = Hashtbl.create 0 in
-			let map_var v =
-				if not (Hashtbl.mem vars v.v_id) then begin
-					Hashtbl.add vars v.v_id ();
-					if not (read_local v).i_outside then v.v_type <- map_type v.v_type;
-				end;
-				v
-			in
-			let rec map_expr_type e = Type.map_expr_type map_expr_type map_type map_var e in
-			Some (map_expr_type e)
-
-(* Same as type_inline, but modifies the function body to add field inits *)
-and type_inline_ctor ctx c cf tf ethis el po =
-	let field_inits =
-		let cparams = List.map snd c.cl_params in
-		let ethis = mk (TConst TThis) (TInst (c,cparams)) c.cl_pos in
-		let el = List.fold_left (fun acc cf ->
-			match cf.cf_kind,cf.cf_expr with
-			| Var _,Some e ->
-				let lhs = mk (TField(ethis,FInstance (c,cparams,cf))) cf.cf_type e.epos in
-				let eassign = mk (TBinop(OpAssign,lhs,e)) cf.cf_type e.epos in
-				eassign :: acc
-			| _ -> acc
-		) [] c.cl_ordered_fields in
-		List.rev el
-	in
-	let tf =
-		if field_inits = [] then tf
-		else
-			let bl = match tf.tf_expr with {eexpr = TBlock b } -> b | x -> [x] in
-			{tf with tf_expr = mk (TBlock (field_inits @ bl)) ctx.t.tvoid c.cl_pos}
-	in
-	type_inline ctx cf tf ethis el ctx.t.tvoid None po true
+open Inline
 
 (* ---------------------------------------------------------------------- *)
 (* SANITIZE *)

+ 2 - 2
src/typing/calls.ml

@@ -56,7 +56,7 @@ let make_call ctx e params t p =
 		(match f.cf_expr_unoptimized,f.cf_expr with
 		| Some fd,_
 		| None,Some { eexpr = TFunction fd } ->
-			(match Optimizer.type_inline ctx f fd ethis params t config p force_inline with
+			(match Inline.type_inline ctx f fd ethis params t config p force_inline with
 			| None ->
 				if force_inline then error "Inline could not be done" p;
 				raise Exit;
@@ -500,7 +500,7 @@ let rec acc_get ctx g p =
 		| Some e ->
 			let rec loop e = Type.map_expr loop { e with epos = p } in
 			let e = loop e in
-			let e = Optimizer.inline_metadata e f.cf_meta in
+			let e = Inline.inline_metadata e f.cf_meta in
 			if not (type_iseq f.cf_type e.etype) then mk (TCast(e,None)) f.cf_type e.epos
 			else e
 		end

+ 17 - 33
std/lua/_std/Reflect.hx

@@ -35,33 +35,29 @@ import lua.Boot;
 			if (field == "length"){
 				return lua.NativeStringTools.len(o);
 			} else return untyped String.prototype[field];
-		} else return try o[field] catch( e : Dynamic ) null;
+		} else {
+		   	return try o[field] catch( e : Dynamic ) null;
+		}
 	}
 
 	public inline static function setField( o : Dynamic, field : String, value : Dynamic ) : Void untyped {
 		o[field] = value;
 	}
 
-	public static inline function getProperty( o : Dynamic, field : String ) : Dynamic {
-		var tmp : Dynamic;
+	public static function getProperty( o : Dynamic, field : String ) : Dynamic {
 		return if( o == null ) {
-				untyped __define_feature__("Reflect.getProperty",null);
-			} else if( o.__properties__ != null && Reflect.field(o, "get_" + field) != null){
-				callMethod(o, Reflect.field(o,"get_" + field), []);
-			} else {
-				Reflect.field(o,field);
-			}
+			untyped __define_feature__("Reflect.getProperty",null);
+		} else if( o.__properties__ != null && Reflect.field(o, "get_" + field) != null){
+			callMethod(o, Reflect.field(o,"get_" + field), []);
+		} else {
+			Reflect.field(o,field);
+		}
 	}
 
-	public static inline function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void untyped {
-		var tmp : String;
-		if( o.__properties__ && o.__properties__["set_"+field]) {
-			tmp = o.__properties__["set_" + field];
-			if (o.__name__ != null){
-				callMethod(null,Reflect.field(o, tmp), [value]) ;
-			} else {
-				callMethod(o,Reflect.field(o, tmp), [value]) ;
-			}
+	public static function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void untyped {
+		if( o.__properties__ != null && o.__properties__["set_"+field]) {
+			var tmp : String = o.__properties__["set_" + field];
+			callMethod(o,Reflect.field(o, tmp), [value]) ;
 		} else {
 			o[field] = __define_feature__("Reflect.setProperty",value);
 		}
@@ -72,25 +68,13 @@ import lua.Boot;
 			return func(o);
 		} else {
 			var self_arg = false;
-			if (o != null ){
-				// if o is not null, it means we need to pass it as the "self"
-				// parameter.  However, we should also check to see if it's
-				// a valid class instance in the first place.
-				// TODO: Find a more flexible way of determining if we need
-				// self or not
+			if (o != null && o.__name__ == null){
 				self_arg = true;
 			}
-			var new_args = lua.Table.create();
-			for (i in 0...args.length){
-				// copy args to 1-indexed table
-				new_args[i + 1] = args[i];
-			}
 			return if (self_arg){
-				// call with o as leading self param
-				func(o, lua.TableTools.unpack(new_args, 1, TableTools.maxn(new_args)));
+				func(o, lua.TableTools.unpack(cast args, 0, args.length-1));
 			} else {
-				// call with no self param
-				func(lua.TableTools.unpack(new_args, 1,  TableTools.maxn(new_args)));
+				func(lua.TableTools.unpack(cast args, 0,  args.length-1));
 			}
 		}
 	}

+ 28 - 0
tests/optimization/src/issues/Issue3881.hx

@@ -0,0 +1,28 @@
+package issues;
+
+private class A implements B {
+
+    public function new () {}
+
+    public inline function plus (a:Int,b:Int):Int {
+        return a + b;
+    }
+}
+
+private interface B {
+    public function plus (a:Int,b:Int):Int;
+}
+
+class Issue3881 {
+    public inline static function plusFoo (x:B) {
+        return x.plus(1,1);
+    }
+
+	@:js('issues_Issue3881.call(2);')
+    public static function test() {
+        var a = new A();
+        call(plusFoo(a));
+	}
+
+	@:pure(false) static function call(d) { }
+}

+ 20 - 0
tests/optimization/src/issues/Issue5436.hx

@@ -0,0 +1,20 @@
+package issues;
+
+class Issue5436 {
+	@:js('
+		var f = function() {};
+		issues_Issue5436.call(f);
+		issues_Issue5436.call(f);
+	')
+	@:analyzer(no_local_dce)
+    static function test() {
+        inlineMe(function() { });
+    }
+
+    static inline function inlineMe(f:Void -> Void) {
+        call(f);
+        call(f);
+    }
+
+	static function call(f:Void -> Void) { }
+}

+ 23 - 0
tests/optimization/src/issues/Issue6047.hx

@@ -0,0 +1,23 @@
+package issues;
+
+class Issue6047 {
+	static inline function foo (x:Int, f:Int->Int) {
+		return f(x);
+	};
+
+	static inline function plus2 (x:Int) return x + 2;
+
+	@:js('
+		issues_Issue6047.call(3);
+		issues_Issue6047.call(3);
+	')
+	static function test() {
+		var r1 = foo(1, plus2); // plus2 is not inlined
+		var r2 = foo(1, function (i) return plus2(i)); // plus2 gets inlined when wrapped in anon function
+
+		call(r1);
+		call(r2);
+	}
+
+	@:pure(false) static function call(d) { }
+}

+ 32 - 0
tests/optimization/src/issues/Issue6715.hx

@@ -0,0 +1,32 @@
+package issues;
+
+class Issue6715 {
+	@:js('
+		var f = function() {
+			var x = 1;
+		};
+		issues_Issue6715.x = f;
+		issues_Issue6715.x = f;
+		issues_Issue6715.x = f;
+		var x1 = 1;
+		var x2 = 1;
+		var x3 = 1;
+	')
+	@:analyzer(no_local_dce)
+	static public function main() {
+		insanity(function() var x = 1);
+	}
+
+	static var x;
+
+	static inline function insanity(f:Void -> Void)
+	{
+		x = f;
+		x = f;
+		x = f;
+
+		f();
+		f();
+		f();
+	}
+}

+ 13 - 0
tests/unit/src/unit/issues/Issue6706.hx

@@ -0,0 +1,13 @@
+package unit.issues;
+
+class Issue6706 extends unit.Test {
+
+	static inline function foo( d : Dynamic ):Dynamic {
+		return d;
+	}
+
+	function test() {
+		var v:Float = 12;
+		HelperMacros.typedAs((null : Dynamic), foo(v));
+	}
+}

+ 35 - 0
tests/unit/src/unit/issues/Issue6711.hx

@@ -0,0 +1,35 @@
+package unit.issues;
+
+private class V2 {
+	public var x:Float;
+	public var y:Float;
+	inline public function new(x, y) {
+		this.x = x;
+		this.y = y;
+	}
+}
+
+class Issue6711 extends unit.Test {
+	function test() {
+		eq("(0,1)(0,1)(0,1)", run());
+	}
+
+	inline static function getPos() : V2 {
+		return new V2(0, 1);
+	}
+
+	inline static public function iteration(func) {
+		func();
+		func();
+		func();
+	}
+
+	public static function run() {
+		var r = "";
+		iteration(function() {
+			var wpos = getPos();
+			r += '(${wpos.x},${wpos.y})';
+		});
+		return r;
+	}
+}