Merging /releases/1.5/@r3630:3863 into /releases/1.6/

svn merge -r3630:3713 https://google-web-toolkit.googlecode.com/svn/releases/1.5 .
svn merge -r3714:3863 https://google-web-toolkit.googlecode.com/svn/releases/1.5 .



git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@3864 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index eaab6ad..3a86ae0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -116,6 +116,9 @@
             && !stmts.isEmpty()) {
           // clinit() calls cannot be inlined unless they are empty
           possibleToInline = false;
+        } else if (!body.locals.isEmpty()) {
+          // methods with local variables cannot be inlined
+          possibleToInline = false;
         } else {
           JMultiExpression multi = createMultiExpressionFromBody(body,
               ignoringReturnValueFor == x);
@@ -474,6 +477,7 @@
       this.method = method;
     }
 
+    @Override
     public void endVisit(JMethodCall x, Context ctx) {
       if (x.getTarget() == method) {
         isRecursive = true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index a9e3662..2d39be2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -479,7 +479,7 @@
     @Override
     public boolean visit(JBinaryOperation x, Context ctx) {
       // special string concat handling
-      if (x.getOp() == JBinaryOperator.ADD
+      if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD)
           && x.getType() == program.getTypeJavaLangString()) {
         rescueByConcat(x.getLhs().getType());
         rescueByConcat(x.getRhs().getType());
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 10daca0..65ed0ab 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -650,9 +650,13 @@
      * Find a replacement method. If the original method is abstract, this will
      * return the leaf, final implementation of the method. If the method is
      * already concrete, but enclosed by an abstract type, the overriding method
-     * from the leaf concrete type will be returned.
+     * from the leaf concrete type will be returned. If the method is static,
+     * return <code>null</code> no matter what.
      */
     private JMethod getSingleConcreteMethod(JMethod method) {
+      if (method.isStatic()) {
+        return null;
+      }
       if (getSingleConcreteType(method.getEnclosingType()) != null) {
         return getSingleConcrete(method, overriders);
       } else {
diff --git a/distro-source/core/src/release_notes.html b/distro-source/core/src/release_notes.html
index 37721ff..37c804b 100644
--- a/distro-source/core/src/release_notes.html
+++ b/distro-source/core/src/release_notes.html
@@ -29,6 +29,7 @@
       <h1>Google Web Toolkit Release Notes</h1>
       <ul>
 		    <li><a href="#Release_Notes_Current">@GWT_VERSION@</a></li>
+		    <li><a href="#Release_Notes_1_5_2">1.5.2</a></li>
 		    <li><a href="#Release_Notes_1_5_1">1.5.1 (RC2)</a></li>
 		    <li><a href="#Release_Notes_1_5_0">1.5.0 (RC)</a></li>
 		    <li><a href="#Release_Notes_1_4_60">1.4.60</a></li>
@@ -46,6 +47,25 @@
       <hr/>
       <a name="Release_Notes_Current"></a>
       <h2>Release Notes for @GWT_VERSION@</h2>
+      <h3>Fixed Issues</h3>
+      <ul>
+        <li>RPC requests no longer fail on the embedded Android web browser</li>
+        <li>Leaf <code>TreeItems</code> now line up with their non-leaf siblings</li>
+        <li>Removing the last child node from a <code>TreeItem</code> no longer creates extra margins on the left</li>
+        <li><code>HTTPRequest</code> no longer uses POST instead of GET on some IE installs because of incorrect XHR selection</li>
+        <li>Compiler now uses a more reliable check to prevent methods with local variables from being inlined</li>
+        <li><code>getAbsoluteTop()/Left()</code> can no longer return non-integral values</li>
+        <li><code>Time.valueOf()</code> no longer fails to parse <code>"08:00:00"</code> or incorrectly accepts <code>"0xC:0xB:0xA"</code>.</li>
+      </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_3&num=1000">
+        the complete list of bug fixes and enhancements</a> in this release.
+      </p>
+
+      <hr/>
+      <a name="Release_Notes_1_5_2"></a>
+      <h2>Release Notes for 1.5.2</h2>
       <h3>Potentially breaking changes and fixes</h3>
       <ul>
         <li><code>History.onHistoryChanged()</code> has been added back (it was missing from 1.5 RC2) but is now deprecated.  Application startup should be handled by calling the new <code>History.fireCurrentHistoryState()</code>.</li>
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
index 1fa5461..0d1d36b 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -26,9 +26,10 @@
     // so we use getBoundingClientRect() whenever possible (but it's not
     // supported on older versions). If changing this code, make sure to check
     // the museum entry for issue 1932.
+    // (x) | 0 is used to coerce the value to an integer
     if (Element.prototype.getBoundingClientRect) {
-      return elem.getBoundingClientRect().left +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft;
+      return (elem.getBoundingClientRect().left +
+        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft) | 0;
     } else {
       // We cannot use DOMImpl here because offsetLeft/Top return erroneous
       // values when overflow is not visible.  We have to difference screenX
@@ -46,9 +47,10 @@
     // so we use getBoundingClientRect() whenever possible (but it's not
     // supported on older versions). If changing this code, make sure to check
     // the museum entry for issue 1932.
+    // (x) | 0 is used to coerce the value to an integer
     if (Element.prototype.getBoundingClientRect) {
-      return elem.getBoundingClientRect().top +
-        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop;
+      return (elem.getBoundingClientRect().top +
+        @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop) | 0;
     } else {
       // We cannot use DOMImpl here because offsetLeft/Top return erroneous
       // values when overflow is not visible.  We have to difference screenX
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java
index 7a98120..25e7da2 100644
--- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java
@@ -33,12 +33,10 @@
 import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.Map.Entry;
-import java.util.regex.Pattern;
 
 /**
  * Abstract base functionality for <code>MessagesInterfaceCreator</code> and
@@ -50,20 +48,32 @@
 
     @Override
     public String format(String key) {
-      if (methodNames.contains(key)) {
+      while (methodNames.contains(key)) {
         key += "_dup";
-        return format(key);
-      } else {
-        methodNames.add(key);
-        return key;
       }
+      methodNames.add(key);
+      return key;
     }
   }
 
   private static class ReplaceBadChars extends ResourceKeyFormatter {
     @Override
     public String format(String key) {
-      return DEFAULT_CHARS.matcher(key).replaceAll("_");
+      StringBuilder buf = new StringBuilder();
+      int keyLen = key == null ? 0 : key.length();
+      for (int i = 0; i < keyLen; i = key.offsetByCodePoints(i, 1)) {
+        int codePoint = key.codePointAt(i);
+        if (i == 0 ? Character.isJavaIdentifierStart(codePoint)
+            : Character.isJavaIdentifierPart(codePoint)) {
+          buf.appendCodePoint(codePoint);
+        } else {
+          buf.append('_');
+        }
+      }
+      if (buf.length() == 0) {
+        buf.append('_');
+      }
+      return buf.toString();
     }
   }
 
@@ -71,7 +81,47 @@
     public abstract String format(String key);
   }
 
-  private static Pattern DEFAULT_CHARS = Pattern.compile("[.-]");
+  /**
+   * Index into this array using a nibble, 4 bits, to get the corresponding
+   * hexadecimal character representation.
+   */
+  private static final char NIBBLE_TO_HEX_CHAR[] = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+        'E', 'F'
+    };
+
+  private static void unicodeEscape(char ch, StringBuilder buf) {
+    buf.append('\\');
+    buf.append('u');
+    buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 12) & 0x0F]);
+    buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 8) & 0x0F]);
+    buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]);
+    buf.append(NIBBLE_TO_HEX_CHAR[ch & 0x0F]);
+  }
+
+  private static boolean needsUnicodeEscape(char ch) {
+    if (ch == ' ') {
+      return false;
+    }
+    switch (Character.getType(ch)) {
+      case Character.COMBINING_SPACING_MARK:
+      case Character.ENCLOSING_MARK:
+      case Character.NON_SPACING_MARK:
+      case Character.UNASSIGNED:
+      case Character.PRIVATE_USE:
+      case Character.SPACE_SEPARATOR:
+      case Character.CONTROL:
+      case Character.LINE_SEPARATOR:
+      case Character.FORMAT:
+      case Character.PARAGRAPH_SEPARATOR:
+      case Character.SURROGATE:
+        return true;
+  
+      default:
+        break;
+    }
+    return false;
+  }
 
   /**
    * Composer for the current Constant.
@@ -163,17 +213,19 @@
     p.load(propStream, Util.DEFAULT_ENCODING);
     addFormatters();
     // TODO: Look for a generic version of Tapestry's LocalizedProperties class
-    Iterator<Entry<String, String>> elements =
-      p.getPropertyMap().entrySet().iterator(); // suppress warnings
-    if (elements.hasNext() == false) {
+    Set<String> keySet = p.getPropertyMap().keySet();
+    // sort keys for deterministic results
+    String[] keys = keySet.toArray(new String[keySet.size()]);
+    Arrays.sort(keys);
+    if (keys.length == 0) {
       throw new IllegalStateException(
           "File '"
               + resourceFile
               + "' cannot be used to generate message classes, as it has no key/value pairs defined.");
     }
-    while (elements.hasNext()) {
-      Entry<String, String> s = elements.next();
-      genSimpleMethodDecl(s.getKey(), s.getValue());
+    for (String key : keys) {
+      String value = p.getProperty(key);
+      genSimpleMethodDecl(key, value);
     }
     composer.commit(new PrintWriterTreeLogger());
   }
@@ -190,23 +242,19 @@
     for (ResourceKeyFormatter formatter : formatters) {
       key = formatter.format(key);
     }
-    if (Util.isValidJavaIdent(key) == false) {
-      // TODO(jat): we could synthesize legal method names and add an
-      // @Key annotation to keep the matching key name.
-      throw new IllegalArgumentException(key
-          + " is not a legitimate method name.");
-    }
     return key;
   }
 
   private void genMethodDecl(String type, String defaultValue, String key) {
     composer.beginJavaDocComment();
-    composer.println("Translated \"" + defaultValue + "\".\n");
-    composer.println("@return translated \"" + defaultValue + "\"");
+    String escaped = makeJavaString(defaultValue);
+    composer.println("Translated " + escaped + ".\n");
+    composer.print("@return translated " + escaped);
     composer.endJavaDocComment();
     genValueAnnotation(defaultValue);
-    key = formatKey(key);
-    composer.print(type + " " + key);
+    composer.println("@Key(" + makeJavaString(key) + ")");
+    String methodName = formatKey(key);
+    composer.print(type + " " + methodName);
     composer.print("(");
     genMethodArgs(defaultValue);
     composer.print(");\n");
@@ -229,4 +277,32 @@
     resourceFile = resourceBundle;
     sourceFile = targetLocation;
   }
+
+  protected String makeJavaString(String value) {
+    StringBuilder buf = new StringBuilder();
+    buf.append('\"');
+    for (int i = 0; i < value.length(); ++i) {
+      char c = value.charAt(i);
+      switch (c) {
+        case '\r':
+          buf.append("\\r");
+          break;
+        case '\n':
+          buf.append("\\n");
+          break;
+        case '\"':
+          buf.append("\\\"");
+          break;
+        default:
+          if (needsUnicodeEscape(c)) {
+            unicodeEscape(c, buf);
+          } else {
+            buf.append(c);
+          }
+          break;
+      }
+    }
+    buf.append('\"');
+    return buf.toString();
+  }
 }
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java
index ef7bcd9..de7685b 100644
--- a/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java
@@ -50,13 +50,13 @@
 
   @Override
   protected void genValueAnnotation(String defaultValue) {
-    composer.println("@DefaultStringValue(\"" + defaultValue.replace("\"", "\\\"")
-        + "\")");
+    composer.println("@DefaultStringValue(" + makeJavaString(defaultValue)
+        + ")");
   }
 
   @Override
   protected String javaDocComment(String path) {
-    return "Interface to represent the constants contained in resource  bundle:\n\t'"
+    return "Interface to represent the constants contained in resource bundle:\n\t'"
       + path + "'.";
   }
 }
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java
index 84ee86a..9c84209 100644
--- a/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java
@@ -92,8 +92,7 @@
 
   @Override
   protected void genValueAnnotation(String defaultValue) {
-    composer.println("@DefaultMessage(\"" + defaultValue.replace("\"", "\\\"")
-        + "\")");
+    composer.println("@DefaultMessage(" + makeJavaString(defaultValue) + ")");
   }
 
   @Override
diff --git a/user/src/com/google/gwt/i18n/tools/I18NSync.java b/user/src/com/google/gwt/i18n/tools/I18NSync.java
index 18da760..06bed9a 100644
--- a/user/src/com/google/gwt/i18n/tools/I18NSync.java
+++ b/user/src/com/google/gwt/i18n/tools/I18NSync.java
@@ -337,7 +337,11 @@
               + "'should not contain an extension. \"com.google.gwt.SomeClass\" is an example of a correctly formed class string");
     }
     String resourcePath = className.replace('.', '/') + ".properties";
-    URL r = ClassLoader.getSystemResource(resourcePath);
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    if (cl == null) {
+      cl = ClassLoader.getSystemClassLoader();
+    }
+    URL r = cl.getResource(resourcePath);
     if (r == null) {
       throw new FileNotFoundException("Could not find the resource '"
           + resourcePath + " matching '" + className
diff --git a/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java b/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java
index ea4356d..1bea53c 100644
--- a/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/HTTPRequestImplIE6.java
@@ -24,6 +24,14 @@
 
   @Override
   protected native JavaScriptObject doCreateXmlHTTPRequest() /*-{
-    return new ActiveXObject("Microsoft.XMLHTTP");
+    if ($wnd.XMLHttpRequest) {
+      return new XMLHttpRequest();
+    } else {
+      try {
+        return new ActiveXObject('MSXML2.XMLHTTP.3.0');
+      } catch (e) {
+        return new ActiveXObject("Microsoft.XMLHTTP");
+      }
+    }
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
index 51c4483..800551b 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.rpc.impl;
 
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
+
 /**
  * Base class for the client and server serialization streams. This class
  * handles the basic serialization and deserialization formatting for primitive
@@ -23,9 +25,21 @@
 public abstract class AbstractSerializationStream {
 
   /**
+   * The character used to separate fields in client->server RPC messages.
+   * 
+   * Note that this character is referenced in the following places not
+   * using this constant, and they must be changed if this is:
+   * <ul>
+   * <li>{@link ServerSerializationStreamWriter}.deserializeStringTable
+   * <li>{@link ClientSerializationStreamReader}.getQuotingRegex
+   * </ul>
+   */
+  public static final char RPC_SEPARATOR_CHAR = '|';
+
+  /**
    * This is the only supported RPC protocol version.
    */
