Title: [212939] trunk
Revision
212939
Author
sbar...@apple.com
Date
2017-02-23 20:07:30 -0800 (Thu, 23 Feb 2017)

Log Message

Intrinsicify parseInt
https://bugs.webkit.org/show_bug.cgi?id=168627

Reviewed by Filip Pizlo.

JSTests:

* stress/parse-int-intrinsic.js: Added.
(assert):
(testIntrinsic.let.s):
(testIntrinsic):
(testIntrinsic2.baz):
(testIntrinsic2):
(testIntrinsic3.foo):
(testIntrinsic3):
(testIntrinsic4.foo):
(testIntrinsic4):
(testIntrinsic5.foo):
(testIntrinsic5):
(testIntrinsic6.foo):
(testIntrinsic6):
(testIntrinsic7.foo):
(testIntrinsic7):

Source/_javascript_Core:

This patch makes parseInt an intrinsic in the DFG and FTL.
We do our best to eliminate this node. If we speculate that
the first operand to the operation is an int32, and that there
isn't a second operand, we convert to the identity of the first
operand. That's because parseInt(someInt) === someInt.

If the first operand is proven to be an integer, and the second
operand is the integer 0 or the integer 10, we can eliminate the
node by making it an identity over its first operand. That's
because parseInt(someInt, 0) === someInt and parseInt(someInt, 10) === someInt.

If we are not able to constant fold the node away, we try to remove
checks. The most common use case of parseInt is that its first operand
is a proven string. The DFG might be able to remove type checks in this
case. We also set up CSE rules for parseInt(someString, someIntRadix)
because it's a "pure" operation (modulo resolving a rope).

This looks to be a 4% Octane/Box2D progression.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* 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/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::parseIntResult):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileParseInt):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::SpeculativeJIT::appendCallSetResult):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileParseInt):
* jit/JITOperations.h:
* parser/Lexer.cpp:
* runtime/ErrorInstance.cpp:
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::toStringView): Deleted.
(JSC::isStrWhiteSpace): Deleted.
(JSC::parseDigit): Deleted.
(JSC::parseIntOverflow): Deleted.
(JSC::parseInt): Deleted.
* runtime/JSGlobalObjectFunctions.h:
* runtime/ParseInt.h: Added.
(JSC::parseDigit):
(JSC::parseIntOverflow):
(JSC::isStrWhiteSpace):
(JSC::parseInt):
(JSC::toStringView):
* runtime/StringPrototype.cpp:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (212938 => 212939)


--- trunk/JSTests/ChangeLog	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/JSTests/ChangeLog	2017-02-24 04:07:30 UTC (rev 212939)
@@ -1,3 +1,27 @@
+2017-02-23  Saam Barati  <sbar...@apple.com>
+
+        Intrinsicify parseInt
+        https://bugs.webkit.org/show_bug.cgi?id=168627
+
+        Reviewed by Filip Pizlo.
+
+        * stress/parse-int-intrinsic.js: Added.
+        (assert):
+        (testIntrinsic.let.s):
+        (testIntrinsic):
+        (testIntrinsic2.baz):
+        (testIntrinsic2):
+        (testIntrinsic3.foo):
+        (testIntrinsic3):
+        (testIntrinsic4.foo):
+        (testIntrinsic4):
+        (testIntrinsic5.foo):
+        (testIntrinsic5):
+        (testIntrinsic6.foo):
+        (testIntrinsic6):
+        (testIntrinsic7.foo):
+        (testIntrinsic7):
+
 2017-02-23  JF Bastien  <jfbast...@apple.com>
 
         WebAssembly: support 0x1 version

Added: trunk/JSTests/stress/parse-int-intrinsic.js (0 => 212939)


--- trunk/JSTests/stress/parse-int-intrinsic.js	                        (rev 0)
+++ trunk/JSTests/stress/parse-int-intrinsic.js	2017-02-24 04:07:30 UTC (rev 212939)
@@ -0,0 +1,97 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad")
+}
+
+function testIntrinsic(radix) {
+    let s = `
+        {
+            function foo(n) {
+                n = n|0;
+                return parseInt(n, ${radix});
+            }
+            noInline(foo);
+            for (let i = 0; i < 10000; i++)
+                assert(foo(i) === i);
+            assert(foo("20") === 20);
+        }
+    `;
+
+    eval(s);
+}
+
+testIntrinsic(10);
+testIntrinsic(0);
+
+function testIntrinsic2() {
+    function baz(n) {
+        n = n | 0;
+        return parseInt(n, 16);
+    }
+    noInline(baz);
+
+    for (let i = 0; i < 100000; i++)
+        assert(baz(i) === parseInt("0x" + i));
+}
+noDFG(testIntrinsic2);
+testIntrinsic2();
+
+function testIntrinsic3() {
+    function foo(s) {
+        return parseInt(s) + 1;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 100000; i++)
+        assert(foo(i + "") === i + 1);
+}
+noDFG(testIntrinsic3);
+testIntrinsic3();
+
+function testIntrinsic4() {
+    function foo(s) {
+        return parseInt(s, 0) + 1;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 100000; i++)
+        assert(foo(i + "") === i + 1);
+}
+testIntrinsic4();
+
+function testIntrinsic5() {
+    function foo(s) {
+        return parseInt(s, 10) + 1;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 100000; i++)
+        assert(foo(i + "") === i + 1);
+}
+testIntrinsic5();
+
+function testIntrinsic6() {
+    function foo(s) {
+        return parseInt(s, 16) + 1;
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 100000; i++)
+        assert(foo(i + "") === (parseInt("0x" + i) + 1));
+}
+noDFG(testIntrinsic6);
+testIntrinsic6();
+
+function testIntrinsic7() {
+    function foo(s) {
+        return parseInt(s, 16) + parseInt(s, 16);
+    }
+    noInline(foo);
+
+    for (let i = 0; i < 100000; i++)
+        assert(foo(i + "") === (parseInt("0x" + i) * 2));
+}
+noDFG(testIntrinsic7);
+testIntrinsic7();

