Title: [221601] trunk
Revision
221601
Author
[email protected]
Date
2017-09-04 20:03:03 -0700 (Mon, 04 Sep 2017)

Log Message

[DFG][FTL] Efficiently execute number#toString()
https://bugs.webkit.org/show_bug.cgi?id=170007

Reviewed by Keith Miller.

JSTests:

* microbenchmarks/number-to-string-strength-reduction.js: Added.
(test):
* microbenchmarks/number-to-string-with-radix-10.js: Added.
(test):
* microbenchmarks/number-to-string-with-radix-cse.js: Added.
(test):
* microbenchmarks/number-to-string-with-radix.js: Added.
(test):
* stress/number-to-string-strength-reduction.js: Added.
(shouldBe):
(test):
* stress/number-to-string-with-radix-10.js: Added.
(shouldBe):
(test):
* stress/number-to-string-with-radix-cse.js: Added.
(shouldBe):
(test):
* stress/number-to-string-with-radix-invalid.js: Added.
(shouldThrow):
* stress/number-to-string-with-radix-watchpoint.js: Added.
(shouldBe):
(test):
(i.i.1e3.Number.prototype.toString):
* stress/number-to-string-with-radix.js: Added.
(shouldBe):
(test):

Source/_javascript_Core:

In JS, the natural way to convert number to string with radix is `number.toString(radix)`.
However, our IC only cares about cells. If the base value is a number, it always goes to the slow path.

While extending our IC for number and boolean, the most meaningful use of this IC is calling `number.toString(radix)`.
So, in this patch, we first add a fast path for this in DFG by using watchpoint. We set up a watchpoint for
Number.prototype.toString. And if this watchpoint is kept alive and GetById(base, "toString")'s base should be
speculated as Number, we emit Number related Checks and convert GetById to Number.prototype.toString constant.
It removes costly GetById slow path, and makes it non-clobbering node (JSConstant).

In addition, we add NumberToStringWithValidRadixConstant node. We have NumberToStringWithRadix node, but it may
throw an error if the valid value is incorrect (for example, number.toString(2000)). So its clobbering rule is
conservatively use read(World)/write(Heap). But in reality, `number.toString` is mostly called with the constant
radix, and we can easily figure out this radix is valid (2 <= radix && radix < 32).
We add a rule to the constant folding phase to convert NumberToStringWithRadix to NumberToStringWithValidRadixConstant.
It ensures that it has valid constant radix. And we relax our clobbering rule for NumberToStringWithValidRadixConstant.

Added microbenchmarks show performance improvement.

                                              baseline                  patched

number-to-string-with-radix-cse           43.8312+-1.3017     ^      7.4930+-0.5105        ^ definitely 5.8496x faster
number-to-string-with-radix-10             7.2775+-0.5225     ^      2.1906+-0.1864        ^ definitely 3.3222x faster
number-to-string-with-radix               39.7378+-1.4921     ^     16.6137+-0.7776        ^ definitely 2.3919x faster
number-to-string-strength-reduction       94.9667+-2.7157     ^      9.3060+-0.7202        ^ definitely 10.2049x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::isWatchingGlobalObjectWatchpoint):
(JSC::DFG::Graph::isWatchingArrayIteratorProtocolWatchpoint):
(JSC::DFG::Graph::isWatchingNumberToStringWatchpoint):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToNumberToStringWithValidRadixConstant):
(JSC::DFG::Node::hasValidRadixConstant):
(JSC::DFG::Node::validRadixConstant):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor):
(JSC::DFG::SpeculativeJIT::compileNumberToStringWithValidRadixConstant):
(JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNumberToStringWithValidRadixConstant):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::numberToStringWatchpoint):
(JSC::JSGlobalObject::numberProtoToStringFunction const):
* runtime/NumberPrototype.cpp:
(JSC::NumberPrototype::finishCreation):
(JSC::toStringWithRadixInternal):
(JSC::toStringWithRadix):
(JSC::int32ToStringInternal):
(JSC::numberToStringInternal):
* runtime/NumberPrototype.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (221600 => 221601)


