Diff
Modified: trunk/LayoutTests/ChangeLog (183693 => 183694)
--- trunk/LayoutTests/ChangeLog 2015-05-01 22:38:02 UTC (rev 183693)
+++ trunk/LayoutTests/ChangeLog 2015-05-01 22:44:30 UTC (rev 183694)
@@ -1,3 +1,13 @@
+2015-05-01 Jordan Harband <[email protected]>
+
+ String#startsWith/endsWith/includes don't handle Infinity position/endPosition args correctly
+ https://bugs.webkit.org/show_bug.cgi?id=144314
+
+ Reviewed by Darin Adler.
+
+ * js/script-tests/string-includes.js:
+ * js/string-includes-expected.txt:
+
2015-05-01 Martin Robinson <[email protected]>
Unreviewed gardening for GTK+.
Modified: trunk/LayoutTests/js/script-tests/string-includes.js (183693 => 183694)
--- trunk/LayoutTests/js/script-tests/string-includes.js 2015-05-01 22:38:02 UTC (rev 183693)
+++ trunk/LayoutTests/js/script-tests/string-includes.js 2015-05-01 22:44:30 UTC (rev 183694)
@@ -1,6 +1,7 @@
-description("This test checks the ES6 string functions startsWith(), endsWith() and includes().");
+description("This test checks the ES6 string functions startsWith(), endsWith(), and includes().");
// Test includes
+shouldBe("String.prototype.includes.name", "'includes'");
shouldBe("String.prototype.includes.length", "1");
shouldBe("'foo bar'.includes('bar')", "true");
shouldBe("'foo bar'.includes('bar', 4)", "true");
@@ -30,8 +31,16 @@
shouldBe("'foo 1e100 bar'.includes(1e100)", "false");
shouldBe("'フーバー'.includes('ーバ')", "true");
shouldBe("'フーバー'.includes('クー')", "false");
+shouldBeFalse("'abc'.includes('a', 'abc'.length)");
+shouldBeFalse("'abc'.includes('a', Math.pow(2, 33))");
+shouldBeFalse("'abc'.includes('a', Infinity)");
+shouldBeTrue("'abc'.includes('ab', -Infinity)");
+shouldBeFalse("'abc'.includes('cd', -Infinity)");
+shouldBeTrue("'abc'.includes('ab', 0)");
+shouldBeFalse("'abc'.includes('cd', 0)");
// Test startsWith
+shouldBe("String.prototype.startsWith.name", "'startsWith'");
shouldBe("String.prototype.startsWith.length", "1");
shouldBe("'foo bar'.startsWith('foo')", "true");
shouldBe("'foo bar'.startsWith('foo', 0)", "true");
@@ -60,8 +69,15 @@
shouldBe("'フーバー'.startsWith('abc', 1)", "false");
shouldBe("'foo bar'.startsWith('フー')", "false");
shouldBe("'foo bar'.startsWith('フー', 1)", "false");
+shouldBeFalse("'abc'.startsWith('a', Infinity)");
+shouldBeFalse("'abc'.startsWith('a', 1)");
+shouldBeTrue("'abc'.startsWith('b', 1)");
+shouldBeFalse("'abc'.startsWith('b', 2)");
+shouldBeTrue("'abc'.startsWith('c', 2)");
+shouldBeFalse("'abc'.startsWith('a', Math.pow(2, 33))");
// Test endsWith
+shouldBe("String.prototype.endsWith.name", "'endsWith'");
shouldBe("String.prototype.endsWith.length", "1");
shouldBe("'foo bar'.endsWith('bar')", "true");
shouldBe("'foo bar'.endsWith('ba', 6)", "true");
@@ -93,6 +109,14 @@
shouldBe("'フーバー'.endsWith('abc')", "false");
shouldBe("'foo bar'.endsWith('フー')", "false");
shouldBe("'foo bar'.endsWith('フー', 3)", "false");
+shouldBeTrue("'abc'.endsWith('bc', Infinity)");
+shouldBeTrue("'abc'.endsWith('bc', Math.pow(2, 33))");
+shouldBeFalse("'abc'.endsWith('a', 0)");
+shouldBeTrue("'abc'.endsWith('a', 1)");
+shouldBeFalse("'abc'.endsWith('b', 1)");
+shouldBeTrue("'abc'.endsWith('b', 2)");
+shouldBeFalse("'abc'.endsWith('bc', 2)");
+shouldBeTrue("'abc'.endsWith('bc', 3)");
// Call functions with an environment record as 'this'.
shouldThrow("(function() { var f = String.prototype.startsWith; (function() { f('a'); })(); })()");
Modified: trunk/LayoutTests/js/string-includes-expected.txt (183693 => 183694)
--- trunk/LayoutTests/js/string-includes-expected.txt 2015-05-01 22:38:02 UTC (rev 183693)
+++ trunk/LayoutTests/js/string-includes-expected.txt 2015-05-01 22:44:30 UTC (rev 183694)
@@ -1,8 +1,9 @@
-This test checks the ES6 string functions startsWith(), endsWith() and includes().
+This test checks the ES6 string functions startsWith(), endsWith(), and includes().
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+PASS String.prototype.includes.name is 'includes'
PASS String.prototype.includes.length is 1
PASS 'foo bar'.includes('bar') is true
PASS 'foo bar'.includes('bar', 4) is true
@@ -32,6 +33,14 @@
PASS 'foo 1e100 bar'.includes(1e100) is false
PASS 'フーバー'.includes('ーバ') is true
PASS 'フーバー'.includes('クー') is false
+PASS 'abc'.includes('a', 'abc'.length) is false
+PASS 'abc'.includes('a', Math.pow(2, 33)) is false
+PASS 'abc'.includes('a', Infinity) is false
+PASS 'abc'.includes('ab', -Infinity) is true
+PASS 'abc'.includes('cd', -Infinity) is false
+PASS 'abc'.includes('ab', 0) is true
+PASS 'abc'.includes('cd', 0) is false
+PASS String.prototype.startsWith.name is 'startsWith'
PASS String.prototype.startsWith.length is 1
PASS 'foo bar'.startsWith('foo') is true
PASS 'foo bar'.startsWith('foo', 0) is true
@@ -60,6 +69,13 @@
PASS 'フーバー'.startsWith('abc', 1) is false
PASS 'foo bar'.startsWith('フー') is false
PASS 'foo bar'.startsWith('フー', 1) is false
+PASS 'abc'.startsWith('a', Infinity) is false
+PASS 'abc'.startsWith('a', 1) is false
+PASS 'abc'.startsWith('b', 1) is true
+PASS 'abc'.startsWith('b', 2) is false
+PASS 'abc'.startsWith('c', 2) is true
+PASS 'abc'.startsWith('a', Math.pow(2, 33)) is false
+PASS String.prototype.endsWith.name is 'endsWith'
PASS String.prototype.endsWith.length is 1
PASS 'foo bar'.endsWith('bar') is true
PASS 'foo bar'.endsWith('ba', 6) is true
@@ -91,6 +107,14 @@
PASS 'フーバー'.endsWith('abc') is false
PASS 'foo bar'.endsWith('フー') is false
PASS 'foo bar'.endsWith('フー', 3) is false
+PASS 'abc'.endsWith('bc', Infinity) is true
+PASS 'abc'.endsWith('bc', Math.pow(2, 33)) is true
+PASS 'abc'.endsWith('a', 0) is false
+PASS 'abc'.endsWith('a', 1) is true
+PASS 'abc'.endsWith('b', 1) is false
+PASS 'abc'.endsWith('b', 2) is true
+PASS 'abc'.endsWith('bc', 2) is false
+PASS 'abc'.endsWith('bc', 3) is true
PASS (function() { var f = String.prototype.startsWith; (function() { f('a'); })(); })() threw exception TypeError: Type error.
PASS (function() { var f = String.prototype.endsWith; (function() { f('a'); })(); })() threw exception TypeError: Type error.
PASS (function() { var f = String.prototype.includes; (function() { f('a'); })(); })() threw exception TypeError: Type error.
Modified: trunk/Source/_javascript_Core/ChangeLog (183693 => 183694)
--- trunk/Source/_javascript_Core/ChangeLog 2015-05-01 22:38:02 UTC (rev 183693)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-05-01 22:44:30 UTC (rev 183694)
@@ -1,3 +1,21 @@
+2015-05-01 Jordan Harband <[email protected]>
+
+ String#startsWith/endsWith/includes don't handle Infinity position/endPosition args correctly
+ https://bugs.webkit.org/show_bug.cgi?id=144314
+
+ Reviewed by Darin Adler.
+
+ 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):
+
2015-05-01 Basile Clement <[email protected]>
Math.abs() returns negative
Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (183693 => 183694)
--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2015-05-01 22:38:02 UTC (rev 183693)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2015-05-01 22:44:30 UTC (rev 183694)
@@ -1669,6 +1669,15 @@
return JSValue::encode(trimString(exec, thisValue, TrimRight));
}
+static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
+{
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+ return static_cast<unsigned>(value);
+}
+
EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
{
JSValue thisValue = exec->thisValue();
@@ -1687,9 +1696,16 @@
if (exec->hadException())
return JSValue::encode(jsUndefined());
- unsigned start = std::max(0, exec->argument(1).toInt32(exec));
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ JSValue positionArg = exec->argument(1);
+ unsigned start = 0;
+ if (positionArg.isInt32())
+ start = std::max(0, positionArg.asInt32());
+ else {
+ unsigned length = stringToSearchIn.length();
+ start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
}
@@ -1713,13 +1729,18 @@
return JSValue::encode(jsUndefined());
unsigned length = stringToSearchIn.length();
- JSValue a1 = exec->argument(1);
- int pos = a1.isUndefined() ? length : a1.toInt32(exec);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
- unsigned end = std::min<unsigned>(std::max(pos, 0), length);
- return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, end)));
+ JSValue endPositionArg = exec->argument(1);
+ unsigned end = length;
+ if (endPositionArg.isInt32())
+ end = std::max(0, endPositionArg.asInt32());
+ else if (!endPositionArg.isUndefined()) {
+ end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
+
+ return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
}
EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
@@ -1740,9 +1761,16 @@
if (exec->hadException())
return JSValue::encode(jsUndefined());
- unsigned start = std::max(0, exec->argument(1).toInt32(exec));
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ JSValue positionArg = exec->argument(1);
+ unsigned start = 0;
+ if (positionArg.isInt32())
+ start = std::max(0, positionArg.asInt32());
+ else {
+ unsigned length = stringToSearchIn.length();
+ start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
}