Modified: trunk/Source/_javascript_Core/ChangeLog (212938 => 212939)


--- trunk/Source/_javascript_Core/ChangeLog	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-02-24 04:07:30 UTC (rev 212939)
@@ -1,3 +1,85 @@
+2017-02-23  Saam Barati  <sbar...@apple.com>
+
+        Intrinsicify parseInt
+        https://bugs.webkit.org/show_bug.cgi?id=168627
+
+        Reviewed by Filip Pizlo.
+
+        This patch makes parseInt an intrinsic in the DFG and FTL.
+        We do our best to eliminate this node. If we speculate that
+        the first operand to the operation is an int32, and that there
+        isn't a second operand, we convert to the identity of the first
+        operand. That's because parseInt(someInt) === someInt.
+        
+        If the first operand is proven to be an integer, and the second
+        operand is the integer 0 or the integer 10, we can eliminate the
+        node by making it an identity over its first operand. That's
+        because parseInt(someInt, 0) === someInt and parseInt(someInt, 10) === someInt.
+        
+        If we are not able to constant fold the node away, we try to remove
+        checks. The most common use case of parseInt is that its first operand
+        is a proven string. The DFG might be able to remove type checks in this
+        case. We also set up CSE rules for parseInt(someString, someIntRadix)
+        because it's a "pure" operation (modulo resolving a rope).
+
+        This looks to be a 4% Octane/Box2D progression.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * 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/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        (JSC::DFG::parseIntResult):
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileParseInt):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        (JSC::DFG::SpeculativeJIT::appendCallSetResult):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileParseInt):
+        * jit/JITOperations.h:
+        * parser/Lexer.cpp:
+        * runtime/ErrorInstance.cpp:
+        * runtime/Intrinsic.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::toStringView): Deleted.
+        (JSC::isStrWhiteSpace): Deleted.
+        (JSC::parseDigit): Deleted.
+        (JSC::parseIntOverflow): Deleted.
+        (JSC::parseInt): Deleted.
+        * runtime/JSGlobalObjectFunctions.h:
+        * runtime/ParseInt.h: Added.
+        (JSC::parseDigit):
+        (JSC::parseIntOverflow):
+        (JSC::isStrWhiteSpace):
+        (JSC::parseInt):
+        (JSC::toStringView):
+        * runtime/StringPrototype.cpp:
+
 2017-02-23  JF Bastien  <jfbast...@apple.com>
 
         WebAssembly: support 0x1 version

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -2928,6 +2928,29 @@
     case CheckTypeInfoFlags:
         break;
 
+    case ParseInt: {
+        AbstractValue value = forNode(node->child1());
+        if (value.m_type && !(value.m_type & ~SpecInt32Only)) {
+            JSValue radix;
+            if (!node->child2())
+                radix = jsNumber(0);
+            else
+                radix = forNode(node->child2()).m_value;
+
+            if (radix.isNumber()
+                && (radix.asNumber() == 0 || radix.asNumber() == 10)) {
+                m_state.setFoundConstants(true);
+                forNode(node).setType(SpecInt32Only);
+                break;
+            }
+        }
+
+        if (node->child1().useKind() == UntypedUse)
+            clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).setType(m_graph, SpecBytecodeNumber);
+        break;
+    }
+
     case CreateRest:
         if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
             // This means we're already having a bad time.

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -2360,6 +2360,27 @@
         }
     }
 
+    case ParseIntIntrinsic: {
+        if (argumentCountIncludingThis < 2)
+            return false;
+
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        insertChecks();
+        VirtualRegister valueOperand = virtualRegisterForArgument(1, registerOffset);
+        Node* parseInt;
+        if (argumentCountIncludingThis == 2)
+            parseInt = addToGraph(ParseInt, OpInfo(), OpInfo(prediction), get(valueOperand));
+        else {
+            ASSERT(argumentCountIncludingThis > 2);
+            VirtualRegister radixOperand = virtualRegisterForArgument(2, registerOffset);
+            parseInt = addToGraph(ParseInt, OpInfo(), OpInfo(prediction), get(valueOperand), get(radixOperand));
+        }
+        set(VirtualRegister(resultOperand), parseInt);
+        return true;
+    }
+
     case CharCodeAtIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -888,6 +888,17 @@
         def(HeapLocation(CheckTypeInfoFlagsLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));
         return;
 
