Fix implementation of @JsExport on constructors and add test cases.

When @JsExport is annotated on a class:
1. If the class has only one non-private constructor
(explicit or implicit), the constructor is exported automatically.
2. If the class has more than one non-private constructor,
each non-private constructor has to be annotated by
@JsExport("foo") or @JsNoExport.

Change-Id: I1d6ae14193ac7164d16d966a517b4d2155885e96
diff --git a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
index 45b800c..18503fb 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -19,6 +19,7 @@
 import com.google.gwt.dev.util.InstalledHelpInfo;
 import com.google.gwt.dev.util.collect.Stack;
 import com.google.gwt.thirdparty.guava.common.base.Strings;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
 
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -41,6 +42,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -67,6 +69,8 @@
       + "static final fields in public classes.";
   public static final String ERR_EITHER_JSEXPORT_JSNOEXPORT =
       "@JsExport and @JsNoExport is not allowed at the same time.";
+  public static final String ERR_EXPLICIT_JSEXPORT_OR_JSNOEXPORT_ON_CONSTRUCTORS =
+      "@JsExport or @JsNoExport should be set on constructors if a class is @JsExported and has more than one non private constructors";
   public static final String ERR_JSPROPERTY_ONLY_BEAN_OR_FLUENT_STYLE_NAMING =
       "@JsProperty is only allowed on JavaBean-style or fluent-style named methods";
   public static final String ERR_JSEXPORT_ON_ENUMERATION =
@@ -265,6 +269,35 @@
       }
     }
 
+    private void checkJsExport(ReferenceBinding rb) {
+      if (JdtUtil.getAnnotation(rb, JsInteropUtil.JSEXPORT_CLASS) == null) {
+        return;
+      }
+      List<MethodBinding> publicConstructors = getPublicConstructors(rb);
+      if (publicConstructors.size() <= 1) {
+        return;
+      }
+      for (MethodBinding mb : publicConstructors) {
+        AnnotationBinding jsexportAnnotation =
+            JdtUtil.getAnnotation(mb, JsInteropUtil.JSEXPORT_CLASS);
+        AnnotationBinding jsnoexportAnnotation =
+            JdtUtil.getAnnotation(mb, JsInteropUtil.JSNOEXPORT_CLASS);
+        if (jsexportAnnotation == null && jsnoexportAnnotation == null) {
+          errorOn(mb, ERR_EXPLICIT_JSEXPORT_OR_JSNOEXPORT_ON_CONSTRUCTORS);
+        }
+      }
+    }
+
+    private List<MethodBinding> getPublicConstructors(ReferenceBinding rb) {
+      List<MethodBinding> publicConstructors = Lists.newArrayList();
+      for (MethodBinding mb : rb.methods()) {
+        if (mb.isConstructor() && mb.isPublic()) {
+          publicConstructors.add(mb);
+        }
+      }
+      return publicConstructors;
+    }
+
     private void checkJsExport(MethodBinding mb) {
       if (JdtUtil.getAnnotation(mb, JsInteropUtil.JSEXPORT_CLASS) != null) {
         boolean isStatic = mb.isConstructor() || mb.isStatic();
@@ -349,7 +382,7 @@
 
     private ClassState checkType(TypeDeclaration type) {
       SourceTypeBinding binding = type.binding;
-
+      checkJsExport(binding);
       if (isJsType(type.binding)) {
         checkJsType(type, type.binding);
         return ClassState.JSTYPE;
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 17d2dd5..7148b4e 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
@@ -2739,31 +2739,16 @@
       String lastProvidedNamespace = null;
       boolean createdClinit = false;
 
-      // export 1 constructor
-      JConstructor ctor = null;
       for (JMethod m : x.getMethods()) {
-        if (m instanceof JConstructor) {
-          if (!((JConstructor) m).isDefaultConstructor()
-              && typeOracle.isExportedMethod(m)) {
-            ctor = (JConstructor) m;
-            break;
-          }
-        }
-      }
-
-      for (JMethod m : x.getMethods()) {
-        if (m instanceof JConstructor && m != ctor) {
-          continue;
-        }
         // static functions or constructors may be exported
-        if (m == ctor && !m.isPrivate() ||
-            (m.isStatic()) && typeOracle.isExportedMethod(m)) {
+        if (typeOracle.isExportedMethod(m)) {
 
           createdClinit = maybeHoistClinit(exportStmts, createdClinit, maybeCreateClinitCall(m));
-          JsExpression exportRhs = createJsInteropBridgeMethod(m,
-              names.get(m).makeRef(m.getSourceInfo()));
+          JsExpression exportRhs =
+              createJsInteropBridgeMethod(m, names.get(m).makeRef(m.getSourceInfo()));
           String exportName = m.getQualifiedExportName();
-          lastProvidedNamespace = exportMember(x, globalStmts, lastProvidedNamespace, exportRhs, exportName);
+          lastProvidedNamespace =
+              exportMember(x, globalStmts, lastProvidedNamespace, exportRhs, exportName);
         }
       }
 
@@ -2774,11 +2759,12 @@
                 + " is discouraged. Due to the way exporting works, the value of the exported field"
                 + " will not be reflected across Java&JavaScript border.");
           }
-          createdClinit = maybeHoistClinit(exportStmts, createdClinit,
-              maybeCreateClinitCall(f, true));
+          createdClinit =
+              maybeHoistClinit(exportStmts, createdClinit, maybeCreateClinitCall(f, true));
           JsNameRef exportRhs = names.get(f).makeRef(f.getSourceInfo());
           String exportName = f.getQualifiedExportName();
-          lastProvidedNamespace = exportMember(x, globalStmts, lastProvidedNamespace, exportRhs, exportName);
+          lastProvidedNamespace =
+              exportMember(x, globalStmts, lastProvidedNamespace, exportRhs, exportName);
         }
       }
     }
