Bug 1018893 part 3 - Make startsWith and endsWith work with Latin1 strings. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 03 Jun 2014 09:32:26 +0200
changeset 205470 ff1d94fe525c12d6b5374e2a6ac4a829f931f052
parent 205469 3bd75df9c682576ddb69f77eaedeb9b37c19a911
child 205471 11aac63ed5d61a543f79fa8fccc804e8ad768266
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1018893
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1018893 part 3 - Make startsWith and endsWith work with Latin1 strings. r=luke
js/src/jit-test/tests/basic/latin1-indexOf.js
js/src/jit-test/tests/basic/latin1-indexing.js
js/src/jit-test/tests/basic/latin1.js
js/src/jit-test/tests/latin1/basic.js
js/src/jit-test/tests/latin1/indexOf.js
js/src/jit-test/tests/latin1/indexing.js
js/src/jit-test/tests/latin1/startsWith-endsWith.js
js/src/jsstr.cpp
rename from js/src/jit-test/tests/basic/latin1.js
rename to js/src/jit-test/tests/latin1/basic.js
rename from js/src/jit-test/tests/basic/latin1-indexOf.js
rename to js/src/jit-test/tests/latin1/indexOf.js
rename from js/src/jit-test/tests/basic/latin1-indexing.js
rename to js/src/jit-test/tests/latin1/indexing.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/startsWith-endsWith.js
@@ -0,0 +1,65 @@
+function testStartsWith() {
+    var s1 = toLatin1("abc\u0099def");
+    var s2 = toLatin1("abc\u0099d");
+    var s3 = toLatin1("abc\u0098d");
+    var s4 = toLatin1("bc\u0099");
+
+    // Latin1 + Latin1
+    assertEq(s1.startsWith(s2), true);
+    assertEq(s1.startsWith(s3), false);
+    assertEq(s1.startsWith(s4), false);
+    assertEq(s1.startsWith(s4, 1), true);
+    assertEq(s1.startsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.startsWith("abc\u0099\u1200".slice(0, -1)), true);
+    assertEq(s1.startsWith("abc\u0099e\u1200".slice(0, -1)), false);
+    assertEq(s1.startsWith("bc\u0099\u1200".slice(0, -1), 1), true);
+    assertEq(s1.startsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "abc\u0099de\u1200";
+    assertEq(s5.startsWith(s1), false);
+    assertEq(s5.startsWith(s2), true);
+    assertEq(s5.startsWith(s4), false);
+    assertEq(s5.startsWith(s4, 1), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.startsWith(s5), true);
+    assertEq(s5.startsWith("\u1200"), false);
+    assertEq(s5.startsWith("\u1200", 6), true);
+}
+testStartsWith();
+
+function testEndsWith() {
+    var s1 = toLatin1("zabc\u0099defg");
+    var s2 = toLatin1("\u0099defg");
+    var s3 = toLatin1("\u0098defg");
+    var s4 = toLatin1("zabc\u0099def");
+
+    // Latin1 + Latin1
+    assertEq(s1.endsWith(s2), true);
+    assertEq(s1.endsWith(s3), false);
+    assertEq(s1.endsWith(s4), false);
+    assertEq(s1.endsWith(s4, 8), true);
+    assertEq(s1.endsWith(s1), true);
+
+    // Latin1 + TwoByte
+    assertEq(s1.endsWith("abc\u0099defg\u1200".slice(0, -1)), true);
+    assertEq(s1.endsWith("\u1100efg\u1200".slice(0, -1)), false);
+    assertEq(s1.endsWith("bc\u0099\u1200".slice(0, -1), 5), true);
+    assertEq(s1.endsWith("\u1200"), false);
+
+    // TwoByte + Latin1
+    var s5 = "\u1200zabc\u0099defg";
+    assertEq(s5.endsWith(s1), true);
+    assertEq(s5.endsWith(s2), true);
+    assertEq(s5.endsWith(s4), false);
+    assertEq(s5.endsWith(s4, 9), true);
+
+    // TwoByte + TwoByte
+    assertEq(s5.endsWith(s5), true);
+    assertEq(s5.endsWith("\u1200"), false);
+    assertEq(s5.endsWith("\u1200za", 3), true);
+}
+testEndsWith();
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1556,16 +1556,49 @@ str_lastIndexOf(JSContext *cx, unsigned 
         else
             res = LastIndexOfImpl(textChars, textLen, pat->twoByteChars(nogc), patLen, start);
     }
 
     args.rval().setInt32(res);
     return true;
 }
 