+    case ParseInt:
+        // Note: We would have eliminated a ParseInt that has just a single child as an Int32Use inside fixup.
+        if (node->child1().useKind() == StringUse && (!node->child2() || node->child2().useKind() == Int32Use)) {
+            def(PureValue(node));
+            return;
+        }
+
+        read(World);
+        write(Heap);
+        return;
+
     case OverridesHasInstance:
         read(JSCell_typeInfoFlags);
         def(HeapLocation(OverridesHasInstanceLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));

Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -602,6 +602,29 @@
                 break;
             }
 
+            case ParseInt: {
+                AbstractValue& value = m_state.forNode(node->child1());
+                if (!value.m_type || (value.m_type & ~SpecInt32Only))
+                    break;
+
+                JSValue radix;
+                if (!node->child2())
+                    radix = jsNumber(0);
+                else
+                    radix = m_state.forNode(node->child2()).m_value;
+
+                if (!radix.isNumber())
+                    break;
+
+                if (radix.asNumber() == 0 || radix.asNumber() == 10) {
+                    node->child2() = Edge();
+                    node->convertToIdentity();
+                    changed = true;
+                }
+
+                break;
+            }
+
             case Check: {
                 alreadyHandled = true;
                 m_interpreter.execute(indexInBlock);

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -310,6 +310,7 @@
     case CallDOMGetter:
     case CallDOM:
     case ArraySlice:
+    case ParseInt: // We might resolve a rope even though we don't clobber anything.
         return true;
         
     case MultiPutByOffset:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -1793,6 +1793,24 @@
             break;
         }
 
+        case ParseInt: {
+            if (node->child1()->shouldSpeculateInt32() && !node->child2()) {
+                fixEdge<Int32Use>(node->child1());
+                node->convertToIdentity();
+                break;
+            }
+
+            if (node->child1()->shouldSpeculateString()) {
+                fixEdge<StringUse>(node->child1());
+                node->clearFlags(NodeMustGenerate);
+            }
+
+            if (node->child2())
+                fixEdge<Int32Use>(node->child2());
+
+            break;
+        }
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -1484,6 +1484,7 @@
         case LoadFromJSMapBucket:
         case CallDOMGetter:
         case CallDOM:
+        case ParseInt:
             return true;
         default:
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -244,6 +244,7 @@
     macro(CheckInBounds, NodeMustGenerate) \
     macro(CheckStringIdent, NodeMustGenerate) \
     macro(CheckTypeInfoFlags, NodeMustGenerate) /* Takes an OpInfo with the flags you want to test are set */\
+    macro(ParseInt, NodeMustGenerate | NodeResultJS) \
     \
     /* Optimizations for array mutation. */\
     macro(ArrayPush, NodeResultJS | NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -56,6 +56,7 @@
 #include "JSSet.h"
 #include "ObjectConstructor.h"
 #include "Operations.h"
+#include "ParseInt.h"
 #include "RegExpObject.h"
 #include "Repatch.h"
 #include "ScopedArguments.h"
@@ -174,6 +175,14 @@
     baseValue.putInline(exec, ident, putValue, slot);
 }
 
+static ALWAYS_INLINE EncodedJSValue parseIntResult(double input)
+{
+    int asInt = static_cast<int>(input);
+    if (static_cast<double>(asInt) == input)
+        return JSValue::encode(jsNumber(asInt));
+    return JSValue::encode(jsNumber(input));
+}
+
 extern "C" {
 
 EncodedJSValue JIT_OPERATION operationToThis(ExecState* exec, EncodedJSValue encodedOp)
@@ -848,6 +857,53 @@
     scope.release();
     return JSValue::encode(asRegExpObject(base)->exec(exec, globalObject, input));
 }