diff --git a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
index 81f6b24..efefe4e 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
@@ -433,6 +433,77 @@
     shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_JSEXPORT_ON_ENUMERATION);
   }
 
+  public void testJsExportOnConstructors() {
+    StringBuilder goodCode = new StringBuilder();
+    goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    goodCode.append("public class Buggy {\n");
+    // A constructor JsExported without explicit symbol is fine here.
+    // Leave it to NameConflictionChecker.
+    goodCode.append("  @JsExport public Buggy() { }\n");
+    goodCode.append("  @JsExport(\"buggy1\") public Buggy(int a) { }\n");
+    goodCode.append("  public Buggy(int a, int b) { }\n");
+    goodCode.append("}");
+
+    shouldGenerateNoError(goodCode);
+  }
+
+  public void testJsExportOnClassWithDefaultConstructor() {
+    StringBuilder goodCode = new StringBuilder();
+    goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    goodCode.append("@JsExport public class Buggy {}");
+
+    shouldGenerateNoError(goodCode);
+  }
+
+  public void testJsExportOnClassWithExplicitConstructor() {
+    StringBuilder goodCode = new StringBuilder();
+    goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    goodCode.append("@JsExport public class Buggy {\n");
+    goodCode.append("  public Buggy() { }");
+    goodCode.append("}");
+
+    shouldGenerateNoError(goodCode);
+  }
+
+  public void testJsExportOnClassWithOnePublicConstructor() {
+    StringBuilder goodCode = new StringBuilder();
+    goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    goodCode.append("@JsExport public class Buggy {\n");
+    goodCode.append("  public Buggy() { }\n");
+    goodCode.append("  private Buggy(int a) { }\n");
+    goodCode.append("  protected Buggy(int a, int b) { }\n");
+    goodCode.append("  Buggy(int a, int b, int c) { }\n");
+    goodCode.append("}");
+
+    shouldGenerateNoError(goodCode);
+  }
+
+  public void testJsExportOnClassWithMultipleConstructors() {
+    StringBuilder goodCode = new StringBuilder();
+    goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    goodCode.append("import com.google.gwt.core.client.js.JsNoExport;\n");
+    goodCode.append("@JsExport public class Buggy {\n");
+    goodCode.append("  @JsExport(\"Buggy1\") public Buggy() { }\n");
+    goodCode.append("  @JsExport(\"Buggy2\") public Buggy(int a) { }\n");
+    goodCode.append("  @JsExport public Buggy(int a, int b) { }\n");
+    goodCode.append("  @JsNoExport public Buggy(int a, int b, int c) { }\n");
+    goodCode.append("}");
+
+    shouldGenerateNoError(goodCode);
+  }
+
+  public void testJsExportNotOnClassWithMultipleConstructors() {
+    StringBuilder buggyCode = new StringBuilder();
+    buggyCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+    buggyCode.append("@JsExport public class Buggy {\n");
+    buggyCode.append("  public Buggy() { }\n");
+    buggyCode.append("  @JsExport(\"foo\") public Buggy(int a) { }\n");
+    buggyCode.append("}");
+
+    shouldGenerateError(buggyCode, "Line 3: "
+        + JSORestrictionsChecker.ERR_EXPLICIT_JSEXPORT_OR_JSNOEXPORT_ON_CONSTRUCTORS);
+  }
+
   public void testJsExportNotOnNonPublicClass() {
     StringBuilder buggyCode = new StringBuilder();
     buggyCode.append("import com.google.gwt.core.client.js.JsExport;\n");
diff --git a/user/test/com/google/gwt/core/client/interop/JsExportTest.java b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
index 4681da0..57c6475 100644
--- a/user/test/com/google/gwt/core/client/interop/JsExportTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
@@ -95,6 +95,58 @@
     return obj.getInstance();
   }-*/;
 
