Pārlūkot izejas kodu

use Reflect.field/setField on dynamic field access

Simon Krajewski 11 gadi atpakaļ
vecāks
revīzija
9a449f84c3

+ 43 - 5
genpy.ml

@@ -74,10 +74,14 @@ module Transformer = struct
 
 	let t_bool = ref t_dynamic
 	let t_void = ref t_dynamic
+	let t_string= ref t_dynamic
+	let c_reflect = ref null_class
 
 	let init com =
 		t_bool := com.basic.tbool;
-		t_void := com.basic.tvoid
+		t_void := com.basic.tvoid;
+		t_string := com.basic.tstring;
+		c_reflect := Utils.class_of_module_type (Utils.find_type com ([],"Reflect"))
 
 	and debug_expr e =
 		let s_type = Type.s_type (print_context()) in
@@ -140,6 +144,25 @@ module Transformer = struct
 		| _ ->
 			e
 
+	let dynamic_field_read e s =
+		Utils.mk_static_call_2 !c_reflect "field" [e;mk (TConst (TString s)) !t_string e.epos] e.epos
+
+	let dynamic_field_write e1 s e2 =
+		Utils.mk_static_call_2 !c_reflect "setField" [e1;mk (TConst (TString s)) !t_string e1.epos;e2] e1.epos
+
+	let dynamic_field_read_write next_id e1 s op e2 =
+		let id = next_id() in
+		let temp_var = to_tvar id e1.etype in
+		let temp_var_def = mk (TVar(temp_var,Some e1)) e1.etype e1.epos in
+		let temp_local = mk (TLocal temp_var) e1.etype e1.epos in
+		let e_field = dynamic_field_read temp_local s in
+		let e_op = mk (TBinop(op,e_field,e2)) e_field.etype e_field.epos in
+		let e_set_field = dynamic_field_write temp_local s e_op in
+		mk (TBlock [
+			temp_var_def;
+			e_set_field;
+		]) e_set_field.etype e_set_field.epos
+
 	let add_non_locals_to_func e =
 		match e.eexpr with
 		| TFunction f ->
@@ -621,7 +644,24 @@ module Transformer = struct
 			let e1 = trans true [] e in
 			let r = { a_expr with eexpr = TUnop(op, Prefix, e1.a_expr) } in
 			lift_expr ~blocks:e1.a_blocks r
-
+		| (_, TField(e,FAnon cf)) when Meta.has Meta.Optional cf.cf_meta ->
+			let e = dynamic_field_read e cf.cf_name in
+			transform_expr e
+		| (_, TBinop(OpAssign,{eexpr = TField(e1,FAnon cf)},e2)) when Meta.has Meta.Optional cf.cf_meta ->
+			let e = dynamic_field_write e1 cf.cf_name e2 in
+			transform_expr e
+		| (_, TBinop(OpAssignOp op,{eexpr = TField(e1,FAnon cf)},e2)) when Meta.has Meta.Optional cf.cf_meta ->
+			let e = dynamic_field_read_write ae.a_next_id e1 cf.cf_name op e2 in
+			transform_expr e
+		| (_, TField(e,FDynamic s)) ->
+			let e = dynamic_field_read e s in
+			transform_expr e
+		| (_, TBinop(OpAssign,{eexpr = TField(e1,FDynamic s)},e2)) ->
+			let e = dynamic_field_write e1 s e2 in
+			transform_expr e
+		| (_, TBinop(OpAssignOp op,{eexpr = TField(e1,FDynamic s)},e2)) ->
+			let e = dynamic_field_read_write ae.a_next_id e1 s op e2 in
+			transform_expr e
 		| (is_value, TBinop(OpAssign, left, right))->
 			(let left = trans true [] left in
 			let right = trans true [] right in
@@ -733,8 +773,6 @@ module Transformer = struct
 
 		| ( _, TBreak ) | ( _, TContinue ) ->
 			lift_expr a_expr
-		(*| _ ->
-			lift_expr ae.a_expr*)
 
 	and transform e =
 		to_expr (transform1 (lift_expr e))
@@ -1444,7 +1482,7 @@ module Generator = struct
 			| _,Some ({eexpr = TFunction f} as ef) ->
 				let ethis = mk (TConst TThis) (TInst(c,List.map snd c.cl_types)) cf.cf_pos in
 				let member_data = List.map (fun cf ->
-					let ef = mk (TField(ethis,FDynamic cf.cf_name)) cf.cf_type cf.cf_pos in
+					let ef = mk (TField(ethis,FInstance(c, cf))) cf.cf_type cf.cf_pos in
 					mk (TBinop(OpAssign,ef,null ef.etype ef.epos)) ef.etype ef.epos
 				) member_inits in
 				let e = {f.tf_expr with eexpr = TBlock (member_data @ [f.tf_expr])} in

+ 1 - 1
std/python/_std/Math.hx

@@ -303,7 +303,7 @@ extern class Math
 		NEGATIVE_INFINITY = __python__("float")('-inf');
 		POSITIVE_INFINITY = __python__("float")('inf');
 		NaN = __python__("float")('nan');
-		PI = Math.pi;
+		PI = Reflect.field(Math, "pi");
 	}
 
 }