+
+EncodedJSValue JIT_OPERATION operationParseIntNoRadixGeneric(ExecState* exec, EncodedJSValue value)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return toStringView(exec, JSValue::decode(value), [&] (StringView view) {
+        // This version is as if radix was undefined. Hence, undefined.toNumber() === 0.
+        return parseIntResult(parseInt(view, 0));
+    });
+}
+
+EncodedJSValue JIT_OPERATION operationParseIntStringNoRadix(ExecState* exec, JSString* string)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto viewWithString = string->viewWithUnderlyingString(*exec);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    // This version is as if radix was undefined. Hence, undefined.toNumber() === 0.
+    return parseIntResult(parseInt(viewWithString.view, 0));
+}
+
+EncodedJSValue JIT_OPERATION operationParseIntString(ExecState* exec, JSString* string, int32_t radix)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto viewWithString = string->viewWithUnderlyingString(*exec);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    // This version is as if radix was undefined. Hence, undefined.toNumber() === 0.
+    return parseIntResult(parseInt(viewWithString.view, radix));
+}
+
+EncodedJSValue JIT_OPERATION operationParseIntGeneric(ExecState* exec, EncodedJSValue value, int32_t radix)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return toStringView(exec, JSValue::decode(value), [&] (StringView view) {
+        return parseIntResult(parseInt(view, radix));
+    });
+}
         
 size_t JIT_OPERATION operationRegExpTestString(ExecState* exec, JSGlobalObject* globalObject, RegExpObject* regExpObject, JSString* input)
 {

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -163,6 +163,11 @@
 JSCell* JIT_OPERATION operationJSMapFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
 JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
 
+EncodedJSValue JIT_OPERATION operationParseIntNoRadixGeneric(ExecState*, EncodedJSValue);
+EncodedJSValue JIT_OPERATION operationParseIntStringNoRadix(ExecState*, JSString*);
+EncodedJSValue JIT_OPERATION operationParseIntString(ExecState*, JSString*, int32_t);
+EncodedJSValue JIT_OPERATION operationParseIntGeneric(ExecState*, EncodedJSValue, int32_t);
+
 JSCell* JIT_OPERATION operationNewStringObject(ExecState*, JSString*, Structure*);
 JSCell* JIT_OPERATION operationToStringOnCell(ExecState*, JSCell*);
 JSCell* JIT_OPERATION operationToString(ExecState*, EncodedJSValue);

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -966,6 +966,14 @@
             setPrediction(SpecString);
             break;
         }
+        case ParseInt: {
+            // We expect this node to almost always produce an int32. However,
+            // it's possible it produces NaN or integers out of int32 range. We
+            // rely on the heap prediction since the parseInt() call profiled
+            // its result.
+            setPrediction(m_currentNode->getHeapPrediction());
+            break;
+        }
 
         case GetLocal:
         case SetLocal:

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -269,6 +269,7 @@
     case ProfileType:
     case ProfileControlFlow:
     case CheckTypeInfoFlags:
+    case ParseInt:
     case OverridesHasInstance:
     case InstanceOf:
     case InstanceOfCustom:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -3120,6 +3120,71 @@
     noResult(node);
 }
 
+void SpeculativeJIT::compileParseInt(Node* node)
+{
+    RELEASE_ASSERT(node->child1().useKind() == UntypedUse || node->child1().useKind() == StringUse);
+
+    GPRFlushedCallResult resultPayload(this);
+    GPRReg resultPayloadGPR = resultPayload.gpr();
+#if USE(JSVALUE64)
+    JSValueRegs resultRegs(resultPayloadGPR);
+#else
+    GPRFlushedCallResult2 resultTag(this);
+    GPRReg resultTagGPR = resultTag.gpr();
+    JSValueRegs resultRegs(resultTagGPR, resultPayloadGPR);
+#endif
+
+    if (node->child2()) {
+        SpeculateInt32Operand radix(this, node->child2());
+        GPRReg radixGPR = radix.gpr();
+        if (node->child1().useKind() == UntypedUse) {
+            JSValueOperand value(this, node->child1());
+
+            flushRegisters();
+#if USE(JSVALUE64)
+            callOperation(operationParseIntGeneric, resultRegs.gpr(), value.gpr(), radixGPR);
+#else
+            callOperation(operationParseIntGeneric, resultRegs, value.jsValueRegs(), radixGPR);
+#endif
+            m_jit.exceptionCheck();
+        } else {
+            SpeculateCellOperand value(this, node->child1());
+            GPRReg valueGPR = value.gpr();
+            speculateString(node->child1(), valueGPR);
+
+            flushRegisters();
+#if USE(JSVALUE64)
+            callOperation(operationParseIntString, resultRegs.gpr(), valueGPR, radixGPR);
+#else
+            callOperation(operationParseIntString, resultRegs, valueGPR, radixGPR);
+#endif
+            m_jit.exceptionCheck();
+        }
+    } else {
+        if (node->child1().useKind() == UntypedUse) {
+            JSValueOperand value(this, node->child1());
+
+            flushRegisters();
+#if USE(JSVALUE64)
+            callOperation(operationParseIntNoRadixGeneric, resultRegs.gpr(), value.jsValueRegs());
+#else
+            callOperation(operationParseIntNoRadixGeneric, resultRegs, value.jsValueRegs());
+#endif
+            m_jit.exceptionCheck();
+        } else {
+            SpeculateCellOperand value(this, node->child1());
+            GPRReg valueGPR = value.gpr();
+            speculateString(node->child1(), valueGPR);
+
+            flushRegisters();
+            callOperation(operationParseIntStringNoRadix, resultRegs, valueGPR);
+            m_jit.exceptionCheck();
+        }
+    }
+
+    jsValueResult(resultRegs, node);
+}
+
 void SpeculativeJIT::compileInstanceOf(Node* node)
 {
     if (node->child1().useKind() == UntypedUse) {

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -1307,6 +1307,13 @@
         m_jit.setupArguments(arg1, arg2);
         return appendCallSetResult(operation, result);
     }
+
+    JITCompiler::Call callOperation(J_JITOperation_EJss operation, JSValueRegs result, GPRReg arg1)
+    {
+        m_jit.setupArgumentsWithExecState(arg1);
+        return appendCallSetResult(operation, result);
+    }
+
     JITCompiler::Call callOperation(T_JITOperation_EJss operation, GPRReg result, GPRReg arg1)
     {
         m_jit.setupArgumentsWithExecState(arg1);
@@ -1889,6 +1896,11 @@
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg.payloadGPR(), arg.tagGPR(), mathIC);
         return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
     }
