Remove ConcurrentModificationDetector.

ConcurrentModificationDetector was using WASM incompatible jsinterop magic
to track mod count. This was done to hide everything magically behind a flag.

Normally removing the read of the modCount should be enough to remove all other usages however we are hitting JsCompiler bugs there so I also guarded
structureChanged calls.

PiperOrigin-RevId: 373473243
Change-Id: I3f5b2160a2bc34bd02a0d5f7f224b548ac3f3cc7
diff --git a/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java b/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java
index d3ffe81..dec4c91 100644
--- a/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java
+++ b/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java
@@ -15,13 +15,11 @@
  */
 package java.util;
 
-import static java.util.ConcurrentModificationDetector.checkStructuralChange;
-import static java.util.ConcurrentModificationDetector.recordLastKnownStructure;
-import static java.util.ConcurrentModificationDetector.structureChanged;
-
 import static javaemul.internal.InternalPreconditions.checkArgument;
+import static javaemul.internal.InternalPreconditions.checkConcurrentModification;
 import static javaemul.internal.InternalPreconditions.checkElement;
 import static javaemul.internal.InternalPreconditions.checkState;
+import static javaemul.internal.InternalPreconditions.isApiChecked;
 
 import javaemul.internal.JsUtils;
 import javaemul.internal.annotations.SpecializeMethod;
@@ -80,10 +78,7 @@
     private Iterator<Entry<K, V>> current = stringMapEntries;
     private Iterator<Entry<K, V>> last;
     private boolean hasNext = computeHasNext();
-
-    public EntrySetIterator() {
-      recordLastKnownStructure(AbstractHashMap.this, this);
-    }
+    private int lastModCount = modCount;
 
     @Override
     public boolean hasNext() {
@@ -103,7 +98,7 @@
 
     @Override
     public Entry<K, V> next() {
-      checkStructuralChange(AbstractHashMap.this, this);
+      checkConcurrentModification(modCount, lastModCount);
       checkElement(hasNext());
 
       last = current;
@@ -116,13 +111,13 @@
     @Override
     public void remove() {
       checkState(last != null);
-      checkStructuralChange(AbstractHashMap.this, this);
+      checkConcurrentModification(modCount, lastModCount);
 
       last.remove();
       last = null;
       hasNext = computeHasNext();
 
-      recordLastKnownStructure(AbstractHashMap.this, this);
+      lastModCount = modCount;
     }
   }
 
@@ -136,6 +131,8 @@
    */
   private transient InternalStringMap<K, V> stringMap;
 
+  int modCount;
+
   public AbstractHashMap() {
     reset();
   }
@@ -166,7 +163,16 @@
   private void reset() {
     hashCodeMap = new InternalHashCodeMap<K, V>(this);
     stringMap = new InternalStringMap<K, V>(this);
-    structureChanged(this);
+    structureChanged();
+  }
+
+  void structureChanged() {
+    if (!isApiChecked()) {
+      // Shouldn't be necessary but JsCompiler chokes on removing modCount so make sure we don't pay
+      // cost for updating the field.
+      return;
+    }
+    this.modCount++;
   }
 
   @SpecializeMethod(params = {String.class}, target = "hasStringValue")
diff --git a/user/super/com/google/gwt/emul/java/util/ConcurrentModificationDetector.java b/user/super/com/google/gwt/emul/java/util/ConcurrentModificationDetector.java
deleted file mode 100644
index 69c8be4..0000000
--- a/user/super/com/google/gwt/emul/java/util/ConcurrentModificationDetector.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package java.util;
-
-import javaemul.internal.InternalPreconditions;
-
-import jsinterop.annotations.JsPackage;
-import jsinterop.annotations.JsProperty;
-import jsinterop.annotations.JsType;
-
-/**
- * A helper to detect concurrent modifications to collections. This is implemented as a helper
- * utility so that we could remove the checks easily by a flag.
- */
-class ConcurrentModificationDetector {
-
-  private static final boolean API_CHECK = InternalPreconditions.isApiChecked();
-
-  public static void structureChanged(Object host) {
-    if (!API_CHECK) {
-      return;
-    }
-
-    ModCountable modCountable = (ModCountable) host;
-    // Ensure that modCount is initialized if it is not already.
-    int modCount = modCountable.getModCount() | 0;
-    modCountable.setModCount(modCount + 1);
-  }
-
-  public static void recordLastKnownStructure(Object host, Iterator<?> iterator) {
-    if (!API_CHECK) {
-      return;
-    }
-
-    ((ModCountable) iterator).setModCount(((ModCountable) host).getModCount());
-  }
-
-  public static void checkStructuralChange(Object host, Iterator<?> iterator) {
-    if (!API_CHECK) {
-      return;
-    }
-
-    if (((ModCountable) iterator).getModCount() != ((ModCountable) host).getModCount()) {
-      throw new ConcurrentModificationException();
-    }
-  }
-
-  @JsType(isNative = true, name = "*", namespace = JsPackage.GLOBAL)
-  private interface ModCountable {
-    @JsProperty(name = "$modCount")
-    void setModCount(int modCount);
-
-    @JsProperty(name = "$modCount")
-    int getModCount();
-  }
-}
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 4b9a251..abefed8 100644
--- a/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java
+++ b/user/super/com/google/gwt/emul/java/util/InternalHashCodeMap.java
@@ -15,8 +15,6 @@
  */
 package java.util;
 
-import static java.util.ConcurrentModificationDetector.structureChanged;
-
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Map.Entry;
 import javaemul.internal.ArrayHelper;
@@ -58,7 +56,7 @@
     }
     chain[chain.length] = new SimpleEntry<K, V>(key, value);
     size++;
