فهرست منبع

[java/cs] Disable `implements Dynamic` support (#7403)

Simon Krajewski 7 سال پیش
والد
کامیت
ae84ea9839

+ 37 - 186
src/codegen/gencommon/reflectionCFs.ml

@@ -104,6 +104,7 @@ type rcf_ctx =
 	rcf_optimize : bool;
 
 	rcf_object_iface : tclass;
+	rcf_dynamic_data_class : tclass option;
 
 	rcf_max_func_arity : int;
 
@@ -142,12 +143,13 @@ type rcf_ctx =
 	rcf_on_call_field : texpr->texpr->string->int32 option->texpr list->texpr;
 }
 
-let new_ctx gen ft object_iface optimize dynamic_getset_field dynamic_call_field hash_function lookup_function insert_function remove_function hash_conflict_ctx rcf_mk_exception =
+let new_ctx gen ft object_iface ?dynamic_data_class optimize dynamic_getset_field dynamic_call_field hash_function lookup_function insert_function remove_function hash_conflict_ctx rcf_mk_exception =
 	{
 		rcf_gen = gen;
 		rcf_ft = ft;
 
 		rcf_optimize = optimize;
+		rcf_dynamic_data_class = dynamic_data_class;
 
 		rcf_object_iface = object_iface;
 
@@ -560,12 +562,6 @@ let get_delete_field ctx cl is_dynamic =
 	cf.cf_expr <- Some({ eexpr = TFunction(fn); etype = fun_type; epos = pos });
 	cf
 
-let rec is_first_dynamic cl =
-	match cl.cl_super with
-		| Some(cl,_) ->
-			if is_some cl.cl_dynamic then false else is_first_dynamic cl
-		| None -> true
-
 let is_override cl = match cl.cl_super with
 	| Some (cl, _) when is_hxgen (TClassDecl cl) -> true
 	| _ -> false
@@ -605,75 +601,6 @@ let implement_dynamic_object_ctor ctx cl =
 	let basic = gen.gcon.basic in
 	let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
 
-	let hashes_field = mk_internal_name "hx" "hashes", gen.gclasses.nativearray hasht in
-	let hashes_f_field = mk_internal_name "hx" "hashes_f", gen.gclasses.nativearray hasht in
-	let dynamics_field = mk_internal_name "hx" "dynamics", gen.gclasses.nativearray t_empty in
-	let dynamics_f_field = mk_internal_name "hx" "dynamics_f", gen.gclasses.nativearray basic.tfloat in
-	let fields =
-	[
-		hashes_field;
-		dynamics_field;
-		hashes_f_field;
-		dynamics_f_field;
-	] in
-
-	let hashes_var = alloc_var (fst hashes_field) (snd hashes_field) in
-	let hashes_f_var = alloc_var (fst hashes_f_field) (snd hashes_f_field) in
-	let tf_args = [
-		hashes_var, None;
-		alloc_var (fst dynamics_field) (snd dynamics_field), None;
-		hashes_f_var, None;
-		alloc_var (fst dynamics_f_field) (snd dynamics_f_field), None;
-	] in
-
-	let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
-	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-	let fun_t = TFun(fun_args tf_args,basic.tvoid) in
-	let ctor = mk_class_field "new" fun_t true pos (Method MethNormal) [] in
-	ctor.cf_expr <- Some(
-	{
-		eexpr = TFunction({
-			tf_args = tf_args;
-			tf_type = basic.tvoid;
-			tf_expr =
-			{
-				eexpr = TBlock(
-					List.map (fun (v,_) ->
-						{ eexpr = TBinop(Ast.OpAssign, mk_this v.v_name v.v_type, mk_local v pos); etype = v.v_type; epos = pos }
-					) tf_args
-					@
-					[
-						mk (TBinop(OpAssign, mk_this (mk_internal_name "hx" "length") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_var pos) pos)) basic.tint pos;
-						mk (TBinop(OpAssign, mk_this (mk_internal_name "hx" "length_f") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_f_var pos) pos)) basic.tint pos;
-					]
-				);
-				etype = basic.tvoid;
-				epos = pos
-			}
-		});
-		etype = fun_t;
-		epos = pos
-	});
-
-	add_constructor cl ctor;
-	(* default ctor also *)
-	let ctor = mk_class_field "new" (TFun([],basic.tvoid)) false pos (Method MethNormal) [] in
-	ctor.cf_expr <- Some {
-		eexpr = TFunction {
-			tf_type = basic.tvoid;
-			tf_args = [];
-			tf_expr = {
-				eexpr = TBlock(List.map (fun (f,t) ->
-					{ eexpr = TBinop(Ast.OpAssign, mk_this f t,{ eexpr = TCall(mk (TIdent "__array__") t_dynamic pos, []); etype = t; epos = pos; }); etype = t; epos = pos }
-				) fields);
-				etype = basic.tvoid;
-				epos = pos;
-			}
-		};
-		etype = ctor.cf_type;
-		epos = pos;
-	};
-	add_constructor cl ctor;
 	(* and finally we will return a function that transforms a TObjectDecl into a new DynamicObject() call *)
 	let rec loop objdecl acc acc_f =
 		match objdecl with
