Do not honor JsNames if -generateJsInteropExports is not set.

Members that were not exported due to missing -generateJsInteropExports
were still being named according to their JsName, which allowed direct access
through the name but did not prevent optimizing them away. Using not exported names
would seem to work when run in -draft or SDM but would break in optimized mode, making
it time consuming to detect these errors.

After this patch only actually exported members get their JsNames making it a more
consistent behaviour between draft and optimized compiles.

Bug: #9369
Bug-Link: http://github.com/gwtproject/gwt/issues/9369
Change-Id: I0c2340db319ceb307a973a7c997d499b95125e20
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 4b9f189..41f4bda 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -199,13 +199,13 @@
    * an existing non-JsMember inside a class.
    */
   public boolean exposesNonJsMember() {
-    if (isInterfaceMethod() || getJsMemberType() == JsMemberType.NONE) {
+    if (isInterfaceMethod() || !JjsUtils.exposesJsName(this)) {
       return false;
     }
 
     boolean hasNonJsMemberParent = false;
     for (JMethod overriddenMethod : overriddenMethods) {
-      if (overriddenMethod.getJsMemberType() == JsMemberType.NONE) {
+      if (!JjsUtils.exposesJsName(overriddenMethod)) {
         hasNonJsMemberParent = true;
       }
       if (overriddenMethod.exposesNonJsMember()) {
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 15493d3..4f01054 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
@@ -245,8 +245,7 @@
       if (x.isStatic()) {
         jsName = topScope.declareName(mangleName(x), x.getName());
       } else {
-        jsName =
-            x.getJsMemberType() != JsMemberType.NONE
+        jsName = JjsUtils.exposesJsName(x)
                 ? scopeStack.peek().declareUnobfuscatableName(x.getJsName())
                 : scopeStack.peek().declareName(mangleName(x), x.getName());
       }
@@ -370,8 +369,7 @@
       String name = x.getName();
       if (x.needsDynamicDispatch()) {
         if (polymorphicNames.get(x) == null) {
-          JsName polyName =
-              x.getJsMemberType() != JsMemberType.NONE
+          JsName polyName = JjsUtils.exposesJsName(x)
                   ? interfaceScope.declareUnobfuscatableName(x.getJsName())
                   : interfaceScope.declareName(mangleNameForPoly(x), name);
           polymorphicNames.put(x, polyName);
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 da9121f..59dc479 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
@@ -626,6 +626,19 @@
     return getNativeSuperClassOrNull(superClass);
   }
 
+  /**
+   * Whether or not to use the JsName when implementing this member.
+   *
+   * <p>A member should only expose a JsName when a JsName has been assigned and the compilation
+   * has been configured to honor those names.
+   * */
+  public static boolean exposesJsName(JMember member) {
+    // JsFunction interfaces and  implementations do not have JsNames but canBeReferencedExternally
+    // or canBeImplementedExternally.
+    return member.getJsMemberType() != JsMemberType.NONE
+        && (member.canBeImplementedExternally() || member.canBeReferencedExternally());
+  }
+
   private JjsUtils() {
   }
 }
\ No newline at end of file
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java
index b15ce75..4553093 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java
@@ -67,7 +67,7 @@
     String[] methodNames = getTraceJava();
 
     StackTraceElement[] expectedTrace = new StackTraceElement[] {
-        createSTE(methodNames[0], "Throwable.java", 68),
+        createSTE(methodNames[0], "Throwable.java", 67),
         createSTE(methodNames[1], "Exception.java", 29),
         createSTE(methodNames[2], "StackTraceExamples.java", 57),
         createSTE(methodNames[3], "StackTraceExamples.java", 52),
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
index 4d86bfc..333547a 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
@@ -88,13 +88,24 @@
       }
 
       return [
-        @StackTraceCreator::getFunctionName(*)(arguments.callee),
-        @StackTraceCreator::getFunctionName(*)(arguments.callee.caller),
+        @StackTraceExamples::getFunctionName(*)(arguments.callee),
+        @StackTraceExamples::getFunctionName(*)(arguments.callee.caller),
       ];
     }
     return native1();
   }-*/;
 
+  private static native String getFunctionName(JavaScriptObject fn) /*-{
+    return fn.name || (fn.name = @StackTraceExamples::extractFunctionName(*)(fn.toString()));
+  }-*/;
+
+  // Visible for testing
+  static native String extractFunctionName(String fnName) /*-{
+    var fnRE = /function(?:\s+([\w$]+))?\s*\(/;
+    var match = fnRE.exec(fnName);
+    return (match && match[1]) || "anonymous";
+  }-*/;
+
   // Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko)
   // Chrome/25.0.1364.97 Safari/537.22
   // Modified to test 'xxx [as yyy]' and 'xxx.yyy' scenarios
diff --git a/user/test/com/google/gwt/dev/jjs/test/NoGenerateJsInteropExportsTest.java b/user/test/com/google/gwt/dev/jjs/test/NoGenerateJsInteropExportsTest.java
new file mode 100644
index 0000000..073d63d
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/test/NoGenerateJsInteropExportsTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.test;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+import com.google.gwt.junit.client.GWTTestCase;
+
+import jsinterop.annotations.JsMethod;
+import jsinterop.annotations.JsType;
+
+/**
+ * Tests for JsInterop that require -nogenerateJsInteropExports.
+ */
+@DoNotRunWith(Platform.Devel)
+public class NoGenerateJsInteropExportsTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.dev.jjs.CompilerSuite";
+  }
+
+  @JsType
+  static class A {
+    @JsMethod(name = "method")
+    public void m() {
+    }
+  }
+
+  @JsType(isNative = true)
+  interface ObjectWithMethod {
+    void method();
+  }
+
+  public void testJsMethodNameNotHonored() {
+    Object o = new A();
+    try {
+      // As this test runs with -nogenerateJsInteropExports, all non native types
+      // should not respect methods JsNames.
+      ((ObjectWithMethod) o).method();
+      fail("Should have failed");
+    } catch (JavaScriptException expected) {
+    }
+  }
+}