Browse Source

[python] fix accessing non-existent fields on anon objects (fixes #7546)

Aleksandr Kuzmenko 6 years ago
parent
commit
05d71b0d3b

+ 16 - 1
src/generators/genpy.ml

@@ -2427,10 +2427,25 @@ module Generator = struct
 			newline ctx;
 			spr ctx "class _hx_AnonObject:\n";
 			if with_body then begin
+				spr ctx "    _hx_disable_getattr = False\n";
 				spr ctx "    def __init__(self, fields):\n";
 				spr ctx "        self.__dict__ = fields\n";
 				spr ctx "    def __repr__(self):\n";
-				spr ctx "        return repr(self.__dict__)"
+				spr ctx "        return repr(self.__dict__)\n";
+				spr ctx "    def __getattr__(self, name):\n";
+				spr ctx "        if (self._hx_disable_getattr):\n";
+				spr ctx "            raise AttributeError('field does not exist')\n";
+				spr ctx "        else:\n";
+				spr ctx "            return None\n";
+				spr ctx "    def _hx_hasattr(self,field):\n";
+				spr ctx "        self._hx_disable_getattr = True\n";
+				spr ctx "        try:\n";
+				spr ctx "            getattr(self, field)\n";
+				spr ctx "            self._hx_disable_getattr = False\n";
+				spr ctx "            return True\n";
+				spr ctx "        except AttributeError:\n";
+				spr ctx "            self._hx_disable_getattr = False\n";
+				spr ctx "            return False\n"
 			end else
 				spr ctx "    pass";
 			Hashtbl.add used_paths ([],"_hx_AnonObject") true;

+ 14 - 4
std/python/Boot.hx

@@ -147,7 +147,6 @@ class Boot {
 
 		if (UBuiltins.hasattr(o, "__class__"))
 		{
-
 			if (isAnonObject(o))
 			{
 				var toStr = null;
@@ -169,7 +168,6 @@ class Boot {
 				{
 					return toStr;
 				}
-
 			}
 			if (UBuiltins.isinstance(o, Enum)) {
 
@@ -246,7 +244,10 @@ class Boot {
 		var a = [];
 		if (o != null) {
 			if (Internal.hasFields(o)) {
-				return (Internal.fieldFields(o) : Array<String>).copy();
+				var fields = Internal.fieldFields(o);
+				if(fields != null) {
+					return (fields : Array<String>).copy();
+				}
 			}
 			if (isAnonObject(o)) {
 
@@ -255,7 +256,8 @@ class Boot {
 				var handler = unhandleKeywords;
 
 				Syntax.code("for k in keys:");
-				Syntax.code("    a.append(handler(k))");
+				Syntax.code("    if (k != '_hx_disable_getattr'):");
+				Syntax.code("        a.append(handler(k))");
 			}
 			else if (UBuiltins.hasattr(o, "__dict__")) {
 				var a = [];
@@ -291,6 +293,14 @@ class Boot {
 		return new MethodClosure(obj, func);
 	}
 
+	static function hasField( o : Dynamic, field : String ) : Bool {
+		if(isAnonObject(o))
+		{
+			return Syntax.code('{0}._hx_hasattr({1})', o, field);
+		}
+		return UBuiltins.hasattr(o, handleKeywords(field));
+	}
+
 	static function field( o : Dynamic, field : String ) : Dynamic {
 		if (field == null) return null;
 

+ 8 - 3
std/python/_std/Reflect.hx

@@ -29,6 +29,7 @@ import python.internal.MethodClosure;
 import python.lib.Inspect;
 import python.Syntax;
 import python.VarArgs;
+import python.Boot;
 import python.Boot.handleKeywords;
 
 @:access(python.Boot)
@@ -36,13 +37,13 @@ import python.Boot.handleKeywords;
 class Reflect {
 
 	public static inline function hasField( o : Dynamic, field : String ) : Bool {
-		return UBuiltins.hasattr(o, handleKeywords(field));
+		return Boot.hasField(o, field);
 	}
 
 
 	@:ifFeature("dynamic_read", "anon_optional_read")
 	public static function field( o : Dynamic, field : String ) : Dynamic {
-		return python.Boot.field(o, field);
+		return Boot.field(o, field);
 	}
 
 	@:ifFeature("dynamic_write", "anon_optional_write")
@@ -56,6 +57,8 @@ class Reflect {
 			return null;
 
 		field = handleKeywords(field);
+		if(Boot.isAnonObject(o))
+			return Reflect.field(o, field);
 		var tmp = Reflect.field(o, "get_" + field);
 		if (tmp != null && UBuiltins.callable(tmp))
 			return tmp();
@@ -66,7 +69,9 @@ class Reflect {
 	public static function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void {
 
 		var field = handleKeywords(field);
-		if (UBuiltins.hasattr(o, "set_" + field))
+		if(Boot.isAnonObject(o))
+			UBuiltins.setattr(o, field, value);
+		else if (UBuiltins.hasattr(o, "set_" + field))
 			UBuiltins.getattr(o, "set_" + field)(value);
 		else
 			UBuiltins.setattr(o, field, value);

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

@@ -0,0 +1,13 @@
+package unit.issues;
+
+class Issue7546 extends unit.Test {
+	function test() {
+		var instance:Dynamic = {};
+		eq(null, instance.field);
+
+		eq(null, ({}:Dynamic).timeout);
+
+		var instance = ({}:Dynamic);
+		eq(null, instance.field);
+	}
+}

+ 0 - 2
tests/unit/src/unit/issues/Issue8079.hx

@@ -1,7 +1,6 @@
 package unit.issues;
 
 class Issue8079 extends unit.Test {
-#if !python
 	function test() {
 		var instance = ({}:Dynamic);
 		if(instance.timeout != null) {
@@ -9,5 +8,4 @@ class Issue8079 extends unit.Test {
 		}
 		utest.Assert.pass();
 	}
-#end
 }