Merge 1.5 branch changes.
$ svn merge -r 3587:HEAD https://google-web-toolkit.googlecode.com/svn/releases/1.5



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3630 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index b284fad..b3f3b9a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -997,7 +997,6 @@
     JExpression processExpression(FieldReference x) {
       SourceInfo info = makeSourceInfo(x);
       FieldBinding fieldBinding = x.binding;
-      JType type = (JType) typeMap.get(x.resolvedType);
       JField field;
       if (fieldBinding.declaringClass == null) {
         // probably array.length
@@ -1012,11 +1011,15 @@
       JExpression fieldRef = new JFieldRef(program, info, instance, field,
           currentClass);
 
-      /*
-       * Note, this may result in an invalid AST due to an LHS cast operation.
-       * We fix this up in FixAssignmentToUnbox.
-       */
-      return maybeCast(type, fieldRef);
+      if (x.genericCast != null) {
+        JType castType = (JType) typeMap.get(x.genericCast);
+        /*
+         * Note, this may result in an invalid AST due to an LHS cast operation.
+         * We fix this up in FixAssignmentToUnbox.
+         */
+        return maybeCast(castType, fieldRef);
+      }
+      return fieldRef;
     }
 
     JExpression processExpression(InstanceOfExpression x) {
@@ -1028,7 +1031,6 @@
 
     JExpression processExpression(MessageSend x) {
       SourceInfo info = makeSourceInfo(x);
-      JType type = (JType) typeMap.get(x.resolvedType);
       JMethod method = (JMethod) typeMap.get(x.binding);
 
       JExpression qualifier;
@@ -1064,7 +1066,11 @@
       // The arguments come first...
       addCallArgs(x.arguments, call, x.binding);
 
-      return maybeCast(type, call);
+      if (x.valueCast != null) {
+        JType castType = (JType) typeMap.get(x.valueCast);
+        return maybeCast(castType, call);
+      }
+      return call;
     }
 
     @SuppressWarnings("unused")
@@ -1197,6 +1203,10 @@
       JVariable variable = (JVariable) node;
 
       JExpression curRef = createVariableRef(info, variable, binding);
+      if (x.genericCast != null) {
+        JType castType = (JType) typeMap.get(x.genericCast);
+        curRef = maybeCast(castType, curRef);
+      }
 
       /*
        * Wackiness: JDT represents multiple field access as an array of fields,
@@ -1204,7 +1214,8 @@
        * otherBindings takes the current expression as a qualifier.
        */
       if (x.otherBindings != null) {
-        for (FieldBinding fieldBinding : x.otherBindings) {
+        for (int i = 0; i < x.otherBindings.length; ++i) {
+          FieldBinding fieldBinding = x.otherBindings[i];
           JField field;
           if (fieldBinding.declaringClass == null) {
             // probably array.length
@@ -1217,6 +1228,10 @@
             field = (JField) typeMap.get(fieldBinding);
           }
           curRef = new JFieldRef(program, info, curRef, field, currentClass);
+          if (x.otherGenericCasts != null && x.otherGenericCasts[i] != null) {
+            JType castType = (JType) typeMap.get(x.otherGenericCasts[i]);
+            curRef = maybeCast(castType, curRef);
+          }
         }
       }
 
@@ -1254,15 +1269,22 @@
        * instance. CreateThisRef should compute a "this" access of the
        * appropriate type, unless the field is static.
        */
+      JExpression result = null;
       if (x.syntheticAccessors != null) {
         JField field = (JField) variable;
         if (!field.isStatic()) {
           JExpression instance = createThisRef(info, field.getEnclosingType());
-          return new JFieldRef(program, info, instance, field, currentClass);
+          result = new JFieldRef(program, info, instance, field, currentClass);
         }
       }
-
-      return createVariableRef(info, variable, binding);
+      if (result == null) {
+        result = createVariableRef(info, variable, binding);
+      }
+      if (x.genericCast != null) {
+        JType castType = (JType) typeMap.get(x.genericCast);
+        result = maybeCast(castType, result);
+      }
+      return result;
     }
 
     JExpression processExpression(SuperReference x) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
index a17dbc4..9b1cbfe 100644
--- a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
+++ b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
@@ -63,11 +63,12 @@
   }
 
   private void addMember(Member member, String sig) {
-    if (!memberIdByName.containsKey(sig)) {
-      memberById.add(member);
-      int index = memberById.size() - 1;
-      memberIdByName.put(sig, new Integer(index));
+    if (member.isSynthetic()) {
+      return;
     }
+    int index = memberById.size();
+    memberById.add(member);
+    memberIdByName.put(sig, index);
   }
 
   private String getJsniSignature(Member member) {
@@ -148,7 +149,8 @@
 
   private void lazyInitTargetMembersUsingReflectionHelper(Class<?> targetClass,
       boolean addConstructors) {
-    // Start by analyzing the superclass recursively.
+    // Start by analyzing the superclass recursively; the concrete class will
+    // clobber on overrides.
     Class<?> superclass = targetClass.getSuperclass();
     if (superclass != null) {
       lazyInitTargetMembersUsingReflectionHelper(superclass, false);
@@ -158,11 +160,10 @@
     }
 
     if (addConstructors) {
-      Constructor<?>[] constructors = targetClass.getDeclaredConstructors();
-      for (Constructor<?> c : constructors) {
-        c.setAccessible(true);
-        String sig = getJsniSignature(c);
-        addMember(c, sig);
+      for (Constructor<?> ctor : targetClass.getDeclaredConstructors()) {
+        ctor.setAccessible(true);
+        String sig = getJsniSignature(ctor);
+        addMember(ctor, sig);
       }
     }
 
@@ -180,19 +181,17 @@
      */
 
     // Get the methods on this class/interface.
-    //
-    Method[] methods = targetClass.getDeclaredMethods();
-    for (int i = 0; i < methods.length; i++) {
-      methods[i].setAccessible(true);
-      String sig = getJsniSignature(methods[i]);
-      addMember(methods[i], sig);
+    for (Method method : targetClass.getDeclaredMethods()) {
+      method.setAccessible(true);
+      String sig = getJsniSignature(method);
+      addMember(method, sig);
     }
 
     // Get the fields on this class/interface.
     Field[] fields = targetClass.getDeclaredFields();
-    for (int i = 0; i < fields.length; i++) {
-      fields[i].setAccessible(true);
-      addMember(fields[i], fields[i].getName());
+    for (Field field : fields) {
+      field.setAccessible(true);
+      addMember(field, field.getName());
     }
   }
 }
diff --git a/distro-source/core/src/release_notes.html b/distro-source/core/src/release_notes.html
index ee3896a..37721ff 100644
--- a/distro-source/core/src/release_notes.html
+++ b/distro-source/core/src/release_notes.html
@@ -66,7 +66,7 @@
       </ul>
       <p>
         See the GWT issue tracker for
-        <a href="http://code.google.com/p/google-web-toolkit/issues/list?can=1&q=status%3AFixed%2CFixedNotReleased%20milestone%3A1_5_Final%20type%3Adefect&num=1000">
+        <a href="http://code.google.com/p/google-web-toolkit/issues/list?can=1&q=status%3AFixed%2CFixedNotReleased%20milestone%3A1_5_Final&num=1000">
         the complete list of bug fixes and enhancements</a> in this release.
       </p>
 
diff --git a/user/super/com/google/gwt/emul/java/lang/Integer.java b/user/super/com/google/gwt/emul/java/lang/Integer.java
index 28e57ee..6a4c1ad 100644
--- a/user/super/com/google/gwt/emul/java/lang/Integer.java
+++ b/user/super/com/google/gwt/emul/java/lang/Integer.java
@@ -184,19 +184,20 @@
 
     final int bufSize = 33;
     char[] buf = new char[bufSize];
+    char[] digits = __Digits.digits;
     int pos = bufSize - 1;
     if (value >= 0) {
       while (value >= radix) {
-        buf[pos--] = __Digits.digits[value % radix];
+        buf[pos--] = digits[value % radix];
         value /= radix;
       }
-      buf[pos] = __Digits.digits[value];
+      buf[pos] = digits[value];
     } else {
       while (value <= -radix) {
-        buf[pos--] = __Digits.digits[-(value % radix)];
+        buf[pos--] = digits[-(value % radix)];
         value /= radix;
       }
-      buf[pos--] = __Digits.digits[-value];
+      buf[pos--] = digits[-value];
       buf[pos] = '-';
     }
     return String.__valueOf(buf, pos, bufSize);
@@ -227,19 +228,20 @@
     final int bufSize = 32 / shift;
     int bitMask = (1 << shift) - 1;
     char[] buf = new char[bufSize];
+    char[] digits = __Digits.digits;
     int pos = bufSize - 1;
     if (value >= 0) {
       while (value > bitMask) {
-        buf[pos--] = __Digits.digits[value & bitMask];
+        buf[pos--] = digits[value & bitMask];
         value >>= shift;
       }
     } else {
       while (pos > 0) {
-        buf[pos--] = __Digits.digits[value & bitMask];
+        buf[pos--] = digits[value & bitMask];
         value >>= shift;
       }
     }
-    buf[pos] = __Digits.digits[value & bitMask];
+    buf[pos] = digits[value & bitMask];
     return String.__valueOf(buf, pos, bufSize);
   }
 
diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java
index fb7f338..d544038 100644
--- a/user/super/com/google/gwt/emul/java/lang/Long.java
+++ b/user/super/com/google/gwt/emul/java/lang/Long.java
@@ -225,21 +225,22 @@
 
     final int bufSize = 65;
     char[] buf = new char[bufSize];
+    char[] digits = __Digits.digits;
     int pos = bufSize - 1;
     // Cache a converted version for performance (pure long ops are faster).
     long radix = intRadix;
     if (value >= 0) {
       while (value >= radix) {
-        buf[pos--] = __Digits.digits[(int) (value % radix)];
+        buf[pos--] = digits[(int) (value % radix)];
         value /= radix;
       }
-      buf[pos] = __Digits.digits[(int) value];
+      buf[pos] = digits[(int) value];
     } else {
       while (value <= -radix) {
-        buf[pos--] = __Digits.digits[(int) -(value % radix)];
+        buf[pos--] = digits[(int) -(value % radix)];
         value /= radix;
       }
-      buf[pos--] = __Digits.digits[(int) -value];
+      buf[pos--] = digits[(int) -value];
       buf[pos] = '-';
     }
     return String.__valueOf(buf, pos, bufSize);
@@ -292,19 +293,20 @@
     final int bufSize = 64 / shift;
     long bitMask = (1 << shift) - 1;
     char[] buf = new char[bufSize];
+    char[] digits = __Digits.digits;
     int pos = bufSize - 1;
     if (value >= 0) {
       while (value > bitMask) {
-        buf[pos--] = __Digits.digits[(int) (value & bitMask)];
+        buf[pos--] = digits[(int) (value & bitMask)];
         value >>= shift;
       }
     } else {
       while (pos > 0) {
-        buf[pos--] = __Digits.digits[(int) (value & bitMask)];
+        buf[pos--] = digits[(int) (value & bitMask)];
         value >>= shift;
       }
     }
-    buf[pos] = __Digits.digits[(int) (value & bitMask)];
+    buf[pos] = digits[(int) (value & bitMask)];
     return String.__valueOf(buf, pos, bufSize);
   }
 
diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
index 79e0b26..b079d19 100644
--- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
+++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
@@ -24,6 +24,7 @@
 import com.google.gwt.dev.jjs.test.CoverageTest;
 import com.google.gwt.dev.jjs.test.EnhancedForLoopTest;
 import com.google.gwt.dev.jjs.test.EnumsTest;
+import com.google.gwt.dev.jjs.test.GenericCastTest;
 import com.google.gwt.dev.jjs.test.HostedTest;
 import com.google.gwt.dev.jjs.test.InnerClassTest;
 import com.google.gwt.dev.jjs.test.InnerOuterSuperTest;
@@ -62,6 +63,7 @@
     suite.addTestSuite(CoverageTest.class);
     suite.addTestSuite(EnhancedForLoopTest.class);
     suite.addTestSuite(EnumsTest.class);
+    suite.addTestSuite(GenericCastTest.class);
     suite.addTestSuite(HostedTest.class);
     suite.addTestSuite(InnerClassTest.class);
     suite.addTestSuite(InnerOuterSuperTest.class);
diff --git a/user/test/com/google/gwt/dev/jjs/test/GenericCastTest.java b/user/test/com/google/gwt/dev/jjs/test/GenericCastTest.java
new file mode 100644
index 0000000..228c759
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/test/GenericCastTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2008 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.junit.client.GWTTestCase;
+
+/**
+ * Tests for appropriate generation of type checks when generic methods/fields
+ * are referenced. This is actually testing GenerateJavaAST's appropriate use of
+ * maybeCast(). We test such references in so many contexts (field, method, as
+ * qualifier, etc) to ensures we cover all the code paths, because JDT has a lot
+ * of representation variants with respect to fields (SingleNameReference,
+ * QualifiedNameReference, FieldAccess).
+ */
+@SuppressWarnings("unused")
+public class GenericCastTest extends GWTTestCase {
+
+  /**
+   * Always contains an Object internally, the parameterization is a lie.
+   */
+  static class Liar<T> {
+    @SuppressWarnings("unchecked")
+    public final T value = (T) new Object();
+
+    public T get() {
+      return value;
+    }
+  }
+  static class LiarFoo extends Liar<Foo> {
+    public void testOuterField() {
+      new Runnable() {
+        public void run() {
+          // Should succeed
+          Object a = value;
+
+          try {
+            Foo b = value;
+            fail("Expected ClassCastException 1");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = value.bar;
+            fail("Expected ClassCastException 2");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = value.baz();
+            fail("Expected ClassCastException 3");
+          } catch (ClassCastException expected) {
+          }
+        }
+      }.run();
+    }
+
+    public void testOuterMethod() {
+      new Runnable() {
+        public void run() {
+          // Should succeed
+          Object a = get();
+
+          try {
+            Foo b = get();
+            fail("Expected ClassCastException 1");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = get().bar;
+            fail("Expected ClassCastException 2");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = get().baz();
+            fail("Expected ClassCastException 3");
+          } catch (ClassCastException expected) {
+          }
+        }
+      }.run();
+    }
+
+    public void testSuperField() {
+      // Should succeed
+      Object a = value;
+
+      try {
+        Foo b = value;
+        fail("Expected ClassCastException 1");
+      } catch (ClassCastException expected) {
+      }
+      try {
+        String s = value.bar;
+        fail("Expected ClassCastException 2");
+      } catch (ClassCastException expected) {
+      }
+      try {
+        String s = value.baz();
+        fail("Expected ClassCastException 3");
+      } catch (ClassCastException expected) {
+      }
+    }
+
+    public void testSuperMethod() {
+      // Should succeed
+      Object a = get();
+
+      try {
+        Foo b = get();
+        fail("Expected ClassCastException 1");
+      } catch (ClassCastException expected) {
+      }
+      try {
+        String s = get().bar;
+        fail("Expected ClassCastException 2");
+      } catch (ClassCastException expected) {
+      }
+      try {
+        String s = get().baz();
+        fail("Expected ClassCastException 3");
+      } catch (ClassCastException expected) {
+      }
+    }
+
+    void testInternalAccess() {
+      new Runnable() {
+        public void run() {
+          Object a = get();
+          try {
+            Foo b = get();
+            fail("Expected ClassCastException 5a");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = get().bar;
+            fail("Expected ClassCastException 5b");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = get().baz();
+            fail("Expected ClassCastException 5c");
+          } catch (ClassCastException expected) {
+          }
+
+          Object c = value;
+          try {
+            Foo d = value;
+            fail("Expected ClassCastException 6a");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = value.bar;
+            fail("Expected ClassCastException 6b");
+          } catch (ClassCastException expected) {
+          }
+          try {
+            String s = value.baz();
+            fail("Expected ClassCastException 6c");
+          } catch (ClassCastException expected) {
+          }
+        }
+      }.run();
+    }
+  }
+
+  static class Foo {
+    public String bar = "w00t";
+
+    public String baz() {
+      return bar;
+    }
+  }
+
+  public String getModuleName() {
+    return "com.google.gwt.dev.jjs.CompilerSuite";
+  }
+
+  /**
+   * Test explicit references through a local variable qualifier.
+   */
+  public void testExplicitField() {
+    Liar<Foo> bug = new Liar<Foo>();
+
+    // Should succeed
+    Object a = bug.value;
+
+    try {
+      Foo b = bug.value;
+      fail("Expected ClassCastException 1");
+    } catch (ClassCastException expected) {
+    }
+    try {
+      String s = bug.value.bar;
+      fail("Expected ClassCastException 2");
+    } catch (ClassCastException expected) {
+    }
+    try {
+      String s = bug.value.baz();
+      fail("Expected ClassCastException 3");
+    } catch (ClassCastException expected) {
+    }
+  }
+
+  /**
+   * Test explicit references through a local variable qualifier.
+   */
+  public void testExplicitMethod() {
+    Liar<Foo> bug = new Liar<Foo>();
+
+    // Should succeed
+    Object a = bug.get();
+
+    try {
+      Foo b = bug.get();
+      fail("Expected ClassCastException 1");
+    } catch (ClassCastException expected) {
+    }
+    try {
+      String s = bug.get().bar;
+      fail("Expected ClassCastException 2");
+    } catch (ClassCastException expected) {
+    }
+    try {
+      String s = bug.get().baz();
+      fail("Expected ClassCastException 3");
+    } catch (ClassCastException expected) {
+    }
+  }
+
+  /**
+   * Test implicit references through an outer class.
+   */
+  public void testOuterField() {
+    new LiarFoo().testSuperField();
+  }
+
+  /**
+   * Test implicit references through an outer class.
+   */
+  public void testOuterMethod() {
+    new LiarFoo().testSuperMethod();
+  }
+
+  /**
+   * Test implicit references through a super class.
+   */
+  public void testSuperField() {
+    new LiarFoo().testSuperField();
+  }
+
+  /**
+   * Test implicit references through a super class.
+   */
+  public void testSuperMethod() {
+    new LiarFoo().testSuperMethod();
+  }
+}