Fix reference error in SDM related to @JsFunction interfaces.

@JsFunction methods were not considered exported by ControlFlowRecorder.

Change-Id: I8d2dd0be25f60255c8cd5e69bfae4407d5236c74
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 823554d..dd51b0e 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
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
+import com.google.gwt.dev.jjs.impl.JjsUtils;
 import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.thirdparty.guava.common.base.Preconditions;
@@ -332,7 +333,7 @@
 
   @Override
   public String getJavahSignatureName() {
-    return "L" + name.replaceAll("_", "_1").replace('.', '_') + "_2";
+    return JjsUtils.javahSignatureFromName(name);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
index 7e45789..23110e6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
@@ -137,11 +137,12 @@
           overriddenMethodName);
     }
 
-    if (x.isExported() || x.isOrOverridesJsTypeMethod()) {
+    if (x.isExported() || x.isOrOverridesJsTypeMethod() || x.isOrOverridesJsFunctionMethod()) {
+      stringAnalyzableTypeEnvironment.recordExportedMethodInType(currentMethodName, typeName);
+
       if (x.isStatic() || x.isConstructor()) {
         stringAnalyzableTypeEnvironment.recordExportedStaticReferenceInType(typeName);
       }
-      stringAnalyzableTypeEnvironment.recordExportedMethodInType(currentMethodName, typeName);
     }
 
     if (x.isConstructor()) {
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 0c8b94f..24045c5 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
@@ -3522,14 +3522,11 @@
   }
 
   String mangleName(JField x) {
-    String s = JjsUtils.mangledNameString(x.getEnclosingType()) + '_' + JjsUtils.mangledNameString(
-        x);
-    return s;
+    return JjsUtils.mangleMemberName(x.getEnclosingType().getName(), x.getName());
   }
 
   String mangleNameForGlobal(JMethod x) {
-    String s =
-        JjsUtils.mangledNameString(x.getEnclosingType()) + '_' + JjsUtils.mangledNameString(x) + "__";
+    String s = JjsUtils.mangleMemberName(x.getEnclosingType().getName(), x.getName()) + "__";
     for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
       JType type = x.getOriginalParamTypes().get(i);
       s += type.getJavahSignatureName();
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 8220385..9e6c555 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
@@ -465,7 +465,7 @@
 
       // Create a field in the class literal holder to hold the object.
       field =
-          new JField(info, getClassLiteralName(type), typeClassLiteralHolder, program
+          new JField(info, getClassLiteralFieldName(type), typeClassLiteralHolder, program
               .getTypeJavaLangClass(), true, Disposition.FINAL);
       typeClassLiteralHolder.addField(field);
       info.addCorrelation(info.getCorrelator().by(Literal.CLASS));
@@ -480,7 +480,7 @@
     return field;
   }
 
-  private static String getClassLiteralName(JType type) {
-    return type.getJavahSignatureName() + "_classLit";
+  private static String getClassLiteralFieldName(JType type) {
+    return JjsUtils.classLiteralFieldNameFromJavahTypeSignatureName(type.getJavahSignatureName());
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
index 4b07e67..e651c69 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -85,6 +85,13 @@
   }
 
   /**
+   * Returns the class literal field name.
+   */
+  public static String classLiteralFieldNameFromJavahTypeSignatureName(String javahSignatureName) {
+    return javahSignatureName + "_classLit";
+  }
+
+  /**
    * Java8 Method References such as String::equalsIgnoreCase should produce inner class names
    * that are a function of the samInterface (e.g. Runnable), the method being referred to,
    * and the qualifying disposition (this::foo vs Class::foo if foo is an instance method)
@@ -300,6 +307,18 @@
   }
 
   /**
+   * Mangles a qualified name into a Javah signature.
+   */
+  public static String javahSignatureFromName(String name) {
+    return "L" + mangledNameString(name) + "_2";
+  }
+
+  public static String mangleMemberName(String enclosingTypeName, String fieldName) {
+    return mangledNameString(
+        enclosingTypeName + '_' + mangledNameString(fieldName));
+  }
+
+  /**
    * Returns an valid identifier for a named Java entity.
    */
   public static String mangledNameString(HasName hasName) {
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index e90eb55..072d027 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -27,6 +27,7 @@
 import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
 import com.google.gwt.dev.javac.testing.impl.MockResource;
 import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.impl.JjsUtils;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.OptionJsInteropMode;
 import com.google.gwt.dev.util.arg.SourceLevel;
@@ -950,6 +951,70 @@
         new MinimalRebuildCache(), emptySet, JsOutputOption.OBFUSCATED);
   }
 
+   /**
+   * Test that some lightly referenced interface through a @JsFunction is included in the output.
+   */
+  public void testReferenceThroughJsFunction() throws Exception {
+    MockJavaResource someJsFunction =
+        JavaResourceBase.createMockJavaResource(
+            "com.foo.SomeJsFunction",
+            "package com.foo;",
+            "import com.google.gwt.core.client.js.JsFunction;",
+            "@JsFunction",
+            "public interface SomeJsFunction {",
+            "  void m();",
+            "}");
+
+    MockJavaResource jsFunctionInterfaceImplementation =
+        JavaResourceBase.createMockJavaResource(
+            "com.foo.Impl",
+            "package com.foo;",
+            "public class Impl implements SomeJsFunction {",
+            "  public void m() { SomeInterface.class.getName(); } ",
+            "}");
+
+    MockJavaResource someInterface =
+        JavaResourceBase.createMockJavaResource(
+            "com.foo.SomeInterface",
+            "package com.foo;",
+            "public interface SomeInterface {",
+            "}");
+
+    MockJavaResource testEntryPoint =
+        JavaResourceBase.createMockJavaResource(
+            "com.foo.TestEntryPoint",
+            "package com.foo;",
+            "import com.google.gwt.core.client.EntryPoint;",
+            "public class TestEntryPoint implements EntryPoint {",
+            "  private static native void f(SomeJsFunction f) /*-{}-*/;",
+            "  public void onModuleLoad() {",
+                // Create Impl and pass it to JS but do not explicitly call m
+            "    f(new Impl());",
+            "  }",
+            "}");
+
+    MockResource moduleResource =
+        JavaResourceBase.createMockResource(
+            "com/foo/TestEntryPoint.gwt.xml",
+            "<module>",
+            "  <source path=''/>",
+            "  <entry-point class='com.foo.TestEntryPoint'/>",
+            "</module>");
+
+    CompilerOptions compilerOptions = new CompilerOptionsImpl();
+    compilerOptions.setJsInteropMode(OptionJsInteropMode.Mode.JS);
+    String js = compileToJs(compilerOptions, Files.createTempDir(), testEntryPoint.getTypeName(),
+        Lists.newArrayList(moduleResource, testEntryPoint, someJsFunction,
+            jsFunctionInterfaceImplementation, someInterface),
+        new MinimalRebuildCache(), emptySet, JsOutputOption.DETAILED);
+    // Make sure the referenced class literals ends up beign included in the resulting JS.
+    String classliteralHolderVarName =
+        JjsUtils.mangleMemberName("com.google.gwt.lang.ClassLiteralHolder",
+        JjsUtils.classLiteralFieldNameFromJavahTypeSignatureName(
+            JjsUtils.javahSignatureFromName(someInterface.getTypeName())));
+    assertTrue(js.contains("var " + classliteralHolderVarName + " = "));
+  }
+
   public void testJsInteropNameCollision() throws Exception {
     MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache();
     File applicationDir = Files.createTempDir();
@@ -1452,7 +1517,6 @@
 
   public void checkIncrementalRecompile_classLiteralNewReference(JsOutputOption output)
       throws UnableToCompleteException, IOException, InterruptedException {
-    // Tests that when a superclass has a "inherits" a default method,
     MockJavaResource interfaceA =
         JavaResourceBase.createMockJavaResource(
             "com.foo.A",
@@ -1504,7 +1568,6 @@
 
   public void checkIncrementalRecompile_primitiveClassLiteralReference(JsOutputOption output)
       throws UnableToCompleteException, IOException, InterruptedException {
-    // Tests that when a superclass has a "inherits" a default method,
     MockJavaResource classA =
         JavaResourceBase.createMockJavaResource(
             "com.foo.A",