Selaa lähdekoodia

[java/cs] Add experimental -D fast_cast to minimize the amount of casts and their execution cost

 * Closes #3527
 * Closes #3932
 * Related #4048 (doesn't fix it)
 * Related #3379
Cauê Waneck 9 vuotta sitten
vanhempi
commit
ec3afeec42

+ 1 - 0
extra/CHANGES.txt

@@ -18,6 +18,7 @@
 	js : introduced new jQuery extern (js.jquery.*) for jQuery 1.12.2 / 2.2.2 support. (#4377)
 	js : introduced new SWFObject extern (js.swfobject.SWFObject) for SWFObject 2.3.20130521 (#4451)
 	js : added js.Lib.rethrow (#4551)
+	cs/java : added -D fast_cast as an experimental feature to cleanup casts
 
 	Bugfixes:
 

+ 163 - 37
src/generators/gencommon.ml

@@ -241,6 +241,11 @@ let get_fun t =
 
 let mk_cast t e = Type.mk_cast e t e.epos
 
+(** TODO: when adding new AST, make a new cast type for those fast casts. For now, we're using this hack
+ *        of using null_class to tell a fast cast from a normal one. Also note that this only works since both
+ *        C# and Java do not use the second part of TCast for anything *)
+let mk_castfast t e = { e with eexpr = TCast(e, Some (TClassDecl null_class)); etype = t }
+
 let mk_classtype_access cl pos = Codegen.ExprBuilder.make_static_this cl pos
 
 let mk_static_field_access_infer cl field pos params =
@@ -3046,7 +3051,7 @@ struct
 										| _ ->
 											let i = ref 0 in
 											let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-											dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+											dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 							)
 						(* | FNotFound ->
 							{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
@@ -3058,7 +3063,7 @@ struct
 								| _ ->
 									let i = ref 0 in
 									let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-									dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+									dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 					)
 				| TField(ecl, FClosure (_,cf)) ->
 					transform_closure e (run ecl) cf.cf_name
@@ -3078,7 +3083,7 @@ struct
 									("p" ^ (string_of_int !i), false, e.etype)
 								) params, e.etype)
 							in
-							fun e -> mk_cast t e
+							fun e -> mk_castfast t e
 					in
 					dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
 				| _ -> Type.map_expr run e
@@ -5913,6 +5918,16 @@ struct
 		let is_cl_related cl tl super superl = map_cls gen (gen.guse_tp_constraints || (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true)) (fun _ _ -> true) super cl tl in
 		is_cl_related cl tl super superl || is_cl_related super superl cl tl
 
+	let is_exactly_basic gen t1 t2 =
+		match gen.gfollow#run_f t1, gen.gfollow#run_f t2 with
+			| TAbstract(a1, []), TAbstract(a2, []) ->
+				a1 == a2 && Common.defined gen.gcon Define.FastCast
+			| TInst(c1, []), TInst(c2, []) ->
+				c1 == c2 && Common.defined gen.gcon Define.FastCast
+			| TEnum(e1, []), TEnum(e2, []) ->
+				e1 == e2 && Common.defined gen.gcon Define.FastCast
+			| _ ->
+				false
 
 	let rec is_unsafe_cast gen to_t from_t =
 		match (follow to_t, follow from_t) with
@@ -5998,19 +6013,21 @@ struct
 		let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
 		let to_t, from_t = real_to_t, real_from_t in
 
-		let mk_cast t e =
+		let mk_cast fast t e =
 			match e.eexpr with
 				(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
 				| TThrow _ -> { e with etype = t }
-				| _ -> mk_cast t e
+				| _ -> if fast then mk_castfast t e else mk_cast t e
 		in
 
 		let e = { e with etype = real_from_t } in
 		if try fast_eq real_to_t real_from_t with Invalid_argument("List.for_all2") -> false then e else
 		match real_to_t, real_from_t with
 			(* string is the only type that can be implicitly converted from any other *)
+			| TInst( { cl_path = ([], "String") }, []), TInst( { cl_path = ([], "String") }, [] ) ->
+				mk_cast true to_t e
 			| TInst( { cl_path = ([], "String") }, []), _ ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
 				let ret = ref None in
 				(*
@@ -6034,7 +6051,7 @@ struct
 								if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
 								we'll just make a cast to indicate that this place needs type parameter-involved casting
 							*)
-							ret := Some (mk_cast to_t e);
+							ret := Some (mk_cast true to_t e);
 							true
 						end else
 							(*
@@ -6043,16 +6060,16 @@ struct
 							*)
 							try
 								List.iter2 (type_eq gen EqRightDynamic) tl params_to;
-								ret := Some (mk_cast to_t e);
+								ret := Some (mk_cast true to_t e);
 								true
 							with | Unify_error _ ->
-								ret := Some (mk_cast to_t (mk_cast (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
+								ret := Some (mk_cast true to_t (mk_cast true (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
 								true
 				) cl_to cl_from params_from);
 				if is_some !ret then
 					get !ret
 				else if is_cl_related gen cl_from params_from cl_to params_to then
-					mk_cast to_t e
+					mk_cast true to_t e
 				else
 					(* potential unsafe cast *)
 					(do_unsafe_cast ())
@@ -6064,18 +6081,18 @@ struct
 			| TMono _, _
 			| TDynamic _, _
 			| TAnon _, _ when gen.gneeds_box real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TMono _, _
 			| TDynamic _, _ -> e
 			| _, TMono _
-			| _, TDynamic _ -> mk_cast to_t e
+			| _, TDynamic _ -> mk_cast false to_t e
 			| TAnon (a_to), TAnon (a_from) ->
 				if a_to == a_from then
 					e
 				else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
 					e
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| _, TAnon(anon) -> (try
 				let p2 = match !(anon.a_status) with
 				| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
@@ -6088,7 +6105,7 @@ struct
 				| _ -> assert false in
 				handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
 			with | Not_found ->
-				mk_cast to_t e)
+				mk_cast false to_t e)
 			| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
 				e
 			| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
@@ -6098,11 +6115,11 @@ struct
 			| _, TAbstract _ ->
 				(try
 					unify from_t to_t;
-					mk_cast to_t e
+					mk_cast true to_t e
 				with | Unify_error _ ->
 					try
 						unify to_t from_t;
-						mk_cast to_t e
+						mk_cast true to_t e
 					with | Unify_error _ ->
 						do_unsafe_cast())
 			| TEnum(e_to, []), TEnum(e_from, []) ->
@@ -6149,13 +6166,13 @@ struct
 					e
 			| TType(t_to, _), TType(t_from,_) ->
 				if gen.gspecial_needs_cast real_to_t real_from_t then
-					mk_cast to_t e
+					mk_cast false to_t e
 				else
 					e
 			| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			(*| TType(t_to, _), TType(t_from, _) ->
 				if t_to.t_path = t_from.t_path then
 					e
@@ -6168,15 +6185,15 @@ struct
 				if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
 					(do_unsafe_cast ())
 				else
-					mk_cast to_t e
+					mk_cast false to_t e
 			| TAnon anon, _ ->
 				if PMap.is_empty anon.a_fields then
 					e
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| TFun(args, ret), TFun(args2, ret2) ->
 				let get_args = List.map (fun (_,_,t) -> t) in
-				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast to_t e)
+				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast true to_t e)
 			| _, _ ->
 				do_unsafe_cast ()
 
@@ -6310,8 +6327,16 @@ struct
 		let args,ret = get_fun tfun in
 		TFun(loop [] args elist, ret)
 
-	(*
+	let fastcast_if_needed gen expr real_to_t real_from_t =
+		if Common.defined gen.gcon Define.FastCast then begin
+			if type_iseq gen real_to_t real_from_t then
+				{ expr with etype = real_to_t }
+			else
+				mk_castfast real_to_t { expr with etype=real_from_t }
+		end else
+			handle_cast gen expr real_to_t real_from_t
 
+	(*
 		Type parameter handling
 		It will detect if/what type parameters were used, and call the cast handler
 		It will handle both TCall(TField) and TCall by receiving a texpr option field: e
@@ -6351,6 +6376,14 @@ struct
 					| TInst(_,params) -> params
 					| _ -> params
 				in
+				let local_mk_cast t expr =
+					(* handle_cast gen expr t expr.etype *)
+					if is_exactly_basic gen t expr.etype then
+						expr
+					else
+						mk_castfast t expr
+				in
+
 				let ecall = get e in
 				let ef = ref ef in
 				let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
@@ -6419,7 +6452,7 @@ struct
 					if is_void ecall.etype then
 						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 					else
-						mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
+						local_mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 				else begin
 					(* infer arguments *)
 					(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
@@ -6444,12 +6477,12 @@ struct
 						let elist = List.map2 (fun applied (_,_,funct) ->
 							match is_overload, applied.eexpr with
 							| true, TConst TNull ->
-								mk_cast (gen.greal_type funct) applied
+								local_mk_cast (gen.greal_type funct) applied
 							| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
 								let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
 								(match ret.eexpr with
 								| TCast _ -> ret
-								| _ -> mk_cast (funct) ret)
+								| _ -> local_mk_cast (funct) ret)
 							| _ ->
 								handle_cast gen applied (funct) (gen.greal_type applied.etype)
 						) applied args_ft in
@@ -6553,6 +6586,89 @@ struct
 			| _ -> false
 		in
 
+		let binop_type op main_expr e1 e2 =
+			let name = Common.platform_name gen.gcon.platform in
+			let basic = gen.gcon.basic in
+			(* If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
+			 * Otherwise, if either operand is of type double, the other operand is converted to type double.
+			 * Otherwise, if either operand is of type float, the other operand is converted to type float.
+			 * Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
+			 * Otherwise, if either operand is of type long, the other operand is converted to type long.
+			 * Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
+			 * Otherwise, if either operand is of type uint, the other operand is converted to type uint.
+			 * Otherwise, both operands are converted to type int.
+			 *  *)
+			let t1, t2 = follow (run_follow gen e1.etype), follow (run_follow gen e2.etype) in
+			match t1, t2 with
+				| TAbstract(a1,[]), TAbstract(a2,[]) when a1 == a2 ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst(i1,[]), TInst(i2,[]) when i1 == i2 ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst({ cl_path = ([],"String") },[]), _ when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, e1, mk_cast basic.tstring e2); etype = basic.tstring }
+				| _, TInst({ cl_path = ([],"String") },[]) when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, mk_cast basic.tstring e1, e2); etype = basic.tstring }
+				| TAbstract({ a_path = ([], "Float") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Float") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "Single") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Single") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "UInt64") }, []), _ when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "UInt64") }, []) when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "Int64") }, []), _ when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "Int64") }, []) when pf = name ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "UInt") }, []), tother when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } *)
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t1 ret
+					else
+						ret
+				| tother, TAbstract({ a_path = ([], "UInt") }, []) when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					(* { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } *)
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t2 ret
+					else
+						ret
+				| TAbstract({ a_path = ([], "UInt") }, []), _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, mk_castfast t1 e2); etype = e1.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "UInt") }, []) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast t2 e1, e2); etype = e2.etype } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract(a1,[]), TAbstract(a2,[]) ->
+					(* { main_expr with eexpr = TBinop(op, mk_castfast basic.tint e1, mk_castfast basic.tint e2); etype = basic.tint } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = basic.tint }
+				| _ ->
+					(* { main_expr with eexpr = TBinop(op, e1, e2) } *)
+					{ main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+		let binop_type = if Common.defined gen.gcon Define.FastCast then
+			binop_type
+		else
+			fun op main_expr e1 e2 -> { main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+
 		let rec run ?(just_type = false) e =
 			let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
 			let was_in_value = !in_value in
@@ -6581,7 +6697,12 @@ struct
 				| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
 					let e1 = run e1 in
 					let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
-					{ e with eexpr = TBinop(op, e1, e2) }
+					let rett = binop_type op e e1 e2 in
+					{ e with eexpr = TBinop(op, e1, e2); etype = rett.etype }
+				| TBinop( (OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpMod) as op, e1, e2 ) ->
+					binop_type op e (run e1) (run e2)
+				| TBinop( (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr) as op, e1, e2 ) ->
+					handle { e with eexpr = TBinop(op, run e1, run e2) } e.etype gen.gcon.basic.tbool
 				| TField(ef, f) ->
 					handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
 				| TArrayDecl el ->
@@ -6667,15 +6788,17 @@ struct
 					{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
 				| TArray(arr, idx) ->
 					let arr_etype = match follow arr.etype with
-					| (TInst _ as t) -> t
-					| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-						follow (Abstract.get_underlying_type a pl)
-					| t -> t in
+						| (TInst _ as t) -> t
+						| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+							follow (Abstract.get_underlying_type a pl)
+						| t -> t
+					in
+					let idx = run idx in
 					let idx = match gen.greal_type idx.etype with
-					| TAbstract({ a_path = [],"Int" },_) -> run idx
-					| _ -> match handle (run idx) gen.gcon.basic.tint (gen.greal_type idx.etype) with
-					| ({ eexpr = TCast _ } as idx) -> idx
-					| idx -> mk_cast gen.gcon.basic.tint idx
+						| TAbstract({ a_path = [],"Int" },_) -> idx
+						| _ -> match handle idx gen.gcon.basic.tint (gen.greal_type idx.etype) with
+							| ({ eexpr = TCast _ } as idx) -> idx
+							| idx -> mk_cast gen.gcon.basic.tint idx
 					in
 					let e = { e with eexpr = TArray(run arr, idx) } in
 					(* get underlying class (if it's a class *)
@@ -6691,7 +6814,8 @@ struct
 									let real_t = apply_params cl.cl_params params param in
 									(* see if it needs a cast *)
 
-									handle (e) (gen.greal_type e.etype) (gen.greal_type real_t)
+									fastcast_if_needed gen e (gen.greal_type e.etype) (gen.greal_type real_t)
+									(* handle (e) (gen.greal_type e.etype) (gen.greal_type real_t) *)
 							)
 						| _ -> Type.map_expr run e)
 				| TVar (v, eopt) ->
@@ -6741,6 +6865,8 @@ struct
 								mk_cast (gen.greal_type e.etype) enull
 					| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
 						run { expr with etype = expr.etype }
+					| _ when is_exactly_basic gen expr.etype e.etype ->
+						run { expr with etype = expr.etype }
 					| _ ->
 						match gen.greal_type e.etype, gen.greal_type expr.etype with
 							| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
@@ -10752,7 +10878,7 @@ struct
 
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
 
-	let is_int = like_int
+	let is_int t = like_int t && not (like_i64 t)
 
 	let is_exactly_int t = match follow t with
 		| TAbstract ({ a_path=[],"Int" }, []) -> true

+ 9 - 0
src/generators/gencs.ml

@@ -426,6 +426,9 @@ struct
 			| _ -> ""
 		in
 
+		let as_var = alloc_var "__as__" t_dynamic in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
+
 		let rec run e =
 			match e.eexpr with
 
@@ -516,6 +519,12 @@ struct
 						epos = expr.epos
 					}
 
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					if is_cs_basic_type (gen.greal_type e.etype) || is_tparam (gen.greal_type e.etype) then
+						{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+					else
+						{ e with eexpr = TCall(mk_local as_var e.epos, [run expr]) }
+
 				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && name() <> "haxe.lang.Runtime" ->
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
 				| TBinop( (Ast.OpNotEq as op), e1, e2)

+ 5 - 1
src/generators/genjava.ml

@@ -570,6 +570,7 @@ struct
 		(* let tsingle = mt_to_t_dyn ( get_type gen ([], "Single") ) in *)
 		let ti64 = mt_to_t_dyn ( get_type gen (["java"], "Int64") ) in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
 
 		let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
 
@@ -657,8 +658,11 @@ struct
 						epos = expr.epos
 					}
 
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+
 				| TBinop( (Ast.OpAssignOp OpAdd as op), e1, e2)
-				| TBinop( (Ast.OpAdd as op), e1, e2) when is_string e.etype || is_string e1.etype || is_string e2.etype ->
+				| TBinop( (Ast.OpAdd as op), e1, e2) when not fast_cast && (is_string e.etype || is_string e1.etype || is_string e2.etype) ->
 						let is_assign = match op with Ast.OpAssignOp _ -> true | _ -> false in
 						let mk_to_string e = { e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" e.epos [], [run e] ); etype = gen.gcon.basic.tstring	} in
 						let check_cast e = match gen.greal_type e.etype with

+ 2 - 0
src/typing/common.ml

@@ -185,6 +185,7 @@ module Define = struct
 		| DumpIgnoreVarIds
 		| DynamicInterfaceClosures
 		| EraseGenerics
+		| FastCast
 		| Fdb
 		| FileExtension
 		| FlashStrict
@@ -274,6 +275,7 @@ module Define = struct
 		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Remove variable IDs from non-pretty dumps (helps with diff)")
 		| DynamicInterfaceClosures -> ("dynamic_interface_closures","Use slow path for interface closures to save space")
 		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
+		| FastCast -> ("fast_cast","Enables an experimental casts cleanup on C# and Java")
 		| Fdb -> ("fdb","Enable full flash debug infos for FDB interactive debugging")
 		| FileExtension -> ("file_extension","Output filename extension for cpp source code")
 		| FlashStrict -> ("flash_strict","More strict typing for flash target")

+ 3 - 1
std/cs/internal/Runtime.hx

@@ -107,7 +107,9 @@ import cs.system.Object;
 			{
 				case [Decimal, _] | [_, Decimal]:
 					return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-				case [UInt64 | Int64 | DateTime, _] | [_, UInt64 | Int64 | DateTime]:
+				case [Int64, _] | [_, Int64]:
+					return v1c.ToInt64(null) == v2c.ToInt64(null);
+				case [UInt64 | DateTime, _] | [_, UInt64 | DateTime]:
 					return v1c.ToUInt64(null) == v2c.ToUInt64(null);
 				case [Double | Single, _] | [_, Double | Single]:
 					return v1c.ToDouble(null) == v2c.ToDouble(null);

+ 10 - 10
std/java/internal/Runtime.hx

@@ -161,18 +161,18 @@ package java.internal;
 		return false;
 	}
 
-	@:functionCode('
-		if (obj != null && obj instanceof java.lang.Number)
-		{
-			java.lang.Number n = (java.lang.Number) obj;
+	@:overload public static function isInt(obj:Dynamic):Bool
+	{
+		if (Std.is(obj, java.lang.Number)) {
+			var n:java.lang.Number = obj;
 			return n.doubleValue() == n.intValue();
 		} else {
 			return false;
 		}
-	')
-	public static function isInt(obj:Dynamic):Bool
-	{
-		return false;
+	}
+
+	@:overload public static function isInt(num:java.lang.Number):Bool {
+		return num != null && num.doubleValue() == num.intValue();
 	}
 
 	@:functionCode('
@@ -573,8 +573,8 @@ package java.internal;
 		if (obj == null)
 			return null;
 
-		if (isInt(obj))
-			return (cast(obj, Int)) + "";
+		if (Std.is(obj, java.lang.Number) && !Std.is(obj, java.lang.Integer.IntegerClass) && isInt( (obj : java.lang.Number) ))
+			return java.lang.Integer._toString(toInt(obj));
 		return untyped obj.toString();
 	}
 

+ 11 - 8
tests/RunCi.hx

@@ -1106,32 +1106,35 @@ class RunCi {
 					runCommand("haxe", ['compile-cs$compl.hxml']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs$compl.hxml','-dce','no']);
+					runCommand("haxe", ['compile-cs$compl.hxml','-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml']);
+					runCommand("haxe", ['compile-cs$compl.hxml','-dce','no','-D','fast_cast']);
+					runCs("bin/cs/bin/Test-Debug.exe");
+
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml','-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs$compl.hxml',"-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs$compl.hxml',"-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs$compl.hxml',"-D","no_root"]);
+					runCommand("haxe", ['compile-cs$compl.hxml',"-D","no_root",'-D','fast_cast']);
 					runCs("bin/cs/bin/Test-Debug.exe");
 
-					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","no_root","-D","erase_generics"]);
+					runCommand("haxe", ['compile-cs-unsafe$compl.hxml',"-D","no_root","-D","erase_generics",'-D','fast_cast']);
 					runCs("bin/cs_unsafe/bin/Test-Debug.exe");
 
 					changeDirectory(sysDir);
-					runCommand("haxe", ["compile-cs.hxml"]);
+					runCommand("haxe", ["compile-cs.hxml",'-D','fast_cast']);
 					runCs("bin/cs/bin/Main-Debug.exe", []);
 
 					changeDirectory(miscDir + "csTwoLibs");
 					for (i in 1...5)
 					{
-						runCommand("haxe", ['compile-$i.hxml']);
+						runCommand("haxe", ['compile-$i.hxml','-D','fast_cast']);
 						runCs("bin/main/bin/Main.exe");
 					}
 

+ 1 - 0
tests/unit/compile-java.hxml

@@ -9,3 +9,4 @@ compile-each.hxml
 -java-lib native_java/native.jar
 -java-lib java_drivers/mysql-connector-java-5.1.32-bin.jar
 -java-lib java_drivers/sqlite-jdbc-3.7.2.jar
+-D dump