Переглянути джерело

[cs] Added -D erase_generics . Closes #1107

- Take off @:functionCode from most places on Reflect/Type
- Do not convert __get on Strings
- Make Runtime.eq() more robust
- Take care of what should be considered hxgeneric - not only TClassDecl
- Take off genericCast when erase_generics
- Test erase_generics on the CI as well
Cauê Waneck 10 роки тому
батько
коміт
f60e748942

+ 2 - 0
common.ml

@@ -185,6 +185,7 @@ module Define = struct
 		| Dump
 		| DumpDependencies
 		| DumpIgnoreVarIds
+		| EraseGenerics
 		| Fdb
 		| FileExtension
 		| FlashStrict
@@ -268,6 +269,7 @@ module Define = struct
 		| Dump -> ("dump","Dump the complete typed AST for internal debugging")
 		| DumpDependencies -> ("dump_dependencies","Dump the classes dependencies")
 		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Dump files do not contain variable IDs (helps with diff)")
+		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
 		| 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")

+ 80 - 11
gencommon.ml

@@ -1435,7 +1435,7 @@ let field_access gen (t:t) (field:string) : (tfield_access) =
 					| None -> None
 					| Some(cf,t,dt,_,cl,_,_) -> Some(cf,t,dt,cl)
 				in
-				Hashtbl.add gen.greal_field_types (orig_cl.cl_path, hashtbl_field) ret;
+				if ret <> None then Hashtbl.add gen.greal_field_types (orig_cl.cl_path, hashtbl_field) ret;
 				ret
 			in
 			(match types with
@@ -2070,10 +2070,11 @@ struct
 		let ensure_simple_expr gen e =
 			let rec iter e = match e.eexpr with
 				| TConst _ | TLocal _ | TArray _ | TBinop _
-				| TField _ | TTypeExpr _ | TParenthesis _
+				| TField _ | TTypeExpr _ | TParenthesis _ | TCast _
 				| TCall _ | TNew _ | TUnop _ ->
 					Type.iter iter e
 				| _ ->
+					print_endline (debug_expr e);
 					gen.gcon.error "Expression is too complex for a readonly variable initialization" e.epos
 			in
 			iter e
@@ -3252,6 +3253,10 @@ struct
 			let path = (fst ft.fgen.gcurrent_path, Printf.sprintf "%s_%s_%d__Fun" (snd ft.fgen.gcurrent_path) cfield cur_line) in
 			let cls = mk_class (get ft.fgen.gcurrent_class).cl_module path tfunc.tf_expr.epos in
 			if in_unsafe then cls.cl_meta <- (Meta.Unsafe,[],Ast.null_pos) :: cls.cl_meta;
+
+			if Common.defined gen.gcon Define.EraseGenerics then begin
+				cls.cl_meta <- (Meta.HaxeGeneric,[],Ast.null_pos) :: cls.cl_meta
+			end;
 			cls.cl_module <- (get ft.fgen.gcurrent_class).cl_module;
 			cls.cl_params <- cltypes;
 
@@ -4215,15 +4220,39 @@ struct
 				| TInst(_, params) -> List.fold_left (fun acc t -> acc || has_type_params t) false params
 				| _ -> false
 
-		let is_hxgeneric = function
+		let rec follow_all_md md =
+			match md with
+			| TClassDecl { cl_kind = KAbstractImpl a } ->
+				follow_all_md (TAbstractDecl a)
+			| TAbstractDecl a -> if Meta.has Meta.CoreType a.a_meta then
+				None
+			else (
+				match follow (apply_params a.a_params (List.map snd a.a_params) a.a_this) with
+					| TInst(c,_) -> follow_all_md (TClassDecl c)
+					| TEnum(e,_) -> follow_all_md (TEnumDecl e)
+					| TAbstract(a,_) -> follow_all_md (TAbstractDecl a)
+					| TType(t,_) -> follow_all_md (TTypeDecl t)
+					| _ -> None)
+			| TTypeDecl t -> (
+				match follow (apply_params t.t_params (List.map snd t.t_params) t.t_type) with
+				| TInst(c,_) -> follow_all_md (TClassDecl c)
+				| TEnum(e,_) -> follow_all_md (TEnumDecl e)
+				| TAbstract(a,_) -> follow_all_md (TAbstractDecl a)
+				| TType(t,_) -> follow_all_md (TTypeDecl t)
+				| _ -> None)
+			| md -> Some md
+
+		let rec is_hxgeneric md =
+			match md with
+			| TClassDecl { cl_kind = KAbstractImpl a } ->
+				is_hxgeneric (TAbstractDecl a)
 			| TClassDecl(cl) ->
 				not (Meta.has Meta.NativeGeneric cl.cl_meta)
 			| TEnumDecl(e) ->
 				not (Meta.has Meta.NativeGeneric e.e_meta)
-			| TTypeDecl(t) ->
-				not (Meta.has Meta.NativeGeneric t.t_meta)
-			| TAbstractDecl a ->
-				not (Meta.has Meta.NativeGeneric a.a_meta)
+			| md -> match follow_all_md md with
+				| Some md -> is_hxgeneric md
+				| None -> true
 
 		let rec set_hxgeneric gen mds isfirst md =
 			let path = t_path md in
@@ -4351,10 +4380,41 @@ struct
 			end
 
 		let set_hxgeneric gen md =
-			match set_hxgeneric gen [] true md with
-				| None ->
-					get (set_hxgeneric gen [] false md)
-				| Some v -> v
+			let ret = match md with
+				| TClassDecl { cl_kind = KAbstractImpl a } -> (match follow_all_md md with
+					| Some md ->
+						let ret = set_hxgeneric gen [] true md in
+						if ret = None then get (set_hxgeneric gen [] false md) else get ret
+					| None ->
+						true)
+				| _ -> match set_hxgeneric gen [] true md with
+					| None ->
+						get (set_hxgeneric gen [] false md)
+					| Some v ->
+						v
+			in
+			if not ret then begin
+				match md with
+				| TClassDecl c ->
+					let set_hxgeneric (_,param) = match follow param with
+						| TInst(c,_) ->
+							c.cl_meta <- (Meta.NativeGeneric, [], c.cl_pos) :: c.cl_meta
+						| _ -> ()
+					in
+					List.iter set_hxgeneric c.cl_params;
+					let rec handle_field cf =
+						List.iter set_hxgeneric cf.cf_params;
+						List.iter handle_field cf.cf_overloads
+					in
+					(match c.cl_kind with
+						| KAbstractImpl a ->
+							List.iter set_hxgeneric a.a_params;
+						| _ -> ());
+					List.iter handle_field c.cl_ordered_fields;
+					List.iter handle_field c.cl_ordered_statics
+				| _ -> ()
+			end;
+			ret
 
 		let params_has_tparams params =
 			List.fold_left (fun acc t -> acc || has_type_params t) false params
@@ -4378,6 +4438,14 @@ struct
 		module RealTypeParamsModf =
 		struct
 
+			let set_only_hxgeneric gen =
+				let rec run md =
+					match md with
+						| TTypeDecl _ | TAbstractDecl _ -> md
+						| _ -> ignore (set_hxgeneric gen md); md
+				in
+				run
+
 			let name = "real_type_params_modf"
 
 			let priority = solve_deps name []
@@ -8701,6 +8769,7 @@ struct
 		let configure = if is_synf then DynamicFieldAccess.configure_as_synf else DynamicFieldAccess.configure in
 		let maybe_hash = if ctx.rcf_optimize then fun str pos -> Some (hash_field_i32 ctx pos str) else fun str pos -> None in
 		configure gen (DynamicFieldAccess.abstract_implementation gen is_dynamic
+			(* print_endline *)
 			(fun expr fexpr field set is_unsafe ->
 				let hash = maybe_hash field fexpr.epos in
 				ctx.rcf_on_getset_field expr fexpr field hash set is_unsafe

+ 167 - 123
gencs.ml

@@ -107,15 +107,15 @@ let is_tparam t =
 		| TInst( { cl_kind = KTypeParameter _ }, [] ) -> true
 		| _ -> false
 
-let rec is_int_float t =
-	match follow t with
+let rec is_int_float gen t =
+	match follow (gen.greal_type t) with
 		| TInst( { cl_path = (["haxe"], "Int32") }, [] )
 		| TAbstract ({ a_path = ([], "Int") },[])
 		| TAbstract ({ a_path = ([], "Float") },[]) ->
 			true
 		| TAbstract _ when like_float t && not (like_i64 t) ->
 			true
-		| TInst( { cl_path = (["haxe"; "lang"], "Null") }, [t] ) -> is_int_float t
+		| TInst( { cl_path = (["haxe"; "lang"], "Null") }, [t] ) -> is_int_float gen t
 		| _ -> false
 
 let is_bool t =
@@ -485,7 +485,7 @@ struct
 						etype = basic.tbool;
 						epos = e.epos
 					}
-				| TCast(expr, _) when is_int_float e.etype && not (is_cs_basic_type expr.etype) && not (is_null e.etype) && name() <> "haxe.lang.Runtime" ->
+				| TCast(expr, _) when is_int_float gen e.etype && not (is_cs_basic_type (gen.greal_type expr.etype)) && ( Common.defined gen.gcon Define.EraseGenerics || not (is_null e.etype) ) && name() <> "haxe.lang.Runtime" ->
 					let needs_cast = match gen.gfollow#run_f e.etype with
 						| TInst _ -> false
 						| _ -> true
@@ -516,7 +516,7 @@ struct
 						}, [ run e1; run e2 ])
 					}
 