+ 29 - 44
std/python/_std/Reflect.hx

@@ -1,12 +1,3 @@
-import Map;
-import haxe.ds.StringMap;
-import python.internal.KeywordHandler;
-import python.lib.Builtin;
-import python.lib.Inspect;
-import python.lib.Types;
-
-
-
 /*
  * Copyright (C)2005-2012 Haxe Foundation
  *
@@ -28,35 +19,37 @@ import python.lib.Types;
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-@:coreApi class Reflect {
 
-	public static function hasField( o : Dynamic, field : String ) : Bool 
+import python.internal.KeywordHandler;
+import python.lib.Builtin;
+import python.lib.Inspect;
+import python.lib.Types;
+
+@:coreApi
+class Reflect {
+
+	public static function hasField( o : Dynamic, field : String ) : Bool
 	{
 		var field = KeywordHandler.handleKeywords(field);
-		//return untyped __js__('Object').prototype.hasOwnProperty.call(o, field);
 		return Builtin.hasattr(o, field);
-		
 	}
 
-	public static function field( o : Dynamic, field : String ) : Dynamic 
+	@:keep public static function field( o : Dynamic, field : String ) : Dynamic
 	{
 		if (field == null) return null;
-		var field = KeywordHandler.handleKeywords(field);	
+		var field = KeywordHandler.handleKeywords(field);
 		return if (Builtin.hasattr(o, field)) Builtin.getattr(o, field) else null;
-		
 	}
 
-	public inline static function setField( o : Dynamic, field : String, value : Dynamic ) : Void untyped 
+	@:keep public static function setField( o : Dynamic, field : String, value : Dynamic ) : Void untyped
 	{
 		var field = KeywordHandler.handleKeywords(field);
 		return __define_feature__("Reflect.setField",Builtin.setattr(o,field,value));
 	}
 
-	public static function getProperty( o : Dynamic, field : String ) : Dynamic 
+	public static function getProperty( o : Dynamic, field : String ) : Dynamic
 	{
 		var field = KeywordHandler.handleKeywords(field);
-		//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];
 		var tmp = null;
 		if (o == null) {
 			return null;
@@ -68,11 +61,10 @@ import python.lib.Types;
 				return Reflect.field(o, field);
 			}
 		}
-		//return if (o == null) null else if ( (tmp=Reflect.field(o, "get_" + field)) != null && Builtin.callable(tmp)) tmp() else Reflect.field(o, field);
 	}
 
-	public static inline function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void {
-		
+	public static function setProperty( o : Dynamic, field : String, value : Dynamic ) : Void {
+
 		var field = KeywordHandler.handleKeywords(field);
 
 		return if (Builtin.hasattr(o,"set_"+field)) {
@@ -80,39 +72,36 @@ import python.lib.Types;
 			tmp(value);
 		}
 		else Builtin.setattr(o,field, untyped __define_feature__("Reflect.setProperty",value));
-		
-		
-		
 	}
 
-	public static function callMethod( o : Dynamic, func : Dynamic, args : Array<Dynamic> ) : Dynamic 
+	public static function callMethod( o : Dynamic, func : Dynamic, args : Array<Dynamic> ) : Dynamic
 	{
 		var args:VarArgs = args;
 		return if (Builtin.callable(func)) func(untyped __python_varargs__(args)) else null;
 	}
 
-	public static function fields( o : Dynamic ) : Array<String> 
+	public static function fields( o : Dynamic ) : Array<String>
 	{
 		var a = [];
-		if (o != null) 
+		if (o != null)
 		{
-			if (Builtin.hasattr(o, "_hx_fields")) 
+			if (Builtin.hasattr(o, "_hx_fields"))
 			{
-				
+
 				var fields:Array<String> = o._hx_fields;
 				return fields.copy();
 			}
-			if (Builtin.isinstance(o, untyped __python__("_hx_c._hx_AnonObject"))) 
+			if (Builtin.isinstance(o, untyped __python__("_hx_c._hx_AnonObject")))
 			{
-				
+
 				var d:Dict<String, Dynamic> = Builtin.getattr(o, "__dict__");
 				var keys  = d.keys();
 				var handler = python.internal.KeywordHandler.unhandleKeywords;
 				untyped __python__("for k in keys:");
 				untyped __python__("	a.append(handler(k))");
-				
-			} 
-			else if (Builtin.hasattr(o, "__dict__")) 
+
+			}
+			else if (Builtin.hasattr(o, "__dict__"))
 			{
 				var a = [];
 				var d:Dict<String, Dynamic> = Builtin.getattr(o, "__dict__");
@@ -123,20 +112,18 @@ import python.lib.Types;
 			}
 		}
 		return a;
-
 	}
 
-	public static function isFunction( f : Dynamic ) : Bool 
+	public static function isFunction( f : Dynamic ) : Bool
 	{
 		return Inspect.isfunction(f) || Inspect.ismethod(f);
 	}
 
 	public static function compare<T>( a : T, b : T ) : Int {
 		if (a == null && b == null) return 0;
-		return 
+		return
 		if (a == null) 1 else if (b == null) -1 else
 		( a == b ) ? 0 : (((cast a) > (cast b)) ? 1 : -1);
-		//return throw "not implemented";
 	}
 
 	public static function compareMethods( f1 : Dynamic, f2 : Dynamic ) : Bool {
@@ -144,7 +131,7 @@ import python.lib.Types;
 			return true;
 		if( !isFunction(f1) || !isFunction(f2) )
 			return false;
-		
+
 		return false;
 	}
 
@@ -154,9 +141,8 @@ import python.lib.Types;
 			case TObject, TClass(_): true;
 			case _ : false;
 		}
-
 	}
-	
+
 	public static function isEnumValue( v : Dynamic ) : Bool {
 		return v != Enum && Builtin.isinstance(v, untyped Enum);
 	}
@@ -177,7 +163,6 @@ import python.lib.Types;
 	@:overload(function( f : Array<Dynamic> -> Void ) : Dynamic {})
 	public static function makeVarArgs( f : Array<Dynamic> -> Dynamic ) : Dynamic {
 		return throw "not implemented";
-		
 	}
 
 }

+ 6 - 15
std/python/_std/Std.hx

@@ -1,8 +1,3 @@
-package;
-
-import python.lib.Builtin;
-import python.lib.Inspect;
-import python.Boot;
 /*
  * Copyright (C)2005-2012 Haxe Foundation
  *
@@ -24,7 +19,12 @@ import python.Boot;
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-//import flash.Boot;
+
+package;
+
+import python.lib.Builtin;
+import python.lib.Inspect;
+import python.Boot;
 
 @:keepInit
 @:coreApi /*extern*/ class Std {
@@ -85,8 +85,6 @@ import python.Boot;
 
         if (t == Class) return false; // && !Builtin.isinstance(v, untyped Enum) && Builtin.hasattr(v, "__class__") && untyped Builtin.hasattr(v.__class__, "_hx_class_name") && !untyped Builtin.hasattr(v.__class__, "_hx_constructs")) return true;
 
-
-
         if (try Builtin.isinstance(v, t) catch (e:Dynamic) false) {
             return true;
         }
@@ -117,13 +115,8 @@ import python.Boot;
         } else {
             return false;
         }
