Bug 1026680 part 5 - Make escape work with Latin1 strings. r=njn
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 18 Jun 2014 11:33:52 +0200
changeset 189454 0784505c202aea8b270f2fa7b1622f4b1a9f16f8
parent 189453 fd9bc8054cdd0098c0cb870e44d4df4304c979f2
child 189455 47d6ee2696920c1e0bbfdae287645de00a0e4ea5
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersnjn
bugs1026680
milestone33.0a1
Bug 1026680 part 5 - Make escape work with Latin1 strings. r=njn
js/src/jit-test/tests/latin1/escape-unescape.js
js/src/jsstr.cpp
--- a/js/src/jit-test/tests/latin1/escape-unescape.js
+++ b/js/src/jit-test/tests/latin1/escape-unescape.js
@@ -3,8 +3,16 @@ s = toLatin1("a%2b%20def%00A0");
 assertEq(unescape(s), "a+ def\x00A0");
 
 s = toLatin1("a%2b%20def%00A0%u1200");
 assertEq(unescape(s), "a+ def\x00A0\u1200");
 
 // TwoByte
 s += "\u1200";
 assertEq(unescape(s), "a+ def\x00A0\u1200\u1200");
+
+// Latin1
+s = toLatin1("abc \u00ff");
+assertEq(escape(s), "abc%20%FF");
+
+// TwoByte
+s += "\u1200";
+assertEq(escape(s), "abc%20%FF%u1200");
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -108,90 +108,109 @@ static bool
 str_encodeURI_Component(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Global string methods
  */
 
 
 /* ES5 B.2.1 */
-static bool
-str_escape(JSContext *cx, unsigned argc, Value *vp)
+template <typename CharT>
+static jschar *
+Escape(JSContext *cx, const CharT *chars, uint32_t length, uint32_t *newLengthOut)
 {
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    JSLinearString *str = ArgToRootedString(cx, args, 0);
-    if (!str)
-        return false;
-
-    uint32_t length = str->length();
-    const jschar *chars = str->chars();
-
     static const uint8_t shouldPassThrough[128] = {
          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
          0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,       /*    !"#$%&'()*+,-./  */
          1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,       /*   0123456789:;<=>?  */
          1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,       /*   @ABCDEFGHIJKLMNO  */
          1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,       /*   PQRSTUVWXYZ[\]^_  */
          0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,       /*   `abcdefghijklmno  */
          1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,       /*   pqrstuvwxyz{\}~  DEL */
     };
 
     /* Take a first pass and see how big the result string will need to be. */
-    uint32_t newlength = length;
+    uint32_t newLength = length;
     for (size_t i = 0; i < length; i++) {
         jschar ch = chars[i];
         if (ch < 128 && shouldPassThrough[ch])
             continue;
 
         /* The character will be encoded as %XX or %uXXXX. */
-        newlength += (ch < 256) ? 2 : 5;
+        newLength += (ch < 256) ? 2 : 5;
 
         /*
          * newlength is incremented by at most 5 on each iteration, so worst
          * case newlength == length * 6. This can't overflow.
          */
         static_assert(JSString::MAX_LENGTH < UINT32_MAX / 6,
                       "newlength must not overflow");
     }
 
-    ScopedJSFreePtr<jschar> newchars(cx->pod_malloc<jschar>(newlength + 1));
-    if (!newchars)
-        return false;
+    jschar *newChars = cx->pod_malloc<jschar>(newLength + 1);
+    if (!newChars)
+        return nullptr;
 
     static const char digits[] = "0123456789ABCDEF";
 
     size_t i, ni;
     for (i = 0, ni = 0; i < length; i++) {
         jschar ch = chars[i];
         if (ch < 128 && shouldPassThrough[ch]) {
-            newchars[ni++] = ch;
+            newChars[ni++] = ch;
         } else if (ch < 256) {
-            newchars[ni++] = '%';
-            newchars[ni++] = digits[ch >> 4];
-            newchars[ni++] = digits[ch & 0xF];
+            newChars[ni++] = '%';
+            newChars[ni++] = digits[ch >> 4];
+            newChars[ni++] = digits[ch & 0xF];
         } else {
-            newchars[ni++] = '%';
-            newchars[ni++] = 'u';
-            newchars[ni++] = digits[ch >> 12];
-            newchars[ni++] = digits[(ch & 0xF00) >> 8];
-            newchars[ni++] = digits[(ch & 0xF0) >> 4];
-            newchars[ni++] = digits[ch & 0xF];
+            newChars[ni++] = '%';
+            newChars[ni++] = 'u';
+            newChars[ni++] = digits[ch >> 12];
+            newChars[ni++] = digits[(ch & 0xF00) >> 8];
+            newChars[ni++] = digits[(ch & 0xF0) >> 4];
+            newChars[ni++] = digits[ch & 0xF];
         }
     }
-    JS_ASSERT(ni == newlength);
-    newchars[newlength] = 0;
-
-    JSString *retstr = js_NewString<CanGC>(cx, newchars.get(), newlength);
-    if (!retstr)
+    JS_ASSERT(ni == newLength);
+    newChars[newLength] = 0;
+
+    *newLengthOut = newLength;
+    return newChars;
+}
+
+static bool
+str_escape(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSLinearString *str = ArgToRootedString(cx, args, 0);
+    if (!str)
         return false;
 
-    newchars.forget();
-    args.rval().setString(retstr);
+    /* TODO: Once Latin1 strings are enabled, return a Latin1 string. */
+    ScopedJSFreePtr<jschar> newChars;
+    uint32_t newLength;
+    if (str->hasLatin1Chars()) {
+        AutoCheckCannotGC nogc;
+        newChars = Escape(cx, str->latin1Chars(nogc), str->length(), &newLength);
+    } else {
+        AutoCheckCannotGC nogc;
+        newChars = Escape(cx, str->twoByteChars(nogc), str->length(), &newLength);
+    }
+
+    if (!newChars)
+        return false;
+
+    JSString *res = js_NewString<CanGC>(cx, newChars.get(), newLength);
+    if (!res)
+        return false;
+
+    newChars.forget();
+    args.rval().setString(res);
     return true;
 }
 
 template <typename CharT>
 static inline bool
 Unhex4(const RangedPtr<const CharT> chars, jschar *result)
 {
     jschar a = chars[0],