-				| TCast(expr, _) when is_tparam e.etype && name() <> "haxe.lang.Runtime" ->
+				| TCast(expr, _) when is_tparam e.etype && name() <> "haxe.lang.Runtime" && not (Common.defined gen.gcon Define.EraseGenerics) ->
 					let static = mk_static_field_access_infer (runtime_cl) "genericCast" e.epos [e.etype] in
 					{ e with eexpr = TCall(static, [mk_local (alloc_var "$type_param" e.etype) expr.epos; run expr]); }
 
@@ -550,119 +550,122 @@ struct
 
 end;;
 
-(* Type Parameters Handling *)
-let handle_type_params gen ifaces base_generic =
+let add_cast_handler gen =
 	let basic = gen.gcon.basic in
-		(*
-			starting to set gtparam_cast.
-		*)
+	(*
+		starting to set gtparam_cast.
+	*)
 
-		(* NativeArray: the most important. *)
+	(* NativeArray: the most important. *)
 
-		(*
-			var new_arr = new NativeArray<TO_T>(old_arr.Length);
-			var i = -1;
-			while( i < old_arr.Length )
-			{
-				new_arr[i] = (TO_T) old_arr[i];
-			}
-		*)
+	(*
+		var new_arr = new NativeArray<TO_T>(old_arr.Length);
+		var i = -1;
+		while( i < old_arr.Length )
+		{
+			new_arr[i] = (TO_T) old_arr[i];
+		}
+	*)
 
-		let native_arr_cl = get_cl ( get_type gen (["cs"], "NativeArray") ) in
+	let native_arr_cl = get_cl ( get_type gen (["cs"], "NativeArray") ) in
 
-		let get_narr_param t = match follow t with
-			| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) -> param
-			| _ -> assert false
-		in
+	let get_narr_param t = match follow t with
+		| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) -> param
+		| _ -> assert false
+	in
 
-		let gtparam_cast_native_array e to_t =
-			let old_param = get_narr_param e.etype in
-			let new_param = get_narr_param to_t in
+	let gtparam_cast_native_array e to_t =
+		let old_param = get_narr_param e.etype in
+		let new_param = get_narr_param to_t in
 
-			let new_v = mk_temp gen "new_arr" to_t in
-			let i = mk_temp gen "i" basic.tint in
-			let old_len = mk_field_access gen e "Length" e.epos in
-			let obj_v = mk_temp gen "obj" t_dynamic in
-			let check_null = {eexpr = TBinop(Ast.OpNotEq, e, null e.etype e.epos); etype = basic.tbool; epos = e.epos} in
-			let block = [
-				{
-					eexpr = TVar(
-						new_v, Some( {
-							eexpr = TNew(native_arr_cl, [new_param], [old_len] );
-							etype = to_t;
-							epos = e.epos
-						} )
-					);
-					etype = basic.tvoid;
-					epos = e.epos
-				};
-				{
-					eexpr = TVar(i, Some( mk_int gen (-1) e.epos ));
-					etype = basic.tvoid;
-					epos = e.epos
-				};
-				{
-					eexpr = TWhile(
+		let new_v = mk_temp gen "new_arr" to_t in
+		let i = mk_temp gen "i" basic.tint in
+		let old_len = mk_field_access gen e "Length" e.epos in
+		let obj_v = mk_temp gen "obj" t_dynamic in
+		let check_null = {eexpr = TBinop(Ast.OpNotEq, e, null e.etype e.epos); etype = basic.tbool; epos = e.epos} in
+		let block = [
+			{
+				eexpr = TVar(
+					new_v, Some( {
+						eexpr = TNew(native_arr_cl, [new_param], [old_len] );
+						etype = to_t;
+						epos = e.epos
+					} )
+				);
+				etype = basic.tvoid;
+				epos = e.epos
+			};
+			{
+				eexpr = TVar(i, Some( mk_int gen (-1) e.epos ));
+				etype = basic.tvoid;
+				epos = e.epos
+			};
+			{
+				eexpr = TWhile(
+					{
+						eexpr = TBinop(
+							Ast.OpLt,
+							{ eexpr = TUnop(Ast.Increment, Ast.Prefix, mk_local i e.epos); etype = basic.tint; epos = e.epos },
+							old_len
+						);
+						etype = basic.tbool;
+						epos = e.epos
+					},
+					{ eexpr = TBlock [
 						{
-							eexpr = TBinop(
-								Ast.OpLt,
-								{ eexpr = TUnop(Ast.Increment, Ast.Prefix, mk_local i e.epos); etype = basic.tint; epos = e.epos },
-								old_len
-							);
-							etype = basic.tbool;
+							eexpr = TVar(obj_v, Some (mk_cast t_dynamic { eexpr = TArray(e, mk_local i e.epos); etype = old_param; epos = e.epos }));
+							etype = basic.tvoid;
 							epos = e.epos
-						},
-						{ eexpr = TBlock [
-							{
-								eexpr = TVar(obj_v, Some (mk_cast t_dynamic { eexpr = TArray(e, mk_local i e.epos); etype = old_param; epos = e.epos }));
-								etype = basic.tvoid;
+						};
+						{
+							eexpr = TIf({
+								eexpr = TBinop(Ast.OpNotEq, mk_local obj_v e.epos, null e.etype e.epos);
+								etype = basic.tbool;
 								epos = e.epos
-							};
+							},
 							{
-								eexpr = TIf({
-									eexpr = TBinop(Ast.OpNotEq, mk_local obj_v e.epos, null e.etype e.epos);
-									etype = basic.tbool;
-									epos = e.epos
-								},
-								{
-									eexpr = TBinop(
-										Ast.OpAssign,
-										{ eexpr = TArray(mk_local new_v e.epos, mk_local i e.epos); etype = new_param; epos = e.epos },
-										mk_cast new_param (mk_local obj_v e.epos)
-									);
-									etype = new_param;
-									epos = e.epos
-								},
-								None);
-								etype = basic.tvoid;
+								eexpr = TBinop(
+									Ast.OpAssign,
+									{ eexpr = TArray(mk_local new_v e.epos, mk_local i e.epos); etype = new_param; epos = e.epos },
+									mk_cast new_param (mk_local obj_v e.epos)
+								);
+								etype = new_param;
 								epos = e.epos
-							}
-						]; etype = basic.tvoid; epos = e.epos },
-						Ast.NormalWhile
-					);
-					etype = basic.tvoid;
-					epos = e.epos;
-				};
-				mk_local new_v e.epos
-			] in
-			{
-				eexpr = TIf(
-					check_null,
-					{
-						eexpr = TBlock(block);
-						etype = to_t;
-						epos = e.epos;
-					},
-					Some(null new_v.v_type e.epos)
+							},
+							None);
+							etype = basic.tvoid;
+							epos = e.epos
+						}
+					]; etype = basic.tvoid; epos = e.epos },
+					Ast.NormalWhile
 				);
-				etype = to_t;
+				etype = basic.tvoid;
 				epos = e.epos;
-			}
-		in
+			};
+			mk_local new_v e.epos
+		] in
+		{
+			eexpr = TIf(
+				check_null,
+				{
+					eexpr = TBlock(block);
+					etype = to_t;
+					epos = e.epos;
+				},
+				Some(null new_v.v_type e.epos)
+			);
+			etype = to_t;
+			epos = e.epos;
+		}
+	in
+
+	Hashtbl.add gen.gtparam_cast (["cs"], "NativeArray") gtparam_cast_native_array
+	(* end set gtparam_cast *)
 
