Require JsFunction implementations be marked final.

The requirement does not affect current expressivity as there
is a restriction in place that JsFunction implementations can not
be subclassed.

Change-Id: I45ab86af287e746e4ad5d0c0a8fc8c508a52bc9c
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index 021dd5c..063e6d7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -688,6 +688,10 @@
   }
 
   private void checkJsFunctionImplementation(JDeclaredType type) {
+    if (!type.isFinal()) {
+      logError("JsFunction implementation '%s' must be final.",
+          type);
+    }
     if (type.getImplements().size() != 1) {
       logError("JsFunction implementation '%s' cannot implement more than one interface.",
           type);
@@ -704,11 +708,6 @@
   }
 
   private void checkJsFunctionSubtype(JDeclaredType type) {
-    JClassType superClass = type.getSuperClass();
-    if (superClass != null && superClass.isJsFunctionImplementation()) {
-      logError(type, "'%s' cannot extend JsFunction implementation '%s'.",
-          JjsUtils.getReadableDescription(type), JjsUtils.getReadableDescription(superClass));
-    }
     for (JInterfaceType superInterface : type.getImplements()) {
       if (superInterface.isJsFunction()) {
         logError(type, "'%s' cannot extend JsFunction '%s'.",
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index 3be61df..f76803b 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -1140,17 +1140,6 @@
     assertBuggySucceeds();
   }
 
-  public void testJsFunctionWithNoExtendsSucceeds() throws Exception {
-    addSnippetImport("jsinterop.annotations.JsFunction");
-    addSnippetClassDecl(
-        "@JsFunction",
-        "public interface Buggy {",
-        "  void foo();",
-        "}");
-
-    assertBuggySucceeds();
-  }
-
   public void testJsFunctionExtendsInterfaceFails() throws Exception {
     addSnippetImport("jsinterop.annotations.JsFunction");
     addSnippetClassDecl(
@@ -1185,11 +1174,19 @@
         "Line 6: 'EntryPoint.Buggy' cannot be both a JsFunction and a JsType at the same time.");
   }
 
-  public void testJsFunctionImplementationWithSingleInterfaceSucceeds() throws Exception {
-    addAll(jsFunctionInterface);
+  public void testJsFunctionImplementationSucceeds() throws Exception {
+    addSnippetImport("jsinterop.annotations.JsFunction");
     addSnippetClassDecl(
-        "public static class Buggy implements MyJsFunctionInterface {",
-        "  public int foo(int x) { return 0; }",
+        "@JsFunction",
+        "public interface Function {",
+        "  void foo();",
+        "}",
+        "public static final class Buggy implements Function {",
+        "  public void foo() {",
+        "    new Function() {",
+        "       public void foo() {}",
+        "    }.foo();",
+        "  }",
         "}");
 
     assertBuggySucceeds();
@@ -1199,7 +1196,7 @@
     addAll(jsFunctionInterface);
     addSnippetClassDecl(
         "interface AnotherInterface {}",
-        "public static class Buggy implements MyJsFunctionInterface, AnotherInterface {",
+        "public static final class Buggy implements MyJsFunctionInterface, AnotherInterface {",
         "  public int foo(int x) { return 0; }",
         "  public int bar(int x) { return 0; }",
         "}");
@@ -1212,7 +1209,7 @@
     addAll(jsFunctionInterface);
     addSnippetClassDecl(
         "public static class BaseClass {}",
-        "public static class Buggy extends BaseClass implements MyJsFunctionInterface {",
+        "public static final class Buggy extends BaseClass implements MyJsFunctionInterface {",
         "  public int foo(int x) { return 0; }",
         "}");
 
@@ -1220,7 +1217,7 @@
         + "extend a class.");
   }
 
-  public void testJsFunctionImplementationWithSubclassesFails() throws Exception {
+  public void testJsFunctionImplementationNotFinalFails() throws Exception {
     addAll(jsFunctionInterface);
     addSnippetClassDecl(
         "public static class BaseClass implements MyJsFunctionInterface {",
@@ -1229,8 +1226,7 @@
         "public static class Buggy extends BaseClass  {",
         "}");
 
-    assertBuggyFails("Line 6: 'EntryPoint.Buggy' cannot extend "
-        + "JsFunction implementation 'EntryPoint.BaseClass'.");
+    assertBuggyFails("Line 3: JsFunction implementation 'EntryPoint.BaseClass' must be final.");
   }
 
   public void testJsFunctionImplementationMarkedAsJsTypeFails() throws Exception {
@@ -1238,7 +1234,7 @@
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetClassDecl(
         "@JsType",
-        "public static class Buggy implements MyJsFunctionInterface {",
+        "public static final class Buggy implements MyJsFunctionInterface {",
         "  public int foo(int x) { return 0; }",
         "}");