-        //return untyped __is__(v , t);  //TODO(av) macro to check t is a Type and not null as dart only perfomrs "is" at compile time despite having a runtime Type
     }
 
-//    public static inline function instance<T>( v : {}, c : Class<T> ) : T {
-//        return untyped __as__(v, c);
-//    }
-
     @:access(python.Boot)
     @:keep
     public static function string( s : Dynamic ) : String
@@ -169,8 +162,6 @@ import python.Boot;
                 return r;
             }
         }
-
-
     }
 
     static function shortenPossibleNumber (x:String):String

+ 5 - 5
std/python/_std/haxe/ds/StringMap.hx

@@ -15,7 +15,7 @@ class StringMap<T> implements Map.IMap<String, T> {
 
 	public function get( key : String ) : Null<T> {
 		return h.get("$"+key, null);
-		
+
 	}
 
 	public function exists( key : String ) : Bool {
@@ -31,12 +31,12 @@ class StringMap<T> implements Map.IMap<String, T> {
 	}
 
 	public function keys() : Iterator<String> {
-		
+
 		var a = [];
-		
+
 		untyped __python__("for key in self.h:");
 		untyped __python__("	a.append(key[1:])");
-		
+
 		return a.iterator();
 	}
 	public function iterator() : Iterator<T> {
@@ -47,7 +47,7 @@ class StringMap<T> implements Map.IMap<String, T> {
 			next : function() { var i = iter.next(); return ref.get("$"+i, null); }
 		};
 	}
-	
+
 	public function toString() : String {
 
 		var s = new StringBuf();

+ 2 - 4
std/python/internal/KeywordHandler.hx

@@ -1,12 +1,9 @@
-
 package python.internal;
 
 import haxe.ds.StringMap;
 
 using Lambda;
 
-
-
 class KeywordHandler {
 
 	static var keywords:StringMap<Bool> = [
@@ -16,7 +13,8 @@ class KeywordHandler {
 		"break" => true,     "except" => true,    "import" => true,    "print" => true,		"float" => true,
 		"class" => true,     "exec" => true,      "in" => true,        "raise" => true,
 		"continue" => true,  "finally" => true,   "is" => true,        "return" => true,
-		"def" => true,       "for" => true,       "lambda" => true,    "try" => true, "None" => true, "list" => true
+		"def" => true,       "for" => true,       "lambda" => true,    "try" => true,
+		"None" => true,      "list" => true
 	];
 
 	public static inline function handleKeywords(name:String)

+ 22 - 0
tests/unit/TestPython.hx

@@ -1,5 +1,9 @@
 package unit;
 
+private typedef T = {
+	var value:Int;
+	@:optional var maybeValue:Int;
+}
 class TestPython extends Test {
 
 	public function testDoWhileAsExpression () {
@@ -33,6 +37,24 @@ class TestPython extends Test {
 
 	public function testX() {
 		trace(sys.io.File.getBytes("res1.txt"));
+	}
 
+	public function testOptionalStructureFields() {
+		var v:T = haxe.Json.parse('{"value": 1 }');
+		eq(1, v.value);
+		eq(null, v.maybeValue);
+		v.maybeValue = 12;
+		eq(12, v.maybeValue);
+		v.maybeValue += 9;
+		eq(21, v.maybeValue);
+
+		var v:T = haxe.Json.parse('{"value": 1 }');
+		var d:Dynamic = v;
+		eq(1, d.value);
+		eq(null, d.maybeValue);
+		d.maybeValue = 12;
+		eq(12, d.maybeValue);
+		d.maybeValue += 9;
+		eq(21, d.maybeValue);
 	}
 }

+ 2 - 1
tests/unit/compile-python.hxml

@@ -1,3 +1,4 @@
 compile-each.hxml
 -main unit.Test
--python unit.py
+-python unit.py
+-cmd "python unit.py"