Forráskód Böngészése

Map key/value Iterators (#7516)

* Add IntMap key/value iterator

* IntMap order is undefined, use sorting

* Formatting

* Rename IntMapKeyValueIterator to MapKeyValueIterator

 * If this works, it can be used for all map types

* Add keyValueIterator() to other maps

* use haxe.Constraints.IMap instead of Map

 * To make it work for BalancedTree/HashMap

* ignore HashMap

* Add BalancedTree keyValueIterator unit test

* missing semicolon

* fix test + fix ObjectMap for eval

* Fix StringMap for eval / add StringMap unit test / Add missing ObjectMap for js

* Update StringMap.unit.hx

"Solve" as3 unit test

* Add keyValueIterator for UnsafeStringMap

* Unit test map iterators though dynamic

* add more tests

* Add @:runtime to all implementations

 * "you need `@:runtime` if the Map class is `extern`"

* Moved some tests to own unit test

* [eval] support keyValueIterator on maps

* fence undefined behavior
Mark Knol 6 éve
szülő
commit
b94c54c055
48 módosított fájl, 539 hozzáadás és 25 törlés
  1. 2 1
      src/macro/eval/evalHash.ml
  2. 17 0
      src/macro/eval/evalStdLib.ml
  3. 5 1
      std/cpp/_std/haxe/ds/IntMap.hx
  4. 5 1
      std/cpp/_std/haxe/ds/ObjectMap.hx
  5. 5 1
      std/cpp/_std/haxe/ds/StringMap.hx
  6. 5 1
      std/cpp/_std/haxe/ds/WeakMap.hx
  7. 4 0
      std/cs/_std/haxe/ds/IntMap.hx
  8. 4 0
      std/cs/_std/haxe/ds/ObjectMap.hx
  9. 4 0
      std/cs/_std/haxe/ds/StringMap.hx
  10. 5 1
      std/flash/_std/haxe/ds/IntMap.hx
  11. 5 1
      std/flash/_std/haxe/ds/ObjectMap.hx
  12. 5 1
      std/flash/_std/haxe/ds/StringMap.hx
  13. 5 1
      std/flash/_std/haxe/ds/UnsafeStringMap.hx
  14. 5 1
      std/flash/_std/haxe/ds/WeakMap.hx
  15. 2 1
      std/haxe/Constraints.hx
  16. 7 0
      std/haxe/ds/BalancedTree.hx
  17. 11 0
      std/haxe/ds/IntMap.hx
  18. 9 0
      std/haxe/ds/Map.hx
  19. 11 1
      std/haxe/ds/ObjectMap.hx
  20. 11 0
      std/haxe/ds/StringMap.hx
  21. 8 1
      std/haxe/ds/WeakMap.hx
  22. 53 0
      std/haxe/iterators/MapKeyValueIterator.hx
  23. 5 1
      std/hl/_std/haxe/ds/IntMap.hx
  24. 5 1
      std/hl/_std/haxe/ds/ObjectMap.hx
  25. 5 1
      std/hl/_std/haxe/ds/StringMap.hx
  26. 5 0
      std/java/_std/haxe/ds/IntMap.hx
  27. 3 0
      std/java/_std/haxe/ds/ObjectMap.hx
  28. 4 0
      std/java/_std/haxe/ds/StringMap.hx
  29. 6 0
      std/java/_std/haxe/ds/WeakMap.hx
  30. 4 0
      std/js/_std/haxe/ds/IntMap.hx
  31. 4 0
      std/js/_std/haxe/ds/ObjectMap.hx
  32. 4 0
      std/js/_std/haxe/ds/StringMap.hx
  33. 4 0
      std/lua/_std/haxe/ds/IntMap.hx
  34. 5 1
      std/lua/_std/haxe/ds/ObjectMap.hx
  35. 5 1
      std/lua/_std/haxe/ds/StringMap.hx
  36. 5 1
      std/neko/_std/haxe/ds/IntMap.hx
  37. 5 1
      std/neko/_std/haxe/ds/ObjectMap.hx
  38. 5 1
      std/neko/_std/haxe/ds/StringMap.hx
  39. 7 0
      std/php/_std/haxe/ds/IntMap.hx
  40. 4 0
      std/php/_std/haxe/ds/ObjectMap.hx
  41. 4 0
      std/php/_std/haxe/ds/StringMap.hx
  42. 5 1
      std/python/_std/haxe/ds/IntMap.hx
  43. 5 1
      std/python/_std/haxe/ds/ObjectMap.hx
  44. 5 1
      std/python/_std/haxe/ds/StringMap.hx
  45. 99 1
      tests/unit/src/unitstd/Map.unit.hx
  46. 10 0
      tests/unit/src/unitstd/haxe/ds/BalancedTree.unit.hx
  47. 71 0
      tests/unit/src/unitstd/haxe/ds/IntMap.unit.hx
  48. 67 0
      tests/unit/src/unitstd/haxe/ds/StringMap.unit.hx

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

@@ -130,4 +130,5 @@ let key_eval_vm_Thread = hash "eval.vm.Thread"
 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_eval_toplevel = hash "eval-toplevel"
+let key_haxe_iterators_map_key_value_iterator = hash "haxe.iterators.MapKeyValueIterator"

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

@@ -1381,6 +1381,14 @@ let encode_list_iterator l =
 		)
 	]
 
