From d5444db3abec3b717ade677e7fe4543b7d0f1cd2 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 18 Mar 2025 14:11:29 +0100 Subject: [PATCH 1/6] add haxe.runtime.FieldHost and use it for Reflect.fields --- std/Reflect.hx | 8 ++++---- std/cpp/_std/Reflect.hx | 4 ++-- std/flash/_std/Reflect.hx | 4 ++-- std/haxe/runtime/FieldHost.hx | 6 ++++++ std/hl/_std/Reflect.hx | 2 +- std/js/_std/Reflect.hx | 4 ++-- std/jvm/_std/Reflect.hx | 6 +++--- std/lua/_std/Reflect.hx | 2 +- std/neko/_std/Reflect.hx | 2 +- std/php/_std/Reflect.hx | 4 ++-- std/python/_std/Reflect.hx | 4 ++-- 11 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 std/haxe/runtime/FieldHost.hx diff --git a/std/Reflect.hx b/std/Reflect.hx index 346165b70a8..03bccd7ae10 100644 --- a/std/Reflect.hx +++ b/std/Reflect.hx @@ -98,14 +98,14 @@ extern class Reflect { static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic; /** - Returns the fields of structure `o`. + Returns the fields of anonymous structure or class instance `o`. - This method is only guaranteed to work on anonymous structures. Refer to - `Type.getInstanceFields` for a function supporting class instances. + On flash, this method is only guaranteed to work on anonymous structures. + Refer to `Type.getInstanceFields` for a function supporting class instances. If `o` is null, the result is unspecified. **/ - static function fields(o:Dynamic):Array; + static function fields(o:haxe.runtime.FieldHost):Array; /** Returns true if `f` is a function, false otherwise. diff --git a/std/cpp/_std/Reflect.hx b/std/cpp/_std/Reflect.hx index 98571ba50dd..ff12603f787 100644 --- a/std/cpp/_std/Reflect.hx +++ b/std/cpp/_std/Reflect.hx @@ -59,7 +59,7 @@ class Reflect { return untyped func.__Run(args); } - public static function fields(o:Dynamic):Array untyped { + public static function fields(o:haxe.runtime.FieldHost):Array untyped { if (o == null) return new Array(); var a:Array = []; @@ -108,7 +108,7 @@ class Reflect { if (untyped o.__GetType() == ObjectType.vtArray) return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))(); var o2:Dynamic = {}; - for (f in Reflect.fields(o)) + for (f in Reflect.fields(cast o)) Reflect.setField(o2, f, Reflect.field(o, f)); return o2; } diff --git a/std/flash/_std/Reflect.hx b/std/flash/_std/Reflect.hx index 5687bf016a7..caa3a68860e 100644 --- a/std/flash/_std/Reflect.hx +++ b/std/flash/_std/Reflect.hx @@ -55,7 +55,7 @@ return func.apply(o, args); } - public static function fields(o:Dynamic):Array untyped { + public static function fields(o:haxe.runtime.FieldHost):Array untyped { if (o == null) return new Array(); var i = 0; @@ -107,7 +107,7 @@ if (o == null) return null; var o2:Dynamic = {}; - for (f in Reflect.fields(o)) + for (f in Reflect.fields(cast o)) Reflect.setField(o2, f, Reflect.field(o, f)); return o2; } diff --git a/std/haxe/runtime/FieldHost.hx b/std/haxe/runtime/FieldHost.hx new file mode 100644 index 00000000000..798fb4e8c25 --- /dev/null +++ b/std/haxe/runtime/FieldHost.hx @@ -0,0 +1,6 @@ +package haxe.runtime; + +@:transitive +abstract FieldHost(Dynamic) from {} + from Dynamic + #if (jvm || neko || js) from Class#end {} diff --git a/std/hl/_std/Reflect.hx b/std/hl/_std/Reflect.hx index 833771be405..3ec373a9cc5 100644 --- a/std/hl/_std/Reflect.hx +++ b/std/hl/_std/Reflect.hx @@ -87,7 +87,7 @@ class Reflect { return null; } - public static function fields(o:Dynamic):Array { + public static function fields(o:haxe.runtime.FieldHost):Array { var fields = getObjectFields(o); if (fields == null) return []; diff --git a/std/js/_std/Reflect.hx b/std/js/_std/Reflect.hx index db99e909748..e95fff52d9e 100644 --- a/std/js/_std/Reflect.hx +++ b/std/js/_std/Reflect.hx @@ -55,7 +55,7 @@ return (cast func : js.lib.Function).apply(o, args); } - public static function fields(o:Dynamic):Array { + public static function fields(o:haxe.runtime.FieldHost):Array { var a = []; if (o != null) untyped { var hasOwnProperty = js.lib.Object.prototype.hasOwnProperty; @@ -104,7 +104,7 @@ if (o == null) return null; var o2:Dynamic = {}; - for (f in Reflect.fields(o)) + for (f in Reflect.fields(cast o)) Reflect.setField(o2, f, Reflect.field(o, f)); return o2; } diff --git a/std/jvm/_std/Reflect.hx b/std/jvm/_std/Reflect.hx index bbb51666a5c..f466d6792cd 100644 --- a/std/jvm/_std/Reflect.hx +++ b/std/jvm/_std/Reflect.hx @@ -77,12 +77,12 @@ class Reflect { return Jvm.call(cast func, @:privateAccess args.getNative()); } - public static function fields(o:Dynamic):Array { + public static function fields(o:haxe.runtime.FieldHost):Array { if (!Jvm.instanceof(o, jvm.DynamicObject)) { if (Jvm.instanceof(o, java.lang.Class)) { - return Type.getClassFields(o); + return Type.getClassFields(cast o); } - var c = (o : java.lang.Object).getClass(); + var c = (cast o : java.lang.Object).getClass(); var ret = []; for (f in c.getDeclaredFields()) { if (java.lang.reflect.Modifier.isStatic(f.getModifiers()) == false && !f.isSynthetic()) { diff --git a/std/lua/_std/Reflect.hx b/std/lua/_std/Reflect.hx index de7935d7cfe..6e78e385437 100644 --- a/std/lua/_std/Reflect.hx +++ b/std/lua/_std/Reflect.hx @@ -83,7 +83,7 @@ import lua.TableTools; } } - public static function fields(o:Dynamic):Array { + public static function fields(o:haxe.runtime.FieldHost):Array { if (lua.Lua.type(o) == "string") { return Reflect.fields(untyped String.prototype); } else { diff --git a/std/neko/_std/Reflect.hx b/std/neko/_std/Reflect.hx index 49bc73b2e36..6e331135219 100644 --- a/std/neko/_std/Reflect.hx +++ b/std/neko/_std/Reflect.hx @@ -62,7 +62,7 @@ return $call(func, o, a); } - public static function fields(o:Dynamic):Array untyped { + public static function fields(o:haxe.runtime.FieldHost):Array untyped { if ($typeof(o) != $tobject) return new Array(); else { diff --git a/std/php/_std/Reflect.hx b/std/php/_std/Reflect.hx index 5c63cbba370..612c04bbebe 100644 --- a/std/php/_std/Reflect.hx +++ b/std/php/_std/Reflect.hx @@ -113,9 +113,9 @@ using php.Global; return Global.call_user_func_array(func, @:privateAccess args.arr); } - public static function fields(o:Dynamic):Array { + public static function fields(o:haxe.runtime.FieldHost):Array { if (Global.is_object(o)) { - return @:privateAccess Array.wrap(Global.get_object_vars(o).array_keys()); + return @:privateAccess Array.wrap(Global.get_object_vars(cast o).array_keys()); } return []; } diff --git a/std/python/_std/Reflect.hx b/std/python/_std/Reflect.hx index 2979f5cfe95..9a95b0f6396 100644 --- a/std/python/_std/Reflect.hx +++ b/std/python/_std/Reflect.hx @@ -76,7 +76,7 @@ class Reflect { return if (UBuiltins.callable(func)) func(python.Syntax.varArgs(args)) else null; } - public static inline function fields(o:Dynamic):Array { + public static inline function fields(o:haxe.runtime.FieldHost):Array { return python.Boot.fields(o); } @@ -131,7 +131,7 @@ class Reflect { if (o == null) return null; var o2:Dynamic = {}; - for (f in Reflect.fields(o)) + for (f in Reflect.fields(cast o)) Reflect.setField(o2, f, Reflect.field(o, f)); return o2; } From 4317780e7cf8b08001337a00b67d83e30479b10f Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 18 Mar 2025 14:20:34 +0100 Subject: [PATCH 2/6] lua & dodge --- std/haxe/runtime/FieldHost.hx | 2 +- std/lua/_std/Reflect.hx | 2 +- tests/unit/src/unit/issues/Issue6801.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/std/haxe/runtime/FieldHost.hx b/std/haxe/runtime/FieldHost.hx index 798fb4e8c25..98d0c774c49 100644 --- a/std/haxe/runtime/FieldHost.hx +++ b/std/haxe/runtime/FieldHost.hx @@ -3,4 +3,4 @@ package haxe.runtime; @:transitive abstract FieldHost(Dynamic) from {} from Dynamic - #if (jvm || neko || js) from Class#end {} + #if (jvm || neko || js || lua) from Class#end {} diff --git a/std/lua/_std/Reflect.hx b/std/lua/_std/Reflect.hx index 6e78e385437..d92049f8e92 100644 --- a/std/lua/_std/Reflect.hx +++ b/std/lua/_std/Reflect.hx @@ -134,7 +134,7 @@ import lua.TableTools; if (o == null) return null; var o2:Dynamic = {}; - for (f in Reflect.fields(o)) + for (f in Reflect.fields(cast o)) Reflect.setField(o2, f, Reflect.field(o, f)); return o2; } diff --git a/tests/unit/src/unit/issues/Issue6801.hx b/tests/unit/src/unit/issues/Issue6801.hx index a963c066794..5986afeeecb 100644 --- a/tests/unit/src/unit/issues/Issue6801.hx +++ b/tests/unit/src/unit/issues/Issue6801.hx @@ -10,11 +10,11 @@ class Issue6801 extends unit.Test { var expected = {c:'hello', c2:true, p:1, p2:0}; var actual = Json.parse(json); - eq(Reflect.fields(expected).length, Reflect.fields(actual).length); eq(expected.c, actual.c); eq(expected.c2, actual.c2); eq(expected.p, actual.p); eq(expected.p2, actual.p2); + eq(Reflect.fields(expected).length, Reflect.fields(actual).length); } } From 41dcbe6759e4a106975dc9b85a4c2e6873068e96 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Tue, 18 Mar 2025 17:34:30 +0100 Subject: [PATCH 3/6] avoid docgen problems --- std/haxe/runtime/FieldHost.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/haxe/runtime/FieldHost.hx b/std/haxe/runtime/FieldHost.hx index 98d0c774c49..0ec764e7a82 100644 --- a/std/haxe/runtime/FieldHost.hx +++ b/std/haxe/runtime/FieldHost.hx @@ -2,5 +2,6 @@ package haxe.runtime; @:transitive abstract FieldHost(Dynamic) from {} - from Dynamic - #if (jvm || neko || js || lua) from Class#end {} + from Dynamic // #if (jvm || neko || js || lua) // can't do this because of docgen + from Class // #end +{} From 9b441a85e1b079814143d6d8cbb64806dd498789 Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Wed, 19 Mar 2025 06:14:19 +0100 Subject: [PATCH 4/6] go all the way --- std/Reflect.hx | 16 ++++++------ std/cpp/_std/Reflect.hx | 21 ++++++++-------- std/flash/_std/Reflect.hx | 19 +++++++++------ std/haxe/runtime/FieldHost.hx | 24 +++++++++++++++--- std/hl/_std/Reflect.hx | 17 +++++++------ std/js/_std/Reflect.hx | 23 ++++++++++-------- std/jvm/_std/Reflect.hx | 15 ++++++------ std/lua/_std/Reflect.hx | 19 ++++++++------- std/neko/_std/Reflect.hx | 17 +++++++------ std/php/_std/Reflect.hx | 31 ++++++++++++------------ std/python/_std/Reflect.hx | 17 +++++++------ tests/unit/src/unit/issues/Issue10993.hx | 17 ++++++------- tests/unit/src/unit/issues/Issue2963.hx | 2 +- tests/unit/src/unitstd/Reflect.unit.hx | 3 +-- 14 files changed, 138 insertions(+), 103 deletions(-) diff --git a/std/Reflect.hx b/std/Reflect.hx index 03bccd7ae10..014944c70ed 100644 --- a/std/Reflect.hx +++ b/std/Reflect.hx @@ -20,6 +20,8 @@ * DEALINGS IN THE SOFTWARE. */ +import haxe.runtime.FieldHost; + /** The Reflect API is a way to manipulate values dynamically through an abstract interface in an untyped manner. Use with care. @@ -35,7 +37,7 @@ extern class Reflect { If `o` or `field` are null, the result is unspecified. **/ - static function hasField(o:Dynamic, field:String):Bool; + static function hasField(o:FieldHost, field:String):Bool; /** Returns the value of the field named `field` on object `o`. @@ -48,7 +50,7 @@ extern class Reflect { If `field` is null, the result is unspecified. **/ - static function field(o:Dynamic, field:String):Dynamic; + static function field(o:FieldHost, field:String):Dynamic; /** Sets the field named `field` of object `o` to value `value`. @@ -58,7 +60,7 @@ extern class Reflect { If `o` or `field` are null, the result is unspecified. **/ - static function setField(o:Dynamic, field:String, value:Dynamic):Void; + static function setField(o:FieldHost, field:String, value:Dynamic):Void; /** Returns the value of the field named `field` on object `o`, taking @@ -69,7 +71,7 @@ extern class Reflect { If `o` or `field` are null, the result is unspecified. **/ - static function getProperty(o:Dynamic, field:String):Dynamic; + static function getProperty(o:FieldHost, field:String):Dynamic; /** Sets the field named `field` of object `o` to value `value`, taking @@ -80,7 +82,7 @@ extern class Reflect { If `field` is null, the result is unspecified. **/ - static function setProperty(o:Dynamic, field:String, value:Dynamic):Void; + static function setProperty(o:FieldHost, field:String, value:Dynamic):Void; /** Call a method `func` with the given arguments `args`. @@ -105,7 +107,7 @@ extern class Reflect { If `o` is null, the result is unspecified. **/ - static function fields(o:haxe.runtime.FieldHost):Array; + static function fields(o:FieldHost):Array; /** Returns true if `f` is a function, false otherwise. @@ -182,7 +184,7 @@ extern class Reflect { If `o` or `field` are null, the result is unspecified. **/ - static function deleteField(o:Dynamic, field:String):Bool; + static function deleteField(o:FieldHost, field:String):Bool; /** Copies the fields of structure `o`. diff --git a/std/cpp/_std/Reflect.hx b/std/cpp/_std/Reflect.hx index ff12603f787..6520958bebc 100644 --- a/std/cpp/_std/Reflect.hx +++ b/std/cpp/_std/Reflect.hx @@ -21,30 +21,31 @@ */ import cpp.ObjectType; +import haxe.runtime.FieldHost; @:coreApi @:analyzer(ignore) class Reflect { - public static function hasField(o:Dynamic, field:String):Bool untyped { + public static function hasField(o:FieldHost, field:String):Bool untyped { return o != null && o.__HasField(field); } - public static function field(o:Dynamic, field:String):Dynamic untyped { + public static function field(o:FieldHost, field:String):Dynamic untyped { return (o == null) ? null : o.__Field(field, untyped __cpp__("::hx::paccNever")); } - public static function setField(o:Dynamic, field:String, value:Dynamic):Void untyped { + public static function setField(o:FieldHost, field:String, value:Dynamic):Void untyped { if (o != null) o.__SetField(field, value, untyped __cpp__("::hx::paccNever")); } - public static function getProperty(o:Dynamic, field:String):Dynamic { - return (o == null) ? null : o.__Field(field, untyped __cpp__("::hx::paccAlways")); + public static function getProperty(o:FieldHost, field:String):Dynamic { + return (o == null) ? null : o.asDynamic().__Field(field, untyped __cpp__("::hx::paccAlways")); } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void { if (o != null) - o.__SetField(field, value, untyped __cpp__("::hx::paccAlways")); + o.asDynamic().__SetField(field, value, untyped __cpp__("::hx::paccAlways")); } public static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array):Dynamic untyped { @@ -59,7 +60,7 @@ class Reflect { return untyped func.__Run(args); } - public static function fields(o:haxe.runtime.FieldHost):Array untyped { + public static function fields(o:FieldHost):Array untyped { if (o == null) return new Array(); var a:Array = []; @@ -94,7 +95,7 @@ class Reflect { return v != null && v.__GetType() == ObjectType.vtEnum; } - public static function deleteField(o:Dynamic, field:String):Bool untyped { + public static function deleteField(o:FieldHost, field:String):Bool untyped { if (o == null) return false; return untyped __global__.__hxcpp_anon_remove(o, field); @@ -109,7 +110,7 @@ class Reflect { return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))(); var o2:Dynamic = {}; for (f in Reflect.fields(cast o)) - Reflect.setField(o2, f, Reflect.field(o, f)); + Reflect.setField(cast o2, f, Reflect.field(cast o, f)); return o2; } diff --git a/std/flash/_std/Reflect.hx b/std/flash/_std/Reflect.hx index caa3a68860e..8622bb34c37 100644 --- a/std/flash/_std/Reflect.hx +++ b/std/flash/_std/Reflect.hx @@ -19,20 +19,23 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ + +import haxe.runtime.FieldHost; + @:coreApi class Reflect { - public static function hasField(o:Dynamic, field:String):Bool untyped { + public static function hasField(o:FieldHost, field:String):Bool untyped { return o.hasOwnProperty(field); } - public static function field(o:Dynamic, field:String):Dynamic untyped { + public static function field(o:FieldHost, field:String):Dynamic untyped { return o != null && __in__(field, o) ? o[field] : null; } - public inline static function setField(o:Dynamic, field:String, value:Dynamic):Void untyped { + public inline static function setField(o:FieldHost, field:String, value:Dynamic):Void untyped { o[field] = value; } - public static function getProperty(o:Dynamic, field:String):Dynamic untyped { + public static function getProperty(o:FieldHost, field:String):Dynamic untyped { if (o == null) return null; var getter = 'get_$field'; @@ -42,7 +45,7 @@ return __in__(field, o) ? o[field] : null; } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void untyped { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void untyped { var setter = 'set_$field'; if (__in__(setter, o)) { o[setter](value); @@ -55,7 +58,7 @@ return func.apply(o, args); } - public static function fields(o:haxe.runtime.FieldHost):Array untyped { + public static function fields(o:FieldHost):Array untyped { if (o == null) return new Array(); var i = 0; @@ -96,7 +99,7 @@ return try v.__enum__ == true catch (e:Dynamic) false; } - public static function deleteField(o:Dynamic, field:String):Bool untyped { + public static function deleteField(o:FieldHost, field:String):Bool untyped { if (o.hasOwnProperty(field) != true) return false; __delete__(o, field); @@ -108,7 +111,7 @@ return null; var o2:Dynamic = {}; for (f in Reflect.fields(cast o)) - Reflect.setField(o2, f, Reflect.field(o, f)); + Reflect.setField(cast o2, f, Reflect.field(cast o, f)); return o2; } diff --git a/std/haxe/runtime/FieldHost.hx b/std/haxe/runtime/FieldHost.hx index 0ec764e7a82..0d1ec079af3 100644 --- a/std/haxe/runtime/FieldHost.hx +++ b/std/haxe/runtime/FieldHost.hx @@ -2,6 +2,24 @@ package haxe.runtime; @:transitive abstract FieldHost(Dynamic) from {} - from Dynamic // #if (jvm || neko || js || lua) // can't do this because of docgen - from Class // #end -{} + from Dynamic + from Class +{ + public inline function asArrayAccess():ArrayAccess { + return cast this; + } + + public inline function asStructure():{} { + return cast this; + } + + public inline function asDynamic():Dynamic { + return cast this; + } + + #if (neko || js || flash || python || lua) + @:from static public inline function fromEnum(en:Enum):FieldHost { + return cast en; + } + #end +} diff --git a/std/hl/_std/Reflect.hx b/std/hl/_std/Reflect.hx index 3ec373a9cc5..1969d33a808 100644 --- a/std/hl/_std/Reflect.hx +++ b/std/hl/_std/Reflect.hx @@ -19,35 +19,38 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ + +import haxe.runtime.FieldHost; + @:coreApi class Reflect { - public static function hasField(o:Dynamic, field:String):Bool { + public static function hasField(o:FieldHost, field:String):Bool { if (field == null) return false; var hash = @:privateAccess field.bytes.hash(); return hl.Api.hasField(o, hash); } - public static function field(o:Dynamic, field:String):Dynamic { + public static function field(o:FieldHost, field:String):Dynamic { if (field == null) return null; var hash = @:privateAccess field.bytes.hash(); return hl.Api.getField(o, hash); } - public static function setField(o:Dynamic, field:String, value:Dynamic):Void { + public static function setField(o:FieldHost, field:String, value:Dynamic):Void { var hash = @:privateAccess field.bytes.hash(); hl.Api.setField(o, hash, value); } - public static function getProperty(o:Dynamic, field:String):Dynamic { + public static function getProperty(o:FieldHost, field:String):Dynamic { var f:Dynamic = Reflect.field(o, "get_" + field); if (f != null) return f(); return Reflect.field(o, field); } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void { var f:Dynamic = Reflect.field(o, "set_" + field); if (f != null) f(value); @@ -87,7 +90,7 @@ class Reflect { return null; } - public static function fields(o:haxe.runtime.FieldHost):Array { + public static function fields(o:FieldHost):Array { var fields = getObjectFields(o); if (fields == null) return []; @@ -121,7 +124,7 @@ class Reflect { return t.kind == HEnum; } - public static function deleteField(o:Dynamic, field:String):Bool { + public static function deleteField(o:FieldHost, field:String):Bool { return hl.Api.deleteField(o, @:privateAccess field.bytes.hash()); } diff --git a/std/js/_std/Reflect.hx b/std/js/_std/Reflect.hx index e95fff52d9e..9edc273854e 100644 --- a/std/js/_std/Reflect.hx +++ b/std/js/_std/Reflect.hx @@ -19,31 +19,34 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ + +import haxe.runtime.FieldHost; + @:coreApi class Reflect { @:pure - public inline static function hasField(o:Dynamic, field:String):Bool { + public inline static function hasField(o:FieldHost, field:String):Bool { return js.lib.Object.prototype.hasOwnProperty.call(o, field); } @:pure - public static function field(o:Dynamic, field:String):Dynamic { + public static function field(o:FieldHost, field:String):Dynamic { try - return o[cast field] + return o.asArrayAccess()[cast field] catch (e:Dynamic) return null; } - public inline static function setField(o:Dynamic, field:String, value:Dynamic):Void { - o[cast field] = value; + public inline static function setField(o:FieldHost, field:String, value:Dynamic):Void { + o.asArrayAccess()[cast field] = value; } - public static function getProperty(o:Dynamic, field:String):Dynamic untyped { + public static function getProperty(o:FieldHost, field:String):Dynamic untyped { var tmp; return if (o == null) __define_feature__("Reflect.getProperty", null) else if (o.__properties__ && (tmp = o.__properties__["get_" + field])) o[tmp]() else o[field]; } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void untyped { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void untyped { var tmp; if (o.__properties__ && (tmp = o.__properties__["set_" + field])) o[tmp](value) @@ -55,7 +58,7 @@ return (cast func : js.lib.Function).apply(o, args); } - public static function fields(o:haxe.runtime.FieldHost):Array { + public static function fields(o:FieldHost):Array { var a = []; if (o != null) untyped { var hasOwnProperty = js.lib.Object.prototype.hasOwnProperty; @@ -93,7 +96,7 @@ return v != null && v.__enum__ != null; } - public static function deleteField(o:Dynamic, field:String):Bool { + public static function deleteField(o:FieldHost, field:String):Bool { if (!hasField(o, field)) return false; js.Syntax.delete(o, field); @@ -105,7 +108,7 @@ return null; var o2:Dynamic = {}; for (f in Reflect.fields(cast o)) - Reflect.setField(o2, f, Reflect.field(o, f)); + Reflect.setField(cast o2, f, Reflect.field(cast o, f)); return o2; } diff --git a/std/jvm/_std/Reflect.hx b/std/jvm/_std/Reflect.hx index f466d6792cd..8564e760bc0 100644 --- a/std/jvm/_std/Reflect.hx +++ b/std/jvm/_std/Reflect.hx @@ -27,12 +27,13 @@ import java.lang.Number; import java.math.BigDecimal; import java.math.BigInteger; import jvm.Jvm; +import haxe.runtime.FieldHost; using jvm.NativeTools.NativeClassTools; @:coreApi class Reflect { - public static function hasField(o:Dynamic, field:String):Bool { + public static function hasField(o:FieldHost, field:String):Bool { if (!Jvm.instanceof(o, jvm.DynamicObject)) { var c:java.lang.Class = Jvm.instanceof(o, java.lang.Class) ? cast o : (cast o : java.lang.Object).getClass(); try { @@ -45,18 +46,18 @@ class Reflect { return (cast o : jvm.DynamicObject)._hx_hasField(field); } - public static function field(o:Dynamic, field:String):Dynamic { + public static function field(o:FieldHost, field:String):Dynamic { if (o == null) { return null; } return Jvm.readField(o, field); } - public static function setField(o:Dynamic, field:String, value:Dynamic):Void { + public static function setField(o:FieldHost, field:String, value:Dynamic):Void { Jvm.writeField(o, field, value); } - public static function getProperty(o:Dynamic, field:String):Dynamic { + public static function getProperty(o:FieldHost, field:String):Dynamic { var f = Reflect.field(o, "get_" + field); if (f != null) { return f(); @@ -64,7 +65,7 @@ class Reflect { return Reflect.field(o, field); } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void { var f = Reflect.field(o, "set_" + field); if (f != null) { f(value); @@ -77,7 +78,7 @@ class Reflect { return Jvm.call(cast func, @:privateAccess args.getNative()); } - public static function fields(o:haxe.runtime.FieldHost):Array { + public static function fields(o:FieldHost):Array { if (!Jvm.instanceof(o, jvm.DynamicObject)) { if (Jvm.instanceof(o, java.lang.Class)) { return Type.getClassFields(cast o); @@ -191,7 +192,7 @@ class Reflect { return @:privateAccess Type.isEnumValueClass((cast v : java.lang.Object).getClass()); } - public static function deleteField(o:Dynamic, field:String):Bool { + public static function deleteField(o:FieldHost, field:String):Bool { if (!Jvm.instanceof(o, jvm.DynamicObject)) { return false; } diff --git a/std/lua/_std/Reflect.hx b/std/lua/_std/Reflect.hx index d92049f8e92..e7ed2fa136a 100644 --- a/std/lua/_std/Reflect.hx +++ b/std/lua/_std/Reflect.hx @@ -23,9 +23,10 @@ import lua.Boot; import lua.Lua; import lua.TableTools; +import haxe.runtime.FieldHost; @:coreApi class Reflect { - public inline static function hasField(o:Dynamic, field:String):Bool { + public inline static function hasField(o:FieldHost, field:String):Bool { return if (inline isFunction(o)) { false; } else if (Lua.type(o) == "string" && (untyped String.prototype[field] != null || field == "length")) { @@ -33,7 +34,7 @@ import lua.TableTools; } else untyped o.__fields__ != null ? o.__fields__[field] != null : o[field] != null; } - public static function field(o:Dynamic, field:String):Dynamic untyped { + public static function field(o:FieldHost, field:String):Dynamic untyped { if (Lua.type(o) == "string") { if (field == "length") { return cast(o : String).length; @@ -44,21 +45,21 @@ import lua.TableTools; } } - public inline static function setField(o:Dynamic, field:String, value:Dynamic):Void untyped { + public inline static function setField(o:FieldHost, field:String, value:Dynamic):Void untyped { o[field] = value; } - public static function getProperty(o:Dynamic, field:String):Dynamic { + public static function getProperty(o:FieldHost, field:String):Dynamic { return if (o == null) { untyped __define_feature__("Reflect.getProperty", null); - } else if (o.__properties__ != null && Reflect.field(o, "get_" + field) != null) { + } else if (o.asDynamic().__properties__ != null && Reflect.field(o, "get_" + field) != null) { callMethod(o, Reflect.field(o, "get_" + field), []); } else { Reflect.field(o, field); } } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void untyped { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void untyped { if (o.__properties__ != null && o.__properties__["set_" + field]) { var tmp:String = o.__properties__["set_" + field]; callMethod(o, Reflect.field(o, tmp), [value]); @@ -83,7 +84,7 @@ import lua.TableTools; } } - public static function fields(o:haxe.runtime.FieldHost):Array { + public static function fields(o:FieldHost):Array { if (lua.Lua.type(o) == "string") { return Reflect.fields(untyped String.prototype); } else { @@ -122,7 +123,7 @@ import lua.TableTools; return v != null && Std.isOfType(v, lua.Table) && v.__enum__ != null; } - public static function deleteField(o:Dynamic, field:String):Bool untyped { + public static function deleteField(o:FieldHost, field:String):Bool untyped { if (!hasField(o, field)) return false; o[field] = null; @@ -135,7 +136,7 @@ import lua.TableTools; return null; var o2:Dynamic = {}; for (f in Reflect.fields(cast o)) - Reflect.setField(o2, f, Reflect.field(o, f)); + Reflect.setField(cast o2, f, Reflect.field(cast o, f)); return o2; } diff --git a/std/neko/_std/Reflect.hx b/std/neko/_std/Reflect.hx index 6e331135219..68b71947564 100644 --- a/std/neko/_std/Reflect.hx +++ b/std/neko/_std/Reflect.hx @@ -19,28 +19,31 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ + +import haxe.runtime.FieldHost; + @:coreApi class Reflect { - public static function hasField(o:Dynamic, field:String):Bool untyped { + public static function hasField(o:FieldHost, field:String):Bool untyped { return $typeof(o) == $tobject && $objfield(o, $fasthash(field.__s)); } - public inline static function field(o:Dynamic, field:String):Dynamic untyped { + public inline static function field(o:FieldHost, field:String):Dynamic untyped { return if ($typeof(o) != $tobject) null else $objget(o, $fasthash(field.__s)); } - public inline static function setField(o:Dynamic, field:String, value:Dynamic):Void untyped { + public inline static function setField(o:FieldHost, field:String, value:Dynamic):Void untyped { if ($typeof(o) == $tobject) $objset(o, $hash(field.__s), value); } - public static inline function getProperty(o:Dynamic, field:String):Dynamic untyped { + public static inline function getProperty(o:FieldHost, field:String):Dynamic untyped { var tmp; return if ($typeof(o) != $tobject) null else if (o.__properties__ != null && (tmp = $objget(o.__properties__, $fasthash("get_".__s + field.__s))) != null) $call($objget(o, $fasthash(tmp)), o, $array()) else $objget(o, $fasthash(field.__s)); } - public static inline function setProperty(o:Dynamic, field:String, value:Dynamic):Void untyped { + public static inline function setProperty(o:FieldHost, field:String, value:Dynamic):Void untyped { if ($typeof(o) == $tobject) { var tmp; if (o.__properties__ != null && (tmp = $objget(o.__properties__, $fasthash("set_".__s + field.__s))) != null) @@ -62,7 +65,7 @@ return $call(func, o, a); } - public static function fields(o:haxe.runtime.FieldHost):Array untyped { + public static function fields(o:FieldHost):Array untyped { if ($typeof(o) != $tobject) return new Array(); else { @@ -104,7 +107,7 @@ return $typeof(v) == $tobject && v.__enum__ != null; } - public inline static function deleteField(o:Dynamic, field:String):Bool untyped { + public inline static function deleteField(o:FieldHost, field:String):Bool untyped { return $objremove(o, $fasthash(field.__s)); } diff --git a/std/php/_std/Reflect.hx b/std/php/_std/Reflect.hx index 612c04bbebe..0aa47c7be1b 100644 --- a/std/php/_std/Reflect.hx +++ b/std/php/_std/Reflect.hx @@ -26,18 +26,19 @@ import php.Closure; import php.Const; import php.NativeAssocArray; import php.Syntax; +import haxe.runtime.FieldHost; using php.Global; @:coreApi class Reflect { - public static function hasField(o:Dynamic, field:String):Bool { + public static function hasField(o:FieldHost, field:String):Bool { if (!o.is_object()) return false; if (o.property_exists(field)) return true; if (Boot.isClass(o)) { - var phpClassName = Boot.castClass(o).phpClassName; + var phpClassName = Boot.castClass(o.asDynamic()).phpClassName; return Global.property_exists(phpClassName, field) || Global.method_exists(phpClassName, field) || Global.defined('$phpClassName::$field'); @@ -46,9 +47,9 @@ using php.Global; return false; } - public static function field(o:Dynamic, field:String):Dynamic { + public static function field(o:FieldHost, field:String):Dynamic { if (o.is_string()) { - return Syntax.field(Boot.dynamicString(o), field); + return Syntax.field(Boot.dynamicString(o.asDynamic()), field); } if (!o.is_object()) return null; @@ -58,14 +59,14 @@ using php.Global; } if (o.property_exists(field)) { - return Syntax.field(o, field); + return Syntax.field(o.asDynamic(), field); } if (o.method_exists(field)) { - return Boot.getInstanceClosure(o, field); + return Boot.getInstanceClosure(o.asDynamic(), field); } if (Boot.isClass(o)) { - var phpClassName = Boot.castClass(o).phpClassName; + var phpClassName = Boot.castClass(o.asDynamic()).phpClassName; if (Global.defined('$phpClassName::$field')) { return Global.constant('$phpClassName::$field'); } @@ -80,18 +81,18 @@ using php.Global; return null; } - public static function setField(o:Dynamic, field:String, value:Dynamic):Void { + public static function setField(o:FieldHost, field:String, value:Dynamic):Void { Syntax.setField(o, field, value); } - public static function getProperty(o:Dynamic, field:String):Dynamic { + public static function getProperty(o:FieldHost, field:String):Dynamic { if (o.is_object()) { if (Boot.isClass(o)) { - var phpClassName = Boot.castClass(o).phpClassName; + var phpClassName = Boot.castClass(o.asDynamic()).phpClassName; if (Boot.hasGetter(phpClassName, field)) { return Syntax.staticCall(phpClassName, 'get_$field'); } - } else if (Boot.hasGetter(Global.get_class(o), field)) { + } else if (Boot.hasGetter(Global.get_class(o.asDynamic()), field)) { return Syntax.call(o, 'get_$field'); } } @@ -99,9 +100,9 @@ using php.Global; return Reflect.field(o, field); } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void { if (o.is_object()) { - if (Boot.hasSetter(Global.get_class(o), field)) { + if (Boot.hasSetter(Global.get_class(o.asStructure()), field)) { Syntax.call(o, 'set_$field', value); } else { Syntax.setField(o, field, value); @@ -113,7 +114,7 @@ using php.Global; return Global.call_user_func_array(func, @:privateAccess args.arr); } - public static function fields(o:haxe.runtime.FieldHost):Array { + public static function fields(o:FieldHost):Array { if (Global.is_object(o)) { return @:privateAccess Array.wrap(Global.get_object_vars(cast o).array_keys()); } @@ -154,7 +155,7 @@ using php.Global; return Boot.isEnumValue(v); } - public static function deleteField(o:Dynamic, field:String):Bool { + public static function deleteField(o:FieldHost, field:String):Bool { if (hasField(o, field)) { Global.unset(Syntax.field(o, field)); return true; diff --git a/std/python/_std/Reflect.hx b/std/python/_std/Reflect.hx index 9a95b0f6396..8400b929c1a 100644 --- a/std/python/_std/Reflect.hx +++ b/std/python/_std/Reflect.hx @@ -30,25 +30,26 @@ import python.internal.MethodClosure; import python.internal.StringImpl; import python.internal.UBuiltins; import python.lib.Inspect; +import haxe.runtime.FieldHost; @:access(python.Boot) @:coreApi class Reflect { - public static inline function hasField(o:Dynamic, field:String):Bool { + public static inline function hasField(o:FieldHost, field:String):Bool { return Boot.hasField(o, field); } @:ifFeature("dynamic_read", "anon_optional_read") - public static function field(o:Dynamic, field:String):Dynamic { + public static function field(o:FieldHost, field:String):Dynamic { return Boot.field(o, field); } @:ifFeature("dynamic_write", "anon_optional_write") - public static inline function setField(o:Dynamic, field:String, value:Dynamic):Void { + public static inline function setField(o:FieldHost, field:String, value:Dynamic):Void { UBuiltins.setattr(o, handleKeywords(field), value); } - public static function getProperty(o:Dynamic, field:String):Dynamic { + public static function getProperty(o:FieldHost, field:String):Dynamic { if (o == null) return null; @@ -62,7 +63,7 @@ class Reflect { return Reflect.field(o, field); } - public static function setProperty(o:Dynamic, field:String, value:Dynamic):Void { + public static function setProperty(o:FieldHost, field:String, value:Dynamic):Void { var field = handleKeywords(field); if (Boot.isAnonObject(o)) UBuiltins.setattr(o, field, value); @@ -76,7 +77,7 @@ class Reflect { return if (UBuiltins.callable(func)) func(python.Syntax.varArgs(args)) else null; } - public static inline function fields(o:haxe.runtime.FieldHost):Array { + public static inline function fields(o:FieldHost):Array { return python.Boot.fields(o); } @@ -119,7 +120,7 @@ class Reflect { return v != Enum && UBuiltins.isinstance(v, cast Enum); } - public static function deleteField(o:Dynamic, field:String):Bool { + public static function deleteField(o:FieldHost, field:String):Bool { field = handleKeywords(field); if (!hasField(o, field)) return false; @@ -132,7 +133,7 @@ class Reflect { return null; var o2:Dynamic = {}; for (f in Reflect.fields(cast o)) - Reflect.setField(o2, f, Reflect.field(o, f)); + Reflect.setField(cast o2, f, Reflect.field(cast o, f)); return o2; } diff --git a/tests/unit/src/unit/issues/Issue10993.hx b/tests/unit/src/unit/issues/Issue10993.hx index 4be324ca523..fd4b4966a91 100644 --- a/tests/unit/src/unit/issues/Issue10993.hx +++ b/tests/unit/src/unit/issues/Issue10993.hx @@ -5,13 +5,12 @@ enum Issue10993_TestEnum { } class Issue10993 extends Test { - function testHasFieldWithEnum() { - final foo = Issue10993_TestEnum.FOO; - eq(false, Reflect.hasField(foo, "bar")); - } - - function testHasFieldWithFunction() { - final foo = () -> null; - eq(false, Reflect.hasField(foo, "bar")); - } + // function testHasFieldWithEnum() { + // final foo = Issue10993_TestEnum.FOO; + // eq(false, Reflect.hasField(foo, "bar")); + // } + // function testHasFieldWithFunction() { + // final foo = () -> null; + // eq(false, Reflect.hasField(foo, "bar")); + // } } diff --git a/tests/unit/src/unit/issues/Issue2963.hx b/tests/unit/src/unit/issues/Issue2963.hx index 56df7d155b4..cb72a69dc26 100644 --- a/tests/unit/src/unit/issues/Issue2963.hx +++ b/tests/unit/src/unit/issues/Issue2963.hx @@ -9,7 +9,7 @@ class Issue2963 extends Test { var x:Dynamic> = {}; var v2:Null = Reflect.field(x,'k'); eq(v2,null); - var v3:Null = Reflect.field(a,'k'); + var v3:Null = Reflect.field(cast a, 'k'); eq(v3,null); var v4 = x.k; eq(v4,null); diff --git a/tests/unit/src/unitstd/Reflect.unit.hx b/tests/unit/src/unitstd/Reflect.unit.hx index 7fa84752cb4..8a9918f3ab3 100644 --- a/tests/unit/src/unitstd/Reflect.unit.hx +++ b/tests/unit/src/unitstd/Reflect.unit.hx @@ -14,8 +14,7 @@ Reflect.field(c, "prop") == "prop"; Reflect.field(c, "func")() == "foo"; Reflect.field(c, "propAcc") == "0"; var n = null; -Reflect.field(n, n) == null; -Reflect.field(1, "foo") == null; +Reflect.field(n, null) == null; // setField Reflect.setField(x, "a", 2); From 016325a695cd0c2ec4e67f033460661c3e6801ba Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Wed, 19 Mar 2025 06:25:18 +0100 Subject: [PATCH 5/6] haxelib submodule update --- extra/haxelib_src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/haxelib_src b/extra/haxelib_src index 5a836287828..25207418100 160000 --- a/extra/haxelib_src +++ b/extra/haxelib_src @@ -1 +1 @@ -Subproject commit 5a836287828fdaeb6aa91695a5eb399cee0f6640 +Subproject commit 252074181003db5cab9774d570eeec5f2ac78af9 From ac7dc50e3d022e4f17d5a30e496fecae798d2b5e Mon Sep 17 00:00:00 2001 From: Simon Krajewski Date: Wed, 19 Mar 2025 06:42:59 +0100 Subject: [PATCH 6/6] avoid HL's wrath --- std/haxe/runtime/FieldHost.hx | 4 ---- std/js/_std/Reflect.hx | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/std/haxe/runtime/FieldHost.hx b/std/haxe/runtime/FieldHost.hx index 0d1ec079af3..1f01790d231 100644 --- a/std/haxe/runtime/FieldHost.hx +++ b/std/haxe/runtime/FieldHost.hx @@ -5,10 +5,6 @@ abstract FieldHost(Dynamic) from {} from Dynamic from Class { - public inline function asArrayAccess():ArrayAccess { - return cast this; - } - public inline function asStructure():{} { return cast this; } diff --git a/std/js/_std/Reflect.hx b/std/js/_std/Reflect.hx index 9edc273854e..8ce6f2a9385 100644 --- a/std/js/_std/Reflect.hx +++ b/std/js/_std/Reflect.hx @@ -31,13 +31,13 @@ import haxe.runtime.FieldHost; @:pure public static function field(o:FieldHost, field:String):Dynamic { try - return o.asArrayAccess()[cast field] + return o.asDynamic()[cast field] catch (e:Dynamic) return null; } public inline static function setField(o:FieldHost, field:String, value:Dynamic):Void { - o.asArrayAccess()[cast field] = value; + o.asDynamic()[cast field] = value; } public static function getProperty(o:FieldHost, field:String):Dynamic untyped {