-  public static final int SERIALIZATION_STREAM_VERSION = 4;
+  public static final int SERIALIZATION_STREAM_VERSION = 5;
 
   private int flags = 0;
   private int version = SERIALIZATION_STREAM_VERSION;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index f44d1ec..2b695c4 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -16,8 +16,10 @@
 package com.google.gwt.user.client.rpc.impl;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
 
 import java.util.List;
 
@@ -27,18 +29,97 @@
 public final class ClientSerializationStreamWriter extends
     AbstractSerializationStreamWriter {
 
+  /**
+   * Used by JSNI, see {@link #quoteString(String)}.
+   */
+  @SuppressWarnings("unused")
+  private static JavaScriptObject regex = getQuotingRegex();
+  
   private static void append(StringBuffer sb, String token) {
     assert (token != null);
     sb.append(token);
-    sb.append('\uffff');
+    sb.append(RPC_SEPARATOR_CHAR);
   }
 
+  /**
+   * Create the RegExp instance used for quoting dangerous characters in
+   * user payload strings.
+   *
+   * Note that {@link AbstractSerializationStream#RPC_SEPARATOR_CHAR} is used in
+   * this expression, which must be updated if the separator character is
+   * changed.
+   * 
+   * For Android WebKit, we quote many more characters to keep them from
+   * being mangled.
+   * 
+   * @return RegExp object
+   */
+  private static native JavaScriptObject getQuotingRegex() /*-{
+    // "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR
+    var ua = navigator.userAgent.toLowerCase();
+    var webkitregex = /webkit\/([\d]+)/;
+    var webkit = 0;
+    var result = webkitregex.exec(ua);
+    if (result) {
+      webkit = parseInt(result[1]);
+    }
+    if (ua.indexOf("android") != -1) {
+      // initial version of Android WebKit has a double-encoding bug for UTF8,
+      // so we have to encode every non-ASCII character.
+      // TODO(jat): revisit when this bug is fixed in Android
+      return /[\u0000\|\\\u0080-\uFFFF]/g;
+    } else if (webkit < 522) {
+      // Safari 2 doesn't handle \\uXXXX in regexes
+      // TODO(jat): should iPhone be treated specially?
+      return /[\x00\|\\]/g;
+    } else if (webkit > 0) {
+      // other WebKit-based browsers need some additional quoting
+      return /[\u0000\|\\\u0300-\u036F\u0590-\u05FF\uD800-\uFFFF]/g;
+    } else {
+      return /[\u0000\|\\\uD800-\uFFFF]/g;
+    }
+  }-*/;
+
   @UnsafeNativeLong
   // Keep synchronized with LongLib
   private static native double[] makeLongComponents0(long value) /*-{
     return value;
   }-*/;
 
+  /**
+   * Quote characters in a user-supplied string to make sure they are safe to
+   * send to the server.
+   *
+   * See {@link ServerSerializationStreamReader#deserializeStringTable}
+   * for the corresponding dequoting.
+   *
+   * @param str string to quote
+   * @return quoted string
+   */
+  private static native String quoteString(String str) /*-{
+    var regex = @com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::regex;
+    var idx = 0;
+    var out = "";
+    var result;
+    while ((result = regex.exec(str)) != null) {
+       out += str.substring(idx, result.index);
+       idx = result.index + 1;
+       var ch = result[0].charCodeAt(0);
+       if (ch == 0) {
+         out += "\\0";
+       } else if (ch == 92) { // backslash
+         out += "\\\\";
+       } else if (ch == 124) { // vertical bar
+         // 124 = "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR
+         out += "\\!";
+       } else {
+         var hex = ch.toString(16);
+         out += "\\u0000".substring(0, 6 - hex.length) + hex;
+       }
+    }
+    return out + str.substring(idx);
+  }-*/;
+
   private StringBuffer encodeBuffer;
 
   private final String moduleBaseURL;
@@ -67,6 +148,7 @@
    * Call this method before attempting to append any tokens. This method
    * implementation <b>must</b> be called by any overridden version.
    */
+  @Override
   public void prepareToWrite() {
     super.prepareToWrite();
     encodeBuffer = new StringBuffer();
@@ -148,7 +230,7 @@
     List<String> stringTable = getStringTable();
     append(buffer, String.valueOf(stringTable.size()));
     for (String s : stringTable) {
-      append(buffer, s);
+      append(buffer, quoteString(s));
     }
     return buffer;
   }
diff --git a/user/src/com/google/gwt/user/client/ui/Tree.java b/user/src/com/google/gwt/user/client/ui/Tree.java
index 222f319..0a26558 100644
--- a/user/src/com/google/gwt/user/client/ui/Tree.java
+++ b/user/src/com/google/gwt/user/client/ui/Tree.java
@@ -777,7 +777,7 @@
    * @param treeItem the tree item
    */
   void showLeafImage(TreeItem treeItem) {
-    if (useLeafImages) {
+    if (useLeafImages || treeItem.isFullNode()) {
       showImage(treeItem, images.treeLeaf());
     } else {
       DOM.setStyleAttribute(treeItem.getElement(), "paddingLeft", indentValue);
diff --git a/user/src/com/google/gwt/user/client/ui/TreeItem.java b/user/src/com/google/gwt/user/client/ui/TreeItem.java
index 724beab..8b6c66f 100644
--- a/user/src/com/google/gwt/user/client/ui/TreeItem.java
+++ b/user/src/com/google/gwt/user/client/ui/TreeItem.java
@@ -107,11 +107,6 @@
     void initializeClonableElements() {
       super.initializeClonableElements();
       if (GWT.isClient()) {
-        // Remove the padding from the cells and re-add it to the table element
-        DOM.setElementPropertyInt(BASE_INTERNAL_ELEM, "cellPadding", 0);
-        DOM.setElementPropertyInt(BASE_INTERNAL_ELEM, "cellSpacing", 0);
-        BASE_INTERNAL_ELEM.getStyle().setPropertyPx("paddingBottom", 3);
-
         // We can't use a 3px padding all around because IE will wrap the
         // childSpan to the next line, so we need to add a 3px margin on the top
         // and bottom instead. However, margins overlap, so we need a 6px bottom
@@ -688,7 +683,7 @@
   }
 
   Element getImageHolderElement() {
-    if (imageHolder == null) {
+    if (!isFullNode()) {
       convertToFullNode();
     }
     return imageHolder;
@@ -702,6 +697,10 @@
     children = new ArrayList<TreeItem>();
   }
 
+  boolean isFullNode() {
+    return imageHolder != null;
+  }
+
   void setParentItem(TreeItem parent) {
     this.parent = parent;
   }
diff --git a/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java b/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java
index 658c1a6..034949c 100644
--- a/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java
+++ b/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java
@@ -57,22 +57,24 @@
       throw new IllegalArgumentException("Cannot supply a null package name to"
           + targetClassShortName);
     }
-    // Inlined header to only have one method with a huge number of methods.
+    // TODO: support a user-specified file header
     if (targetPackageName.length() > 0) {
       println("package " + targetPackageName + ";");
     }
     
-    println();
     if (imports != null && imports.length > 0) {
+      println();
       for (int i = 0, n = imports.length; i < n; ++i) {
         println("import " + imports[i] + ";");
       }
-      println();
     }
     if (classJavaDocComment != null) {
       beginJavaDocComment();
       print(classJavaDocComment);
       endJavaDocComment();
+    } else {
+      // beginJavaDocComment adds its own leading newline, make up for it here.
+      println();
     }
     if (category == JavaSourceCategory.CLASS) {
       emitClassDecl(targetClassShortName, superClassName, interfaceNames);
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 3e1d87e..92e1cd3 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -77,43 +77,50 @@
   private enum ValueReader {
     BOOLEAN {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readBoolean();
       }
     },
     BYTE {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readByte();
       }
     },
     CHAR {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readChar();
       }
     },
     DOUBLE {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readDouble();
       }
     },
     FLOAT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readFloat();
       }
     },
     INT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readInt();
       }
     },
     LONG {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readLong();
       }
     },