+let map_key_value_iterator path = vifun0 (fun vthis ->
+	let ctx = get_ctx() 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
+)
+
 module StdIntMap = struct
 	let this vthis = match vthis with
 		| VInstance {ikind = IIntMap h} -> h
@@ -1410,6 +1418,8 @@ module StdIntMap = struct
 		encode_list_iterator keys
 	)
 
+	let keyValueIterator = map_key_value_iterator key_haxe_iterators_map_key_value_iterator
+
 	let remove = vifun1 (fun vthis vkey ->
 		let this = this vthis in
 		let key = decode_int vkey in
@@ -1462,6 +1472,8 @@ module StdStringMap = struct
 		encode_list_iterator keys
 	)
 
+	let keyValueIterator = map_key_value_iterator key_haxe_iterators_map_key_value_iterator
+
 	let remove = vifun1 (fun vthis vkey ->
 		let this = this vthis in
 		let key = decode_vstring vkey in
@@ -1514,6 +1526,8 @@ module StdObjectMap = struct
 		encode_list_iterator keys
 	)
 
+	let keyValueIterator = map_key_value_iterator key_haxe_iterators_map_key_value_iterator
+
 	let remove = vifun1 (fun vthis vkey ->
 		let this = this vthis in
 		let b = ValueHashtbl.mem this vkey in
@@ -2854,6 +2868,7 @@ let init_maps builtins =
 		"get",StdIntMap.get;
 		"iterator",StdIntMap.iterator;
 		"keys",StdIntMap.keys;
+		"keyValueIterator",StdIntMap.keyValueIterator;
 		"remove",StdIntMap.remove;
 		"set",StdIntMap.set;
 		"toString",StdIntMap.toString;
@@ -2864,6 +2879,7 @@ let init_maps builtins =
 		"get",StdObjectMap.get;
 		"iterator",StdObjectMap.iterator;
 		"keys",StdObjectMap.keys;
+		"keyValueIterator",StdObjectMap.keyValueIterator;
 		"remove",StdObjectMap.remove;
 		"set",StdObjectMap.set;
 		"toString",StdObjectMap.toString;
@@ -2874,6 +2890,7 @@ let init_maps builtins =
 		"get",StdStringMap.get;
 		"iterator",StdStringMap.iterator;
 		"keys",StdStringMap.keys;
+		"keyValueIterator",StdStringMap.keyValueIterator;
 		"remove",StdStringMap.remove;
 		"set",StdStringMap.set;
 		"toString",StdStringMap.toString;

+ 5 - 1
std/cpp/_std/haxe/ds/IntMap.hx

@@ -82,7 +82,11 @@ package haxe.ds;
 		var a:Array<Dynamic> = untyped __global__.__int_hash_values(h);
 		return a.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/cpp/_std/haxe/ds/ObjectMap.hx

@@ -81,7 +81,11 @@ class ObjectMap<K:{},V> implements haxe.Constraints.IMap<K,V> {
 		var a:Array<Dynamic> = untyped __global__.__object_hash_values(h);
 		return a.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/cpp/_std/haxe/ds/StringMap.hx

@@ -81,7 +81,11 @@ package haxe.ds;
 		var a:Array<Dynamic> = untyped __global__.__string_hash_values(h);
 		return a.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/cpp/_std/haxe/ds/WeakMap.hx

@@ -74,7 +74,11 @@ class WeakMap<K:{},V> implements haxe.Constraints.IMap<K,V> {
 		var a:Array<Dynamic> = untyped __global__.__object_hash_values(h);
 		return a.iterator();
 	}
-	
+
+	public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : WeakMap<K,V> {
 		var copied = new WeakMap();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/cs/_std/haxe/ds/IntMap.hx

@@ -396,6 +396,10 @@ import cs.NativeArray;
 		return new IntMapValueIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap<T>();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/cs/_std/haxe/ds/ObjectMap.hx

@@ -395,6 +395,10 @@ import cs.NativeArray;
 		return new ObjectMapValueIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap<K, V>();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/cs/_std/haxe/ds/StringMap.hx

@@ -399,6 +399,10 @@ import cs.NativeArray;
 		return new StringMapValueIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap<T>();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/flash/_std/haxe/ds/IntMap.hx

@@ -75,7 +75,11 @@ package haxe.ds;
 	}
 
 	#end
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/flash/_std/haxe/ds/ObjectMap.hx

@@ -69,7 +69,11 @@ class ObjectMap<K:{},V> extends flash.utils.Dictionary implements haxe.Constrain
 	}
 
 	#end
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/flash/_std/haxe/ds/StringMap.hx

@@ -111,7 +111,11 @@ package haxe.ds;
 	}
 
 	#end
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/flash/_std/haxe/ds/UnsafeStringMap.hx

