Emit correct code for @JsMethod varargs when arguments is shadowed.

Change-Id: I532157dadc2b017888025bedebb9000f2810f1ab
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/NameClashesFixer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/NameClashesFixer.java
index ca63dfe37..359b938 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/NameClashesFixer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/NameClashesFixer.java
@@ -18,11 +18,16 @@
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBlock;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
 import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap;
 import com.google.gwt.thirdparty.guava.common.collect.Maps;
 import com.google.gwt.thirdparty.guava.common.collect.Multimap;
@@ -222,8 +227,42 @@
   }
 
   public static void exec(JProgram program) {
+    // Rename variables that should not be shadowed.
+    new JModVisitor() {
+      @Override
+      public boolean visit(JMethod method, Context ctx) {
+        return !method.isJsniMethod() && method.isJsMethodVarargs();
+      }
+
+      @Override
+      public void endVisit(JLocal variable, Context ctx) {
+        maybeRename(variable);
+      }
+
+      @Override
+      public void endVisit(JParameter parameter, Context ctx) {
+        maybeRename(parameter);
+      }
+    }.accept(program);
+
+    // Resolve clashes.
     new FixNameClashesVisitor().accept(program);
   }
+
+  private static Set<String> unshadowableNames = ImmutableSet.of("arguments");
+
+  private static void maybeRename(JVariable variable) {
+    // In normal scenarios local variables from our Java programs will shadow existing JavaScript
+    // variables. The only exception is the implicit "arguments" variable which should not be
+    // shadowed.
+    // There is no need to care about collisions here as the JVariable object is what defines a
+    // variable, not its name; FixNameClashesVisitor, run immediately after, will ensure that names
+    // do not clash.
+    if (unshadowableNames.contains(variable.getName())) {
+      variable.setName("_" + variable.getName());
+    }
+  }
+
   private NameClashesFixer() {
   }
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index 26ca07c..d9897a6 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -30,11 +30,13 @@
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import javaemul.internal.annotations.DoNotInline;
+import jsinterop.annotations.JsMethod;
 import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsProperty;
 import jsinterop.annotations.JsType;
@@ -369,4 +371,34 @@
     final AbstractNativeType o = createAbstractNativeType("Hello");
     assertEquals("Hello", o.getTextContent());
   }
+
+  @JsMethod
+  private static List<String> singletonFrom(int i, String... arguments) {
+    // Make the second parameter varargs and pass it as a whole to trigger the arguments copying
+    // preamble.
+    return Arrays.asList(arguments).subList(i,i + 1);
+  }
+
+  @JsMethod
+  private static List<String> argumentsParameterClasher(int arguments, String... others) {
+    // Make the second parameter varargs and pass it as a whole to trigger the arguments copying
+    // preamble.
+    return Arrays.asList(others).subList(0, arguments);
+  }
+
+  @JsMethod
+  private static List<String> argumentsVariableClasher(int i, String... others) {
+    // Make the second parameter varargs and pass it as a whole to trigger the arguments copying
+    // preamble.
+    {
+      int arguments = 3;
+    }
+    return Arrays.asList(others).subList(0, i);
+  }
+
+ public void testVarargsNamedArguments() {
+    assertEquals("GoodBye", singletonFrom(1, "Hello", "GoodBye").get(0));
+    assertEquals("Hello", argumentsParameterClasher(1, "Hello", "GoodBye").get(0));
+    assertEquals("Hello", argumentsVariableClasher(1, "Hello", "GoodBye").get(0));
+  }
 }