+    JITCompiler::Call callOperation(J_JITOperation_EJZ operation, JSValueRegs result, JSValueRegs arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), arg2);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), arg2.payloadGPR(), arg2.tagGPR(), mathIC);
@@ -2435,6 +2447,14 @@
         m_jit.setupResults(result1, result2);
         return call;
     }
+    JITCompiler::Call appendCallSetResult(const FunctionPtr& function, JSValueRegs resultRegs)
+    {
+#if USE(JSVALUE64)
+        return appendCallSetResult(function, resultRegs.gpr());
+#else
+        return appendCallSetResult(function, resultRegs.payloadGPR(), resultRegs.tagGPR());
+#endif
+    }
 #if CPU(X86)
     JITCompiler::Call appendCallSetResult(const FunctionPtr& function, FPRReg result)
     {
@@ -2684,6 +2704,8 @@
 
     void compileCheckTypeInfoFlags(Node*);
     void compileCheckStringIdent(Node*);
+
+    void compileParseInt(Node*);
     
     void compileValueRep(Node*);
     void compileDoubleRep(Node*);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -4666,6 +4666,11 @@
         break;
     }
 
+    case ParseInt: {
+        compileParseInt(node);
+        break;
+    }
+
     case CheckTypeInfoFlags: {
         compileCheckTypeInfoFlags(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -4634,6 +4634,11 @@
         break;
     }
 
+    case ParseInt: {
+        compileParseInt(node);
+        break;
+    }
+
     case OverridesHasInstance: {
 
         Node* hasInstanceValueNode = node->child2().node();

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -283,6 +283,7 @@
     case CallDOM:
     case CallDOMGetter:
     case ArraySlice:
+    case ParseInt:
         // These are OK.
         break;
 

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -960,6 +960,9 @@
         case IsTypedArrayView:
             compileIsTypedArrayView();
             break;
+        case ParseInt:
+            compileParseInt();
+            break;
         case TypeOf:
             compileTypeOf();
             break;
@@ -8221,6 +8224,25 @@
         setBoolean(m_out.phi(Int32, fastResult, slowResult));
     }
 
+    void compileParseInt()
+    {
+        RELEASE_ASSERT(m_node->child1().useKind() == UntypedUse || m_node->child1().useKind() == StringUse);
+        LValue result;
+        if (m_node->child2()) {
+            LValue radix = lowInt32(m_node->child2());
+            if (m_node->child1().useKind() == UntypedUse)
+                result = vmCall(Int64, m_out.operation(operationParseIntGeneric), m_callFrame, lowJSValue(m_node->child1()), radix);
+            else
+                result = vmCall(Int64, m_out.operation(operationParseIntString), m_callFrame, lowString(m_node->child1()), radix);
+        } else {
+            if (m_node->child1().useKind() == UntypedUse)
+                result = vmCall(Int64, m_out.operation(operationParseIntNoRadixGeneric), m_callFrame, lowJSValue(m_node->child1()));
+            else
+                result = vmCall(Int64, m_out.operation(operationParseIntStringNoRadix), m_callFrame, lowString(m_node->child1()));
+        }
+        setJSValue(result);
+    }
+
     void compileOverridesHasInstance()
     {
         FrozenValue* defaultHasInstanceFunction = m_node->cellOperand();

Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (212938 => 212939)


--- trunk/Source/_javascript_Core/jit/JITOperations.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -153,6 +153,7 @@
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJJMic)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJMic)(ExecState*, EncodedJSValue, void*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssZ)(ExecState*, JSString*, int32_t);
+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJss)(ExecState*, JSString*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssReo)(ExecState*, JSString*, RegExpObject*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssReoJss)(ExecState*, JSString*, RegExpObject*, JSString*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJP)(ExecState*, EncodedJSValue, void*);

Modified: trunk/Source/_javascript_Core/parser/Lexer.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/parser/Lexer.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/parser/Lexer.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -29,10 +29,10 @@
 #include "Identifier.h"
 #include "JSCInlines.h"
 #include "JSFunctionInlines.h"
-#include "JSGlobalObjectFunctions.h"
 #include "KeywordLookup.h"
 #include "Lexer.lut.h"
 #include "Nodes.h"
+#include "ParseInt.h"
 #include "Parser.h"
 #include <ctype.h>
 #include <limits.h>

Modified: trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -25,7 +25,7 @@
 #include "InlineCallFrame.h"
 #include "JSScope.h"
 #include "JSCInlines.h"