@@ -724,7 +651,6 @@ let implement_dynamic_object_ctor ctx cl =
 		in
 
 		let odecl, odecl_f = List.sort sort_fn odecl, List.sort sort_fn odecl_f in
-
 		let ret = {
 			e with eexpr = TNew(cl,[],
 				[
@@ -745,79 +671,6 @@ let implement_dynamic_object_ctor ctx cl =
 	in
 	do_objdecl
 
-let implement_dynamics ctx cl =
-	let pos = cl.cl_pos in
-	let is_override = is_override cl in
-	if is_some cl.cl_dynamic then begin
-		if is_first_dynamic cl then begin
-			(*
-				* add hx_hashes, hx_hashes_f, hx_dynamics, hx_dynamics_f to class
-				* implement hx_deleteField
-			*)
-			let gen = ctx.rcf_gen in
-			let basic = gen.gcon.basic in
-			let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
-
-			let new_fields =
-			[
-				mk_class_field (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				mk_class_field (mk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				mk_class_field (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				mk_class_field (mk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-			] in
-
-			(if cl.cl_path <> (["haxe"; "lang"], "DynamicObject") then
-				List.iter (fun cf -> cf.cf_expr <- Some { eexpr = TCall(mk (TIdent "__array__") t_dynamic pos, []); etype = cf.cf_type; epos = cf.cf_pos }) new_fields
-			);
-
-			let new_fields =
-				if ctx.rcf_optimize then
-					let f = mk_class_field (mk_internal_name "hx" "conflicts") (Option.get ctx.rcf_hash_conflict_ctx).t false pos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
-					f :: new_fields
-				else
-					new_fields
-			in
-
-			let delete = get_delete_field ctx cl true in
-
-			let new_fields = new_fields @ [
-				mk_class_field (mk_internal_name "hx" "length") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				mk_class_field (mk_internal_name "hx" "length_f") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				delete;
-			] in
-
-			List.iter (fun cf ->
-				cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
-			) new_fields;
-
-	(*
-			let rec last_ctor cl =
-				match cl.cl_constructor with
-					| None -> (match cl.cl_super with | None -> None | Some (cl,_) -> last_ctor cl)
-					| Some c -> Some c
-			in
-	*)
-			(*
-				in order for the next to work, we need to execute our script before InitFunction, so the expressions inside the variables are initialized by the constructor
-			*)
-			(*
-				Now we need to add their initialization.
-				This will consist of different parts:
-					Check if there are constructors. If not, create one and add initialization to it (calling super, ok)
-					If there are, add as first statement (or second if there is a super() call in the first)
-					If class has @:dynamicObject meta, also create another new() class with its parameters as constructor arguments
-			*)
-
-			cl.cl_ordered_fields <- cl.cl_ordered_fields @ new_fields;
-			if is_override then cl.cl_overrides <- delete :: cl.cl_overrides
-		end
-	end else if not is_override then begin
-		let delete = get_delete_field ctx cl false in
-		cl.cl_ordered_fields <- cl.cl_ordered_fields @ [delete];
-		cl.cl_fields <- PMap.add delete.cf_name delete cl.cl_fields
-	end
-
-
 (*
 	Implements:
 		__hx_lookupField(field:String, throwErrors:Bool, isCheck:Bool, handleProperties:Bool, isFirst:Bool):Dynamic
@@ -927,25 +780,16 @@ let implement_final_lookup ctx cl =
 			if is_override then cl.cl_overrides <- cf :: cl.cl_overrides
 		) cfs
 	in
-
-	if is_some cl.cl_dynamic then begin
-		(* let abstract_dyn_lookup_implementation ctx this hash_local may_value is_float pos = *)
-		(* callback : is_float fields_args switch_var throw_errors_option is_check_option value_option : texpr list *)
-		if is_first_dynamic cl then
-			create_cfs true (fun is_float fields_args switch_var _ _ value_opt ->
-				let v_name = match fields_args with (v,_) :: _ -> v | _ -> assert false in
-				abstract_dyn_lookup_implementation ctx this (mk_local v_name pos) (mk_local switch_var pos) (Option.map (fun v -> mk_local v pos) value_opt) is_float pos
-			)
-	end else if not is_override then begin
+	if not is_override then begin
 		create_cfs false (fun is_float fields_args switch_var _ _ value_opt ->
 			match value_opt with
-				| None -> (* is not set *)
-					[]
-				| Some _ -> (* is set *)
-					if is_float then
-						[ mk_throw "Cannot access field for writing or incompatible type." pos ]
-					else
-						[ mk_throw "Cannot access field for writing." pos ]
+			| None -> (* is not set *)
+				[]
+			| Some _ -> (* is set *)
+				if is_float then
+					[ mk_throw "Cannot access field for writing or incompatible type." pos ]
+				else
+					[ mk_throw "Cannot access field for writing." pos ]
 		)
 	end
 
@@ -1228,22 +1072,14 @@ let implement_getFields ctx cl =
 	(*
 		if it is first_dynamic, then we need to enumerate the dynamic fields
 	*)
-	let exprs =
-		if is_some cl.cl_dynamic && is_first_dynamic cl then begin
-			has_value := true;
-			enumerate_dynamic_fields ctx cl mk_push base_arr
-		end else
-			[]
-	in
-
 	let exprs =
 		if is_override cl then
 			let tparams = List.map snd cl.cl_params in
 			let esuper = mk (TConst TSuper) (TInst(cl, tparams)) pos in
 			let efield = mk (TField (esuper, FInstance (cl, tparams, cf))) t pos in
-			exprs @ [mk (TCall (efield, [base_arr])) basic.tvoid pos]
+			[mk (TCall (efield, [base_arr])) basic.tvoid pos]
 		else
-			exprs
+			[]
 	in
 
 	let exprs = map_fields (collect_fields cl (Some false)) @ exprs in
@@ -1640,10 +1476,7 @@ struct
 					(* don't add any base classes to abstract implementations *)
 					()
 				| TClassDecl ({ cl_super = None } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
-					if is_some cl.cl_dynamic then
-						cl.cl_super <- Some (basedynamic,[])
-					else
-						cl.cl_super <- Some (baseclass,[])
+					cl.cl_super <- Some (baseclass,[])
 				| TClassDecl ({ cl_super = Some(super,_) } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && not (is_hxgen (TClassDecl super)) ->
 					cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
 				| _ ->
@@ -1659,15 +1492,33 @@ end;;
 *)
 let priority = solve_deps name [DAfter UniversalBaseClass.priority]
 
+let add_override cl cf =
+	if List.memq cf cl.cl_overrides then
+		cl.cl_overrides
+	else
+		cf :: cl.cl_overrides
+
+let has_field_override cl name =
+	try
+		cl.cl_overrides <- add_override cl (PMap.find name cl.cl_fields);
+		true
+	with | Not_found ->
+		false
+
 let configure ctx baseinterface ~slow_invoke =
 	let run md =
 		(match md with
 		| TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) ->
-			implement_dynamics ctx cl;
-			if not (PMap.mem (mk_internal_name "hx" "lookupField") cl.cl_fields) then implement_final_lookup ctx cl;
-			if not (PMap.mem (mk_internal_name "hx" "getField") cl.cl_fields) then implement_get_set ctx cl;
-			if not (PMap.mem (mk_internal_name "hx" "invokeField") cl.cl_fields) then implement_invokeField ctx slow_invoke cl;
-			if not (PMap.mem (mk_internal_name "hx" "getFields") cl.cl_fields) then implement_getFields ctx cl;
+			if is_some cl.cl_super then begin
+				ignore (has_field_override cl (mk_internal_name "hx" "setField"));
+				ignore (has_field_override cl (mk_internal_name "hx" "setField_f"));
+				ignore (has_field_override cl (mk_internal_name "hx" "getField_f"));
+			end;
+
+			if not (has_field_override cl (mk_internal_name "hx" "lookupField")) then implement_final_lookup ctx cl;
+			if not (has_field_override cl (mk_internal_name "hx" "getField")) then implement_get_set ctx cl;
+			if not (has_field_override cl (mk_internal_name "hx" "invokeField")) then implement_invokeField ctx slow_invoke cl;
+			if not (has_field_override cl (mk_internal_name "hx" "getFields")) then implement_getFields ctx cl;
 		| _ -> ());
 		md
 	in

+ 7 - 11
src/generators/gencs.ml

@@ -2773,17 +2773,13 @@ let generate con =
 		let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" null_pos [] in
 
 		let rcf_static_insert, rcf_static_remove =
-			if erase_generics then begin
-				let get_specialized_postfix t = match t with
-					| TAbstract({a_path = [],("Float" | "Int" as name)}, _) -> name
-					| TAnon _ | TDynamic _ -> "Dynamic"
-					| _ -> print_endline (debug_type t); assert false
-				in
-				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos []),
-				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [])
-			end else
-				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "insert" null_pos [t]),
-				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "remove" null_pos [t])
+			let get_specialized_postfix t = match t with
+				| TAbstract({a_path = [],("Float" | "Int" as name)}, _) -> name
+				| TAnon _ | TDynamic _ -> "Dynamic"
+				| _ -> print_endline (debug_type t); assert false
+			in
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos []),
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [])
 		in
 
 		let can_be_float = like_float in

+ 12 - 72
std/cs/internal/FieldLookup.hx

@@ -129,7 +129,6 @@ package cs.internal;
 
 	public static function lookupHash(key:Int):String
 	{
-		//start of binary search algorithm
 		var ids = fieldIds;
 		var min = 0;
 		var max = length;
@@ -157,7 +156,6 @@ package cs.internal;
 
 		var key = doHash(s);
 
-		//start of binary search algorithm
 		var ids = fieldIds,
 		    fld = fields;
 		var min = 0;
@@ -188,15 +186,8 @@ package cs.internal;
 			if (len != length) //race condition which will very rarely happen - other thread modified sooner.
 				return hash(s); //since we already own the lock, this second try will always succeed
 
-#if erase_generics
 			fieldIds = insertInt(fieldIds, length, min, key);
 			fields = insertString(fields, length, min, s);
-#else
-			insert(fieldIds, length, min, key);
-			insert(fields, length, min, s);
-			// ids.insert(min, key);
-			// fields.insert(min, s);
-#end
 			++length;
 		});
 		return key;
@@ -209,7 +200,7 @@ package cs.internal;
 
 		while (min < max)
 		{
-			var mid = Std.int((max + min) / 2); //overflow safe
+			var mid = Std.int((max + min) / 2);
 			var imid = hashs[mid];
 			if (hash < imid)
 			{
@@ -224,67 +215,17 @@ package cs.internal;
 		return ~min;
 	}
 
-	#if !erase_generics
-	static function remove<T>(a:cs.NativeArray<T>, length:Int, pos:Int)
-	{
-		cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
-		a[length - 1] = null;
-	}
-
-	static function insert<T>(a:cs.Ref<cs.NativeArray<T>>, length:Int, pos:Int, x:T)
-	{
-		var capacity = a.Length;
-		if (pos == length)
-		{
-			if (capacity == length)
-			{
-				var newarr = new NativeArray((length << 1) + 1);
-				a.CopyTo(newarr, 0);
-				a = newarr;
-			}
-		}
-		else if (pos == 0)
-		{
-			if (capacity == length)
-			{
-				var newarr = new NativeArray((length << 1) + 1);
-				cs.system.Array.Copy(a, 0, newarr, 1, length);
-				a = newarr;
-			}
-			else
-			{
-				cs.system.Array.Copy(a, 0, a, 1, length);
-			}
-		}
-		else
-		{
-			if (capacity == length)
-			{
-				var newarr = new NativeArray((length << 1) + 1);
-				cs.system.Array.Copy(a, 0, newarr, 0, pos);
-				cs.system.Array.Copy(a, pos, newarr, pos + 1, length - pos);
-				a = newarr;
-			}
-			else
-			{
-				cs.system.Array.Copy(a, pos, a, pos + 1, length - pos);
-				cs.system.Array.Copy(a, 0, a, 0, pos);
-			}
-		}
-		a[pos] = x;
-	}
-	#else
-	static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int)
+	public static function removeInt(a:cs.NativeArray<Int>, length:Int, pos:Int)
 	{
 		cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = 0;
 	}
-	static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int)
+	public static function removeFloat(a:cs.NativeArray<Float>, length:Int, pos:Int)
 	{
 		cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = 0;
 	}