+  public void testExportClass_implicitConstructor() {
+    assertNotNull(createMyExportedClassWithImplicitConstructor());
+  }
+
+  private native Object createMyExportedClassWithImplicitConstructor() /*-{
+    return new $wnd.woo.MyExportedClassWithImplicitConstructor();
+  }-*/;
+
+  public void testExportClass_multipleConstructors() {
+    assertEquals(3, getSumByDefaultConstructor());
+    assertEquals(30, getSumByConstructor());
+  }
+
+  private native int getSumByDefaultConstructor() /*-{
+    var obj = new $wnd.MyClassConstructor1();
+    return obj.sum();
+  }-*/;
+
+  private native int getSumByConstructor() /*-{
+    var obj = new $wnd.MyClassConstructor2(10, 20);
+    return obj.sum();
+  }-*/;
+
+  public void testExportClass_instanceOf() {
+    assertTrue(createMyExportedClassWithMultipleConstructors1()
+        instanceof MyExportedClassWithMultipleConstructors);
+    assertTrue(createMyExportedClassWithMultipleConstructors2()
+        instanceof MyExportedClassWithMultipleConstructors);
+  }
+
+  private native Object createMyExportedClassWithMultipleConstructors1() /*-{
+    return new $wnd.MyClassConstructor1();
+  }-*/;
+
+  private native Object createMyExportedClassWithMultipleConstructors2() /*-{
+    return new $wnd.MyClassConstructor2(10, 20);
+  }-*/;
+
+  public void testExportConstructors() {
+    assertEquals(4, getFooByConstructorWithExportSymbol());
+    assertNull(getNotExportedConstructor());
+  }
+
+  private native int getFooByConstructorWithExportSymbol() /*-{
+    var obj = new $wnd.MyClassExportsConstructors1(2);
+    return obj.foo();
+  }-*/;
+
+  private native Object getNotExportedConstructor() /*-{
+    return $wnd.woo.MyClassExportsConstructors;
+  }-*/;
+
   public void testExportedField() {
     assertEquals(100, MyExportedClass.EXPORTED_1);
     assertEquals(100, getExportedField());
diff --git a/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java b/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java
new file mode 100644
index 0000000..2384b65
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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.core.client.interop;
+
+import com.google.gwt.core.client.js.JsExport;
+import com.google.gwt.core.client.js.JsType;
+
+/**
+ * A test class that exhibits a variety of @JsExports on constructors.
+ */
+@JsType
+public class MyClassExportsConstructors {
+  private int a;
+
+  @JsExport("MyClassExportsConstructors1")
+  public MyClassExportsConstructors(int a) {
+    this.a = a;
+  }
+
+  public MyClassExportsConstructors() {
+    a = 1;
+  }
+
+  public int foo() {
+    return a * 2;
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/interop/MyExportedClassWithImplicitConstructor.java b/user/test/com/google/gwt/core/client/interop/MyExportedClassWithImplicitConstructor.java
new file mode 100644
index 0000000..f4d62a5
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyExportedClassWithImplicitConstructor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 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.core.client.interop;
+
+import com.google.gwt.core.client.js.JsExport;
+
+/**
+ * A class which only has implicit default constructor and is annotated by "JsExport".
+ * Its default constructor is exported automatically.
+ */
+@JsExport
+public class MyExportedClassWithImplicitConstructor {
+
+}
diff --git a/user/test/com/google/gwt/core/client/interop/MyExportedClassWithMultipleConstructors.java b/user/test/com/google/gwt/core/client/interop/MyExportedClassWithMultipleConstructors.java
new file mode 100644
index 0000000..8bc60fc
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyExportedClassWithMultipleConstructors.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 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.core.client.interop;
+
+import com.google.gwt.core.client.js.JsExport;
+import com.google.gwt.core.client.js.JsType;
+
+/**
+ * A class which has two public constructors and is annotated by "JsExport". These two constructors
+ * have to be annotated by "JsExport" with explicit symbol or by "JsNoExport".
+ */
+@JsExport
+@JsType
+public class MyExportedClassWithMultipleConstructors {
+  public int a;
+  public int b;
+
+  @JsExport("MyClassConstructor1")
+  public MyExportedClassWithMultipleConstructors() {
+    a = 1;
+    b = 2;
+  }
+
+  @JsExport("MyClassConstructor2")
+  public MyExportedClassWithMultipleConstructors(int a, int b) {
+    this.a = a;
+    this.b = b;
+  }
+
+  public int sum() {
+    return a + b;
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/interop/StaticInitializerVirtualMethod.java b/user/test/com/google/gwt/core/client/interop/StaticInitializerVirtualMethod.java
index b603d96..2cd3676 100644
--- a/user/test/com/google/gwt/core/client/interop/StaticInitializerVirtualMethod.java
+++ b/user/test/com/google/gwt/core/client/interop/StaticInitializerVirtualMethod.java
@@ -29,7 +29,6 @@
     return STATIC;
   }
 
-  // only explicit constructors are exported
   public StaticInitializerVirtualMethod() {
   }
 }