--- trunk/JSTests/ChangeLog	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/JSTests/ChangeLog	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1,3 +1,37 @@
+2017-09-03  Yusuke Suzuki  <[email protected]>
+
+        [DFG][FTL] Efficiently execute number#toString()
+        https://bugs.webkit.org/show_bug.cgi?id=170007
+
+        Reviewed by Keith Miller.
+
+        * microbenchmarks/number-to-string-strength-reduction.js: Added.
+        (test):
+        * microbenchmarks/number-to-string-with-radix-10.js: Added.
+        (test):
+        * microbenchmarks/number-to-string-with-radix-cse.js: Added.
+        (test):
+        * microbenchmarks/number-to-string-with-radix.js: Added.
+        (test):
+        * stress/number-to-string-strength-reduction.js: Added.
+        (shouldBe):
+        (test):
+        * stress/number-to-string-with-radix-10.js: Added.
+        (shouldBe):
+        (test):
+        * stress/number-to-string-with-radix-cse.js: Added.
+        (shouldBe):
+        (test):
+        * stress/number-to-string-with-radix-invalid.js: Added.
+        (shouldThrow):
+        * stress/number-to-string-with-radix-watchpoint.js: Added.
+        (shouldBe):
+        (test):
+        (i.i.1e3.Number.prototype.toString):
+        * stress/number-to-string-with-radix.js: Added.
+        (shouldBe):
+        (test):
+
 2017-09-02  Yusuke Suzuki  <[email protected]>
 
         [DFG] Relax arity requirement

Added: trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js (0 => 221601)


--- trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-strength-reduction.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,9 @@
+function test()
+{
+    var target = 42;
+    return target.toString(16);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    test();

Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js (0 => 221601)


--- trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix-10.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,10 @@
+function test()
+{
+    for (var i = 0; i < 10; ++i)
+        var result = i.toString(10);
+    return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    test();

Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js (0 => 221601)


--- trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix-cse.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,14 @@
+function test()
+{
+    for (var i = 0; i < 1e2; ++i) {
+        i.toString(16);
+        i.toString(16);
+        i.toString(16);
+        i.toString(16);
+        i.toString(16);
+    }
+}
+noInline(test);
+
+for (var i = 0; i < 1e3; ++i)
+    test();

Added: trunk/JSTests/microbenchmarks/number-to-string-with-radix.js (0 => 221601)


--- trunk/JSTests/microbenchmarks/number-to-string-with-radix.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/number-to-string-with-radix.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,16 @@
+function test()
+{
+    for (var i = 0; i < 10; ++i) {
+        var result = '';
+        result += i.toString(2);
+        result += i.toString(4);
+        result += i.toString(8);
+        result += i.toString(16);
+        result += i.toString(32);
+    }
+    return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    test();

Added: trunk/JSTests/stress/number-to-string-strength-reduction.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-strength-reduction.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-strength-reduction.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,14 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    var target = 42;
+    return target.toString(16);
+}
+noInline(test);
+
+for (var i = 0; i < 1e6; ++i)
+    shouldBe(test(), `2a`);

Added: trunk/JSTests/stress/number-to-string-with-radix-10.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-with-radix-10.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-10.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+noInline(shouldBe);
+
+function test()
+{
+    for (var i = 0; i < 10; ++i)
+        shouldBe(i.toString(10), "" + i);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    test();

Added: trunk/JSTests/stress/number-to-string-with-radix-cse.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-with-radix-cse.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-cse.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    var result;
+    for (var i = 0; i < 1e2; ++i) {
+        i.toString(16);
+        i.toString(16);
+        i.toString(16);
+        i.toString(16);
+        result = i.toString(16);
+    }
+    return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e3; ++i)
+    shouldBe(test(), `63`);

Added: trunk/JSTests/stress/number-to-string-with-radix-invalid.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-with-radix-invalid.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-invalid.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,24 @@
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function test(i, radix)
+{
+    return i.toString(radix);
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    shouldThrow(() => test(i, 42), `RangeError: toString() radix argument must be between 2 and 36`);
+}

Added: trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix-watchpoint.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,27 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    for (var i = 0; i < 10; ++i) {
+        var result = '';
+        result += i.toString(2);
+        result += i.toString(4);
+        result += i.toString(8);
+        result += i.toString(16);
+        result += i.toString(32);
+    }
+    return result;
+}
+noInline(test);
+
+var result = `1001211199`;
+for (var i = 0; i < 1e4; ++i) {
+    if (i === 1e3) {
+        Number.prototype.toString = function (radix) { return "Hello"; }
+        result = `HelloHelloHelloHelloHello`;
+    }
+    shouldBe(test(), result);
+}

Added: trunk/JSTests/stress/number-to-string-with-radix.js (0 => 221601)


--- trunk/JSTests/stress/number-to-string-with-radix.js	                        (rev 0)
+++ trunk/JSTests/stress/number-to-string-with-radix.js	2017-09-05 03:03:03 UTC (rev 221601)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test()
+{
+    for (var i = 0; i < 10; ++i) {
+        var result = '';
+        result += i.toString(2);
+        result += i.toString(4);
+        result += i.toString(8);
+        result += i.toString(16);
+        result += i.toString(32);
+    }
+    return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(test(), `1001211199`);

Modified: trunk/Source/_javascript_Core/ChangeLog (221600 => 221601)


--- trunk/Source/_javascript_Core/ChangeLog	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1,3 +1,88 @@
+2017-09-03  Yusuke Suzuki  <[email protected]>
+
+        [DFG][FTL] Efficiently execute number#toString()
+        https://bugs.webkit.org/show_bug.cgi?id=170007
+
+        Reviewed by Keith Miller.
+
+        In JS, the natural way to convert number to string with radix is `number.toString(radix)`.
+        However, our IC only cares about cells. If the base value is a number, it always goes to the slow path.
+
+        While extending our IC for number and boolean, the most meaningful use of this IC is calling `number.toString(radix)`.
+        So, in this patch, we first add a fast path for this in DFG by using watchpoint. We set up a watchpoint for
+        Number.prototype.toString. And if this watchpoint is kept alive and GetById(base, "toString")'s base should be
+        speculated as Number, we emit Number related Checks and convert GetById to Number.prototype.toString constant.
+        It removes costly GetById slow path, and makes it non-clobbering node (JSConstant).
+
+        In addition, we add NumberToStringWithValidRadixConstant node. We have NumberToStringWithRadix node, but it may
+        throw an error if the valid value is incorrect (for example, number.toString(2000)). So its clobbering rule is
+        conservatively use read(World)/write(Heap). But in reality, `number.toString` is mostly called with the constant
+        radix, and we can easily figure out this radix is valid (2 <= radix && radix < 32).
+        We add a rule to the constant folding phase to convert NumberToStringWithRadix to NumberToStringWithValidRadixConstant.
+        It ensures that it has valid constant radix. And we relax our clobbering rule for NumberToStringWithValidRadixConstant.
+
+        Added microbenchmarks show performance improvement.
+
+                                                      baseline                  patched
+
+        number-to-string-with-radix-cse           43.8312+-1.3017     ^      7.4930+-0.5105        ^ definitely 5.8496x faster
+        number-to-string-with-radix-10             7.2775+-0.5225     ^      2.1906+-0.1864        ^ definitely 3.3222x faster
+        number-to-string-with-radix               39.7378+-1.4921     ^     16.6137+-0.7776        ^ definitely 2.3919x faster
+        number-to-string-strength-reduction       94.9667+-2.7157     ^      9.3060+-0.7202        ^ definitely 10.2049x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::isWatchingGlobalObjectWatchpoint):
+        (JSC::DFG::Graph::isWatchingArrayIteratorProtocolWatchpoint):
+        (JSC::DFG::Graph::isWatchingNumberToStringWatchpoint):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToNumberToStringWithValidRadixConstant):
+        (JSC::DFG::Node::hasValidRadixConstant):
+        (JSC::DFG::Node::validRadixConstant):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructor):
+        (JSC::DFG::SpeculativeJIT::compileNumberToStringWithValidRadixConstant):
+        (JSC::DFG::SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber): Deleted.
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNumberToStringWithValidRadixConstant):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::JSGlobalObject):
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::numberToStringWatchpoint):
+        (JSC::JSGlobalObject::numberProtoToStringFunction const):
+        * runtime/NumberPrototype.cpp:
+        (JSC::NumberPrototype::finishCreation):
+        (JSC::toStringWithRadixInternal):
+        (JSC::toStringWithRadix):
+        (JSC::int32ToStringInternal):
+        (JSC::numberToStringInternal):
+        * runtime/NumberPrototype.h:
+
 2017-09-04  Yusuke Suzuki  <[email protected]>
 
         [DFG] Consider increasing the number of DFG worklist threads

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1949,10 +1949,25 @@
         break;
     }
 