@@ -80,7 +80,11 @@ class UnsafeStringMap<T> implements haxe.Constraints.IMap<String,T> {
 	}
 
 	#end
-	
+
+	public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : UnsafeStringMap<T> {
 		var copied = new UnsafeStringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/flash/_std/haxe/ds/WeakMap.hx

@@ -69,7 +69,11 @@ class WeakMap<K:{},V> extends flash.utils.Dictionary implements haxe.Constraints
 	}
 
 	#end
-	
+
+	public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : WeakMap<K,V> {
 		var copied = new WeakMap();
 		for(key in keys()) copied.set(key, get(key));

+ 2 - 1
std/haxe/Constraints.hx

@@ -63,6 +63,7 @@ interface IMap<K,V> {
 	public function remove(k:K):Bool;
 	public function keys():Iterator<K>;
 	public function iterator():Iterator<V>;
+	public function keyValueIterator():KeyValueIterator<K, V>;
 	public function copy():IMap<K,V>;
 	public function toString():String;
-}
+}

+ 7 - 0
std/haxe/ds/BalancedTree.hx

@@ -118,6 +118,13 @@ class BalancedTree<K,V> implements haxe.Constraints.IMap<K,V> {
 		return ret.iterator();
 	}
 
+	/**
+		See `Map.keyValueIterator`
+	**/
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	/**
 		Iterates over the keys of `this` BalancedTree.
 

+ 11 - 0
std/haxe/ds/IntMap.hx

@@ -65,6 +65,17 @@ extern class IntMap<T> implements haxe.Constraints.IMap<Int,T> {
 	**/
 	public function iterator() : Iterator<T>;
 
