Make sure that enums exposed with JsInterop are not ordinalized.

Change-Id: I831168f18d4f6450e5cf424e872fb9fc767b62c0
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
index dbd4349..51ce69a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
@@ -326,16 +326,17 @@
     @Override
     public void endVisit(JClassType x, Context ctx) {
       // keep track of all enum classes visited
-      JEnumType maybeEnum = x.isEnumOrSubclass();
-      if (maybeEnum == null) {
+      JEnumType enumClass = x.isEnumOrSubclass();
+      if (enumClass == null) {
         return;
       }
 
-      enumsVisited.add(maybeEnum);
+      enumsVisited.add(enumClass);
 
       // don't need to re-ordinalize a previously ordinalized enum
-      if (maybeEnum.isOrdinalized()) {
-        addToBlackList(maybeEnum, x.getSourceInfo());
+      if (enumClass.isOrdinalized()
+          || enumClass.canBeReferencedExternally()) {
+        addToBlackList(enumClass, x.getSourceInfo());
       }
     }
 
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index 7e5f7bc..6eb0415 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -192,7 +192,8 @@
           "package java.lang;",
           "import java.io.Serializable;",
           "import com.google.gwt.core.client.JavaScriptObject;",
-          "public abstract class Enum<E extends Enum<E>> implements Serializable {",
+          "import jsinterop.annotations.JsType;",
+          "@JsType public abstract class Enum<E extends Enum<E>> implements Serializable {",
           "  public static native <T extends Enum<T>> T valueOf(Class<T> enumType,",
           "      String name) /*-{ return enumType + name; }-*/;",
           "  public static native <T extends Enum<T>> T valueOf(JavaScriptObject enumType,",
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
index d80f771..95e1b77 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
@@ -538,6 +538,32 @@
     assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
   }
 
+  public void testNotOrdinalizableJsTypeEnum()
+      throws UnableToCompleteException {
+    setupJsTypeEnums();
+    addSnippetClassDecl(
+        "public static JsFruit instanceFruit;",
+        "public static JsCustom instanceCustom;");
+    Result result = optimize("void",
+        "instanceFruit = JsFruit.JSAPPLE;",
+        "instanceCustom = JsCustom.VALUE1;");
+
+    EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
+    assertTrue(tracker.isVisited("test.EntryPoint$JsFruit"));
+    assertFalse(tracker.isOrdinalized("test.EntryPoint$JsFruit"));
+
+    assertTrue(tracker.isVisited("test.EntryPoint$JsCustom"));
+    assertFalse(tracker.isOrdinalized("test.EntryPoint$JsCustom"));
+
+    assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
+  }
+
+  private void setupJsTypeEnums() {
+    addSnippetImport("jsinterop.annotations.JsType");
+    addSnippetClassDecl("@JsType public enum JsFruit {JSAPPLE, JSORANGE}");
+    addSnippetClassDecl("@JsType public enum JsCustom {VALUE0, VALUE1 {} }");
+  }
+
   public void testNotOrdinalizableJsniFieldRef()
       throws UnableToCompleteException {
     setupFruitEnum();
diff --git a/user/test/com/google/gwt/core/interop/JsTypeTest.java b/user/test/com/google/gwt/core/interop/JsTypeTest.java
index dc1ea1c..8fd9ddd 100644
--- a/user/test/com/google/gwt/core/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/interop/JsTypeTest.java
@@ -79,6 +79,32 @@
     assertEquals(10, concreteJsTypeSubclass.publicSubclassMethod());
   }
 
+  @JsType
+  enum JsTypeEnum {
+    JSVALUE0,
+    JSVALUE1;
+  }
+
+  public void testJsTypeEnum() {
+    JsTypeEnum value = JsTypeEnum.JSVALUE1;
+
+    assertEquals(value.ordinal(), ((TestAccessor) this).callJsTypeEmumOrdinalMethod(value));
+  }
+
+  // Obscure the call with an alias so that the call is not detected by EnumOrdinalizer.
+  @JsType(isNative = true)
+  private interface TestAccessor {
+    @JsMethod
+    // Receive the JsType enum as its own type so that it is not seen as an upcast by
+    // EnumOrdinalizer, but actually dispach to a method that takes Object.
+    int callJsTypeEmumOrdinalMethod(JsTypeEnum value);
+  }
+
+  @JsMethod
+  private int callJsTypeEmumOrdinalMethod(Object value) {
+    return callIntFunction(value, "ordinal");
+  }
+
   public void testConcreteJsTypeNoTypeTightenField() {
     // If we type-tighten, java side will see no calls and think that field could only AImpl1.
     ConcreteJsType concreteJsType = new ConcreteJsType();