瀏覽代碼

refactor optimize_for_loop to allow iteration of abstract that have a get_length field an an @:arrayAccess getter (closes #2564, closes #2770)

Simon Krajewski 11 年之前
父節點
當前提交
9740e2dbcf
共有 4 個文件被更改,包括 92 次插入17 次删除
  1. 1 0
      extra/CHANGES.txt
  2. 44 14
      optimizer.ml
  3. 2 3
      std/haxe/ds/Vector.hx
  4. 45 0
      tests/unit/issues/Issue2564.hx

+ 1 - 0
extra/CHANGES.txt

@@ -9,6 +9,7 @@
 	General improvements and optimizations:
 
 	all : cached file exist checks to speed up compilations with a lot of class paths
+	all : allowed iterating on abstract which have get_length and @:arrayAccess fields
 
 2014-03-15: 3.1.1
 

+ 44 - 14
optimizer.ml

@@ -549,7 +549,7 @@ let rec optimize_for_loop ctx i e1 e2 p =
 	let mk_field e n =
 		TField (e,try quick_field e.etype n with Not_found -> assert false)
 	in
-	let gen_int_iter pt =
+	let gen_int_iter pt f_get f_length =
 		let i = add_local ctx i pt in
 		let index = gen_local ctx t_int in
 		let arr, avars = (match e1.eexpr with
@@ -560,21 +560,14 @@ let rec optimize_for_loop ctx i e1 e2 p =
 		) in
 		let iexpr = mk (TLocal index) t_int p in
 		let e2 = type_expr ctx e2 NoValue in
-		let aget = mk (TVar (i,Some (mk (TArray (arr,iexpr)) pt p))) t_void p in
+		let aget = mk (TVar (i,Some (f_get arr iexpr pt p))) t_void p in
 		let incr = mk (TUnop (Increment,Prefix,iexpr)) t_int p in
 		let block = match e2.eexpr with
 			| TBlock el -> mk (TBlock (aget :: incr :: el)) t_void e2.epos
 			| _ -> mk (TBlock [aget;incr;e2]) t_void p
 		in
 		let ivar = Some (mk (TConst (TInt 0l)) t_int p) in
-		let elength = match follow e1.etype with
-			| TAbstract({a_impl = Some c},_) ->
-				let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
-				let ethis = mk (TTypeExpr (TClassDecl c)) ta e1.epos in
-				let efield = mk (mk_field ethis "get_length") (tfun [arr.etype] t_int) p in
-				make_call ctx efield [arr] t_int e1.epos
-			| _ -> mk (mk_field arr "length") t_int p
-		in
+		let elength = f_length arr p in
 		let el = [mk (TWhile (
 				mk (TBinop (OpLt, iexpr, elength)) ctx.t.tbool p,
 				block,
@@ -585,6 +578,12 @@ let rec optimize_for_loop ctx i e1 e2 p =
 		let el = (mk (TVar (index,ivar)) t_void p) :: el in
 		lblock el
 	in
+	let get_next_array_element arr iexpr pt p =
+		(mk (TArray (arr,iexpr)) pt p)
+	in
+	let get_array_length arr p =
+		mk (mk_field arr "length") ctx.com.basic.tint p
+	in
 	match e1.eexpr, follow e1.etype with
 	| TNew ({ cl_path = ([],"IntIterator") },[],[i1;i2]) , _ ->
 		let max = (match i1.eexpr , i2.eexpr with
@@ -642,11 +641,42 @@ let rec optimize_for_loop ctx i e1 e2 p =
 			])
 	| _ , TInst({ cl_path = [],"Array" },[pt])
 	| _ , TInst({ cl_path = ["flash"],"Vector" },[pt]) ->
-		gen_int_iter pt
+		gen_int_iter pt get_next_array_element get_array_length
 	| _ , TInst({ cl_array_access = Some pt } as c,pl) when (try match follow (PMap.find "length" c.cl_fields).cf_type with TAbstract ({ a_path = [],"Int" },[]) -> true | _ -> false with Not_found -> false) && not (PMap.mem "iterator" c.cl_fields) ->
-		gen_int_iter (apply_params c.cl_types pl pt)
-	| (TLocal _ | TField _), TAbstract({a_impl = Some c} as a,[pt]) when Meta.has Meta.ArrayAccess a.a_meta && (try match follow (PMap.find "length" c.cl_statics).cf_type with TAbstract ({ a_path = [],"Int" },[]) -> true | _ -> false with Not_found -> false) && not (PMap.mem "iterator" c.cl_statics) ->
-		gen_int_iter pt
+		gen_int_iter (apply_params c.cl_types pl pt) get_next_array_element get_array_length
+	| _, TAbstract({a_impl = Some c} as a,tl) ->
+		begin try
+			let cf_length = PMap.find "get_length" c.cl_statics in
+			let get_length e p =
+				make_static_call ctx c cf_length (apply_params a.a_types tl) [e] ctx.com.basic.tint p
+			in
+			begin match follow cf_length.cf_type with
+				| TFun(_,tr) ->
+					begin match follow tr with
+						| TAbstract({a_path = [],"Int"},_) -> ()
+						| _ -> raise Not_found
+					end
+				| _ ->
+					raise Not_found
+			end;
+			begin try
+				(* first try: do we have an @:arrayAccess getter field? *)
+				let cf,tf,r = find_array_access a tl ctx.com.basic.tint (mk_mono()) false in
+				let get_next e_base e_index t p =
+					make_static_call ctx c cf (apply_params a.a_types tl) [e_base;e_index] r p
+				in
+				gen_int_iter r get_next get_length
+			with Not_found ->
+				(* second try: do we have @:arrayAccess on the abstract itself? *)
+				if not (Meta.has Meta.ArrayAccess a.a_meta) then raise Not_found;
+				(* let's allow this only for core-type abstracts *)
+				if not (Meta.has Meta.CoreType a.a_meta) then raise Not_found;
+				(* in which case we assume that a singular type parameter is the element type *)
+				let t = match tl with [t] -> t | _ -> raise Not_found in
+				gen_int_iter t get_next_array_element get_length
+		end with Not_found ->
+			None
+		end
 	| _ , TInst ({ cl_kind = KGenericInstance ({ cl_path = ["haxe";"ds"],"GenericStack" },[t]) } as c,[]) ->
 		let tcell = (try (PMap.find "head" c.cl_fields).cf_type with Not_found -> assert false) in
 		let i = add_local ctx i t in

+ 2 - 3
std/haxe/ds/Vector.hx

@@ -37,7 +37,6 @@ private typedef VectorData<T> = #if flash10
 	A Vector is a storage of fixed size. It can be faster than Array on some
 	targets, and is never slower.
 **/
-@:arrayAccess
 abstract Vector<T>(VectorData<T>) {
 	/**
 		Creates a new Vector of length `length`.
@@ -75,7 +74,7 @@ abstract Vector<T>(VectorData<T>) {
 		If `index` is negative or exceeds `this.length`, the result is
 		unspecified.
 	**/
-	public inline function get(index:Int):Null<T> {
+	@:arrayAccess public inline function get(index:Int):Null<T> {
 		return this[index];
 	}
 
@@ -85,7 +84,7 @@ abstract Vector<T>(VectorData<T>) {
 		If `index` is negative or exceeds `this.length`, the result is
 		unspecified.
 	**/
-	public inline function set(index:Int, val:T):T {
+	@:arrayAccess public inline function set(index:Int, val:T):T {
 		return this[index] = val;
 	}
 

+ 45 - 0
tests/unit/issues/Issue2564.hx

@@ -0,0 +1,45 @@
+package unit.issues;
+import unit.Test;
+
+private abstract A1<T>(Array<T>) {
+	public function new(arr:Array<T>):Void {
+		this = arr;
+	}
+
+	public var length(get, never):Int;
+	inline function get_length() return this.length;
+
+	@:arrayAccess inline function access(key:Int):T return this[key];
+}
+
+//@:coreType
+//@:arrayAccess
+//private abstract A2<T> from Int {
+	//public var length(get, never):Int;
+	//inline function get_length():Int return cast this;
+//}
+
+class Issue2564 extends Test {
+	function test() {
+		var acc = "";
+		for (i in new A1([1, 2, 3])) {
+			acc += ";" + i;
+		}
+		eq(";1;2;3", acc);
+		
+		// can't really test this cross-target
+		//for (i in (0 : A2<Int>)) {
+			//
+		//}
+		
+		var vec = new haxe.ds.Vector(3);
+		vec[0] = 1;
+		vec[1] = 2;
+		vec[2] = 3;
+		var acc = "";
+		for (i in vec) {
+			acc += ";" + i;
+		}
+		eq(";1;2;3", acc);
+	}
+}