-    case NumberToStringWithRadix:
+    case NumberToStringWithRadix: {
+        JSValue radixValue = forNode(node->child2()).m_value;
+        if (radixValue && radixValue.isInt32()) {
+            int32_t radix = radixValue.asInt32();
+            if (2 <= radix && radix <= 36) {
+                m_state.setFoundConstants(true);
+                forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+                break;
+            }
+        }
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
         break;
+    }
+
+    case NumberToStringWithValidRadixConstant: {
+        forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+        break;
+    }
         
     case NewStringObject: {
         ASSERT(node->structure()->classInfo() == StringObject::info());

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1584,9 +1584,14 @@
         return;
 
     case NumberToStringWithRadix:
+        // If the radix is invalid, NumberToStringWithRadix can throw an error.
         read(World);
         write(Heap);
         return;
+
+    case NumberToStringWithValidRadixConstant:
+        def(PureValue(node, node->validRadixConstant()));
+        return;
         
     case LastNodeType:
         RELEASE_ASSERT_NOT_REACHED();

Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -645,6 +645,23 @@
                 break;
             }
 
+            case NumberToStringWithRadix: {
+                JSValue radixValue = m_state.forNode(node->child2()).m_value;
+                if (radixValue && radixValue.isInt32()) {
+                    int32_t radix = radixValue.asInt32();
+                    if (2 <= radix && radix <= 36) {
+                        if (radix == 10) {
+                            node->setOpAndDefaultFlags(ToString);
+                            node->child2() = Edge();
+                        } else
+                            node->convertToNumberToStringWithValidRadixConstant(radix);
+                        changed = true;
+                        break;
+                    }
+                }
+                break;
+            }
+
             case Check: {
                 alreadyHandled = true;
                 m_interpreter.execute(indexInBlock);

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -178,6 +178,7 @@
     case ToString:
     case CallStringConstructor:
     case NumberToStringWithRadix:
+    case NumberToStringWithValidRadixConstant:
     case In:
     case HasOwnProperty:
     case Jump:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1311,6 +1311,7 @@
             // FIXME: This should be done in the ByteCodeParser based on reading the
             // PolymorphicAccess, which will surely tell us that this is a AccessCase::ArrayLength.
             // https://bugs.webkit.org/show_bug.cgi?id=154990
+            auto uid = m_graph.identifiers()[node->identifierNumber()];
             if (node->child1()->shouldSpeculateCellOrOther()
                 && !m_graph.hasExitSite(node->origin.semantic, BadType)
                 && !m_graph.hasExitSite(node->origin.semantic, BadCache)
@@ -1317,8 +1318,6 @@
                 && !m_graph.hasExitSite(node->origin.semantic, BadIndexingType)
                 && !m_graph.hasExitSite(node->origin.semantic, ExoticObjectMode)) {
                 
-                auto uid = m_graph.identifiers()[node->identifierNumber()];
-                
                 if (uid == vm().propertyNames->length.impl()) {
                     attemptToMakeGetArrayLength(node);
                     break;
@@ -1333,6 +1332,30 @@
                 }
             }
 
+            if (node->child1()->shouldSpeculateNumber()) {
+                if (uid == vm().propertyNames->toString.impl()) {
+                    if (m_graph.isWatchingNumberToStringWatchpoint(node)) {
+                        JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+                        if (node->child1()->shouldSpeculateInt32()) {
+                            insertCheck<Int32Use>(node->child1().node());
+                            m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+                            break;
+                        }
+
+                        if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+                            insertCheck<Int52RepUse>(node->child1().node());
+                            m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+                            break;
+                        }
+
+                        ASSERT(node->child1()->shouldSpeculateNumber());
+                        insertCheck<DoubleRepUse>(node->child1().node());
+                        m_graph.convertToConstant(node, m_graph.freeze(globalObject->numberProtoToStringFunction()));
+                        break;
+                    }
+                }
+            }
+
             if (node->child1()->shouldSpeculateCell())
                 fixEdge<CellUse>(node->child1());
             break;
