Avoid tighthening @JsType/@JsFunction types.

Change-Id: I25314dc26df90ee34e82ee6b611c733c61f0a7f8
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
index d702194..48dd9f8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
@@ -85,6 +85,11 @@
   }
 
   @Override
+  public boolean isJsType() {
+    return getLeafType().isJsType() || getLeafType().isJsFunction();
+  }
+
+  @Override
   public boolean isJsoType() {
     return false;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index dd51b0e..3e683a3 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -349,10 +349,12 @@
     return methods;
   }
 
+  @Override
   public boolean isJsType() {
     return isJsType;
   }
 
+  @Override
   public boolean isOrExtendsJsType() {
     if (isJsType()) {
       return true;
@@ -365,10 +367,12 @@
     return false;
   }
 
+  @Override
   public boolean isJsFunction() {
     return isJsFunction;
   }
 
+  @Override
   public boolean isOrExtendsJsFunction() {
     if (isJsFunction()) {
       return true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index 0a66231..0b03ddb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -69,6 +69,22 @@
     return false;
   }
 
+  public boolean isJsType() {
+    return false;
+  }
+
+  public boolean isOrExtendsJsType() {
+    return isJsType();
+  }
+
+  public boolean isJsFunction() {
+    return false;
+  }
+
+  public boolean isOrExtendsJsFunction() {
+    return isJsFunction();
+  }
+
   /**
    * Returns {@code true} if this is a JavaScriptObject type.
    */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 641fa7c..526f561 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -555,6 +555,7 @@
         assert !x.isStaticDispatchOnly();
         JMethodCall newCall = new JMethodCall(x.getSourceInfo(), x.getInstance(), concreteMethod);
         newCall.addArgs(x.getArgs());
+        newCall.setCannotBePolymorphic();
         ctx.replaceMe(newCall);
       }
     }
@@ -625,7 +626,7 @@
      * type.
      */
     private JReferenceType getSingleConcreteType(JType type) {
-      if (!(type instanceof JReferenceType)) {
+      if (!(type instanceof JReferenceType) || type.isJsType() || type.isJsFunction()) {
         return null;
       }
 
diff --git a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
index 0ffd3b0..e76de02 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
@@ -19,6 +19,8 @@
 
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.ScriptInjector;
+import com.google.gwt.core.client.js.JsFunction;
+import com.google.gwt.core.client.js.JsType;
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.util.Iterator;
@@ -360,4 +362,74 @@
       assertFalse("Field '" + field + "' should not be exported", hasField(obj, field));
     }
   }
+
+  @JsType
+  interface SimpleJsTypeFieldInterface {
+  }
+
+  static class SimpleJsTypeFieldClass implements SimpleJsTypeFieldInterface {
+  }
+
+  @JsType
+  static class SimpleJsTypeWithField {
+    public SimpleJsTypeFieldInterface someField;
+  }
+
+  public void testJsTypeField() {
+    new SimpleJsTypeFieldClass();
+    SimpleJsTypeWithField holder = new SimpleJsTypeWithField();
+    fillJsTypeField(holder);
+    SimpleJsTypeFieldInterface someField = holder.someField;
+    assertNotNull(someField);
+  }
+
+  private native void fillJsTypeField(SimpleJsTypeWithField jstype) /*-{
+    jstype.someField = {};
+  }-*/;
+
+  @JsType
+  interface InterfaceWithSingleJavaConcrete {
+    int m();
+  }
+
+  static class JavaConcrete implements InterfaceWithSingleJavaConcrete {
+    public int m() {
+      return 5;
+    }
+  }
+
+  private native Object nativeObjectImplementingM() /*-{
+    return {m: function() { return 3;} }
+  }-*/;
+
+  public void testSingleJavaConcreteInterface() {
+    // Create a couple of instances and use the objects in some way to avoid complete pruning
+    // of JavaConcrete
+    assertTrue(new JavaConcrete() != new JavaConcrete());
+    assertSame(5, new JavaConcrete().m());
+    assertSame(3, ((InterfaceWithSingleJavaConcrete) nativeObjectImplementingM()).m());
+  }
+
+  @JsFunction
+  interface JsFunctionInterface {
+    int m();
+  }
+
+  static class JavaConcreteJsFunction implements JsFunctionInterface {
+    public int m() {
+      return 5;
+    }
+  }
+
+  private native Object nativeJsFunction() /*-{
+    return function() { return 3;};
+  }-*/;
+
+  public void testSingleJavaConcreteJsFunction() {
+    // Create a couple of instances and use the objects in some way to avoid complete pruning
+    // of JavaConcrete
+    assertTrue(new JavaConcreteJsFunction() != new JavaConcreteJsFunction());
+    assertSame(5, new JavaConcreteJsFunction().m());
+    assertSame(3, ((JsFunctionInterface) nativeJsFunction()).m());
+  }
 }