Make JsFunction implementation behave exactly as native functions.

JsFunctions no longer copy j.l.Object methods/properties into
their prototypes effectively behaving like native JS functions.

Bug: #9366
Bug-Link: http://github.com/gwtproject/gwt/issues/9366
Change-Id: I1d90f2606f099f53b458eed145b2e9ad459e50d3
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 572af74..13ed73a 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
@@ -127,6 +127,11 @@
   }
 
   @Override
+  public boolean isJsFunctionImplementation() {
+    return false;
+  }
+
+  @Override
   public boolean isJsNative() {
     return getLeafType().isJsNative();
   }
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 3d4e8b0..86012c5 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
@@ -379,10 +379,6 @@
     return isJsFunction;
   }
 
-  public boolean isJsFunctionImplementation() {
-    return false;
-  }
-
   public boolean isClassWideExport() {
     return isClassWideExport;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
index be8f9c3..b633e7e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
@@ -77,6 +77,11 @@
   }
 
   @Override
+  public boolean isJsFunctionImplementation() {
+    return false;
+  }
+
+  @Override
   public boolean isJavaLangObject() {
     return false;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
index 8e62df3..6475060 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
@@ -95,6 +95,11 @@
   }
 
   @Override
+  public boolean isJsFunctionImplementation() {
+    return false;
+  }
+
+  @Override
   public boolean isJsNative() {
     return false;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
index e3611d2..205e133 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
@@ -81,6 +81,11 @@
         }
 
         @Override
+        public boolean isJsFunctionImplementation() {
+          return false;
+        }
+
+        @Override
         public boolean isJsNative() {
           return false;
         }
@@ -212,6 +217,11 @@
     }
 
     @Override
+    public boolean isJsFunctionImplementation() {
+      return ref.isJsFunctionImplementation();
+    }
+
+    @Override
     public boolean isJsoType() {
       return ref.isJsoType();
     }
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 4dfe990..a6406cd 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
@@ -76,6 +76,8 @@
 
   public abstract boolean isJsFunction();
 
+  public abstract boolean isJsFunctionImplementation();
+
   public abstract boolean isJsNative();
 
   public abstract boolean canBeImplementedExternally();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index b076ab3..133cd79 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -2001,12 +2001,20 @@
           RuntimeConstants.RUNTIME_DEFINE_CLASS, defineClassArguments).makeStmt();
       addTypeDefinitionStatement(type, defineClassStatement);
 
-      if (jsPrototype != null) {
+      maybeCopyObjProperties(
+          type,
+          getPrototypeQualifierViaLookup(program.getTypeJavaLangObject(), type.getSourceInfo()),
+          globalTemp.makeRef(type.getSourceInfo()));
+    }
+
+    private void maybeCopyObjProperties(
+        JDeclaredType type, JsExpression toPrototype, JsExpression fromPrototype) {
+      if (getSuperPrototype(type) != null && !type.isJsFunctionImplementation()) {
         JsStatement statement =
         constructInvocation(type.getSourceInfo(),
             RuntimeConstants.RUNTIME_COPY_OBJECT_PROPERTIES,
-            getPrototypeQualifierViaLookup(program.getTypeJavaLangObject(), type.getSourceInfo()),
-            globalTemp.makeRef(type.getSourceInfo()))
+            fromPrototype,
+            toPrototype)
             .makeStmt();
         addTypeDefinitionStatement(type, statement);
       }
@@ -2134,14 +2142,10 @@
 
       // inline assignment of castableTypeMap field instead of using defineClass()
       setupCastMapOnPrototype(type);
-      if (jsPrototype != null) {
-        JsStatement statement =
-            constructInvocation(info,
-                RuntimeConstants.RUNTIME_COPY_OBJECT_PROPERTIES,
-                getPrototypeQualifierOf(program.getTypeJavaLangObject(), info),
-                getPrototypeQualifierOf(type, info)).makeStmt();
-        addTypeDefinitionStatement(type, statement);
-      }
+      maybeCopyObjProperties(
+          type,
+          getPrototypeQualifierOf(program.getTypeJavaLangObject(), info),
+          getPrototypeQualifierOf(type, info));
     }
 
     private void setupCastMapOnPrototype(JDeclaredType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
index 11d373a..3006bda 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
@@ -469,7 +469,7 @@
    * </pre>
    */
   private JField resolveClassLiteralField(JType type) {
-    type = type.isJsNative()
+    type = type.isJsNative() || type.isJsFunction() || type.isJsFunctionImplementation()
         ? program.getJavaScriptObject() : program.normalizeJsoType(type);
     JField field = classLiteralFields.get(type);
     if (field == null) {
diff --git a/user/test/com/google/gwt/core/interop/JsFunctionTest.java b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
index b71d71a..8423f77 100644
--- a/user/test/com/google/gwt/core/interop/JsFunctionTest.java
+++ b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
@@ -256,6 +256,57 @@
     assertSame(jsFuncionProperty.func.m(), funcInVar.m());
   }
 
+  public void testGetClass() {
+
+    MyJsFunctionInterface jsfunctionImplementation =
+        new MyJsFunctionInterface() {
+          @Override
+          public int foo(int a) {
+            return a;
+          }
+        };
+    assertEquals(MyJsFunctionInterface.class, jsfunctionImplementation.getClass());
+    assertEquals(MyJsFunctionInterface.class, ((Object) jsfunctionImplementation).getClass());
+    assertEquals(MyJsFunctionInterface.class, createMyJsFunction().getClass());
+    assertEquals(MyJsFunctionInterface.class, ((Object) createMyJsFunction()).getClass());
+  }
+
+  public void testGetClassA() {
+
+    MyJsFunctionInterface jsfunctionImplementation =
+        new MyJsFunctionInterface() {
+          @Override
+          public int foo(int a) {
+            return a;
+          }
+        };
+    assertEquals(MyJsFunctionInterface.class, ((Object) jsfunctionImplementation).getClass());
+  }
+
+  public void testGetClassB() {
+
+    MyJsFunctionInterface jsfunctionImplementation =
+        new MyJsFunctionInterface() {
+          @Override
+          public int foo(int a) {
+            return a;
+          }
+        };
+    assertEquals(MyJsFunctionInterface.class, createMyJsFunction().getClass());
+  }
+
+  public void testGetClassC() {
+
+    MyJsFunctionInterface jsfunctionImplementation =
+        new MyJsFunctionInterface() {
+          @Override
+          public int foo(int a) {
+            return a;
+          }
+        };
+    assertEquals(MyJsFunctionInterface.class, ((Object) createMyJsFunction()).getClass());
+  }
+
   // uncomment when Java8 is supported.
 //  public void testJsFunctionLambda_JS() {
 //    MyJsFunctionInterface jsFunctionInterface = a -> { return a + 2; };