-		Hashtbl.add gen.gtparam_cast (["cs"], "NativeArray") gtparam_cast_native_array;
-		(* end set gtparam_cast *)
 
+(* Type Parameters Handling *)
+let handle_type_params gen ifaces base_generic =
+	add_cast_handler gen;
 	TypeParams.RealTypeParams.default_config gen (fun e t -> gen.gcon.warning ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces base_generic
 
 let connecting_string = "?" (* ? see list here http://www.fileformat.info/info/unicode/category/index.htm and here for C# http://msdn.microsoft.com/en-us/library/aa664670.aspx *)
@@ -712,8 +715,9 @@ let rec get_fun_modifiers meta access modifiers =
 let configure gen =
 	let basic = gen.gcon.basic in
 
+	let erase_generics = Common.defined gen.gcon Define.EraseGenerics in
 	let fn_cl = get_cl (get_type gen (["haxe";"lang"],"Function")) in
-	let null_t = (get_cl (get_type gen (["haxe";"lang"],"Null")) ) in
+	let null_t = if erase_generics then null_class else (get_cl (get_type gen (["haxe";"lang"],"Null")) ) in
 	let runtime_cl = get_cl (get_type gen (["haxe";"lang"],"Runtime")) in
 	let no_root = Common.defined gen.gcon Define.NoRoot in
 	let change_id name = try
@@ -775,6 +779,18 @@ let configure gen =
 			null_abstract
 	in
 
+	let is_hxgeneric md =
+		TypeParams.RealTypeParams.is_hxgeneric md
+	in
+
+	let rec field_is_hxgeneric e = match e.eexpr with
+		| TParenthesis e | TMeta(_,e) -> field_is_hxgeneric e
+		| TField(_, (FStatic(cl,_) | FInstance(cl,_,_)) ) ->
+			(* print_endline ("is_hxgeneric " ^ path_s cl.cl_path ^ " : " ^ string_of_bool (is_hxgeneric (TClassDecl cl))); *)
+			is_hxgeneric (TClassDecl cl)
+		| _ -> true
+	in
+
 	gen.gfollow#add ~name:"follow_basic" (fun t -> match t with
 			| TAbstract ({ a_path = ([], "Bool") },[])
 			| TAbstract ({ a_path = ([], "Void") },[])
@@ -846,12 +862,16 @@ let configure gen =
 				real_type (Abstract.get_underlying_type a pl)
 			| TAbstract ({ a_path = (["cs";"_Flags"], "EnumUnderlying") }, [t]) ->
 				real_type t
+			| TInst( { cl_path = (["cs";"system"], "String") }, [] ) ->
+				gen.gcon.basic.tstring;
 			| TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
 			| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
 			| TAbstract( { a_path = [],"Class" }, _ )
 			| TAbstract( { a_path = [],"Enum" }, _ )
 			| TInst( { cl_path = ([], "Class") }, _ )
 			| TInst( { cl_path = ([], "Enum") }, _ ) -> TInst(ttype,[])
+			| TInst( ({ cl_kind = KTypeParameter _ } as cl), _ ) when erase_generics && not (Meta.has Meta.NativeGeneric cl.cl_meta) ->
+				t_dynamic
 			| TEnum(_, [])
 			| TInst(_, []) -> t
 			| TInst(cl, params) when
@@ -870,10 +890,16 @@ let configure gen =
 					It works on cases such as Hash<T> returning Null<T> since cast_detect will invoke real_type at the original type,
 					Null<T>, which will then return the type haxe.lang.Null<>
 				*)
-				(match real_type t with
-					| TInst( { cl_kind = KTypeParameter _ }, _ ) -> TInst(null_t, [t])
-					| _ when is_cs_basic_type t -> TInst(null_t, [t])
-					| _ -> real_type t)
+				if erase_generics then
+					if is_cs_basic_type t then
+						t_dynamic
+					else
+						real_type t
+				else
+					(match real_type t with
+						| TInst( { cl_kind = KTypeParameter _ }, _ ) -> TInst(null_t, [t])
+						| _ when is_cs_basic_type t -> TInst(null_t, [t])
+						| _ -> real_type t)
 			| TAbstract _
 			| TType _ -> t
 			| TAnon (anon) when (match !(anon.a_status) with | Statics _ | EnumStatics _ | AbstractStatics _ -> true | _ -> false) -> t
@@ -916,7 +942,7 @@ let configure gen =
 			| true, x ->
 				dynamic_anon
 		in
-		if is_hxgeneric && List.exists (fun t -> match follow t with | TDynamic _ -> true | _ -> false) tl then
+		if is_hxgeneric && (erase_generics || List.exists (fun t -> match follow t with | TDynamic _ -> true | _ -> false) tl) then
 			List.map (fun _ -> t_dynamic) tl
 		else
 			List.map ret tl
@@ -1001,6 +1027,8 @@ let configure gen =
 	and path_param_s md path params =
 			match params with
 				| [] -> "global::" ^ module_s md
+				| _ when erase_generics && is_hxgeneric md ->
+					"global::" ^ module_s md
 				| _ ->
 					let params = (List.map (fun t -> t_s t) (change_param_type md params)) in
 					let str,params = module_s_params md params in
@@ -1593,8 +1621,7 @@ let configure gen =
 			expr_s w e;
 
 			(match params with
-				| [] -> ()
-				| params ->
+				| _ :: _ when not (erase_generics && field_is_hxgeneric e) ->
 					let md = match e.eexpr with
 						| TField(ef, _) ->
 							t_to_md (run_follow gen ef.etype)
@@ -1607,6 +1634,7 @@ let configure gen =
 						acc + 1
 					) 0 (change_param_type md params));
 					write w ">"
+				| _ -> ()
 			);
 
 			let rec loop acc elist tlist =
@@ -1744,11 +1772,10 @@ let configure gen =
 			ret
 	in
 
-	let get_string_params hxgen cl_params =
+	let get_string_params cl cl_params =
+		let hxgen = is_hxgen (TClassDecl cl) in
 		match cl_params with
-			| [] ->
-				("","")
-			| _ ->
+			| (_ :: _) when not (erase_generics && is_hxgeneric (TClassDecl cl)) ->
 				let get_param_name t = match follow t with TInst(cl, _) -> snd cl.cl_path | _ -> assert false in
 				let params = sprintf "<%s>" (String.concat ", " (List.map (fun (_, tcl) -> get_param_name tcl) cl_params)) in
 				let params_extends =
@@ -1788,6 +1815,7 @@ let configure gen =
 								| _ -> acc
 						) [] cl_params in
 				(params, String.concat " " params_extends)
+			| _ -> ("","")
 	in
 
 	let gen_field_decl w visibility v_n modifiers t n =
@@ -1971,7 +1999,7 @@ let configure gen =
 				(* public static void funcName *)
 				gen_field_decl w visibility v_n modifiers (if not is_new then (rett_s (run_follow gen ret_type)) else "") (change_field name);
 