-	static function removeDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int)
+	public static function removeDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int)
 	{
 		cs.system.Array.Copy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = null;
@@ -335,13 +276,12 @@ package cs.internal;
 		return a;
 	}
 
-	static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int> return __insert(a, length, pos, x);
-	static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float> return __insert(a, length, pos, x);
-	static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic> return __insert(a, length, pos, x);
-	static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<String> return __insert(a, length, pos, x);
-	#end
+	public static function insertInt(a:cs.NativeArray<Int>, length:Int, pos:Int, x:Int):cs.NativeArray<Int> return __insert(a, length, pos, x);
+	public static function insertFloat(a:cs.NativeArray<Float>, length:Int, pos:Int, x:Float):cs.NativeArray<Float> return __insert(a, length, pos, x);
+	public static function insertDynamic(a:cs.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):cs.NativeArray<Dynamic> return __insert(a, length, pos, x);
+	public static function insertString(a:cs.NativeArray<String>, length:Int, pos:Int, x:String):cs.NativeArray<String> return __insert(a, length, pos, x);
 
-	static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
+	public static function getHashConflict(head:FieldHashConflict, hash:Int, name:String):FieldHashConflict {
 		while (head != null) {
 			if (head.hash == hash && head.name == name) {
 				return head;
@@ -351,7 +291,7 @@ package cs.internal;
 		return null;
 	}
 
-	static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
+	public static function setHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String, value:Dynamic):Void {
 		var node = head;
 		while (node != null) {
 			if (node.hash == hash && node.name == name) {
@@ -363,7 +303,7 @@ package cs.internal;
 		head = new FieldHashConflict(hash, name, value, head);
 	}
 
-	static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
+	public static function deleteHashConflict(head:cs.Ref<FieldHashConflict>, hash:Int, name:String):Bool {
 		// no conflicting fields at all
 		if (head == null) {
 			return false;
@@ -389,7 +329,7 @@ package cs.internal;
 		return false;
 	}
 
-	static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
+	public static function addHashConflictNames(head:FieldHashConflict, arr:Array<String>):Void {
 		while (head != null) {
 			arr.push(head.name);
 			head = head.next;

+ 202 - 1
std/cs/internal/HxObject.hx

@@ -22,11 +22,16 @@
 package cs.internal;
 import cs.system.Type;
 import haxe.ds.Vector;
+import cs.internal.FieldLookup;
 private typedef StdType = std.Type;
 
 @:keep @:native('haxe.lang.HxObject')
 class HxObject implements IHxObject
 {
+	public function __hx_deleteField(field:String, hash:Int):Bool
+	{
+		return false;
+	}
 }
 
 @:keep @:native('haxe.lang.IHxObject')
@@ -38,8 +43,204 @@ interface IHxObject
 @:meta(System.Serializable)
 #end
 @:keep @:native('haxe.lang.DynamicObject')
-class DynamicObject extends HxObject implements Dynamic
+class DynamicObject extends HxObject
 {
+	@:skipReflection var __hx_hashes:NativeArray<Int>;
+	@:skipReflection var __hx_dynamics:NativeArray<Dynamic>;
+
+	@:skipReflection var __hx_hashes_f:NativeArray<Int>;
+	@:skipReflection var __hx_dynamics_f:NativeArray<Float>;
+
+	@:skipReflection var __hx_length:Int;
+	@:skipReflection var __hx_length_f:Int;
+	@:skipReflection var __hx_conflicts:FieldHashConflict;
+
+	@:overload public function new()
+	{
+		this.__hx_hashes = new NativeArray(0);
+		this.__hx_dynamics = new NativeArray(0);
+		this.__hx_hashes_f = new NativeArray(0);
+		this.__hx_dynamics_f = new NativeArray(0);
+		this.__hx_conflicts = null;
+	}
+
+	@:overload public function new(hashes:NativeArray<Int>, dynamics:NativeArray<Dynamic>, hashes_f:NativeArray<Int>, dynamics_f:NativeArray<Float>)
+	{
+		this.__hx_hashes = hashes;
+		this.__hx_dynamics = dynamics;
+		this.__hx_hashes_f = hashes_f;
+		this.__hx_dynamics_f = dynamics_f;
+		this.__hx_length = hashes.length;
+		this.__hx_length_f = hashes_f.length;
+		this.__hx_conflicts = null;
+	}
+
+	override public function __hx_deleteField(field:String, hash:Int):Bool
+	{
+		if (hash < 0)
+		{
+			return FieldLookup.deleteHashConflict(this.__hx_conflicts, hash, field);
+		}
+
+		var res = FieldLookup.findHash(hash, this.__hx_hashes, this.__hx_length);
+		if (res >= 0)
+		{
+			FieldLookup.removeInt(this.__hx_hashes, this.__hx_length, res);
+			FieldLookup.removeDynamic(this.__hx_dynamics, this.__hx_length, res);
+			this.__hx_length--;
+			return true;
+		}
+		res = FieldLookup.findHash(hash, this.__hx_hashes_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			FieldLookup.removeInt(this.__hx_hashes_f, this.__hx_length_f, res);
+			FieldLookup.removeFloat(this.__hx_dynamics_f, this.__hx_length_f, res);
+			this.__hx_length_f--;
+			return true;
+		}
+		return false;
+	}
+
+	public function __hx_getField(field:String, hash:Int, throwErrors:Bool, isCheck:Bool, handleProperties:Bool):Dynamic
+	{
+		if (hash < 0)
+		{
+			var conflict = FieldLookup.getHashConflict(this.__hx_conflicts, hash, field);
+			if (conflict != null)
+			{
+				return conflict.value;
+			}
+		}
+
+		var res = FieldLookup.findHash(hash, this.__hx_hashes, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res];
+		}
+		res = FieldLookup.findHash(hash, this.__hx_hashes_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res];
+		}
+
+		return isCheck ? Runtime.undefined : null;
+	}
+
+	public function __hx_setField(field:String, hash:Int, value:Dynamic, handleProperties:Bool):Dynamic
+	{
+		if (hash < 0)
+		{
+			FieldLookup.setHashConflict(this.__hx_conflicts, hash, field, value);
+			return value;
+		}
+
+		var res = FieldLookup.findHash(hash, this.__hx_hashes, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res] = value;
+		} else {
+			var res = FieldLookup.findHash(hash, this.__hx_hashes_f, this.__hx_length_f);
+			if (res >= 0)
+			{
+				if (Std.is(value, Float))
+				{
+					return this.__hx_dynamics_f[res] = value;
+				}
+
+				FieldLookup.removeInt(this.__hx_hashes_f, this.__hx_length_f, res);
+				FieldLookup.removeFloat(this.__hx_dynamics_f, this.__hx_length_f, res);
+				this.__hx_length_f--;
+			}
+		}
+
+		this.__hx_hashes = FieldLookup.insertInt(this.__hx_hashes, this.__hx_length,  ~(res) , hash);
+		this.__hx_dynamics = FieldLookup.insertDynamic(this.__hx_dynamics, this.__hx_length,  ~(res) , value);
+		this.__hx_length++;
+		return value;
+	}
+
+	public function __hx_getField_f(field:String, hash:Int, throwErrors:Bool, handleProperties:Bool):Float
+	{
+		if (hash < 0)
+		{
+			var conflict = FieldLookup.getHashConflict(this.__hx_conflicts, hash, field);
+			if (conflict != null)
+			{
+				return conflict.value;
+			}
+		}
+
+		var res = FieldLookup.findHash(hash, this.__hx_hashes_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res];
+		}
+		res = FieldLookup.findHash(hash, this.__hx_hashes, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res];
+		}
+
+		return 0.0;
+	}
+
+	public function __hx_setField_f(field:String, hash:Int, value:Float, handleProperties:Bool):Float
+	{
+		if (hash < 0)
+		{
+			FieldLookup.setHashConflict(this.__hx_conflicts, hash, field, value);
+			return value;
+		}
+
+		var res = FieldLookup.findHash(hash, this.__hx_hashes_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res] = value;
+		} else {
+			var res = FieldLookup.findHash(hash, this.__hx_hashes, this.__hx_length);
+			if (res >= 0)
+			{
+				// return this.__hx_dynamics[res] = value;
+				FieldLookup.removeInt(this.__hx_hashes, this.__hx_length, res);
+				FieldLookup.removeDynamic(this.__hx_dynamics, this.__hx_length, res);
+				this.__hx_length--;
+			}
+		}
+
+		this.__hx_hashes_f = FieldLookup.insertInt(this.__hx_hashes_f, this.__hx_length_f,  ~(res) , hash);
+		this.__hx_dynamics_f = FieldLookup.insertFloat(this.__hx_dynamics_f, this.__hx_length_f,  ~(res) , value);
+		this.__hx_length_f++;
+		return value;
+	}
+
+	public function __hx_getFields(baseArr:Array<String>):Void
+	{
+		for (i in 0...this.__hx_length)
+		{
+			baseArr.push(FieldLookup.lookupHash(this.__hx_hashes[i]));
+		}
+		for (i in 0...this.__hx_length_f)
+		{
+			baseArr.push(FieldLookup.lookupHash(this.__hx_hashes_f[i]));
+		}
+		FieldLookup.addHashConflictNames(this.__hx_conflicts, baseArr);
+	}
+
+	public function __hx_invokeField(field:String, hash:Int, dynargs:NativeArray<Dynamic>):Dynamic
+	{
+		if (field == "toString")
+		{
+			return this.toString();
+		}
+		var fn:Function = this.__hx_getField(field, hash, false, false, false);
+		if (fn == null)
+		{
+			throw 'Cannot invoke field $field: It does not exist';
+		}
+
+		return untyped fn.__hx_invokeDynamic(dynargs);
+	}
+
 	@:skipReflection public function toString():String
 	{
 		var ts = Reflect.field(this, "toString");

+ 7 - 7
std/java/internal/FieldLookup.hx

@@ -25,7 +25,7 @@ import java.lang.System;
 
 @:native('haxe.lang.FieldLookup')
 @:keep
-@:static private class FieldLookup
+@:static class FieldLookup
 {
 
 	@:functionCode('
@@ -58,17 +58,17 @@ import java.lang.System;
 		return ~min;
 	}
 
-	static function removeString(a:java.NativeArray<String>, length:Int, pos:Int) {
+	public static function removeString(a:java.NativeArray<String>, length:Int, pos:Int) {
 		System.arraycopy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = null;
 	}
 
-	static function removeFloat(a:java.NativeArray<Float>, length:Int, pos:Int) {
+	public static function removeFloat(a:java.NativeArray<Float>, length:Int, pos:Int) {
 		System.arraycopy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = 0;
 	}
 
-	static function removeDynamic(a:java.NativeArray<Dynamic>, length:Int, pos:Int) {
+	public static function removeDynamic(a:java.NativeArray<Dynamic>, length:Int, pos:Int) {
 		System.arraycopy(a, pos + 1, a, pos, length - pos - 1);
 		a[length - 1] = null;
 	}
@@ -118,7 +118,7 @@ import java.lang.System;
 		return a;
 	}
 
-	static function insertString(a:java.NativeArray<String>, length:Int, pos:Int, x:String):java.NativeArray<String> return __insert(a, length, pos, x);
-	static function insertFloat(a:java.NativeArray<Float>, length:Int, pos:Int, x:Float):java.NativeArray<Float> return __insert(a, length, pos, x);
-	static function insertDynamic(a:java.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):java.NativeArray<Dynamic> return __insert(a, length, pos, x);
+	public static function insertString(a:java.NativeArray<String>, length:Int, pos:Int, x:String):java.NativeArray<String> return __insert(a, length, pos, x);
+	public static function insertFloat(a:java.NativeArray<Float>, length:Int, pos:Int, x:Float):java.NativeArray<Float> return __insert(a, length, pos, x);
+	public static function insertDynamic(a:java.NativeArray<Dynamic>, length:Int, pos:Int, x:Dynamic):java.NativeArray<Dynamic> return __insert(a, length, pos, x);
 }

+ 160 - 3
std/java/internal/HxObject.hx

@@ -39,11 +39,168 @@ interface IHxObject
 
 @:native('haxe.lang.DynamicObject')
 @:keep
-class DynamicObject extends HxObject implements Dynamic
+class DynamicObject extends HxObject
 {
-	@:skipReflection public function toString():String
+	@:skipReflection var __hx_fields:java.NativeArray<String>;
+	@:skipReflection var __hx_dynamics:java.NativeArray<Dynamic>;
+
+	@:skipReflection var __hx_fields_f:java.NativeArray<String>;
+	@:skipReflection var __hx_dynamics_f:java.NativeArray<Float>;
+
+	@:skipReflection var __hx_length:Int;
+	@:skipReflection var __hx_length_f:Int;
+
+	@:overload public function new()
+	{
+		this.__hx_fields = new java.NativeArray(0);
+		this.__hx_dynamics = new java.NativeArray(0);
+		this.__hx_fields_f = new java.NativeArray(0);
+		this.__hx_dynamics_f = new java.NativeArray(0);
+	}
+
+	@:overload public function new(fields:NativeArray<String>, dynamics:NativeArray<Dynamic>, fields_f:NativeArray<String>, dynamics_f:NativeArray<Float>)
+	{
+		this.__hx_fields = fields;
+		this.__hx_dynamics = dynamics;
+		this.__hx_fields_f = fields_f;
+		this.__hx_dynamics_f = dynamics_f;
+		this.__hx_length = fields.length;
+		this.__hx_length_f = fields_f.length;
+	}
+
+	public function __hx_deleteField(field:String):Bool
+	{
+		var res = FieldLookup.findHash(field, this.__hx_fields, this.__hx_length);
+		if (res >= 0)
+		{
+			FieldLookup.removeString(this.__hx_fields, this.__hx_length, res);
+			FieldLookup.removeDynamic(this.__hx_dynamics, this.__hx_length, res);
+			this.__hx_length--;
+			return true;
+		}
+		var res = FieldLookup.findHash(field, this.__hx_fields_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			FieldLookup.removeString(this.__hx_fields_f, this.__hx_length_f, res);
+			FieldLookup.removeFloat(this.__hx_dynamics_f, this.__hx_length_f, res);
+			this.__hx_length_f--;
+			return true;
+		}
+		return false;
+	}
+
+	public function __hx_getField(field:String, throwErrors:Bool, isCheck:Bool, handleProperties:Bool):Dynamic
+	{
+		var res = FieldLookup.findHash(field, this.__hx_fields, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res];
+		}
+		res = FieldLookup.findHash(field, this.__hx_fields_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res];
+		}
+
+		return isCheck ? Runtime.undefined : null;
+	}
+
+	public function __hx_setField(field:String, value:Dynamic, handleProperties:Bool):Dynamic
+	{
+		var res = FieldLookup.findHash(field, this.__hx_fields, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res] = value;
+		} else {
+			var res = FieldLookup.findHash(field, this.__hx_fields_f, this.__hx_length_f);
+			if (res >= 0)
+			{
+				if (Std.is(value, Float))
+				{
+					return this.__hx_dynamics_f[res] = value;
+				}
+
+				FieldLookup.removeString(this.__hx_fields_f, this.__hx_length_f, res);
+				FieldLookup.removeFloat(this.__hx_dynamics_f, this.__hx_length_f, res);
+				this.__hx_length_f--;
+			}
+		}
+
+		this.__hx_fields = FieldLookup.insertString(this.__hx_fields, this.__hx_length,  ~(res) , field);
+		this.__hx_dynamics = FieldLookup.insertDynamic(this.__hx_dynamics, this.__hx_length,  ~(res) , value);
+		this.__hx_length++;
+		return value;
+	}
+
+	public function __hx_getField_f(field:String, throwErrors:Bool, handleProperties:Bool):Float
+	{
+		var res = FieldLookup.findHash(field, this.__hx_fields_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res];
+		}
+		res = FieldLookup.findHash(field, this.__hx_fields, this.__hx_length);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics[res];
+		}
+
+		return 0.0;
+	}
+
+	public function __hx_setField_f(field:String, value:Float, handleProperties:Bool):Float
+	{
+		var res = FieldLookup.findHash(field, this.__hx_fields_f, this.__hx_length_f);
+		if (res >= 0)
+		{
+			return this.__hx_dynamics_f[res] = value;
+		} else {
+			var res = FieldLookup.findHash(field, this.__hx_fields, this.__hx_length);
+			if (res >= 0)
+			{
+				// return this.__hx_dynamics[res] = value;
+				FieldLookup.removeString(this.__hx_fields, this.__hx_length, res);
+				FieldLookup.removeDynamic(this.__hx_dynamics, this.__hx_length, res);
+				this.__hx_length--;
+			}
+		}
+
+		this.__hx_fields_f = FieldLookup.insertString(this.__hx_fields_f, this.__hx_length_f,  ~(res) , field);
+		this.__hx_dynamics_f = FieldLookup.insertFloat(this.__hx_dynamics_f, this.__hx_length_f,  ~(res) , value);
+		this.__hx_length_f++;
+		return value;
+	}
+
+	public function __hx_getFields(baseArr:Array<String>):Void
+	{
+		for (i in 0...this.__hx_length)
+		{
+			baseArr.push(this.__hx_fields[i]);
+		}
+		for (i in 0...this.__hx_length_f)
+		{
+			baseArr.push(this.__hx_fields_f[i]);
+		}
+	}
+
+	public function __hx_invokeField(field:String, dynargs:NativeArray<Dynamic>):Dynamic
+	{
+		if (field == "toString")
+		{
+			return this.toString();
+		}
+		var fn:Function = this.__hx_getField(field, false, false, false);
+		if (fn == null)
+		{
+			throw 'Cannot invoke field $field: It does not exist';
+		}
+
+		return untyped fn.__hx_invokeDynamic(dynargs);
+	}
+
+	public function toString():String
 	{
-		var ts = Reflect.field(this, "toString");
+		var ts = this.__hx_getField("toString", false, false, false);
 		if (ts != null)
 			return ts();
 		var ret = new StringBuf();

+ 1 - 1
tests/unit/src/unitstd/Type.unit.hx

@@ -107,7 +107,7 @@ var requiredFields = ["func", "v", "prop"];
 for (f in fields)
 	t(requiredFields.remove(f));
 requiredFields == [];
-#if (!hl && !eval) // no support for implements Dynamic yet
+#if (!cs && !java && !hl && !eval) // no support for implements Dynamic yet
 var cdyn = new CDyn();
 cdyn.foo = "1";
 Reflect.setField(cdyn, "bar", 1);