@@ -2022,6 +2045,7 @@
         case PutByValWithThis:
         case GetByValWithThis:
         case CompareEqPtr:
+        case NumberToStringWithValidRadixConstant:
             break;
 #else
         default:

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -730,10 +730,8 @@
         return watchpoints().isWatched(globalObject->havingABadTimeWatchpoint());
     }
 
-    bool isWatchingArrayIteratorProtocolWatchpoint(Node* node)
+    bool isWatchingGlobalObjectWatchpoint(JSGlobalObject* globalObject, InlineWatchpointSet& set)
     {
-        JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
-        InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint();
         if (watchpoints().isWatched(set))
             return true;
 
@@ -749,6 +747,20 @@
 
         return false;
     }
+
+    bool isWatchingArrayIteratorProtocolWatchpoint(Node* node)
+    {
+        JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
+        InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint();
+        return isWatchingGlobalObjectWatchpoint(globalObject, set);
+    }
+
+    bool isWatchingNumberToStringWatchpoint(Node* node)
+    {
+        JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic);
+        InlineWatchpointSet& set = globalObject->numberToStringWatchpoint();
+        return isWatchingGlobalObjectWatchpoint(globalObject, set);
+    }
     
     Profiler::Compilation* compilation() { return m_plan.compilation.get(); }
     

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -691,6 +691,15 @@
         children.setChild2(Edge());
         m_opInfo = cell;
     }
+
+    void convertToNumberToStringWithValidRadixConstant(int32_t radix)
+    {
+        ASSERT(m_op == NumberToStringWithRadix);
+        ASSERT(2 <= radix && radix <= 36);
+        setOpAndDefaultFlags(NumberToStringWithValidRadixConstant);
+        children.setChild2(Edge());
+        m_opInfo = radix;
+    }
     
     void convertToDirectCall(FrozenValue*);
 
@@ -2583,6 +2592,17 @@
         return m_opInfo.as<BucketOwnerType>();
     }
 
+    bool hasValidRadixConstant()
+    {
+        return op() == NumberToStringWithValidRadixConstant;
+    }
+
+    int32_t validRadixConstant()
+    {
+        ASSERT(hasValidRadixConstant());
+        return m_opInfo.as<int32_t>();
+    }
+
     uint32_t errorType()
     {
         ASSERT(op() == ThrowStaticError);

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -354,6 +354,7 @@
     macro(CallObjectConstructor, NodeResultJS) \
     macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
     macro(NumberToStringWithRadix, NodeResultJS | NodeMustGenerate) \
+    macro(NumberToStringWithValidRadixConstant, NodeResultJS) \
     macro(NewStringObject, NodeResultJS) \
     macro(MakeRope, NodeResultJS) \
     macro(In, NodeResultBoolean | NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -912,6 +912,7 @@
         case CallStringConstructor:
         case ToString:
         case NumberToStringWithRadix:
+        case NumberToStringWithValidRadixConstant:
         case MakeRope:
         case StrCat: {
             setPrediction(SpecString);

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -288,6 +288,7 @@
     case ToString:
     case ToNumber:
     case NumberToStringWithRadix:
+    case NumberToStringWithValidRadixConstant:
     case SetFunctionName:
     case StrCat:
     case CallStringConstructor:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -8353,7 +8353,7 @@
     case Int32Use:
     case Int52RepUse:
     case DoubleRepUse:
-        compileToStringOrCallStringConstructorOnNumber(node);
+        compileNumberToStringWithValidRadixConstant(node, 10);
         return;
 
     default:
@@ -8434,11 +8434,16 @@
     }
 }
 
-void SpeculativeJIT::compileToStringOrCallStringConstructorOnNumber(Node* node)
+void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node)
 {
+    compileNumberToStringWithValidRadixConstant(node, node->validRadixConstant());
+}
+
+void SpeculativeJIT::compileNumberToStringWithValidRadixConstant(Node* node, int32_t radix)
+{
     auto callToString = [&] (auto operation, GPRReg resultGPR, auto valueReg) {
         flushRegisters();
-        callOperation(operation, resultGPR, valueReg, CCallHelpers::TrustedImm32(10));
+        callOperation(operation, resultGPR, valueReg, TrustedImm32(radix));
         m_jit.exceptionCheck();
         cellResult(resultGPR, node);
     };

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -2766,8 +2766,9 @@
     void emitSwitch(Node*);
     
     void compileToStringOrCallStringConstructor(Node*);
-    void compileToStringOrCallStringConstructorOnNumber(Node*);
     void compileNumberToStringWithRadix(Node*);
+    void compileNumberToStringWithValidRadixConstant(Node*);
+    void compileNumberToStringWithValidRadixConstant(Node*, int32_t radix);
     void compileNewStringObject(Node*);
     
     void compileNewTypedArray(Node*);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -2843,6 +2843,11 @@
         break;
     }
 