-#include "JSGlobalObjectFunctions.h"
+#include "ParseInt.h"
 #include <wtf/text/StringBuilder.h>
 
 namespace JSC {

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -79,6 +79,7 @@
     AtomicsWakeIntrinsic,
     AtomicsXorIntrinsic,
     ToLowerCaseIntrinsic,
+    ParseIntIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -576,7 +576,7 @@
     }
     m_proxyRevokeStructure.set(vm, this, ProxyRevoke::createStructure(vm, this, m_functionPrototype.get()));
 
-    m_parseIntFunction.set(vm, this, JSFunction::create(vm, this, 2, vm.propertyNames->parseInt.string(), globalFuncParseInt, NoIntrinsic));
+    m_parseIntFunction.set(vm, this, JSFunction::create(vm, this, 2, vm.propertyNames->parseInt.string(), globalFuncParseInt, ParseIntIntrinsic));
     putDirectWithoutTransition(vm, vm.propertyNames->parseInt, m_parseIntFunction.get(), DontEnum);
     
     m_arrayBufferPrototype.set(vm, this, JSArrayBufferPrototype::create(vm, this, JSArrayBufferPrototype::createStructure(vm, this, m_objectPrototype.get()), ArrayBufferSharingMode::Default));

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -41,6 +41,7 @@
 #include "LiteralParser.h"
 #include "Nodes.h"
 #include "JSCInlines.h"
+#include "ParseInt.h"
 #include "Parser.h"
 #include "StackVisitor.h"
 #include <wtf/dtoa.h>
@@ -61,19 +62,6 @@
 
 static const char* const ObjectProtoCalledOnNullOrUndefinedError = "Object.prototype.__proto__ called on null or undefined";
 
-template<typename CallbackWhenNoException>
-static ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(StringView)>::type toStringView(ExecState* exec, JSValue value, CallbackWhenNoException callback)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    JSString* string = value.toStringOrNull(exec);
-    if (UNLIKELY(!string))
-        return { };
-    auto viewWithString = string->viewWithUnderlyingString(*exec);
-    RETURN_IF_EXCEPTION(scope, { });
-    return callback(viewWithString.view);
-}
-
 template<unsigned charactersCount>
 static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount])
 {
@@ -251,185 +239,6 @@
     });
 }
 
