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"