+    case NumberToStringWithValidRadixConstant: {
+        compileNumberToStringWithValidRadixConstant(node);
+        break;
+    }
+
     case GetByValWithThis: {
         JSValueOperand base(this, node->child1());
         JSValueRegs baseRegs = base.jsValueRegs();

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -5303,6 +5303,11 @@
         break;
     }
 
+    case NumberToStringWithValidRadixConstant: {
+        compileNumberToStringWithValidRadixConstant(node);
+        break;
+    }
+
     case IsObject: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this, Reuse, value);

Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -426,6 +426,19 @@
             break;
         }
 
+        case NumberToStringWithValidRadixConstant: {
+            Edge& child1 = m_node->child1();
+            if (child1->hasConstant()) {
+                JSValue value = child1->constant()->value();
+                if (value && value.isNumber()) {
+                    String result = toStringWithRadix(value.asNumber(), m_node->validRadixConstant());
+                    m_node->convertToLazyJSConstant(m_graph, LazyJSValue::newString(m_graph, result));
+                    m_changed = true;
+                }
+            }
+            break;
+        }
+
         case GetArrayLength: {
             if (m_node->arrayMode().type() == Array::Generic
                 || m_node->arrayMode().type() == Array::String) {

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -284,6 +284,7 @@
     case DefineAccessorProperty:
     case ToLowerCase:
     case NumberToStringWithRadix:
+    case NumberToStringWithValidRadixConstant:
     case CheckSubClass:
     case CallDOM:
     case CallDOMGetter:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -1123,6 +1123,9 @@
         case NumberToStringWithRadix:
             compileNumberToStringWithRadix();
             break;
+        case NumberToStringWithValidRadixConstant:
+            compileNumberToStringWithValidRadixConstant();
+            break;
         case CheckSubClass:
             compileCheckSubClass();
             break;
@@ -10419,6 +10422,23 @@
         }
     }
 
