Intrinsicify parseInt
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Feb 2017 04:07:30 +0000 (04:07 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Feb 2017 04:07:30 +0000 (04:07 +0000)
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/JavaScriptCore:

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:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@212939 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
JSTests/ChangeLog
JSTests/stress/parse-int-intrinsic.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/runtime/ErrorInstance.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/ParseInt.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/StringPrototype.cpp

index 877bcf9..7412016 100644 (file)
@@ -1,3 +1,27 @@
+2017-02-23  Saam Barati  <sbarati@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  <jfbastien@apple.com>
 
         WebAssembly: support 0x1 version
diff --git a/JSTests/stress/parse-int-intrinsic.js b/JSTests/stress/parse-int-intrinsic.js
new file mode 100644 (file)
index 0000000..9be1a4d
--- /dev/null
@@ -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();
index 146c996..acaba14 100644 (file)
@@ -1,3 +1,85 @@
+2017-02-23  Saam Barati  <sbarati@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  <jfbastien@apple.com>
 
         WebAssembly: support 0x1 version
index dbe3a47..54f0ab6 100644 (file)
@@ -2928,6 +2928,29 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     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.
index 584deeb..85374b5 100644 (file)
@@ -2360,6 +2360,27 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         }
     }
 
+    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;
index 619f274..4df48a2 100644 (file)
@@ -888,6 +888,17 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         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));
index dadd00a..114f01d 100644 (file)
@@ -602,6 +602,29 @@ private:
                 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);
index cdc0406..d5eab16 100644 (file)
@@ -310,6 +310,7 @@ bool doesGC(Graph& graph, Node* node)
     case CallDOMGetter:
     case CallDOM:
     case ArraySlice:
+    case ParseInt: // We might resolve a rope even though we don't clobber anything.
         return true;
         
     case MultiPutByOffset:
index 4aca996..3ce6494 100644 (file)
@@ -1793,6 +1793,24 @@ private:
             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:
index e590424..81d540a 100644 (file)
@@ -1484,6 +1484,7 @@ public:
         case LoadFromJSMapBucket:
         case CallDOMGetter:
         case CallDOM:
+        case ParseInt:
             return true;
         default:
             return false;
index 6af4b35..05d2065 100644 (file)
@@ -244,6 +244,7 @@ namespace JSC { namespace DFG {
     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) \
index 025a7cf..0b4e84d 100644 (file)
@@ -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 @@ static ALWAYS_INLINE void putWithThis(ExecState* exec, EncodedJSValue encodedBas
     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 @@ EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState* exec, JSGloba
     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)
 {
index 0404c41..852195c 100644 (file)
@@ -163,6 +163,11 @@ int32_t JIT_OPERATION operationMapHash(ExecState*, EncodedJSValue input);
 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);
index d0f4b47..7a081fe 100644 (file)
@@ -966,6 +966,14 @@ private:
             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:
index 1fe1e9e..4bcc68b 100644 (file)
@@ -269,6 +269,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ProfileType:
     case ProfileControlFlow:
     case CheckTypeInfoFlags:
+    case ParseInt:
     case OverridesHasInstance:
     case InstanceOf:
     case InstanceOfCustom:
index c81bbdd..1954cf4 100644 (file)
@@ -3120,6 +3120,71 @@ void SpeculativeJIT::compileCheckTypeInfoFlags(Node* node)
     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) {
index 0c1691b..226ee31 100644 (file)
@@ -1307,6 +1307,13 @@ public:
         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 @@ public:
         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 @@ public:
         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 @@ public:
 
     void compileCheckTypeInfoFlags(Node*);
     void compileCheckStringIdent(Node*);
+
+    void compileParseInt(Node*);
     
     void compileValueRep(Node*);
     void compileDoubleRep(Node*);
index 3f6d6e2..d91ddd4 100644 (file)
@@ -4666,6 +4666,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ParseInt: {
+        compileParseInt(node);
+        break;
+    }
+
     case CheckTypeInfoFlags: {
         compileCheckTypeInfoFlags(node);
         break;
index 3b3a9eb..848fa4a 100644 (file)
@@ -4634,6 +4634,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ParseInt: {
+        compileParseInt(node);
+        break;
+    }
+
     case OverridesHasInstance: {
 
         Node* hasInstanceValueNode = node->child2().node();
index 4383f12..1a151ec 100644 (file)
@@ -283,6 +283,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CallDOM:
     case CallDOMGetter:
     case ArraySlice:
+    case ParseInt:
         // These are OK.
         break;
 
index 6420e67..2e733e0 100644 (file)
@@ -960,6 +960,9 @@ private:
         case IsTypedArrayView:
             compileIsTypedArrayView();
             break;
+        case ParseInt:
+            compileParseInt();
+            break;
         case TypeOf:
             compileTypeOf();
             break;
@@ -8221,6 +8224,25 @@ private:
         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();
index 5b703f0..d72ffbe 100644 (file)
@@ -153,6 +153,7 @@ typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJJArp)(ExecState*, Encode
 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*);
index f1e1e0f..daea56d 100644 (file)
 #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>
index 21b2bb6..6c5b37f 100644 (file)
@@ -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 {
index adae5ef..263b93e 100644 (file)
@@ -79,6 +79,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     AtomicsWakeIntrinsic,
     AtomicsXorIntrinsic,
     ToLowerCaseIntrinsic,
+    ParseIntIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,
index ea04268..76c965c 100644 (file)
@@ -576,7 +576,7 @@ void JSGlobalObject::init(VM& vm)
     }
     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));
index e3a4146..84a9b93 100644 (file)
@@ -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 @@ namespace JSC {
 
 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 @@ static JSValue decode(ExecState* exec, const Bitmap<256>& doNotUnescape, bool st
     });
 }
 
-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>
index cd42704..d69b4d7 100644 (file)
@@ -52,9 +52,6 @@ EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*);
 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
diff --git a/Source/JavaScriptCore/runtime/ParseInt.h b/Source/JavaScriptCore/runtime/ParseInt.h
new file mode 100644 (file)
index 0000000..44d712d
--- /dev/null
@@ -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
index 6eb3557..dabbc26 100644 (file)
@@ -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"