-bool isStrWhiteSpace(UChar c)
-{
-    switch (c) {
-        // ECMA-262-5th 7.2 & 7.3
-        case 0x0009:
-        case 0x000A:
-        case 0x000B:
-        case 0x000C:
-        case 0x000D:
-        case 0x0020:
-        case 0x00A0:
-        case 0x180E: // This character used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such.
-        case 0x2028:
-        case 0x2029:
-        case 0xFEFF:
-            return true;
-        default:
-            return c > 0xFF && u_charType(c) == U_SPACE_SEPARATOR;
-    }
-}
-
-static int parseDigit(unsigned short c, int radix)
-{
-    int digit = -1;
-
-    if (isASCIIDigit(c))
-        digit = c - '0';
-    else if (isASCIIUpper(c))
-        digit = c - 'A' + 10;
-    else if (isASCIILower(c))
-        digit = c - 'a' + 10;
-
-    if (digit >= radix)
-        return -1;
-    return digit;
-}
-
-double parseIntOverflow(const LChar* s, unsigned length, int radix)
-{
-    double number = 0.0;
-    double radixMultiplier = 1.0;
-
-    for (const LChar* p = s + length - 1; p >= s; p--) {
-        if (radixMultiplier == std::numeric_limits<double>::infinity()) {
-            if (*p != '0') {
-                number = std::numeric_limits<double>::infinity();
-                break;
-            }
-        } else {
-            int digit = parseDigit(*p, radix);
-            number += digit * radixMultiplier;
-        }
-
-        radixMultiplier *= radix;
-    }
-
-    return number;
-}
-
-static double parseIntOverflow(const UChar* s, unsigned length, int radix)
-{
-    double number = 0.0;
-    double radixMultiplier = 1.0;
-
-    for (const UChar* p = s + length - 1; p >= s; p--) {
-        if (radixMultiplier == std::numeric_limits<double>::infinity()) {
-            if (*p != '0') {
-                number = std::numeric_limits<double>::infinity();
-                break;
-            }
-        } else {
-            int digit = parseDigit(*p, radix);
-            number += digit * radixMultiplier;
-        }
-
-        radixMultiplier *= radix;
-    }
-
-    return number;
-}
-
-static double parseIntOverflow(StringView string, int radix)
-{
-    if (string.is8Bit())
-        return parseIntOverflow(string.characters8(), string.length(), radix);
-    return parseIntOverflow(string.characters16(), string.length(), radix);
-}
-
-// ES5.1 15.1.2.2
-template <typename CharType>
-ALWAYS_INLINE
-static double parseInt(StringView s, const CharType* data, int radix)
-{
-    // 1. Let inputString be ToString(string).
-    // 2. Let S be a newly created substring of inputString consisting of the first character that is not a
-    //    StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white
-    //    space.) If inputString does not contain any such characters, let S be the empty string.
-    int length = s.length();
-    int p = 0;
-    while (p < length && isStrWhiteSpace(data[p]))
-        ++p;
-
-    // 3. Let sign be 1.
-    // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1.
-    // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S.
-    double sign = 1;
-    if (p < length) {
-        if (data[p] == '+')
-            ++p;
-        else if (data[p] == '-') {
-            sign = -1;
-            ++p;
-        }
-    }
-
-    // 6. Let R = ToInt32(radix).
-    // 7. Let stripPrefix be true.
-    // 8. If R != 0,then
-    //   b. If R != 16, let stripPrefix be false.
-    // 9. Else, R == 0
-    //   a. LetR = 10.
-    // 10. If stripPrefix is true, then
-    //   a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X,
-    //      then remove the first two characters from S and let R = 16.
-    // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S
-    //     consisting of all characters before the first such character; otherwise, let Z be S.
-    if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
-        radix = 16;
-        p += 2;
-    } else if (radix == 0)
-        radix = 10;
-
-    // 8.a If R < 2 or R > 36, then return NaN.
-    if (radix < 2 || radix > 36)
-        return PNaN;
-
-    // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters
-    //     A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant
-    //     digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation;
-    //     and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the
-    //     mathematical integer value that is represented by Z in radix-R notation.)
-    // 14. Let number be the Number value for mathInt.
-    int firstDigitPosition = p;
-    bool sawDigit = false;
-    double number = 0;
-    while (p < length) {
-        int digit = parseDigit(data[p], radix);
-        if (digit == -1)
-            break;
-        sawDigit = true;
-        number *= radix;
-        number += digit;
-        ++p;
-    }
-
-    // 12. If Z is empty, return NaN.
-    if (!sawDigit)
-        return PNaN;
-
-    // Alternate code path for certain large numbers.
-    if (number >= mantissaOverflowLowerBound) {
-        if (radix == 10) {
-            size_t parsedLength;
-            number = parseDouble(s.substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
-        } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
-            number = parseIntOverflow(s.substring(firstDigitPosition, p - firstDigitPosition), radix);
-    }
-
-    // 15. Return sign x number.
-    return sign * number;
-}
-
-static double parseInt(StringView s, int radix)
-{
-    if (s.is8Bit())
-        return parseInt(s, s.characters8(), radix);
-    return parseInt(s, s.characters16(), radix);
-}
-
 static const int SizeOfInfinity = 8;
 
 template <typename CharType>

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.h (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.h	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -52,9 +52,6 @@
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncImportModule(ExecState*);
 
-static const double mantissaOverflowLowerBound = 9007199254740992.0;
-double parseIntOverflow(const LChar*, unsigned length, int radix);
-bool isStrWhiteSpace(UChar);
 double jsToNumber(StringView);
 
 } // namespace JSC

Added: trunk/Source/_javascript_Core/runtime/ParseInt.h (0 => 212939)