+    void compileNumberToStringWithValidRadixConstant()
+    {
+        switch (m_node->child1().useKind()) {
+        case Int32Use:
+            setJSValue(vmCall(pointerType(), m_out.operation(operationInt32ToStringWithValidRadix), m_callFrame, lowInt32(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+            break;
+        case Int52RepUse:
+            setJSValue(vmCall(pointerType(), m_out.operation(operationInt52ToStringWithValidRadix), m_callFrame, lowStrictInt52(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+            break;
+        case DoubleRepUse:
+            setJSValue(vmCall(pointerType(), m_out.operation(operationDoubleToStringWithValidRadix), m_callFrame, lowDouble(m_node->child1()), m_out.constInt32(m_node->validRadixConstant())));
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    }
+
     void compileResolveScopeForHoistingFuncDeclInEval()
     {
         UniquedStringImpl* uid = m_graph.identifiers()[m_node->identifierNumber()];

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -325,6 +325,7 @@
     , m_mapSetWatchpoint(IsWatched)
     , m_setAddWatchpoint(IsWatched)
     , m_arraySpeciesWatchpoint(ClearWatchpoint)
+    , m_numberToStringWatchpoint(IsWatched)
     , m_templateRegistry(vm)
     , m_runtimeFlags()
     , m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
@@ -1015,6 +1016,13 @@
             m_setPrototypeAddWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_setAddWatchpoint);
             m_setPrototypeAddWatchpoint->install();
         }
+
+        {
+            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(numberPrototype(), m_vm.propertyNames->toString);
+            m_numberPrototypeToStringWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_numberToStringWatchpoint);
+            m_numberPrototypeToStringWatchpoint->install();
+            m_numberProtoToStringFunction.set(vm, this, jsCast<JSFunction*>(numberPrototype()->getDirect(vm, vm.propertyNames->toString)));
+        }
     }
 
     resetPrototype(vm, getPrototypeDirect());
@@ -1264,6 +1272,7 @@
     thisObject->m_iteratorProtocolFunction.visit(visitor);
     thisObject->m_promiseResolveFunction.visit(visitor);
     visitor.append(thisObject->m_objectProtoValueOfFunction);
+    visitor.append(thisObject->m_numberProtoToStringFunction);
     visitor.append(thisObject->m_newPromiseCapabilityFunction);
     visitor.append(thisObject->m_functionProtoHasInstanceSymbolFunction);
     thisObject->m_throwTypeErrorGetterSetter.visit(visitor);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (221600 => 221601)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -269,6 +269,7 @@
     LazyProperty<JSGlobalObject, JSFunction> m_iteratorProtocolFunction;
     LazyProperty<JSGlobalObject, JSFunction> m_promiseResolveFunction;
     WriteBarrier<JSFunction> m_objectProtoValueOfFunction;
+    WriteBarrier<JSFunction> m_numberProtoToStringFunction;
     WriteBarrier<JSFunction> m_newPromiseCapabilityFunction;
     WriteBarrier<JSFunction> m_functionProtoHasInstanceSymbolFunction;
     LazyProperty<JSGlobalObject, GetterSetter> m_throwTypeErrorGetterSetter;
@@ -413,6 +414,7 @@
     InlineWatchpointSet& mapSetWatchpoint() { return m_mapSetWatchpoint; }
     InlineWatchpointSet& setAddWatchpoint() { return m_setAddWatchpoint; }
     InlineWatchpointSet& arraySpeciesWatchpoint() { return m_arraySpeciesWatchpoint; }
+    InlineWatchpointSet& numberToStringWatchpoint() { return m_numberToStringWatchpoint; }
     // If this hasn't been invalidated, it means the array iterator protocol
     // is not observable to user code yet.
     InlineWatchpointSet m_arrayIteratorProtocolWatchpoint;
@@ -422,6 +424,7 @@
     InlineWatchpointSet m_mapSetWatchpoint;
     InlineWatchpointSet m_setAddWatchpoint;
     InlineWatchpointSet m_arraySpeciesWatchpoint;
+    InlineWatchpointSet m_numberToStringWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_arrayPrototypeSymbolIteratorWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_arrayIteratorPrototypeNext;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapPrototypeSymbolIteratorWatchpoint;
@@ -432,6 +435,7 @@
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_stringIteratorPrototypeNextWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapPrototypeSetWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_setPrototypeAddWatchpoint;
+    std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_numberPrototypeToStringWatchpoint;
 
     bool isArrayPrototypeIteratorProtocolFastAndNonObservable();
     bool isMapPrototypeIteratorProtocolFastAndNonObservable();
@@ -542,6 +546,7 @@
     JSFunction* iteratorProtocolFunction() const { return m_iteratorProtocolFunction.get(this); }
     JSFunction* promiseResolveFunction() const { return m_promiseResolveFunction.get(this); }
     JSFunction* objectProtoValueOfFunction() const { return m_objectProtoValueOfFunction.get(); }
+    JSFunction* numberProtoToStringFunction() const { return m_numberProtoToStringFunction.get(); }
     JSFunction* newPromiseCapabilityFunction() const { return m_newPromiseCapabilityFunction.get(); }
     JSFunction* functionProtoHasInstanceSymbolFunction() const { return m_functionProtoHasInstanceSymbolFunction.get(); }
     JSObject* regExpProtoExecFunction() const { return m_regExpProtoExec.get(); }

Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp (221600 => 221601)


--- trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.cpp	2017-09-05 03:03:03 UTC (rev 221601)
@@ -58,7 +58,6 @@
 
 /* Source for NumberPrototype.lut.h
 @begin numberPrototypeTable
-  toString          numberProtoFuncToString         DontEnum|Function 1 NumberPrototypeToStringIntrinsic
   toLocaleString    numberProtoFuncToLocaleString   DontEnum|Function 0
   valueOf           numberProtoFuncValueOf          DontEnum|Function 0
   toFixed           numberProtoFuncToFixed          DontEnum|Function 1
@@ -79,10 +78,9 @@
     Base::finishCreation(vm);
     setInternalValue(vm, jsNumber(0));
 
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, numberProtoFuncToString, DontEnum, 1, NumberPrototypeToStringIntrinsic);
 #if ENABLE(INTL)
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("toLocaleString", numberPrototypeToLocaleStringCodeGenerator, DontEnum);
-#else
-    UNUSED_PARAM(globalObject);
 #endif // ENABLE(INTL)
 
     ASSERT(inherits(vm, info()));
@@ -165,7 +163,7 @@
     return startOfResultString;
 }
 
-static char* toStringWithRadix(RadixBuffer& buffer, double originalNumber, unsigned radix)
+static char* toStringWithRadixInternal(RadixBuffer& buffer, double originalNumber, unsigned radix)
 {
     ASSERT(std::isfinite(originalNumber));
     ASSERT(radix >= 2 && radix <= 36);
@@ -359,7 +357,7 @@
     return startOfResultString;
 }
 
-static String toStringWithRadix(int32_t number, unsigned radix)
+static String toStringWithRadixInternal(int32_t number, unsigned radix)
 {
     LChar buf[1 + 32]; // Worst case is radix == 2, which gives us 32 digits + sign.
     LChar* end = std::end(buf);
@@ -384,6 +382,21 @@
     return String(p, static_cast<unsigned>(end - p));
 }
 
+String toStringWithRadix(double doubleValue, int32_t radix)
+{
+    ASSERT(2 <= radix && radix <= 36);
+
+    int32_t integerValue = static_cast<int32_t>(doubleValue);
+    if (integerValue == doubleValue)
+        return toStringWithRadixInternal(integerValue, radix);
+
+    if (radix == 10 || !std::isfinite(doubleValue))
+        return String::numberToStringECMAScript(doubleValue);
+
+    RadixBuffer buffer;
+    return toStringWithRadixInternal(buffer, doubleValue, radix);
+}
+
 // toExponential converts a number to a string, always formatting as an exponential.
 // This method takes an optional argument specifying a number of *decimal places*
 // to round the significand to (or, put another way, this method optionally rounds
@@ -520,7 +533,7 @@
     if (radix == 10)
         return jsNontrivialString(&vm, vm.numericStrings.add(value));
 
-    return jsNontrivialString(&vm, toStringWithRadix(value, radix));
+    return jsNontrivialString(&vm, toStringWithRadixInternal(value, radix));
 
 }
 
@@ -539,7 +552,7 @@
         return jsNontrivialString(&vm, String::numberToStringECMAScript(doubleValue));
 
     RadixBuffer buffer;
-    return jsString(&vm, toStringWithRadix(buffer, doubleValue, radix));
+    return jsString(&vm, toStringWithRadixInternal(buffer, doubleValue, radix));
 }
 
 JSString* int32ToString(VM& vm, int32_t value, int32_t radix)

Modified: trunk/Source/_javascript_Core/runtime/NumberPrototype.h (221600 => 221601)


--- trunk/Source/_javascript_Core/runtime/NumberPrototype.h	2017-09-05 01:48:31 UTC (rev 221600)
+++ trunk/Source/_javascript_Core/runtime/NumberPrototype.h	2017-09-05 03:03:03 UTC (rev 221601)
@@ -54,5 +54,6 @@
 JSString* int32ToString(VM&, int32_t value, int32_t radix);
 JSString* int52ToString(VM&, int64_t value, int32_t radix);
 JSString* numberToString(VM&, double value, int32_t radix);
+String toStringWithRadix(double doubleValue, int32_t radix);
 
 } // namespace JSC
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to