+static bool
+EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
+{
+    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
+        if (jschar(*s1) != *s2)
+            return false;
+    }
+    return true;
+}
+
+static bool
+HasSubstringAt(JSLinearString *text, JSLinearString *pat, size_t start)
+{
+    MOZ_ASSERT(start + pat->length() <= text->length());
+
+    size_t patLen = pat->length();
+
+    AutoCheckCannotGC nogc;
+    if (text->hasLatin1Chars()) {
+        const Latin1Char *textChars = text->latin1Chars(nogc) + start;
+        if (pat->hasLatin1Chars())
+            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
+
+        return EqualCharsLatin1TwoByte(textChars, pat->twoByteChars(nogc), patLen);
+    }
+
+    const jschar *textChars = text->twoByteChars(nogc) + start;
+    if (pat->hasTwoByteChars())
+        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
+
+    return EqualCharsLatin1TwoByte(pat->latin1Chars(nogc), textChars, patLen);
+}
+
 /* ES6 20131108 draft 21.1.3.18. */
 static bool
 str_startsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
@@ -1595,35 +1628,35 @@ str_startsWith(JSContext *cx, unsigned a
             if (!ToInteger(cx, args[1], &d))
                 return false;
             pos = uint32_t(Min(Max(d, 0.0), double(UINT32_MAX)));
         }
     }
 
     // Step 9
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Step 10
     uint32_t start = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 12
     if (searchLen + start < searchLen || searchLen + start > textLen) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Steps 13 and 14
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 /* ES6 20131108 draft 21.1.3.7. */
 static bool
 str_endsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1642,19 +1675,16 @@ str_endsWith(JSContext *cx, unsigned arg
 
     // Steps 5 and 6
     Rooted<JSLinearString *> searchStr(cx, ArgToRootedString(cx, args, 0));
     if (!searchStr)
         return false;
 
     // Step 7
     uint32_t textLen = str->length();
-    const jschar *textChars = str->getChars(cx);
-    if (!textChars)
-        return false;
 
     // Steps 8 and 9
     uint32_t pos = textLen;
     if (args.hasDefined(1)) {
         if (args[1].isInt32()) {
             int i = args[1].toInt32();
             pos = (i < 0) ? 0U : uint32_t(i);
         } else {
@@ -1665,29 +1695,32 @@ str_endsWith(JSContext *cx, unsigned arg
         }
     }
 
     // Step 10
     uint32_t end = Min(Max(pos, 0U), textLen);
 
     // Step 11
     uint32_t searchLen = searchStr->length();
-    const jschar *searchChars = searchStr->chars();
 
     // Step 13 (reordered)
     if (searchLen > end) {
         args.rval().setBoolean(false);
         return true;
     }
 
     // Step 12
     uint32_t start = end - searchLen;
 
     // Steps 14 and 15
-    args.rval().setBoolean(PodEqual(textChars + start, searchChars, searchLen));
+    JSLinearString *text = str->ensureLinear(cx);
+    if (!text)
+        return false;
+
+    args.rval().setBoolean(HasSubstringAt(text, searchStr, start));
     return true;
 }
 
 static bool
 js_TrimString(JSContext *cx, Value *vp, bool trimLeft, bool trimRight)
 {
     CallReceiver call = CallReceiverFromVp(vp);
     RootedString str(cx, ThisToStringForStringProto(cx, call));
@@ -4241,26 +4274,16 @@ js::ValueToSource(JSContext *cx, HandleV
 
 JSString *
 js::StringToSource(JSContext *cx, JSString *str)
 {
     return js_QuoteString(cx, str, '"');
 }
 
 static bool
-EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
-{
-    for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (jschar(*s1) != *s2)
-            return false;
-    }
-    return true;
-}
-
-static bool
 EqualChars(JSLinearString *str1, JSLinearString *str2)
 {
     MOZ_ASSERT(str1->length() == str2->length());
 
     size_t len = str1->length();
 
     AutoCheckCannotGC nogc;
     if (str1->hasTwoByteChars()) {