浏览代码

[WiP] Adding keyValueIterator to Array (#7422)

* starting to add keyValueIterator to Array

* array kv iterator for flash/hl + unit test

* adding support for k=>v iterator using RealyUniqueName iterator class

* [eval] support dynamic keyValueIterator

* [std] keey ArrayKeyValueIterator if keyValueIterator is read dynamically

* add test through Dynamic to see how much fails

* this wasn't what I wanted to test

* try to get keyValueIterator on JS under control

* flash

a-ah

* python

* hl (doesn't work)

* fix hl

* missed conflict

* fix test

* disable dynamic/anon test for cpp

* add keyValueIterator to cpp

* remove the test for keyValueIterator on Dynamic

Co-authored-by: Simon Krajewski <[email protected]>
Co-authored-by: Aleksandr Kuzmenko <[email protected]>
Co-authored-by: Aurel Bílý <[email protected]>
Guillaume Desquesnes 5 年之前
父节点
当前提交
80638bec2b

+ 1 - 1
src/generators/genhl.ml

@@ -3990,7 +3990,7 @@ let create_context com is_macro dump =
 let add_types ctx types =
 	List.iter (fun t ->
 		match t with
-		| TClassDecl ({ cl_path = ["hl";"types"], ("BytesIterator"|"ArrayBytes") } as c) ->
+		| TClassDecl ({ cl_path = ["hl";"types"], ("BytesIterator"|"BytesKeyValueIterator"|"ArrayBytes") } as c) ->
 			c.cl_extern <- true
 		| TClassDecl c ->
 			let rec loop p f =

+ 29 - 6
src/generators/genjs.ml

@@ -337,6 +337,10 @@ let rec needs_switch_break e =
 
 let this ctx = match ctx.in_value with None -> "this" | Some _ -> "$this"
 
+let is_iterator_field_access fa = match field_name fa with
+	| "iterator" | "keyValueIterator" -> true
+	| _ -> true
+
 let is_dynamic_iterator ctx e =
 	let check x =
 		let rec loop t = match follow t with
@@ -353,7 +357,7 @@ let is_dynamic_iterator ctx e =
 		has_feature ctx "haxe.iterators.ArrayIterator.*" && loop x.etype
 	in
 	match e.eexpr with
-	| TField (x,f) when field_name f = "iterator" -> check x
+	| TField (x,f) when is_iterator_field_access f -> check x
 	| _ ->
 		false
 
@@ -482,6 +486,11 @@ let rec gen_call ctx e el in_value =
 		print ctx "$getIterator(";
 		gen_value ctx x;
 		print ctx ")";
+	| TField (x,f), [] when field_name f = "keyValueIterator" && is_dynamic_iterator ctx e ->
+		add_feature ctx "use.$getKeyValueIterator";
+		print ctx "$getKeyValueIterator(";
+		gen_value ctx x;
+		print ctx ")";
 	| _ ->
 		gen_value ctx e;
 		spr ctx "(";
@@ -514,9 +523,9 @@ and gen_expr ctx e =
 		spr ctx "[";
 		gen_value ctx e2;
 		spr ctx "]";
-	| TBinop (op,{ eexpr = TField (x,f) },e2) when field_name f = "iterator" ->
+	| TBinop (op,{ eexpr = TField (x,f) },e2) when is_iterator_field_access f ->
 		gen_value ctx x;
-		spr ctx (field "iterator");
+		spr ctx (field (field_name f));
 		print ctx " %s " (Ast.s_binop op);
 		gen_value ctx e2;
 	| TBinop (op,e1,e2) ->
@@ -528,16 +537,21 @@ and gen_expr ctx e =
 		print ctx "$iterator(";
 		gen_value ctx x;
 		print ctx ")";
+	| TField (x,f) when field_name f = "keyValueIterator" && is_dynamic_iterator ctx e ->
+		add_feature ctx "use.$keyValueIterator";
+		print ctx "$keyValueIterator(";
+		gen_value ctx x;
+		print ctx ")";
 	(* Don't generate `$iterator(value)` for exprs like `value.iterator--` *)
-	| TUnop (op,flag,({eexpr = TField (x,f)} as fe)) when field_name f = "iterator" && is_dynamic_iterator ctx fe ->
+	| TUnop (op,flag,({eexpr = TField (x,f)} as fe)) when is_iterator_field_access f && is_dynamic_iterator ctx fe ->
 		(match flag with
 			| Prefix ->
 				spr ctx (Ast.s_unop op);
 				gen_value ctx x;
-				spr ctx ".iterator"
+				print ctx ".%s" (field_name f)
 			| Postfix ->
 				gen_value ctx x;
-				spr ctx ".iterator";
+				print ctx ".%s" (field_name f);
 				spr ctx (Ast.s_unop op))
 	| TField (x,FClosure (Some ({cl_path=[],"Array"},_), {cf_name="push"})) ->
 		(* see https://github.com/HaxeFoundation/haxe/issues/1997 *)
@@ -1835,11 +1849,20 @@ let generate com =
 		print ctx "function $iterator(o) { if( o instanceof Array ) return function() { return new %s(o); }; return typeof(o.iterator) == 'function' ? $bind(o,o.iterator) : o.iterator; }" array_iterator;
 		newline ctx;
 	end;
+	if has_feature ctx "use.$keyValueIterator" then begin
+		add_feature ctx "use.$bind";
+		print ctx "function $keyValueIterator(o) { if( o instanceof Array ) return function() { return HxOverrides.keyValueIter(o); }; return typeof(o.keyValueIterator) == 'function' ? $bind(o,o.keyValueIterator) : o.keyValueIterator; }";
+		newline ctx;
+	end;
 	if has_feature ctx "use.$getIterator" then begin
 		let array_iterator = s_path ctx (["haxe"; "iterators"], "ArrayIterator") in
 		print ctx "function $getIterator(o) { if( o instanceof Array ) return new %s(o); else return o.iterator(); }" array_iterator;
 		newline ctx;
 	end;
+	if has_feature ctx "use.$getKeyValueIterator" then begin
+		print ctx "function $getKeyValueIterator(o) { if( o instanceof Array ) return HxOverrides.keyValueIter(o); else return o.keyValueIterator(); }";
+		newline ctx;
+	end;
 	if has_feature ctx "use.$bind" then begin
 		add_feature ctx "$global.$haxeUID";
 		if not has_dollar_underscore then begin

+ 2 - 2
src/generators/genpy.ml

@@ -1436,7 +1436,7 @@ module Printer = struct
 		in
 		let call_override s =
 			match s with
-			| "iterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift" | "join" | "push" | "map" | "filter" -> true
+			| "iterator" | "keyValueIterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift" | "join" | "push" | "map" | "filter" -> true
 			| _ -> false
 		in
 		match fa with
@@ -1602,7 +1602,7 @@ module Printer = struct
 					"print(str(" ^ (print_expr pctx e) ^ "))"
 			| TField(e1,((FAnon {cf_name = (("split" | "join" | "push" | "map" | "filter") as s)}) | FDynamic (("split" | "join" | "push" | "map" | "filter") as s))), [x] ->
 				Printf.sprintf "HxOverrides.%s(%s, %s)" s (print_expr pctx e1) (print_expr pctx x)
-			| TField(e1,((FAnon {cf_name = (("iterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift") as s)}) | FDynamic (("iterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift") as s))), [] ->
+			| TField(e1,((FAnon {cf_name = (("iterator" | "keyValueIterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift") as s)}) | FDynamic (("iterator" | "keyValueIterator" | "toUpperCase" | "toLowerCase" | "pop" | "shift") as s))), [] ->
 				Printf.sprintf "HxOverrides.%s(%s)" s (print_expr pctx e1)
 			| TField(_, (FStatic({cl_path = ["python"; "_KwArgs"], "KwArgs_Impl_"},{ cf_name="fromT" }))), [e2]  ->
 				let t = match follow call_expr.etype with

+ 2 - 1
src/generators/genswf9.ml

@@ -335,7 +335,8 @@ let property ctx fa t =
 		(match p with
 		| "length" -> ident p, Some KInt, false (* UInt in the spec *)
 		| "map" | "filter" when Common.defined ctx.com Define.NoFlashOverride -> ident (p ^ "HX"), None, true
-		| "copy" | "insert" | "contains" | "remove" | "iterator" | "toString" | "map" | "filter" | "resize" -> ident p , None, true
+		| "copy" | "insert" | "contains" | "remove" | "iterator" | "keyValueIterator"
+		| "toString" | "map" | "filter" | "resize" -> ident p , None, true
 		| _ -> as3 p, None, false);
 	| TInst ({ cl_path = ["flash"],"Vector" },_) ->
 		(match p with

+ 1 - 0
src/macro/eval/evalHash.ml

@@ -125,6 +125,7 @@ let key_haxe_zip_Compress = hash "haxe.zip.Compress"
 let key_haxe_zip_Uncompress = hash "haxe.zip.Uncompress"
 let key_done = hash "done"
 let key_eval_toplevel = hash "eval-toplevel"
+let key_haxe_iterators_array_key_value_iterator = hash "haxe.iterators.ArrayKeyValueIterator"
 let key_haxe_iterators_map_key_value_iterator = hash "haxe.iterators.MapKeyValueIterator"
 let key_sys_net_Mutex = hash "sys.thread.Mutex"
 let key_sys_net_Lock = hash "sys.thread.Lock"

+ 10 - 0
src/macro/eval/evalStdLib.ml

@@ -152,6 +152,15 @@ module StdArray = struct
 		vstring s
 	)
 
+	let keyValueIterator = vifun0 (fun vthis ->
+		let ctx = get_ctx() in
+		let path = key_haxe_iterators_array_key_value_iterator in
+		let vit = encode_instance path in
+		let fnew = get_instance_constructor ctx path null_pos in
+		ignore(call_value_on vit (Lazy.force fnew) [vthis]);
+		vit
+	)
+
 	let lastIndexOf = vifun2 (fun vthis x fromIndex ->
 		let this = this vthis in
 		let last = this.alength - 1 in
@@ -3262,6 +3271,7 @@ let init_standard_library builtins =
 		"insert",StdArray.insert;
 		"iterator",StdArray.iterator;
 		"join",StdArray.join;
+		"keyValueIterator",StdArray.keyValueIterator;
 		"lastIndexOf",StdArray.lastIndexOf;
 		"map",StdArray.map;
 		"pop",StdArray.pop;

+ 3 - 0
src/optimization/dce.ml

@@ -443,6 +443,9 @@ and expr_field dce e fa is_call_expr =
 			| FDynamic _ ->
 				check_and_add_feature dce "dynamic_read";
 				check_and_add_feature dce ("dynamic_read." ^ n);
+			| FClosure _ ->
+				check_and_add_feature dce "closure_read";
+				check_and_add_feature dce ("closure_read." ^ n);
 			| _ -> ());
 			begin match follow e.etype, fa with
 				| TInst(c,_), _

+ 10 - 0
std/Array.hx

@@ -27,6 +27,9 @@
 	@see https://haxe.org/manual/std-Array.html
 	@see https://haxe.org/manual/lf-array-comprehension.html
 **/
+
+import haxe.iterators.ArrayKeyValueIterator;
+
 extern class Array<T> {
 	/**
 		The length of `this` Array.
@@ -278,6 +281,13 @@ extern class Array<T> {
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	/**
+		Returns an iterator of the Array indices and values.
+	**/
+	@:pure @:runtime public inline function keyValueIterator() : ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	/**
 		Creates a new Array by applying function `f` to all elements of `this`.
 

+ 4 - 0
std/cpp/VirtualArray.hx

@@ -44,6 +44,7 @@ package cpp;
 	function lastIndexOf(x:Dynamic, ?fromIndex:Int):Int;
 	function copy():VirtualArray;
 	function iterator():Iterator<Dynamic>;
+	function keyValueIterator():KeyValueIterator<Int, Dynamic>;
 	function map<S>(f:Dynamic->S):VirtualArray;
 	function filter(f:Dynamic->Bool):VirtualArray;
 	function resize(len:Int):Void;
@@ -121,6 +122,9 @@ abstract VirtualArray(NativeVirtualArray) {
 	extern inline public function iterator():Iterator<Dynamic>
 		return this.iterator();
 
+	extern inline public function keyValueIterator():KeyValueIterator<Int, Dynamic>
+		return this.keyValueIterator();
+
 	extern inline public function map<S>(f:Dynamic->S):VirtualArray
 		return this.map(f);
 

+ 6 - 0
std/cs/_std/Array.hx

@@ -21,6 +21,7 @@
  */
 
 import cs.NativeArray;
+import haxe.iterators.ArrayKeyValueIterator;
 
 #if core_api_serialize
 @:meta(System.Serializable)
@@ -430,6 +431,11 @@ final class Array<T> implements ArrayAccess<T> {
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	public inline function keyValueIterator() : ArrayKeyValueIterator<T>
+	{
+		return new ArrayKeyValueIterator(this);
+	}
+
 	public function resize(len:Int):Void {
 		if (length < len) {
 			if (__a.length < len) {

+ 4 - 0
std/flash/Boot.hx

@@ -316,6 +316,9 @@ class Boot extends flash.display.MovieClip {
 			aproto.iterator = function() {
 				return new haxe.iterators.ArrayIterator(cast __this__);
 			};
+			aproto.keyValueIterator = function() {
+				return new haxe.iterators.ArrayKeyValueIterator(untyped __this__);
+			};
 			aproto.resize = function(len) {
 				__this__.length = len;
 			};
@@ -324,6 +327,7 @@ class Boot extends flash.display.MovieClip {
 			aproto.setPropertyIsEnumerable("contains", false);
 			aproto.setPropertyIsEnumerable("remove", false);
 			aproto.setPropertyIsEnumerable("iterator", false);
+			aproto.setPropertyIsEnumerable("keyValueIterator", false);
 			aproto.setPropertyIsEnumerable("resize", false);
 			#if no_flash_override
 			aproto.filterHX = function(f) {

+ 44 - 0
std/haxe/iterators/ArrayKeyValueIterator.hx

@@ -0,0 +1,44 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.iterators;
+
+@:ifFeature("anon_read.keyValueIterator", "dynamic_read.keyValueIterator")
+class ArrayKeyValueIterator<T> {
+	var current:Int = 0;
+	var array:Array<T>;
+
+	#if !hl inline #end
+	public function new(array:Array<T>) {
+		this.array = array;
+	}
+
+	#if !hl inline #end
+	public function hasNext():Bool {
+		return current < array.length;
+	}
+
+	#if !hl inline #end
+	public function next():{key:Int,value:T} {
+		return {value:array[current], key:current++};
+	}
+}

+ 18 - 0
std/hl/NativeArray.hx

@@ -42,6 +42,24 @@ package hl;
 	}
 }
 
+@:generic class NativeArrayKeyValueIterator<T> {
+	var arr : NativeArray<T>;
+	var pos : Int;
+	var length : Int;
+ 	public inline function new(arr:NativeArray<T>) {
+		this.arr = arr;
+		pos = 0;
+		length = arr.length;
+	}
+ 	public inline function hasNext() {
+		return pos < length;
+	}
+ 	public inline function next() {
+		var v = arr[pos];
+		return {key:pos++, value:v};
+	}
+}
+
 @:coreType abstract NativeArray<T> {
 	public var length(get, never):Int;
 

+ 25 - 0
std/hl/types/ArrayBytes.hx

@@ -23,6 +23,7 @@
 package hl.types;
 
 import haxe.iterators.ArrayIterator;
+import haxe.iterators.ArrayKeyValueIterator;
 
 @:keep
 @:generic
@@ -43,6 +44,26 @@ class BytesIterator<T> extends ArrayIterator<T> {
 	}
 }
 
+@:keep
+@:generic
+class BytesKeyValueIterator<T> extends ArrayKeyValueIterator<T> {
+	var a : ArrayBytes<T>;
+
+	public function new(a) {
+		super((null:Dynamic));
+		this.a = a;
+	}
+
+	override public function hasNext():Bool {
+		return current < a.length;
+	}
+
+	override public function next():{key:Int, value:T} {
+		var v = @:privateAccess a.bytes.get(current);
+		return {key:current++, value:v};
+	}
+}
+
 @:keep
 @:generic class ArrayBytes<T> extends ArrayBase {
 	var bytes:hl.BytesAccess<T>;
@@ -263,6 +284,10 @@ class BytesIterator<T> extends ArrayIterator<T> {
 		return new BytesIterator(this);
 	}
 
+	public function keyValueIterator() : ArrayKeyValueIterator<T> {
+		return new BytesKeyValueIterator<T>(this);
+	}
+
 	public function map<S>(f:T->S):ArrayDyn@:privateAccess {
 		var a = new ArrayObj();
 		if (length > 0)

+ 23 - 0
std/hl/types/ArrayDyn.hx

@@ -24,6 +24,7 @@ package hl.types;
 
 import hl.types.ArrayBase;
 import haxe.iterators.ArrayIterator;
+import haxe.iterators.ArrayKeyValueIterator;
 
 class ArrayDynIterator extends ArrayIterator<Dynamic> {
 	var a:ArrayBase;
@@ -42,6 +43,24 @@ class ArrayDynIterator extends ArrayIterator<Dynamic> {
 	}
 }
 
+class ArrayDynKeyValueIterator extends ArrayKeyValueIterator<Dynamic> {
+	var a : ArrayBase;
+
+	public function new(a) {
+		super((null:Dynamic));
+		this.a = a;
+	}
+
+	override public function hasNext() {
+		return current < a.length;
+	}
+
+	override public function next() {
+		var v = a.getDyn(current);
+		return {key:current++, value:v};
+	}
+}
+
 @:keep
 class ArrayDyn extends ArrayAccess {
 	public var length(get, never):Int;
@@ -175,6 +194,10 @@ class ArrayDyn extends ArrayAccess {
 		return new ArrayDynIterator(array);
 	}
 
+	public function keyValueIterator() : ArrayKeyValueIterator<Dynamic> {
+		return new ArrayDynKeyValueIterator(array);
+	}
+
 	public function map(f:Dynamic->Dynamic):ArrayDyn {
 		var a = new NativeArray<Dynamic>(length);
 		for (i in 0...length)

+ 25 - 2
std/hl/types/ArrayObj.hx

@@ -23,6 +23,7 @@
 package hl.types;
 
 import haxe.iterators.ArrayIterator;
+import haxe.iterators.ArrayKeyValueIterator;
 
 class ArrayObjIterator<T> extends ArrayIterator<T> {
 	var arr:ArrayObj<T>;
@@ -32,15 +33,33 @@ class ArrayObjIterator<T> extends ArrayIterator<T> {
 		this.arr = arr;
 	}
 
-	override public function hasNext() {
+	override public function hasNext():Bool {
 		return current < arr.length;
 	}
 
-	override public function next() {
+	override public function next():T {
 		return @:privateAccess arr.array[current++];
 	}
 }
 
+class ArrayObjKeyValueIterator<T> extends ArrayKeyValueIterator<T> {
+	var arr:ArrayObj<T>;
+
+	public inline function new(arr:ArrayObj<T>) {
+		super((null:Dynamic));
+		this.arr = arr;
+	}
+
+	override public function hasNext():Bool {
+		return current < arr.length;
+	}
+
+	override public function next():{key:Int, value:T} {
+		var v = @:privateAccess arr.array[current];
+		return {key:current++, value:v};
+	}
+}
+
 @:keep
 class ArrayObj<T> extends ArrayBase {
 	var array:hl.NativeArray<Dynamic>;
@@ -271,6 +290,10 @@ class ArrayObj<T> extends ArrayBase {
 		return new ArrayObjIterator(this);
 	}
 
+	public function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayObjKeyValueIterator<T>(this);
+	}
+
 	public function map<S>(f:T->S):ArrayDyn {
 		var a = new ArrayObj();
 		if (length > 0)

+ 5 - 0
std/java/_std/Array.hx

@@ -22,6 +22,7 @@
 
 import java.lang.System;
 import java.NativeArray;
+import haxe.iterators.ArrayKeyValueIterator;
 
 @:classCode('
 	public Array(T[] _native)
@@ -429,6 +430,10 @@ import java.NativeArray;
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	public inline function keyValueIterator() : ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	public function resize(len:Int):Void {
 		if (length < len) {
 			if (__a.length < len) {

+ 7 - 0
std/js/_std/Array.hx

@@ -19,6 +19,9 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+
+import haxe.iterators.ArrayKeyValueIterator;
+
 @:coreApi
 extern class Array<T> {
 	var length(default, null):Int;
@@ -86,6 +89,10 @@ extern class Array<T> {
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	@:runtime inline function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	inline function resize(len:Int):Void {
 		this.length = len;
 	}

+ 5 - 0
std/js/_std/HxOverrides.hx

@@ -138,6 +138,11 @@ class HxOverrides {
 			};
 		}
 
+	@:ifFeature("anon_read.keyValueIterator", "dynamic_read.keyValueIterator", "closure_read.keyValueIterator")
+	static function keyValueIter<T>( a : Array<T> ) {
+		return new haxe.iterators.ArrayKeyValueIterator(a);
+	}
+
 	@:pure
 	static function now(): Float return js.lib.Date.now();
 

+ 7 - 0
std/lua/_std/Array.hx

@@ -19,6 +19,9 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+
+import haxe.iterators.ArrayKeyValueIterator;
+
 @:coreApi
 class Array<T> {
 	public var length(default, null):Int;
@@ -247,6 +250,10 @@ class Array<T> {
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	public inline function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	public function resize(len:Int):Void {
 		if (length < len) {
 			this.length = len;

+ 7 - 0
std/neko/_std/Array.hx

@@ -19,6 +19,9 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+
+import haxe.iterators.ArrayKeyValueIterator;
+
 @:coreApi final class Array<T> {
 	private var __a:neko.NativeArray<T>;
 
@@ -55,6 +58,10 @@
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	public inline function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	public function insert(pos:Int, x:T):Void {
 		var l = this.length;
 		if (pos < 0) {

+ 7 - 0
std/php/_std/Array.hx

@@ -23,6 +23,8 @@
 import php.*;
 import php.ArrayIterator as NativeArrayIterator;
 
+import haxe.iterators.ArrayKeyValueIterator;
+
 using php.Global;
 
 @:coreApi
@@ -93,6 +95,11 @@ final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	@:keep
+	public inline function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	public function join(sep:String):String {
 		return Global.implode(sep, Global.array_map(Syntax.nativeClassName(Boot) + '::stringify', arr));
 	}

+ 2 - 0
std/python/Boot.hx

@@ -333,6 +333,8 @@ class Boot {
 					createClosure(o, ArrayImpl.copy);
 				case "iterator":
 					createClosure(o, ArrayImpl.iterator);
+				case "keyValueIterator":
+					createClosure(o, ArrayImpl.keyValueIterator);
 				case "insert":
 					createClosure(o, ArrayImpl.insert);
 				case "join":

+ 5 - 0
std/python/_std/Array.hx

@@ -24,6 +24,7 @@
 import python.internal.ArrayImpl;
 import python.NativeIterator;
 #end
+import haxe.iterators.ArrayKeyValueIterator;
 
 @:native("list")
 @:coreApi
@@ -44,6 +45,10 @@ extern class Array<T> implements ArrayAccess<T> {
 		return new haxe.iterators.ArrayIterator(this);
 	}
 
+	@:runtime inline public function keyValueIterator():ArrayKeyValueIterator<T> {
+		return new ArrayKeyValueIterator(this);
+	}
+
 	inline function insert(pos:Int, x:T):Void {
 		ArrayImpl.insert(this, pos, x);
 	}

+ 5 - 0
std/python/internal/ArrayImpl.hx

@@ -45,6 +45,11 @@ class ArrayImpl {
 		return new HaxeIterator(Syntax.callField(x, "__iter__"));
 	}
 
+	@:ifFeature("dynamic_read.keyValueIterator", "anon_optional_read.keyValueIterator", "python.internal.ArrayImpl.keyValueIterator")
+	public static inline function keyValueIterator<T>(x:Array<T>) : KeyValueIterator<Int, T> {
+		return new haxe.iterators.ArrayKeyValueIterator(x);
+	}
+
 	@:ifFeature("dynamic_read.indexOf", "anon_optional_read.indexOf", "python.internal.ArrayImpl.indexOf")
 	public static function indexOf<T>(a:Array<T>, x:T, ?fromIndex:Int):Int {
 		var len = a.length;

+ 8 - 0
std/python/internal/HxOverrides.hx

@@ -40,6 +40,14 @@ class HxOverrides {
 		return Syntax.callField(x, "iterator");
 	}
 
+	@:ifFeature("dynamic_read.keyValueIterator", "anon_optional_read.keyValueIterator", "anon_read.keyValueIterator")
+	static public function keyValueIterator(x) {
+		if (Boot.isArray(x)) {
+			return (x:Array<Dynamic>).keyValueIterator();
+		}
+		return Syntax.callField(x, "keyValueIterator");
+	}
+
 	@:ifFeature("dynamic_binop_==", "dynamic_binop_!=")
 	static function eq(a:Dynamic, b:Dynamic):Bool {
 		if (Boot.isArray(a) || Boot.isArray(b)) {

+ 43 - 0
tests/unit/src/unitstd/Array.unit.hx

@@ -307,3 +307,46 @@ a[2] != 3;
 a.resize(0);
 a.length == 0;
 a == [];
+
+// keyValueIterator
+var a : Array<Int> = [1,2,3,5,8];
+[for (k=>v in a) k] == [0,1,2,3,4];
+[for (k=>v in a) v] == [1,2,3,5,8];
+[for (k=>v in a) k*v] == [0,2,6,15,32];
+
+// keyValueIterator through Structure
+var a : Array<Int> = [1,2,3,5,8];
+var it : KeyValueIterator<Int, Int> = a.keyValueIterator();
+var a2 = [for (k=>v in it) k];
+a2 == [0,1,2,3,4];
+var it : KeyValueIterator<Int, Int> = a.keyValueIterator();
+a2 = [for (k=>v in it) v];
+a2 == [1,2,3,5,8];
+var it : KeyValueIterator<Int, Int> = a.keyValueIterator();
+a2 = [for (k=>v in it) k*v];
+a2 == [0,2,6,15,32];
+
+// keyValueIterator through Structure
+var a : Array<Int> = [1,2,3,5,8];
+var it : KeyValueIterable<Int, Int> = a;
+[for (k=>v in it) k] == [0,1,2,3,4];
+[for (k=>v in it) v] == [1,2,3,5,8];
+[for (k=>v in it) k*v] == [0,2,6,15,32];
+
+#if !flash
+// Can't create this closure on Flash apparently
+// keyValueIterator closure because why not
+var a : Array<Int> = [1,2,3,5,8];
+var itf : Void -> KeyValueIterator<Int, Int> = a.keyValueIterator;
+var it = itf();
+var a2 = [for (k=>v in it) k];
+a2 == [0,1,2,3,4];
+var itf : Void -> KeyValueIterator<Int, Int> = a.keyValueIterator;
+var it = itf();
+a2 = [for (k=>v in it) v];
+a2 == [1,2,3,5,8];
+var itf : Void -> KeyValueIterator<Int, Int> = a.keyValueIterator;
+var it = itf();
+a2 = [for (k=>v in it) k*v];
+a2 == [0,2,6,15,32];
+#end