--- trunk/Source/_javascript_Core/runtime/ParseInt.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/runtime/ParseInt.h	2017-02-24 04:07:30 UTC (rev 212939)
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "JSCJSValue.h"
+#include <wtf/dtoa.h>
+
+namespace JSC {
+
+static const double mantissaOverflowLowerBound = 9007199254740992.0;
+
+ALWAYS_INLINE static int parseDigit(unsigned short c, int radix)
+{
+    int digit = -1;
+
+    if (isASCIIDigit(c))
+        digit = c - '0';
+    else if (isASCIIUpper(c))
+        digit = c - 'A' + 10;
+    else if (isASCIILower(c))
+        digit = c - 'a' + 10;
+
+    if (digit >= radix)
+        return -1;
+    return digit;
+}
+
+static double parseIntOverflow(const LChar* s, unsigned length, int radix)
+{
+    double number = 0.0;
+    double radixMultiplier = 1.0;
+
+    for (const LChar* p = s + length - 1; p >= s; p--) {
+        if (radixMultiplier == std::numeric_limits<double>::infinity()) {
+            if (*p != '0') {
+                number = std::numeric_limits<double>::infinity();
+                break;
+            }
+        } else {
+            int digit = parseDigit(*p, radix);
+            number += digit * radixMultiplier;
+        }
+
+        radixMultiplier *= radix;
+    }
+
+    return number;
+}
+
+static double parseIntOverflow(const UChar* s, unsigned length, int radix)
+{
+    double number = 0.0;
+    double radixMultiplier = 1.0;
+
+    for (const UChar* p = s + length - 1; p >= s; p--) {
+        if (radixMultiplier == std::numeric_limits<double>::infinity()) {
+            if (*p != '0') {
+                number = std::numeric_limits<double>::infinity();
+                break;
+            }
+        } else {
+            int digit = parseDigit(*p, radix);
+            number += digit * radixMultiplier;
+        }
+
+        radixMultiplier *= radix;
+    }
+
+    return number;
+}
+
+static double parseIntOverflow(StringView string, int radix)
+{
+    if (string.is8Bit())
+        return parseIntOverflow(string.characters8(), string.length(), radix);
+    return parseIntOverflow(string.characters16(), string.length(), radix);
+}
+
+ALWAYS_INLINE static bool isStrWhiteSpace(UChar c)
+{
+    switch (c) {
+    // ECMA-262-5th 7.2 & 7.3
+    case 0x0009:
+    case 0x000A:
+    case 0x000B:
+    case 0x000C:
+    case 0x000D:
+    case 0x0020:
+    case 0x00A0:
+    case 0x180E: // This character used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such.
+    case 0x2028:
+    case 0x2029:
+    case 0xFEFF:
+        return true;
+    default:
+        return c > 0xFF && u_charType(c) == U_SPACE_SEPARATOR;
+    }
+}
+
+// ES5.1 15.1.2.2
+template <typename CharType>
+ALWAYS_INLINE
+static double parseInt(StringView s, const CharType* data, int radix)
+{
+    // 1. Let inputString be ToString(string).
+    // 2. Let S be a newly created substring of inputString consisting of the first character that is not a
+    //    StrWhiteSpaceChar and all characters following that character. (In other words, remove leading white
+    //    space.) If inputString does not contain any such characters, let S be the empty string.
+    int length = s.length();
+    int p = 0;
+    while (p < length && isStrWhiteSpace(data[p]))
+        ++p;
+
+    // 3. Let sign be 1.
+    // 4. If S is not empty and the first character of S is a minus sign -, let sign be -1.
+    // 5. If S is not empty and the first character of S is a plus sign + or a minus sign -, then remove the first character from S.
+    double sign = 1;
+    if (p < length) {
+        if (data[p] == '+')
+            ++p;
+        else if (data[p] == '-') {
+            sign = -1;
+            ++p;
+        }
+    }
+
+    // 6. Let R = ToInt32(radix).
+    // 7. Let stripPrefix be true.
+    // 8. If R != 0,then
+    //   b. If R != 16, let stripPrefix be false.
+    // 9. Else, R == 0
+    //   a. LetR = 10.
+    // 10. If stripPrefix is true, then
+    //   a. If the length of S is at least 2 and the first two characters of S are either ―0x or ―0X,
+    //      then remove the first two characters from S and let R = 16.
+    // 11. If S contains any character that is not a radix-R digit, then let Z be the substring of S
+    //     consisting of all characters before the first such character; otherwise, let Z be S.
+    if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) {
+        radix = 16;
+        p += 2;
+    } else if (radix == 0)
+        radix = 10;
+
+    // 8.a If R < 2 or R > 36, then return NaN.
+    if (radix < 2 || radix > 36)
+        return PNaN;
+
+    // 13. Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters
+    //     A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant
+    //     digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation;
+    //     and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the
+    //     mathematical integer value that is represented by Z in radix-R notation.)
+    // 14. Let number be the Number value for mathInt.
+    int firstDigitPosition = p;
+    bool sawDigit = false;
+    double number = 0;
+    while (p < length) {
+        int digit = parseDigit(data[p], radix);
+        if (digit == -1)
+            break;
+        sawDigit = true;
+        number *= radix;
+        number += digit;
+        ++p;
+    }
+
+    // 12. If Z is empty, return NaN.
+    if (!sawDigit)
+        return PNaN;
+
+    // Alternate code path for certain large numbers.
+    if (number >= mantissaOverflowLowerBound) {
+        if (radix == 10) {
+            size_t parsedLength;
+            number = parseDouble(s.substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
+        } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
+            number = parseIntOverflow(s.substring(firstDigitPosition, p - firstDigitPosition), radix);
+    }
+
+    // 15. Return sign x number.
+    return sign * number;
+}
+
+ALWAYS_INLINE static double parseInt(StringView s, int radix)
+{
+    if (s.is8Bit())
+        return parseInt(s, s.characters8(), radix);
+    return parseInt(s, s.characters16(), radix);
+}
+
+template<typename CallbackWhenNoException>
+static ALWAYS_INLINE typename std::result_of<CallbackWhenNoException(StringView)>::type toStringView(ExecState* exec, JSValue value, CallbackWhenNoException callback)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    JSString* string = value.toStringOrNull(exec);
+    if (UNLIKELY(!string))
+        return { };
+    auto viewWithString = string->viewWithUnderlyingString(*exec);
+    RETURN_IF_EXCEPTION(scope, { });
+    return callback(viewWithString.view);
+}
+
+} // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (212938 => 212939)


--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2017-02-24 03:22:05 UTC (rev 212938)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2017-02-24 04:07:30 UTC (rev 212939)
@@ -37,6 +37,7 @@
 #include "JSStringIterator.h"
 #include "Lookup.h"
 #include "ObjectPrototype.h"
+#include "ParseInt.h"
 #include "PropertyNameArray.h"
 #include "RegExpCache.h"
 #include "RegExpConstructor.h"
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to