+	/**
+		See `Map.keyValueIterator`
+	**/
+#if eval
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+#else
+	public function keyValueIterator() : KeyValueIterator<Int, T>;
+#end
+
 	/**
 		See `Map.copy`
 	**/

+ 9 - 0
std/haxe/ds/Map.hx

@@ -122,6 +122,15 @@ abstract Map<K,V>(IMap<K,V> ) {
 	**/
 	public inline function iterator():Iterator<V> {
 		return this.iterator();
+	}	
+
+	/**
+		Returns an Iterator over the keys and values of `this` Map.
+
+		The order of values is undefined.
+	**/
+	public inline function keyValueIterator():KeyValueIterator<K, V> {
+		return this.keyValueIterator();
 	}
 
 	/**

+ 11 - 1
std/haxe/ds/ObjectMap.hx

@@ -68,7 +68,17 @@ extern class ObjectMap < K: { }, V > implements haxe.Constraints.IMap<K,V> {
 		See `Map.iterator`
 	**/
 	public function iterator():Iterator<V>;
-	
+
+	/**
+		See `Map.keyValueIterator`
+	**/
+#if eval
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+#else
+	public function keyValueIterator() : KeyValueIterator<K, V>;
+#end
 	/**
 		See `Map.copy`
 	**/

+ 11 - 0
std/haxe/ds/StringMap.hx

@@ -66,6 +66,17 @@ extern class StringMap<T> implements haxe.Constraints.IMap<String,T> {
 	**/
 	public function iterator() : Iterator<T>;
 
+	/**
+		See `Map.keyValueIterator`
+	**/
+#if eval
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+#else
+	public function keyValueIterator() : KeyValueIterator<String, T>;
+#end
+
 	/**
 		See `Map.copy`
 	**/

+ 8 - 1
std/haxe/ds/WeakMap.hx

@@ -80,7 +80,14 @@ class WeakMap<K: { },V> implements haxe.Constraints.IMap<K,V> {
 	public function iterator():Iterator<V> {
 		return null;
 	}
-	
+
+	/**
+		See `Map.keyValueIterator`
+	**/
+	public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return null;
+	}
+
 	/**
 		See `Map.copy`
 	**/

+ 53 - 0
std/haxe/iterators/MapKeyValueIterator.hx

@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import haxe.ds.IntMap;
+
+/**
+	This Key/Value iterator can be used to iterate across maps.
+**/
+@:ifFeature("anon_read.keyValueIterator", "dynamic_read.keyValueIterator")
+class MapKeyValueIterator<K, V> {
+	var map:haxe.Constraints.IMap<K, V>;
+	var keys:Iterator<K>;
+
+ 	public inline function new(map:haxe.Constraints.IMap<K, V>) {
+		this.map = map;
+		this.keys = map.keys();
+	}
+
+	/**
+		See `Iterator.hasNext`
+	**/
+ 	public inline function hasNext():Bool {
+		return keys.hasNext();
+	}
+
+	/**
+		See `Iterator.next`
+	**/
+ 	public inline function next():{key:K,value:V} {
+		var key = keys.next();
+		return {value: map.get(key), key:key};
+	}
+}

+ 5 - 1
std/hl/_std/haxe/ds/IntMap.hx

@@ -54,7 +54,11 @@ class IntMap<T> implements haxe.Constraints.IMap<Int,T> {
 	public function iterator() : Iterator<T> {
 		return h.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/hl/_std/haxe/ds/ObjectMap.hx

@@ -54,7 +54,11 @@ class ObjectMap<K:{},T> implements haxe.Constraints.IMap<K,T> {
 	public function iterator() : Iterator<T> {
 		return h.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,T> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/hl/_std/haxe/ds/StringMap.hx

@@ -79,7 +79,11 @@ class StringMap<T> implements haxe.Constraints.IMap<String,T> {
 	public function iterator() : Iterator<T> {
 		return h.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 0
std/java/_std/haxe/ds/IntMap.hx

@@ -397,6 +397,11 @@ import java.NativeArray;
 		return new IntMapValueIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> 
+	{
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 3 - 0
std/java/_std/haxe/ds/ObjectMap.hx

@@ -395,6 +395,9 @@ import java.NativeArray;
 		return new ObjectMapValueIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
 
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap();

+ 4 - 0
std/java/_std/haxe/ds/StringMap.hx

@@ -390,6 +390,10 @@ import java.NativeArray;
 		return new StringMapKeyIterator(this);
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	/**
 		Returns an iterator of all values in the hashtable.
 		Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration

+ 6 - 0
std/java/_std/haxe/ds/WeakMap.hx

@@ -429,6 +429,12 @@ import java.lang.ref.ReferenceQueue;
 		return new WeakMapValueIterator(this);
 	}
 
+	/**
+		See `Map.keyValueIterator`
+	**/
+	public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
 
 	public function copy() : WeakMap<K,V> {
 		var copied = new WeakMap();

+ 4 - 0
std/js/_std/haxe/ds/IntMap.hx

@@ -62,6 +62,10 @@ package haxe.ds;
 		};
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/js/_std/haxe/ds/ObjectMap.hx

@@ -88,6 +88,10 @@ class ObjectMap<K:{ }, V> implements haxe.Constraints.IMap<K,V> {
 		};
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/js/_std/haxe/ds/StringMap.hx

@@ -125,6 +125,10 @@ private class StringMapIterator<T> {
 		return new StringMapIterator(this, arrayKeys());
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 4 - 0
std/lua/_std/haxe/ds/IntMap.hx

@@ -81,6 +81,10 @@ class IntMap<T> implements haxe.Constraints.IMap<Int,T> {
 		};
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/lua/_std/haxe/ds/ObjectMap.hx

@@ -79,7 +79,11 @@ class ObjectMap<A,B> implements haxe.Constraints.IMap<A,B> {
 			next : function() return h[itr.next()]
 		};
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<A, B> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<A,B> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/lua/_std/haxe/ds/StringMap.hx

@@ -80,7 +80,11 @@ class StringMap<T> implements haxe.Constraints.IMap<String,T> {
 			next : function() return h[it.next()]
 		};
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/neko/_std/haxe/ds/IntMap.hx

@@ -56,7 +56,11 @@ package haxe.ds;
 		untyped __dollar__hiter(h,function(_,v) { l.push(v); });
 		return l.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/neko/_std/haxe/ds/ObjectMap.hx

@@ -75,7 +75,11 @@ class ObjectMap<K:{},V> implements haxe.Constraints.IMap<K,V> {
 		untyped __dollar__hiter(h,function(_,v) { l.push(v); });
 		return l.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K, V> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/neko/_std/haxe/ds/StringMap.hx

@@ -56,7 +56,11 @@ package haxe.ds;
 		untyped __dollar__hiter(h,function(_,v) { l.push(v); });
 		return l.iterator();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 7 - 0
std/php/_std/haxe/ds/IntMap.hx

@@ -84,6 +84,13 @@ import php.NativeIndexedArray;
 		return Global.array_values(data).iterator();
 	}
 
+	/**
+		See `Map.keyValueIterator`
+	**/
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public inline function copy() : IntMap<T> {
 		return Syntax.clone(this);
 	}

+ 4 - 0
std/php/_std/haxe/ds/ObjectMap.hx

@@ -67,6 +67,10 @@ class ObjectMap <K:{ }, V> implements haxe.Constraints.IMap<K,V> {
 		return _values.iterator();
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public inline function copy() : ObjectMap<K,V> {
 		return Syntax.clone(this);
 	}

+ 4 - 0
std/php/_std/haxe/ds/StringMap.hx

@@ -63,6 +63,10 @@ import haxe.Constraints;
 		return data.iterator();
 	}
 
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public inline function copy() : StringMap<T> {
 		return Syntax.clone(this);
 	}

+ 5 - 1
std/python/_std/haxe/ds/IntMap.hx

@@ -57,7 +57,11 @@ class IntMap<T> implements haxe.Constraints.IMap<Int, T> {
 	public function iterator() : Iterator<T> {
 		return h.values().iter();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<Int, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : IntMap<T> {
 		var copied = new IntMap();
 		for(key in keys()) copied.set(key, get(key));

+ 5 - 1
std/python/_std/haxe/ds/ObjectMap.hx

@@ -58,7 +58,11 @@ class ObjectMap<K:{},V> implements haxe.Constraints.IMap<K, V> {
 	public function iterator() : Iterator<V> {
 		return h.values().iter();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : ObjectMap<K,V> {
 		var copied = new ObjectMap();
 		for(key in keys()) copied.set(key, get(key));

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

@@ -57,7 +57,11 @@ class StringMap<T> implements haxe.Constraints.IMap<String, T> {
 	public function iterator() : Iterator<T> {
 		return h.values().iter();
 	}
-	
+
+	@:runtime public inline function keyValueIterator() : KeyValueIterator<String, T> {
+		return new haxe.iterators.MapKeyValueIterator(this);
+	}
+
 	public function copy() : StringMap<T> {
 		var copied = new StringMap();
 		for(key in keys()) copied.set(key, get(key));

+ 99 - 1
tests/unit/src/unitstd/Map.unit.hx

@@ -56,6 +56,15 @@ map.exists("bar") == false;
 map.exists("baz") == true;
 map.get("bar") == null;
 
+var map3 = [1=>"2",2=>"4",3=>"6"];
+var keys = [for (k=>v in map3) k];
+keys.sort(Reflect.compare);
+keys == [1,2,3];
+var values = [for (k=>v in map3) v];
+values.sort(Reflect.compare);
+values == ["2","4","6"];
+
+
 // Int
 var map = new Map();
 map.exists(1) == false;
@@ -104,6 +113,14 @@ map.exists(2) == false;
 map.exists(3) == true;
 map.get(2) == null;
 
+var map3 = [1=>2,2=>4,3=>6];
+var keys = [for (k=>v in map3) k];
+keys.sort(Reflect.compare);
+keys == [1,2,3];
+var values = [for (k=>v in map3) v];
+values.sort(Reflect.compare);
+values == [2,4,6];
+
 // Hashable
 var map = new Map();
 var a = new unit.MyAbstract.ClassWithHashCode(1);
@@ -121,6 +138,15 @@ map.get(a) == 1;
 map.get(b) == 2;
 map.get(c) == 3;
 
+var keys = [for (k=>v in map) k];
+keys[0] in [a,b,c];
+keys[1] in [a,b,c];
+keys[2] in [a,b,c];
+var values = [for (k=>v in map) v];
+values[0] in [1,2,3];
+values[1] in [1,2,3];
+values[2] in [1,2,3];
+
 var copied = map.copy();
 copied != map;
 copied.exists(a) == map.exists(a);
@@ -171,6 +197,15 @@ map.get(a) == 1;
 map.get(b) == 2;
 map.get(c) == 3;
 
+var keys = [for (k=>v in map) k];
+keys[0] in [a,b,c];
+keys[1] in [a,b,c];
+keys[2] in [a,b,c];
+var values = [for (k=>v in map) v];
+values[0] in [1,2,3];
+values[1] in [1,2,3];
+values[2] in [1,2,3];
+
 var copied = map.copy();
 copied != map;
 copied.exists(a) == map.exists(a);
@@ -232,4 +267,67 @@ map["foo"] == 9;
 
 var map:Map<String, Int>;
 HelperMacros.typedAs((null : Map<String, Int>), map = []);
-HelperMacros.typeError(map[1] = 1) == true;
+HelperMacros.typeError(map[1] = 1) == true;
+
+#if !(java || cs)
+['' => ''].keyValueIterator().next().key == '';
+['' => ''].keyValueIterator().next().value == '';
+[2 => 3].keyValueIterator().next().key == 2;
+[2 => 3].keyValueIterator().next().value == 3;
+#end
+
+// Test unification
+
+var map = [1=>"2",2=>"4"];
+var iterable:KeyValueIterable<Int, String> = map;
+var values = [for(kv in iterable.keyValueIterator()) kv.value];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+
+var iterator:KeyValueIterator<Int,String> = iterable.keyValueIterator();
+var keys = [for(kv in iterator) kv.key];
+keys[0] in [1,2];
+keys[1] in [1,2];
+
+
+// Test through Dynamic
+
+var map = [1=>"2",2=>"4"];
+var dyn:Dynamic = map;
+var it = dyn.iterator();
+var it:Iterator<String> = cast it;
+var values = [for(v in it) v];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+
+var it = dyn.keyValueIterator();
+var it:KeyValueIterator<Int,String> = cast it;
+var values = [for(kv in it) kv.value];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+var it = dyn.keyValueIterator();
+var it:KeyValueIterator<Int,String> = cast it;
+var keys = [for(kv in it) kv.key];
+keys[0] in [1,2];
+keys[1] in [1,2];
+
+
+var map = ["1a"=>"2","1b"=> "4"];
+var dyn:Dynamic = map;
+var it = dyn.iterator();
+var it:Iterator<String> = cast it;
+var values = [for(v in it) v];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+
+var it = dyn.keyValueIterator();
+var it:KeyValueIterator<String,String> = cast it;
+var values = [for(kv in it) kv.value];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+
+var it = dyn.keyValueIterator();
+var it:KeyValueIterator<String,String> = cast it;
+var keys = [for(kv in it) kv.key];
+keys[0] in ["1a","1b"];
+keys[1] in ["1a","1b"];

+ 10 - 0
tests/unit/src/unitstd/haxe/ds/BalancedTree.unit.hx

@@ -72,3 +72,13 @@ for (k in test.keys()) {
 	eq(ms.remove(Std.string(k)), true);
 	eq(ms.exists(Std.string(k)), false);
 }
+
+// keyValueIterator
+var test2 = new haxe.ds.BalancedTree<Int, Int>();
+var keys1 = [1,2,3];
+var values1 = [2,4,6];
+for(i in 0 ... keys1.length) test2.set(keys1[i], values1[i]);
+
+[for(k=>v in test2) k] == [1,2,3];
+[for(k=>v in test2) v] == [2,4,6];
+[for(k=>v in test2) k*v] == [2,8,18];

+ 71 - 0
tests/unit/src/unitstd/haxe/ds/IntMap.unit.hx

@@ -0,0 +1,71 @@
+var map1 = new haxe.ds.IntMap();
+(map1 is haxe.ds.IntMap) == true;
+map1.set(1, 2);
+map1.set(2, 4);
+map1.set(3, 6);
+map1.get(1) == 2;
+map1.get(2) == 4; 
+map1.get(3) != 8;
+
+// iterator
+var keys1a = [for (k in map1.keys()) k];
+keys1a.sort(Reflect.compare); // Order is undefined
+keys1a == [1,2,3];
+
+var values1a = [for (v in map1) v];
+values1a.sort(Reflect.compare); 
+values1a == [2,4,6];
+
+// key value iterator
+var keys1b = [for (k=>v in map1) k];
+keys1b.sort(Reflect.compare); 
+keys1b == [1,2,3];
+
+var values1b = [for (k=>v in map1) v];
+values1b.sort(Reflect.compare); 
+values1b == [2,4,6];
+
+var values1c = [for (k=>v in map1) k*v];
+values1c.sort(Reflect.compare); 
+values1c == [2,8,18];
+
+
+var map2 = new haxe.ds.IntMap();
+(map2 is haxe.ds.IntMap) == true;
+map2.set(1, "2");
+map2.set(2, "4");
+map2.set(3, "6");
+map2.get(1) == "2";
+map2.get(2) == "4"; 
+map2.get(3) != "8";
+
+// iterator
+var keys2a = [for (k in map2.keys()) k];
+keys2a.sort(Reflect.compare); 
+keys2a == [1,2,3];
+
+var values2a = [for (v in map2) v];
+values2a.sort(Reflect.compare); 
+values2a == ["2","4","6"];
+// key value iterator
+var keys2b = [for (k=>v in map2) k];
+keys2b.sort(Reflect.compare); 
+keys2b == [1,2,3];
+
+var values2b = [for (k=>v in map2) v];
+values2b.sort(Reflect.compare); 
+values2b == ["2","4","6"];
+
+
+// Test unification
+
+var map:haxe.ds.IntMap<String> = [1=>"2",2=>"4"];
+var iterable:KeyValueIterable<Int, String> = map; 
+var iterator:KeyValueIterator<Int,String> = iterable.keyValueIterator();
+var values = [for(kv in iterator) kv.value];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+var iterator:KeyValueIterator<Int,String> = iterable.keyValueIterator();
+var keys = [for(kv in iterator) kv.key];
+keys[0] in [1,2];
+keys[1] in [1,2];

+ 67 - 0
tests/unit/src/unitstd/haxe/ds/StringMap.unit.hx

@@ -0,0 +1,67 @@
+var map1 = new haxe.ds.StringMap();
+(map1 is haxe.ds.StringMap) == true;
+map1.set("1a", 2);
+map1.set("2a", 4);
+map1.set("3a", 6);
+map1.get("1a") == 2;
+map1.get("2a") == 4; 
+map1.get("3a") != 8;
+
+// iterator
+var keys1a = [for (k in map1.keys()) k];
+keys1a.sort(Reflect.compare); // Order is undefined
+keys1a == ["1a","2a","3a"];
+
+var values1a = [for (v in map1) v];
+values1a.sort(Reflect.compare); 
+values1a == [2,4,6];
+
+// key value iterator
+var keys1b = [for (k=>v in map1) k];
+keys1b.sort(Reflect.compare); 
+keys1b == ["1a","2a","3a"];
+
+var values1b = [for (k=>v in map1) v];
+values1b.sort(Reflect.compare); 
+values1b == [2,4,6];
+
+
+var map2 = new haxe.ds.StringMap();
+(map2 is haxe.ds.StringMap) == true;
+map2.set("1a", "2");
+map2.set("2a", "4");
+map2.set("3a", "6");
+map2.get("1a") == "2";
+map2.get("2a") == "4"; 
+map2.get("3a") != "8";
+
+// iterator
+var keys2a = [for (k in map2.keys()) k];
+keys2a.sort(Reflect.compare); 
+keys2a == ["1a","2a","3a"];
+
+var values2a = [for (v in map2) v];
+values2a.sort(Reflect.compare); 
+values2a == ["2","4","6"];
+// key value iterator
+var keys2b = [for (k=>v in map2) k];
+keys2b.sort(Reflect.compare); 
+keys2b == ["1a","2a","3a"];
+
+var values2b = [for (k=>v in map2) v];
+values2b.sort(Reflect.compare); 
+values2b == ["2","4","6"];
+
+
+// Test unification
+
+var map:haxe.ds.StringMap<String> = ["1a"=>"2","1b"=>"4"];
+var iterable:KeyValueIterable<String, String> = map; 
+var iterator:KeyValueIterator<String,String> = iterable.keyValueIterator();
+var values = [for(kv in iterator) kv.value];
+values[0] in ["2","4"];
+values[1] in ["2","4"];
+var iterator:KeyValueIterator<String,String> = iterable.keyValueIterator();
+var keys = [for(kv in iterator) kv.key];
+keys[0] in ["1a", "1b"];
+keys[1] in ["1a", "1b"];