Fix iterator remove bug. See the test case for details of the bug. To put an end to these bugs I decided to simplify with a small additional cost on removal. Simpler is saner... Change-Id: If64056d90b71c226b9c07ee99ab5c8ff76ded57d Review-Link: https://gwt-review.googlesource.com/#/c/13489/
diff --git a/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java b/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java index 19a0011..1d78932 100644 --- a/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java +++ b/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java
@@ -75,6 +75,7 @@ Entry<K, V> entry = chain[i]; if (host.equals(key, entry.getKey())) { if (chain.length == 1) { + ArrayHelper.setLength(chain, 0); // remove the whole array backingMap.delete(hashCode); } else { @@ -112,7 +113,6 @@ final InternalJsIterator<Object> chains = backingMap.entries(); int itemIndex = 0; Entry<K, V>[] chain = newEntryChain(); - Entry<K, V>[] lastChain = null; Entry<K, V> lastEntry = null; @Override @@ -134,7 +134,6 @@ public Entry<K, V> next() { checkElement(hasNext()); - lastChain = chain; lastEntry = chain[itemIndex++]; return lastEntry; } @@ -143,14 +142,11 @@ public void remove() { checkState(lastEntry != null); - boolean isLastChain = itemIndex == lastChain.length; InternalHashCodeMap.this.remove(lastEntry.getKey()); - - // If this was not the last item in the chain, our itemIndex just jumped an item. - if (!isLastChain) { + // Unless we are in a new chain, all items have shifted so our itemIndex should as well... + if (itemIndex != 0) { itemIndex--; } - lastEntry = null; } };
diff --git a/user/test/com/google/gwt/emultest/java/util/HashMapTest.java b/user/test/com/google/gwt/emultest/java/util/HashMapTest.java index 28f232f..81054aa 100644 --- a/user/test/com/google/gwt/emultest/java/util/HashMapTest.java +++ b/user/test/com/google/gwt/emultest/java/util/HashMapTest.java
@@ -758,7 +758,7 @@ } // see: https://github.com/gwtproject/gwt/issues/9184 - public void testIterationWithCollidingHashCodes() { + public void testIteration_withCollidingHashCodes() { Object firstKey = createObjectWithHashCode(1); Object secondKey = createObjectWithHashCode(1); @@ -793,7 +793,7 @@ } // see: https://github.com/gwtproject/gwt/issues/9184 - public void testIterationWithCollidingHashCodes_FullIteration() { + public void testIteration_withCollidingHashCodesAndFullIteration() { List<Object> keys = new ArrayList<>(); HashMap<Object, String> testMap = new HashMap<>(); @@ -818,6 +818,22 @@ assertTrue(keys.isEmpty()); } + public void testIteration_hasNextThenRemove() { + HashMap<Object, String> testMap = new HashMap<>(); + testMap.put(createObjectWithHashCode(1), "one"); + testMap.put(createObjectWithHashCode(2), "two"); + + Iterator<Object> it = testMap.keySet().iterator(); + it.next(); + assertTrue("iterator should have next after first", it.hasNext()); + it.remove(); + assertTrue("iterator should have next after removal", it.hasNext()); + + it.next(); + it.remove(); + assertFalse(it.hasNext()); + } + private Object createObjectWithHashCode(final int hashCode) { return new Object() { @Override