@@ -126,13 +133,15 @@
     },
     SHORT {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readShort();
       }
     },
     STRING {
       @Override
-      Object readValue(ServerSerializationStreamReader stream) {
+      Object readValue(ServerSerializationStreamReader stream)
+          throws SerializationException {
         return stream.readString();
       }
     };
@@ -323,6 +332,7 @@
   private final ArrayList<String> tokenList = new ArrayList<String>();
 
   private int tokenListIndex;
+
   {
     CLASS_TO_VECTOR_READER.put(boolean[].class, VectorReader.BOOLEAN_VECTOR);
     CLASS_TO_VECTOR_READER.put(byte[].class, VectorReader.BYTE_VECTOR);
@@ -374,11 +384,30 @@
     stringTable = null;
 
     int idx = 0, nextIdx;
-    while (-1 != (nextIdx = encodedTokens.indexOf('\uffff', idx))) {
+    while (-1 != (nextIdx = encodedTokens.indexOf(RPC_SEPARATOR_CHAR, idx))) {
       String current = encodedTokens.substring(idx, nextIdx);
       tokenList.add(current);
       idx = nextIdx + 1;
     }
+    if (idx == 0) {
+      // Didn't find any separator, assume an older version with different
+      // separators and get the version as the sequence of digits at the
+      // beginning of the encoded string.
+      while (idx < encodedTokens.length()
+          && Character.isDigit(encodedTokens.charAt(idx))) {
+        ++idx;
+      }
+      if (idx == 0) {
+        throw new IncompatibleRemoteServiceException(
+            "Malformed or old RPC message received - expecting version "
+            + SERIALIZATION_STREAM_VERSION);
+      } else {
+        int version = Integer.valueOf(encodedTokens.substring(0, idx));
+        throw new IncompatibleRemoteServiceException("Expecting version "
+            + SERIALIZATION_STREAM_VERSION + " from client, got " + version
+            + ".");
+      }
+    }
 
     super.prepareToRead(encodedTokens);
 
@@ -407,42 +436,42 @@
     }
   }
 
-  public boolean readBoolean() {
+  public boolean readBoolean() throws SerializationException {
     return !extract().equals("0");
   }
 
-  public byte readByte() {
+  public byte readByte() throws SerializationException {
     return Byte.parseByte(extract());
   }
 
-  public char readChar() {
+  public char readChar() throws SerializationException {
     // just use an int, it's more foolproof
     return (char) Integer.parseInt(extract());
   }
 
-  public double readDouble() {
+  public double readDouble() throws SerializationException {
     return Double.parseDouble(extract());
   }
 
-  public float readFloat() {
+  public float readFloat() throws SerializationException {
     return (float) Double.parseDouble(extract());
   }
 
-  public int readInt() {
+  public int readInt() throws SerializationException {
     return Integer.parseInt(extract());
   }
 
-  public long readLong() {
+  public long readLong() throws SerializationException {
     // Keep synchronized with LongLib. The wire format are the two component
     // parts of the double in the client code.
     return (long) readDouble() + (long) readDouble();
   }
 
-  public short readShort() {
+  public short readShort() throws SerializationException {
     return Short.parseShort(extract());
   }
 
-  public String readString() {
+  public String readString() throws SerializationException {
     return getString(readInt());
   }
 
@@ -587,7 +616,50 @@
     BoundedList<String> buffer = new BoundedList<String>(String.class,
         typeNameCount);
     for (int typeNameIndex = 0; typeNameIndex < typeNameCount; ++typeNameIndex) {
-      buffer.add(extract());
+      String str = extract();
+      // Change quoted characters back.
+      int idx = str.indexOf('\\');
+      if (idx >= 0) {
+        StringBuilder buf = new StringBuilder();
+        int pos = 0;
+        while (idx >= 0) {
+          buf.append(str.substring(pos, idx));
+          if (++idx == str.length()) {
+            throw new SerializationException("Unmatched backslash: \""
+                + str + "\"");
+          }
+          char ch = str.charAt(idx);
+          pos = idx + 1;
+          switch (ch) {
+            case '0':
+              buf.append('\u0000');
+              break;
+            case '!':
+              buf.append(RPC_SEPARATOR_CHAR);
+              break;
+            case '\\':
+              buf.append(ch);
+              break;
+            case 'u':
+              try {
+                ch = (char) Integer.parseInt(str.substring(idx + 1, idx + 5), 16);
+              } catch (NumberFormatException e) {
+                throw new SerializationException(
+                    "Invalid Unicode escape sequence in \"" + str + "\"");
+              }
+              buf.append(ch);
+              pos += 4;
+              break;
+            default:
+              throw new SerializationException("Unexpected escape character "
+                  + ch + " after backslash: \"" + str + "\"");
+          }
+          idx = str.indexOf('\\', pos);
+        }
+        buf.append(str.substring(pos));
+        str = buf.toString();
+      }
+      buffer.add(str);
     }
 
     if (buffer.size() != buffer.getExpectedSize()) {
@@ -613,14 +685,18 @@
     throw new NoSuchMethodException("deserialize");
   }
 
-  private String extract() {
-    return tokenList.get(tokenListIndex++);
+  private String extract() throws SerializationException {
+    try {
+      return tokenList.get(tokenListIndex++);
+    } catch (IndexOutOfBoundsException e) {
+      throw new SerializationException("Too few tokens in RPC request", e);
+    }
   }
 
   private Object instantiate(Class<?> customSerializer, Class<?> instanceClass)
       throws InstantiationException, IllegalAccessException,
       IllegalArgumentException, InvocationTargetException,
-      NoSuchMethodException {
+      NoSuchMethodException, SerializationException {
     if (customSerializer != null) {
       for (Method method : customSerializer.getMethods()) {
         if ("instantiate".equals(method.getName())) {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index f355f3c..8f837cd 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -371,11 +371,7 @@
 
     for (int i = 0, n = input.length; i < n; ++i) {
       char c = input[i];
-      if (c < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[c] != 0) {
-        charVector.add(JS_ESCAPE_CHAR);
-        charVector.add(JS_CHARS_ESCAPED[c]);
-      } else if (needsUnicodeEscape(c)) {
-        charVector.add(JS_ESCAPE_CHAR);
+      if (needsUnicodeEscape(c)) {
         unicodeEscape(c, charVector);
       } else {
         charVector.add(c);
@@ -444,43 +440,57 @@
    * </ol>
    */
   private static boolean needsUnicodeEscape(char ch) {
-    switch (Character.getType(ch)) {
-      // Conservative
-      case Character.COMBINING_SPACING_MARK:
-      case Character.ENCLOSING_MARK:
-      case Character.NON_SPACING_MARK:
-      case Character.UNASSIGNED:
-      case Character.PRIVATE_USE:
-      case Character.SPACE_SEPARATOR:
-      case Character.CONTROL:
-
-        // Minimal
-      case Character.LINE_SEPARATOR:
-      case Character.FORMAT:
-      case Character.PARAGRAPH_SEPARATOR:
-      case Character.SURROGATE:
+    switch (ch) {
+      case ' ':
+        // ASCII space gets caught in SPACE_SEPARATOR below, but does not
+        // need to be escaped
+        return false;
+      case JS_QUOTE_CHAR:
+      case JS_ESCAPE_CHAR:
+        // these must be quoted or they will break the protocol
         return true;
-
-      default:
-        if (ch == NON_BREAKING_HYPHEN) {
+      case NON_BREAKING_HYPHEN:
           // This can be expanded into a break followed by a hyphen
           return true;
+      default:
+        switch (Character.getType(ch)) {
+          // Conservative
+          case Character.COMBINING_SPACING_MARK:
+          case Character.ENCLOSING_MARK:
+          case Character.NON_SPACING_MARK:
+          case Character.UNASSIGNED:
+          case Character.PRIVATE_USE:
+          case Character.SPACE_SEPARATOR:
+          case Character.CONTROL:
+
+            // Minimal
+          case Character.LINE_SEPARATOR:
+          case Character.FORMAT:
+          case Character.PARAGRAPH_SEPARATOR:
+          case Character.SURROGATE:
+            return true;
+
+          default:
+            break;
         }
         break;
     }
-
     return false;
   }
 
   /**
-   * Writes either the two or four character escape sequence for a character.
-   * 
+   * Writes a safe escape sequence for a character.  Some characters have a
+   * short form, such as \n for U+000D, while others are represented as \\xNN
+   * or \\uNNNN.
    * 
    * @param ch character to unicode escape
    * @param charVector char vector to receive the unicode escaped representation
    */
   private static void unicodeEscape(char ch, CharVector charVector) {
-    if (ch < 256) {
+    charVector.add(JS_ESCAPE_CHAR);
+    if (ch < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[ch] != 0) {
+      charVector.add(JS_CHARS_ESCAPED[ch]);
+    } else if (ch < 256) {
       charVector.add('x');
       charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]);
       charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]);
diff --git a/user/super/com/google/gwt/emul/java/sql/Time.java b/user/super/com/google/gwt/emul/java/sql/Time.java
index a3302af..81c81cd 100644
--- a/user/super/com/google/gwt/emul/java/sql/Time.java
+++ b/user/super/com/google/gwt/emul/java/sql/Time.java
@@ -27,9 +27,9 @@
     }
 
     try {
-      int hh = Integer.decode(split[0]);
-      int mm = Integer.decode(split[1]);
-      int ss = Integer.decode(split[2]);
+      int hh = Integer.parseInt(split[0]);
+      int mm = Integer.parseInt(split[1]);
+      int ss = Integer.parseInt(split[2]);
 
       return new Time(hh, mm, ss);
     } catch (NumberFormatException e) {
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 4c744e7..e6e3657 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -26,9 +26,29 @@
 @SuppressWarnings("unused")
 public class CompilerTest extends GWTTestCase {
 
+  private abstract static class AbstractSuper {
+    public static String foo() {
+      if (FALSE) {
+        // prevent inlining
+        return foo();
+      }
+      return "AbstractSuper";
+    }
+  }
+
   private abstract static class Apple implements Fruit {
   }
 
+  private static class ConcreteSub extends AbstractSuper {
+    public static String foo() {
+      if (FALSE) {
+        // prevent inlining
+        return foo();
+      }
+      return "ConcreteSub";
+    }
+  }
+
   private static interface Fruit {
   }
 
@@ -174,6 +194,8 @@
     return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead;
   }-*/;
 
+  private Integer boxedInteger = 0;
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -557,6 +579,16 @@
     assertEquals("null true", test);
   }
 
+  /**
+   * Issue 2886: inlining should cope with local variables that do not have an
+   * explicit declaration node.
+   */
+  public void testInliningBoxedIncrement() {
+    // should not actually inline, because it has a temp variable
+    incrementBoxedInteger();
+    assertEquals((Integer) 1, boxedInteger);
+  }
+
   public void testJavaScriptReservedWords() {
     boolean delete = TRUE;
     for (int in = 0; in < 10; ++in) {
@@ -777,6 +809,11 @@
     assertEquals(new Foo(2).i, 2);
   }
 
+  public void testStaticMethodResolution() {
+    // Issue 2922
+    assertEquals("AbstractSuper", AbstractSuper.foo());
+  }
+
   public void testStringOptimizations() {
     assertEquals("Herro, AJAX", "Hello, AJAX".replace('l', 'r'));
     assertEquals('J', "Hello, AJAX".charAt(8));
@@ -988,6 +1025,11 @@
     }
   }
 
+  private void incrementBoxedInteger() {
+    // the following will need a temporary variable created
+    boxedInteger++;
+  }
+
   private boolean returnFalse() {
     return false;
   }
diff --git a/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java b/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java
index e3129f4..0646e3c 100644
--- a/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java
+++ b/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java
@@ -102,13 +102,20 @@
     }
 
     Time t = Time.valueOf("13:01:30");
-    // Months are 0-based, days are 1-based
     assertEquals(13, t.getHours());
     assertEquals(1, t.getMinutes());
     assertEquals(30, t.getSeconds());
 
     Time d2 = Time.valueOf(t.toString());
     assertEquals(t, d2);
+
+    // tests to see if the various parts are indeed decoded in base-10 (till
+    // r3728 the base was first inferred)
+    Time t2 = Time.valueOf("08:09:01");
+    assertEquals(8, t2.getHours());
+    assertEquals(9, t2.getMinutes());
+    assertEquals(1, t2.getSeconds());
+    assertEquals(t2, Time.valueOf(t2.toString()));
   }
 
   public void testToString() {
diff --git a/user/test/com/google/gwt/i18n/client/I18N2Test.java b/user/test/com/google/gwt/i18n/client/I18N2Test.java
index 9e9da3d..89568ab 100644
--- a/user/test/com/google/gwt/i18n/client/I18N2Test.java
+++ b/user/test/com/google/gwt/i18n/client/I18N2Test.java
@@ -105,6 +105,10 @@
     assertEquals("a_b_c", test.a_b_c());
     assertEquals("a_b_c", test.getString("a_b_c"));
     assertEquals("__s_dup_dup", test.__s_dup_dup());
+    assertEquals("e in b_C_d", test.getString("__dup_dup"));
+    assertEquals("e in b_C_d", test.__dup_dup());
+    assertEquals("andStar", test.getString("__"));
+    assertEquals("andStar", test.__());
   }
 
   public void testBinding() {
diff --git a/user/test/com/google/gwt/i18n/client/gen/Colors.java b/user/test/com/google/gwt/i18n/client/gen/Colors.java
index 2da193e..5c2f661 100644
--- a/user/test/com/google/gwt/i18n/client/gen/Colors.java
+++ b/user/test/com/google/gwt/i18n/client/gen/Colors.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,71 +17,79 @@
 
 /**
  * Interface to represent the constants contained in resource bundle:
- * com/google/gwt/i18n/client/gen/Colors.properties.
+ * 	'com/google/gwt/i18n/client/gen/Colors.properties'.
  */
 public interface Colors extends com.google.gwt.i18n.client.Constants {
-
+  
   /**
-   * Translated "ĝrééñ".
+   * Translated "bļåçķ".
    * 
-   * @return translated "ĝrééñ"
-   * @gwt.key green
+   * @return translated "bļåçķ"
    */
-  String green();
-
-  /**
-   * Translated "réd ".
-   * 
-   * @return translated "réd "
-   * @gwt.key red
-   */
-  String red();
-
-  /**
-   * Translated "ŵĥîţé".
-   * 
-   * @return translated "ŵĥîţé"
-   * @gwt.key white
-   */
-  String white();
-
-  /**
-   * Translated "ĝréý".
-   * 
-   * @return translated "ĝréý"
-   * @gwt.key grey
-   */
-  String grey();
+  @DefaultStringValue("bļåçķ")
+  @Key("black")
+  String black();
 
   /**
    * Translated "bļûç".
    * 
    * @return translated "bļûç"
-   * @gwt.key blue
    */
+  @DefaultStringValue("bļûç")
+  @Key("blue")
   String blue();
 
   /**
-   * Translated "ýéļļöŵ".
+   * Translated "ĝrééñ".
    * 
-   * @return translated "ýéļļöŵ"
-   * @gwt.key yellow
+   * @return translated "ĝrééñ"
    */
-  String yellow();
+  @DefaultStringValue("ĝrééñ")
+  @Key("green")
+  String green();
 
   /**
-   * Translated "bļåçķ".
+   * Translated "ĝréý".
    * 
-   * @return translated "bļåçķ"
-   * @gwt.key black
+   * @return translated "ĝréý"
    */
-  String black();
+  @DefaultStringValue("ĝréý")
+  @Key("grey")
+  String grey();
+
+  /**
+   * Translated "réd ".
+   * 
+   * @return translated "réd "
+   */
+  @DefaultStringValue("réd ")
+  @Key("red")
+  String red();
 
   /**
    * Translated "any primary color".
    * 
    * @return translated "any primary color"
-   * @gwt.key shapeColor
    */
+  @DefaultStringValue("any primary color")
+  @Key("shapeColor")
   String shapeColor();
+
+  /**
+   * Translated "ŵĥîţé".
+   * 
+   * @return translated "ŵĥîţé"
+   */
+  @DefaultStringValue("ŵĥîţé")
+  @Key("white")
+  String white();
+
+  /**
+   * Translated "ýéļļöŵ".
+   * 
+   * @return translated "ýéļļöŵ"
+   */
+  @DefaultStringValue("ýéļļöŵ")
+  @Key("yellow")
+  String yellow();
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/Shapes.java b/user/test/com/google/gwt/i18n/client/gen/Shapes.java
index 089cd74..008fa79 100644
--- a/user/test/com/google/gwt/i18n/client/gen/Shapes.java
+++ b/user/test/com/google/gwt/i18n/client/gen/Shapes.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,39 +17,43 @@
 
 /**
  * Interface to represent the constants contained in resource bundle:
- * com/google/gwt/i18n/client/gen/Shapes.properties.
+ * 	'com/google/gwt/i18n/client/gen/Shapes.properties'.
  */
 public interface Shapes extends com.google.gwt.i18n.client.Constants {
-
-  /**
-   * Translated "a triangle".
-   * 
-   * @return translated "a triangle"
-   * @gwt.key triangle
-   */
-  String triangle();
-
-  /**
-   * Translated "a square ".
-   * 
-   * @return translated "a square "
-   * @gwt.key square
-   */
-  String square();
-
+  
   /**
    * Translated "a circle".
    * 
    * @return translated "a circle"
-   * @gwt.key circle
    */
+  @DefaultStringValue("a circle")
+  @Key("circle")
   String circle();
 
   /**
    * Translated "a color wheel".
    * 
    * @return translated "a color wheel"
-   * @gwt.key shapeColor
    */
+  @DefaultStringValue("a color wheel")
+  @Key("shapeColor")
   String shapeColor();
+
+  /**
+   * Translated "a square\u0009".
+   * 
+   * @return translated "a square\u0009"
+   */
+  @DefaultStringValue("a square\u0009")
+  @Key("square")
+  String square();
+
+  /**
+   * Translated "a triangle".
+   * 
+   * @return translated "a triangle"
+   */
+  @DefaultStringValue("a triangle")
+  @Key("triangle")
+  String triangle();
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java b/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java
index 57648d9..cfe955c 100644
--- a/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java
+++ b/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,15 +17,16 @@
 
 /**
  * Interface to represent the constants contained in resource bundle:
- * com/google/gwt/i18n/client/gen/SingleConstant.properties.
+ * 	'com/google/gwt/i18n/client/gen/SingleConstant.properties'.
  */
 public interface SingleConstant extends com.google.gwt.i18n.client.Constants {
-
+  
   /**
    * Translated "me".
    * 
    * @return translated "me"
-   * @gwt.key justOne
    */
+  @DefaultStringValue("me")
+  @Key("justOne")
   String justOne();
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java b/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java
index a97c277..765cce2 100644
--- a/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java
+++ b/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,15 +17,16 @@
 
 /**
  * Interface to represent the messages contained in resource bundle:
- * com/google/gwt/i18n/client/gen/SingleMessages.properties.
+ * 	'com/google/gwt/i18n/client/gen/SingleMessages.properties'.
  */
 public interface SingleMessages extends com.google.gwt.i18n.client.Messages {
-
+  
   /**
    * Translated "me".
    * 
    * @return translated "me"
-   * @gwt.key justOne
    */
+  @DefaultMessage("me")
+  @Key("justOne")
   String justOne();
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java
index 08ca867..4ce5b34 100644
--- a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java
+++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,178 +17,214 @@
 
 /**
  * Interface to represent the constants contained in resource bundle:
- * com/google/gwt/i18n/client/gen/TestBadKeys.properties.
+ * 	'com/google/gwt/i18n/client/gen/TestBadKeys.properties'.
  */
-public interface TestBadKeys extends
-    com.google.gwt.i18n.client.ConstantsWithLookup {
+public interface TestBadKeys extends com.google.gwt.i18n.client.ConstantsWithLookup {
+  
+  /**
+   * Translated "andStar".
+   * 
+   * @return translated "andStar"
+   */
+  @DefaultStringValue("andStar")
+  @Key("&*")
+  String __();
 
   /**
    * Translated "_".
    * 
    * @return translated "_"
-   * @gwt.key -
    */
+  @DefaultStringValue("_")
+  @Key("-")
   String _();
 
   /**
-   * Translated
-   * "________________________________________________________________".
+   * Translated "________________________________________________________________".
    * 
-   * @return translated
-   *         "________________________________________________________________"
-   * @gwt.key ----------------------------------------------------------------
+   * @return translated "________________________________________________________________"
    */
+  @DefaultStringValue("________________________________________________________________")
+  @Key("----------------------------------------------------------------")
   String ________________________________________________________________();
 
   /**
-   * Translated "__dup".
-   * 
-   * @return translated "__dup"
-   * @gwt.key .
-   */
-  String __dup();
-
-  /**
    * Translated "__s".
    * 
    * @return translated "__s"
-   * @gwt.key --s
    */
+  @DefaultStringValue("__s")
+  @Key("--s")
   String __s();
 
   /**
    * Translated "__s_dup".
    * 
    * @return translated "__s_dup"
-   * @gwt.key -.s
    */
+  @DefaultStringValue("__s_dup")
+  @Key("-.s")
   String __s_dup();
 
   /**
-   * Translated "__s_dup_dup".
-   * 
-   * @return translated "__s_dup_dup"
-   * @gwt.key ..s
-   */
-  String __s_dup_dup();
-
-  /**
-   * Translated "_1_2_3_4".
-   * 
-   * @return translated "_1_2_3_4"
-   * @gwt.key _1.2.3.4
-   */
-  String _1_2_3_4();
-
-  /**
    * Translated "_c_____".
    * 
    * @return translated "_c_____"
-   * @gwt.key -c..-.-
    */
+  @DefaultStringValue("_c_____")
+  @Key("-c..-.-")
   String _c_____();
 
   /**
+   * Translated "__dup".
+   * 
+   * @return translated "__dup"
+   */
+  @DefaultStringValue("__dup")
+  @Key(".")
+  String __dup();
+
+  /**
+   * Translated "__s_dup_dup".
+   * 
+   * @return translated "__s_dup_dup"
+   */
+  @DefaultStringValue("__s_dup_dup")
+  @Key("..s")
+  String __s_dup_dup();
+
+  /**
    * Translated "_level".
    * 
    * @return translated "_level"
-   * @gwt.key .level
    */
+  @DefaultStringValue("_level")
+  @Key(".level")
   String _level();
 
   /**
-   * Translated "a__b".
-   * 
-   * @return translated "a__b"
-   * @gwt.key a-.b
-   */
-  String a__b();
-
-  /**
-   * Translated "a_b_c".
-   * 
-   * @return translated "a_b_c"
-   * @gwt.key a-b-c
-   */
-  String a_b_c();
-
-  /**
    * Translated "AWT_end".
    * 
    * @return translated "AWT_end"
-   * @gwt.key AWT.end
    */
+  @DefaultStringValue("AWT_end")
+  @Key("AWT.end")
   String AWT_end();
 
   /**
    * Translated "AWT_f5".
    * 
    * @return translated "AWT_f5"
-   * @gwt.key AWT.f5
    */
+  @DefaultStringValue("AWT_f5")
+  @Key("AWT.f5")
   String AWT_f5();
 
   /**
-   * Translated "cell_2_5".
-   * 
-   * @return translated "cell_2_5"
-   * @gwt.key cell.2.5
-   */
-  String cell_2_5();
-
-  /**
    * Translated "Cursor_MoveDrop_32x32_File".
    * 
    * @return translated "Cursor_MoveDrop_32x32_File"
-   * @gwt.key Cursor.MoveDrop.32x32.File
    */
+  @DefaultStringValue("Cursor_MoveDrop_32x32_File")
+  @Key("Cursor.MoveDrop.32x32.File")
   String Cursor_MoveDrop_32x32_File();
 
   /**
+   * Translated "_1_2_3_4".
+   * 
+   * @return translated "_1_2_3_4"
+   */
+  @DefaultStringValue("_1_2_3_4")
+  @Key("_1.2.3.4")
+  String _1_2_3_4();
+
+  /**
+   * Translated "a__b".
+   * 
+   * @return translated "a__b"
+   */
+  @DefaultStringValue("a__b")
+  @Key("a-.b")
+  String a__b();
+
+  /**
+   * Translated "a_b_c".
+   * 
+   * @return translated "a_b_c"
+   */
+  @DefaultStringValue("a_b_c")
+  @Key("a-b-c")
+  String a_b_c();
+
+  /**
+   * Translated "cell_2_5".
+   * 
+   * @return translated "cell_2_5"
+   */
+  @DefaultStringValue("cell_2_5")
+  @Key("cell.2.5")
+  String cell_2_5();
+
+  /**
    * Translated "entity_160".
    * 
    * @return translated "entity_160"
-   * @gwt.key entity.160
    */
+  @DefaultStringValue("entity_160")
+  @Key("entity.160")
   String entity_160();
 
   /**
    * Translated "logger_org_hibernate_jdbc".
    * 
    * @return translated "logger_org_hibernate_jdbc"
-   * @gwt.key logger.org.hibernate.jdbc
    */
+  @DefaultStringValue("logger_org_hibernate_jdbc")
+  @Key("logger.org.hibernate.jdbc")
   String logger_org_hibernate_jdbc();
 
   /**
    * Translated "maven_checkstyle_properties".
    * 
    * @return translated "maven_checkstyle_properties"
-   * @gwt.key maven.checkstyle.properties
    */
+  @DefaultStringValue("maven_checkstyle_properties")
+  @Key("maven.checkstyle.properties")
   String maven_checkstyle_properties();
 
   /**
    * Translated "maven_jdiff_old_tag".
    * 
    * @return translated "maven_jdiff_old_tag"
-   * @gwt.key maven.jdiff.old.tag
    */
+  @DefaultStringValue("maven_jdiff_old_tag")
+  @Key("maven.jdiff.old.tag")
   String maven_jdiff_old_tag();
 
   /**
    * Translated "permissions_755".
    * 
    * @return translated "permissions_755"
-   * @gwt.key permissions.755
    */
+  @DefaultStringValue("permissions_755")
+  @Key("permissions.755")
   String permissions_755();
 
   /**
    * Translated "zh_spacer".
    * 
    * @return translated "zh_spacer"
-   * @gwt.key zh.spacer
    */
+  @DefaultStringValue("zh_spacer")
+  @Key("zh.spacer")
   String zh_spacer();
+
+  /**
+   * Translated "e".
+   * 
+   * @return translated "e"
+   */
+  @DefaultStringValue("e")
+  @Key("�")
+  String __dup_dup();
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties
index 46ab4d5..4761eff 100644
--- a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties
+++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties
@@ -19,4 +19,5 @@
 -c..-.- = _c_____
 ---------------------------------------------------------------- = ________________________________________________________________
 _1.2.3.4 = _1_2_3_4
-
+&* = andStar
+é = e
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties
new file mode 100644
index 0000000..a036225
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties
@@ -0,0 +1 @@
+é = e in b_C_d
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java
new file mode 100644
index 0000000..48a9369
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java
@@ -0,0 +1,59 @@
+/*
+ * 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.i18n.client.gen;
+
+/**
+ * Interface to represent the constants contained in resource bundle:
+ * 	'com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties'.
+ */
+public interface TestConstantsQuoting extends com.google.gwt.i18n.client.Constants {
+  
+  /**
+   * Translated "Doesn''t work this way here".
+   * 
+   * @return translated "Doesn''t work this way here"
+   */
+  @DefaultStringValue("Doesn''t work this way here")
+  @Key("doubledQuote")
+  String doubledQuote();
+
+  /**
+   * Translated "Embedded\r\ncr-nl.".
+   * 
+   * @return translated "Embedded\r\ncr-nl."
+   */
+  @DefaultStringValue("Embedded\r\ncr-nl.")
+  @Key("embeddedCRNL")
+  String embeddedCRNL();
+
+  /**
+   * Translated "This line has an\nembedded newline".
+   * 
+   * @return translated "This line has an\nembedded newline"
+   */
+  @DefaultStringValue("This line has an\nembedded newline")
+  @Key("embeddedNL")
+  String embeddedNL();
+
+  /**
+   * Translated "\"Don't worry, be happy\" he said.".
+   * 
+   * @return translated "\"Don't worry, be happy\" he said."
+   */
+  @DefaultStringValue("\"Don't worry, be happy\" he said.")
+  @Key("embeddedQuote")
+  String embeddedQuote();
+}
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties
new file mode 100644
index 0000000..97baf73
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties
@@ -0,0 +1,4 @@
+embeddedNL=This line has an\nembedded newline
+embeddedCRNL=Embedded\r\ncr-nl.
+embeddedQuote="Don't worry, be happy" he said.
+doubledQuote=Doesn''t work this way here
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessages.java b/user/test/com/google/gwt/i18n/client/gen/TestMessages.java
index 136c8cd..d873fd7 100644
--- a/user/test/com/google/gwt/i18n/client/gen/TestMessages.java
+++ b/user/test/com/google/gwt/i18n/client/gen/TestMessages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -16,107 +16,116 @@
 package com.google.gwt.i18n.client.gen;
 
 /**
- * Interface to represent the messages contained in resource bundle
- * com/google/gwt/i18n/client/gen/TestMessages.properties.
+ * Interface to represent the messages contained in resource bundle:
+ * 	'com/google/gwt/i18n/client/gen/TestMessages.properties'.
  */
 public interface TestMessages extends com.google.gwt.i18n.client.Messages {
-
-  /**
-   * Translated "{0},{1}, "a","b", "{0}", "{1}", ''a'', 'b', '{0}', ''{1}''".
-   * 
-   * @return translated "{0},{1}, "a","b", "{0}", "{1}", ''a'', 'b', '{0}',
-   *         ''{1}''"
-   * @gwt.key argsWithQuotes
-   */
-  String argsWithQuotes(String arg0, String arg1);
-
-  /**
-   * Translated "{1} is the second arg, {0} is the first".
-   * 
-   * @return translated "{1} is the second arg, {0} is the first"
-   * @gwt.key args2
-   */
-  String args2(String arg0, String arg1);
-
+  
   /**
    * Translated "no args".
    * 
    * @return translated "no args"
-   * @gwt.key args0
    */
+  @DefaultMessage("no args")
+  @Key("args0")
   String args0();
 
   /**
-   * Translated "{0}".
-   * 
-   * @return translated "{0}"
-   * @gwt.key simpleMessageTest
-   */
-  String simpleMessageTest(String arg0);
-
-  /**
-   * Translated ""~" ~~ "~~~~ """.
-   * 
-   * @return translated ""~" ~~ "~~~~ """
-   * @gwt.key testWithXs
-   */
-  String testWithXs();
-
-  /**
-   * Translated "arg0arg1 arg0,arg1 {0}arg4".
-   * 
-   * @return translated "arg0arg1 arg0,arg1 {0}arg4"
-   * @gwt.key argsTest
-   */
-  String argsTest(String arg0);
-
-  /**
-   * Translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}".
-   * 
-   * @return translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}"
-   * @gwt.key testLotsOfUsageOfArgs
-   */
-  String testLotsOfUsageOfArgs(String arg0, String arg1);
-
-  /**
-   * Translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}".
-   * 
-   * @return translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}"
-   * @gwt.key args10
-   */
-  String args10(String arg0, String arg1, String arg2, String arg3,
-      String arg4, String arg5, String arg6, String arg7, String arg8,
-      String arg9);
-
-  /**
-   * Translated "お{0}你{1}好".
-   * 
-   * @return translated "お{0}你{1}好"
-   * @gwt.key unicode
-   */
-  String unicode(String arg0, String arg1);
-
-  /**
    * Translated "{0} is a arg".
    * 
    * @return translated "{0} is a arg"
-   * @gwt.key args1
    */
+  @DefaultMessage("{0} is a arg")
+  @Key("args1")
   String args1(String arg0);
 
   /**
-   * Translated "{quoted}".
+   * Translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}".
    * 
-   * @return translated "{quoted}"
-   * @gwt.key quotedBraces
+   * @return translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}"
    */
-  String quotedBraces();
+  @DefaultMessage("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}")
+  @Key("args10")
+  String args10(String arg0,  String arg1,  String arg2,  String arg3,  String arg4,  String arg5,  String arg6,  String arg7,  String arg8,  String arg9);
+
+  /**
+   * Translated "{1} is the second arg, {0} is the first".
+   * 
+   * @return translated "{1} is the second arg, {0} is the first"
+   */
+  @DefaultMessage("{1} is the second arg, {0} is the first")
+  @Key("args2")
+  String args2(String arg0,  String arg1);
+
+  /**
+   * Translated "arg0arg1 arg0,arg1 {0}arg4".
+   * 
+   * @return translated "arg0arg1 arg0,arg1 {0}arg4"
+   */
+  @DefaultMessage("arg0arg1 arg0,arg1 {0}arg4")
+  @Key("argsTest")
+  String argsTest(String arg0);
+
+  /**
+   * Translated "{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', '{0}', ''{1}''".
+   * 
+   * @return translated "{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', '{0}', ''{1}''"
+   */
+  @DefaultMessage("{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', '{0}', ''{1}''")
+  @Key("argsWithQuotes")
+  String argsWithQuotes(String arg0,  String arg1);
 
   /**
    * Translated "".
    * 
    * @return translated ""
-   * @gwt.key empty
    */
+  @DefaultMessage("")
+  @Key("empty")
   String empty();
+
+  /**
+   * Translated "'{'quoted'}'".
+   * 
+   * @return translated "'{'quoted'}'"
+   */
+  @DefaultMessage("'{'quoted'}'")
+  @Key("quotedBraces")
+  String quotedBraces();
+
+  /**
+   * Translated "{0}".
+   * 
+   * @return translated "{0}"
+   */
+  @DefaultMessage("{0}")
+  @Key("simpleMessageTest")
+  String simpleMessageTest(String arg0);
+
+  /**
+   * Translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}".
+   * 
+   * @return translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}"
+   */
+  @DefaultMessage("repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}")
+  @Key("testLotsOfUsageOfArgs")
+  String testLotsOfUsageOfArgs(String arg0,  String arg1);
+
+  /**
+   * Translated "\"~\" ~~ \"~~~~ \"\"".
+   * 
+   * @return translated "\"~\" ~~ \"~~~~ \"\""
+   */
+  @DefaultMessage("\"~\" ~~ \"~~~~ \"\"")
+  @Key("testWithXs")
+  String testWithXs();
+
+  /**
+   * Translated "お{0}你{1}好".
+   * 
+   * @return translated "お{0}你{1}好"
+   */
+  @DefaultMessage("お{0}你{1}好")
+  @Key("unicode")
+  String unicode(String arg0,  String arg1);
 }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.java b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.java
new file mode 100644
index 0000000..4e7887c
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.java
@@ -0,0 +1,50 @@
+/*
+ * 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.i18n.client.gen;
+
+/**
+ * Interface to represent the messages contained in resource bundle:
+ * 	'com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties'.
+ */
+public interface TestMessagesQuoting extends com.google.gwt.i18n.client.Messages {
+  
+  /**
+   * Translated "Embedded\r\ncr-nl.".
+   * 
+   * @return translated "Embedded\r\ncr-nl."
+   */
+  @DefaultMessage("Embedded\r\ncr-nl.")
+  @Key("embeddedCRNL")
+  String embeddedCRNL();
+
+  /**
+   * Translated "This line has an\nembedded newline".
+   * 
+   * @return translated "This line has an\nembedded newline"
+   */
+  @DefaultMessage("This line has an\nembedded newline")
+  @Key("embeddedNL")
+  String embeddedNL();
+
+  /**
+   * Translated "\"Don''t worry, be happy\" he said.".
+   * 
+   * @return translated "\"Don''t worry, be happy\" he said."
+   */
+  @DefaultMessage("\"Don''t worry, be happy\" he said.")
+  @Key("embeddedQuote")
+  String embeddedQuote();
+}
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties
new file mode 100644
index 0000000..35263b8
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties
@@ -0,0 +1,3 @@
+embeddedNL=This line has an\nembedded newline
+embeddedCRNL=Embedded\r\ncr-nl.
+embeddedQuote="Don''t worry, be happy" he said.
diff --git a/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java b/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java
index 30d0024..389359d 100644
--- a/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java
+++ b/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java
@@ -50,7 +50,12 @@
       // Should be caught
     }
   }
-
+  
+  public void testConstantsQuoting() throws IOException  {
+    String className = CLIENT_SOURCE_PACKAGE + "TestConstantsQuoting";
+    I18NSync.createConstantsInterfaceFromClassName(className, CLIENT_SOURCE_DIR);
+  }
+  
   public void testFileIsDirCase() {
     try {
       I18NSync.createMessagesInterfaceFromClassName(CLIENT_SOURCE_PACKAGE, null);
@@ -77,15 +82,14 @@
     I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR);
   }
 
+  public void testMessagesQuoting() throws IOException  {
+    String className = CLIENT_SOURCE_PACKAGE + "TestMessagesQuoting";
+    I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR);
+  }
+
   public void testMethodRenaming() throws IOException {
     String className = CLIENT_SOURCE_PACKAGE + "TestBadKeys";
     I18NSync.createConstantsWithLookupInterfaceFromClassName(className,
         CLIENT_SOURCE_DIR);
   }
-
-  public void testWarning() throws IOException {
-    String className = CLIENT_SOURCE_PACKAGE + "TestReallyBadKeys";
-    I18NSync.createConstantsWithLookupInterfaceFromClassName(className);
-  }
-
 }
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
index dfe5cbe..a0b21f8 100644
--- a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
+++ b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
@@ -505,6 +505,12 @@
     return true;
   }
 
+  /**
+   * Wrap an exception in RuntimeException if necessary so it doesn't have to be listed in
+   * throws clauses.
+   * 
+   * @param caught exception to wrap
+   */
   public static void rethrowException(Throwable caught) {
     if (caught instanceof RuntimeException) {
       throw (RuntimeException) caught;
@@ -512,7 +518,6 @@
       throw new RuntimeException(caught);
     }
   }
-
   private static boolean equalsWithNullCheck(Object a, Object b) {
     return a == b || (a != null && a.equals(b));
   }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java
index 7edbbfa..d31416f 100644
--- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java
+++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java
@@ -19,12 +19,69 @@
  * Service used to test unicode escaping.
  */
 public interface UnicodeEscapingService extends RemoteService {
+  
+  /**
+   * Exception for escaping errors.
+   */
+  public static class InvalidCharacterException extends Exception {
+
+    private static String toHex(int val) {
+      String hex = Integer.toHexString(val);
+      return "00000".substring(hex.length()) + hex;
+    }
+
+    private int index;
+    private int expected;
+    private int actual;
+
+    protected InvalidCharacterException() { }
+
+    public InvalidCharacterException(int index, int expected, int actual) {
+      super(index < 0 ? "String length mismatch: expected = " + expected + ", actual = " + actual
+          : "At index " + index + ", expected = U+" + toHex(expected) + ", actual = U+"
+          + toHex(actual));
+      this.index = index;
+      this.expected = expected;
+      this.actual = actual;
+    }
+
+    public int getActual() {
+      return actual;
+    }
+
+    public int getExpected() {
+      return expected;
+    }
+
+    public int getIndex() {
+      return index;
+    }
+  }
+
   /**
    * Returns a string containing the characters from start to end.
    * 
-   * @param start start character value, inclusive
-   * @param end end character value, exclusive
+   * Used to verify server->client escaping.
+   * 
+   * @param start start character value, inclusive -- note if greater
+   *     than {@link Character#MIN_SUPPLEMENTARY_CODE_POINT} it will
+   *     be included as surrogate pairs in the returned string.
+   * @param end end character value, exclusive (see above comment)
    * @return a string containing the characters from start to end
    */
   String getStringContainingCharacterRange(int start, int end);
+
+  /**
+   * Verifies that the string contains the specified characters.
+   * 
+   * Used to verify client->server escaping.
+   *
+   * @param start start code point value included
+   * @param end first code point not included
+   * @param str string to verify
+   * @throws InvalidCharacterException if the string does not contain the specified characters
+   * @return true if the verification succeeded
+   */
+  boolean verifyStringContainingCharacterRange(int start, int end, String str)
+      throws InvalidCharacterException;
 }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java
index f563218..6ae9fd4 100644
--- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java
+++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java
@@ -15,10 +15,14 @@
  */
 package com.google.gwt.user.client.rpc;
 
+import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException;
+
 /**
  * Async version of the {@link UnicodeEscapingService} interface.
  */
 public interface UnicodeEscapingServiceAsync {
   void getStringContainingCharacterRange(int start, int end,
-      AsyncCallback callback);
+      AsyncCallback<String> callback);
+  void verifyStringContainingCharacterRange(int start, int end, String str,
+      AsyncCallback<Boolean> callback) throws InvalidCharacterException;
 }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
index 1c5f989..f45777e 100644
--- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
@@ -17,68 +17,256 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException;
 
 /**
- * Test which verifies that we properly escape JSON strings sent back from the
- * server.
+ * Test that any valid string can be sent via RPC in both directions.
+ * 
+ * TODO(jat): make unpaired surrogates work properly if it is possible to do
+ * so on all browsers, then add them to this test.
  */
 public class UnicodeEscapingTest extends GWTTestCase {
 
-  private static final int DEFAULT_TEST_FINISH_DELAY_MS = 5000;
-  private static final int CHARACTER_RANGE_SIZE = 1024;
-  private static final int LAST_CHARACTER = 0x10000;
+  /** the size of a block of characters to test */
+  private static final int CHARACTER_BLOCK_SIZE = 64;
 
-  private int start = 0;
+  /**
+   * When doing the non-BMP test, we don't test every block of characters
+   * because it takes too long - this is the increment to use.  It is not a
+   * power of two so we alter the alignment of the block of characters we skip.
+   */
+  private static final int NON_BMP_TEST_INCREMENT = 8192 + 64;
 
+  /** the time to wait for the test of a block of characters */
+  private static final int TEST_FINISH_DELAY_MS = 500000;
+
+  /**
+   * Generates a string containing a sequence of code points.
+   * 
+   * @param start first code point to include in the string
+   * @param end one past the last code point to include in the string
+   * @return a string containing all the requested code points
+   */
+  public static String getStringContainingCharacterRange(int start, int end) {
+    StringBuffer buf = new StringBuffer();
+    for (int codePoint = start; codePoint < end; ++codePoint) {
+      if (Character.isSupplementaryCodePoint(codePoint)) {
+        buf.append(Character.toChars(codePoint));
+      } else {
+        buf.append((char) codePoint);
+      }
+    }
+
+    return buf.toString();
+  }
+
+  /*
+   * Copied from HistoryTest.
+   */
+  private static native boolean isSafari2() /*-{
+    var exp = / AppleWebKit\/([\d]+)/;
+    var result = exp.exec(navigator.userAgent);
+    if (result) {
+      // The standard history implementation works fine on WebKit >= 522
+      // (Safari 3 beta).
+      if (parseInt(result[1]) >= 522) {
+        return false;
+      }
+    }
+  
+    // The standard history implementation works just fine on the iPhone, which
+    // unfortunately reports itself as WebKit/420+.
+    if (navigator.userAgent.indexOf('iPhone') != -1) {
+      return false;
+    }
+  
+    return true;
+  }-*/;
+  /**
+   * Verifies that the supplied string includes the requested code points.
+   * 
+   * @param start first code point to include in the string
+   * @param end one past the last code point to include in the string
+   * @param str the string to test
+   * @throws InvalidCharacterException if a character doesn't match
+   * @throws RuntimeException if the string is too long
+   */
+  public static void verifyStringContainingCharacterRange(int start, int end,
+      String str) throws InvalidCharacterException {
+    if (str == null) {
+      throw new NullPointerException("String is null");
+    }
+    int expectedLen = end - start;
+    int strLen = str.codePointCount(0, str.length());
+    for (int i = 0, codePoint = start; i < strLen;
+        i = Character.offsetByCodePoints(str, i, 1)) {
+      int strCodePoint = str.codePointAt(i);
+      if (strCodePoint != codePoint) {
+        throw new InvalidCharacterException(i, codePoint, strCodePoint);
+      }
+      ++codePoint;
+    }
+    if (strLen < expectedLen) {
+      throw new InvalidCharacterException(strLen, start + strLen, -1);
+    } else if (expectedLen != strLen) {
+      throw new RuntimeException("Too many characters returned on block from U+"
+          + Integer.toHexString(start) + " to U+" + Integer.toHexString(end)
+          + ": expected=" + expectedLen + ", actual=" + strLen);
+    }
+  }
   private static UnicodeEscapingServiceAsync getService() {
-    UnicodeEscapingServiceAsync service = (UnicodeEscapingServiceAsync) GWT.create(UnicodeEscapingService.class);
+    UnicodeEscapingServiceAsync service = GWT.create(
+        UnicodeEscapingService.class);
     ServiceDefTarget target = (ServiceDefTarget) service;
     target.setServiceEntryPoint(GWT.getModuleBaseURL() + "unicodeEscape");
     return service;
   }
 
+  /** start of current block being tested */
+  private int current;
+
+  @Override
   public String getModuleName() {
     return "com.google.gwt.user.RPCSuite";
   }
 
   /**
+   * Generate strings containing ranges of characters and sends them to the
+   * server for verification. This ensures that client->server string escaping
+   * properly handles all BMP characters.
+   * 
+   * Unpaired or improperly paired surrogates are not tested here, as some
+   * browsers refuse to accept them.  Properly paired surrogates are tested
+   * in the non-BMP test.
+   *  
+   * Note that this does not test all possible combinations, which might be an
+   * issue, particularly with combining marks, though they should be logically
+   * equivalent in that case.
+   * 
+   * @throws InvalidCharacterException
+   */
+  public void testClientToServerBMP() throws InvalidCharacterException {
+    delayTestFinish(TEST_FINISH_DELAY_MS);
+    if (isSafari2()) {
+      // Safari2 can't be fixed for many characters, including null
+      // We only guarantee that basic ISO-Latin characters are unmolested.
+      clientToServerVerifyRange(0x0001, 0x0300, CHARACTER_BLOCK_SIZE,
+          CHARACTER_BLOCK_SIZE);
+    } else {
+      clientToServerVerifyRange(Character.MIN_CODE_POINT,
+          Character.MIN_SURROGATE, CHARACTER_BLOCK_SIZE,
+          CHARACTER_BLOCK_SIZE);
+      clientToServerVerifyRange(Character.MAX_SURROGATE + 1,
+          Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE,
+          CHARACTER_BLOCK_SIZE);
+    }
+  }
+
+  /**
+   * Generate strings containing ranges of characters and sends them to the
+   * server for verification. This ensures that client->server string escaping
+   * properly handles all non-BMP characters.
+   * 
+   * Note that this does not test all possible combinations, which might be an
+   * issue, particularly with combining marks, though they should be logically
+   * equivalent in that case.
+   * 
+   * @throws InvalidCharacterException
+   */
+  public void testClientToServerNonBMP() throws InvalidCharacterException {
+    delayTestFinish(TEST_FINISH_DELAY_MS);
+    clientToServerVerifyRange(Character.MIN_SUPPLEMENTARY_CODE_POINT,
+        Character.MAX_CODE_POINT + 1, CHARACTER_BLOCK_SIZE,
+        NON_BMP_TEST_INCREMENT);
+  }
+
+  /**
+   * Requests strings of CHARACTER_RANGE_SIZE from the server and validates
+   * that the returned string length matches CHARACTER_RANGE_SIZE and that all
+   * of the characters remain intact.
+   * 
+   * Note that this does not test all possible combinations, which might be an
+   * issue, particularly with combining marks, though they should be logically
+   * equivalent in that case.
+   */
+  public void testServerToClientBMP() {
+    delayTestFinish(TEST_FINISH_DELAY_MS);
+    serverToClientVerify(Character.MIN_CODE_POINT,
+        Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE,
+        CHARACTER_BLOCK_SIZE);
+  }
+
+  /**
    * Requests strings of CHARACTER_RANGE_SIZE from the server and validates that
    * the returned string length matches CHARACTER_RANGE_SIZE and that all of the
-   * characters remain intact.
+   * characters remain intact.  Note that this test verifies non-BMP characters
+   * (ie, those which are represented as pairs of surrogates).
+   * 
+   * Note that this does not test all possible combinations, which might be an
+   * issue, particularly with combining marks, though they should be logically
+   * equivalent in that case.
    */
-  public void testUnicodeEscaping() {
-    delayTestFinish(DEFAULT_TEST_FINISH_DELAY_MS);
+  public void testServerToClientNonBMP() {
+    delayTestFinish(TEST_FINISH_DELAY_MS);
+    serverToClientVerify(Character.MIN_SUPPLEMENTARY_CODE_POINT,
+        Character.MAX_CODE_POINT + 1, CHARACTER_BLOCK_SIZE,
+        NON_BMP_TEST_INCREMENT);
+  }
 
-    getService().getStringContainingCharacterRange(0, CHARACTER_RANGE_SIZE,
-        new AsyncCallback() {
-          public void onFailure(Throwable caught) {
-            TestSetValidator.rethrowException(caught);
+  private void clientToServerVerifyRange(final int start, final int end,
+      final int size, final int step) throws InvalidCharacterException {
+    current = start;
+    int blockEnd = Math.min(end, current + size);
+    getService().verifyStringContainingCharacterRange(current, blockEnd,
+        getStringContainingCharacterRange(start, blockEnd),
+        new AsyncCallback<Boolean>() {
+      public void onFailure(Throwable caught) {
+        TestSetValidator.rethrowException(caught);
+      }
+
+      public void onSuccess(Boolean ignored) {
+        current += step;
+        if (current < end) {
+          delayTestFinish(TEST_FINISH_DELAY_MS);
+          int blockEnd = Math.min(end, current + size);
+          try {
+            getService().verifyStringContainingCharacterRange(current, blockEnd,
+                getStringContainingCharacterRange(current, blockEnd), this);
+          } catch (InvalidCharacterException e) {
+            TestSetValidator.rethrowException(e);
           }
+        } else {
+          finishTest();
+        }
+      }
+    });
+  }
 
-          public void onSuccess(Object result) {
-            String str = (String) result;
+  private void serverToClientVerify(final int start, final int end,
+      final int size, final int step) {
+    current = start;
+    getService().getStringContainingCharacterRange(start, Math.min(end,
+        current + size), new AsyncCallback<String>() {
+      public void onFailure(Throwable caught) {
+        TestSetValidator.rethrowException(caught);
+      }
 
-            assertTrue("expected: " + Integer.toString(CHARACTER_RANGE_SIZE)
-                + " actual: " + str.length() + " for character range ["
-                + Integer.toString(start) + ", "
-                + Integer.toString(start + CHARACTER_RANGE_SIZE) + ")",
-                CHARACTER_RANGE_SIZE == str.length());
-
-            char[] chars = str.toCharArray();
-            for (int i = 0; i < CHARACTER_RANGE_SIZE; ++i) {
-              assertEquals(i + start, chars[i]);
-            }
-
-            start += CHARACTER_RANGE_SIZE;
-            if (start < LAST_CHARACTER) {
-              delayTestFinish(DEFAULT_TEST_FINISH_DELAY_MS);
-
-              getService().getStringContainingCharacterRange(start,
-                  start + CHARACTER_RANGE_SIZE, this);
-            } else {
-              finishTest();
-            }
-          }
-        });
+      public void onSuccess(String str) {
+        try {
+          verifyStringContainingCharacterRange(current, Math.min(end,
+              current + size), str);
+        } catch (InvalidCharacterException e) {
+          TestSetValidator.rethrowException(e);
+        }
+        current += step;
+        if (current < end) {
+          delayTestFinish(TEST_FINISH_DELAY_MS);
+          getService().getStringContainingCharacterRange(current,
+              Math.min(end, current + size), this);
+        } else {
+          finishTest();
+        }
+      }
+    });
   }
 }
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
index 8eb1bb3..20dd1b9 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -15,10 +15,14 @@
  */
 package com.google.gwt.user.server.rpc;
 
+import static com.google.gwt.user.client.rpc.impl.AbstractSerializationStream.RPC_SEPARATOR_CHAR;
+
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.RemoteService;
 import com.google.gwt.user.client.rpc.SerializableException;
 import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
 
 import junit.framework.TestCase;
 
@@ -27,6 +31,7 @@
 /**
  * Tests for the {@link com.google.gwt.user.server.rpc.RPC RPC} class.
  */
+@SuppressWarnings("deprecation")
 public class RPCTest extends TestCase {
 
   private static interface A extends RemoteService {
@@ -41,50 +46,74 @@
     void method1();
   }
 
-  private static final String VALID_ENCODED_REQUEST = "4\uffff" + // version
-      "0\uffff" + // flags
-      "4\uffff" + // string table entry count
-      A.class.getName() + "\uffff" + // string table entry #0
-      "method2" + "\uffff" + // string table entry #1
-      "moduleBaseURL" + "\uffff" + // string table entry #2
-      "whitelistHashcode" + "\uffff" + // string table entry #4
-      "3\uffff" + // module base URL
-      "4\uffff" + // whitelist hashcode
-      "1\uffff" + // interface name
-      "2\uffff" + // method name
-      "0\uffff"; // param count
+  private static final String VALID_ENCODED_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "4" + RPC_SEPARATOR_CHAR + // string table entry count
+      A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1
+      "method2" + RPC_SEPARATOR_CHAR + // string table entry #2
+      "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3
+      "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "3" + RPC_SEPARATOR_CHAR + // module base URL
+      "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "1" + RPC_SEPARATOR_CHAR + // interface name
+      "2" + RPC_SEPARATOR_CHAR + // method name
+      "0" + RPC_SEPARATOR_CHAR; // param count
 
-  private static final String INVALID_METHOD_REQUEST = "4\uffff" + // version
-      "0\uffff" + // flags
-      "4\uffff" + // string table entry count
-      A.class.getName() + "\uffff" + // string table entry #0
-      "method3" + "\uffff" + // string table entry #1
-      "moduleBaseURL" + "\uffff" + // string table entry #2
-      "whitelistHashcode" + "\uffff" + // string table entry #4
-      "3\uffff" + // module base URL
-      "4\uffff" + // whitelist hashcode
-      "1\uffff" + // interface name
-      "2\uffff" + // method name
-      "0\uffff"; // param count
+  private static final String INVALID_METHOD_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "4" + RPC_SEPARATOR_CHAR + // string table entry count
+      A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1
+      "method3" + RPC_SEPARATOR_CHAR + // string table entry #2
+      "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3
+      "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "3" + RPC_SEPARATOR_CHAR + // module base URL
+      "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "1" + RPC_SEPARATOR_CHAR + // interface name
+      "2" + RPC_SEPARATOR_CHAR + // method name
+      "0" + RPC_SEPARATOR_CHAR; // param count
 
-  private static final String INVALID_INTERFACE_REQUEST = "4\uffff" + // version
-      "0\uffff" + // flags
-      "4\uffff" + // string table entry count
-      B.class.getName() + "\uffff" + // string table entry #0
-      "method1" + "\uffff" + // string table entry #1
-      "moduleBaseURL" + "\uffff" + // string table entry #2
-      "whitelistHashcode" + "\uffff" + // string table entry #4
-      "3\uffff" + // module base URL
-      "4\uffff" + // whitelist hashcode
-      "1\uffff" + // interface name
-      "2\uffff" + // method name
-      "0\uffff"; // param count
+  private static final String INVALID_INTERFACE_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "4" + RPC_SEPARATOR_CHAR + // string table entry count
+      B.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1
+      "method1" + RPC_SEPARATOR_CHAR + // string table entry #2
+      "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3
+      "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "3" + RPC_SEPARATOR_CHAR + // module base URL
+      "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "1" + RPC_SEPARATOR_CHAR + // interface name
+      "2" + RPC_SEPARATOR_CHAR + // method name
+      "0" + RPC_SEPARATOR_CHAR; // param count
+
+  private static final String STRING_QUOTE_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "7" + RPC_SEPARATOR_CHAR + // string table entry count
+      A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1
+      "method2" + RPC_SEPARATOR_CHAR + // string table entry #2
+      "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3
+      "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "Raw backslash \\\\" + RPC_SEPARATOR_CHAR + // string table entry #5
+      "Quoted separator \\!" + RPC_SEPARATOR_CHAR + // string table entry #6
+      "\\uffff\\\\!\\\\0\\0" + RPC_SEPARATOR_CHAR + // string table entry #7
+      "3" + RPC_SEPARATOR_CHAR + // module base URL
+      "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "5" + RPC_SEPARATOR_CHAR + // begin test data
+      "6" + RPC_SEPARATOR_CHAR +
+      "7" + RPC_SEPARATOR_CHAR;
 
   private static final String VALID_V2_ENCODED_REQUEST = "2\uffff" + // version
       "0\uffff" + // flags
       "2\uffff" + // string table entry count
-      A.class.getName() + "\uffff" + // string table entry #0
-      "method2" + "\uffff" + // string table entry #1
+      A.class.getName() + "\uffff" + // string table entry #1
+      "method2\uffff" + // string table entry #2
       "1\uffff" + // interface name
       "2\uffff" + // method name
       "0\uffff"; // param count
@@ -92,9 +121,22 @@
   private static final String VALID_V3_ENCODED_REQUEST = "3\uffff" + // version
       "0\uffff" + // flags
       "4\uffff" + // string table entry count
-      A.class.getName() + "\uffff" + // string table entry #0
-      "method2" + "\uffff" + // string table entry #1
-      "moduleBaseURL" + "\uffff" + // string table entry #2
+      A.class.getName() + "\uffff" + // string table entry #1
+      "method2\uffff" + // string table entry #2
+      "moduleBaseURL\uffff" + // string table entry #3
+      "whitelistHashcode\uffff" + // string table entry #4
+      "3\uffff" + // module base URL
+      "4\uffff" + // whitelist hashcode
+      "1\uffff" + // interface name
+      "2\uffff" + // method name
+      "0\uffff"; // param count
+
+  private static final String VALID_V4_ENCODED_REQUEST = "4\uffff" + // version
+      "0\uffff" + // flags
+      "4\uffff" + // string table entry count
+      A.class.getName() + "\uffff" + // string table entry #1
+      "method2" + "\uffff" + // string table entry #2
+      "moduleBaseURL" + "\uffff" + // string table entry #3
       "whitelistHashcode" + "\uffff" + // string table entry #4
       "3\uffff" + // module base URL
       "4\uffff" + // whitelist hashcode
@@ -120,6 +162,13 @@
     } catch (IncompatibleRemoteServiceException e) {
       // Expected
     }
+
+    try {
+      RPC.decodeRequest(VALID_V4_ENCODED_REQUEST, A.class, null);
+      fail("Should have thrown an IncompatibleRemoteServiceException");
+    } catch (IncompatibleRemoteServiceException e) {
+      // Expected
+    }
   }
 
   /**
@@ -402,4 +451,12 @@
       }
     }, A_method1, null);
   }
+  
+  public void testSerializationStreamDequote() throws SerializationException {
+    ServerSerializationStreamReader reader = new ServerSerializationStreamReader(null, null);
+    reader.prepareToRead(STRING_QUOTE_REQUEST);
+    assertEquals("Raw backslash \\", reader.readString());
+    assertEquals("Quoted separator " + RPC_SEPARATOR_CHAR, reader.readString());
+    assertEquals("\uffff\\!\\0\u0000", reader.readString());
+  }
 }
diff --git a/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java
index 76bd2e7..7d4e99f 100644
--- a/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java
+++ b/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.server.rpc;
 
 import com.google.gwt.user.client.rpc.UnicodeEscapingService;
+import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
 
 /**
  * Implementation of the {@link UnicodeEscapingService} interface. 
@@ -24,18 +25,18 @@
     UnicodeEscapingService {
 
   /**
-   * @see com.google.gwt.user.client.rpc.UnicodeEscapingService#getStringContainingCharacterRange(int,
-   *      int)
+   * @see UnicodeEscapingService#getStringContainingCharacterRange(int, int)
    */
   public String getStringContainingCharacterRange(int start, int end) {
-    int nChars = end - start;
-      
-    char[] chars = new char[nChars];
-    for (int i = 0; i < nChars; ++i) {
-      char ch = (char) (start + i);
-      chars[i] = ch;
-    }
+    return UnicodeEscapingTest.getStringContainingCharacterRange(start, end);
+  }
 
-    return new String(chars);
+  /**
+   * @see UnicodeEscapingService#verifyStringContainingCharacterRange(int, int, String)
+   */
+  public boolean verifyStringContainingCharacterRange(int start, int end,
+      String str) throws InvalidCharacterException {
+    UnicodeEscapingTest.verifyStringContainingCharacterRange(start, end, str);
+    return true;
   }
 }
diff --git a/user/test/com/google/gwt/xml/client/XMLTest.java b/user/test/com/google/gwt/xml/client/XMLTest.java
index a34f366..180bad7 100644
--- a/user/test/com/google/gwt/xml/client/XMLTest.java
+++ b/user/test/com/google/gwt/xml/client/XMLTest.java
@@ -312,7 +312,13 @@
     assertEquals(top.getChildNodes().getLength(), 1);
   }
 
-  public void testParse() {
+  /**
+   * This test is failing on one Safari configuration in web mode in the 1.5
+   * release branch, but it passes in all other configurations and in the trunk.
+   * The files in the xml package are identical between the trunk and the 1.5
+   * branch.
+   */
+  public void disabledTestParse() {
     Document docA = XMLParser.parse("<!--hello-->   <a spam=\"ham\">\n  <?pi hello ?>dfgdfg  <b/>\t</a>");
 
     Document docB = XMLParser.createDocument();