Changeset 183694 in webkit


Ignore:
Timestamp:
May 1, 2015 3:44:30 PM (9 years ago)
Author:
commit-queue@webkit.org
Message:

String#startsWith/endsWith/includes don't handle Infinity position/endPosition args correctly
https://bugs.webkit.org/show_bug.cgi?id=144314

Patch by Jordan Harband <ljharb@gmail.com> on 2015-05-01
Reviewed by Darin Adler.

Source/JavaScriptCore:

Fixing handling of Infinity position args, per
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.includes
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith

  • runtime/StringPrototype.cpp:

(JSC::clampInt32):
(JSC::stringProtoFuncStartsWith):
(JSC::stringProtoFuncEndsWith):
(JSC::stringProtoFuncIncludes):

LayoutTests:

  • js/script-tests/string-includes.js:
  • js/string-includes-expected.txt:
Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r183686 r183694  
     12015-05-01  Jordan Harband  <ljharb@gmail.com>
     2
     3        String#startsWith/endsWith/includes don't handle Infinity position/endPosition args correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=144314
     5
     6        Reviewed by Darin Adler.
     7
     8        * js/script-tests/string-includes.js:
     9        * js/string-includes-expected.txt:
     10
    1112015-05-01  Martin Robinson  <mrobinson@igalia.com>
    212
  • trunk/LayoutTests/js/script-tests/string-includes.js

    r182872 r183694  
    1 description("This test checks the ES6 string functions startsWith(), endsWith() and includes().");
     1description("This test checks the ES6 string functions startsWith(), endsWith(), and includes().");
    22
    33// Test includes
     4shouldBe("String.prototype.includes.name", "'includes'");
    45shouldBe("String.prototype.includes.length", "1");
    56shouldBe("'foo bar'.includes('bar')", "true");
     
    3132shouldBe("'フーバー'.includes('ーバ')", "true");
    3233shouldBe("'フーバー'.includes('クー')", "false");
     34shouldBeFalse("'abc'.includes('a', 'abc'.length)");
     35shouldBeFalse("'abc'.includes('a', Math.pow(2, 33))");
     36shouldBeFalse("'abc'.includes('a', Infinity)");
     37shouldBeTrue("'abc'.includes('ab', -Infinity)");
     38shouldBeFalse("'abc'.includes('cd', -Infinity)");
     39shouldBeTrue("'abc'.includes('ab', 0)");
     40shouldBeFalse("'abc'.includes('cd', 0)");
    3341
    3442// Test startsWith
     43shouldBe("String.prototype.startsWith.name", "'startsWith'");
    3544shouldBe("String.prototype.startsWith.length", "1");
    3645shouldBe("'foo bar'.startsWith('foo')", "true");
     
    6170shouldBe("'foo bar'.startsWith('フー')", "false");
    6271shouldBe("'foo bar'.startsWith('フー', 1)", "false");
     72shouldBeFalse("'abc'.startsWith('a', Infinity)");
     73shouldBeFalse("'abc'.startsWith('a', 1)");
     74shouldBeTrue("'abc'.startsWith('b', 1)");
     75shouldBeFalse("'abc'.startsWith('b', 2)");
     76shouldBeTrue("'abc'.startsWith('c', 2)");
     77shouldBeFalse("'abc'.startsWith('a', Math.pow(2, 33))");
    6378
    6479// Test endsWith
     80shouldBe("String.prototype.endsWith.name", "'endsWith'");
    6581shouldBe("String.prototype.endsWith.length", "1");
    6682shouldBe("'foo bar'.endsWith('bar')", "true");
     
    94110shouldBe("'foo bar'.endsWith('フー')", "false");
    95111shouldBe("'foo bar'.endsWith('フー', 3)", "false");
     112shouldBeTrue("'abc'.endsWith('bc', Infinity)");
     113shouldBeTrue("'abc'.endsWith('bc', Math.pow(2, 33))");
     114shouldBeFalse("'abc'.endsWith('a', 0)");
     115shouldBeTrue("'abc'.endsWith('a', 1)");
     116shouldBeFalse("'abc'.endsWith('b', 1)");
     117shouldBeTrue("'abc'.endsWith('b', 2)");
     118shouldBeFalse("'abc'.endsWith('bc', 2)");
     119shouldBeTrue("'abc'.endsWith('bc', 3)");
    96120
    97121// Call functions with an environment record as 'this'.
  • trunk/LayoutTests/js/string-includes-expected.txt

    r182872 r183694  
    1 This test checks the ES6 string functions startsWith(), endsWith() and includes().
     1This test checks the ES6 string functions startsWith(), endsWith(), and includes().
    22
    33On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
    44
    55
     6PASS String.prototype.includes.name is 'includes'
    67PASS String.prototype.includes.length is 1
    78PASS 'foo bar'.includes('bar') is true
     
    3334PASS 'フーバー'.includes('ーバ') is true
    3435PASS 'フーバー'.includes('クー') is false
     36PASS 'abc'.includes('a', 'abc'.length) is false
     37PASS 'abc'.includes('a', Math.pow(2, 33)) is false
     38PASS 'abc'.includes('a', Infinity) is false
     39PASS 'abc'.includes('ab', -Infinity) is true
     40PASS 'abc'.includes('cd', -Infinity) is false
     41PASS 'abc'.includes('ab', 0) is true
     42PASS 'abc'.includes('cd', 0) is false
     43PASS String.prototype.startsWith.name is 'startsWith'
    3544PASS String.prototype.startsWith.length is 1
    3645PASS 'foo bar'.startsWith('foo') is true
     
    6170PASS 'foo bar'.startsWith('フー') is false
    6271PASS 'foo bar'.startsWith('フー', 1) is false
     72PASS 'abc'.startsWith('a', Infinity) is false
     73PASS 'abc'.startsWith('a', 1) is false
     74PASS 'abc'.startsWith('b', 1) is true
     75PASS 'abc'.startsWith('b', 2) is false
     76PASS 'abc'.startsWith('c', 2) is true
     77PASS 'abc'.startsWith('a', Math.pow(2, 33)) is false
     78PASS String.prototype.endsWith.name is 'endsWith'
    6379PASS String.prototype.endsWith.length is 1
    6480PASS 'foo bar'.endsWith('bar') is true
     
    92108PASS 'foo bar'.endsWith('フー') is false
    93109PASS 'foo bar'.endsWith('フー', 3) is false
     110PASS 'abc'.endsWith('bc', Infinity) is true
     111PASS 'abc'.endsWith('bc', Math.pow(2, 33)) is true
     112PASS 'abc'.endsWith('a', 0) is false
     113PASS 'abc'.endsWith('a', 1) is true
     114PASS 'abc'.endsWith('b', 1) is false
     115PASS 'abc'.endsWith('b', 2) is true
     116PASS 'abc'.endsWith('bc', 2) is false
     117PASS 'abc'.endsWith('bc', 3) is true
    94118PASS (function() { var f = String.prototype.startsWith; (function() { f('a'); })(); })() threw exception TypeError: Type error.
    95119PASS (function() { var f = String.prototype.endsWith; (function() { f('a'); })(); })() threw exception TypeError: Type error.
  • trunk/Source/JavaScriptCore/ChangeLog

    r183692 r183694  
     12015-05-01  Jordan Harband  <ljharb@gmail.com>
     2
     3        String#startsWith/endsWith/includes don't handle Infinity position/endPosition args correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=144314
     5
     6        Reviewed by Darin Adler.
     7
     8        Fixing handling of Infinity position args, per
     9        https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.includes
     10        https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.startswith
     11        https://people.mozilla.org/~jorendorff/es6-draft.html#sec-string.prototype.endswith
     12
     13        * runtime/StringPrototype.cpp:
     14        (JSC::clampInt32):
     15        (JSC::stringProtoFuncStartsWith):
     16        (JSC::stringProtoFuncEndsWith):
     17        (JSC::stringProtoFuncIncludes):
     18
    1192015-05-01  Basile Clement  <basile_clement@apple.com>
    220
  • trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp

    r183141 r183694  
    16701670}
    16711671
     1672static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
     1673{
     1674    if (value < min)
     1675        return min;
     1676    if (value > max)
     1677        return max;
     1678    return static_cast<unsigned>(value);
     1679}
     1680
    16721681EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
    16731682{
     
    16881697        return JSValue::encode(jsUndefined());
    16891698
    1690     unsigned start = std::max(0, exec->argument(1).toInt32(exec));
    1691     if (exec->hadException())
    1692         return JSValue::encode(jsUndefined());
     1699    JSValue positionArg = exec->argument(1);
     1700    unsigned start = 0;
     1701    if (positionArg.isInt32())
     1702        start = std::max(0, positionArg.asInt32());
     1703    else {
     1704        unsigned length = stringToSearchIn.length();
     1705        start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
     1706        if (exec->hadException())
     1707            return JSValue::encode(jsUndefined());
     1708    }
    16931709
    16941710    return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
     
    17141730
    17151731    unsigned length = stringToSearchIn.length();
    1716     JSValue a1 = exec->argument(1);
    1717     int pos = a1.isUndefined() ? length : a1.toInt32(exec);
    1718     if (exec->hadException())
    1719         return JSValue::encode(jsUndefined());
    1720     unsigned end = std::min<unsigned>(std::max(pos, 0), length);
    1721 
    1722     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, end)));
     1732
     1733    JSValue endPositionArg = exec->argument(1);
     1734    unsigned end = length;
     1735    if (endPositionArg.isInt32())
     1736        end = std::max(0, endPositionArg.asInt32());
     1737    else if (!endPositionArg.isUndefined()) {
     1738        end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
     1739        if (exec->hadException())
     1740            return JSValue::encode(jsUndefined());
     1741    }
     1742
     1743    return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
    17231744}
    17241745
     
    17411762        return JSValue::encode(jsUndefined());
    17421763
    1743     unsigned start = std::max(0, exec->argument(1).toInt32(exec));
    1744     if (exec->hadException())
    1745         return JSValue::encode(jsUndefined());
     1764    JSValue positionArg = exec->argument(1);
     1765    unsigned start = 0;
     1766    if (positionArg.isInt32())
     1767        start = std::max(0, positionArg.asInt32());
     1768    else {
     1769        unsigned length = stringToSearchIn.length();
     1770        start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
     1771        if (exec->hadException())
     1772            return JSValue::encode(jsUndefined());
     1773    }
    17461774
    17471775    return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
Note: See TracChangeset for help on using the changeset viewer.