Diff
Modified: trunk/LayoutTests/ChangeLog (91965 => 91966)
--- trunk/LayoutTests/ChangeLog 2011-07-29 00:38:32 UTC (rev 91965)
+++ trunk/LayoutTests/ChangeLog 2011-07-29 00:43:15 UTC (rev 91966)
@@ -1,3 +1,17 @@
+2011-07-28 Gavin Barraclough <[email protected]>
+
+ https://bugs.webkit.org/show_bug.cgi?id=65325
+ Performance tweak to parseInt
+
+ Reviewed by Oliver Hunt.
+
+ * fast/js/parseInt-expected.txt: Added.
+ * fast/js/parseInt.html: Added.
+ * fast/js/script-tests/parseInt.js: Added.
+ - Added test cases.
+ * sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.2_parseInt/S15.1.2.2_A1_T2-expected.txt:
+ - Fixed.
+
2011-07-28 Anders Carlsson <[email protected]>
Unreviewed, rolling out r88601. (Requested by Sam Weinig).
Added: trunk/LayoutTests/fast/js/parseInt-expected.txt (0 => 91966)
--- trunk/LayoutTests/fast/js/parseInt-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/js/parseInt-expected.txt 2011-07-29 00:43:15 UTC (rev 91966)
@@ -0,0 +1,46 @@
+Tests for the parseInt function.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS parseInt('123') is 123
+PASS parseInt('123x4') is 123
+PASS parseInt('-123') is -123
+PASS parseInt('0x123') is 0x123
+PASS parseInt('0x123x4') is 0x123
+PASS parseInt('-0x123x4') is -0x123
+PASS parseInt('-') is Number.NaN
+PASS parseInt('0x') is Number.NaN
+PASS parseInt('-0x') is Number.NaN
+PASS parseInt('123', undefined) is 123
+PASS parseInt('123', null) is 123
+PASS parseInt('123', 0) is 123
+PASS parseInt('123', 10) is 123
+PASS parseInt('123', 16) is 0x123
+PASS parseInt('0x123', undefined) is 0x123
+PASS parseInt('0x123', null) is 0x123
+PASS parseInt('0x123', 0) is 0x123
+PASS parseInt('0x123', 10) is 0
+PASS parseInt('0x123', 16) is 0x123
+PASS parseInt(Math.pow(10, 20)) is 100000000000000000000
+PASS parseInt(Math.pow(10, 21)) is 1
+PASS parseInt(Math.pow(10, -6)) is 0
+PASS parseInt(Math.pow(10, -7)) is 1
+PASS parseInt(-Math.pow(10, 20)) is -100000000000000000000
+PASS parseInt(-Math.pow(10, 21)) is -1
+PASS parseInt(-Math.pow(10, -6)) is -0
+PASS parseInt(-Math.pow(10, -7)) is -1
+PASS parseInt('0') is 0
+PASS parseInt('-0') is -0
+PASS parseInt(0) is 0
+PASS parseInt(-0) is 0
+PASS parseInt(2147483647) is 2147483647
+PASS parseInt(2147483648) is 2147483648
+PASS parseInt('2147483647') is 2147483647
+PASS parseInt('2147483648') is 2147483648
+PASS state = null; try { parseInt('123', throwingRadix); } catch (e) {} state; is "throwingRadix"
+PASS state = null; try { parseInt(throwingString, throwingRadix); } catch (e) {} state; is "throwingString"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/js/parseInt.html (0 => 91966)
--- trunk/LayoutTests/fast/js/parseInt.html (rev 0)
+++ trunk/LayoutTests/fast/js/parseInt.html 2011-07-29 00:43:15 UTC (rev 91966)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href=""
+<script src=""
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src=""
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/fast/js/script-tests/parseInt.js (0 => 91966)
--- trunk/LayoutTests/fast/js/script-tests/parseInt.js (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/parseInt.js 2011-07-29 00:43:15 UTC (rev 91966)
@@ -0,0 +1,56 @@
+description('Tests for the parseInt function.');
+
+// Simple hex & dec integer values.
+shouldBe("parseInt('123')", '123');
+shouldBe("parseInt('123x4')", '123');
+shouldBe("parseInt('-123')", '-123');
+shouldBe("parseInt('0x123')", '0x123');
+shouldBe("parseInt('0x123x4')", '0x123');
+shouldBe("parseInt('-0x123x4')", '-0x123');
+shouldBe("parseInt('-')", 'Number.NaN');
+shouldBe("parseInt('0x')", 'Number.NaN');
+shouldBe("parseInt('-0x')", 'Number.NaN');
+
+// These call default to base 10, unless radix is explicitly 16.
+shouldBe("parseInt('123', undefined)", '123');
+shouldBe("parseInt('123', null)", '123');
+shouldBe("parseInt('123', 0)", '123');
+shouldBe("parseInt('123', 10)", '123');
+shouldBe("parseInt('123', 16)", '0x123');
+// These call default to base 16, unless radix is explicitly 10.
+shouldBe("parseInt('0x123', undefined)", '0x123');
+shouldBe("parseInt('0x123', null)", '0x123');
+shouldBe("parseInt('0x123', 0)", '0x123');
+shouldBe("parseInt('0x123', 10)", '0');
+shouldBe("parseInt('0x123', 16)", '0x123');
+
+// Test edge cases for the Number.toString exponential ranges.
+shouldBe("parseInt(Math.pow(10, 20))", '100000000000000000000');
+shouldBe("parseInt(Math.pow(10, 21))", '1');
+shouldBe("parseInt(Math.pow(10, -6))", '0');
+shouldBe("parseInt(Math.pow(10, -7))", '1');
+shouldBe("parseInt(-Math.pow(10, 20))", '-100000000000000000000');
+shouldBe("parseInt(-Math.pow(10, 21))", '-1');
+shouldBe("parseInt(-Math.pow(10, -6))", '-0');
+shouldBe("parseInt(-Math.pow(10, -7))", '-1');
+
+// Test correct handling for -0.
+shouldBe("parseInt('0')", '0');
+shouldBe("parseInt('-0')", '-0');
+shouldBe("parseInt(0)", '0');
+shouldBe("parseInt(-0)", '0');
+
+// Test edge cases of our optimized int handling.
+shouldBe("parseInt(2147483647)", '2147483647');
+shouldBe("parseInt(2147483648)", '2147483648');
+shouldBe("parseInt('2147483647')", '2147483647');
+shouldBe("parseInt('2147483648')", '2147483648');
+
+// Add test cases where the ToString/ToInt32 conversions throw.
+var state;
+var throwingRadix = { valueOf: function(){ state = "throwingRadix"; throw null; } };
+var throwingString = { toString: function(){ state = "throwingString"; throw null; } };
+shouldBe("state = null; try { parseInt('123', throwingRadix); } catch (e) {} state;", '"throwingRadix"');
+shouldBe("state = null; try { parseInt(throwingString, throwingRadix); } catch (e) {} state;", '"throwingString"');
+
+var successfullyParsed = true;
Modified: trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.2_parseInt/S15.1.2.2_A1_T2-expected.txt (91965 => 91966)
--- trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.2_parseInt/S15.1.2.2_A1_T2-expected.txt 2011-07-29 00:38:32 UTC (rev 91965)
+++ trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.1_The_Global_Object/15.1.2/15.1.2.2_parseInt/S15.1.2.2_A1_T2-expected.txt 2011-07-29 00:43:15 UTC (rev 91966)
@@ -1,6 +1,6 @@
S15.1.2.2_A1_T2
-FAIL SputnikError: #4: parseInt(-0) === +0. Actual: 0
+PASS
TEST COMPLETE
Modified: trunk/Source/_javascript_Core/ChangeLog (91965 => 91966)
--- trunk/Source/_javascript_Core/ChangeLog 2011-07-29 00:38:32 UTC (rev 91965)
+++ trunk/Source/_javascript_Core/ChangeLog 2011-07-29 00:43:15 UTC (rev 91966)
@@ -29,6 +29,18 @@
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncParseInt):
+ - This change may an existing optimization redundant,
+ cleanup from Darin's comments, plus fix existing bugs.
+
+2011-07-28 Gavin Barraclough <[email protected]>
+
+ https://bugs.webkit.org/show_bug.cgi?id=65325
+ Performance tweak to parseInt
+
+ Reviewed by Oliver Hunt.
+
+ * runtime/JSGlobalObjectFunctions.cpp:
+ (JSC::globalFuncParseInt):
- parseInt applied to small positive numbers = floor.
2011-07-28 Dan Bernstein <[email protected]>
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp (91965 => 91966)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp 2011-07-29 00:38:32 UTC (rev 91965)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp 2011-07-29 00:43:15 UTC (rev 91966)
@@ -460,29 +460,29 @@
EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
{
JSValue value = exec->argument(0);
+ JSValue radixValue = exec->argument(1);
- // Optimized case: If the argument is a positive number in the integer
- // range, and the exponent is 10, then parseInt is equivalent to floor.
+ // Optimized handling for numbers:
+ // If the argument is 0 or a number in range 10^-6 <= n < INT_MAX+1, then parseInt
+ // results in a truncation to integer. In the case of -0, this is converted to 0.
+ //
+ // This is also a truncation for values in the range INT_MAX+1 <= n < 10^21,
+ // however these values cannot be trivially truncated to int since 10^21 exceeds
+ // even the int64_t range. Negative numbers are a little trickier, the case for
+ // values in the range -10^21 < n <= -1 are similar to those for integer, but
+ // values in the range -1 < n <= -10^-6 need to truncate to -0, not 0.
+ static const double tenToTheMinus6 = 0.000001;
+ static const double intMaxPlusOne = 2147483648.0;
double n;
- if (value.getNumber(n) && n >= 0 && n < INT_MAX && exec->argument(1).isUndefined())
- return JSValue::encode(jsNumber(static_cast<int>(n)));
+ if (value.getNumber(n) && ((n < intMaxPlusOne && n >= tenToTheMinus6) || !n) && radixValue.isUndefinedOrNull())
+ return JSValue::encode(jsNumber(static_cast<int32_t>(n)));
- int32_t radix = exec->argument(1).toInt32(exec);
+ // If ToString throws, we shouldn't call ToInt32.
+ UString s = value.toString(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
- if (radix != 0 && radix != 10)
- return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix)));
-
- if (value.isInt32())
- return JSValue::encode(value);
-
- if (value.isDouble()) {
- double d = value.asDouble();
- if (isfinite(d))
- return JSValue::encode(jsNumber((d > 0) ? floor(d) : ceil(d)));
- return JSValue::encode(jsNaN());
- }
-
- return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix)));
+ return JSValue::encode(jsNumber(parseInt(s, radixValue.toInt32(exec))));
}
EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)