-    structureChanged(host);
+    host.structureChanged();
     return null;
   }
 
@@ -77,7 +75,7 @@
           ArrayHelper.removeFrom(chain, i, 1);
         }
         size--;
-        structureChanged(host);
+        host.structureChanged();
         return entry.getValue();
       }
     }
diff --git a/user/super/com/google/gwt/emul/java/util/InternalStringMap.java b/user/super/com/google/gwt/emul/java/util/InternalStringMap.java
index 8366f6f..52fe42d 100644
--- a/user/super/com/google/gwt/emul/java/util/InternalStringMap.java
+++ b/user/super/com/google/gwt/emul/java/util/InternalStringMap.java
@@ -15,10 +15,7 @@
  */
 package java.util;
 
-import static java.util.ConcurrentModificationDetector.structureChanged;
-
 import java.util.Map.Entry;
-
 import javaemul.internal.JsUtils;
 
 /**
@@ -56,7 +53,7 @@
 
     if (JsUtils.isUndefined(oldValue)) {
       size++;
-      structureChanged(host);
+      host.structureChanged();
     } else {
       valueMod++;
     }
@@ -68,7 +65,7 @@
     if (!JsUtils.isUndefined(value)) {
       backingMap.delete(key);
       size--;
-      structureChanged(host);
+      host.structureChanged();
     } else {
       valueMod++;
     }
diff --git a/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java b/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java
index c1f5f6c..5cd2a6d 100644
--- a/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java
+++ b/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java
@@ -15,9 +15,7 @@
  */
 package java.util;
 
-import static java.util.ConcurrentModificationDetector.checkStructuralChange;
-import static java.util.ConcurrentModificationDetector.recordLastKnownStructure;
-
+import static javaemul.internal.InternalPreconditions.checkConcurrentModification;
 import static javaemul.internal.InternalPreconditions.checkCriticalElement;
 import static javaemul.internal.InternalPreconditions.checkState;
 
@@ -93,9 +91,11 @@
       // The next entry to return from this iterator.
       private ChainEntry next;
 
+      private int lastModCount;
+
       public EntryIterator() {
         next = head.next;
-        recordLastKnownStructure(map, this);
+        lastModCount = map.modCount;
       }
 
       @Override
@@ -105,7 +105,7 @@
 
       @Override
       public Map.Entry<K, V> next() {
-        checkStructuralChange(map, this);
+        checkConcurrentModification(map.modCount, lastModCount);
         checkCriticalElement(hasNext());
 
         last = next;
@@ -116,11 +116,11 @@
       @Override
       public void remove() {
         checkState(last != null);
-        checkStructuralChange(map, this);
+        checkConcurrentModification(map.modCount, lastModCount);
 
         last.remove();
         map.remove(last.getKey());
-        recordLastKnownStructure(map, this);
+        lastModCount = map.modCount;
         last = null;
       }
     }
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java b/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
index f41d857..b754266 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/InternalPreconditions.java
@@ -17,6 +17,7 @@
 
 import static java.lang.System.getProperty;
 
+import java.util.ConcurrentModificationException;
 import java.util.NoSuchElementException;
 
 /**
@@ -139,7 +140,7 @@
     } else if (IS_ASSERTED) {
       try {
         checkCriticalType(expression, message);
-      } catch (Exception e) {
+      } catch (RuntimeException e) {
         throw new AssertionError(e);
       }
     }
@@ -586,6 +587,24 @@
     }
   }
 
+  public static void checkConcurrentModification(int currentModCount, int recordedModCount) {
+    if (IS_API_CHECKED) {
+      checkCriticalConcurrentModification(currentModCount, recordedModCount);
+    } else if (IS_ASSERTED) {
+      try {
+        checkCriticalConcurrentModification(currentModCount, recordedModCount);
+      } catch (Exception e) {
+        throw new AssertionError(e);
+      }
+    }
+  }
+
+  public static void checkCriticalConcurrentModification(
+      double currentModCount, double recordedModCount) {
+    if (currentModCount != recordedModCount) {
+      throw new ConcurrentModificationException();
+    }
+  }
   // Hides the constructor for this static utility class.
   private InternalPreconditions() { }
 }