-				let params, params_ext = get_string_params (is_hxgen (TClassDecl cl)) cf.cf_params in
+				let params, params_ext = get_string_params cl cf.cf_params in
 				(* <T>(string arg1, object arg2) with T : object *)
 				(match cf.cf_expr with
 				| Some { eexpr = TFunction tf } ->
@@ -2294,7 +2322,7 @@ let configure gen =
 		let modifiers = [access] @ modifiers in
 		print w "%s %s %s" (String.concat " " modifiers) clt (change_clname (snd cl.cl_path));
 		(* type parameters *)
-		let params, params_ext = get_string_params (is_hxgen (TClassDecl cl)) cl.cl_params in
+		let params, params_ext = get_string_params cl cl.cl_params in
 		let extends_implements = (match cl.cl_super with | None -> [] | Some (cl,p) -> [path_param_s (TClassDecl cl) cl.cl_path p]) @ (List.map (fun (cl,p) -> path_param_s (TClassDecl cl) cl.cl_path p) cl.cl_implements) in
 		(match extends_implements with
 			| [] -> print w "%s%s " params params_ext
@@ -2587,7 +2615,7 @@ let configure gen =
 
 	let tp_v = alloc_var "$type_param" t_dynamic in
 	let mk_tp t pos = { eexpr = TLocal(tp_v); etype = t; epos = pos } in
-	TypeParams.configure gen (fun ecall efield params elist ->
+	if not erase_generics then TypeParams.configure gen (fun ecall efield params elist ->
 		match efield.eexpr with
 		| TField(_, FEnum _) ->
 				{ ecall with eexpr = TCall(efield, elist) }
@@ -2595,7 +2623,7 @@ let configure gen =
 				{ ecall with eexpr = TCall(efield, (List.map (fun t -> mk_tp t ecall.epos ) params) @ elist) }
 	);
 
-	HardNullableSynf.configure gen (HardNullableSynf.traverse gen
+	if not erase_generics then HardNullableSynf.configure gen (HardNullableSynf.traverse gen
 		(fun e ->
 			match real_type e.etype with
 				| TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
@@ -2644,6 +2672,7 @@ let configure gen =
 	let explicit_fn_name c tl fname =
 		path_param_s (TClassDecl c) c.cl_path tl ^ "." ^ fname
 	in
+
 	FixOverrides.configure ~explicit_fn_name:explicit_fn_name gen;
 	Normalize.configure gen ~metas:(Hashtbl.create 0);
 
@@ -2734,7 +2763,12 @@ let configure gen =
 		mk_cast ecall.etype { ecall with eexpr = TCall(infer, call_args) }
 	in
 
-	handle_type_params gen ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject")));
+	if not erase_generics then
+		handle_type_params gen ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject")))
+	else begin
+		add_cast_handler gen;
+		TypeParams.RealTypeParams.RealTypeParamsModf.configure gen (TypeParams.RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen)
+	end;
 
 	let rcf_ctx = ReflectionCFs.new_ctx gen closure_t object_iface true rcf_on_getset_field rcf_on_call_field (fun hash hash_array ->
 		{ hash with eexpr = TCall(rcf_static_find, [hash; hash_array]); etype=basic.tint }
@@ -2775,9 +2809,14 @@ let configure gen =
 	fun e binop ->
 		match e.eexpr with
 			| TArray(e1, e2) ->
-				( match follow e1.etype with
+				(match follow e1.etype with
 					| TDynamic _ | TAnon _ | TMono _ -> true
 					| TInst({ cl_kind = KTypeParameter _ }, _) -> true
+					| TInst(c,p) when erase_generics && is_hxgeneric (TClassDecl c) && is_hxgen (TClassDecl c) -> (match c.cl_path with
+						| [],"String"
+						| ["cs"],"NativeArray" -> false
+						| _ ->
+							true)
 					| _ -> match binop, change_param_type (t_to_md e1.etype) [e.etype] with
 						| Some(Ast.OpAssignOp _), ([TDynamic _] | [TAnon _]) ->
 							true
@@ -2787,8 +2826,13 @@ let configure gen =
 
 	let field_is_dynamic t field =
 		match field_access_esp gen (gen.greal_type t) field with
-			| FEnumField _
-			| FClassField _ -> false
+			| FEnumField _ -> false
+			| FClassField (cl,p,_,_,_,t,_) ->
+				if not erase_generics then
+					false
+				else
+					let p = change_param_type (TClassDecl cl) p in
+					is_dynamic (apply_params cl.cl_params p t)
 			| _ -> true
 	in
 
@@ -2965,7 +3009,7 @@ let configure gen =
 		get_typeof e
 	));
 
-	CastDetect.configure gen (CastDetect.default_implementation gen (Some (TEnum(empty_e, []))) true ~native_string_cast:false ~overloads_cast_to_base:true);
+	CastDetect.configure gen (CastDetect.default_implementation gen (Some (TEnum(empty_e, []))) (not erase_generics) ~native_string_cast:false ~overloads_cast_to_base:true);
 
 	(*FollowAll.configure gen;*)
 

+ 2 - 0
std/cs/Boot.hx

@@ -26,7 +26,9 @@ import cs.internal.Function;
 import cs.internal.HxObject;
 import cs.internal.Runtime;
 import cs.internal.Iterator;
+#if !erase_generics
 import cs.internal.Null;
+#end
 import cs.internal.StringExt;
 #if unsafe
 import cs.internal.BoxedPointer;

+ 26 - 3
std/cs/Lib.hx

@@ -60,16 +60,23 @@ class Lib
 	@:extern inline public static function nativeArray<T>(arr:Array<T>, equalLengthRequired:Bool):NativeArray<T>
 	{
 		var ret = new cs.NativeArray(arr.length);
+#if erase_generics
+		for (i in 0...arr.length)
+			ret[i] = arr[i];
+#else
 		p_nativeArray(arr,ret);
+#end
 		return ret;
 	}
 
+#if !erase_generics
 	static function p_nativeArray<T>(arr:Array<T>, ret:cs.system.Array):Void
 	{
 		var native:NativeArray<T> = untyped arr.__a;
 		var len = arr.length;
 		cs.system.Array.Copy(native, 0, ret, 0, len);
 	}
+#end
 
 	/**
 		Provides support for the "as" keyword in C#.
@@ -120,14 +127,30 @@ class Lib
 		return untyped obj.GetType();
 	}
 
+#if erase_generics
+	inline private static function mkDynamic<T>(native:NativeArray<T>):NativeArray<Dynamic>
+	{
+		var ret = new cs.NativeArray<Dynamic>(native.Length);
+		for (i in 0...native.Length)
+			ret[i] = native[i];
+		return ret;
+	}
+#end
+
 	/**
 		Returns a Haxe Array of a native Array.
-		It won't copy the contents of the native array, so unless any operation triggers an array resize,
-		all changes made to the Haxe array will affect the native array argument.
+		Unless `erase_generics` is defined, it won't copy the contents of the native array,
+		so unless any operation triggers an array resize, all changes made to the Haxe array
+		will affect the native array argument.
 	**/
-	public static function array<T>(native:cs.NativeArray<T>):Array<T>
+	inline public static function array<T>(native:cs.NativeArray<T>):Array<T>
 	{
+#if erase_generics
+		var dyn:NativeArray<Dynamic> = mkDynamic(native);
+		return untyped Array.ofNative(dyn);
+#else
 		return untyped Array.ofNative(native);
+#end
 	}
 
 	/**

+ 5 - 11
std/cs/_std/Array.hx

@@ -24,26 +24,20 @@ import cs.NativeArray;
 #if core_api_serialize
 @:meta(System.Serializable)
 #end
-@:final @:coreApi class Array<T> implements ArrayAccess<T> {
+@:final class Array<T> implements ArrayAccess<T> {
 
 	public var length(default,null) : Int;
 
 	private var __a:NativeArray<T>;
 
-	@:functionCode('
-			return new Array<X>(native);
-	')
-	private static function ofNative<X>(native:NativeArray<X>):Array<X>
+	inline private static function ofNative<X>(native:NativeArray<X>):Array<X>
 	{
-		return null;
+		return new Array(native);
 	}
 
-	@:functionCode('
-			return new Array<Y>(new Y[size]);
-	')
-	private static function alloc<Y>(size:Int):Array<Y>
+	inline private static function alloc<Y>(size:Int):Array<Y>
 	{
-		return null;
+		return new Array(new NativeArray(size));
 	}
 
 	@:overload public function new() : Void

+ 50 - 63
std/cs/_std/Reflect.hx

@@ -22,91 +22,81 @@
 import cs.internal.Function;
 import cs.system.reflection.*;
 
-@:keep @:coreApi class Reflect {
+import cs.internal.*;
+import cs.internal.HxObject;
+import cs.internal.Runtime;
+import cs.Flags;
+import cs.Lib;
+import cs.system.Object;
+import cs.system.reflection.*;
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
-			return ((haxe.lang.IHxObject) o).__hx_getField(field, haxe.lang.FieldLookup.hash(field), false, true, false) != haxe.lang.Runtime.undefined;
+@:keep @:coreApi class Reflect {
 
-		return haxe.lang.Runtime.slowHasField(o, field);
-	')
 	public static function hasField( o : Dynamic, field : String ) : Bool
 	{
-		return false;
-	}
+		var ihx:IHxObject = Lib.as(o,IHxObject);
+		if (ihx != null) return untyped ihx.__hx_getField(field, FieldLookup.hash(field), false, true, false) != Runtime.undefined;
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
-			return ((haxe.lang.IHxObject) o).__hx_getField(field, haxe.lang.FieldLookup.hash(field), false, false, false);
+		return Runtime.slowHasField(o,field);
+	}
 
-		return haxe.lang.Runtime.slowGetField(o, field, false);
-	')
 	public static function field( o : Dynamic, field : String ) : Dynamic
 	{
-		return null;
+		var ihx:IHxObject = Lib.as(o,IHxObject);
+		if (ihx != null) return untyped ihx.__hx_getField(field, FieldLookup.hash(field), false, false, false);
+
+		return Runtime.slowGetField(o,field,false);
 	}
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
-			((haxe.lang.IHxObject) o).__hx_setField(field, haxe.lang.FieldLookup.hash(field), value, false);
-		else
-			haxe.lang.Runtime.slowSetField(o, field, value);
-	')
 	public static function setField( o : Dynamic, field : String, value : Dynamic ) : Void
 	{
-
+		var ihx:IHxObject = Lib.as(o,IHxObject);
+		if (ihx != null)
+			untyped ihx.__hx_setField(field, FieldLookup.hash(field), value, false);
+		else
+			Runtime.slowSetField(o,field,value);
 	}
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
-			return ((haxe.lang.IHxObject) o).__hx_getField(field, haxe.lang.FieldLookup.hash(field), false, false, true);
-
-		if (haxe.lang.Runtime.slowHasField(o, "get_" + field))
-			return haxe.lang.Runtime.slowCallField(o, "get_" + field, null);
-
-		return haxe.lang.Runtime.slowGetField(o, field, false);
-	')
 	public static function getProperty( o : Dynamic, field : String ) : Dynamic
 	{
-		return null;
+		var ihx:IHxObject = Lib.as(o,IHxObject);
+		if (ihx != null) return untyped ihx.__hx_getField(field, FieldLookup.hash(field), false, false, true);
+
+		if (Runtime.slowHasField(o, "get_" + field))
+			return Runtime.slowCallField(o, "get_" + field, null);
+
+		return Runtime.slowGetField(o, field, false);
 	}
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
-			((haxe.lang.IHxObject) o).__hx_setField(field, haxe.lang.FieldLookup.hash(field), value, true);
-		else if (haxe.lang.Runtime.slowHasField(o, "set_" + field))
-			haxe.lang.Runtime.slowCallField(o, "set_" + field, new Array<object>(new object[]{value}));
-		else
-			haxe.lang.Runtime.slowSetField(o, field, value);
-	')
 	public static function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void
 	{
-
+		var ihx:IHxObject = Lib.as(o,IHxObject);
+		if (ihx != null)
+			untyped ihx.__hx_setField(field, FieldLookup.hash(field), value, true);
+		else if (Runtime.slowHasField(o, 'set_$field'))
+			Runtime.slowCallField(o, 'set_$field', [value]);
+		else
+			Runtime.slowSetField(o,field,value);
 	}
 
-	@:functionCode('
-		return ((haxe.lang.Function) func).__hx_invokeDynamic(args);
-	')
 	public static function callMethod( o : Dynamic, func : haxe.Constraints.Function, args : Array<Dynamic> ) : Dynamic
 	{
-		return null;
+		return untyped cast(func, Function).__hx_invokeDynamic(args);
 	}
 
-	@:functionCode('
-		if (o is haxe.lang.IHxObject)
+	public static function fields( o : Dynamic ) : Array<String>
+	{
+		var ihx = Lib.as(o,IHxObject);
+		if (o != null)
 		{
-			Array<object> ret = new Array<object>();
-				((haxe.lang.IHxObject) o).__hx_getFields(ret);
+			var ret = [];
+			untyped ihx.__hx_getFields(ret);
 			return ret;
-		} else if (o is System.Type) {
-			return Type.getClassFields( (System.Type) o);
+		} else if (Std.is(o, cs.system.Type)) {
+			return Type.getClassFields(o);
 		} else {
-			return instanceFields( (System.Type) o );
+			return instanceFields( untyped o.GetType() );
 		}
-	')
-	public static function fields( o : Dynamic ) : Array<String>
-	{
-		return null;
 	}
 
 	private static function instanceFields( c : Class<Dynamic> ) : Array<String>
@@ -122,12 +112,9 @@ import cs.system.reflection.*;
 		return ret;
 	}
 
-	@:functionCode('
-		return f is haxe.lang.Function;
-	')
-	public static function isFunction( f : Dynamic ) : Bool
+	inline public static function isFunction( f : Dynamic ) : Bool
 	{
-		return false;
+		return Std.is(f, Function);
 	}
 
 	public static function compare<T>( a : T, b : T ) : Int
@@ -172,11 +159,11 @@ import cs.system.reflection.*;
 		}
 	}
 
-	@:functionCode('
-		return (o is haxe.lang.DynamicObject && ((haxe.lang.DynamicObject) o).__hx_deleteField(field, haxe.lang.FieldLookup.hash(field)));
-	')
 	public static function deleteField( o : Dynamic, field : String ) : Bool
 	{
+		var ihx = Lib.as(o,DynamicObject);
+		if (ihx != null)
+			return untyped ihx.__hx_deleteField(field, FieldLookup.hash(field));
 		return false;
 	}
 

+ 48 - 76
std/cs/_std/Type.hx

@@ -22,6 +22,8 @@
 import cs.Lib;
 import cs.internal.HxObject;
 import cs.internal.Runtime;
+import cs.Flags;
+import cs.system.Object;
 import cs.system.reflection.*;
 using StringTools;
 
@@ -39,24 +41,18 @@ using StringTools;
 
 @:keep @:coreApi class Type {
 
-	@:functionCode('
-		if (o == null || o is haxe.lang.DynamicObject || o is System.Type)
-			return null;
-
-		return o.GetType();
-	')
 	public static function getClass<T>( o : T ) : Class<T> untyped
 	{
-		return null;
+		if (Object.ReferenceEquals(o,null) || Std.is(o,DynamicObject) || Std.is(o,cs.system.Type))
+			return null;
+
+		return untyped o.GetType();
 	}
 
-	@:functionCode('
-		if (o is System.Enum || o is haxe.lang.Enum)
-			return o.GetType();
-		return null;
-	')
 	public static function getEnum( o : EnumValue ) : Enum<Dynamic> untyped
 	{
+		if (Std.is(o, cs.system.Enum) || Std.is(o,HxEnum))
+			return untyped o.GetType();
 		return null;
 	}
 
@@ -150,17 +146,13 @@ using StringTools;
 		}
 	}
 
-	@:functionCode('
-		if (name == "Bool") return typeof(bool);
-		System.Type t = resolveClass(name);
-		if (t != null && (t.BaseType.Equals(typeof(System.Enum)) || (typeof(haxe.lang.Enum)).IsAssignableFrom(t)))
-			return t;
-		return null;
-	')
 	public static function resolveEnum( name : String ) : Enum<Dynamic> untyped
 	{
+		if (name == "Bool") return Bool;
+		var t = Lib.toNativeType(resolveClass(name));
+		if (t != null && t.BaseType.Equals( Lib.toNativeType(cs.system.Enum) ) || Lib.toNativeType(HxEnum).IsAssignableFrom(t))
+			return t;
 		return null;
-		// if (ret != null && (ret.BaseType.Equals(cs.Lib.toNativeType(cs.system.Enum)) || ret.IsAssignableFrom(cs.Lib.toNativeType(haxe.
 	}
 
 	public static function createInstance<T>( cl : Class<T>, args : Array<Dynamic> ) : T
@@ -205,20 +197,17 @@ using StringTools;
 		return cs.system.Activator.CreateInstance(t);
 	}
 
-	@:functionCode('
-		if (@params == null || @params[0] == null)
+	public static function createEnum<T>( e : Enum<T>, constr : String, ?params : Array<Dynamic> ) : T
+	{
+		if (params == null || params[0] == null)
 		{
-			object ret = haxe.lang.Runtime.slowGetField(e, constr, true);
-			if (ret is haxe.lang.Function)
-				throw haxe.lang.HaxeException.wrap("Constructor " + constr + " needs parameters");
-			return (T) ret;
+			var ret = cs.internal.Runtime.slowGetField(e, constr, true);
+			if (Reflect.isFunction(ret))
+				throw 'Constructor $constr needs parameters';
+			return ret;
 		} else {
-			return (T) haxe.lang.Runtime.slowCallField(e, constr, @params);
+			return cs.internal.Runtime.slowCallField(e,constr,params);
 		}
-	')
-	public static function createEnum<T>( e : Enum<T>, constr : String, ?params : Array<Dynamic> ) : T
-	{
-		return null;
 	}
 
 	public static function createEnumIndex<T>( e : Enum<T>, index : Int, ?params : Array<Dynamic> ) : T {
@@ -253,27 +242,23 @@ using StringTools;
 		return ret;
 	}
 
-	@:functionCode('
-		Array<object> ret = new Array<object>();
-
-		if (c == typeof(string))
+	public static function getClassFields( c : Class<Dynamic> ) : Array<String> {
+		if (Object.ReferenceEquals(c, String))
 		{
-			ret.push("fromCharCode");
-			return ret;
+			return ['fromCharCode'];
 		}
 
-        System.Reflection.MemberInfo[] mis = c.GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
-        for (int i = 0; i < mis.Length; i++)
-        {
-            string n = mis[i].Name;
-			if (!n.StartsWith("__hx_"))
-				ret.push(mis[i].Name);
-        }
-
-        return ret;
-	')
-	public static function getClassFields( c : Class<Dynamic> ) : Array<String> {
-		return null;
+		var ret = [];
+		var infos = Lib.toNativeType(c).GetMembers(new Flags(BindingFlags.Public) | BindingFlags.Static);
+		for (i in 0...infos.Length)
+		{
+			var name = infos[i].Name;
+			if (!name.startsWith('__hx_'))
+			{
+				ret.push(name);
+			}
+		}
+		return ret;
 	}
 
 	public static function getEnumConstructs( e : Enum<Dynamic> ) : Array<String> {
@@ -337,48 +322,35 @@ using StringTools;
 		return null;
 	}
 
-	@:functionCode('
-			if (a is haxe.lang.Enum)
-				return a.Equals(b);
-			else
-				return haxe.lang.Runtime.eq(a, b);
-	')
 	public static function enumEq<T>( a : T, b : T ) : Bool
 	{
-		return untyped a.Equals(b);
+		if (a == null)
+			return b == null;
+		else if (b == null)
+			return false;
+		else
+			return untyped a.Equals(b);
 	}
 
-	@:functionCode('
-		if (e is System.Enum)
-			return e + "";
-		else
-			return ((haxe.lang.Enum) e).getTag();
-	')
 	public static function enumConstructor( e : EnumValue ) : String untyped
 	{
-		return e.tag;
+		return Std.is(e, cs.system.Enum) ? e+'' : cast(e,HxEnum).getTag();
 	}
 
-	@:functionCode('
-		return ( e is System.Enum ) ? new Array<object>() : ((haxe.lang.Enum) e).getParams();
-	')
 	public static function enumParameters( e : EnumValue ) : Array<Dynamic> untyped
 	{
-		return null;
+		return Std.is(e, cs.system.Enum) ? [] : cast(e,HxEnum).getParams();
 	}
 
-	@:functionCode('
-		if (e is System.Enum)
+	public static function enumIndex( e : EnumValue ) : Int  untyped
+	{
+		if (Std.is(e, cs.system.Enum))
 		{
-			System.Array values = System.Enum.GetValues(e.GetType());
-			return System.Array.IndexOf(values, e);
+			var values = cs.system.Enum.GetValues(Lib.nativeType(e));
+			return cs.system.Array.IndexOf(values, e);
 		} else {
-			return ((haxe.lang.Enum) e).index;
+			return cast(e, HxEnum).index;
 		}
-	')
-	public static function enumIndex( e : EnumValue ) : Int  untyped
-	{
-		return e.index;
 	}
 
 	public static function allEnums<T>( e : Enum<T> ) : Array<T>

+ 1 - 1
std/cs/internal/FieldLookup.hx

@@ -23,7 +23,7 @@ package cs.internal;
 
 @:native('haxe.lang.FieldLookup')
 @:final @:nativeGen
-@:keep @:static private class FieldLookup
+@:keep @:static class FieldLookup
 {
 
 	@:private private static var fieldIds:Array<Int>;

+ 2 - 2
std/cs/internal/Function.hx

@@ -27,7 +27,7 @@ package cs.internal;
  in modules (untested).
 **/
 
-@:keep @:abstract @:nativeGen @:native("haxe.lang.Function") private class Function
+@:keep @:abstract @:nativeGen @:native("haxe.lang.Function") class Function
 {
 	function new(arity:Int, type:Int)
 	{
@@ -77,4 +77,4 @@ package cs.internal;
 	{
 		return Runtime.callField(obj, field, hash, dynArgs);
 	}
-}
+}

+ 5 - 5
std/cs/internal/HxObject.hx

@@ -25,12 +25,12 @@ import haxe.ds.Vector;
 private typedef StdType = std.Type;
 
 @:keep @:native('haxe.lang.HxObject')
-private class HxObject implements IHxObject
+class HxObject implements IHxObject
 {
 }
 
 @:keep @:native('haxe.lang.IHxObject')
-private interface IHxObject
+interface IHxObject
 {
 }
 
@@ -38,7 +38,7 @@ private interface IHxObject
 @:meta(System.Serializable)
 #end
 @:keep @:native('haxe.lang.DynamicObject')
-private class DynamicObject extends HxObject implements Dynamic
+class DynamicObject extends HxObject implements Dynamic
 {
 	@:skipReflection public function toString():String
 	{
@@ -72,7 +72,7 @@ private class DynamicObject extends HxObject implements Dynamic
 #if core_api_serialize
 @:meta(System.Serializable)
 #end
-private class Enum
+class HxEnum
 {
 	@:readOnly private var index(default,never):Int;
 
@@ -98,7 +98,7 @@ private class Enum
 }
 
 @:keep @:native('haxe.lang.ParamEnum') @:nativeGen
-private class ParamEnum extends Enum
+private class ParamEnum extends HxEnum
 {
 	@:readOnly private var params(default,never):Vector<Dynamic>;
 

+ 119 - 61
std/cs/internal/Runtime.hx

@@ -29,6 +29,7 @@ import cs.system.IConvertible;
 import cs.system.IComparable;
 import cs.system.reflection.MethodBase;
 import cs.system.reflection.MethodInfo;
+import cs.system.reflection.*;
 import cs.system.Type;
 import cs.system.Object;
 
@@ -70,7 +71,7 @@ import cs.system.Object;
 ')
 @:keep class Runtime
 {
-	public static var undefined(default, never):Dynamic = { };
+	@:readOnly public static var undefined(default, never):Dynamic = new cs.system.Object();
 
 	@:functionCode('
 		return new haxe.lang.Closure(obj, field, hash);
@@ -104,28 +105,16 @@ import cs.system.Object;
 			if (t1 == cs.system.TypeCode.String || t2 == cs.system.TypeCode.String)
 				return false;
 
-			switch(t1)
+			switch [t1,t2]
 			{
-				case Decimal:
+				case [Decimal, _] | [_, Decimal]:
 					return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-				case UInt64 | Int64:
-					if (t2 == Decimal)
-						return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-					else
-						return v1c.ToUInt64(null) == v2c.ToUInt64(null);
-				default:
-					switch(t2)
-					{
-						case Decimal:
-							return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-						case UInt64 | Int64:
-							if (t2 == Decimal)
-								return v1c.ToDecimal(null) == v2c.ToDecimal(null);
-							else
-								return v1c.ToUInt64(null) == v2c.ToUInt64(null);
-						default:
-							return v1c.ToDouble(null) == v2c.ToDouble(null);
-					}
+				case [UInt64 | Int64 | DateTime, _] | [_, UInt64 | Int64 | DateTime]:
+					return v1c.ToUInt64(null) == v2c.ToUInt64(null);
+				case [Double | Single, _] | [_, Double | Single]:
+					return v1c.ToDouble(null) == v2c.ToDouble(null);
+				case _:
+					return v1c.ToInt32(null) == v2c.ToInt32(null);
 			}
 		}
 
@@ -254,6 +243,12 @@ import cs.system.Object;
 		if (Std.is(v1,String) || Std.is(v2,String))
 			return Std.string(v1) + Std.string(v2);
 
+		if (v1 == null)
+		{
+			if (v2 == null) return null;
+			v1 = 0;
+		} else if (v2 == null) v2 = 0;
+
 		var cv1 = Lib.as(v1, IConvertible);
 		if (cv1 != null)
 		{
@@ -555,6 +550,7 @@ import cs.system.Object;
 		}
 	}
 
+#if !erase_generics
 	@:functionCode('
 		if (nullableType.ContainsGenericParameters)
 			return haxe.lang.Null<object>.ofDynamic<object>(obj);
@@ -564,50 +560,119 @@ import cs.system.Object;
 	{
 		return null;
 	}
-
-	@:functionCode('
-		if (field == "toString")
+#else
+	public static function mkNullable(obj:Dynamic, nullable:Type):Dynamic
+	{
+		return obj; //do nothing
+	}
+#end
+
+	// @:functionCode('
+	// 	if (field == "toString")
+	// 	{
+	// 		if (args == null)
+	// 			return obj.ToString();
+	// 		field = "ToString";
+	// 	}
+	// 	if (args == null) args = new Array<object>();
+
+	// 	System.Reflection.BindingFlags bf;
+	// 	System.Type t = obj as System.Type;
+	// 	if (t == null)
+	// 	{
+	// 		string s = obj as string;
+	// 		if (s != null)
+	// 			return haxe.lang.StringRefl.handleCallField(s, field, args);
+	// 		t = obj.GetType();
+	// 		bf = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy;
+	// 	} else {
+	// 		if (t == typeof(string) && field.Equals("fromCharCode"))
+	// 			return haxe.lang.StringExt.fromCharCode(toInt(args[0]));
+	// 		obj = null;
+	// 		bf = System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public;
+	// 	}
+
+	// 	System.Reflection.MethodInfo[] mis = t.GetMethods(bf);
+	// 	int last = 0;
+	// 	for (int i = 0; i < mis.Length; i++)
+	// 	{
+	// 		string name = mis[i].Name;
+	// 		if (name.Equals(field))
+	// 		{
+	// 			mis[last++] = mis[i];
+	// 		}
+	// 	}
+
+	// 	if (last == 0 && (field == "__get" || field == "__set"))
+	// 	{
+	// 		field = field == "__get" ? "get_Item" : "set_Item";
+	// 		for (int i = 0; i < mis.Length; i++)
+	// 		{
+	// 			string name = mis[i].Name;
+	// 			if (name.Equals(field))
+	// 			{
+	// 				mis[last++] = mis[i];
+	// 			}
+	// 		}
+	// 	}
+
+	// 	if (last == 0 && t.IsCOMObject)
+	// 	{
+	// 		object[] oargs = new object[arrLen(args)];
+	// 		for (int i = 0; i < oargs.Length; i++)
+	// 		{
+	// 			oargs[i] = args[i];
+	// 		}
+	// 		return t.InvokeMember(field, System.Reflection.BindingFlags.InvokeMethod, null, obj, oargs);
+	// 	}
+
+	// 	if (last == 0)
+	// 	{
+	// 		throw haxe.lang.HaxeException.wrap("Method \'" + field + "\' not found on type " + t);
+	// 	}
+
+	// 	return haxe.lang.Runtime.callMethod(obj, mis, last, args);
+	// ')
+	public static function slowCallField(obj:Dynamic, field:String, args:Array<Dynamic>):Dynamic
+	{
+		if (field == "toString" && (args == null || args.length == 0))
 		{
-			if (args == null)
-				return obj.ToString();
-			field = "ToString";
+			return obj.ToString();
 		}
-		if (args == null) args = new Array<object>();
+		if (args == null) args = [];
 
-		System.Reflection.BindingFlags bf;
-		System.Type t = obj as System.Type;
+		var bf:BindingFlags;
+		var t = Lib.as(obj,cs.system.Type);
 		if (t == null)
 		{
-			string s = obj as string;
+			var s = Lib.as(obj,String);
 			if (s != null)
-				return haxe.lang.StringRefl.handleCallField(s, field, args);
-			t = obj.GetType();
-			bf = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy;
+				return cs.internal.StringExt.StringRefl.handleCallField(untyped s, untyped field, args);
+			t = untyped obj.GetType();
+			bf = new Flags(BindingFlags.Instance) | BindingFlags.Public | BindingFlags.FlattenHierarchy;
 		} else {
-			if (t == typeof(string) && field.Equals("fromCharCode"))
-				return haxe.lang.StringExt.fromCharCode(toInt(args[0]));
+			if (t == Lib.toNativeType(String) && field == 'fromCharCode')
+				return cs.internal.StringExt.fromCharCode(toInt(args[0]));
 			obj = null;
-			bf = System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public;
+			bf = new Flags(BindingFlags.Static) | BindingFlags.Public;
 		}
 
-		System.Reflection.MethodInfo[] mis = t.GetMethods(bf);
-		int last = 0;
-		for (int i = 0; i < mis.Length; i++)
+		var mis:NativeArray<MethodBase> = untyped t.GetMethods(bf);
+		var last = 0;
+		for (i in 0...mis.Length)
 		{
-			string name = mis[i].Name;
-			if (name.Equals(field))
-			{
+			var name = mis[i].Name;
+			if (name == field)
 				mis[last++] = mis[i];
-			}
 		}
 
 		if (last == 0 && (field == "__get" || field == "__set"))
 		{
 			field = field == "__get" ? "get_Item" : "set_Item";
-			for (int i = 0; i < mis.Length; i++)
+			for (i in 0...mis.Length)
 			{
-				string name = mis[i].Name;
-				if (name.Equals(field))
+				var name = mis[i].Name;
+				if (name == field)
 				{
 					mis[last++] = mis[i];
 				}
@@ -616,29 +681,20 @@ import cs.system.Object;
 
 		if (last == 0 && t.IsCOMObject)
 		{
-			object[] oargs = new object[arrLen(args)];
-			for (int i = 0; i < oargs.Length; i++)
+			var oargs = new NativeArray(args.length);
+			for (i in 0...oargs.Length)
 			{
 				oargs[i] = args[i];
 			}
-			return t.InvokeMember(field, System.Reflection.BindingFlags.InvokeMethod, null, obj, oargs);
+			return t.InvokeMember(field, BindingFlags.InvokeMethod, null, obj, oargs);
 		}
 
 		if (last == 0)
 		{
-			throw haxe.lang.HaxeException.wrap("Method \'" + field + "\' not found on type " + t);
+			throw 'Method "$field" not found on type $t';
 		}
 
-		return haxe.lang.Runtime.callMethod(obj, mis, last, args);
-	')
-	public static function slowCallField(obj:Dynamic, field:String, args:Array<Dynamic>):Dynamic
-	{
-		throw "not implemented";
-	}
-
-	@:private static function arrLen(arr:Array<Dynamic>):Int
-	{
-		return arr.length;
+		return Runtime.callMethod(obj, mis, last, args);
 	}
 
 	@:functionCode('
@@ -735,6 +791,7 @@ import cs.system.Object;
 	}
 
 
+#if !erase_generics
 	@:functionCode('
 		if (obj is To)
 			return (To) obj;
@@ -755,6 +812,7 @@ import cs.system.Object;
 	{
 		return null;
 	}
+#end
 
 	@:functionCode('
 		return (s1 == null ? "null" : s1) + (s2 == null ? "null" : s2);

+ 92 - 111
std/cs/internal/StringExt.hx

@@ -21,153 +21,143 @@
  */
 package cs.internal;
 import cs.internal.Function;
-private typedef NativeString = String;
+private typedef NativeString = cs.system.String;
 
 @:keep @:nativeGen @:native("haxe.lang.StringExt") class StringExt
 {
+	@:readOnly static var empty(default,never) = new NativeString(cast 0,0);
 
-	@:functionCode('
-			if ( ((uint) index) >= me.Length)
-				return "";
-			else
-				return new string(me[index], 1);
-	')
 	public static function charAt(me:NativeString, index:Int):NativeString
 	{
-		return null;
+		if (cast(index,UInt) >= me.Length)
+			return empty;
+		else
+			return new NativeString(me[index], 1);
 	}
 
 	public static function charCodeAt(me:NativeString, index:Int):Null<Int>
 	{
-		if (cast(index,UInt) >= me.length)
+		if (cast(index,UInt) >= me.Length)
 			return null;
 		else
-			return cast me[index];
+			return cast(me[index], Int);
 	}
 
-	public static function indexOf(me:NativeString, str:NativeString, ?startIndex:Int):Int
+	public static function indexOf(me:NativeString, str:String, ?startIndex:Int):Int
 	{
 		var sIndex:Int = startIndex != null ? startIndex : 0;
-		if (sIndex >= me.length)
+		if (sIndex >= me.Length)
 			return -1;
 		return @:privateAccess me.IndexOf(str, sIndex, cs.system.StringComparison.Ordinal);
 	}
 
-	@:functionCode('
-			int sIndex = (startIndex.hasValue) ? (startIndex.@value) : (me.Length - 1);
-			if (sIndex >= me.Length)
-				sIndex = me.Length - 1;
-			else if (sIndex < 0)
-				return -1;
+	public static function lastIndexOf(me:NativeString, str:NativeString, ?startIndex:Int):Int
+	{
+		var sIndex:Int = startIndex == null ? me.Length - 1 : startIndex;
+		if (sIndex >= me.Length)
+			sIndex = me.Length - 1;
+		else if (sIndex < 0)
+			return -1;
 
-			//TestBaseTypes.hx@133 fix
-			if (startIndex.hasValue)
+		//TestBaseTypes.hx@133 fix
+		if (startIndex != null)
+		{
+			var i = sIndex + 1;
+			while (i --> 0)
 			{
-				for(int i = sIndex; i >= 0; i--)
+				var found = true;
+				for (j in 0...str.Length)
 				{
-					bool found = true;
-					for(int j = 0; j < str.Length; j++)
+					if(me[i + j] != str[j])
 					{
-						if(me[i + j] != str[j])
-						{
-							found = false;
-							break;
-						}
+						found = false;
+						break;
 					}
-
-					if (found)
-						return i;
 				}
 
-				return -1;
-			} else {
-				return me.LastIndexOf(str, sIndex, System.StringComparison.Ordinal);
+				if (found)
+					return i;
 			}
-	')
-	public static function lastIndexOf(me:NativeString, str:NativeString, ?startIndex:Int):Int
-	{
+
+			return -1;
+		} else {
+			return me.LastIndexOf(untyped str, sIndex, cs.system.StringComparison.Ordinal);
+		}
 		return -1;
 	}
 
-	@:functionCode('
-			string[] native;
-			if (delimiter.Length == 0)
-			{
-				int len = me.Length;
-				native = new string[len];
-				for (int i = 0; i < len; i++)
-					native[i] = new string(me[i], 1);
-			} else {
-				native = me.Split(new string[] { delimiter }, System.StringSplitOptions.None);
-			}
-			return new Array<object>(native);
-	')
-	public static function split(me:NativeString, delimiter:NativeString):Array<NativeString>
+	public static function split(me:NativeString, delimiter:NativeString):Array<String>
 	{
-		return null;
+		var native:NativeArray<String>;
+		if (delimiter.Length == 0)
+		{
+			var len = me.Length;
+			native = new NativeArray(len);
+			for (i in 0...len)
+				native[i] = untyped new NativeString(me[i],1);
+		} else {
+			var str = new NativeArray<String>(1);
+			str[0] = cast delimiter;
+
+			native = me.Split(str, cs.system.StringSplitOptions.None);
+		}
+
+		return cs.Lib.array(native);
 	}
 
-	@:functionCode('
-			int meLen = me.Length;
-			int targetLen = meLen;
-			if (len.hasValue)
-			{
-				targetLen = len.@value;
-				if (targetLen == 0)
-					return "";
-				if( pos != 0 && targetLen < 0 ){
-					return "";
-				}
-			}
+	public static function substr(me:NativeString, pos:Int, ?len:Int):String
+	{
+		var meLen = me.Length;
+		var targetLen = meLen;
+		if (len != null)
+		{
+			targetLen = len;
+			if (targetLen == 0 || (pos != 0 && targetLen < 0))
+				return "";
+		}
 
-			if( pos < 0 ){
-				pos = meLen + pos;
-				if( pos < 0 ) pos = 0;
-			} else if( targetLen < 0 ){
-				targetLen = meLen + targetLen - pos;
-			}
+		if (pos < 0)
+		{
+			pos = meLen + pos;
+			if (pos < 0) pos = 0;
+		} else if (targetLen < 0) {
+			targetLen = meLen + targetLen - pos;
+		}
 
-			if( pos + targetLen > meLen ){
-				targetLen = meLen - pos;
-			}
+		if (pos + targetLen > meLen)
+		{
+			targetLen = meLen - pos;
+		}
 
-			if ( pos < 0 || targetLen <= 0 ) return "";
+		if (pos < 0 || targetLen <= 0) return "";
 
-			return me.Substring(pos, targetLen);
-	')
-	public static function substr(me:NativeString, pos:Int, ?len:Int):NativeString
-	{
-		return null;
+		return me.Substring(pos, targetLen);
 	}
 
-	@:functionCode('
-		int endIdx;
-		int len = me.Length;
-		if ( !endIndex.hasValue ) {
+	public static function substring(me:NativeString, startIndex:Int, ?endIndex:Int):String
+	{
+		var len = me.Length;
+		var endIdx:Int;
+		if (endIndex == null)
 			endIdx = len;
-		} else if ( (endIdx = endIndex.@value) < 0 ) {
+		else if ( (endIdx = endIndex) < 0 )
 			endIdx = 0;
-		} else if ( endIdx > len ) {
+		else if (endIdx > len)
 			endIdx = len;
-		}
 
-		if ( startIndex < 0 ) {
+		if (startIndex < 0)
 			startIndex = 0;
-		} else if ( startIndex > len ) {
+		else if (startIndex > len)
 			startIndex = len;
-		}
 
-		if ( startIndex > endIdx ) {
-			int tmp = startIndex;
+		if (startIndex > endIdx)
+		{
+			var tmp = startIndex;
 			startIndex = endIdx;
 			endIdx = tmp;
 		}
 
 		return me.Substring(startIndex, endIdx - startIndex);
-	')
-	public static function substring(me:NativeString, startIndex:Int, ?endIndex:Int):NativeString
-	{
-		return null;
 	}
 
 	public static function toString(me:NativeString):NativeString
@@ -175,20 +165,14 @@ private typedef NativeString = String;
 		return me;
 	}
 
-	@:functionCode('
-			return me.ToLowerInvariant();
-	')
-	public static function toLowerCase(me:NativeString):NativeString
+	public static function toLowerCase(me:NativeString):String
 	{
-		return null;
+		return me.ToLowerInvariant();
 	}
 
-	@:functionCode('
-			return me.ToUpperInvariant();
-	')
-	public static function toUpperCase(me:NativeString):NativeString
+	public static function toUpperCase(me:NativeString):String
 	{
-		return null;
+		return me.ToUpperInvariant();
 	}
 
 	public static function toNativeString(me:NativeString):NativeString
@@ -196,12 +180,9 @@ private typedef NativeString = String;
 		return me;
 	}
 
-	@:functionCode('
-			return new string( (char) code, 1 );
-	')
 	public static function fromCharCode(code:Int):NativeString
 	{
-		return null;
+		return new NativeString( cast(code,cs.StdTypes.Char16), 1 );
 	}
 }
 
@@ -209,7 +190,7 @@ private typedef NativeString = String;
 {
 	public static var fields = ["length", "toUpperCase", "toLowerCase", "charAt", "charCodeAt", "indexOf", "lastIndexOf", "split", "substr", "substring"];
 
-	public static function handleGetField(str:NativeString, f:NativeString, throwErrors:Bool):Dynamic
+	public static function handleGetField(str:String, f:String, throwErrors:Bool):Dynamic
 	{
 		switch(f)
 		{
@@ -224,7 +205,7 @@ private typedef NativeString = String;
 		}
 	}
 
-	public static function handleCallField(str:NativeString, f:NativeString, args:Array<Dynamic>):Dynamic
+	public static function handleCallField(str:NativeString, f:String, args:Array<Dynamic>):Dynamic
 	{
 		var _args:Array<Dynamic> = [str];
 		if (args == null)

+ 12 - 0
tests/RunCi.hx

@@ -559,12 +559,24 @@ class RunCi {
 
 							runCommand("haxe", ["compile-cs-unsafe-travis.hxml"]);
 							runExe("bin/cs_unsafe/bin/Test-Debug.exe");
+
+							runCommand("haxe", ["compile-cs-travis.hxml","-D","erase_generics"]);
+							runExe("bin/cs/bin/Test-Debug.exe");
+
+							runCommand("haxe", ["compile-cs-unsafe-travis.hxml","-D","erase_generics"]);
+							runExe("bin/cs_unsafe/bin/Test-Debug.exe");
 						case _:
 							runCommand("haxe", ["compile-cs.hxml"]);
 							runExe("bin/cs/bin/Test-Debug.exe");
 
 							runCommand("haxe", ["compile-cs-unsafe.hxml"]);
 							runExe("bin/cs_unsafe/bin/Test-Debug.exe");
+
+							runCommand("haxe", ["compile-cs.hxml","-D","erase_generics"]);
+							runExe("bin/cs/bin/Test-Debug.exe");
+
+							runCommand("haxe", ["compile-cs-unsafe.hxml","-D","erase_generics"]);
+							runExe("bin/cs_unsafe/bin/Test-Debug.exe");
 					}
 
 					changeDirectory(sysDir);